From 55a6b8d899f2c77d33f56a9d53b13b21b7dd11ba Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 27 Mar 2024 05:12:51 +0100 Subject: [PATCH 01/47] Blockstate pattern matching --- .../baritone/MixinBaritonePlayerContext.java | 4 +- .../lambda/interaction/building/Blueprint.kt | 50 +++++++ .../building/verify/StateMatcher.kt | 11 ++ .../building/verify/TargetState.kt | 44 ++++++ .../com/lambda/module/modules/player/Nuker.kt | 18 +++ .../main/kotlin/com/lambda/util/ItemUtils.kt | 126 ++++++++++++++++++ .../src/main/resources/lambda.accesswidener | 5 +- 7 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt create mode 100644 common/src/main/kotlin/com/lambda/util/ItemUtils.kt diff --git a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java index b4e377cd5..955f177db 100644 --- a/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java +++ b/common/src/main/java/com/lambda/mixin/baritone/MixinBaritonePlayerContext.java @@ -22,6 +22,8 @@ void syncRotationWithBaritone(CallbackInfoReturnable cir) { if (baritone != BaritoneUtils.getPrimary()) return; RotationManager rm = RotationManager.INSTANCE; - cir.setReturnValue(new Rotation((float) rm.getCurrentRotation().getYaw(), (float) rm.getCurrentRotation().getPitch())); + cir.setReturnValue(new Rotation( + (float) rm.getCurrentRotation().getYaw(), (float) rm.getCurrentRotation().getPitch()) + ); } } diff --git a/common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt new file mode 100644 index 000000000..2ded4ff02 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt @@ -0,0 +1,50 @@ +package com.lambda.interaction.building + +import com.lambda.interaction.building.verify.TargetState +import net.minecraft.structure.StructureTemplate +import net.minecraft.util.math.BlockBox +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box + +data class Blueprint( + private val structure: Map, + var offset: BlockPos? = null, +) { + val origin: BlockPos + get() = offset ?: structure.keys.firstOrNull() ?: BlockPos.ORIGIN + + private val modifications = mutableMapOf() + val structureMap: Map + get() { + offset?.let { + val offsetMap = mutableMapOf() + for ((pos, state) in structure) { + offsetMap[pos.add(it)] = state + } + return offsetMap + modifications + } + + return structure + modifications + } + + companion object { + fun Box.fromBox(targetState: TargetState) = + Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) + + fun BlockBox.fromBlockBox(targetState: TargetState) = + Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) + + fun BlockPos.fromBlockPos(targetState: TargetState) = + Blueprint(setOf(this).associateWith { targetState }) + +// fun Schematic.fromSchematic() = +// Blueprint(this.blockMap.map { it.key to TargetState.BlockState(it.value) }.toMap()) + + fun StructureTemplate.fromStructureTemplate() = + Blueprint( + blockInfoLists + .flatMap { it.all } + .associate { it.pos to TargetState.State(it.state) } + ) + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt new file mode 100644 index 000000000..e4d990d77 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt @@ -0,0 +1,11 @@ +package com.lambda.interaction.building.verify + +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos + +interface StateMatcher { + fun matches(state: BlockState, pos: BlockPos, world: ClientWorld): Boolean + fun getStack(world: ClientWorld, pos: BlockPos): ItemStack +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt new file mode 100644 index 000000000..b5c3c41cf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt @@ -0,0 +1,44 @@ +package com.lambda.interaction.building.verify + +import com.lambda.util.ItemUtils.block +import net.minecraft.block.BlockState +import net.minecraft.client.world.ClientWorld +import net.minecraft.item.ItemStack +import net.minecraft.item.Items +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +sealed class TargetState : StateMatcher { + data object Air : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.isAir + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = ItemStack.EMPTY + } + data object Solid : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + state.isSolidBlock(world, pos) + override fun getStack(world: ClientWorld, pos: BlockPos) = ItemStack(Items.NETHERRACK) + } + data class Support(val direction: Direction) : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + world.getBlockState(pos.offset(direction)).isSolidBlock(world, pos.offset(direction)) + + override fun getStack(world: ClientWorld, pos: BlockPos) = ItemStack(Items.NETHERRACK) + } + data class State(val blockState: BlockState) : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + state == blockState + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = + blockState.block.getPickStack(world, pos, blockState) + } + data class Block(val block: net.minecraft.block.Block) : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.block == block + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = + block.getPickStack(world, pos, block.defaultState) + } + data class Stack(val itemStack: ItemStack) : TargetState() { + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + state.block == itemStack.item.block + + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = itemStack + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt new file mode 100644 index 000000000..99efe65f8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -0,0 +1,18 @@ +package com.lambda.module.modules.player + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.Module + +object Nuker : Module( + name = "Nuker", + description = "Breaks blocks around you" +) { + private val flatten by setting("Flatten", false) + + init { + listener { + + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/ItemUtils.kt new file mode 100644 index 000000000..e48c6383f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/ItemUtils.kt @@ -0,0 +1,126 @@ +package com.lambda.util + +import net.minecraft.block.Block +import net.minecraft.item.Item +import net.minecraft.item.Items + +object ItemUtils { + val pickaxes = setOf( + Items.WOODEN_PICKAXE, + Items.STONE_PICKAXE, + Items.IRON_PICKAXE, + Items.GOLDEN_PICKAXE, + Items.DIAMOND_PICKAXE, + Items.NETHERITE_PICKAXE, + ) + + val shovels = setOf( + Items.WOODEN_SHOVEL, + Items.STONE_SHOVEL, + Items.IRON_SHOVEL, + Items.GOLDEN_SHOVEL, + Items.DIAMOND_SHOVEL, + Items.NETHERITE_SHOVEL, + ) + + val axes = setOf( + Items.WOODEN_AXE, + Items.STONE_AXE, + Items.IRON_AXE, + Items.GOLDEN_AXE, + Items.DIAMOND_AXE, + Items.NETHERITE_AXE, + ) + + val hoes = setOf( + Items.WOODEN_HOE, + Items.STONE_HOE, + Items.IRON_HOE, + Items.GOLDEN_HOE, + Items.DIAMOND_HOE, + Items.NETHERITE_HOE, + ) + + val swords = setOf( + Items.WOODEN_SWORD, + Items.STONE_SWORD, + Items.IRON_SWORD, + Items.GOLDEN_SWORD, + Items.DIAMOND_SWORD, + Items.NETHERITE_SWORD, + ) + + val misc = setOf( + Items.SHEARS, + ) + + val shulkerBoxes = setOf( + Items.SHULKER_BOX, + Items.WHITE_SHULKER_BOX, + Items.ORANGE_SHULKER_BOX, + Items.MAGENTA_SHULKER_BOX, + Items.LIGHT_BLUE_SHULKER_BOX, + Items.YELLOW_SHULKER_BOX, + Items.LIME_SHULKER_BOX, + Items.PINK_SHULKER_BOX, + Items.GRAY_SHULKER_BOX, + Items.LIGHT_GRAY_SHULKER_BOX, + Items.CYAN_SHULKER_BOX, + Items.PURPLE_SHULKER_BOX, + Items.BLUE_SHULKER_BOX, + Items.BROWN_SHULKER_BOX, + Items.GREEN_SHULKER_BOX, + Items.RED_SHULKER_BOX, + Items.BLACK_SHULKER_BOX, + ) + + val chests = setOf( + Items.CHEST, + Items.TRAPPED_CHEST, + Items.ENDER_CHEST, + Items.BARREL, + ) + + val tools = pickaxes + shovels + axes + hoes + swords + misc + + val Item.block: Block get() = Block.getBlockFromItem(this) + + fun Int.formatToHumanCount(): String { + if (this < 0) { + return "Invalid input" + } + + return buildString { + val dubs = this@formatToHumanCount / (54 * 27) + val shulkers = (this@formatToHumanCount % (54 * 27)) / 64 + val remainingItems = this@formatToHumanCount % 64 + + if (dubs > 0) { + append("$dubs dub") + if (dubs > 1) { + append("s") + } + if (shulkers > 0 || remainingItems > 0) { + append(" ") + } + } + + if (shulkers > 0) { + append("$shulkers shulker") + if (shulkers > 1) { + append("s") + } + if (remainingItems > 0) { + append(" ") + } + } + + if (remainingItems > 0) { + append("$remainingItems item") + if (remainingItems > 1) { + append("s") + } + } + } + } +} diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index eba7ff6b3..dcf61e84f 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -7,6 +7,8 @@ accessible field net/minecraft/client/MinecraftClient pausedTickDelta F # World accessible field net/minecraft/client/world/ClientWorld entityManager Lnet/minecraft/client/world/ClientEntityManager; +accessible field net/minecraft/client/world/ClientEntityManager cache Lnet/minecraft/world/entity/SectionedEntityCache; +accessible field net/minecraft/world/entity/EntityTrackingSection collection Lnet/minecraft/util/collection/TypeFilterableList; # Entity accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity; @@ -30,5 +32,4 @@ accessible field net/minecraft/text/Style font Lnet/minecraft/util/Identifier; accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/text/ClickEvent;Lnet/minecraft/text/HoverEvent;Ljava/lang/String;Lnet/minecraft/util/Identifier;)V # Other -accessible field net/minecraft/client/world/ClientEntityManager cache Lnet/minecraft/world/entity/SectionedEntityCache; -accessible field net/minecraft/world/entity/EntityTrackingSection collection Lnet/minecraft/util/collection/TypeFilterableList; +accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; \ No newline at end of file From 8bba416106c10406f63a737f169bec5d38029ff0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 27 Mar 2024 06:17:02 +0100 Subject: [PATCH 02/47] Item flow management --- .../building/manager/MaterialSourceManager.kt | 106 ++++++++++ .../building/material/MaterialDestination.kt | 15 ++ .../building/material/SourceSelection.kt | 90 ++++++++ .../building/material/StackSelection.kt | 195 ++++++++++++++++++ .../material/source/MaterialSource.kt | 20 ++ .../building/material/source/Source.kt | 69 +++++++ .../material/source/sources/ChestSource.kt | 21 ++ .../material/source/sources/CreativeSource.kt | 49 +++++ .../source/sources/EnderChestSource.kt | 19 ++ .../material/source/sources/HotbarSource.kt | 37 ++++ .../source/sources/InventorySource.kt | 106 ++++++++++ .../material/source/sources/MainHandSource.kt | 26 +++ .../material/source/sources/OffHandSource.kt | 27 +++ .../source/sources/ShulkerBoxSource.kt | 53 +++++ .../material/source/sources/StashSource.kt | 29 +++ .../lambda/module/modules/client/TaskFlow.kt | 15 ++ .../main/kotlin/com/lambda/util/BlockUtils.kt | 105 ++++++++++ .../main/kotlin/com/lambda/util/ItemUtils.kt | 18 +- .../com/lambda/util/player/MovementUtils.kt | 1 + .../com/lambda/util/player/SlotUtils.kt | 30 +++ .../com/lambda/util/world/EntityUtils.kt | 13 ++ 21 files changed, 1043 insertions(+), 1 deletion(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt create mode 100644 common/src/main/kotlin/com/lambda/util/BlockUtils.kt create mode 100644 common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt b/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt new file mode 100644 index 000000000..9d3e41731 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt @@ -0,0 +1,106 @@ +package com.lambda.interaction.building.manager + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.SourceSelection +import com.lambda.interaction.building.material.SourceSelection.Companion.selectSource +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.module.modules.client.TaskFlow.disposables +import com.lambda.interaction.building.material.StackSelection +import com.lambda.interaction.building.material.StackSelection.Companion.select +import com.lambda.util.ItemUtils +import com.lambda.util.player.SlotUtils.combined +import net.minecraft.block.BlockState +import net.minecraft.inventory.Inventories +import net.minecraft.item.BlockItem +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtElement +import net.minecraft.util.collection.DefaultedList + +/** + * Caches all the item sources and destinations and gives a simple interface to check if an item is available and where. + * Sources are: + * 1. Player inventory + * 2. Shulker boxes + * 3. Ender chest + * 4. Stash + */ +object MaterialSourceManager { + + val cache: MutableSet = mutableSetOf() + + val SafeContext.enderChestContent: List + get() = emptyList() + + val SafeContext.stashContent: List + get() = emptyList() + + val SafeContext.playerSources: Set + get() { + if (player.isCreative) { + return Source.CREATIVE.gather(player) + } + + return Source.INVENTORY.gather(player) + } + + val SafeContext.enderChestSources: Set + get() = Source.ENDER_CHEST.gather(player) + + val SafeContext.stashSources: Set + get() = Source.STASH.gather(player) + + val SafeContext.sources: Set + get() = playerSources + enderChestSources + stashSources + + fun SafeContext.getAvailableCount(stackSelection: StackSelection): Int { + return sources.sumOf { it.available(stackSelection) } + } + + fun SafeContext.getSourcesWith(stackSelection: StackSelection): List { + return sources.filter { it.available(stackSelection) >= stackSelection.count } + } + + fun SafeContext.getAvailableStacks(selection: SourceSelection): List { + return sources.filter(selection.selection).flatMap { it.stacks } + } + + fun SafeContext.getGroupedAvailableStacks(): Map> { + return sources.associateWith { it.stacks } + } + + fun getShulkerBoxContents(itemStack: ItemStack) = + BlockItem.getBlockEntityNbt(itemStack)?.takeIf { + it.contains("Items", NbtElement.LIST_TYPE.toInt()) + }?.let { + val list = DefaultedList.ofSize(27, ItemStack.EMPTY) + Inventories.readNbt(it, list) + list + } ?: emptyList() + + fun SafeContext.getBestAvailableTool( + blockState: BlockState, + availableTools: Set = ItemUtils.tools, + ) = + availableTools.map { + it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) + }.filter { (item, speed) -> + speed > 1.0 && + item.isSuitableFor(blockState) && + selectSource { + hasItem(item) + }?.let { + it.available(item.select()) > 0 + } == true + }.maxByOrNull { + it.second + }?.first + + fun SafeContext.nextDisposable() = player.combined.firstOrNull { it.item in disposables } + +// fun SafeContext.nextDisposable() = TaskFlow.disposables.firstOrNull { +// val selection = selectStack { isItem(it) } +// (selectSource { matching(selection) }?.available(selection) ?: 0) > 0 +// } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt new file mode 100644 index 000000000..0b90fc9c9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt @@ -0,0 +1,15 @@ +package com.lambda.task.flow.building.material + +import net.minecraft.item.ItemStack +import net.minecraft.util.math.Box + +sealed class MaterialDestination { + data object MainHand : MaterialDestination() + data object OffHand : MaterialDestination() + data object Hotbar : MaterialDestination() + data class InventorySlot(val slot: Int) : MaterialDestination() + data object Inventory : MaterialDestination() + data class ShulkerBox(val shulkerStack: ItemStack) : MaterialDestination() + data object EnderChest : MaterialDestination() + data class Stash(val pos: Box) : MaterialDestination() +} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt new file mode 100644 index 000000000..9a9fa12e1 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt @@ -0,0 +1,90 @@ +package com.lambda.interaction.building.material + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.manager.MaterialSourceManager.playerSources +import com.lambda.interaction.building.manager.MaterialSourceManager.sources +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.interaction.building.material.source.sources.InventorySource +import com.lambda.interaction.building.material.source.sources.ShulkerBoxSource +import com.lambda.interaction.building.material.StackSelection.Companion.select +import net.minecraft.enchantment.Enchantment +import net.minecraft.item.Item +import net.minecraft.item.ItemStack + +class SourceSelection { + var selection: (MaterialSource) -> Boolean = { true } + + fun isSource(source: Source): (MaterialSource) -> Boolean { + return { it.source == source } + } + + fun hasItem(item: Item, count: Int = 1): (MaterialSource) -> Boolean { + return { source -> + source.available(item.select()) >= count + } + } + + fun hasEnchantment(enchantment: Enchantment, count: Int = 1): (MaterialSource) -> Boolean { + return { source -> + val a = StackSelection().hasEnchantment(enchantment, count) + source.stacks.any { a(it) } + } + } + + fun hasStack(stack: ItemStack, count: Int = 1): (MaterialSource) -> Boolean { + return { source -> + source.available(stack.select()) >= count + } + } + + fun matching(stackSelection: StackSelection, count: Int = 1): (MaterialSource) -> Boolean { + return { source -> + source.available(stackSelection) >= count + } + } + + infix fun ((MaterialSource) -> Boolean).and(other: (MaterialSource) -> Boolean): (MaterialSource) -> Boolean { + return { this(it) && other(it) } + } + + infix fun ((MaterialSource) -> Boolean).or(other: (MaterialSource) -> Boolean): (MaterialSource) -> Boolean { + return { this(it) || other(it) } + } + + fun ((MaterialSource) -> Boolean).not(): (MaterialSource) -> Boolean { + return { !this(it) } + } + + companion object { + fun SafeContext.selectSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): MaterialSource? { + return sources.filter(SourceSelection().block()).minOrNull() + } + + fun SafeContext.selectSourceStacks( + count: Int = StackSelection.DEFAULT_AMOUNT, + selection: StackSelection.() -> (ItemStack) -> Boolean, + ): MaterialSource? { + val stackSelection = StackSelection() + stackSelection.selector = stackSelection.selection() + stackSelection.count = count + return selectSource { matching(stackSelection) } + } + + fun SafeContext.selectPlayerSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): MaterialSource? { + return playerSources.filter(SourceSelection().block()).minOrNull() + } + + fun SafeContext.selectInventorySource(block: SourceSelection.() -> (MaterialSource) -> Boolean): InventorySource? { + return playerSources + .filterIsInstance() + .filter(SourceSelection().block()).minOrNull() + } + + fun SafeContext.selectShulkerSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): ShulkerBoxSource? { + return sources + .filterIsInstance() + .filter(SourceSelection().block()).minOrNull() + } + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt new file mode 100644 index 000000000..85fc4eb11 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt @@ -0,0 +1,195 @@ +package com.lambda.interaction.building.material + +import com.lambda.interaction.building.manager.MaterialSourceManager.getShulkerBoxContents +import com.lambda.util.BlockUtils.item +import net.minecraft.block.Block +import net.minecraft.enchantment.Enchantment +import net.minecraft.enchantment.EnchantmentHelper +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot + +/** + * [StackSelection] is a class that holds a predicate for matching [ItemStack]s. + */ +class StackSelection { + var selector: (ItemStack) -> Boolean = { true } + var count: Int = DEFAULT_AMOUNT + var inShulkerBox: Boolean = false + + var item: Item? = null + var itemClass: Class? = null + private var damage: Int? = null + var itemStack: ItemStack? = null + + val filter: (Slot) -> Boolean + get() = { slot -> + if (inShulkerBox) { + getShulkerBoxContents(slot.stack).any { selector(it) } + } else { + selector(slot.stack) + } + } + + val optimalStack: ItemStack? + get() = itemStack ?: item?.let { ItemStack(it, count) } + + /** + * returns a function that finds a shulker box to push matching items into. + */ + val findShulkerToPush = { stack: ItemStack -> + getShulkerBoxContents(stack).let { inventory -> + if (inventory.all { selector(it) || it.isEmpty }) { + val storableItems = inventory.sumOf { +// if (it.isEmpty) item.itemStackLimit else it.maxStackSize - it.count + if (it.isEmpty) item?.maxCount ?: 0 else 0 + } + + if (storableItems > 0) storableItems else null + } else { + null + } + } + } + + /** + * returns a function that finds a shulker box to pull matching items from. + */ + val findShulkerToPull = { slot: Slot -> + getShulkerBoxContents(slot.stack).let { inventory -> + val usableItems = inventory.sumOf { if (selector(it)) it.count else 0 } + + if (usableItems > 0) slot to usableItems else null + } + } + + /** + * [isItem] returns a predicate that matches a specific [Item]. + * @param item The [Item] to be matched. + * @return A predicate that matches the [Item]. + */ + fun isItem(item: Item): (ItemStack) -> Boolean { + this.item = item + return { it.item == item } + } + + /** + * [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]. + */ + inline fun isItem(): (ItemStack) -> Boolean { + itemClass = T::class.java + return { it.item is T } + } + + /** + * [isBlock] returns a predicate that matches a specific [Block]. + * @param block The [Block] to be matched. + * @return A predicate that matches the [Block]. + */ + fun isBlock(block: Block): (ItemStack) -> Boolean { + item = block.item + return { it.item == block.item } + } + + /** + * [isItemStack] returns a predicate that matches a specific [ItemStack]. + * @param stack The [ItemStack] to be matched. + * @return A predicate that matches the [ItemStack]. + */ + fun isItemStack(stack: ItemStack): (ItemStack) -> Boolean { + this.itemStack = stack + return { ItemStack.areEqual(it, stack) } + } + + /** + * [hasDamage] returns a predicate that matches a specific damage value. + * @param damage The damage value to be matched. + * @return A predicate that matches the damage value. + */ + fun hasDamage(damage: Int): (ItemStack) -> Boolean { + this.damage = damage + return { it.damage == damage } + } + + /** + * [hasEnchantment] returns a predicate that matches a specific [Enchantment] and level. + * @param enchantment The [Enchantment] to be matched. + * @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`. + */ + fun hasEnchantment(enchantment: Enchantment, level: Int = -1): (ItemStack) -> Boolean = { + if (level < 0) { + EnchantmentHelper.getLevel(enchantment, it) > 0 + } else { + EnchantmentHelper.getLevel(enchantment, it) == level + } + } + + /** + * Returns the negation of the original predicate. + * @return A new predicate that matches if the original predicate does not match. + */ + fun ((ItemStack) -> Boolean).not(): (ItemStack) -> Boolean { + return { !this(it) } + } + + /** + * Combines two predicates using the logical AND operator. + * @param otherPredicate The second predicate. + * @return A new predicate that matches if both inputs predicate match. + */ + infix fun ((ItemStack) -> Boolean).and(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { + return { this(it) && otherPredicate(it) } + } + + /** + * Combines two predicates using the logical OR operator. + * @param otherPredicate The second predicate. + * @return A new predicate that matches if either input predicate matches. + */ + infix fun ((ItemStack) -> Boolean).or(otherPredicate: (ItemStack) -> Boolean): (ItemStack) -> Boolean { + return { this(it) || otherPredicate(it) } + } + + override fun toString() = StringBuilder().apply { + append("selection of ${count}x ") + item?.let { append(it.name.string) } + itemClass?.let { append(it.simpleName) } + itemStack?.let { append(it.name.string) } + damage?.let { append(" with damage $it") } + }.toString() + + companion object { + const val DEFAULT_AMOUNT = 1 + val FULL_SHULKERS: (ItemStack) -> Boolean = { stack -> + getShulkerBoxContents(stack).none { it.isEmpty } + } + val EMPTY_SHULKERS: (ItemStack) -> Boolean = { stack -> + getShulkerBoxContents(stack).all { it.isEmpty } + } + val EVERYTHING: (ItemStack) -> Boolean = { true } + + fun Item.select(): StackSelection = selectStack { isItem(this@select) } + fun ItemStack.select(): StackSelection = selectStack { isItemStack(this@select) } + + /** + * Builds a [StackSelection] with the given parameters. + * @param count The count of items to be selected. + * @param block The predicate to be used to select the items. + * @return A [StackSelection] with the given parameters. + */ + fun selectStack( + count: Int = DEFAULT_AMOUNT, + inShulkerBox: Boolean = false, + block: StackSelection.() -> (ItemStack) -> Boolean, + ): StackSelection { + val stackSelection = StackSelection() + stackSelection.selector = stackSelection.block() + stackSelection.count = count + stackSelection.inShulkerBox = inShulkerBox + return stackSelection + } + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt new file mode 100644 index 000000000..afd779a13 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt @@ -0,0 +1,20 @@ +package com.lambda.interaction.building.material.source + +import com.lambda.context.SafeContext +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack + +abstract class MaterialSource : Comparable { + abstract var source: Source + abstract suspend fun SafeContext.receive( + selection: StackSelection, + destination: MaterialDestination + ) + abstract fun available(selection: StackSelection): Int + open val stacks get() = emptyList() + + override fun compareTo(other: MaterialSource): Int { + return compareValuesBy(this, other) { it.source } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt new file mode 100644 index 000000000..2876fe1a0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt @@ -0,0 +1,69 @@ +package com.lambda.interaction.building.material.source + +import com.lambda.interaction.building.manager.MaterialSourceManager +import com.lambda.interaction.building.material.SourceSelection +import com.lambda.interaction.building.material.source.sources.* +import com.lambda.util.ItemUtils +import com.lambda.util.player.SlotUtils.combined +import com.lambda.util.player.SlotUtils.hotbar +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.item.ItemStack +import net.minecraft.util.StringIdentifiable + +enum class Source : StringIdentifiable { + CREATIVE { + override fun gather(player: ClientPlayerEntity) = setOf(CreativeSource) + }, + MAIN_HAND { + override fun gather(player: ClientPlayerEntity) = + setOf(MainHandSource(player.mainHandStack)) + listOf(player.mainHandStack).doShulkerCheck(MAIN_HAND) + }, + OFF_HAND { + override fun gather(player: ClientPlayerEntity) = + setOf(OffHandSource(player.offHandStack)) + listOf(player.offHandStack).doShulkerCheck(OFF_HAND) + }, + HANDS { + override fun gather(player: ClientPlayerEntity) = + MAIN_HAND.gather(player) + OFF_HAND.gather(player) + }, + HOTBAR { + override fun gather(player: ClientPlayerEntity) = + setOf(HotbarSource(player.hotbar)) + player.hotbar.doShulkerCheck(HOTBAR) + }, + INVENTORY { + override fun gather(player: ClientPlayerEntity) = + setOf(InventorySource(player.combined)) + player.combined.doShulkerCheck(INVENTORY) + }, + SHULKER_BOX { + override fun gather(player: ClientPlayerEntity) = emptySet() + }, + ENDER_CHEST { + override fun gather(player: ClientPlayerEntity) = emptySet() + }, + CHEST { + override fun gather(player: ClientPlayerEntity) = emptySet() + }, + STASH { + override fun gather(player: ClientPlayerEntity) = emptySet() + }, ; + + // ToDo: This is an experiment for commands + override fun asString() = name.lowercase() + + abstract fun gather(player: ClientPlayerEntity): Set + + fun select() = SourceSelection().apply { + selection = { it.source == this@Source } + } + + fun List.doShulkerCheck(source: Source) = + filter { + it.item in ItemUtils.shulkerBoxes + }.map { stack -> + ShulkerBoxSource( + MaterialSourceManager.getShulkerBoxContents(stack), + containedIn = source, + shulkerStack = stack, + ) + }.toSet() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt new file mode 100644 index 000000000..0586b5600 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt @@ -0,0 +1,21 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos + +data class ChestSource( + override val stacks: List, + val blockPos: BlockPos, +) : MaterialSource() { + override var source = Source.CHEST + + override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt new file mode 100644 index 000000000..5a40befe0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt @@ -0,0 +1,49 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack + +data object CreativeSource : MaterialSource() { + override var source = Source.CREATIVE + + override fun available(selection: StackSelection): Int = + if (selection.optimalStack != null) Int.MAX_VALUE else 0 + + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + when (destination) { + is MaterialDestination.MainHand -> { + if (ItemStack.areEqual(player.mainHandStack, selection.optimalStack)) { + return + } + + if (!player.isCreative) { + throw Exception("Cannot move from creative to main hand: not in creative mode") + } + + selection.optimalStack?.let { + createStack(it) + return + } + + throw Exception("Cannot move from creative to main hand: no optimal stack") + } + + else -> { + throw Exception("Cannot move from creative to $destination") + } + } + } + + private suspend fun SafeContext.createStack(stack: ItemStack) { + mc.executeSync { + interaction.clickCreativeStack(stack, 36 + player.inventory.selectedSlot) + } + delay(TaskFlow.itemMoveDelay) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt new file mode 100644 index 000000000..c8ac14d96 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt @@ -0,0 +1,19 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack + +data class EnderChestSource( + override val stacks: List, +) : MaterialSource() { + override var source = Source.ENDER_CHEST + + override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt new file mode 100644 index 000000000..34db87cce --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt @@ -0,0 +1,37 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack + +data class HotbarSource( + override val stacks: List, +) : MaterialSource() { + override var source = Source.HOTBAR + + override fun available(selection: StackSelection): Int = + stacks.filter(selection.selector).sumOf { it.count } + + override suspend fun SafeContext.receive( + selection: StackSelection, + destination: MaterialDestination + ) { + when (destination) { + is MaterialDestination.MainHand -> { + stacks.filter(selection.selector).forEach { stack -> + player.inventory.selectedSlot = player.inventory.main.indexOf(stack) + delay(TaskFlow.itemMoveDelay) + } + } + + else -> { + throw IllegalStateException("Cannot move from hotbar to $destination") + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt new file mode 100644 index 000000000..6db7ba3ea --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt @@ -0,0 +1,106 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.Lambda +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import com.lambda.util.player.SlotUtils.combined +import com.lambda.util.player.SlotUtils.hotbar +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +data class InventorySource( + override val stacks: List, +) : MaterialSource() { + override var source = Source.INVENTORY + + override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + when (destination) { + is MaterialDestination.MainHand -> { + stacks.filter(selection.selector).take(selection.count).forEach { stack -> + if (ItemStack.areEqual(stack, player.mainHandStack)) { + return@forEach + } + + if (ItemStack.areEqual(stack, player.offHandStack)) { + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN, + ), + ) + delay(TaskFlow.itemMoveDelay) + return@forEach + } + + if (stack in player.hotbar) { + player.inventory.selectedSlot = player.hotbar.indexOf(stack) + delay(TaskFlow.itemMoveDelay) + return@forEach + } + + interaction.pickFromInventory(player.combined.indexOf(stack)) + delay(TaskFlow.itemMoveDelay) + } + } + + is MaterialDestination.InventorySlot -> { + Lambda.LOG.info("Moving $selection from inventory to slot ${destination.slot}") + stacks.filter(selection.selector).take(selection.count).forEach { stack -> + player.currentScreenHandler?.let { screenHandler -> + if (screenHandler.stacks[destination.slot].item == stack.item) { + return@forEach + } + val currentStackSlot = screenHandler.stacks.indexOf(stack) + if (currentStackSlot == destination.slot) { + return@forEach + } + interaction.clickSlot( + player.currentScreenHandler?.syncId ?: 0, + currentStackSlot, + 0, + SlotActionType.PICKUP, + player, + ) + delay(TaskFlow.itemMoveDelay) + interaction.clickSlot( + player.currentScreenHandler?.syncId ?: 0, + destination.slot, + 0, + SlotActionType.PICKUP, + player, + ) + delay(TaskFlow.itemMoveDelay) + } + } + } + + is MaterialDestination.ShulkerBox -> { + Lambda.LOG.info("Pushing $selection from inventory to shulker box ${destination.shulkerStack.name.string}") + PlaceContainer(destination.shulkerStack).onSuccess { placePos -> + OpenContainer(placePos).onSuccess { + stacks.firstOrNull(selection.selector)?.let { +// interaction.clickSlot() + Lambda.LOG.info("Moving $it to shulker box") + } + }.execute() + }.execute() + } + + is MaterialDestination.Inventory -> {} + + else -> { + Lambda.LOG.warn("Cannot move from inventory to $destination") + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt new file mode 100644 index 000000000..88ba6a3d0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt @@ -0,0 +1,26 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack + +data class MainHandSource( + val stack: ItemStack, +) : MaterialSource() { + override var source = Source.MAIN_HAND + override val stacks: List + get() = listOf(stack) + + override fun available(selection: StackSelection) = + listOf(stack).filter(selection.selector).sumOf { it.count } + + override suspend fun SafeContext.receive( + selection: StackSelection, + destination: MaterialDestination + ) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt new file mode 100644 index 000000000..42f714bbd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt @@ -0,0 +1,27 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack + +data class OffHandSource( + val stack: ItemStack, +) : MaterialSource() { + override var source = Source.OFF_HAND + + override val stacks: List + get() = listOf(stack) + + override fun available(selection: StackSelection): Int = + listOf(stack).filter(selection.selector).sumOf { it.count } + + override suspend fun SafeContext.receive( + selection: StackSelection, + destination: MaterialDestination + ) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt new file mode 100644 index 000000000..7c6779121 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt @@ -0,0 +1,53 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.Lambda +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.interaction.building.verify.TargetState +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.SlotActionType + +data class ShulkerBoxSource( + override val stacks: List, + val containedIn: Source, + val shulkerStack: ItemStack, +) : MaterialSource() { + override var source = Source.SHULKER_BOX + + override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + when (destination) { + is MaterialDestination.MainHand, MaterialDestination.Inventory -> { + Lambda.LOG.info("Pulling from ${shulkerStack.name.string} to main hand any stack that matches $selection") + PlaceContainer(shulkerStack).onSuccess { placePos -> + OpenContainer(placePos, waitForSlotLoad = true).onSuccess { screenHandler -> + // ToDo: proper stack scope + screenHandler.stacks.filter(selection.selector).take(selection.count).forEach { + interaction.clickSlot( + screenHandler.syncId, + screenHandler.stacks.indexOf(it), + 0, + SlotActionType.QUICK_MOVE, + player, + ) + } + }.execute() + delay(TaskFlow.itemMoveDelay) + mc.executeSync { + player.closeHandledScreen() + } + BuildStructure(placePos.fromBlockPos(TargetState.Air), collectDrops = true).execute() + }.execute() + } + + else -> { + throw Exception("Cannot move from shulker box to $destination") + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt new file mode 100644 index 000000000..7cbea1013 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt @@ -0,0 +1,29 @@ +package com.lambda.interaction.building.material.source.sources + +import com.lambda.context.SafeContext +import com.lambda.interaction.building.material.source.MaterialSource +import com.lambda.interaction.building.material.source.Source +import com.lambda.task.flow.building.material.MaterialDestination +import com.lambda.interaction.building.material.StackSelection +import net.minecraft.item.ItemStack +import net.minecraft.util.math.Box + +data class StashSource( + val chests: Set, + val pos: Box, +) : MaterialSource() { + override var source = Source.STASH + + override val stacks: List + get() = chests.flatMap { it.stacks } + + override fun available(selection: StackSelection): Int = chests.sumOf { + with(it) { + available(selection) + } + } + + override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt new file mode 100644 index 000000000..314d7e339 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -0,0 +1,15 @@ +package com.lambda.module.modules.client + +import com.lambda.module.Module +import com.lambda.util.ItemUtils + +object TaskFlow : Module( + name = "TaskFlow", + description = "Settings for task automation" +) { + private val itemMoveDelaySetting = setting("Item Move Delay", 5, 0..20, unit = "ticks") + val disposables by setting("Disposables", ItemUtils.defaultDisposables) + + val itemMoveDelay: Long + get() = itemMoveDelaySetting.value * 50L +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt new file mode 100644 index 000000000..b95def705 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -0,0 +1,105 @@ +package com.lambda.util + +import com.lambda.util.ItemUtils.block +import com.lambda.util.ItemUtils.shulkerBoxes +import net.minecraft.block.Block +import net.minecraft.block.Blocks +import net.minecraft.fluid.Fluids +import net.minecraft.item.Item +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.EightWayDirection +import net.minecraft.util.math.Vec3d +import kotlin.math.floor + +object BlockUtils { + private val shulkerBlocks = shulkerBoxes.map { it.block } + + val interactionBlacklist = mutableSetOf( + Blocks.CHEST, + Blocks.TRAPPED_CHEST, + Blocks.ENDER_CHEST, + Blocks.BARREL, + Blocks.REPEATER, + Blocks.COMPARATOR, + Blocks.DISPENSER, + Blocks.DROPPER, + Blocks.HOPPER, + Blocks.BREWING_STAND, + Blocks.FURNACE, + Blocks.BLAST_FURNACE, + Blocks.SMOKER, + Blocks.CRAFTING_TABLE, + Blocks.ANVIL, + Blocks.CHIPPED_ANVIL, + Blocks.DAMAGED_ANVIL, + Blocks.ENCHANTING_TABLE, + Blocks.BEACON, + Blocks.BELL, + Blocks.CAMPFIRE, + Blocks.SOUL_CAMPFIRE, + Blocks.JUKEBOX, + Blocks.NOTE_BLOCK, + ).apply { addAll(shulkerBlocks) } + + val signs = setOf( + Blocks.OAK_SIGN, + Blocks.BIRCH_SIGN, + Blocks.ACACIA_SIGN, + Blocks.CHERRY_SIGN, + Blocks.JUNGLE_SIGN, + Blocks.DARK_OAK_SIGN, + Blocks.MANGROVE_SIGN, + Blocks.BAMBOO_SIGN, + ) + + val wallSigns = setOf( + Blocks.OAK_WALL_SIGN, + Blocks.BIRCH_WALL_SIGN, + Blocks.ACACIA_WALL_SIGN, + Blocks.CHERRY_WALL_SIGN, + Blocks.JUNGLE_WALL_SIGN, + Blocks.DARK_OAK_WALL_SIGN, + Blocks.MANGROVE_WALL_SIGN, + Blocks.BAMBOO_WALL_SIGN, + ) + + val hangingSigns = setOf( + Blocks.OAK_HANGING_SIGN, + Blocks.BIRCH_HANGING_SIGN, + Blocks.ACACIA_HANGING_SIGN, + Blocks.CHERRY_HANGING_SIGN, + Blocks.JUNGLE_HANGING_SIGN, + Blocks.DARK_OAK_HANGING_SIGN, + Blocks.MANGROVE_HANGING_SIGN, + Blocks.BAMBOO_HANGING_SIGN, + ) + + val hangingWallSigns = setOf( + Blocks.OAK_WALL_HANGING_SIGN, + Blocks.BIRCH_WALL_HANGING_SIGN, + Blocks.ACACIA_WALL_HANGING_SIGN, + Blocks.CHERRY_WALL_HANGING_SIGN, + Blocks.JUNGLE_WALL_HANGING_SIGN, + Blocks.DARK_OAK_WALL_HANGING_SIGN, + Blocks.MANGROVE_WALL_HANGING_SIGN, + Blocks.BAMBOO_WALL_HANGING_SIGN, + ) + + val fluids = listOf( + Fluids.LAVA, + Fluids.FLOWING_LAVA, + Fluids.WATER, + Fluids.FLOWING_WATER, + Fluids.EMPTY, + ) + + val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns + + val Block.item: Item get() = asItem() + val Vec3d.blockPos: BlockPos get() = BlockPos(floor(x).toInt(), floor(y).toInt(), floor(z).toInt()) + fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5)) + fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos = + add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount) + +} diff --git a/common/src/main/kotlin/com/lambda/util/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/ItemUtils.kt index e48c6383f..021bdb466 100644 --- a/common/src/main/kotlin/com/lambda/util/ItemUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/ItemUtils.kt @@ -54,6 +54,8 @@ object ItemUtils { Items.SHEARS, ) + val tools = pickaxes + shovels + axes + hoes + swords + misc + val shulkerBoxes = setOf( Items.SHULKER_BOX, Items.WHITE_SHULKER_BOX, @@ -81,7 +83,21 @@ object ItemUtils { Items.BARREL, ) - val tools = pickaxes + shovels + axes + hoes + swords + misc + val defaultDisposables = setOf( + Items.DIRT, + 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/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index 511b0f1a8..51eb03fac 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -8,6 +8,7 @@ import com.lambda.util.math.MathUtils.toRadian import net.minecraft.client.input.Input import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.Entity +import net.minecraft.util.math.EightWayDirection import net.minecraft.util.math.Vec3d import kotlin.math.cos import kotlin.math.hypot diff --git a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt new file mode 100644 index 000000000..a1902c5c8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -0,0 +1,30 @@ +package com.lambda.util.player + +import com.lambda.context.SafeContext +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.SlotActionType + +object SlotUtils { + val ClientPlayerEntity.hotbar: List get() = inventory.main.subList(0, 9) + val ClientPlayerEntity.storage: List get() = inventory.main.subList(9, 36) + val ClientPlayerEntity.hotbarAndStorage: List get() = inventory.main.subList(0, 36) + val ClientPlayerEntity.combined: List get() = inventory.main + inventory.armor + inventory.offHand + val ClientPlayerEntity.offhand: ItemStack get() = inventory.offHand.first() + + fun SafeContext.clickSlot( + slotId: Int, + button: Int, + actionType: SlotActionType, + ) { + val syncId = player.currentScreenHandler?.syncId ?: return + + interaction.clickSlot( + syncId, + slotId, + button, + actionType, + player, + ) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt index 3072ec978..38694df15 100644 --- a/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/EntityUtils.kt @@ -3,6 +3,7 @@ package com.lambda.util.world import com.lambda.context.SafeContext import net.minecraft.entity.Entity import net.minecraft.util.math.ChunkSectionPos +import net.minecraft.util.math.EightWayDirection import net.minecraft.util.math.Vec3d import kotlin.math.ceil @@ -64,4 +65,16 @@ object EntityUtils { return entities } + + fun Entity.direction(): EightWayDirection { + // Normalize the yaw to be within the range of -180 to 179 degrees + var normalizedYaw = (yaw + 180.0) % 360.0 + if (normalizedYaw < 0) { + normalizedYaw += 360.0 + } + + // Calculate the index of the closest direction + val directionIndex = ((normalizedYaw + 22.5) / 45.0).toInt() % 8 + return EightWayDirection.entries[directionIndex] + } } From d76d2cce2879fd58b567bf67c1971f8f2ff72d55 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 31 Mar 2024 08:02:08 +0200 Subject: [PATCH 03/47] Container ecosystem and new task builder DSL --- .../lambda/mixin/MinecraftClientMixin.java | 26 +++++ .../ClientPlayInteractionManagerMixin.java | 30 +++++ .../mixin/render/ScreenHandlerMixin.java | 21 ++++ .../src/main/kotlin/com/lambda/core/Loader.kt | 5 +- .../main/kotlin/com/lambda/event/EventFlow.kt | 35 ++++-- .../lambda/event/events/InteractionEvent.kt | 12 ++ .../com/lambda/event/events/RotationEvent.kt | 25 +++-- .../com/lambda/event/events/ScreenEvent.kt | 12 ++ .../lambda/event/events/ScreenHandlerEvent.kt | 18 +++ .../com/lambda/interaction/RotationManager.kt | 6 +- .../building/manager/MaterialSourceManager.kt | 106 ------------------ .../building/material/MaterialDestination.kt | 15 --- .../building/material/SourceSelection.kt | 90 --------------- .../material/source/MaterialSource.kt | 20 ---- .../building/material/source/Source.kt | 69 ------------ .../material/source/sources/ChestSource.kt | 21 ---- .../material/source/sources/CreativeSource.kt | 49 -------- .../source/sources/EnderChestSource.kt | 19 ---- .../material/source/sources/HotbarSource.kt | 37 ------ .../source/sources/InventorySource.kt | 106 ------------------ .../material/source/sources/MainHandSource.kt | 26 ----- .../material/source/sources/OffHandSource.kt | 27 ----- .../source/sources/ShulkerBoxSource.kt | 53 --------- .../material/source/sources/StashSource.kt | 29 ----- .../{building => construction}/Blueprint.kt | 10 +- .../verify/StateMatcher.kt | 2 +- .../verify/TargetState.kt | 22 ++-- .../interaction/material/ContainerManager.kt | 101 +++++++++++++++++ .../interaction/material/MaterialContainer.kt | 103 +++++++++++++++++ .../{building => }/material/StackSelection.kt | 63 +++++++---- .../material/container/ChestContainer.kt | 55 +++++++++ .../material/container/CreativeContainer.kt | 59 ++++++++++ .../material/container/EnderChestContainer.kt | 53 +++++++++ .../material/container/HotbarContainer.kt | 31 +++++ .../material/container/InventoryContainer.kt | 47 ++++++++ .../material/container/MainHandContainer.kt | 54 +++++++++ .../material/container/OffHandContainer.kt | 21 ++++ .../material/container/ShulkerBoxContainer.kt | 52 +++++++++ .../material/container/StashContainer.kt | 33 ++++++ .../material/transfer/TransferResult.kt | 11 ++ .../material/transfer/TransferStep.kt | 11 ++ .../interaction/processing/ProcessingStep.kt | 6 + .../interaction/processing/TaskPlanner.kt | 4 + .../visibilty/VisibilityChecker.kt | 4 +- .../com/lambda/module/modules/RotationTest.kt | 2 +- .../lambda/module/modules/client/TaskFlow.kt | 7 +- .../com/lambda/module/modules/player/Nuker.kt | 16 ++- .../kotlin/com/lambda/task/OptionalTask.kt | 13 +++ .../src/main/kotlin/com/lambda/task/Task.kt | 37 ++++-- .../kotlin/com/lambda/task/TaskBuilder.kt | 55 +++++++++ .../main/kotlin/com/lambda/task/TaskChain.kt | 19 ++++ .../com/lambda/task/TaskDecisionTree.kt | 5 + .../com/lambda/task/tasks/AcquireMaterial.kt | 30 +++++ .../com/lambda/task/tasks/BuildStructure.kt | 44 ++++++++ .../kotlin/com/lambda/task/tasks/GoalTask.kt | 32 ++++++ .../com/lambda/task/tasks/HelloWorldTask.kt | 2 +- .../com/lambda/task/tasks/InteractBlock.kt | 37 ++++++ .../com/lambda/task/tasks/InventoryTask.kt | 67 +++++++++++ .../com/lambda/task/tasks/LookAtBlock.kt | 56 +++++++++ .../com/lambda/task/tasks/OpenContainer.kt | 55 +++++++++ .../com/lambda/task/tasks/PlaceContainer.kt | 23 ++++ .../com/lambda/task/tasks/TaskTester.kt | 48 +++++--- .../kotlin/com/lambda/threading/Threading.kt | 42 +++++-- .../main/kotlin/com/lambda/util/BlockUtils.kt | 4 +- .../com/lambda/util/item/ItemStackUtils.kt | 62 ++++++++++ .../com/lambda/util/{ => item}/ItemUtils.kt | 10 +- .../util/primitives/extension/Screen.kt | 12 ++ .../src/main/resources/lambda.accesswidener | 1 + .../main/resources/lambda.mixins.common.json | 4 +- 69 files changed, 1503 insertions(+), 779 deletions(-) create mode 100644 common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java create mode 100644 common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java create mode 100644 common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt create mode 100644 common/src/main/kotlin/com/lambda/event/events/ScreenEvent.kt create mode 100644 common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt delete mode 100644 common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt rename common/src/main/kotlin/com/lambda/interaction/{building => construction}/Blueprint.kt (84%) rename common/src/main/kotlin/com/lambda/interaction/{building => construction}/verify/StateMatcher.kt (86%) rename common/src/main/kotlin/com/lambda/interaction/{building => construction}/verify/TargetState.kt (84%) create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt rename common/src/main/kotlin/com/lambda/interaction/{building => }/material/StackSelection.kt (80%) create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferStep.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/processing/ProcessingStep.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/processing/TaskPlanner.kt create mode 100644 common/src/main/kotlin/com/lambda/task/OptionalTask.kt create mode 100644 common/src/main/kotlin/com/lambda/task/TaskBuilder.kt create mode 100644 common/src/main/kotlin/com/lambda/task/TaskChain.kt create mode 100644 common/src/main/kotlin/com/lambda/task/TaskDecisionTree.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt create mode 100644 common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt rename common/src/main/kotlin/com/lambda/util/{ => item}/ItemUtils.kt (92%) create mode 100644 common/src/main/kotlin/com/lambda/util/primitives/extension/Screen.kt diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 903b77141..5c2452097 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -3,10 +3,16 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.ClientEvent; +import com.lambda.event.events.ScreenEvent; +import com.lambda.event.events.ScreenHandlerEvent; import com.lambda.event.events.TickEvent; import com.lambda.module.modules.player.Interact; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; import net.minecraft.client.network.ClientPlayerInteractionManager; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -38,6 +44,26 @@ private void onStartup(CallbackInfo ci) { EventFlow.post(new ClientEvent.Startup()); } + @Inject(method = "setScreen", at = @At("HEAD")) + private void onScreenOpen(@Nullable Screen screen, CallbackInfo ci) { + if (screen == null) return; + if (screen instanceof ScreenHandlerProvider handledScreen) { + EventFlow.post(new ScreenHandlerEvent.Open<>(handledScreen.getScreenHandler())); + } + + EventFlow.post(new ScreenEvent.Open<>(screen)); + } + + @Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;removed()V", shift = At.Shift.AFTER)) + private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { + if (screen == null) return; + if (screen instanceof ScreenHandlerProvider handledScreen) { + EventFlow.post(new ScreenHandlerEvent.Close<>(handledScreen.getScreenHandler())); + } + + EventFlow.post(new ScreenEvent.Close<>(screen)); + } + @Redirect(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;isBreakingBlock()Z")) boolean injectMultiActon(ClientPlayerInteractionManager instance) { if (instance == null) return true; diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java new file mode 100644 index 000000000..03da40258 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -0,0 +1,30 @@ +package com.lambda.mixin.entity; + +import com.lambda.event.EventFlow; +import com.lambda.event.events.InteractionEvent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ClientPlayerInteractionManager.class) +public class ClientPlayInteractionManagerMixin { + + @Final + @Shadow + private MinecraftClient client; + + @Inject(method = "interactBlock", at = @At("HEAD")) + public void interactBlockHead(final ClientPlayerEntity player, final Hand hand, final BlockHitResult hitResult, final CallbackInfoReturnable cir) { + if (client.world == null) return; + EventFlow.post(new InteractionEvent.Block(client.world, hitResult)); + } +} diff --git a/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java b/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java new file mode 100644 index 000000000..bde96fbf7 --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java @@ -0,0 +1,21 @@ +package com.lambda.mixin.render; + +import com.lambda.event.EventFlow; +import com.lambda.event.events.ScreenEvent; +import com.lambda.event.events.ScreenHandlerEvent; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(ScreenHandler.class) +public class ScreenHandlerMixin { + @Inject(method = "updateSlotStacks", at = @At("HEAD")) + private void onUpdateSlotStacksHead(int revision, List stacks, ItemStack cursorStack, CallbackInfo ci) { + EventFlow.post(new ScreenHandlerEvent.Loaded(revision, stacks, cursorStack)); + } +} diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index a30ecaef5..cef254b02 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -6,6 +6,7 @@ import com.lambda.command.CommandManager import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry +import com.lambda.task.tasks.TaskTester import com.lambda.util.Communication.ascii import kotlin.system.measureTimeMillis @@ -14,13 +15,15 @@ object Loader { ModuleRegistry, CommandManager, RotationManager, - PlayerPacketManager + PlayerPacketManager, ) fun initialize() { ascii.split("\n").forEach { LOG.info(it) } LOG.info("Initializing ${Lambda.MOD_NAME} ${Lambda.VERSION}") + TaskTester + val initTime = measureTimeMillis { loadables.forEach { loadable -> var info: String diff --git a/common/src/main/kotlin/com/lambda/event/EventFlow.kt b/common/src/main/kotlin/com/lambda/event/EventFlow.kt index 3c9a4b313..74a303cea 100644 --- a/common/src/main/kotlin/com/lambda/event/EventFlow.kt +++ b/common/src/main/kotlin/com/lambda/event/EventFlow.kt @@ -3,14 +3,9 @@ package com.lambda.event import com.lambda.event.callback.ICancellable import com.lambda.event.listener.Listener import com.lambda.threading.runConcurrent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.* import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.* /** @@ -31,7 +26,7 @@ object EventFlow { * useful when you have multiple independent [Job]s running in parallel. */ val lambdaScope = CoroutineScope(Dispatchers.Default + SupervisorJob()) - private val concurrentFlow = MutableSharedFlow( + val concurrentFlow = MutableSharedFlow( extraBufferCapacity = 1000, onBufferOverflow = BufferOverflow.DROP_OLDEST ) @@ -52,6 +47,30 @@ object EventFlow { } } + suspend inline fun awaitEvent( + noinline predicate: (E) -> Boolean = { true }, + ) = concurrentFlow.filterIsInstance().first(predicate) + + suspend inline fun awaitEvent( + timeout: Long, + noinline predicate: (E) -> Boolean = { true }, + ) = runBlocking { + withTimeout(timeout) { + concurrentFlow.filterIsInstance().first(predicate) + } + } + + suspend inline fun awaitEvents( + crossinline predicate: (E) -> Boolean = { true }, + ): Flow = flow { + concurrentFlow + .filterIsInstance() + .filter { predicate(it) } + .collect { + emit(it) + } + } + /** * Posts an [Event] to the event flow [concurrentFlow] and the synchronous [Listener]s. * diff --git a/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt b/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt new file mode 100644 index 000000000..8fce4adc7 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt @@ -0,0 +1,12 @@ +package com.lambda.event.events + +import com.lambda.event.Event +import net.minecraft.client.world.ClientWorld +import net.minecraft.util.hit.BlockHitResult + +sealed class InteractionEvent : Event { + class Block( + val world: ClientWorld, + val blockHitResult: BlockHitResult + ) : InteractionEvent() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index b86a3f960..1d856ba46 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -8,6 +8,7 @@ import com.lambda.interaction.RotationManager import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.rotation.RotationRequest import com.lambda.interaction.visibilty.VisibilityChecker.findRotation +import com.lambda.module.modules.client.TaskFlow import com.lambda.threading.runSafe import com.lambda.util.world.raycast.RayCastUtils.blockResult import com.lambda.util.world.raycast.RayCastUtils.entityResult @@ -44,22 +45,22 @@ abstract class RotationEvent : Event { } fun lookAtBlock( - rotationConfig: IRotationConfig, - interactionConfig: InteractionConfig, blockPos: BlockPos, + rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + interactionConfig: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet(), priority: Int = 0, - ) { - runSafe { - val state = world.getBlockState(blockPos) - val voxelShape = state.getOutlineShape(world, blockPos) - val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } - findRotation(rotationConfig, interactionConfig, boundingBoxes, priority, sides) { - blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) - }?.let { - requests.add(it) - } + ) = runSafe { + val state = world.getBlockState(blockPos) + val voxelShape = state.getOutlineShape(world, blockPos) + val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } + findRotation(rotationConfig, interactionConfig, boundingBoxes, priority, sides) { + blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) + }?.let { + requests.add(it) + return@runSafe it } + return@runSafe null } } diff --git a/common/src/main/kotlin/com/lambda/event/events/ScreenEvent.kt b/common/src/main/kotlin/com/lambda/event/events/ScreenEvent.kt new file mode 100644 index 000000000..fea72d4f9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/ScreenEvent.kt @@ -0,0 +1,12 @@ +package com.lambda.event.events + +import com.lambda.event.Event +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.item.ItemStack +import net.minecraft.screen.ScreenHandler + +sealed class ScreenEvent : Event { + class Open(val screen: T) : ScreenEvent() + class Close(val screen: T) : ScreenEvent() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt new file mode 100644 index 000000000..46d68fc9a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt @@ -0,0 +1,18 @@ +package com.lambda.event.events + +import com.lambda.event.Event +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider +import net.minecraft.item.ItemStack +import net.minecraft.screen.ScreenHandler + +sealed class ScreenHandlerEvent : Event { + class Open(val screen: T) : ScreenHandlerEvent() + class Close(val screen: T) : ScreenHandlerEvent() + data class Loaded( + val revision: Int, + val stacks: List, + val cursorStack: ItemStack, + ) : ScreenHandlerEvent() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index a1a900a92..82e7ea6ba 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -15,7 +15,7 @@ import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.rotation.RotationMode import com.lambda.interaction.rotation.RotationRequest import com.lambda.module.modules.client.Baritone -import com.lambda.threading.runOnGameThread +import com.lambda.threading.runOnGameThreadConcurrent import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.toRadian @@ -52,7 +52,7 @@ object RotationManager : Loadable { val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listener - runOnGameThread { + runOnGameThreadConcurrent { reset(Rotation(packet.yaw, packet.pitch)) } } @@ -117,7 +117,7 @@ object RotationManager : Loadable { } } - return@runSafe chosenRequest + return@runSafe currentRequest } private fun reset(rotation: Rotation) { diff --git a/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt b/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt deleted file mode 100644 index 9d3e41731..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/manager/MaterialSourceManager.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.lambda.interaction.building.manager - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.SourceSelection -import com.lambda.interaction.building.material.SourceSelection.Companion.selectSource -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.module.modules.client.TaskFlow.disposables -import com.lambda.interaction.building.material.StackSelection -import com.lambda.interaction.building.material.StackSelection.Companion.select -import com.lambda.util.ItemUtils -import com.lambda.util.player.SlotUtils.combined -import net.minecraft.block.BlockState -import net.minecraft.inventory.Inventories -import net.minecraft.item.BlockItem -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.nbt.NbtElement -import net.minecraft.util.collection.DefaultedList - -/** - * Caches all the item sources and destinations and gives a simple interface to check if an item is available and where. - * Sources are: - * 1. Player inventory - * 2. Shulker boxes - * 3. Ender chest - * 4. Stash - */ -object MaterialSourceManager { - - val cache: MutableSet = mutableSetOf() - - val SafeContext.enderChestContent: List - get() = emptyList() - - val SafeContext.stashContent: List - get() = emptyList() - - val SafeContext.playerSources: Set - get() { - if (player.isCreative) { - return Source.CREATIVE.gather(player) - } - - return Source.INVENTORY.gather(player) - } - - val SafeContext.enderChestSources: Set - get() = Source.ENDER_CHEST.gather(player) - - val SafeContext.stashSources: Set - get() = Source.STASH.gather(player) - - val SafeContext.sources: Set - get() = playerSources + enderChestSources + stashSources - - fun SafeContext.getAvailableCount(stackSelection: StackSelection): Int { - return sources.sumOf { it.available(stackSelection) } - } - - fun SafeContext.getSourcesWith(stackSelection: StackSelection): List { - return sources.filter { it.available(stackSelection) >= stackSelection.count } - } - - fun SafeContext.getAvailableStacks(selection: SourceSelection): List { - return sources.filter(selection.selection).flatMap { it.stacks } - } - - fun SafeContext.getGroupedAvailableStacks(): Map> { - return sources.associateWith { it.stacks } - } - - fun getShulkerBoxContents(itemStack: ItemStack) = - BlockItem.getBlockEntityNbt(itemStack)?.takeIf { - it.contains("Items", NbtElement.LIST_TYPE.toInt()) - }?.let { - val list = DefaultedList.ofSize(27, ItemStack.EMPTY) - Inventories.readNbt(it, list) - list - } ?: emptyList() - - fun SafeContext.getBestAvailableTool( - blockState: BlockState, - availableTools: Set = ItemUtils.tools, - ) = - availableTools.map { - it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) - }.filter { (item, speed) -> - speed > 1.0 && - item.isSuitableFor(blockState) && - selectSource { - hasItem(item) - }?.let { - it.available(item.select()) > 0 - } == true - }.maxByOrNull { - it.second - }?.first - - fun SafeContext.nextDisposable() = player.combined.firstOrNull { it.item in disposables } - -// fun SafeContext.nextDisposable() = TaskFlow.disposables.firstOrNull { -// val selection = selectStack { isItem(it) } -// (selectSource { matching(selection) }?.available(selection) ?: 0) > 0 -// } -} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt deleted file mode 100644 index 0b90fc9c9..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/MaterialDestination.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.lambda.task.flow.building.material - -import net.minecraft.item.ItemStack -import net.minecraft.util.math.Box - -sealed class MaterialDestination { - data object MainHand : MaterialDestination() - data object OffHand : MaterialDestination() - data object Hotbar : MaterialDestination() - data class InventorySlot(val slot: Int) : MaterialDestination() - data object Inventory : MaterialDestination() - data class ShulkerBox(val shulkerStack: ItemStack) : MaterialDestination() - data object EnderChest : MaterialDestination() - data class Stash(val pos: Box) : MaterialDestination() -} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt deleted file mode 100644 index 9a9fa12e1..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/SourceSelection.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.lambda.interaction.building.material - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.manager.MaterialSourceManager.playerSources -import com.lambda.interaction.building.manager.MaterialSourceManager.sources -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.interaction.building.material.source.sources.InventorySource -import com.lambda.interaction.building.material.source.sources.ShulkerBoxSource -import com.lambda.interaction.building.material.StackSelection.Companion.select -import net.minecraft.enchantment.Enchantment -import net.minecraft.item.Item -import net.minecraft.item.ItemStack - -class SourceSelection { - var selection: (MaterialSource) -> Boolean = { true } - - fun isSource(source: Source): (MaterialSource) -> Boolean { - return { it.source == source } - } - - fun hasItem(item: Item, count: Int = 1): (MaterialSource) -> Boolean { - return { source -> - source.available(item.select()) >= count - } - } - - fun hasEnchantment(enchantment: Enchantment, count: Int = 1): (MaterialSource) -> Boolean { - return { source -> - val a = StackSelection().hasEnchantment(enchantment, count) - source.stacks.any { a(it) } - } - } - - fun hasStack(stack: ItemStack, count: Int = 1): (MaterialSource) -> Boolean { - return { source -> - source.available(stack.select()) >= count - } - } - - fun matching(stackSelection: StackSelection, count: Int = 1): (MaterialSource) -> Boolean { - return { source -> - source.available(stackSelection) >= count - } - } - - infix fun ((MaterialSource) -> Boolean).and(other: (MaterialSource) -> Boolean): (MaterialSource) -> Boolean { - return { this(it) && other(it) } - } - - infix fun ((MaterialSource) -> Boolean).or(other: (MaterialSource) -> Boolean): (MaterialSource) -> Boolean { - return { this(it) || other(it) } - } - - fun ((MaterialSource) -> Boolean).not(): (MaterialSource) -> Boolean { - return { !this(it) } - } - - companion object { - fun SafeContext.selectSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): MaterialSource? { - return sources.filter(SourceSelection().block()).minOrNull() - } - - fun SafeContext.selectSourceStacks( - count: Int = StackSelection.DEFAULT_AMOUNT, - selection: StackSelection.() -> (ItemStack) -> Boolean, - ): MaterialSource? { - val stackSelection = StackSelection() - stackSelection.selector = stackSelection.selection() - stackSelection.count = count - return selectSource { matching(stackSelection) } - } - - fun SafeContext.selectPlayerSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): MaterialSource? { - return playerSources.filter(SourceSelection().block()).minOrNull() - } - - fun SafeContext.selectInventorySource(block: SourceSelection.() -> (MaterialSource) -> Boolean): InventorySource? { - return playerSources - .filterIsInstance() - .filter(SourceSelection().block()).minOrNull() - } - - fun SafeContext.selectShulkerSource(block: SourceSelection.() -> (MaterialSource) -> Boolean): ShulkerBoxSource? { - return sources - .filterIsInstance() - .filter(SourceSelection().block()).minOrNull() - } - } -} diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt deleted file mode 100644 index afd779a13..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/MaterialSource.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.lambda.interaction.building.material.source - -import com.lambda.context.SafeContext -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack - -abstract class MaterialSource : Comparable { - abstract var source: Source - abstract suspend fun SafeContext.receive( - selection: StackSelection, - destination: MaterialDestination - ) - abstract fun available(selection: StackSelection): Int - open val stacks get() = emptyList() - - override fun compareTo(other: MaterialSource): Int { - return compareValuesBy(this, other) { it.source } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt deleted file mode 100644 index 2876fe1a0..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/Source.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.lambda.interaction.building.material.source - -import com.lambda.interaction.building.manager.MaterialSourceManager -import com.lambda.interaction.building.material.SourceSelection -import com.lambda.interaction.building.material.source.sources.* -import com.lambda.util.ItemUtils -import com.lambda.util.player.SlotUtils.combined -import com.lambda.util.player.SlotUtils.hotbar -import net.minecraft.client.network.ClientPlayerEntity -import net.minecraft.item.ItemStack -import net.minecraft.util.StringIdentifiable - -enum class Source : StringIdentifiable { - CREATIVE { - override fun gather(player: ClientPlayerEntity) = setOf(CreativeSource) - }, - MAIN_HAND { - override fun gather(player: ClientPlayerEntity) = - setOf(MainHandSource(player.mainHandStack)) + listOf(player.mainHandStack).doShulkerCheck(MAIN_HAND) - }, - OFF_HAND { - override fun gather(player: ClientPlayerEntity) = - setOf(OffHandSource(player.offHandStack)) + listOf(player.offHandStack).doShulkerCheck(OFF_HAND) - }, - HANDS { - override fun gather(player: ClientPlayerEntity) = - MAIN_HAND.gather(player) + OFF_HAND.gather(player) - }, - HOTBAR { - override fun gather(player: ClientPlayerEntity) = - setOf(HotbarSource(player.hotbar)) + player.hotbar.doShulkerCheck(HOTBAR) - }, - INVENTORY { - override fun gather(player: ClientPlayerEntity) = - setOf(InventorySource(player.combined)) + player.combined.doShulkerCheck(INVENTORY) - }, - SHULKER_BOX { - override fun gather(player: ClientPlayerEntity) = emptySet() - }, - ENDER_CHEST { - override fun gather(player: ClientPlayerEntity) = emptySet() - }, - CHEST { - override fun gather(player: ClientPlayerEntity) = emptySet() - }, - STASH { - override fun gather(player: ClientPlayerEntity) = emptySet() - }, ; - - // ToDo: This is an experiment for commands - override fun asString() = name.lowercase() - - abstract fun gather(player: ClientPlayerEntity): Set - - fun select() = SourceSelection().apply { - selection = { it.source == this@Source } - } - - fun List.doShulkerCheck(source: Source) = - filter { - it.item in ItemUtils.shulkerBoxes - }.map { stack -> - ShulkerBoxSource( - MaterialSourceManager.getShulkerBoxContents(stack), - containedIn = source, - shulkerStack = stack, - ) - }.toSet() -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt deleted file mode 100644 index 0586b5600..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ChestSource.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack -import net.minecraft.util.math.BlockPos - -data class ChestSource( - override val stacks: List, - val blockPos: BlockPos, -) : MaterialSource() { - override var source = Source.CHEST - - override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt deleted file mode 100644 index 5a40befe0..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/CreativeSource.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import kotlinx.coroutines.delay -import net.minecraft.item.ItemStack - -data object CreativeSource : MaterialSource() { - override var source = Source.CREATIVE - - override fun available(selection: StackSelection): Int = - if (selection.optimalStack != null) Int.MAX_VALUE else 0 - - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - when (destination) { - is MaterialDestination.MainHand -> { - if (ItemStack.areEqual(player.mainHandStack, selection.optimalStack)) { - return - } - - if (!player.isCreative) { - throw Exception("Cannot move from creative to main hand: not in creative mode") - } - - selection.optimalStack?.let { - createStack(it) - return - } - - throw Exception("Cannot move from creative to main hand: no optimal stack") - } - - else -> { - throw Exception("Cannot move from creative to $destination") - } - } - } - - private suspend fun SafeContext.createStack(stack: ItemStack) { - mc.executeSync { - interaction.clickCreativeStack(stack, 36 + player.inventory.selectedSlot) - } - delay(TaskFlow.itemMoveDelay) - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt deleted file mode 100644 index c8ac14d96..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/EnderChestSource.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack - -data class EnderChestSource( - override val stacks: List, -) : MaterialSource() { - override var source = Source.ENDER_CHEST - - override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt deleted file mode 100644 index 34db87cce..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/HotbarSource.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import kotlinx.coroutines.delay -import net.minecraft.item.ItemStack - -data class HotbarSource( - override val stacks: List, -) : MaterialSource() { - override var source = Source.HOTBAR - - override fun available(selection: StackSelection): Int = - stacks.filter(selection.selector).sumOf { it.count } - - override suspend fun SafeContext.receive( - selection: StackSelection, - destination: MaterialDestination - ) { - when (destination) { - is MaterialDestination.MainHand -> { - stacks.filter(selection.selector).forEach { stack -> - player.inventory.selectedSlot = player.inventory.main.indexOf(stack) - delay(TaskFlow.itemMoveDelay) - } - } - - else -> { - throw IllegalStateException("Cannot move from hotbar to $destination") - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt deleted file mode 100644 index 6db7ba3ea..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/InventorySource.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.Lambda -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import com.lambda.util.player.SlotUtils.combined -import com.lambda.util.player.SlotUtils.hotbar -import kotlinx.coroutines.delay -import net.minecraft.item.ItemStack -import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket -import net.minecraft.screen.slot.SlotActionType -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction - -data class InventorySource( - override val stacks: List, -) : MaterialSource() { - override var source = Source.INVENTORY - - override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - when (destination) { - is MaterialDestination.MainHand -> { - stacks.filter(selection.selector).take(selection.count).forEach { stack -> - if (ItemStack.areEqual(stack, player.mainHandStack)) { - return@forEach - } - - if (ItemStack.areEqual(stack, player.offHandStack)) { - connection.sendPacket( - PlayerActionC2SPacket( - PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, - BlockPos.ORIGIN, - Direction.DOWN, - ), - ) - delay(TaskFlow.itemMoveDelay) - return@forEach - } - - if (stack in player.hotbar) { - player.inventory.selectedSlot = player.hotbar.indexOf(stack) - delay(TaskFlow.itemMoveDelay) - return@forEach - } - - interaction.pickFromInventory(player.combined.indexOf(stack)) - delay(TaskFlow.itemMoveDelay) - } - } - - is MaterialDestination.InventorySlot -> { - Lambda.LOG.info("Moving $selection from inventory to slot ${destination.slot}") - stacks.filter(selection.selector).take(selection.count).forEach { stack -> - player.currentScreenHandler?.let { screenHandler -> - if (screenHandler.stacks[destination.slot].item == stack.item) { - return@forEach - } - val currentStackSlot = screenHandler.stacks.indexOf(stack) - if (currentStackSlot == destination.slot) { - return@forEach - } - interaction.clickSlot( - player.currentScreenHandler?.syncId ?: 0, - currentStackSlot, - 0, - SlotActionType.PICKUP, - player, - ) - delay(TaskFlow.itemMoveDelay) - interaction.clickSlot( - player.currentScreenHandler?.syncId ?: 0, - destination.slot, - 0, - SlotActionType.PICKUP, - player, - ) - delay(TaskFlow.itemMoveDelay) - } - } - } - - is MaterialDestination.ShulkerBox -> { - Lambda.LOG.info("Pushing $selection from inventory to shulker box ${destination.shulkerStack.name.string}") - PlaceContainer(destination.shulkerStack).onSuccess { placePos -> - OpenContainer(placePos).onSuccess { - stacks.firstOrNull(selection.selector)?.let { -// interaction.clickSlot() - Lambda.LOG.info("Moving $it to shulker box") - } - }.execute() - }.execute() - } - - is MaterialDestination.Inventory -> {} - - else -> { - Lambda.LOG.warn("Cannot move from inventory to $destination") - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt deleted file mode 100644 index 88ba6a3d0..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/MainHandSource.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack - -data class MainHandSource( - val stack: ItemStack, -) : MaterialSource() { - override var source = Source.MAIN_HAND - override val stacks: List - get() = listOf(stack) - - override fun available(selection: StackSelection) = - listOf(stack).filter(selection.selector).sumOf { it.count } - - override suspend fun SafeContext.receive( - selection: StackSelection, - destination: MaterialDestination - ) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt deleted file mode 100644 index 42f714bbd..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/OffHandSource.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack - -data class OffHandSource( - val stack: ItemStack, -) : MaterialSource() { - override var source = Source.OFF_HAND - - override val stacks: List - get() = listOf(stack) - - override fun available(selection: StackSelection): Int = - listOf(stack).filter(selection.selector).sumOf { it.count } - - override suspend fun SafeContext.receive( - selection: StackSelection, - destination: MaterialDestination - ) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt deleted file mode 100644 index 7c6779121..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/ShulkerBoxSource.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.Lambda -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.interaction.building.verify.TargetState -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import kotlinx.coroutines.delay -import net.minecraft.item.ItemStack -import net.minecraft.screen.slot.SlotActionType - -data class ShulkerBoxSource( - override val stacks: List, - val containedIn: Source, - val shulkerStack: ItemStack, -) : MaterialSource() { - override var source = Source.SHULKER_BOX - - override fun available(selection: StackSelection): Int = stacks.filter(selection.selector).sumOf { it.count } - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - when (destination) { - is MaterialDestination.MainHand, MaterialDestination.Inventory -> { - Lambda.LOG.info("Pulling from ${shulkerStack.name.string} to main hand any stack that matches $selection") - PlaceContainer(shulkerStack).onSuccess { placePos -> - OpenContainer(placePos, waitForSlotLoad = true).onSuccess { screenHandler -> - // ToDo: proper stack scope - screenHandler.stacks.filter(selection.selector).take(selection.count).forEach { - interaction.clickSlot( - screenHandler.syncId, - screenHandler.stacks.indexOf(it), - 0, - SlotActionType.QUICK_MOVE, - player, - ) - } - }.execute() - delay(TaskFlow.itemMoveDelay) - mc.executeSync { - player.closeHandledScreen() - } - BuildStructure(placePos.fromBlockPos(TargetState.Air), collectDrops = true).execute() - }.execute() - } - - else -> { - throw Exception("Cannot move from shulker box to $destination") - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt b/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt deleted file mode 100644 index 7cbea1013..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/source/sources/StashSource.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.lambda.interaction.building.material.source.sources - -import com.lambda.context.SafeContext -import com.lambda.interaction.building.material.source.MaterialSource -import com.lambda.interaction.building.material.source.Source -import com.lambda.task.flow.building.material.MaterialDestination -import com.lambda.interaction.building.material.StackSelection -import net.minecraft.item.ItemStack -import net.minecraft.util.math.Box - -data class StashSource( - val chests: Set, - val pos: Box, -) : MaterialSource() { - override var source = Source.STASH - - override val stacks: List - get() = chests.flatMap { it.stacks } - - override fun available(selection: StackSelection): Int = chests.sumOf { - with(it) { - available(selection) - } - } - - override suspend fun SafeContext.receive(selection: StackSelection, destination: MaterialDestination) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt index 2ded4ff02..1c7e609ee 100644 --- a/common/src/main/kotlin/com/lambda/interaction/building/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt @@ -1,6 +1,6 @@ -package com.lambda.interaction.building +package com.lambda.interaction.construction -import com.lambda.interaction.building.verify.TargetState +import com.lambda.interaction.construction.verify.TargetState import net.minecraft.structure.StructureTemplate import net.minecraft.util.math.BlockBox import net.minecraft.util.math.BlockPos @@ -28,13 +28,13 @@ data class Blueprint( } companion object { - fun Box.fromBox(targetState: TargetState) = + fun Box.from(targetState: TargetState) = Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) - fun BlockBox.fromBlockBox(targetState: TargetState) = + fun BlockBox.from(targetState: TargetState) = Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) - fun BlockPos.fromBlockPos(targetState: TargetState) = + fun BlockPos.from(targetState: TargetState) = Blueprint(setOf(this).associateWith { targetState }) // fun Schematic.fromSchematic() = diff --git a/common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt similarity index 86% rename from common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt index e4d990d77..ce05f9fe8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/building/verify/StateMatcher.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt @@ -1,4 +1,4 @@ -package com.lambda.interaction.building.verify +package com.lambda.interaction.construction.verify import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld diff --git a/common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt similarity index 84% rename from common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt rename to common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index b5c3c41cf..0414cd1a8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/building/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -1,6 +1,6 @@ -package com.lambda.interaction.building.verify +package com.lambda.interaction.construction.verify -import com.lambda.util.ItemUtils.block +import com.lambda.util.item.ItemUtils.block import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld import net.minecraft.item.ItemStack @@ -10,19 +10,23 @@ import net.minecraft.util.math.Direction sealed class TargetState : StateMatcher { data object Air : TargetState() { - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.isAir - override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = ItemStack.EMPTY + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + state.isAir + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = + ItemStack.EMPTY } data object Solid : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.isSolidBlock(world, pos) - override fun getStack(world: ClientWorld, pos: BlockPos) = ItemStack(Items.NETHERRACK) + override fun getStack(world: ClientWorld, pos: BlockPos) = + ItemStack(Items.NETHERRACK) } data class Support(val direction: Direction) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = world.getBlockState(pos.offset(direction)).isSolidBlock(world, pos.offset(direction)) - override fun getStack(world: ClientWorld, pos: BlockPos) = ItemStack(Items.NETHERRACK) + override fun getStack(world: ClientWorld, pos: BlockPos) = + ItemStack(Items.NETHERRACK) } data class State(val blockState: BlockState) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = @@ -31,7 +35,8 @@ sealed class TargetState : StateMatcher { blockState.block.getPickStack(world, pos, blockState) } data class Block(val block: net.minecraft.block.Block) : TargetState() { - override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.block == block + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = + state.block == block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = block.getPickStack(world, pos, block.defaultState) } @@ -39,6 +44,7 @@ sealed class TargetState : StateMatcher { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.block == itemStack.item.block - override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = itemStack + override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = + itemStack } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt new file mode 100644 index 000000000..37ce4c4bd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -0,0 +1,101 @@ +package com.lambda.interaction.material + +import com.lambda.context.SafeContext +import com.lambda.event.events.InteractionEvent +import com.lambda.event.events.ScreenHandlerEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.* +import com.lambda.module.modules.client.TaskFlow +import com.lambda.util.Communication.info +import com.lambda.util.item.ItemUtils +import com.lambda.util.player.SlotUtils.combined +import com.lambda.util.primitives.extension.containerStacks +import net.minecraft.block.BlockState +import net.minecraft.block.entity.BlockEntity +import net.minecraft.block.entity.ChestBlockEntity +import net.minecraft.block.entity.EnderChestBlockEntity +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.screen.GenericContainerScreenHandler +import net.minecraft.screen.ScreenHandlerType +import java.util.TreeSet + +// ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. +object ContainerManager { + // ToDo: Maybe use reflection to get all containers? + val container = TreeSet().apply { + add(CreativeContainer) + add(EnderChestContainer) + add(HotbarContainer) + add(InventoryContainer) + add(MainHandContainer) + add(OffHandContainer) + } + + private var lastInteractedBlockEntity: BlockEntity? = null + + init { + listener { + lastInteractedBlockEntity = world.getBlockEntity(it.blockHitResult.blockPos) + } + + listener> { event -> + val handler = event.screen + + when (val block = lastInteractedBlockEntity) { + is EnderChestBlockEntity -> { + if (handler.type != ScreenHandlerType.GENERIC_9X3) return@listener + + this@ContainerManager.info("Updating EnderChestContainer") + EnderChestContainer.update(handler.containerStacks) + } + is ChestBlockEntity -> { + // ToDo: Handle double chests and single chests + if (handler.type != ScreenHandlerType.GENERIC_9X6) return@listener + val stacks = handler.containerStacks + + this@ContainerManager.info("Updating ChestContainer") + container + .filterIsInstance() + .find { + it.blockPos == block.pos + }?.update(stacks) ?: container.add(ChestContainer(stacks, block.pos)) + } + } + lastInteractedBlockEntity = null + } + } + + fun findContainer( + block: (MaterialContainer) -> Boolean + ): MaterialContainer? = container.find(block) + + fun findContainerWithSelection( + selection: StackSelection + ): MaterialContainer? = + container.find { it.available(selection) >= selection.count } + + fun findContainerWithStacks( + count: Int = StackSelection.DEFAULT_AMOUNT, + selection: (ItemStack) -> Boolean, + ): MaterialContainer? = + findContainerWithSelection(selection.select()) + + fun findBestAvailableTool( + blockState: BlockState, + availableTools: Set = ItemUtils.tools, + ) = availableTools.map { + it to it.getMiningSpeedMultiplier(it.defaultStack, blockState) + }.filter { (item, speed) -> + speed > 1.0 + && item.isSuitableFor(blockState) + && findContainerWithSelection(item.select()) != null + }.maxByOrNull { + it.second + }?.first + +// fun SafeContext.nextDisposable() = player.combined.firstOrNull { it.item in TaskFlow.disposables } + + class NoContainerFound(selection: StackSelection): Exception("No container found matching $selection") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt new file mode 100644 index 000000000..6858c8c14 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -0,0 +1,103 @@ +package com.lambda.interaction.material + +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.ShulkerBoxContainer +import com.lambda.interaction.material.transfer.TransferResult +import com.lambda.task.TaskChain +import com.lambda.task.emptyChain +import com.lambda.util.item.ItemStackUtils.count +import com.lambda.util.item.ItemStackUtils.empty +import com.lambda.util.item.ItemStackUtils.shulkerBoxContents +import com.lambda.util.item.ItemStackUtils.spaceLeft +import com.lambda.util.item.ItemUtils +import com.lambda.util.primitives.extension.containerSlots +import net.minecraft.client.gui.screen.ingame.HandledScreen +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot +import java.util.concurrent.ConcurrentSkipListSet + +// ToDo: Make jsonable to persistently store them +abstract class MaterialContainer( + private val rank: Rank +) : Comparable { + abstract var stacks: List + + fun update(stacks: List) { + this.stacks = stacks + } + + /** + * Brings the player into a withdrawal/deposit state. E.g.: move to a chest etc. + */ + open fun prepare(): TaskChain = emptyChain() + + /** + * Withdraws items from the container to the player's inventory. + */ + abstract fun withdraw(selection: StackSelection): TaskChain + + /** + * Deposits items from the player's inventory into the container. + */ + abstract fun deposit(selection: StackSelection): TaskChain + + open fun filter(selection: StackSelection) = + selection.filterStacks(stacks) + + open fun filter(selection: (ItemStack) -> Boolean) = + filter(selection.select()) + + open fun available(selection: StackSelection) = + filter(selection).count + + open fun spaceLeft(selection: StackSelection) = + filter(selection).spaceLeft + stacks.empty * selection.stackSize + + fun StackSelection.transfer(destination: MaterialContainer): TransferResult { + val amount = available(this) + if (amount < count) { + return TransferResult.MissingItems(amount - count) + } + + val space = destination.spaceLeft(this) + if (space == 0) { + return TransferResult.NoSpace + } + + val transferAmount = minOf(amount, space) + selector = { true } + count = transferAmount + val chain = prepare() + withdraw(this) + destination.prepare() + destination.deposit(this) + + return TransferResult.Success(chain) + } + + fun List.doShulkerCheck() = + filter { + it.item in ItemUtils.shulkerBoxes + }.map { stack -> + ShulkerBoxContainer( + stack.shulkerBoxContents, + containedIn = this@MaterialContainer, + shulkerStack = stack, + ) + }.toSet() + + enum class Rank { + CREATIVE, + MAIN_HAND, + OFF_HAND, + HOTBAR, + INVENTORY, + SHULKER_BOX, + ENDER_CHEST, + CHEST, + STASH + } + + override fun compareTo(other: MaterialContainer) = + compareBy { + it.rank + }.compare(this, other) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt similarity index 80% rename from common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt rename to common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index 85fc4eb11..afbfd6269 100644 --- a/common/src/main/kotlin/com/lambda/interaction/building/material/StackSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -1,13 +1,14 @@ -package com.lambda.interaction.building.material +package com.lambda.interaction.material -import com.lambda.interaction.building.manager.MaterialSourceManager.getShulkerBoxContents import com.lambda.util.BlockUtils.item +import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import net.minecraft.block.Block import net.minecraft.enchantment.Enchantment import net.minecraft.enchantment.EnchantmentHelper import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot +import kotlin.reflect.KClass /** * [StackSelection] is a class that holds a predicate for matching [ItemStack]s. @@ -18,27 +19,46 @@ class StackSelection { var inShulkerBox: Boolean = false var item: Item? = null - var itemClass: Class? = null + var itemClass: KClass? = null private var damage: Int? = null var itemStack: ItemStack? = null + val stackSize: Int + get() = optimalStack?.maxCount ?: 64 - val filter: (Slot) -> Boolean - get() = { slot -> + val optimalStack: ItemStack? + get() = itemStack ?: item?.let { ItemStack(it, count) } + + val filterStack: (ItemStack) -> Boolean + get() = { stack -> if (inShulkerBox) { - getShulkerBoxContents(slot.stack).any { selector(it) } + stack.shulkerBoxContents.any { selector(it) } } else { - selector(slot.stack) + selector(stack) } } - val optimalStack: ItemStack? - get() = itemStack ?: item?.let { ItemStack(it, count) } + val filterSlot: (Slot) -> Boolean + get() = { slot -> + filterStack(slot.stack) + } + + val filterStacks: (List) -> List + get() = { stacks -> + // ToDo: count should represent item count not stack count. + // Would need partial selections with permutations + stacks.filter(filterStack).take(count) + } + + val filterSlots: (List) -> List + get() = { slots -> + slots.filter { filterSlot(it) } + } /** * returns a function that finds a shulker box to push matching items into. */ val findShulkerToPush = { stack: ItemStack -> - getShulkerBoxContents(stack).let { inventory -> + stack.shulkerBoxContents.let { inventory -> if (inventory.all { selector(it) || it.isEmpty }) { val storableItems = inventory.sumOf { // if (it.isEmpty) item.itemStackLimit else it.maxStackSize - it.count @@ -56,7 +76,7 @@ class StackSelection { * returns a function that finds a shulker box to pull matching items from. */ val findShulkerToPull = { slot: Slot -> - getShulkerBoxContents(slot.stack).let { inventory -> + slot.stack.shulkerBoxContents.let { inventory -> val usableItems = inventory.sumOf { if (selector(it)) it.count else 0 } if (usableItems > 0) slot to usableItems else null @@ -79,7 +99,7 @@ class StackSelection { * @return A predicate that matches the [Item]. */ inline fun isItem(): (ItemStack) -> Boolean { - itemClass = T::class.java + itemClass = T::class return { it.item is T } } @@ -153,26 +173,27 @@ class StackSelection { return { this(it) || otherPredicate(it) } } - override fun toString() = StringBuilder().apply { + override fun toString() = buildString { append("selection of ${count}x ") item?.let { append(it.name.string) } itemClass?.let { append(it.simpleName) } itemStack?.let { append(it.name.string) } damage?.let { append(" with damage $it") } - }.toString() + } companion object { const val DEFAULT_AMOUNT = 1 val FULL_SHULKERS: (ItemStack) -> Boolean = { stack -> - getShulkerBoxContents(stack).none { it.isEmpty } + stack.shulkerBoxContents.none { it.isEmpty } } val EMPTY_SHULKERS: (ItemStack) -> Boolean = { stack -> - getShulkerBoxContents(stack).all { it.isEmpty } + stack.shulkerBoxContents.all { it.isEmpty } } val EVERYTHING: (ItemStack) -> Boolean = { true } fun Item.select(): StackSelection = selectStack { isItem(this@select) } fun ItemStack.select(): StackSelection = selectStack { isItemStack(this@select) } + fun ((ItemStack) -> Boolean).select() = selectStack { this@select } /** * Builds a [StackSelection] with the given parameters. @@ -184,12 +205,10 @@ class StackSelection { count: Int = DEFAULT_AMOUNT, inShulkerBox: Boolean = false, block: StackSelection.() -> (ItemStack) -> Boolean, - ): StackSelection { - val stackSelection = StackSelection() - stackSelection.selector = stackSelection.block() - stackSelection.count = count - stackSelection.inShulkerBox = inShulkerBox - return stackSelection + ) = StackSelection().apply { + selector = block() + this.count = count + this.inShulkerBox = inShulkerBox } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt new file mode 100644 index 000000000..8c04560b9 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -0,0 +1,55 @@ +package com.lambda.interaction.material.container + +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.buildChain +import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange +import com.lambda.task.tasks.InventoryTask.Companion.deposit +import com.lambda.task.tasks.InventoryTask.Companion.withdraw +import com.lambda.task.tasks.OpenContainer.Companion.openContainer +import com.lambda.threading.runGameBlocking +import com.lambda.util.Communication.info +import net.minecraft.block.ChestBlock +import net.minecraft.item.ItemStack +import net.minecraft.screen.GenericContainerScreenHandler +import net.minecraft.screen.ScreenHandler +import net.minecraft.util.math.BlockPos + +data class ChestContainer( + override var stacks: List, + val blockPos: BlockPos, +) : MaterialContainer(Rank.CHEST) { + override fun prepare() = buildChain { + moveIntoEntityRange(blockPos) + required { + runGameBlocking { + if (ChestBlock.isChestBlocked(world, blockPos)) { + throw ChestBlockedException() + } + } + } + } + + override fun withdraw(selection: StackSelection) = buildChain { + openContainer(blockPos) + .withMaxAttempts(3) + .withTimeout(1000L) + .onSuccess { screen -> + info("Withdrawing $selection from ${screen.type}") + withdraw(screen, selection) + } + } + + override fun deposit(selection: StackSelection) = buildChain { + openContainer(blockPos) + .withMaxAttempts(3) + .withTimeout(1000L) + .onSuccess { screen -> + info("Depositing $selection to ${screen.type}") + deposit(screen, selection) + } + } + + class ChestBlockedException: Exception("The chest is blocked by another block or a cat") + class UnexpectedScreen(screenHandler: ScreenHandler): Exception("Unexpected screen. Got ${screenHandler.type}") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt new file mode 100644 index 000000000..7d02a9a88 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt @@ -0,0 +1,59 @@ +package com.lambda.interaction.material.container + +import com.lambda.interaction.material.MaterialContainer +import com.lambda.module.modules.client.TaskFlow +import com.lambda.interaction.material.StackSelection +import com.lambda.task.buildTask +import com.lambda.threading.runGameBlocking +import com.lambda.util.item.ItemStackUtils.equal +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack + +data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { + override var stacks = emptyList() + + override fun available(selection: StackSelection): Int = + if (selection.optimalStack != null) Int.MAX_VALUE else 0 + + override fun deposit(selection: StackSelection) = buildTask { + runGameBlocking { + if (!player.isCreative) { + // ToDo: Maybe switch gamemode? + throw NotInCreativeModeException() + } + + interaction.clickCreativeStack( + ItemStack.EMPTY, + 36 + player.inventory.selectedSlot + ) + } + } + + // Withdraws items from the creative menu to the player's main hand + override fun withdraw(selection: StackSelection) = buildTask { + selection.optimalStack?.let { optimalStack -> + runGameBlocking { + if (player.mainHandStack.equal(optimalStack)) { + return@runGameBlocking + } + + if (!player.isCreative) { + // ToDo: Maybe switch gamemode? + throw NotInCreativeModeException() + } + + interaction.clickCreativeStack( + optimalStack, + 36 + player.inventory.selectedSlot + ) + } + delay(TaskFlow.itemMoveDelay) + return@buildTask + } + + throw NoOptimalStackException() + } + + class NotInCreativeModeException : IllegalStateException("Insufficient permission: not in creative mode") + class NoOptimalStackException : IllegalStateException("Cannot move item: no optimal stack") +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt new file mode 100644 index 000000000..fbb514ccb --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -0,0 +1,53 @@ +package com.lambda.interaction.material.container + +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.task.buildChain +import com.lambda.task.tasks.AcquireMaterial.Companion.acquireStack +import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange +import com.lambda.task.tasks.InventoryTask.Companion.withdraw +import com.lambda.task.tasks.OpenContainer.Companion.openContainer +import net.minecraft.block.Blocks +import net.minecraft.item.ItemStack +import net.minecraft.item.Items +import net.minecraft.screen.GenericContainerScreenHandler +import net.minecraft.util.math.BlockPos + +object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { + override var stacks = emptyList() + private var placePos: BlockPos? = null + + override fun prepare() = buildChain { + required { +// findBlock(Blocks.ENDER_CHEST).onSuccess { pos -> +// moveIntoEntityRange(pos) +// placePos = pos +// }.onFailure { +// acquireStack(Items.ENDER_CHEST.select()).onSuccess { stack -> +// placeContainer(stack).onSuccess { pos -> +// placePos = pos +// } +// } +// } + } + } + + override fun withdraw(selection: StackSelection) = buildChain { + placePos?.let { blockPos -> + openContainer(blockPos) + .onSuccess { screen -> + withdraw(screen, selection) + } + } + } + + override fun deposit(selection: StackSelection) = buildChain { + placePos?.let { + openContainer(it) + .onSuccess { screen -> + withdraw(screen, selection) + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt new file mode 100644 index 000000000..cd455f6b0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -0,0 +1,31 @@ +package com.lambda.interaction.material.container + +import com.lambda.Lambda.mc +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.TaskChain +import com.lambda.task.buildTask +import com.lambda.task.emptyChain +import com.lambda.util.player.SlotUtils.hotbar +import net.minecraft.item.ItemStack + +object HotbarContainer : MaterialContainer(Rank.HOTBAR) { + override var stacks: List + get() = mc.player?.hotbar ?: emptyList() + set(_) {} + + override fun prepare() = emptyChain() + + override fun withdraw(selection: StackSelection) = buildTask { +// gameThreadBlocking { +// stacks.filter(selection.selector).forEach { stack -> +// player.inventory.selectedSlot = player.inventory.main.indexOf(stack) +// delay(TaskFlow.itemMoveDelay) +// } +// } + } + + override fun deposit(selection: StackSelection): TaskChain { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt new file mode 100644 index 000000000..62783a980 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -0,0 +1,47 @@ +package com.lambda.interaction.material.container + +import com.lambda.Lambda.mc +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.emptyChain +import com.lambda.util.player.SlotUtils.combined +import net.minecraft.item.ItemStack + +object InventoryContainer : MaterialContainer(Rank.INVENTORY) { + override var stacks: List + get() = mc.player?.combined ?: emptyList() + set(_) {} + + override fun withdraw(selection: StackSelection) = emptyChain() + + override fun deposit(selection: StackSelection) = emptyChain() + +// Lambda.LOG.info("Moving $selection from inventory to slot ${destination.slot}") +// stacks.filter(selection.selector).take(selection.count).forEach { stack -> +// player.currentScreenHandler?.let { screenHandler -> +// if (screenHandler.stacks[destination.slot].item == stack.item) { +// return@forEach +// } +// val currentStackSlot = screenHandler.stacks.indexOf(stack) +// if (currentStackSlot == destination.slot) { +// return@forEach +// } +// interaction.clickSlot( +// player.currentScreenHandler?.syncId ?: 0, +// currentStackSlot, +// 0, +// SlotActionType.PICKUP, +// player, +// ) +// delay(TaskFlow.itemMoveDelay) +// interaction.clickSlot( +// player.currentScreenHandler?.syncId ?: 0, +// destination.slot, +// 0, +// SlotActionType.PICKUP, +// player, +// ) +// delay(TaskFlow.itemMoveDelay) +// } +// } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt new file mode 100644 index 000000000..105d69baf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -0,0 +1,54 @@ +package com.lambda.interaction.material.container + +import com.lambda.Lambda.mc +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.TaskChain +import com.lambda.task.buildChain +import com.lambda.util.player.SlotUtils.combined +import com.lambda.util.player.SlotUtils.hotbar +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { + override var stacks: List + get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() + set(_) {} + + override fun withdraw(selection: StackSelection) = buildChain { +// InventoryContainer.stacks.filter(selection.selector).take(selection.count).forEach { stack -> +// if (ItemStack.areEqual(stack, player.mainHandStack)) { +// return@forEach +// } +// +// if (ItemStack.areEqual(stack, player.offHandStack)) { +// connection.sendPacket( +// PlayerActionC2SPacket( +// PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, +// BlockPos.ORIGIN, +// Direction.DOWN, +// ), +// ) +// delay(TaskFlow.itemMoveDelay) +// return@forEach +// } +// +// if (stack in player.hotbar) { +// player.inventory.selectedSlot = player.hotbar.indexOf(stack) +// delay(TaskFlow.itemMoveDelay) +// return@forEach +// } +// +// interaction.pickFromInventory(player.combined.indexOf(stack)) +// delay(TaskFlow.itemMoveDelay) +// } + } + + override fun deposit(selection: StackSelection): TaskChain { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt new file mode 100644 index 000000000..2d0350109 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt @@ -0,0 +1,21 @@ +package com.lambda.interaction.material.container + +import com.lambda.Lambda.mc +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.TaskChain +import net.minecraft.item.ItemStack + +object OffHandContainer : MaterialContainer(Rank.OFF_HAND) { + override var stacks: List + get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList() + set(_) {} + + override fun withdraw(selection: StackSelection): TaskChain { + TODO("Not yet implemented") + } + + override fun deposit(selection: StackSelection): TaskChain { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt new file mode 100644 index 000000000..ce11c59da --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -0,0 +1,52 @@ +package com.lambda.interaction.material.container + +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.buildChain +import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock +import com.lambda.task.tasks.InventoryTask.Companion.withdraw +import com.lambda.task.tasks.OpenContainer.Companion.openContainer +import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer +import com.lambda.util.Communication.info +import net.minecraft.item.ItemStack +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.ShulkerBoxScreenHandler +import net.minecraft.util.math.BlockPos + +data class ShulkerBoxContainer( + override var stacks: List, + val containedIn: MaterialContainer, + val shulkerStack: ItemStack, +) : MaterialContainer(Rank.SHULKER_BOX) { + private var openScreen: ShulkerBoxScreenHandler? = null + private var placePosition: BlockPos? = null + + override fun prepare() = buildChain { + placeContainer(shulkerStack).onSuccess { placePos -> + placePosition = placePos + openContainer(placePos).onSuccess { screen -> + openScreen = screen + } + } + } + + override fun withdraw(selection: StackSelection) = buildChain { + openScreen?.let { screen -> + info("Withdrawing $selection from ${shulkerStack.name.string}") + withdraw(screen, selection) + } + placePosition?.let { + breakAndCollectBlock(it) + } + } + + override fun deposit(selection: StackSelection) = buildChain { + openScreen?.let { screen -> + info("Depositing $selection to shulker box ${shulkerStack.name.string}") + withdraw(screen, selection) + } + placePosition?.let { + breakAndCollectBlock(it) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt new file mode 100644 index 000000000..924cb195f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt @@ -0,0 +1,33 @@ +package com.lambda.interaction.material.container + +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.buildChain +import net.minecraft.item.ItemStack +import net.minecraft.util.math.Box + +data class StashContainer( + val chests: Set, + val pos: Box, +) : MaterialContainer(Rank.STASH) { + override var stacks: List + get() = chests.flatMap { it.stacks } + set(_) {} + + override fun prepare() = buildChain { + TODO("Not yet implemented") + } + + override fun withdraw(selection: StackSelection) = buildChain { + TODO("Not yet implemented") + } + + override fun deposit(selection: StackSelection) = buildChain { + TODO("Not yet implemented") + } + + override fun available(selection: StackSelection): Int = + chests.sumOf { + it.available(selection) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt new file mode 100644 index 000000000..4db95a9c3 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -0,0 +1,11 @@ +package com.lambda.interaction.material.transfer + +import com.lambda.task.TaskChain + +sealed class TransferResult { + data class Success( + val taskChain: TaskChain + ) : TransferResult() + data object NoSpace : TransferResult() // ToDo: Needs inventory space resolver. compressing or disposing + data class MissingItems(val missing: Int) : TransferResult() // ToDo: Find other satisfying permutations +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferStep.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferStep.kt new file mode 100644 index 000000000..b15e85586 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferStep.kt @@ -0,0 +1,11 @@ +package com.lambda.interaction.material.transfer + +import com.lambda.task.Task + +abstract class TransferStep : Task() { + + + companion object { + + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/processing/ProcessingStep.kt b/common/src/main/kotlin/com/lambda/interaction/processing/ProcessingStep.kt new file mode 100644 index 000000000..17b5c7ff4 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/processing/ProcessingStep.kt @@ -0,0 +1,6 @@ +package com.lambda.interaction.processing + +import com.lambda.task.Task + +abstract class ProcessingStep : Task() { +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/processing/TaskPlanner.kt b/common/src/main/kotlin/com/lambda/interaction/processing/TaskPlanner.kt new file mode 100644 index 000000000..53d1a687a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/processing/TaskPlanner.kt @@ -0,0 +1,4 @@ +package com.lambda.interaction.processing + +object TaskPlanner { +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index 076255428..6425b3bcb 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -111,7 +111,7 @@ object VisibilityChecker { } } - fun Box.bounds(side: Direction) = + private fun Box.bounds(side: Direction) = when (side) { Direction.DOWN -> doubleArrayOf(minX, minY, minZ, maxX, minY, maxZ) Direction.UP -> doubleArrayOf(minX, maxY, minZ, maxX, maxY, maxZ) @@ -121,7 +121,7 @@ object VisibilityChecker { Direction.EAST -> doubleArrayOf(maxX, minY, minZ, maxX, maxY, maxZ) } - fun SafeContext.getVisibleSides(box: Box): Set { + private fun SafeContext.getVisibleSides(box: Box): Set { val visibleSides = EnumSet.noneOf(Direction::class.java) val eyePos = player.eyePos diff --git a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt b/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt index 2efdabd38..885f82a51 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt @@ -33,7 +33,7 @@ object RotationTest : Module( // player.eyePos, interaction.reachDistance.toDouble() // ) ?: return@listener - event.lookAtBlock(rotationConfig, interactionConfig, pos, setOf(side)) + event.lookAtBlock(pos, rotationConfig, interactionConfig, setOf(side)) } listener { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index 314d7e339..5829f0b06 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -1,14 +1,17 @@ package com.lambda.module.modules.client +import com.lambda.config.InteractionSettings +import com.lambda.config.RotationSettings import com.lambda.module.Module -import com.lambda.util.ItemUtils object TaskFlow : Module( name = "TaskFlow", description = "Settings for task automation" ) { + val rotationSettings = RotationSettings(this) + val interactionSettings = InteractionSettings(this) private val itemMoveDelaySetting = setting("Item Move Delay", 5, 0..20, unit = "ticks") - val disposables by setting("Disposables", ItemUtils.defaultDisposables) +// val disposables by setting("Disposables", ItemUtils.defaultDisposables) val itemMoveDelay: Long get() = itemMoveDelaySetting.value * 50L diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 99efe65f8..af1bd7e93 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -1,8 +1,12 @@ package com.lambda.module.modules.player -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module +import com.lambda.task.buildChain +import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock +import com.lambda.threading.taskContext +import com.lambda.util.Communication.info +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.text.Text object Nuker : Module( name = "Nuker", @@ -11,8 +15,14 @@ object Nuker : Module( private val flatten by setting("Flatten", false) init { - listener { + onEnable { + info(player.mainHandStack.toHoverableText() ?: Text.empty()) + taskContext { + buildChain { + breakAndCollectBlock(mc.crosshairTarget?.blockResult?.blockPos ?: return@buildChain) + } + } } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/OptionalTask.kt b/common/src/main/kotlin/com/lambda/task/OptionalTask.kt new file mode 100644 index 000000000..6f2b93401 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/OptionalTask.kt @@ -0,0 +1,13 @@ +package com.lambda.task + +abstract class OptionalTask( + val predicate: () -> Boolean +) : Task() { + companion object { + fun (suspend () -> T).toOptionalTask( + predicate: () -> Boolean + ) = object : OptionalTask(predicate) { + override suspend fun onAction(): T = this@toOptionalTask() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index e36c59e5c..06f819ccd 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,7 +1,9 @@ package com.lambda.task +import baritone.api.BaritoneAPI import com.lambda.Lambda.LOG import com.lambda.context.SafeContext +import com.lambda.event.Event import com.lambda.event.EventFlow import com.lambda.event.Subscriber import com.lambda.threading.runSafe @@ -9,6 +11,10 @@ import com.lambda.util.Nameable import kotlinx.coroutines.CancellationException import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withTimeout /** @@ -45,7 +51,7 @@ abstract class Task( private var delay: Long = 0L, private var timeout: Long = Long.MAX_VALUE, private var maxAttempts: Int = 1, - private var repeats: Int = 1, + open var repeats: Int = 1, private var onSuccess: (suspend (Result) -> Unit) = {}, private var onRetry: (suspend Task.() -> Unit) = {}, private var onTimeout: (suspend Task.() -> Unit) = {}, @@ -59,6 +65,8 @@ abstract class Task( val syncListeners = Subscriber() private val concurrentListeners = Subscriber() + operator fun plus(other: Task<*>) = TaskChain(listOf(this, other)) + /** * Executes the main action of the task. * @@ -67,7 +75,7 @@ abstract class Task( * * @return The result of the action, of type [Result]. */ - abstract suspend fun SafeContext.onAction(): Result + abstract suspend fun onAction(): Result /** * This function is called when the task is canceled. @@ -100,15 +108,12 @@ abstract class Task( } startListening() + iteration++ try { withTimeout(timeout) { - runSafe { - onAction() - } + onAction() }?.let { result -> - iteration++ - if (iteration == repeats) { onSuccess(result) tidyUp() @@ -150,7 +155,7 @@ abstract class Task( EventFlow.syncListeners.unsubscribe(syncListeners) EventFlow.concurrentListeners.unsubscribe(concurrentListeners) - // ToDo: cancel baritone + BaritoneAPI.getProvider().primaryBaritone?.pathingBehavior?.cancelEverything() } private fun startListening() { @@ -170,6 +175,7 @@ abstract class Task( * @param delay The delay in milliseconds. * @return This task instance with the updated delay. */ + @Ta5kBuilder fun withDelay(delay: Long): Task { this.delay = delay return this @@ -181,6 +187,7 @@ abstract class Task( * @param timeout The timeout in milliseconds * @return This task instance with the updated timeout. */ + @Ta5kBuilder fun withTimeout(timeout: Long): Task { this.timeout = timeout return this @@ -192,6 +199,7 @@ abstract class Task( * @param maxAttempts The maximum number of attempts. * @return This task instance with the updated maximum attempts. */ + @Ta5kBuilder fun withMaxAttempts(maxAttempts: Int): Task { this.maxAttempts = maxAttempts return this @@ -203,6 +211,7 @@ abstract class Task( * @param repeats The number of repeats. * @return This task instance with the updated number of repeats. */ + @Ta5kBuilder fun withRepeats(repeats: Int): Task { this.repeats = repeats return this @@ -214,6 +223,7 @@ abstract class Task( * @param action The action to be performed. * @return The task instance with the updated success action. */ + @Ta5kBuilder fun onSuccess(action: suspend (Result) -> Unit): Task { this.onSuccess = action return this @@ -225,6 +235,7 @@ abstract class Task( * @param action The action to be performed. * @return The task instance with the updated retry action. */ + @Ta5kBuilder fun onRetry(action: suspend Task.() -> Unit): Task { this.onRetry = action return this @@ -236,6 +247,7 @@ abstract class Task( * @param action The action to be performed. * @return The task instance with the updated timeout action. */ + @Ta5kBuilder fun onTimeout(action: suspend Task.() -> Unit): Task { this.onTimeout = action return this @@ -247,6 +259,7 @@ abstract class Task( * @param action The action to be performed. * @return The task instance with the updated exception action. */ + @Ta5kBuilder fun onFailure(action: suspend Task.(Throwable) -> Unit): Task { this.onException = action return this @@ -258,8 +271,16 @@ abstract class Task( * @param action The action to be performed. * @return The task instance with the updated repeat action. */ + @Ta5kBuilder fun onRepeat(action: suspend Task.(Int) -> Unit): Task { this.onRepeat = action return this } + + companion object { + fun (suspend () -> T).toTask(name: String = "Task") = object : Task() { + override val name = name + override suspend fun onAction() = this@toTask() + } + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt b/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt new file mode 100644 index 000000000..68e65f401 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt @@ -0,0 +1,55 @@ +package com.lambda.task + +import com.lambda.task.OptionalTask.Companion.toOptionalTask +import com.lambda.task.Task.Companion.toTask + +@DslMarker +annotation class TaskCha1nBuilder // Name is used to force the dsl style turquoise (name hash) + +@DslMarker +annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) + +@TaskCha1nBuilder +fun emptyChain() = TaskChain() + +@TaskCha1nBuilder +fun buildTask( + block: suspend () -> Unit +) = TaskChain(listOf(block.toTask())) + +@TaskCha1nBuilder +fun buildChain( + block: TaskChainBuilder.() -> Unit +) = TaskChainBuilder().apply { + block() +}.build() + +@TaskCha1nBuilder +class TaskChainBuilder { + private val steps: MutableList> = mutableListOf() + + @TaskCha1nBuilder + fun required(block: suspend () -> Unit) { + steps.add(block.toTask()) + } + + @TaskCha1nBuilder + fun required(task: Task<*>) { + steps.add(task) + } + + @TaskCha1nBuilder + fun optional(block: suspend () -> Unit) { + // ToDo: Find application to tell design. Should be based on a task strategy + steps.add(block.toOptionalTask { + true + }) + } + + @TaskCha1nBuilder + fun optional(task: OptionalTask<*>) { + steps.add(task) + } + + fun build() = TaskChain(steps) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/TaskChain.kt b/common/src/main/kotlin/com/lambda/task/TaskChain.kt new file mode 100644 index 000000000..82c79b646 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/TaskChain.kt @@ -0,0 +1,19 @@ +package com.lambda.task + +import com.lambda.util.Communication.info + +class TaskChain( + private val steps: List> = listOf() +) { + operator fun plus(other: TaskChain) = TaskChain(steps + other.steps) + + suspend fun execute() { +// info("Executing TaskChain $this") + steps.forEach { + info("Executing task: ${it.name}") + it.execute() + } + } + + override fun toString() = "TaskChain:\n${steps.withIndex().joinToString("\n") { "${it.index + 1}. ${it.value}" }}" +} diff --git a/common/src/main/kotlin/com/lambda/task/TaskDecisionTree.kt b/common/src/main/kotlin/com/lambda/task/TaskDecisionTree.kt new file mode 100644 index 000000000..1f8f3313e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/TaskDecisionTree.kt @@ -0,0 +1,5 @@ +package com.lambda.task + +class TaskDecisionTree { + +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt new file mode 100644 index 000000000..95513fb52 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -0,0 +1,30 @@ +package com.lambda.task.tasks + +import com.lambda.interaction.material.ContainerManager +import com.lambda.interaction.material.ContainerManager.findContainerWithSelection +import com.lambda.interaction.material.StackSelection +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import net.minecraft.item.ItemStack + +class AcquireMaterial( + val selection: StackSelection +) : Task() { + override suspend fun onAction(): StackSelection { + // Find the best source with enough supplies + findContainerWithSelection(selection)?.let { container -> + (container.prepare() + container.withdraw(selection)).execute() + } ?: throw ContainerManager.NoContainerFound(selection) // ToDo: Create crafting path + + return selection + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.acquireStack(selection: StackSelection) = + AcquireMaterial(selection).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt new file mode 100644 index 000000000..52e3f7e9d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -0,0 +1,44 @@ +package com.lambda.task.tasks + +import com.lambda.interaction.construction.Blueprint +import com.lambda.interaction.construction.Blueprint.Companion.from +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import net.minecraft.util.math.BlockPos + +class BuildStructure( + val blueprint: Blueprint, + private val collectDrops: Boolean = false, + private val skipWeakBlocks: Boolean = false, + private val pathing: Boolean = true, +) : Task() { + override suspend fun onAction() { + TODO("Not yet implemented") + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.buildStructure( + blueprint: Blueprint, + collectDrops: Boolean = false, + skipWeakBlocks: Boolean = false, + pathing: Boolean = true, + ) = BuildStructure( + blueprint, + collectDrops, + skipWeakBlocks, + pathing + ).apply { + required(this) + } + + @TaskCha1nBuilder + fun TaskChainBuilder.breakAndCollectBlock( + blockPos: BlockPos + ) = BuildStructure(blockPos.from(TargetState.Air), collectDrops = true).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt new file mode 100644 index 000000000..4efce6b7d --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -0,0 +1,32 @@ +package com.lambda.task.tasks + +import baritone.api.BaritoneAPI +import baritone.api.pathing.goals.Goal +import baritone.api.pathing.goals.GoalXZ +import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import net.minecraft.util.math.BlockPos + +class GoalTask( + private val goal: Goal +) : Task() { + override suspend fun onAction() { + BaritoneAPI.getProvider().primaryBaritone.customGoalProcess.setGoalAndPath(goal) + + awaitEvent { + BaritoneAPI.getProvider().primaryBaritone.pathingBehavior.isPathing + } + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.moveIntoEntityRange(blockPos: BlockPos) = + GoalTask(GoalXZ(blockPos.x, blockPos.z)).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt index d03aa5a14..a04a02cd8 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt @@ -14,7 +14,7 @@ class HelloWorldTask : Task() { } } - override suspend fun SafeContext.onAction() { + override suspend fun onAction() { LOG.info("Hello, World!") delay(250) LOG.info("Bye, World! Action completed") diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt new file mode 100644 index 000000000..300097a64 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt @@ -0,0 +1,37 @@ +package com.lambda.task.tasks + +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import com.lambda.task.buildChain +import com.lambda.task.tasks.LookAtBlock.Companion.lookAtBlock +import com.lambda.threading.runGameBlocking +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos + +class InteractBlock( + val blockPos: BlockPos +) : Task() { + + override suspend fun onAction() { + buildChain { + lookAtBlock(blockPos) + .withTimeout(3000L) + .onSuccess { request -> + runGameBlocking { + val cast = request.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") + interaction.interactBlock(player, Hand.MAIN_HAND, cast) + } + } + }.execute() + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.interactWithBlock(blockPos: BlockPos) = + InteractBlock(blockPos).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt new file mode 100644 index 000000000..712f181cf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt @@ -0,0 +1,67 @@ +package com.lambda.task.tasks + +import com.lambda.interaction.material.StackSelection +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import com.lambda.threading.runGameBlocking +import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.primitives.extension.containerSlots +import com.lambda.util.primitives.extension.inventorySlots +import kotlinx.coroutines.delay +import net.minecraft.item.ItemStack +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.slot.Slot +import net.minecraft.screen.slot.SlotActionType + +class InventoryTask( + val screen: H, + val from: List, + private val closeScreen: Boolean = true +) : Task>() { + + // ToDo: Needs smart code to move as efficient as possible. + // Also should handle overflow etc. Should be more generic + override suspend fun onAction(): List { + val moved = mutableListOf() + + from.forEach { + runGameBlocking { + // ToDo: Qickmove will not work in most situations + val preMove = it.stack.copy() + screen.onSlotClick(it.index, 0, SlotActionType.QUICK_MOVE, player) + if (!it.stack.equal(preMove)) { + moved.add(preMove) + } + } + delay(TaskFlow.itemMoveDelay) +// screen.syncState() // ToDo: Needed? + } + runGameBlocking { + if (closeScreen) player.closeHandledScreen() + } + + return moved + } + + companion object { + @TaskCha1nBuilder + inline fun TaskChainBuilder.withdraw(screen: H, selection: StackSelection) = + InventoryTask( + screen, + selection.filterSlots(screen.containerSlots) + ).apply { + required(this) + } + + @TaskCha1nBuilder + inline fun TaskChainBuilder.deposit(screen: H, selection: StackSelection) = + InventoryTask( + screen, + selection.filterSlots(screen.inventorySlots) + ).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt new file mode 100644 index 000000000..753ec5e59 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt @@ -0,0 +1,56 @@ +package com.lambda.task.tasks + +import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.InteractionConfig +import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.interaction.rotation.RotationRequest +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +class LookAtBlock( + val blockPos: BlockPos, + private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + private val sides: Set = emptySet(), + private val priority: Int = 0, +) : Task() { + private var finished = false + private var request: RotationRequest? = null + init { + listener { + request = it.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides, priority) + } + + listener { + request?.let { + finished = !it.isPending + } + } + } + + override suspend fun onAction(): RotationRequest { + awaitEvent { finished } + + return request ?: throw IllegalStateException("Failed to look at block") + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.lookAtBlock( + blockPos: BlockPos, + rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + sides: Set = emptySet(), + priority: Int = 0, + ) = LookAtBlock(blockPos, rotationConfig, interactionConfig, sides, priority).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt new file mode 100644 index 000000000..feae2cf04 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -0,0 +1,55 @@ +package com.lambda.task.tasks + +import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.ScreenHandlerEvent +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import com.lambda.task.buildChain +import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange +import com.lambda.task.tasks.InteractBlock.Companion.interactWithBlock +import com.lambda.task.tasks.LookAtBlock.Companion.lookAtBlock +import com.lambda.threading.runGameBlocking +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.screen.ScreenHandler +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos + +class OpenContainer( + private val blockPos: BlockPos, + private val waitForSlotLoad: Boolean = true, +) : Task() { + override suspend fun onAction(): H { + var screen: H? = null + + buildChain { + moveIntoEntityRange(blockPos) + .withTimeout(60000L) + lookAtBlock(blockPos) + .withTimeout(5000L) + .onSuccess { request -> + runGameBlocking { + val cast = request.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") + interaction.interactBlock(player, Hand.MAIN_HAND, cast) + } + + screen = awaitEvent>().screen + + // block until the stacks were loaded + if (waitForSlotLoad) awaitEvent() + } + }.execute() + + return screen ?: throw IllegalStateException("Failed to open container") + } + + companion object { + @TaskCha1nBuilder + inline fun TaskChainBuilder.openContainer( + blockPos: BlockPos, + waitForSlotLoad: Boolean = true + ) = OpenContainer(blockPos, waitForSlotLoad).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt new file mode 100644 index 000000000..c62585141 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -0,0 +1,23 @@ +package com.lambda.task.tasks + +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos + +class PlaceContainer( + val stack: ItemStack, +) : Task() { + override suspend fun onAction(): BlockPos { + TODO("Not yet implemented") + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.placeContainer(stack: ItemStack) = + PlaceContainer(stack).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt b/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt index e62141efe..299120337 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt @@ -3,7 +3,12 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.event.events.KeyPressEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.task.buildChain +import com.lambda.task.tasks.OpenContainer.Companion.openContainer import com.lambda.threading.taskContext +import com.lambda.util.Communication.info +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.screen.GenericContainerScreenHandler import org.lwjgl.glfw.GLFW object TaskTester { @@ -14,23 +19,32 @@ object TaskTester { } taskContext { - HelloWorldTask() - .withDelay(500L) - .withTimeout(200L) - .withRepeats(10) - .withMaxAttempts(5) - .onSuccess { - LOG.info("Hello, World! Task completed") - }.onRepeat { repeats -> - LOG.info("Hello, World! Task $name repeated $repeats times") - }.onTimeout { - LOG.warn("Hello, World! Task $name timed out") - HelloWorldTask().execute() - }.onRetry { - LOG.warn("Hello, World! Task $name retrying") - }.onFailure { error -> - LOG.error("Hello, World! Task failed", error) - }.execute() + val blockPos = mc.crosshairTarget?.blockResult?.blockPos ?: return@taskContext + + this@TaskTester.info("Testing task") + buildChain { + openContainer(blockPos) + .onSuccess { + this@TaskTester.info("Opened container") + } + }.execute() +// HelloWorldTask() +// .withDelay(500L) +// .withTimeout(200L) +// .withRepeats(10) +// .withMaxAttempts(5) +// .onSuccess { +// LOG.info("Hello, World! Task completed") +// }.onRepeat { repeats -> +// LOG.info("Hello, World! Task $name repeated $repeats times") +// }.onTimeout { +// LOG.warn("Hello, World! Task $name timed out") +// HelloWorldTask().execute() +// }.onRetry { +// LOG.warn("Hello, World! Task $name retrying") +// }.onFailure { error -> +// LOG.error("Hello, World! Task failed", error) +// }.execute() } } } diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 51fbbb3f2..a64cbf206 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -1,10 +1,12 @@ package com.lambda.threading -import com.lambda.Lambda +import com.lambda.Lambda.mc import com.lambda.context.ClientContext import com.lambda.context.SafeContext import com.lambda.event.EventFlow +import kotlinx.coroutines.future.await import kotlinx.coroutines.launch +import java.util.concurrent.CompletableFuture /** * Executes a block of code only if the context is safe. A context is considered safe when all the following properties are not null: @@ -18,13 +20,12 @@ import kotlinx.coroutines.launch * @param block The block of code to be executed within the safe context. * @return The result of the block execution if the context is safe, null otherwise. */ -inline fun runSafe(block: SafeContext.() -> T): T? { - return ClientContext().toSafe()?.let { block(it) } -} +inline fun runSafe(block: SafeContext.() -> T) = + ClientContext().toSafe()?.let { block(it) } /** * This function is used to execute a block of code on a new thread running asynchronously to the game thread. - * It should only be used when you need to perform read actions on the game data. + * It should only be used when you need to perform read actions on the game data (not write). * * Caution: Using this function to write to the game data can lead to race conditions. Therefore, it is recommended * to use this function only for read operations to avoid potential concurrency issues. @@ -72,8 +73,8 @@ inline fun runSafeConcurrent(crossinline block: SafeContext.() -> Unit) { * * @param block The task to be executed on the game's main thread. */ -inline fun runOnGameThread(crossinline block: () -> Unit) { - Lambda.mc.executeSync { block() } +inline fun runOnGameThreadConcurrent(crossinline block: () -> Unit) { + mc.executeSync { block() } } /** @@ -93,6 +94,27 @@ inline fun runOnGameThread(crossinline block: () -> Unit) { * * @param block The task to be executed on the game's main thread within a safe context. */ -inline fun runSafeOnGameThread(crossinline block: SafeContext.() -> Unit) { - runOnGameThread { runSafe { block() } } -} \ No newline at end of file +inline fun runSafeOnGameThreadConcurrent(crossinline block: SafeContext.() -> Unit) { + runOnGameThreadConcurrent { runSafe { block() } } +} + +/** + * Executes a given task on the game's main thread within a safe context + * and blocks the coroutine until the task is completed. + * A context is considered safe when all the following properties are not null: + * - [SafeContext.world] + * - [SafeContext.player] + * - [SafeContext.interaction] + * - [SafeContext.connection] + * + * This function is used when a task needs to be performed on the game's main thread, + * as certain operations are not safe to perform on other threads. + * + * Note: + * This function is blocking + * as it uses [CompletableFuture]'s [await] method to [suspend] the coroutine until the task is completed. + * + * @param block The task to be executed on the game's main thread within a safe context. + */ +suspend inline fun runGameBlocking(noinline block: SafeContext.() -> T) = + CompletableFuture.supplyAsync({ runSafe { block() } }, mc).await() ?: throw IllegalStateException("Unsafe") \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index b95def705..8fd5d9ee7 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -1,7 +1,7 @@ package com.lambda.util -import com.lambda.util.ItemUtils.block -import com.lambda.util.ItemUtils.shulkerBoxes +import com.lambda.util.item.ItemUtils.block +import com.lambda.util.item.ItemUtils.shulkerBoxes import net.minecraft.block.Block import net.minecraft.block.Blocks import net.minecraft.fluid.Fluids diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt new file mode 100644 index 000000000..0049b6d18 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt @@ -0,0 +1,62 @@ +package com.lambda.util.item + +import net.minecraft.inventory.Inventories +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtElement +import net.minecraft.util.collection.DefaultedList + +object ItemStackUtils { + val ItemStack.spaceLeft get() = maxCount - count + val ItemStack.hasSpace get() = spaceLeft > 0 + val List.spaceLeft get() = sumOf { it.spaceLeft } + val List.empty: Int get() = count { it.isEmpty } + val List.count: Int get() = sumOf { it.count } + val List.copy: List get() = map { it.copy() } + + val List.compressed: List get() = + fold(mutableListOf()) { acc, itemStack -> + acc merge itemStack + acc + } + + infix fun List.merge(other: ItemStack): List { + return flatMap { + it merge other + } + } + + infix fun ItemStack.merge(other: ItemStack): List { + if (!isStackable || !other.isStackable) { + return listOf(this, other) + } + + val newCount = count + other.count + if (newCount <= maxCount) { + return listOf(copyWithCount(newCount)) + } + val remainder = newCount - maxCount + return listOf(copyWithCount(maxCount), copyWithCount(remainder)) + } + + val ItemStack.shulkerBoxContents: List get() = + BlockItem.getBlockEntityNbt(this)?.takeIf { + it.contains("Items", NbtElement.LIST_TYPE.toInt()) + }?.let { + val list = DefaultedList.ofSize(27, ItemStack.EMPTY) + Inventories.readNbt(it, list) + list + } ?: emptyList() + + /** + * Checks if the given item stacks are equal, including the item count and NBT. + * + * @param other The other item stack to compare with. + * @return `true` if the item stacks are equal, `false` otherwise. + * @see ItemStack.areItemsEqual Checks if the items in two item stacks are equal. + * @see ItemStack.canCombine Checks if two item stacks can be combined into one stack. + */ + fun ItemStack?.equal(other: ItemStack?) = ItemStack.areEqual(this, other) + + fun ItemStack.combines(other: ItemStack) = ItemStack.canCombine(this, other) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/ItemUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt similarity index 92% rename from common/src/main/kotlin/com/lambda/util/ItemUtils.kt rename to common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index 021bdb466..3d9455762 100644 --- a/common/src/main/kotlin/com/lambda/util/ItemUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -1,4 +1,4 @@ -package com.lambda.util +package com.lambda.util.item import net.minecraft.block.Block import net.minecraft.item.Item @@ -101,15 +101,15 @@ object ItemUtils { val Item.block: Block get() = Block.getBlockFromItem(this) - fun Int.formatToHumanCount(): String { + fun Int.toItemCount(): String { if (this < 0) { return "Invalid input" } return buildString { - val dubs = this@formatToHumanCount / (54 * 27) - val shulkers = (this@formatToHumanCount % (54 * 27)) / 64 - val remainingItems = this@formatToHumanCount % 64 + val dubs = this@toItemCount / (54 * 27) + val shulkers = (this@toItemCount % (54 * 27)) / 64 + val remainingItems = this@toItemCount % 64 if (dubs > 0) { append("$dubs dub") diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/Screen.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/Screen.kt new file mode 100644 index 000000000..35e7b56aa --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/Screen.kt @@ -0,0 +1,12 @@ +package com.lambda.util.primitives.extension + +import net.minecraft.entity.player.PlayerInventory +import net.minecraft.item.ItemStack +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.slot.Slot + +val ScreenHandler.containerSlots: List get() = slots.filter { it.inventory !is PlayerInventory } +val ScreenHandler.inventorySlots: List get() = slots.filter { it.inventory is PlayerInventory } + +val ScreenHandler.containerStacks: List get() = containerSlots.map { it.stack } +val ScreenHandler.inventoryStacks: List get() = inventorySlots.map { it.stack } \ No newline at end of file diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index dcf61e84f..2757e2995 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -4,6 +4,7 @@ accessible field net/minecraft/client/MinecraftClient itemUseCooldown I accessible field net/minecraft/client/MinecraftClient attackCooldown I accessible field net/minecraft/client/MinecraftClient paused Z accessible field net/minecraft/client/MinecraftClient pausedTickDelta F +accessible field net/minecraft/client/MinecraftClient thread Ljava/lang/Thread; # World accessible field net/minecraft/client/world/ClientWorld entityManager Lnet/minecraft/client/world/ClientEntityManager; diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index c416f6561..1672d3a3b 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -6,9 +6,10 @@ "client": [ "ClientConnectionMixin", "MinecraftClientMixin", - "baritone.MixinLookBehavior", "baritone.MixinBaritonePlayerContext", + "baritone.MixinLookBehavior", "entity.ClientPlayerEntityMixin", + "entity.ClientPlayInteractionManagerMixin", "entity.EntityMixin", "entity.LivingEntityMixin", "entity.PlayerEntityMixin", @@ -21,6 +22,7 @@ "render.DebugHudMixin", "render.GameRendererMixin", "render.RenderTickCounterMixin", + "render.ScreenHandlerMixin", "render.WorldRendererMixin" ], "injectors": { From e89de22d18c5c84e2770e4a0b131ecf03b8237da Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 31 Mar 2024 08:07:23 +0200 Subject: [PATCH 04/47] Baritone helper --- common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt | 7 +++---- common/src/main/kotlin/com/lambda/util/BaritoneUtils.kt | 3 +++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 4efce6b7d..5698d91a7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -1,24 +1,23 @@ package com.lambda.task.tasks -import baritone.api.BaritoneAPI import baritone.api.pathing.goals.Goal import baritone.api.pathing.goals.GoalXZ import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder import com.lambda.task.TaskChainBuilder +import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockPos class GoalTask( private val goal: Goal ) : Task() { override suspend fun onAction() { - BaritoneAPI.getProvider().primaryBaritone.customGoalProcess.setGoalAndPath(goal) + BaritoneUtils.setGoalAndPath(goal) awaitEvent { - BaritoneAPI.getProvider().primaryBaritone.pathingBehavior.isPathing + BaritoneUtils.isPathing } } diff --git a/common/src/main/kotlin/com/lambda/util/BaritoneUtils.kt b/common/src/main/kotlin/com/lambda/util/BaritoneUtils.kt index a16dbfce6..560181d4d 100644 --- a/common/src/main/kotlin/com/lambda/util/BaritoneUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BaritoneUtils.kt @@ -3,6 +3,7 @@ package com.lambda.util import baritone.api.BaritoneAPI import baritone.api.IBaritone import baritone.api.Settings +import baritone.api.pathing.goals.Goal object BaritoneUtils { private val baritone = BaritoneAPI.getProvider() @@ -15,5 +16,7 @@ object BaritoneUtils { val isActive: Boolean get() = primary.customGoalProcess.isActive || primary.pathingBehavior.isPathing || primary.pathingControlManager.mostRecentInControl().orElse(null)?.isActive == true + fun setGoalAndPath(goal: Goal) = primary.customGoalProcess.setGoalAndPath(goal) + fun cancel() = primary.pathingBehavior.cancelEverything() } From 448aaec78707fa65b09dcb93b27e91bc7551290c Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 18 Apr 2024 01:50:48 +0200 Subject: [PATCH 05/47] Task control with registry, block break tests --- .../kotlin/com/lambda/config/Configuration.kt | 5 +- .../src/main/kotlin/com/lambda/core/Loader.kt | 7 +- .../main/kotlin/com/lambda/event/EventFlow.kt | 10 +++ .../com/lambda/event/events/RotationEvent.kt | 9 +-- .../lambda/event/events/ScreenHandlerEvent.kt | 7 +- .../com/lambda/graphics/buffer/vao/VAO.kt | 4 +- .../com/lambda/interaction/RotationManager.kt | 4 +- .../interaction/material/ContainerManager.kt | 5 +- .../material/container/HotbarContainer.kt | 14 +--- .../material/container/InventoryContainer.kt | 29 ------- .../interaction/rotation/IRotationConfig.kt | 1 + .../interaction/rotation/RotationContext.kt | 8 +- .../interaction/rotation/RotationMode.kt | 2 + .../visibilty/VisibilityChecker.kt | 48 +++++------ .../com/lambda/module/modules/Packetlogger.kt | 3 +- .../com/lambda/module/modules/RotationTest.kt | 54 ------------- .../com/lambda/module/modules/player/Nuker.kt | 57 ++++++++++--- .../module/modules/render/InventoryDebug.kt | 5 +- .../src/main/kotlin/com/lambda/task/Task.kt | 39 ++++----- .../main/kotlin/com/lambda/task/TaskChain.kt | 24 ++++-- .../kotlin/com/lambda/task/TaskRegistry.kt | 46 +++++++++++ .../com/lambda/task/tasks/AcquireMaterial.kt | 3 +- .../com/lambda/task/tasks/BreakBlock.kt | 80 +++++++++++++++++++ .../com/lambda/task/tasks/BuildStructure.kt | 4 +- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 3 +- .../com/lambda/task/tasks/InteractBlock.kt | 6 +- .../com/lambda/task/tasks/LookAtBlock.kt | 30 +++---- .../com/lambda/task/tasks/OpenContainer.kt | 34 +++++--- .../com/lambda/task/tasks/TaskTester.kt | 51 ------------ .../com/lambda/threading/MainThreadInit.kt | 2 +- .../kotlin/com/lambda/threading/Threading.kt | 15 ++-- .../kotlin/com/lambda/util/Communication.kt | 7 +- 32 files changed, 316 insertions(+), 300 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt create mode 100644 common/src/main/kotlin/com/lambda/task/TaskRegistry.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt diff --git a/common/src/main/kotlin/com/lambda/config/Configuration.kt b/common/src/main/kotlin/com/lambda/config/Configuration.kt index 3dc6768a7..88e8ce446 100644 --- a/common/src/main/kotlin/com/lambda/config/Configuration.kt +++ b/common/src/main/kotlin/com/lambda/config/Configuration.kt @@ -9,6 +9,7 @@ import com.lambda.config.configurations.ModuleConfig import com.lambda.event.EventFlow.lambdaScope import com.lambda.event.events.ClientEvent import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener +import com.lambda.threading.runIO import com.lambda.util.Communication.info import com.lambda.util.Communication.logError import com.lambda.util.StringUtils.capitalize @@ -81,7 +82,7 @@ abstract class Configuration : Jsonable { } fun tryLoad() { - lambdaScope.launch(Dispatchers.IO) { + runIO { runCatching { load(primary) } .onSuccess { val message = "${configName.capitalize()} config loaded." @@ -108,7 +109,7 @@ abstract class Configuration : Jsonable { } fun trySave() { - lambdaScope.launch(Dispatchers.IO) { + runIO { runCatching { save() } .onSuccess { val message = "Saved ${configName.capitalize()} config." diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 1d05f3a95..7c4b66131 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -3,14 +3,12 @@ package com.lambda.core import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.command.CommandManager -import com.lambda.config.configurations.GuiConfig import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.gui.impl.clickgui.GuiConfigurable -import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry -import com.lambda.task.tasks.TaskTester +import com.lambda.task.TaskRegistry import com.lambda.util.Communication.ascii import kotlin.system.measureTimeMillis @@ -27,8 +25,6 @@ object Loader { ascii.split("\n").forEach { LOG.info(it) } LOG.info("Initializing ${Lambda.MOD_NAME} ${Lambda.VERSION}") - TaskTester - val initTime = measureTimeMillis { loadables.forEach { loadable -> var info: String @@ -42,6 +38,7 @@ object Loader { LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") + TaskRegistry GuiConfigurable // ToDo: Find more elegant solution } } diff --git a/common/src/main/kotlin/com/lambda/event/EventFlow.kt b/common/src/main/kotlin/com/lambda/event/EventFlow.kt index 74a303cea..ee7683803 100644 --- a/common/src/main/kotlin/com/lambda/event/EventFlow.kt +++ b/common/src/main/kotlin/com/lambda/event/EventFlow.kt @@ -1,8 +1,10 @@ package com.lambda.event +import com.lambda.context.SafeContext import com.lambda.event.callback.ICancellable import com.lambda.event.listener.Listener import com.lambda.threading.runConcurrent +import com.lambda.threading.runSafe import kotlinx.coroutines.* import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.* @@ -48,6 +50,14 @@ object EventFlow { } suspend inline fun awaitEvent( + noinline predicate: SafeContext.(E) -> Boolean = { true }, + ) = concurrentFlow.filterIsInstance().first { + runSafe { + predicate(it) + } ?: false + } + + suspend inline fun awaitEventUnsafe( noinline predicate: (E) -> Boolean = { true }, ) = concurrentFlow.filterIsInstance().first(predicate) diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index 4187cef67..0f730a72e 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -31,7 +31,7 @@ abstract class RotationEvent : Event { init { // Always check if baritone wants to rotate as well RotationManager.BaritoneProcessor.baritoneContext?.let { context -> - this.context = RotationContext(context.rotation, context.config) + this.context = context } } @@ -53,16 +53,15 @@ abstract class RotationEvent : Event { blockPos: BlockPos, rotationConfig: IRotationConfig = TaskFlow.rotationSettings, interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - sides: Set = emptySet(), - priority: Int = 0, + sides: Set = emptySet() ) = runSafe { val state = world.getBlockState(blockPos) val voxelShape = state.getOutlineShape(world, blockPos) val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } - findRotation(rotationConfig, interactionConfig, boundingBoxes, priority, sides) { + findRotation(rotationConfig, interactionConfig, boundingBoxes, sides) { blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) }?.let { - requests.add(it) + context = it return@runSafe it } return@runSafe null diff --git a/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt b/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt index 46d68fc9a..6e6cf8352 100644 --- a/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt @@ -1,15 +1,12 @@ package com.lambda.event.events import com.lambda.event.Event -import net.minecraft.client.gui.screen.Screen -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider import net.minecraft.item.ItemStack import net.minecraft.screen.ScreenHandler sealed class ScreenHandlerEvent : Event { - class Open(val screen: T) : ScreenHandlerEvent() - class Close(val screen: T) : ScreenHandlerEvent() + class Open(val screenHandler: H) : ScreenHandlerEvent() + class Close(val screenHandler: H) : ScreenHandlerEvent() data class Loaded( val revision: Int, val stacks: List, diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index dfec78dc7..1c418f5f5 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -18,7 +18,7 @@ import com.lambda.graphics.gl.VaoUtils.bufferData import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer -import com.lambda.threading.runOnGameThread +import com.lambda.threading.runGameConcurrent import com.mojang.blaze3d.systems.RenderSystem.drawElements import org.lwjgl.opengl.GL30C.* import java.awt.Color @@ -48,7 +48,7 @@ class VAO( val stride = attribGroup.stride objectSize = stride * drawMode.indicesCount - runOnGameThread { + runGameConcurrent { vertices = byteBuffer(objectSize * 256 * 4) verticesPointer = address(vertices) verticesPosition = verticesPointer diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 25cfb7381..ee1a60022 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -16,7 +16,7 @@ import com.lambda.interaction.rotation.Rotation.Companion.lerp import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.rotation.RotationMode import com.lambda.module.modules.client.Baritone -import com.lambda.threading.runOnGameThreadConcurrent +import com.lambda.threading.runGameConcurrent import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.toRadian @@ -51,7 +51,7 @@ object RotationManager : Loadable { val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listener - runOnGameThreadConcurrent { + runGameConcurrent { reset(Rotation(packet.yaw, packet.pitch)) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 37ce4c4bd..c87d9d178 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -1,15 +1,12 @@ package com.lambda.interaction.material -import com.lambda.context.SafeContext import com.lambda.event.events.InteractionEvent import com.lambda.event.events.ScreenHandlerEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.* -import com.lambda.module.modules.client.TaskFlow import com.lambda.util.Communication.info import com.lambda.util.item.ItemUtils -import com.lambda.util.player.SlotUtils.combined import com.lambda.util.primitives.extension.containerStacks import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity @@ -41,7 +38,7 @@ object ContainerManager { } listener> { event -> - val handler = event.screen + val handler = event.screenHandler when (val block = lastInteractedBlockEntity) { is EnderChestBlockEntity -> { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt index cd455f6b0..641cb4f8a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -6,6 +6,7 @@ import com.lambda.interaction.material.StackSelection import com.lambda.task.TaskChain import com.lambda.task.buildTask import com.lambda.task.emptyChain +import com.lambda.threading.runGameBlocking import com.lambda.util.player.SlotUtils.hotbar import net.minecraft.item.ItemStack @@ -16,16 +17,7 @@ object HotbarContainer : MaterialContainer(Rank.HOTBAR) { override fun prepare() = emptyChain() - override fun withdraw(selection: StackSelection) = buildTask { -// gameThreadBlocking { -// stacks.filter(selection.selector).forEach { stack -> -// player.inventory.selectedSlot = player.inventory.main.indexOf(stack) -// delay(TaskFlow.itemMoveDelay) -// } -// } - } + override fun withdraw(selection: StackSelection) = emptyChain() - override fun deposit(selection: StackSelection): TaskChain { - TODO("Not yet implemented") - } + override fun deposit(selection: StackSelection) = emptyChain() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt index 62783a980..1e16b6053 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -15,33 +15,4 @@ object InventoryContainer : MaterialContainer(Rank.INVENTORY) { override fun withdraw(selection: StackSelection) = emptyChain() override fun deposit(selection: StackSelection) = emptyChain() - -// Lambda.LOG.info("Moving $selection from inventory to slot ${destination.slot}") -// stacks.filter(selection.selector).take(selection.count).forEach { stack -> -// player.currentScreenHandler?.let { screenHandler -> -// if (screenHandler.stacks[destination.slot].item == stack.item) { -// return@forEach -// } -// val currentStackSlot = screenHandler.stacks.indexOf(stack) -// if (currentStackSlot == destination.slot) { -// return@forEach -// } -// interaction.clickSlot( -// player.currentScreenHandler?.syncId ?: 0, -// currentStackSlot, -// 0, -// SlotActionType.PICKUP, -// player, -// ) -// delay(TaskFlow.itemMoveDelay) -// interaction.clickSlot( -// player.currentScreenHandler?.syncId ?: 0, -// destination.slot, -// 0, -// SlotActionType.PICKUP, -// player, -// ) -// delay(TaskFlow.itemMoveDelay) -// } -// } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt index c3593654d..2f8f1f317 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt @@ -2,6 +2,7 @@ package com.lambda.interaction.rotation interface IRotationConfig { /** + * - [RotationMode.NONE] No rotation. * - [RotationMode.SILENT] Spoofing server-side rotation. * - [RotationMode.SYNC] Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). * - [RotationMode.LOCK] Locks the camera client-side. diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt index d615c43c4..6e95c8d47 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt @@ -1,12 +1,12 @@ package com.lambda.interaction.rotation -import com.lambda.interaction.RotationManager.currentRotation -import com.lambda.interaction.RotationManager.prevRotation -import com.lambda.interaction.rotation.Rotation.Companion.fixSensitivity +import net.minecraft.util.hit.HitResult data class RotationContext( val rotation: Rotation, val config: IRotationConfig, + val verify: HitResult.() -> Boolean = { true }, + val hitResult: HitResult? = null, ) { - val isPending: Boolean get() = rotation.fixSensitivity(prevRotation) == currentRotation + val isValid: Boolean get() = hitResult?.verify() == true } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt index 9fb5a7900..c5aa4a501 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationMode.kt @@ -1,11 +1,13 @@ package com.lambda.interaction.rotation /** + * @property NONE No rotation. * @property SILENT Spoofing server-side rotation. * @property SYNC Spoofing server-side rotation and adjusting client-side movement based on reported rotation (for Grim). * @property LOCK Locks the camera client-side. */ enum class RotationMode { + NONE, SILENT, SYNC, LOCK diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index 099b55c49..2e33f4806 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -1,6 +1,5 @@ package com.lambda.interaction.visibilty -import com.lambda.config.RotationSettings import com.lambda.context.SafeContext import com.lambda.interaction.InteractionConfig import com.lambda.interaction.RotationManager @@ -26,29 +25,23 @@ object VisibilityChecker { ): RotationContext? { val eye = player.getCameraPosVec(mc.tickDelta) + val currentRotation = RotationManager.currentRotation + val currentCast = currentRotation.rayCast( + interact.reach, + interact.rayCastMask, + eye + ) + if (boxes.any { it.contains(eye) }) { - return stay(rotationConfig) + return RotationContext(currentRotation, rotationConfig, verify, currentCast) } -// val currentRotation = RotationManager.currentRotation -// val currentCast = currentRotation.rayCast( -// interact.reach, -// interact.rayCastMask, -// eye -// ) -// val passed = currentCast?.let { it.verify() } ?: false -// // Slowdown or freeze if looking correct -// (rotationConfig as? RotationSettings)?.slowdownIf(passed) ?: run { -// if (passed) { -// return stay(rotationConfig) -// } -// } - - val validHits = mutableSetOf() + val validHits = mutableMapOf() + val reachSq = interact.reach.pow(2) boxes.forEach { box -> scanVisibleSurfaces(box, sides, interact.resolution) { vec -> - if (eye distSq vec > interact.reach.pow(2)) return@scanVisibleSurfaces + if (eye distSq vec > reachSq) return@scanVisibleSurfaces val newRotation = eye.rotationTo(vec) @@ -59,24 +52,21 @@ object VisibilityChecker { ) ?: return@scanVisibleSurfaces if (!cast.verify()) return@scanVisibleSurfaces - validHits.add(vec) + validHits[vec] = cast } } - validHits.mostCenter?.let { optimum -> - validHits.minByOrNull { optimum distSq it }?.let { closest -> - val optimumRotation = eye.rotationTo(closest) - return RotationContext(optimumRotation, rotationConfig) + validHits.keys.mostCenter?.let { optimum -> + validHits.minByOrNull { optimum distSq it.key }?.let { closest -> + val optimumRotation = eye.rotationTo(closest.key) + return RotationContext(optimumRotation, rotationConfig, verify, closest.value) } } return null } - private fun stay(config: IRotationConfig) = - RotationContext(RotationManager.currentRotation, config) - - private inline fun SafeContext.scanVisibleSurfaces( + inline fun SafeContext.scanVisibleSurfaces( box: Box, sides: Set, resolution: Int, @@ -108,7 +98,7 @@ object VisibilityChecker { acc.add(vec3d) }?.multiply(1.0 / size.toDouble()) - private fun Box.bounds(side: Direction) = + fun Box.bounds(side: Direction) = when (side) { Direction.DOWN -> doubleArrayOf(minX, minY, minZ, maxX, minY, maxZ) Direction.UP -> doubleArrayOf(minX, maxY, minZ, maxX, maxY, maxZ) @@ -118,7 +108,7 @@ object VisibilityChecker { Direction.EAST -> doubleArrayOf(maxX, minY, minZ, maxX, maxY, maxZ) } - private fun SafeContext.getVisibleSurfaces(box: Box): Set { + fun SafeContext.getVisibleSurfaces(box: Box): Set { val visibleSides = EnumSet.noneOf(Direction::class.java) val eyePos = player.eyePos diff --git a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt index f2a44e6e4..190da47ee 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/Packetlogger.kt @@ -10,6 +10,7 @@ import com.lambda.event.listener.UnsafeListener.Companion.unsafeConcurrentListen import com.lambda.event.listener.UnsafeListener.Companion.unsafeListener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runIO import com.lambda.util.Communication import com.lambda.util.Communication.info import com.lambda.util.DynamicReflectionSerializer.dynamicString @@ -71,7 +72,7 @@ object Packetlogger : Module( private val File.relativePath: Path get() = mc.runDirectory.toPath().relativize(toPath()) init { - lambdaScope.launch(Dispatchers.IO) { + runIO { storageFlow.collect { entry -> file?.appendText(entry) if (logToChat) this@Packetlogger.info(entry) diff --git a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt b/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt deleted file mode 100644 index 885f82a51..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/RotationTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.lambda.module.modules - -import com.lambda.config.InteractionSettings -import com.lambda.config.RotationSettings -import com.lambda.event.events.RotationEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.module.Module -import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction - -object RotationTest : Module( - name = "RotationTest", - description = "Test rotation", - defaultTags = setOf() -) { - private val rotationConfig = RotationSettings(this) - private val interactionConfig = InteractionSettings(this) - - private var pos: BlockPos = BlockPos.ORIGIN - private var side = Direction.UP - - init { - onEnable { - val hit = mc.crosshairTarget as? BlockHitResult ?: return@onEnable - pos = hit.blockPos - side = hit.side - } - - listener { event -> -// val target = getClosestEntity( -// player.eyePos, interaction.reachDistance.toDouble() -// ) ?: return@listener - - event.lookAtBlock(pos, rotationConfig, interactionConfig, setOf(side)) - } - - listener { - (mc.crosshairTarget as? BlockHitResult)?.let { hit -> - if (hit.blockPos != pos || hit.side != side) { - interaction.cancelBlockBreaking() - return@listener - } - - interaction.updateBlockBreakingProgress(pos, side) - player.swingHand(Hand.MAIN_HAND) - return@listener - } - - interaction.cancelBlockBreaking() - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index af1bd7e93..b61176574 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -1,28 +1,63 @@ package com.lambda.module.modules.player import com.lambda.module.Module +import com.lambda.task.TaskChain import com.lambda.task.buildChain -import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock -import com.lambda.threading.taskContext +import com.lambda.task.tasks.BreakBlock.Companion.breakBlock import com.lambda.util.Communication.info -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import net.minecraft.text.Text +import com.lambda.util.KeyCode +import net.minecraft.util.math.BlockPos object Nuker : Module( name = "Nuker", - description = "Breaks blocks around you" + description = "Breaks blocks around you", + defaultKeybind = KeyCode.Comma ) { - private val flatten by setting("Flatten", false) + private val flatten by setting("Flatten", true) + + private var taskChain: TaskChain? = null init { onEnable { - info(player.mainHandStack.toHoverableText() ?: Text.empty()) - - taskContext { - buildChain { - breakAndCollectBlock(mc.crosshairTarget?.blockResult?.blockPos ?: return@buildChain) + taskChain = buildChain { + BlockPos.iterateOutwards(player.blockPos, 4, 4, 4).map { + it.toImmutable() + }.filter { + world.getBlockState(it).isSolidBlock(world, it) && (!flatten || it.y >= player.y) + }.forEach { pos -> + breakBlock(pos) + .onSuccess { + this@Nuker.info("Break of ${pos.toShortString()} took $age ms") + } } } + + taskChain?.tryRun() } + + onDisable { + taskChain?.cancel() + } + +// listener { +// taskChain?.cancel() +// +// BlockPos.iterateOutwards(player.blockPos, 4, 4, 4).map { +// it.toImmutable() +// }.filter { +// world.getBlockState(it).isSolidBlock(world, it) && (!flatten || it.y >= player.y) +// }.minByOrNull { +// player.pos distSq it.toCenterPos() +// }?.let { pos -> +// taskChain = buildChain { +// breakBlock(pos) +// .onSuccess { +// this@Nuker.info("Break took $age ms") +// } +// } +// +// taskChain?.tryRun() +// } +// } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt b/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt index 7f89d2ca8..b699f601f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/InventoryDebug.kt @@ -1,6 +1,5 @@ package com.lambda.module.modules.render -import com.lambda.Lambda.LOG import com.lambda.event.events.PacketEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module @@ -19,7 +18,7 @@ object InventoryDebug : Module( listener { when (val packet = it.packet) { is UpdateSelectedSlotS2CPacket, is InventoryS2CPacket -> { - LOG.info(packet.dynamicString()) + this@InventoryDebug.info(packet.dynamicString()) } } } @@ -33,7 +32,7 @@ object InventoryDebug : Module( is CreativeInventoryActionC2SPacket, is PickFromInventoryC2SPacket, is UpdateSelectedSlotC2SPacket -> { - LOG.info(it.packet.dynamicString()) + this@InventoryDebug.info(it.packet.dynamicString()) } } } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 06f819ccd..d313acdd6 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,21 +1,15 @@ package com.lambda.task -import baritone.api.BaritoneAPI -import com.lambda.Lambda.LOG import com.lambda.context.SafeContext -import com.lambda.event.Event import com.lambda.event.EventFlow import com.lambda.event.Subscriber import com.lambda.threading.runSafe +import com.lambda.util.BaritoneUtils +import com.lambda.util.Communication.info +import com.lambda.util.Communication.logError +import com.lambda.util.Communication.warn import com.lambda.util.Nameable -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.* /** * A [Task] represents a time-critical activity that executes a suspending action function. @@ -52,14 +46,14 @@ abstract class Task( private var timeout: Long = Long.MAX_VALUE, private var maxAttempts: Int = 1, open var repeats: Int = 1, - private var onSuccess: (suspend (Result) -> Unit) = {}, + private var onSuccess: (suspend Task.(Result) -> Unit) = {}, private var onRetry: (suspend Task.() -> Unit) = {}, private var onTimeout: (suspend Task.() -> Unit) = {}, private var onRepeat: (suspend Task.(Int) -> Unit) = {}, private var onException: (suspend Task.(Throwable) -> Unit) = {}, ) : Nameable { private val creationTimestamp = System.currentTimeMillis() - private val age: Long get() = System.currentTimeMillis() - creationTimestamp + val age: Long get() = System.currentTimeMillis() - creationTimestamp override val name: String get() = this::class.simpleName ?: "Task" val syncListeners = Subscriber() @@ -93,9 +87,8 @@ abstract class Task( * It also manages the lifecycle of event listeners associated with the task. * * @return The result of the task execution, represented as a [TaskResult]. - * This could be a successful result ([TaskResult.Success]), a cancellation ([TaskResult.Cancelled]), - * a failure ([TaskResult.Failure]) due to an exception, or a timeout - * ([TaskResult.Timeout]). + * This could be a [successful result][TaskResult.Success], a [cancellation][TaskResult.Cancelled], + * a [failure][TaskResult.Failure] due to an exception, or a [timeout][TaskResult.Timeout]. */ suspend fun execute(): TaskResult { var attempt = 1 @@ -103,7 +96,7 @@ abstract class Task( do { if (delay > 0) { - LOG.info("Delaying task $name for $delay ms") + info("Delaying for $delay ms") delay(delay) } @@ -124,16 +117,16 @@ abstract class Task( } } catch (e: TimeoutCancellationException) { attempt++ - LOG.warn("Task $name timed out after $age ms and $attempt attempts") + warn("Timed out after $age ms and $attempt attempts") onRetry() tidyUp() } catch (e: CancellationException) { tidyUp() - LOG.warn("Coroutine for task $name cancelled") +// warn("Job cancelled") return TaskResult.Cancelled } catch (e: Throwable) { attempt++ - LOG.error("Task $name failed after $age ms and $attempt attempts", e) + logError("Failed after $age ms and $attempt attempts with exception: $e") onException(e) tidyUp() return TaskResult.Failure(e) @@ -142,7 +135,7 @@ abstract class Task( tidyUp() } while (attempt < maxAttempts || iteration <= repeats) - LOG.error("Task $name fully timed out after $age ms and $attempt attempts") + logError("Fully timed out after $age ms and $attempt attempts") onTimeout() return TaskResult.Timeout(timeout, attempt) } @@ -155,7 +148,7 @@ abstract class Task( EventFlow.syncListeners.unsubscribe(syncListeners) EventFlow.concurrentListeners.unsubscribe(concurrentListeners) - BaritoneAPI.getProvider().primaryBaritone?.pathingBehavior?.cancelEverything() + BaritoneUtils.cancel() } private fun startListening() { @@ -224,7 +217,7 @@ abstract class Task( * @return The task instance with the updated success action. */ @Ta5kBuilder - fun onSuccess(action: suspend (Result) -> Unit): Task { + fun onSuccess(action: suspend Task.(Result) -> Unit): Task { this.onSuccess = action return this } diff --git a/common/src/main/kotlin/com/lambda/task/TaskChain.kt b/common/src/main/kotlin/com/lambda/task/TaskChain.kt index 82c79b646..2f0709894 100644 --- a/common/src/main/kotlin/com/lambda/task/TaskChain.kt +++ b/common/src/main/kotlin/com/lambda/task/TaskChain.kt @@ -1,19 +1,27 @@ package com.lambda.task -import com.lambda.util.Communication.info +import kotlinx.coroutines.ExperimentalCoroutinesApi class TaskChain( private val steps: List> = listOf() ) { - operator fun plus(other: TaskChain) = TaskChain(steps + other.steps) + suspend fun run() { + steps.forEach { TaskRegistry.run(it) } + } + + fun tryRun() { + steps.forEach { TaskRegistry.tryRun(it) } + } - suspend fun execute() { -// info("Executing TaskChain $this") - steps.forEach { - info("Executing task: ${it.name}") - it.execute() - } + @OptIn(ExperimentalCoroutinesApi::class) + fun cancel() { + TaskRegistry.taskFlow.resetReplayCache() + TaskRegistry.registry.keys + .filter { it in steps } + .forEach { TaskRegistry.cancel(it) } } + operator fun plus(other: TaskChain) = TaskChain(steps + other.steps) + override fun toString() = "TaskChain:\n${steps.withIndex().joinToString("\n") { "${it.index + 1}. ${it.value}" }}" } diff --git a/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt b/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt new file mode 100644 index 000000000..4ac460a1c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt @@ -0,0 +1,46 @@ +package com.lambda.task + +import com.lambda.threading.taskContext +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import java.util.concurrent.ConcurrentHashMap + +object TaskRegistry { + val registry = ConcurrentHashMap, Job>() + + val taskFlow = MutableSharedFlow>( + extraBufferCapacity = 1000, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + suspend fun run(task: Task<*>) { + taskFlow.emit(task) + } + + fun tryRun(task: Task<*>) { + taskFlow.tryEmit(task) + } + + fun cancel(task: Task<*>) { + task.cancel() + registry[task]?.cancel() + registry.remove(task) + } + + init { + taskContext { + taskFlow.collect { task -> +// val pending = taskFlow.replayCache.joinToString { it.name } +// task.info("Executing task: ${task.name} ${if (pending.isNotEmpty()) "(Pending: $pending)" else ""}") + val job = taskContext { + task.execute() + } + registry[task] = job + job.join() + registry.remove(task) +// task.info("Task completed: ${task.name} after ${task.age}ms") + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index 95513fb52..b1a9c0453 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -6,7 +6,6 @@ import com.lambda.interaction.material.StackSelection import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder import com.lambda.task.TaskChainBuilder -import net.minecraft.item.ItemStack class AcquireMaterial( val selection: StackSelection @@ -14,7 +13,7 @@ class AcquireMaterial( override suspend fun onAction(): StackSelection { // Find the best source with enough supplies findContainerWithSelection(selection)?.let { container -> - (container.prepare() + container.withdraw(selection)).execute() + (container.prepare() + container.withdraw(selection)).run() } ?: throw ContainerManager.NoContainerFound(selection) // ToDo: Create crafting path return selection diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt new file mode 100644 index 000000000..85cd924e6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -0,0 +1,80 @@ +package com.lambda.task.tasks + +import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.InteractionConfig +import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.interaction.rotation.RotationMode +import com.lambda.interaction.visibilty.VisibilityChecker +import com.lambda.interaction.visibilty.VisibilityChecker.bounds +import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces +import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder +import com.lambda.task.TaskChainBuilder +import com.lambda.util.primitives.extension.component6 +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.client.particle.DamageParticle +import net.minecraft.client.particle.Particle +import net.minecraft.particle.ParticleEffect +import net.minecraft.particle.ParticleTypes +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d + +class BreakBlock( + private val blockPos: BlockPos, + private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + private val sides: Set = emptySet(), + private val collectDrop: Boolean = false, +) : Task() { + + init { + listener { event -> + event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + } + + listener { + if (!it.context.isValid) return@listener + val hitResult = it.context.hitResult?.blockResult ?: return@listener + + if (interaction.updateBlockBreakingProgress(hitResult.blockPos, hitResult.side)) { + mc.particleManager.addBlockBreakingParticles(hitResult.blockPos, hitResult.side) + player.swingHand(Hand.MAIN_HAND) + } + } +// listener { +// world.getBlockState(blockPos).getCollisionShape(world, blockPos).boundingBoxes.firstOrNull()?.let { box -> +// getVisibleSurfaces(box).firstOrNull()?.let { side -> +// if (interaction.updateBlockBreakingProgress(blockPos, side)) { +// mc.particleManager.addBlockBreakingParticles(blockPos, side) +//// mc.particleManager.addParticle(ParticleTypes.CRIT, hitResult.pos.x, hitResult.pos.y, hitResult.pos.z, 0.0, 0.0, 0.0) +// player.swingHand(Hand.MAIN_HAND) +// } +// } +// } +// } + } + + override suspend fun onAction() { + awaitEvent { world.isAir(blockPos) } + } + + companion object { + @TaskCha1nBuilder + fun TaskChainBuilder.breakBlock( + blockPos: BlockPos, + rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + sides: Set = emptySet(), + collectDrop: Boolean = false, + ) = BreakBlock(blockPos, rotationConfig, interactionConfig, sides, collectDrop).apply { + required(this) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 52e3f7e9d..2dcd550b6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -9,13 +9,13 @@ import com.lambda.task.TaskChainBuilder import net.minecraft.util.math.BlockPos class BuildStructure( - val blueprint: Blueprint, + private val blueprint: Blueprint, private val collectDrops: Boolean = false, private val skipWeakBlocks: Boolean = false, private val pathing: Boolean = true, ) : Task() { override suspend fun onAction() { - TODO("Not yet implemented") + blueprint.structureMap } companion object { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 5698d91a7..16385b67a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -2,6 +2,7 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.Goal import baritone.api.pathing.goals.GoalXZ +import com.lambda.Lambda.mc import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.TickEvent import com.lambda.task.Task @@ -17,7 +18,7 @@ class GoalTask( BaritoneUtils.setGoalAndPath(goal) awaitEvent { - BaritoneUtils.isPathing + !BaritoneUtils.isActive } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt index 300097a64..7d16cee32 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt @@ -18,13 +18,13 @@ class InteractBlock( buildChain { lookAtBlock(blockPos) .withTimeout(3000L) - .onSuccess { request -> + .onSuccess { context -> runGameBlocking { - val cast = request.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") + val cast = context.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") interaction.interactBlock(player, Hand.MAIN_HAND, cast) } } - }.execute() + }.run() } companion object { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt index 753ec5e59..91c34366a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt @@ -2,11 +2,10 @@ package com.lambda.task.tasks import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.RotationEvent -import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig -import com.lambda.interaction.rotation.RotationRequest +import com.lambda.interaction.rotation.RotationContext import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder @@ -18,27 +17,19 @@ class LookAtBlock( val blockPos: BlockPos, private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - private val sides: Set = emptySet(), - private val priority: Int = 0, -) : Task() { - private var finished = false - private var request: RotationRequest? = null + private val sides: Set = emptySet() +) : Task() { + private var context: RotationContext? = null init { listener { - request = it.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides, priority) - } - - listener { - request?.let { - finished = !it.isPending - } + context = it.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } } - override suspend fun onAction(): RotationRequest { - awaitEvent { finished } + override suspend fun onAction(): RotationContext { + awaitEvent { !it.context.isValid } - return request ?: throw IllegalStateException("Failed to look at block") + return context ?: throw IllegalStateException("Failed to look at block") } companion object { @@ -47,9 +38,8 @@ class LookAtBlock( blockPos: BlockPos, rotationConfig: IRotationConfig = TaskFlow.rotationSettings, interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - sides: Set = emptySet(), - priority: Int = 0, - ) = LookAtBlock(blockPos, rotationConfig, interactionConfig, sides, priority).apply { + sides: Set = emptySet() + ) = LookAtBlock(blockPos, rotationConfig, interactionConfig, sides).apply { required(this) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index feae2cf04..0fa2f2f49 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -2,12 +2,12 @@ package com.lambda.task.tasks import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.ScreenHandlerEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder import com.lambda.task.TaskChainBuilder import com.lambda.task.buildChain -import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange -import com.lambda.task.tasks.InteractBlock.Companion.interactWithBlock import com.lambda.task.tasks.LookAtBlock.Companion.lookAtBlock import com.lambda.threading.runGameBlocking import com.lambda.util.world.raycast.RayCastUtils.blockResult @@ -19,28 +19,36 @@ class OpenContainer( private val blockPos: BlockPos, private val waitForSlotLoad: Boolean = true, ) : Task() { - override suspend fun onAction(): H { - var screen: H? = null + private var screenHandler: H? = null + private var slotsLoaded = false + + init { + listener> { + screenHandler = it.screenHandler + } + + listener { + slotsLoaded = true + } + } + override suspend fun onAction(): H { buildChain { - moveIntoEntityRange(blockPos) - .withTimeout(60000L) lookAtBlock(blockPos) - .withTimeout(5000L) + .withTimeout(2000L) .onSuccess { request -> runGameBlocking { val cast = request.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") interaction.interactBlock(player, Hand.MAIN_HAND, cast) } - screen = awaitEvent>().screen - - // block until the stacks were loaded - if (waitForSlotLoad) awaitEvent() + awaitEvent { + screenHandler != null && (!waitForSlotLoad || slotsLoaded) + } } - }.execute() + }.run() - return screen ?: throw IllegalStateException("Failed to open container") + return screenHandler ?: throw IllegalStateException("Failed to open container") } companion object { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt b/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt deleted file mode 100644 index 299120337..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/TaskTester.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.lambda.task.tasks - -import com.lambda.Lambda.LOG -import com.lambda.event.events.KeyPressEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.task.buildChain -import com.lambda.task.tasks.OpenContainer.Companion.openContainer -import com.lambda.threading.taskContext -import com.lambda.util.Communication.info -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import net.minecraft.screen.GenericContainerScreenHandler -import org.lwjgl.glfw.GLFW - -object TaskTester { - init { - listener { - if (it.key != GLFW.GLFW_KEY_Z) { - return@listener - } - - taskContext { - val blockPos = mc.crosshairTarget?.blockResult?.blockPos ?: return@taskContext - - this@TaskTester.info("Testing task") - buildChain { - openContainer(blockPos) - .onSuccess { - this@TaskTester.info("Opened container") - } - }.execute() -// HelloWorldTask() -// .withDelay(500L) -// .withTimeout(200L) -// .withRepeats(10) -// .withMaxAttempts(5) -// .onSuccess { -// LOG.info("Hello, World! Task completed") -// }.onRepeat { repeats -> -// LOG.info("Hello, World! Task $name repeated $repeats times") -// }.onTimeout { -// LOG.warn("Hello, World! Task $name timed out") -// HelloWorldTask().execute() -// }.onRetry { -// LOG.warn("Hello, World! Task $name retrying") -// }.onFailure { error -> -// LOG.error("Hello, World! Task failed", error) -// }.execute() - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt index 074f873ec..c4cb61943 100644 --- a/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt +++ b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt @@ -8,7 +8,7 @@ class MainThreadInit (private val initializer: () -> T) { operator fun getValue(thisRef: Any?, property: KProperty<*>) = value init { - runOnGameThread { + runGameConcurrent { value = initializer() } } diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index a64cbf206..4270a56d4 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -4,6 +4,7 @@ import com.lambda.Lambda.mc import com.lambda.context.ClientContext import com.lambda.context.SafeContext import com.lambda.event.EventFlow +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.future.await import kotlinx.coroutines.launch import java.util.concurrent.CompletableFuture @@ -37,11 +38,15 @@ inline fun runConcurrent(crossinline block: suspend () -> Unit) = block() } -inline fun taskContext(crossinline block: suspend () -> Unit) { +inline fun runIO(crossinline block: suspend () -> Unit) = + EventFlow.lambdaScope.launch(Dispatchers.IO) { + block() + } + +inline fun taskContext(crossinline block: suspend () -> Unit) = EventFlow.lambdaScope.launch { block() } -} /** * This function is used to execute a block of code within a safe context on a new thread running asynchronously to the game thread. @@ -73,7 +78,7 @@ inline fun runSafeConcurrent(crossinline block: SafeContext.() -> Unit) { * * @param block The task to be executed on the game's main thread. */ -inline fun runOnGameThreadConcurrent(crossinline block: () -> Unit) { +inline fun runGameConcurrent(crossinline block: () -> Unit) { mc.executeSync { block() } } @@ -94,8 +99,8 @@ inline fun runOnGameThreadConcurrent(crossinline block: () -> Unit) { * * @param block The task to be executed on the game's main thread within a safe context. */ -inline fun runSafeOnGameThreadConcurrent(crossinline block: SafeContext.() -> Unit) { - runOnGameThreadConcurrent { runSafe { block() } } +inline fun runSafeGameConcurrent(crossinline block: SafeContext.() -> Unit) { + runGameConcurrent { runSafe { block() } } } /** diff --git a/common/src/main/kotlin/com/lambda/util/Communication.kt b/common/src/main/kotlin/com/lambda/util/Communication.kt index e849d26ae..945c07b51 100644 --- a/common/src/main/kotlin/com/lambda/util/Communication.kt +++ b/common/src/main/kotlin/com/lambda/util/Communication.kt @@ -2,9 +2,8 @@ package com.lambda.util import com.lambda.Lambda import com.lambda.Lambda.mc -import com.lambda.threading.runOnGameThread import com.lambda.threading.runSafe -import com.lambda.threading.runSafeOnGameThread +import com.lambda.threading.runSafeGameConcurrent import com.lambda.util.StringUtils.capitalize import com.lambda.util.text.* import net.minecraft.client.toast.SystemToast @@ -39,7 +38,7 @@ object Communication { buildText { text(this@toast.source(logLevel, color = Color.YELLOW)) }.let { title -> - runSafeOnGameThread { + runSafeGameConcurrent { mc.toastManager.add(logLevel.toast(title, message)) } } @@ -59,7 +58,7 @@ object Communication { text(this@log.source(logLevel, source, textSource)) text(message) }.let { log -> - runSafeOnGameThread { + runSafeGameConcurrent { player.sendMessage(log) } } From 6b27bcce3844d731033dbd726c8d0aa16b33733a Mon Sep 17 00:00:00 2001 From: Constructor Date: Wed, 22 May 2024 04:49:37 +0200 Subject: [PATCH 06/47] Dynamic blueprints and structure building prototype --- .../com/lambda/graphics/buffer/vao/VAO.kt | 7 +- .../com/lambda/interaction/RotationManager.kt | 4 +- .../interaction/construction/Blueprint.kt | 65 ++++++++--------- .../construction/DynamicBlueprint.kt | 27 +++++++ .../construction/StaticBlueprint.kt | 11 +++ .../construction/context/BreakContext.kt | 35 +++++++++ .../construction/context/BuildContext.kt | 10 +++ .../construction/context/ComparableContext.kt | 7 ++ .../construction/result/BreakResult.kt | 27 +++++++ .../construction/result/BuildResult.kt | 18 +++++ .../construction/result/ComparableResult.kt | 9 +++ .../interaction/construction/result/Rank.kt | 38 ++++++++++ .../construction/result/Resolvable.kt | 7 ++ .../main/kotlin/com/lambda/module/Module.kt | 2 +- .../module/modules/debug/InventoryDebug.kt | 1 + .../com/lambda/module/modules/player/Nuker.kt | 73 +++++++++++++------ .../com/lambda/module/modules/render/XRay.kt | 6 +- .../main/kotlin/com/lambda/task/TaskChain.kt | 2 +- .../com/lambda/task/tasks/BreakBlock.kt | 72 ++++++++++-------- .../com/lambda/task/tasks/BuildStructure.kt | 57 ++++++++++++++- .../com/lambda/threading/MainThreadInit.kt | 2 +- .../kotlin/com/lambda/threading/Threading.kt | 4 +- .../main/kotlin/com/lambda/util/BlockUtils.kt | 21 ++++-- .../util/primitives/extension/Aliases.kt | 5 +- .../com/lambda/util/world/WorldUtils.kt | 29 ++++---- .../src/main/resources/lambda.accesswidener | 2 - 26 files changed, 415 insertions(+), 126 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/StaticBlueprint.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/context/ComparableContext.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/ComparableResult.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index d4a0f0449..31a878804 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -19,8 +19,7 @@ import com.lambda.graphics.gl.VaoUtils.bufferData import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer -import com.lambda.threading.mainThread -import com.lambda.threading.runOnGameThread +import com.lambda.threading.runGameScheduled import com.mojang.blaze3d.systems.RenderSystem.drawElements import org.lwjgl.opengl.GL30C.* import java.awt.Color @@ -50,7 +49,7 @@ class VAO( val stride = attribGroup.stride objectSize = stride * drawMode.indicesCount - runGameConcurrent { + runGameScheduled { vertices = byteBuffer(objectSize * 256 * 4) verticesPointer = address(vertices) verticesPosition = verticesPointer @@ -198,7 +197,7 @@ class VAO( } fun destroy() { - runOnGameThread { + runGameScheduled { glDeleteBuffers(ibo) glDeleteBuffers(vbo) glDeleteVertexArrays(vao) diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index d9be9a9a0..30b2f9bc5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -15,7 +15,7 @@ import com.lambda.interaction.rotation.Rotation.Companion.slerp import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.rotation.RotationMode import com.lambda.module.modules.client.Baritone -import com.lambda.threading.runGameConcurrent +import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe import com.lambda.util.math.MathUtils.lerp import com.lambda.util.math.MathUtils.toRadian @@ -50,7 +50,7 @@ object RotationManager : Loadable { val packet = event.packet if (packet !is PlayerPositionLookS2CPacket) return@listener - runGameConcurrent { + runGameScheduled { reset(Rotation(packet.yaw, packet.pitch)) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt index 1c7e609ee..9820b9373 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt @@ -1,50 +1,47 @@ package com.lambda.interaction.construction +import com.lambda.context.SafeContext import com.lambda.interaction.construction.verify.TargetState +import com.lambda.util.BlockUtils.blockPos +import com.lambda.util.primitives.extension.Structure import net.minecraft.structure.StructureTemplate import net.minecraft.util.math.BlockBox import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box -data class Blueprint( - private val structure: Map, - var offset: BlockPos? = null, -) { - val origin: BlockPos - get() = offset ?: structure.keys.firstOrNull() ?: BlockPos.ORIGIN - - private val modifications = mutableMapOf() - val structureMap: Map - get() { - offset?.let { - val offsetMap = mutableMapOf() - for ((pos, state) in structure) { - offsetMap[pos.add(it)] = state - } - return offsetMap + modifications - } +abstract class Blueprint { + abstract val structure: Structure - return structure + modifications + fun isDone(safeContext: SafeContext) = + structure.all { (pos, targetState) -> + with(safeContext) { + targetState.matches(world.getBlockState(pos), pos, world) + } } companion object { - fun Box.from(targetState: TargetState) = - Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) - - fun BlockBox.from(targetState: TargetState) = - Blueprint(BlockPos.stream(this).map { BlockPos(it) }.toList().associateWith { targetState }) - - fun BlockPos.from(targetState: TargetState) = - Blueprint(setOf(this).associateWith { targetState }) + fun Box.toStructure(targetState: TargetState): Structure = + BlockPos.stream(this) + .map { it.blockPos } + .toList() + .associateWith { targetState } + + fun BlockBox.toStructure(targetState: TargetState): Structure = + BlockPos.stream(this) + .map { it.blockPos } + .toList() + .associateWith { targetState } + + fun BlockPos.toStructure(targetState: TargetState): Structure = + setOf(this) + .associateWith { targetState } // fun Schematic.fromSchematic() = -// Blueprint(this.blockMap.map { it.key to TargetState.BlockState(it.value) }.toMap()) - - fun StructureTemplate.fromStructureTemplate() = - Blueprint( - blockInfoLists - .flatMap { it.all } - .associate { it.pos to TargetState.State(it.state) } - ) +// this.blockMap.map { it.key to TargetState.BlockState(it.value) }.toMap() + + fun StructureTemplate.toStructure(): Structure = + blockInfoLists + .flatMap { it.all } + .associate { it.pos to TargetState.State(it.state) } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt new file mode 100644 index 000000000..ffb3fed46 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -0,0 +1,27 @@ +package com.lambda.interaction.construction + +import com.lambda.context.SafeContext +import com.lambda.util.primitives.extension.Structure +import net.minecraft.util.math.Vec3i + +data class DynamicBlueprint( + val initial: Structure = emptyMap(), + val update: SafeContext.(Structure) -> Structure, +) : Blueprint() { + fun update(safeContext: SafeContext) = + safeContext.update(structure) + + override val structure: Structure by lazy { initial } + + companion object { + fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure = { + it.map { (pos, state) -> + pos.add(offset) to state + }.toMap() + } + + fun Structure.toBlueprint( + update: SafeContext.(Structure) -> Structure + ) = DynamicBlueprint(this, update) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/StaticBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/StaticBlueprint.kt new file mode 100644 index 000000000..35b4020da --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/StaticBlueprint.kt @@ -0,0 +1,11 @@ +package com.lambda.interaction.construction + +import com.lambda.util.primitives.extension.Structure + +data class StaticBlueprint( + override val structure: Structure +) : Blueprint() { + companion object { + fun Structure.toBlueprint() = StaticBlueprint(this) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt new file mode 100644 index 000000000..c3bc81a4e --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -0,0 +1,35 @@ +package com.lambda.interaction.construction.context + +import net.minecraft.block.BlockState +import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d + +data class BreakContext( +// val hitVec: Vec3d, + val hitPos: BlockPos, + val exposedSides: Int, +// val side: Direction, + override val distance: Double, +// var hand: Hand, +// val pointOfView: Vec3d, +// val blockState: BlockState, +) : BuildContext, ComparableContext { +// override val resultingPos = hitPos +// override fun toBlockHitResult() = BlockHitResult(hitVec, side, hitPos, false) + +// val expectedBlockState = blockState.fluidState.blockState + + override fun compareTo(other: ComparableContext): Int { + return when (other) { + is BreakContext -> compareByDescending { + it.exposedSides + }.thenBy { + it.distance + }.compare(this, other) + else -> 1 + } + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt new file mode 100644 index 000000000..ff5e91f25 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -0,0 +1,10 @@ +package com.lambda.interaction.construction.context + +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos + +interface BuildContext { + val distance: Double +// val resultingPos: BlockPos +// fun toBlockHitResult(): BlockHitResult +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/ComparableContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/ComparableContext.kt new file mode 100644 index 000000000..7ab3c2182 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/ComparableContext.kt @@ -0,0 +1,7 @@ +package com.lambda.interaction.construction.context + +interface ComparableContext : Comparable { + override fun compareTo(other: ComparableContext): Int { + return 0 + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt new file mode 100644 index 000000000..bb1a7797f --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -0,0 +1,27 @@ +package com.lambda.interaction.construction.result + +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.task.buildChain +import com.lambda.task.tasks.BreakBlock.Companion.breakBlock + +sealed class BreakResult : BuildResult() { + + /** + * Represents a successful break. All checks have been passed. + * @param context The context of the break. + */ + data class Success(val context: BreakContext) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_SUCCESS + + override val resolve = buildChain { + breakBlock(context.hitPos) + } + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is Success -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt new file mode 100644 index 000000000..47e2ed680 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -0,0 +1,18 @@ +package com.lambda.interaction.construction.result + +abstract class BuildResult : ComparableResult { + + /** + * The build action is done. + */ + data object Done : BuildResult() { + override val rank = Rank.DONE + } + + /** + * The build action is ignored. + */ + data object Ignored : BuildResult() { + override val rank = Rank.IGNORED + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/ComparableResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/ComparableResult.kt new file mode 100644 index 000000000..3b02c3799 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/ComparableResult.kt @@ -0,0 +1,9 @@ +package com.lambda.interaction.construction.result + +sealed interface ComparableResult> : Comparable> { + val rank: T + + override fun compareTo(other: ComparableResult): Int { + return rank.compareTo(other.rank) + } +} diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt new file mode 100644 index 000000000..f969e792a --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -0,0 +1,38 @@ +package com.lambda.interaction.construction.result + +enum class Rank { + // solvable + BREAK_SUCCESS, + PLACE_SUCCESS, + BREAK_WRONG_TOOL, + BREAK_ITEM_CANT_MINE, + PLACE_WRONG_ITEM, + BREAK_NOT_VISIBLE, + PLACE_NOT_VISIBLE, + BREAK_OUT_OF_REACH, + PLACE_OUT_OF_REACH, + BREAK_NOT_EXPOSED, + BREAK_OUT_OF_WORLD, + PLACE_OUT_OF_WORLD, + BREAK_CHUNK_NOT_LOADED, + PLACE_CHUNK_NOT_LOADED, + PLACE_NO_INTEGRITY, + PLACE_CANT_REPLACE, + PLACE_NOT_ITEM_BLOCK, + BREAK_IS_LIQUID, + BREAK_IS_BLOCKED_BY_LIQUID, + BREAK_PLAYER_ON_TOP, + + // not solvable + BREAK_RESTRICTED, + BREAK_UNBREAKABLE, + BREAK_NO_PERMISSION, + PLACE_SCAFFOLD_EXCEEDED, + PLACE_BLOCK_FEATURE_DISABLED, + PLACE_ILLEGAL_USAGE, + + // not an issue + PLACE_IGNORED, + DONE, + IGNORED, +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt new file mode 100644 index 000000000..3b0250934 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt @@ -0,0 +1,7 @@ +package com.lambda.interaction.construction.result + +import com.lambda.task.TaskChain + +interface Resolvable { + val resolve: TaskChain +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index ee01aee4b..7e46f29c3 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -25,7 +25,7 @@ import com.lambda.util.Nameable * * Each [Module] has a [name], which is displayed in-game. * The [description] of the module is shown when hovering over - * the [ModuleButton] in the GUI and in [Command]s. + * the [ModuleButton] in the GUI and in [Commands]s. * The [Module] can be associated with a [Set] of [ModuleTag]s to allow for * easier filtering and searching in the GUI. * diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt index 898ae6e75..f18fe79bd 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/InventoryDebug.kt @@ -4,6 +4,7 @@ import com.lambda.event.events.PacketEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.util.Communication.info import com.lambda.util.DynamicReflectionSerializer.dynamicString import net.minecraft.network.packet.c2s.play.* import net.minecraft.network.packet.s2c.play.InventoryS2CPacket diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 332e63b3c..738acba5b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -1,42 +1,67 @@ package com.lambda.module.modules.player -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.construction.DynamicBlueprint +import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module -import com.lambda.util.Communication.info +import com.lambda.module.tag.ModuleTag +import com.lambda.task.buildChain +import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.util.BlockUtils.blockPos +import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.BlockUtils.safeLiquid import com.lambda.util.KeyCode -import com.lambda.util.math.VecUtils.dist -import com.lambda.util.world.WorldUtils.searchBlocks +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i object Nuker : Module( name = "Nuker", description = "Breaks blocks around you", - defaultKeybind = KeyCode.Comma + defaultKeybind = KeyCode.Comma, + defaultTags = setOf(ModuleTag.PLAYER) ) { private val flatten by setting("Flatten", true) + private val onlyBreakInstant by setting("Only Break Instant", true) + private val doNotExposeLiquids by setting("Do Not Expose Liquids", true) + private val fillFloor by setting("Fill Floor", false) private val range = Vec3i(4, 4, 4) // TODO: Customizable + private val task = buildChain { + buildStructure( + DynamicBlueprint { _ -> + // ToDo: First filter then map + val selection = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) + .asSequence() + .map { it.blockPos } + .filter { !world.isAir(it) } + .filter { !flatten || it.y >= player.blockPos.y } + .filter { !onlyBreakInstant || instantBreakable(world.getBlockState(it), it) } + .filter { !doNotExposeLiquids || safeLiquid(it) } + .toList() + .associateWith { TargetState.Air } + + if (fillFloor) { + // ToDo: Use smarter iteration pattern + val floor = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) + .asSequence() + .map { it.blockPos } + .filter { it.y == player.blockPos.down().y } + .associateWith { TargetState.Solid } + return@DynamicBlueprint selection + floor + } + + selection + }, + pathing = false + ) + } init { - listener { - searchBlocks(player.blockPos, range, null, - iterator = { state, pos, _ -> - state.getCollisionShape(world, pos).boundingBox - .getVisibleSurfaces(player.eyePos).firstOrNull()?.let { - interaction.updateBlockBreakingProgress(pos, it) // Crashes if the time to mine is not instant ?? - } - this@Nuker.info("Breaking ${state.block.name.string} at $pos with hardness ${state.getHardness(world, pos)}") - }, - ) { state, pos -> - state.isSolidBlock(world, pos) - && !state.isAir - && (!flatten || pos.y >= player.y) - //&& state.getHardness(world, pos) <= 1.0f - && state.getHardness(world, pos) > 0.0f - && player.eyePos dist pos.toCenterPos() <= interaction.reachDistance - 1 - } + onEnable { + task.tryRun() + } + + onDisable { + task.cancel() } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt b/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt index 4a1b8b012..5d8a67c54 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/XRay.kt @@ -23,14 +23,14 @@ object XRay : Module( Blocks.ANCIENT_DEBRIS ) - private val selection by setting("Block Selection", defaultBlocks, "Block selection that will be shown (whitelist) or hidden (blacklist)") +// private val selection by setting("Block Selection", defaultBlocks, "Block selection that will be shown (whitelist) or hidden (blacklist)") private val mode by setting("Selection Mode", Selection.WHITELIST, "The mode of the block selection") @JvmStatic fun isSelected(blockState: BlockState) = mode.select(blockState) enum class Selection(val select: (BlockState) -> Boolean) { - WHITELIST({ it.block in selection }), - BLACKLIST({ it.block !in selection }) + WHITELIST({ it.block in defaultBlocks }), + BLACKLIST({ it.block !in defaultBlocks }) } init { diff --git a/common/src/main/kotlin/com/lambda/task/TaskChain.kt b/common/src/main/kotlin/com/lambda/task/TaskChain.kt index 2f0709894..d42da1ab5 100644 --- a/common/src/main/kotlin/com/lambda/task/TaskChain.kt +++ b/common/src/main/kotlin/com/lambda/task/TaskChain.kt @@ -3,7 +3,7 @@ package com.lambda.task import kotlinx.coroutines.ExperimentalCoroutinesApi class TaskChain( - private val steps: List> = listOf() + val steps: List> = listOf() ) { suspend fun run() { steps.forEach { TaskRegistry.run(it) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 85cd924e6..1f016b4e2 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -1,30 +1,21 @@ package com.lambda.task.tasks +import com.lambda.Lambda.LOG +import com.lambda.context.SafeContext import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig -import com.lambda.interaction.rotation.RotationMode -import com.lambda.interaction.visibilty.VisibilityChecker -import com.lambda.interaction.visibilty.VisibilityChecker.bounds import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces -import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder import com.lambda.task.TaskChainBuilder -import com.lambda.util.primitives.extension.component6 -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import net.minecraft.client.particle.DamageParticle -import net.minecraft.client.particle.Particle -import net.minecraft.particle.ParticleEffect -import net.minecraft.particle.ParticleTypes -import net.minecraft.util.Hand +import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -import net.minecraft.util.math.Vec3d class BreakBlock( private val blockPos: BlockPos, @@ -32,37 +23,52 @@ class BreakBlock( private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), private val collectDrop: Boolean = false, + private val noRotationForInstant: Boolean = true, ) : Task() { + val SafeContext.state: BlockState get() = world.getBlockState(blockPos) init { - listener { event -> - event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) - } +// listener { event -> +// if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener +// event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) +// } +// +// listener { +// if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener +// if (!it.context.isValid) return@listener +// val hitResult = it.context.hitResult?.blockResult ?: return@listener +// +// if (interaction.updateBlockBreakingProgress(hitResult.blockPos, hitResult.side)) { +// mc.particleManager.addBlockBreakingParticles(hitResult.blockPos, hitResult.side) +// player.swingHand(Hand.MAIN_HAND) +// } +// } - listener { - if (!it.context.isValid) return@listener - val hitResult = it.context.hitResult?.blockResult ?: return@listener + listener { +// if (!instantBreakable(state, blockPos) || !noRotationForInstant) return@listener + val shape = state.getCollisionShape(world, blockPos) - if (interaction.updateBlockBreakingProgress(hitResult.blockPos, hitResult.side)) { - mc.particleManager.addBlockBreakingParticles(hitResult.blockPos, hitResult.side) - player.swingHand(Hand.MAIN_HAND) + if (shape.isEmpty) { + LOG.info("BreakBlock $blockPos has $state and empty shape") + return@listener } - } -// listener { -// world.getBlockState(blockPos).getCollisionShape(world, blockPos).boundingBoxes.firstOrNull()?.let { box -> -// getVisibleSurfaces(box).firstOrNull()?.let { side -> + + state.getCollisionShape(world, blockPos).boundingBox + .getVisibleSurfaces(player.eyePos).firstOrNull()?.let { side -> + interaction.attackBlock(blockPos, side) // if (interaction.updateBlockBreakingProgress(blockPos, side)) { // mc.particleManager.addBlockBreakingParticles(blockPos, side) //// mc.particleManager.addParticle(ParticleTypes.CRIT, hitResult.pos.x, hitResult.pos.y, hitResult.pos.z, 0.0, 0.0, 0.0) // player.swingHand(Hand.MAIN_HAND) // } -// } -// } -// } + } + } } override suspend fun onAction() { awaitEvent { world.isAir(blockPos) } + +// if (collectDrop) awaitEvent { it.entity is ItemEntity ... } } companion object { @@ -73,7 +79,15 @@ class BreakBlock( interactionConfig: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet(), collectDrop: Boolean = false, - ) = BreakBlock(blockPos, rotationConfig, interactionConfig, sides, collectDrop).apply { + noRotationForInstant: Boolean = true, + ) = BreakBlock( + blockPos, + rotationConfig, + interactionConfig, + sides, + collectDrop, + noRotationForInstant + ).apply { required(this) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 2dcd550b6..83577a7ed 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,21 +1,69 @@ package com.lambda.task.tasks +import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.Blueprint -import com.lambda.interaction.construction.Blueprint.Companion.from +import com.lambda.interaction.construction.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.DynamicBlueprint +import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.result.BreakResult +import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.verify.TargetState import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder import com.lambda.task.TaskChainBuilder +import com.lambda.threading.taskContext +import kotlinx.coroutines.Job import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction class BuildStructure( private val blueprint: Blueprint, private val collectDrops: Boolean = false, private val skipWeakBlocks: Boolean = false, private val pathing: Boolean = true, + private val limitPerTick: Int = 20 ) : Task() { + private var currentJob: Job? = null + private var lastResult: BuildResult? = null + + init { + listener { + val structure = when (blueprint) { + is DynamicBlueprint -> blueprint.update(this) + else -> blueprint.structure + } + + val results = structure.entries.fold(emptySet()) { acc, (pos, state) -> + val buildContext = BreakContext( + pos, + Direction.entries.count { world.isAir(pos.offset(it)) }, + player.pos.squaredDistanceTo(pos.toCenterPos()) + ) + + acc + BreakResult.Success(buildContext) + } + + results.sorted().take(limitPerTick).forEach { result -> + if (lastResult == result) return@forEach + if (result !is Resolvable) return@forEach + + lastResult = result +// currentJob?.cancel() + currentJob = taskContext { + result.resolve.steps.forEach { it.execute() } + } + } + } + } + override suspend fun onAction() { - blueprint.structureMap + awaitEvent { + blueprint.isDone(this) && false + } } companion object { @@ -37,7 +85,10 @@ class BuildStructure( @TaskCha1nBuilder fun TaskChainBuilder.breakAndCollectBlock( blockPos: BlockPos - ) = BuildStructure(blockPos.from(TargetState.Air), collectDrops = true).apply { + ) = BuildStructure( + blockPos.toStructure(TargetState.Air).toBlueprint(), + collectDrops = true + ).apply { required(this) } } diff --git a/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt index c4cb61943..08e996d52 100644 --- a/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt +++ b/common/src/main/kotlin/com/lambda/threading/MainThreadInit.kt @@ -8,7 +8,7 @@ class MainThreadInit (private val initializer: () -> T) { operator fun getValue(thisRef: Any?, property: KProperty<*>) = value init { - runGameConcurrent { + runGameScheduled { value = initializer() } } diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 4270a56d4..403bb66bd 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -78,7 +78,7 @@ inline fun runSafeConcurrent(crossinline block: SafeContext.() -> Unit) { * * @param block The task to be executed on the game's main thread. */ -inline fun runGameConcurrent(crossinline block: () -> Unit) { +inline fun runGameScheduled(crossinline block: () -> Unit) { mc.executeSync { block() } } @@ -100,7 +100,7 @@ inline fun runGameConcurrent(crossinline block: () -> Unit) { * @param block The task to be executed on the game's main thread within a safe context. */ inline fun runSafeGameConcurrent(crossinline block: SafeContext.() -> Unit) { - runGameConcurrent { runSafe { block() } } + runGameScheduled { runSafe { block() } } } /** diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 8fd5d9ee7..8b3d4966c 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -1,15 +1,15 @@ package com.lambda.util +import com.lambda.context.SafeContext +import com.lambda.util.Communication.info import com.lambda.util.item.ItemUtils.block import com.lambda.util.item.ItemUtils.shulkerBoxes import net.minecraft.block.Block +import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.fluid.Fluids import net.minecraft.item.Item -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction -import net.minecraft.util.math.EightWayDirection -import net.minecraft.util.math.Vec3d +import net.minecraft.util.math.* import kotlin.math.floor object BlockUtils { @@ -96,10 +96,19 @@ object BlockUtils { val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns + fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { + val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) +// info("State: $blockState Ticks to break: $ticksNeeded") + return ticksNeeded <= 1 && ticksNeeded != 0f + } + fun SafeContext.safeLiquid(blockPos: BlockPos) = Direction.entries.all { + if (it == Direction.UP) return@all true + world.getFluidState(blockPos.offset(it)).isEmpty + } + val Vec3i.blockPos: BlockPos get() = BlockPos(this) val Block.item: Item get() = asItem() - val Vec3d.blockPos: BlockPos get() = BlockPos(floor(x).toInt(), floor(y).toInt(), floor(z).toInt()) + val Vec3d.flooredPos: BlockPos get() = BlockPos(floor(x).toInt(), floor(y).toInt(), floor(z).toInt()) fun BlockPos.vecOf(direction: Direction): Vec3d = toCenterPos().add(Vec3d.of(direction.vector).multiply(0.5)) fun BlockPos.offset(eightWayDirection: EightWayDirection, amount: Int): BlockPos = add(eightWayDirection.offsetX * amount, 0, eightWayDirection.offsetZ * amount) - } diff --git a/common/src/main/kotlin/com/lambda/util/primitives/extension/Aliases.kt b/common/src/main/kotlin/com/lambda/util/primitives/extension/Aliases.kt index 5d00a898e..d59ea9904 100644 --- a/common/src/main/kotlin/com/lambda/util/primitives/extension/Aliases.kt +++ b/common/src/main/kotlin/com/lambda/util/primitives/extension/Aliases.kt @@ -1,6 +1,9 @@ package com.lambda.util.primitives.extension +import com.lambda.interaction.construction.verify.TargetState import com.mojang.brigadier.builder.LiteralArgumentBuilder import net.minecraft.command.CommandSource +import net.minecraft.util.math.BlockPos -typealias CommandBuilder = LiteralArgumentBuilder \ No newline at end of file +typealias CommandBuilder = LiteralArgumentBuilder +typealias Structure = Map \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 14469b245..2524f1ab6 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -1,6 +1,7 @@ package com.lambda.util.world import com.lambda.context.SafeContext +import com.lambda.util.BlockUtils.blockPos import com.lambda.util.collections.filterPointer import net.minecraft.block.Block import net.minecraft.block.BlockState @@ -91,11 +92,11 @@ object WorldUtils { type: Class, // This is a class reference, not an instance of the class. pos: Vec3d, distance: Double, - pointer: MutableList? = null, + collector: MutableList? = null, iterator: (Entity, Int) -> Unit = { _, _ -> }, predicate: (Entity) -> Boolean = { true }, ) { - return getFastEntities(pos, distance, pointer, iterator, predicate) + return getFastEntities(pos, distance, collector, iterator, predicate) } /** @@ -144,8 +145,10 @@ object WorldUtils { for (x in sectionX - chunks..sectionX + chunks) { for (y in sectionY - chunks..sectionY + chunks) { for (z in sectionZ - chunks..sectionZ + chunks) { - val section = - world.entityManager.cache.findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue + val section = world + .entityManager + .cache + .findTrackingSection(ChunkSectionPos.asLong(x, y, z)) ?: continue section.collection.filterPointer(pointer, iterator) { entity -> entity != player && @@ -203,8 +206,8 @@ object WorldUtils { * @param rangeY The maximum distance to search for entities in the y-axis. * @param rangeZ The maximum distance to search for entities in the z-axis. * @param pointer The mutable list to store the positions in. - * @param iterator Iterator to perform operations on each block. * @param predicate Predicate to filter the blocks. + * @param iterator Iterator to perform operations on each block. */ inline fun SafeContext.searchBlocks( pos: Vec3i, @@ -212,9 +215,9 @@ object WorldUtils { rangeY: Int, rangeZ: Int, pointer: MutableList? = null, - iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, predicate: (BlockState, BlockPos) -> Boolean = { _, _ -> true }, - ) = searchBlocks(pos, Vec3i(rangeX, rangeY, rangeZ), pointer, iterator, predicate) + iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, + ) = searchBlocks(pos, Vec3i(rangeX, rangeY, rangeZ), pointer, predicate, iterator) /** * Returns all the position within the range where the predicate is true. @@ -229,8 +232,8 @@ object WorldUtils { pos: Vec3i, range: Vec3i, pointer: MutableList? = null, - iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, predicate: (BlockState, BlockPos) -> Boolean = { _, _ -> true }, + iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, ) { iteratePositions(pos, range) { blockPos, index -> val state = world.getBlockState(blockPos) @@ -246,21 +249,21 @@ object WorldUtils { * * @param pos The position to search from. * @param range The maximum distance to search for fluids in each axis. - * @param pointer The mutable list to store the positions in. + * @param collector The mutable list to store the positions in. * @param iterator Iterator to perform operations on each fluid. * @param predicate Predicate to filter the fluids. */ inline fun SafeContext.searchFluids( pos: Vec3i, range: Vec3i, - pointer: MutableList? = null, + collector: MutableList? = null, iterator: (FluidState, BlockPos, Int) -> Unit = { _, _, _ -> }, predicate: (FluidState, BlockPos) -> Boolean = { _, _ -> true }, ) { iteratePositions(pos, range) { blockPos, index -> val state = world.getFluidState(blockPos) if (predicate(state, blockPos)) { - pointer?.add(state.fluid as? T ?: return@iteratePositions) + collector?.add(state.fluid as? T ?: return@iteratePositions) iterator(state, blockPos, index) } } @@ -272,12 +275,12 @@ object WorldUtils { * @param range The maximum distance to search for entities in each axis. * @param iterator Iterator to perform operations on each position. */ - inline fun SafeContext.iteratePositions( + inline fun iteratePositions( pos: Vec3i, range: Vec3i, iterator: (BlockPos, Int) -> Unit, ) { - BlockPos.iterateOutwards(BlockPos(pos), range.x, range.y, range.z) + BlockPos.iterateOutwards(pos.blockPos, range.x, range.y, range.z) .forEachIndexed { index, blockPos -> iterator(blockPos, index) } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 841c8997a..380733fe1 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -46,7 +46,5 @@ accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPa accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractAtHandler # Other -accessible field net/minecraft/client/world/ClientEntityManager cache Lnet/minecraft/world/entity/SectionedEntityCache; -accessible field net/minecraft/world/entity/EntityTrackingSection collection Lnet/minecraft/util/collection/TypeFilterableList; accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; \ No newline at end of file From 0a45e446fcd99755340b9a2a092e047a18dbfdf1 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 26 May 2024 04:09:52 +0200 Subject: [PATCH 07/47] Synchronous indirect task tree traversal --- .../lambda/mixin/world/ClientWorldMixin.java | 18 + .../src/main/kotlin/com/lambda/core/Loader.kt | 3 - .../com/lambda/event/events/RotationEvent.kt | 3 +- .../com/lambda/event/events/WorldEvent.kt | 12 + .../com/lambda/event/listener/SafeListener.kt | 2 +- .../interaction/construction/Blueprint.kt | 5 +- .../construction/context/BreakContext.kt | 4 +- .../construction/result/BreakResult.kt | 7 +- .../construction/result/Resolvable.kt | 4 +- .../construction/verify/TargetState.kt | 3 +- .../interaction/material/MaterialContainer.kt | 23 +- .../material/container/ChestContainer.kt | 36 +- .../material/container/CreativeContainer.kt | 46 +-- .../material/container/EnderChestContainer.kt | 53 ++- .../material/container/HotbarContainer.kt | 15 +- .../material/container/InventoryContainer.kt | 10 +- .../material/container/MainHandContainer.kt | 11 +- .../material/container/OffHandContainer.kt | 6 +- .../material/container/ShulkerBoxContainer.kt | 37 +- .../material/container/StashContainer.kt | 10 +- .../material/transfer/TransferResult.kt | 4 +- .../main/kotlin/com/lambda/module/Module.kt | 1 + .../com/lambda/module/modules/player/Nuker.kt | 66 ++-- .../kotlin/com/lambda/task/OptionalTask.kt | 13 - .../src/main/kotlin/com/lambda/task/Task.kt | 356 +++++++++++++----- .../kotlin/com/lambda/task/TaskBuilder.kt | 50 +-- .../main/kotlin/com/lambda/task/TaskChain.kt | 27 -- .../kotlin/com/lambda/task/TaskRegistry.kt | 46 --- .../com/lambda/task/tasks/AcquireMaterial.kt | 20 +- .../com/lambda/task/tasks/BreakBlock.kt | 99 +++-- .../com/lambda/task/tasks/BuildStructure.kt | 60 +-- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 22 +- .../com/lambda/task/tasks/HelloWorldTask.kt | 19 +- .../com/lambda/task/tasks/InteractBlock.kt | 37 -- .../com/lambda/task/tasks/InventoryTask.kt | 44 +-- .../com/lambda/task/tasks/LookAtBlock.kt | 46 --- .../com/lambda/task/tasks/OpenContainer.kt | 55 ++- .../com/lambda/task/tasks/PlaceContainer.kt | 10 +- .../main/kotlin/com/lambda/util/BlockUtils.kt | 5 +- .../main/kotlin/com/lambda/util/Formatting.kt | 2 +- .../com/lambda/util/combat/Explosion.kt | 6 +- .../com/lambda/util/world/WorldUtils.kt | 3 +- .../main/resources/lambda.mixins.common.json | 3 +- 43 files changed, 635 insertions(+), 667 deletions(-) create mode 100644 common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java create mode 100644 common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/OptionalTask.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/TaskChain.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/TaskRegistry.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt delete mode 100644 common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt diff --git a/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java new file mode 100644 index 000000000..99456258b --- /dev/null +++ b/common/src/main/java/com/lambda/mixin/world/ClientWorldMixin.java @@ -0,0 +1,18 @@ +package com.lambda.mixin.world; + +import com.lambda.event.EventFlow; +import com.lambda.event.events.WorldEvent; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ClientWorld.class) +public class ClientWorldMixin { + @Inject(method = "addEntity", at = @At("HEAD"), cancellable = true) + private void addEntity(Entity entity, CallbackInfo ci) { + if (EventFlow.post(new WorldEvent.EntitySpawn(entity)).isCanceled()) ci.cancel(); + } +} diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 433b6dd46..54a73eba7 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -10,7 +10,6 @@ import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager import com.lambda.module.ModuleRegistry import com.lambda.sound.SoundRegistry -import com.lambda.task.TaskRegistry import com.lambda.util.Communication.ascii import kotlin.system.measureTimeMillis @@ -42,7 +41,5 @@ object Loader { } LOG.info("${Lambda.MOD_NAME} ${Lambda.VERSION} was successfully initialized (${initTime}ms)") - - TaskRegistry } } diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index b48722617..5ac3f4cea 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -10,6 +10,7 @@ import com.lambda.interaction.rotation.RotationContext import com.lambda.interaction.visibilty.VisibilityChecker.findRotation import com.lambda.module.modules.client.TaskFlow import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult import com.lambda.util.world.raycast.RayCastUtils.entityResult import net.minecraft.entity.Entity @@ -55,7 +56,7 @@ abstract class RotationEvent : Event { interactionConfig: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet() ) = runSafe { - val state = world.getBlockState(blockPos) + val state = blockPos.blockState(world) val voxelShape = state.getOutlineShape(world, blockPos) val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } findRotation(rotationConfig, interactionConfig, boundingBoxes, sides) { diff --git a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt new file mode 100644 index 000000000..b1d72af60 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt @@ -0,0 +1,12 @@ +package com.lambda.event.events + +import com.lambda.event.Event +import com.lambda.event.callback.Cancellable +import com.lambda.event.callback.ICancellable +import net.minecraft.entity.Entity + +abstract class WorldEvent : Event { + class EntitySpawn( + val entity: Entity + ) : WorldEvent(), ICancellable by Cancellable() +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt index d1fbc86b6..7f24f3430 100644 --- a/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt +++ b/common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt @@ -126,7 +126,7 @@ class SafeListener( noinline function: SafeContext.(T) -> Unit, ): SafeListener { val listener = SafeListener(priority, this, alwaysListen) { event -> - function(event as T) + function(event as T) // ToDo: run function always on game thread } syncListeners.subscribe(listener) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt index 9820b9373..297ec3ab9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt @@ -3,6 +3,7 @@ package com.lambda.interaction.construction import com.lambda.context.SafeContext import com.lambda.interaction.construction.verify.TargetState import com.lambda.util.BlockUtils.blockPos +import com.lambda.util.BlockUtils.blockState import com.lambda.util.primitives.extension.Structure import net.minecraft.structure.StructureTemplate import net.minecraft.util.math.BlockBox @@ -12,10 +13,10 @@ import net.minecraft.util.math.Box abstract class Blueprint { abstract val structure: Structure - fun isDone(safeContext: SafeContext) = + open fun isDone(safeContext: SafeContext) = structure.all { (pos, targetState) -> with(safeContext) { - targetState.matches(world.getBlockState(pos), pos, world) + targetState.matches(pos.blockState(world), pos, world) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index c3bc81a4e..ecdb2e723 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -24,9 +24,7 @@ data class BreakContext( override fun compareTo(other: ComparableContext): Int { return when (other) { - is BreakContext -> compareByDescending { - it.exposedSides - }.thenBy { + is BreakContext -> compareBy { it.distance }.compare(this, other) else -> 1 diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index bb1a7797f..b14777dc0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -1,8 +1,7 @@ package com.lambda.interaction.construction.result import com.lambda.interaction.construction.context.BreakContext -import com.lambda.task.buildChain -import com.lambda.task.tasks.BreakBlock.Companion.breakBlock +import com.lambda.task.tasks.BreakBlock.Companion.uncheckedBreak sealed class BreakResult : BuildResult() { @@ -13,9 +12,7 @@ sealed class BreakResult : BuildResult() { data class Success(val context: BreakContext) : Resolvable, BreakResult() { override val rank = Rank.BREAK_SUCCESS - override val resolve = buildChain { - breakBlock(context.hitPos) - } + override val resolve = uncheckedBreak(context.hitPos) override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt index 3b0250934..9bfb609c2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt @@ -1,7 +1,7 @@ package com.lambda.interaction.construction.result -import com.lambda.task.TaskChain +import com.lambda.task.Task interface Resolvable { - val resolve: TaskChain + val resolve: Task<*> } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 0414cd1a8..f4b749b46 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -1,5 +1,6 @@ package com.lambda.interaction.construction.verify +import com.lambda.util.BlockUtils.blockState import com.lambda.util.item.ItemUtils.block import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld @@ -23,7 +24,7 @@ sealed class TargetState : StateMatcher { } data class Support(val direction: Direction) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = - world.getBlockState(pos.offset(direction)).isSolidBlock(world, pos.offset(direction)) + pos.offset(direction).blockState(world).isSolidBlock(world, pos.offset(direction)) override fun getStack(world: ClientWorld, pos: BlockPos) = ItemStack(Items.NETHERRACK) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 6858c8c14..83ada9673 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -3,19 +3,14 @@ package com.lambda.interaction.material import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ShulkerBoxContainer import com.lambda.interaction.material.transfer.TransferResult -import com.lambda.task.TaskChain -import com.lambda.task.emptyChain +import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty import com.lambda.util.item.ItemStackUtils.shulkerBoxContents import com.lambda.util.item.ItemStackUtils.spaceLeft import com.lambda.util.item.ItemUtils -import com.lambda.util.primitives.extension.containerSlots -import net.minecraft.client.gui.screen.ingame.HandledScreen -import net.minecraft.entity.player.PlayerInventory import net.minecraft.item.ItemStack -import net.minecraft.screen.slot.Slot -import java.util.concurrent.ConcurrentSkipListSet // ToDo: Make jsonable to persistently store them abstract class MaterialContainer( @@ -30,17 +25,17 @@ abstract class MaterialContainer( /** * Brings the player into a withdrawal/deposit state. E.g.: move to a chest etc. */ - open fun prepare(): TaskChain = emptyChain() + open fun prepare(): Task<*> = emptyTask() /** * Withdraws items from the container to the player's inventory. */ - abstract fun withdraw(selection: StackSelection): TaskChain + abstract fun withdraw(selection: StackSelection): Task<*> /** * Deposits items from the player's inventory into the container. */ - abstract fun deposit(selection: StackSelection): TaskChain + abstract fun deposit(selection: StackSelection): Task<*> open fun filter(selection: StackSelection) = selection.filterStacks(stacks) @@ -68,9 +63,13 @@ abstract class MaterialContainer( val transferAmount = minOf(amount, space) selector = { true } count = transferAmount - val chain = prepare() + withdraw(this) + destination.prepare() + destination.deposit(this) - return TransferResult.Success(chain) + return TransferResult.Success(emptyTask().withSubTasks { + prepare() + withdraw(this@transfer) + destination.prepare() + deposit(this@transfer) + }) } fun List.doShulkerCheck() = diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt index 8c04560b9..94fdd4456 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -2,12 +2,10 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.buildChain import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer -import com.lambda.threading.runGameBlocking import com.lambda.util.Communication.info import net.minecraft.block.ChestBlock import net.minecraft.item.ItemStack @@ -19,36 +17,34 @@ data class ChestContainer( override var stacks: List, val blockPos: BlockPos, ) : MaterialContainer(Rank.CHEST) { - override fun prepare() = buildChain { - moveIntoEntityRange(blockPos) - required { - runGameBlocking { - if (ChestBlock.isChestBlocked(world, blockPos)) { - throw ChestBlockedException() - } + 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() } } - } - override fun withdraw(selection: StackSelection) = buildChain { + override fun withdraw(selection: StackSelection) = openContainer(blockPos) .withMaxAttempts(3) - .withTimeout(1000L) - .onSuccess { screen -> + .withTimeout(20) + .onSuccess { open, screen -> info("Withdrawing $selection from ${screen.type}") - withdraw(screen, selection) + withdraw(screen, selection).start(open) } - } - override fun deposit(selection: StackSelection) = buildChain { + override fun deposit(selection: StackSelection) = openContainer(blockPos) .withMaxAttempts(3) - .withTimeout(1000L) - .onSuccess { screen -> + .withTimeout(20) + .onSuccess { open, screen -> info("Depositing $selection to ${screen.type}") - deposit(screen, selection) + deposit(screen, selection).start(open) } - } class ChestBlockedException: Exception("The chest is blocked by another block or a cat") class UnexpectedScreen(screenHandler: ScreenHandler): Exception("Unexpected screen. Got ${screenHandler.type}") diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt index 7d02a9a88..922584144 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt @@ -1,12 +1,9 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer -import com.lambda.module.modules.client.TaskFlow import com.lambda.interaction.material.StackSelection -import com.lambda.task.buildTask -import com.lambda.threading.runGameBlocking +import com.lambda.task.Task.Companion.buildTask import com.lambda.util.item.ItemStackUtils.equal -import kotlinx.coroutines.delay import net.minecraft.item.ItemStack data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { @@ -16,38 +13,31 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { if (selection.optimalStack != null) Int.MAX_VALUE else 0 override fun deposit(selection: StackSelection) = buildTask { - runGameBlocking { + if (!player.isCreative) { + // ToDo: Maybe switch gamemode? + throw NotInCreativeModeException() + } + + interaction.clickCreativeStack( + ItemStack.EMPTY, + 36 + player.inventory.selectedSlot + ) + } + + // Withdraws items from the creative menu to the player's main hand + override fun withdraw(selection: StackSelection) = buildTask { + selection.optimalStack?.let { optimalStack -> + if (player.mainHandStack.equal(optimalStack)) return@buildTask + if (!player.isCreative) { // ToDo: Maybe switch gamemode? throw NotInCreativeModeException() } interaction.clickCreativeStack( - ItemStack.EMPTY, + optimalStack, 36 + player.inventory.selectedSlot ) - } - } - - // Withdraws items from the creative menu to the player's main hand - override fun withdraw(selection: StackSelection) = buildTask { - selection.optimalStack?.let { optimalStack -> - runGameBlocking { - if (player.mainHandStack.equal(optimalStack)) { - return@runGameBlocking - } - - if (!player.isCreative) { - // ToDo: Maybe switch gamemode? - throw NotInCreativeModeException() - } - - interaction.clickCreativeStack( - optimalStack, - 36 + player.inventory.selectedSlot - ) - } - delay(TaskFlow.itemMoveDelay) return@buildTask } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index fbb514ccb..72077ff38 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -3,11 +3,13 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.select -import com.lambda.task.buildChain +import com.lambda.task.Task import com.lambda.task.tasks.AcquireMaterial.Companion.acquireStack import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange +import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer +import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer import net.minecraft.block.Blocks import net.minecraft.item.ItemStack import net.minecraft.item.Items @@ -18,36 +20,33 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { override var stacks = emptyList() private var placePos: BlockPos? = null - override fun prepare() = buildChain { - required { -// findBlock(Blocks.ENDER_CHEST).onSuccess { pos -> -// moveIntoEntityRange(pos) -// placePos = pos -// }.onFailure { -// acquireStack(Items.ENDER_CHEST.select()).onSuccess { stack -> -// placeContainer(stack).onSuccess { pos -> -// placePos = pos -// } + 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 // } // } - } - } +// } - override fun withdraw(selection: StackSelection) = buildChain { - placePos?.let { blockPos -> - openContainer(blockPos) - .onSuccess { screen -> - withdraw(screen, selection) - } - } + override fun withdraw(selection: StackSelection): Task<*> { + val pos = placePos ?: return Task.emptyTask() + return openContainer(pos) + .onSuccess { _, screen -> + withdraw(screen, selection) + } } - override fun deposit(selection: StackSelection) = buildChain { - placePos?.let { - openContainer(it) - .onSuccess { screen -> - withdraw(screen, selection) - } - } + override fun deposit(selection: StackSelection): Task<*> { + val pos = placePos ?: return Task.emptyTask() + return openContainer(pos) + .onSuccess { _, screen -> + deposit(screen, selection) + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt index 641cb4f8a..f55f1053c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -3,10 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.TaskChain -import com.lambda.task.buildTask -import com.lambda.task.emptyChain -import com.lambda.threading.runGameBlocking +import com.lambda.task.Task import com.lambda.util.player.SlotUtils.hotbar import net.minecraft.item.ItemStack @@ -15,9 +12,11 @@ object HotbarContainer : MaterialContainer(Rank.HOTBAR) { get() = mc.player?.hotbar ?: emptyList() set(_) {} - override fun prepare() = emptyChain() + override fun withdraw(selection: StackSelection): Task<*> { + TODO("Not yet implemented") + } - override fun withdraw(selection: StackSelection) = emptyChain() - - override fun deposit(selection: StackSelection) = emptyChain() + override fun deposit(selection: StackSelection): Task<*> { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt index 1e16b6053..6dc4f7f80 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -3,7 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.emptyChain +import com.lambda.task.Task import com.lambda.util.player.SlotUtils.combined import net.minecraft.item.ItemStack @@ -12,7 +12,11 @@ object InventoryContainer : MaterialContainer(Rank.INVENTORY) { get() = mc.player?.combined ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection) = emptyChain() + override fun withdraw(selection: StackSelection): Task<*> { + TODO("Not yet implemented") + } - override fun deposit(selection: StackSelection) = emptyChain() + override fun deposit(selection: StackSelection): Task<*> { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index 105d69baf..a754c2c2c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -4,8 +4,7 @@ import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.TaskChain -import com.lambda.task.buildChain +import com.lambda.task.Task import com.lambda.util.player.SlotUtils.combined import com.lambda.util.player.SlotUtils.hotbar import kotlinx.coroutines.delay @@ -19,7 +18,9 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection) = buildChain { + override fun withdraw(selection: StackSelection): Task<*> { + TODO("Not yet implemented") + } // InventoryContainer.stacks.filter(selection.selector).take(selection.count).forEach { stack -> // if (ItemStack.areEqual(stack, player.mainHandStack)) { // return@forEach @@ -46,9 +47,9 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { // interaction.pickFromInventory(player.combined.indexOf(stack)) // delay(TaskFlow.itemMoveDelay) // } - } +// } - override fun deposit(selection: StackSelection): TaskChain { + override fun deposit(selection: StackSelection): Task<*> { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt index 2d0350109..7a4257551 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt @@ -3,7 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.TaskChain +import com.lambda.task.Task import net.minecraft.item.ItemStack object OffHandContainer : MaterialContainer(Rank.OFF_HAND) { @@ -11,11 +11,11 @@ object OffHandContainer : MaterialContainer(Rank.OFF_HAND) { get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection): TaskChain { + override fun withdraw(selection: StackSelection): Task<*> { TODO("Not yet implemented") } - override fun deposit(selection: StackSelection): TaskChain { + override fun deposit(selection: StackSelection): Task<*> { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt index ce11c59da..48fa3ba59 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -2,14 +2,14 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.buildChain +import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock +import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer import com.lambda.util.Communication.info import net.minecraft.item.ItemStack -import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ShulkerBoxScreenHandler import net.minecraft.util.math.BlockPos @@ -21,32 +21,29 @@ data class ShulkerBoxContainer( private var openScreen: ShulkerBoxScreenHandler? = null private var placePosition: BlockPos? = null - override fun prepare() = buildChain { - placeContainer(shulkerStack).onSuccess { placePos -> + override fun prepare() = + placeContainer(shulkerStack).onSuccess { _, placePos -> placePosition = placePos - openContainer(placePos).onSuccess { screen -> + openContainer(placePos).onSuccess { _, screen -> openScreen = screen } } - } - override fun withdraw(selection: StackSelection) = buildChain { - openScreen?.let { screen -> - info("Withdrawing $selection from ${shulkerStack.name.string}") - withdraw(screen, selection) - } - placePosition?.let { - breakAndCollectBlock(it) + override fun withdraw(selection: StackSelection): Task<*> { + val open = openScreen ?: return Task.emptyTask() + val place = placePosition ?: return Task.emptyTask() + info("Withdrawing $selection from ${shulkerStack.name.string}") + return withdraw(open, selection).onSuccess { withdraw, _ -> + breakAndCollectBlock(place).start(withdraw) } } - override fun deposit(selection: StackSelection) = buildChain { - openScreen?.let { screen -> - info("Depositing $selection to shulker box ${shulkerStack.name.string}") - withdraw(screen, selection) - } - placePosition?.let { - breakAndCollectBlock(it) + override fun deposit(selection: StackSelection): Task<*> { + val open = openScreen ?: return Task.emptyTask() + val place = placePosition ?: return Task.emptyTask() + info("Depositing $selection to ${shulkerStack.name.string}") + return deposit(open, selection).onSuccess { deposit, _ -> + breakAndCollectBlock(place).start(deposit) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt index 924cb195f..5e2bb22c2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt @@ -2,7 +2,7 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.buildChain +import com.lambda.task.Task import net.minecraft.item.ItemStack import net.minecraft.util.math.Box @@ -14,15 +14,11 @@ data class StashContainer( get() = chests.flatMap { it.stacks } set(_) {} - override fun prepare() = buildChain { + override fun withdraw(selection: StackSelection): Task<*> { TODO("Not yet implemented") } - override fun withdraw(selection: StackSelection) = buildChain { - TODO("Not yet implemented") - } - - override fun deposit(selection: StackSelection) = buildChain { + override fun deposit(selection: StackSelection): Task<*> { TODO("Not yet implemented") } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index 4db95a9c3..de71c9b0a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -1,10 +1,10 @@ package com.lambda.interaction.material.transfer -import com.lambda.task.TaskChain +import com.lambda.task.Task sealed class TransferResult { data class Success( - val taskChain: TaskChain + val task: Task<*> ) : TransferResult() data object NoSpace : TransferResult() // ToDo: Needs inventory space resolver. compressing or disposing data class MissingItems(val missing: Int) : TransferResult() // ToDo: Find other satisfying permutations diff --git a/common/src/main/kotlin/com/lambda/module/Module.kt b/common/src/main/kotlin/com/lambda/module/Module.kt index 7e46f29c3..f8285eaf1 100644 --- a/common/src/main/kotlin/com/lambda/module/Module.kt +++ b/common/src/main/kotlin/com/lambda/module/Module.kt @@ -15,6 +15,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.event.listener.UnsafeListener import com.lambda.gui.impl.clickgui.LambdaClickGui import com.lambda.module.tag.ModuleTag +import com.lambda.task.Task import com.lambda.util.KeyCode import com.lambda.util.Nameable diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 738acba5b..1061b8cdc 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -4,11 +4,13 @@ import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.buildChain +import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.safeLiquid +import com.lambda.util.BlockUtils.blockState import com.lambda.util.KeyCode import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i @@ -25,43 +27,49 @@ object Nuker : Module( private val fillFloor by setting("Fill Floor", false) private val range = Vec3i(4, 4, 4) // TODO: Customizable - private val task = buildChain { - buildStructure( - DynamicBlueprint { _ -> - // ToDo: First filter then map - val selection = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) - .asSequence() - .map { it.blockPos } - .filter { !world.isAir(it) } - .filter { !flatten || it.y >= player.blockPos.y } - .filter { !onlyBreakInstant || instantBreakable(world.getBlockState(it), it) } - .filter { !doNotExposeLiquids || safeLiquid(it) } - .toList() - .associateWith { TargetState.Air } + private var task: Task<*> = emptyTask() - if (fillFloor) { - // ToDo: Use smarter iteration pattern - val floor = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) + init { + onEnable { + task = buildStructure( + pathing = false, + finishOnDone = false + ) { + DynamicBlueprint { _ -> + val selection = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) .asSequence() .map { it.blockPos } - .filter { it.y == player.blockPos.down().y } - .associateWith { TargetState.Solid } - return@DynamicBlueprint selection + floor - } + .filter { !world.isAir(it) } + .filter { !flatten || it.y >= player.blockPos.y } + .filter { !onlyBreakInstant || instantBreakable(it.blockState(world), it) } + .filter { !doNotExposeLiquids || safeLiquid(it) } + .associateWith { TargetState.Air } - selection - }, - pathing = false - ) - } +// if (fillFloor) { +// // ToDo: Use smarter iteration pattern +// val floor = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) +// .filter { it.y == player.blockPos.down().y } +// .map { it.blockPos } +// .associateWith { TargetState.Solid } +// return@DynamicBlueprint selection + floor +// } - init { - onEnable { - task.tryRun() + selection + } + } + task.start(null) } onDisable { task.cancel() } + +// listener { +// task?.let { +// if (!it.isRunning) return@listener +// +// info(it.info) +// } +// } } } diff --git a/common/src/main/kotlin/com/lambda/task/OptionalTask.kt b/common/src/main/kotlin/com/lambda/task/OptionalTask.kt deleted file mode 100644 index 6f2b93401..000000000 --- a/common/src/main/kotlin/com/lambda/task/OptionalTask.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.lambda.task - -abstract class OptionalTask( - val predicate: () -> Boolean -) : Task() { - companion object { - fun (suspend () -> T).toOptionalTask( - predicate: () -> Boolean - ) = object : OptionalTask(predicate) { - override suspend fun onAction(): T = this@toOptionalTask() - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index d313acdd6..6663e3490 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,15 +1,25 @@ package com.lambda.task import com.lambda.context.SafeContext +import com.lambda.event.Event import com.lambda.event.EventFlow import com.lambda.event.Subscriber +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.threading.runSafe import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info import com.lambda.util.Communication.logError import com.lambda.util.Communication.warn import com.lambda.util.Nameable +import com.lambda.util.text.buildText +import com.lambda.util.text.color +import com.lambda.util.text.literal +import com.lambda.util.text.text import kotlinx.coroutines.* +import net.minecraft.text.Text +import org.apache.commons.lang3.time.DurationFormatUtils +import java.awt.Color /** * A [Task] represents a time-critical activity that executes a suspending action function. @@ -33,8 +43,8 @@ import kotlinx.coroutines.* * * @property delay The delay before the task starts, in milliseconds. * @property timeout The maximum time that the task is allowed to run, in milliseconds. - * @property maxAttempts The maximum number of attempts to execute the task before it is considered failed. - * @property repeats The number of times the task should be repeated. + * @property tries The maximum number of attempts to execute the task before it is considered failed. + * @property executions The number of times the task should be repeated. * @property onSuccess The action to be performed when the task completes successfully. * @property onRetry The action to be performed when the task is retried after a failure or timeout. * @property onTimeout The action to be performed when the task times out. @@ -42,113 +52,203 @@ import kotlinx.coroutines.* * @property onException The action to be performed when the task encounters an exception. */ abstract class Task( - private var delay: Long = 0L, - private var timeout: Long = Long.MAX_VALUE, - private var maxAttempts: Int = 1, - open var repeats: Int = 1, - private var onSuccess: (suspend Task.(Result) -> Unit) = {}, - private var onRetry: (suspend Task.() -> Unit) = {}, - private var onTimeout: (suspend Task.() -> Unit) = {}, - private var onRepeat: (suspend Task.(Int) -> Unit) = {}, - private var onException: (suspend Task.(Throwable) -> Unit) = {}, + private var delay: Int = 0, + private var timeout: Int = Int.MAX_VALUE, + private var tries: Int = 1, + private var repeats: Int = 1, + private var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> }, + private var onRetry: SafeContext.(Task) -> Unit = {}, + private var onTimeout: SafeContext.(Task) -> Unit = {}, + private var onRepeat: SafeContext.(Task, Result, Int) -> Unit = { _, _, _ -> }, + private var onException: SafeContext.(Task, Throwable) -> Unit = { _, _ -> }, ) : Nameable { - private val creationTimestamp = System.currentTimeMillis() - val age: Long get() = System.currentTimeMillis() - creationTimestamp - override val name: String get() = this::class.simpleName ?: "Task" + private var parent: Task<*>? = null + + private var executions = 0 + private var attempted = 0 + private val subTasks = mutableListOf>() + private var state = State.IDLE + var age: Int = 0 + + private val isDeactivated get() = state == State.DEACTIVATED + val isActivated get() = state == State.ACTIVATED + val isRunning get() = state == State.ACTIVATED || state == State.DEACTIVATED + val isFailed get() = state == State.FAILED + val isCompleted get() = state == State.COMPLETED + override var name = this::class.simpleName ?: "Task" + + // ToDo: Better color management + private val primaryColor = Color(0, 255, 0, 100) + val info: Text + get() = buildText { + literal("Name ") + color(primaryColor) { literal(name) } + + literal(" State ") + color(primaryColor) { literal(state.name) } + + literal(" Runtime ") + color(primaryColor) { + literal(DurationFormatUtils.formatDuration(age * 50L, "HH:mm:ss,SSS")) + } + + subTasks.forEach { + literal("\n ") + text(it.info) + } + } val syncListeners = Subscriber() private val concurrentListeners = Subscriber() - operator fun plus(other: Task<*>) = TaskChain(listOf(this, other)) + enum class State { + IDLE, + ACTIVATED, + DEACTIVATED, + CANCELLED, + FAILED, + COMPLETED + } - /** - * Executes the main action of the task. - * - * This function should only perform read operations on the game thread due to potential concurrency issues. - * If write operations are necessary, they should be wrapped in the `runSafeOnGameThread { ... }` function to ensure thread safety. - * - * @return The result of the action, of type [Result]. - */ - abstract suspend fun onAction(): Result + operator fun plus(other: Task<*>) = subTasks.add(other) - /** - * This function is called when the task is canceled. - * It should be overridden for tasks that need to perform cleanup operations, - * such as cancelling a block breaking progress, releasing resources, - * or stopping any ongoing operations that were started by the task. - */ - open fun SafeContext.onCancel() {} + init { + listener { + parent?.let { + it.age++ + } + if (++age >= timeout) { + onTimeout(this@Task) + failure(TimeoutException(age, attempted)) + } + } + } - /** - * Executes the task with the configured parameters. - * - * This function will attempt to execute the task for a specified number of attempts and repeats. - * It handles delays before task execution, task timeouts, and task cancellations. - * It also manages the lifecycle of event listeners associated with the task. - * - * @return The result of the task execution, represented as a [TaskResult]. - * This could be a [successful result][TaskResult.Success], a [cancellation][TaskResult.Cancelled], - * a [failure][TaskResult.Failure] due to an exception, or a [timeout][TaskResult.Timeout]. - */ - suspend fun execute(): TaskResult { - var attempt = 1 - var iteration = 0 - - do { - if (delay > 0) { - info("Delaying for $delay ms") - delay(delay) + @Ta5kBuilder + open fun SafeContext.onStart() {} + + @Ta5kBuilder + fun start(parent: Task<*>?, pauseParent: Boolean = true): Task { + this.parent = parent + executions++ + + runSafe { onStart() } + this.parent?.let { par -> + par.subTasks.add(this) + info("${par.name} started this task.") + if (pauseParent && par.isActivated) { + info("Pausing parent ${par.name}") + par.deactivate() } + } ?: info("Root started this task") + activate() + return this + } + + @Ta5kBuilder + private fun activate() { + subTasks.firstOrNull { !it.isCompleted }?.let { + info("Starting subtask ${it.name}") + deactivate() + it.start(this) + } ?: run { + info("Activated") + state = State.ACTIVATED startListening() - iteration++ - - try { - withTimeout(timeout) { - onAction() - }?.let { result -> - if (iteration == repeats) { - onSuccess(result) - tidyUp() - return TaskResult.Success(result) - } - - onRepeat(iteration) - } - } catch (e: TimeoutCancellationException) { - attempt++ - warn("Timed out after $age ms and $attempt attempts") - onRetry() - tidyUp() - } catch (e: CancellationException) { - tidyUp() -// warn("Job cancelled") - return TaskResult.Cancelled - } catch (e: Throwable) { - attempt++ - logError("Failed after $age ms and $attempt attempts with exception: $e") - onException(e) - tidyUp() - return TaskResult.Failure(e) - } + } + } + + @Ta5kBuilder + fun deactivate() { + info("Deactivated") + state = State.DEACTIVATED + stopListening() + } - tidyUp() - } while (attempt < maxAttempts || iteration <= repeats) + @Ta5kBuilder + fun SafeContext.success(result: Result) { + onSuccess(this@Task, result) + + if (executions < repeats) { + executions++ + this@Task.info("Repeating task $executions/$repeats...") + onRepeat(this@Task, result, executions) + reset() + return + } - logError("Fully timed out after $age ms and $attempt attempts") - onTimeout() - return TaskResult.Timeout(timeout, attempt) + this@Task.info("Task completed successfully after $attempted retries and $executions executions.") + state = State.COMPLETED + tidyUp() } - private fun tidyUp() { + @Ta5kBuilder + fun cancel() { + cancelSubTasks() + state = State.CANCELLED + tidyUp() + runSafe { onCancel() } + } + + @Ta5kBuilder + fun cancelSubTasks() { + subTasks + .filter { it.isRunning } + .forEach { it.cancel() } + } + + @Ta5kBuilder + fun failure(message: String) { + failure(IllegalStateException(message)) + } + + @Ta5kBuilder + fun failure(e: Throwable) { + if (attempted < tries) { + attempted++ + warn("Failed task with error: ${e.message}, retrying ($attempted/$tries) ...") + runSafe { + onRetry(this@Task) + } + reset() + return + } + + state = State.FAILED + logError("Task failed after $attempted attempts with error: ${e.message}") + tidyUp() runSafe { - onCancel() + onException(this@Task, e) } + parent?.failure(e) + } - EventFlow.syncListeners.unsubscribe(syncListeners) - EventFlow.concurrentListeners.unsubscribe(concurrentListeners) + /** + * This function is called when the task is canceled. + * It should be overridden for tasks that need to perform cleanup operations, + * such as cancelling a block breaking progress, releasing resources, + * or stopping any ongoing operations that were started by the task. + */ + @Ta5kBuilder + open fun SafeContext.onCancel() {} + private fun tidyUp() { + stopListening() BaritoneUtils.cancel() + parent?.let { +// it.subTasks.remove(this) + if (it.isDeactivated) { + it.activate() + } else { + info("Parent ${it.name} is activated, not reactivating") + } + } + } + + @Ta5kBuilder + private fun reset() { + age = 0 } private fun startListening() { @@ -156,10 +256,9 @@ abstract class Task( EventFlow.concurrentListeners.subscribe(concurrentListeners) } - fun cancel() { - runSafe { - onCancel() - } + private fun stopListening() { + EventFlow.syncListeners.unsubscribe(syncListeners) + EventFlow.concurrentListeners.unsubscribe(concurrentListeners) } /** @@ -169,7 +268,7 @@ abstract class Task( * @return This task instance with the updated delay. */ @Ta5kBuilder - fun withDelay(delay: Long): Task { + fun withDelay(delay: Int): Task { this.delay = delay return this } @@ -181,7 +280,7 @@ abstract class Task( * @return This task instance with the updated timeout. */ @Ta5kBuilder - fun withTimeout(timeout: Long): Task { + fun withTimeout(timeout: Int): Task { this.timeout = timeout return this } @@ -194,7 +293,7 @@ abstract class Task( */ @Ta5kBuilder fun withMaxAttempts(maxAttempts: Int): Task { - this.maxAttempts = maxAttempts + this.tries = maxAttempts return this } @@ -217,7 +316,7 @@ abstract class Task( * @return The task instance with the updated success action. */ @Ta5kBuilder - fun onSuccess(action: suspend Task.(Result) -> Unit): Task { + fun onSuccess(action: SafeContext.(Task, Result) -> Unit): Task { this.onSuccess = action return this } @@ -229,7 +328,7 @@ abstract class Task( * @return The task instance with the updated retry action. */ @Ta5kBuilder - fun onRetry(action: suspend Task.() -> Unit): Task { + fun onRetry(action: SafeContext.(Task) -> Unit): Task { this.onRetry = action return this } @@ -241,7 +340,7 @@ abstract class Task( * @return The task instance with the updated timeout action. */ @Ta5kBuilder - fun onTimeout(action: suspend Task.() -> Unit): Task { + fun onTimeout(action: SafeContext.(Task) -> Unit): Task { this.onTimeout = action return this } @@ -253,7 +352,7 @@ abstract class Task( * @return The task instance with the updated exception action. */ @Ta5kBuilder - fun onFailure(action: suspend Task.(Throwable) -> Unit): Task { + fun onFailure(action: SafeContext.(Task, Throwable) -> Unit): Task { this.onException = action return this } @@ -265,15 +364,64 @@ abstract class Task( * @return The task instance with the updated repeat action. */ @Ta5kBuilder - fun onRepeat(action: suspend Task.(Int) -> Unit): Task { + fun onRepeat(action: SafeContext.(Task, Result, Int) -> Unit): Task { this.onRepeat = action return this } + @TaskCha1nBuilder + fun withSubTasks(subTaskBuilder: SubTaskBuilder.(Task<*>) -> Unit): Task { + with(SubTaskBuilder()) { + subTaskBuilder(this@Task) + subTasks.addAll(tasks) + } + return this + } + + @TaskCha1nBuilder + inline fun withListener( + event: TickEvent.Pre, crossinline action: SafeContext.(Task) -> Unit + ): Task { + listener { + action(this@Task) + } + return this + } + + @TaskCha1nBuilder + fun withName(name: String): Task { + this.name = name + return this + } + + class TimeoutException(age: Int, attempts: Int) : Exception("Task timed out after $age ticks and $attempts attempts") + + class SubTaskBuilder { + val tasks = mutableListOf>() + } + companion object { - fun (suspend () -> T).toTask(name: String = "Task") = object : Task() { - override val name = name - override suspend fun onAction() = this@toTask() + @TaskCha1nBuilder + fun emptyTask() = object : Task() { + override fun SafeContext.onStart() {} + } + + @TaskCha1nBuilder + fun buildTask( + block: SafeContext.() -> Unit + ) = object : Task() { + override fun SafeContext.onStart() { + block() + } + } + + @TaskCha1nBuilder + inline fun buildTaskWithReturn( + crossinline block: SafeContext.() -> Unit + ) = object : Task() { + override fun SafeContext.onStart() { + block() + } } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt b/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt index 68e65f401..c29b8045e 100644 --- a/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt +++ b/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt @@ -1,55 +1,7 @@ package com.lambda.task -import com.lambda.task.OptionalTask.Companion.toOptionalTask -import com.lambda.task.Task.Companion.toTask - @DslMarker annotation class TaskCha1nBuilder // Name is used to force the dsl style turquoise (name hash) @DslMarker -annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) - -@TaskCha1nBuilder -fun emptyChain() = TaskChain() - -@TaskCha1nBuilder -fun buildTask( - block: suspend () -> Unit -) = TaskChain(listOf(block.toTask())) - -@TaskCha1nBuilder -fun buildChain( - block: TaskChainBuilder.() -> Unit -) = TaskChainBuilder().apply { - block() -}.build() - -@TaskCha1nBuilder -class TaskChainBuilder { - private val steps: MutableList> = mutableListOf() - - @TaskCha1nBuilder - fun required(block: suspend () -> Unit) { - steps.add(block.toTask()) - } - - @TaskCha1nBuilder - fun required(task: Task<*>) { - steps.add(task) - } - - @TaskCha1nBuilder - fun optional(block: suspend () -> Unit) { - // ToDo: Find application to tell design. Should be based on a task strategy - steps.add(block.toOptionalTask { - true - }) - } - - @TaskCha1nBuilder - fun optional(task: OptionalTask<*>) { - steps.add(task) - } - - fun build() = TaskChain(steps) -} \ No newline at end of file +annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/TaskChain.kt b/common/src/main/kotlin/com/lambda/task/TaskChain.kt deleted file mode 100644 index d42da1ab5..000000000 --- a/common/src/main/kotlin/com/lambda/task/TaskChain.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.lambda.task - -import kotlinx.coroutines.ExperimentalCoroutinesApi - -class TaskChain( - val steps: List> = listOf() -) { - suspend fun run() { - steps.forEach { TaskRegistry.run(it) } - } - - fun tryRun() { - steps.forEach { TaskRegistry.tryRun(it) } - } - - @OptIn(ExperimentalCoroutinesApi::class) - fun cancel() { - TaskRegistry.taskFlow.resetReplayCache() - TaskRegistry.registry.keys - .filter { it in steps } - .forEach { TaskRegistry.cancel(it) } - } - - operator fun plus(other: TaskChain) = TaskChain(steps + other.steps) - - override fun toString() = "TaskChain:\n${steps.withIndex().joinToString("\n") { "${it.index + 1}. ${it.value}" }}" -} diff --git a/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt b/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt deleted file mode 100644 index 4ac460a1c..000000000 --- a/common/src/main/kotlin/com/lambda/task/TaskRegistry.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.lambda.task - -import com.lambda.threading.taskContext -import kotlinx.coroutines.Job -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.MutableSharedFlow -import java.util.concurrent.ConcurrentHashMap - -object TaskRegistry { - val registry = ConcurrentHashMap, Job>() - - val taskFlow = MutableSharedFlow>( - extraBufferCapacity = 1000, - onBufferOverflow = BufferOverflow.DROP_OLDEST - ) - - suspend fun run(task: Task<*>) { - taskFlow.emit(task) - } - - fun tryRun(task: Task<*>) { - taskFlow.tryEmit(task) - } - - fun cancel(task: Task<*>) { - task.cancel() - registry[task]?.cancel() - registry.remove(task) - } - - init { - taskContext { - taskFlow.collect { task -> -// val pending = taskFlow.replayCache.joinToString { it.name } -// task.info("Executing task: ${task.name} ${if (pending.isNotEmpty()) "(Pending: $pending)" else ""}") - val job = taskContext { - task.execute() - } - registry[task] = job - job.join() - registry.remove(task) -// task.info("Task completed: ${task.name} after ${task.age}ms") - } - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index b1a9c0453..c66e541f9 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -1,29 +1,29 @@ package com.lambda.task.tasks +import com.lambda.context.SafeContext import com.lambda.interaction.material.ContainerManager import com.lambda.interaction.material.ContainerManager.findContainerWithSelection import com.lambda.interaction.material.StackSelection import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder class AcquireMaterial( val selection: StackSelection ) : Task() { - override suspend fun onAction(): StackSelection { - // Find the best source with enough supplies + override fun SafeContext.onStart() { findContainerWithSelection(selection)?.let { container -> - (container.prepare() + container.withdraw(selection)).run() + emptyTask().withSubTasks { + container.prepare() + container.withdraw(selection) + }.onSuccess { _, _ -> + success(selection) + } } ?: throw ContainerManager.NoContainerFound(selection) // ToDo: Create crafting path - - return selection } companion object { @TaskCha1nBuilder - fun TaskChainBuilder.acquireStack(selection: StackSelection) = - AcquireMaterial(selection).apply { - required(this) - } + fun acquireStack(selection: StackSelection) = + AcquireMaterial(selection) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 1f016b4e2..197967f0a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -1,79 +1,102 @@ package com.lambda.task.tasks -import com.lambda.Lambda.LOG import com.lambda.context.SafeContext -import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.module.modules.client.TaskFlow +import com.lambda.task.Ta5kBuilder import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder +import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState +import net.minecraft.entity.ItemEntity +import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class BreakBlock( +class BreakBlock @Ta5kBuilder constructor( private val blockPos: BlockPos, private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), private val collectDrop: Boolean = false, private val noRotationForInstant: Boolean = true, -) : Task() { - val SafeContext.state: BlockState get() = world.getBlockState(blockPos) +) : Task() { + private var beginState: BlockState? = null + val SafeContext.state: BlockState get() = blockPos.blockState(world) + + override fun SafeContext.onStart() { + if (state.isAir && !collectDrop) { + success(null) + return + } + beginState = state + } init { -// listener { event -> -// if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener -// event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) -// } -// -// listener { -// if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener -// if (!it.context.isValid) return@listener -// val hitResult = it.context.hitResult?.blockResult ?: return@listener -// -// if (interaction.updateBlockBreakingProgress(hitResult.blockPos, hitResult.side)) { -// mc.particleManager.addBlockBreakingParticles(hitResult.blockPos, hitResult.side) -// player.swingHand(Hand.MAIN_HAND) -// } -// } + listener { event -> + if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener + event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + } + + listener { + if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener + if (!it.context.isValid) return@listener + val hitResult = it.context.hitResult?.blockResult ?: return@listener + + breakBlock(hitResult.side) + } listener { -// if (!instantBreakable(state, blockPos) || !noRotationForInstant) return@listener - val shape = state.getCollisionShape(world, blockPos) + if (state.isAir && !collectDrop) { + success(null) + return@listener + } + + if (!instantBreakable(state, blockPos) || !noRotationForInstant) return@listener + val shape = state.getOutlineShape(world, blockPos) if (shape.isEmpty) { - LOG.info("BreakBlock $blockPos has $state and empty shape") + failure("${blockPos.toShortString()} in state $state has no outline shape") return@listener } - state.getCollisionShape(world, blockPos).boundingBox + shape.boundingBox .getVisibleSurfaces(player.eyePos).firstOrNull()?.let { side -> - interaction.attackBlock(blockPos, side) -// if (interaction.updateBlockBreakingProgress(blockPos, side)) { -// mc.particleManager.addBlockBreakingParticles(blockPos, side) -//// mc.particleManager.addParticle(ParticleTypes.CRIT, hitResult.pos.x, hitResult.pos.y, hitResult.pos.z, 0.0, 0.0, 0.0) -// player.swingHand(Hand.MAIN_HAND) -// } + breakBlock(side) } } - } - override suspend fun onAction() { - awaitEvent { world.isAir(blockPos) } + listener { + if (state.isAir && !collectDrop) success(null) + } + + listener { + if (it.entity is ItemEntity + && it.entity.pos.isInRange(blockPos.toCenterPos(), 1.0) +// && it.entity.stack.item == beginState?.block?.item // ToDo: The item entities are all air?? + ) success(it.entity) + } + } -// if (collectDrop) awaitEvent { it.entity is ItemEntity ... } + private fun SafeContext.breakBlock(side: Direction) { + if (interaction.updateBlockBreakingProgress(blockPos, side)) { + mc.particleManager.addBlockBreakingParticles(blockPos, side) +// mc.particleManager.addParticle(ParticleTypes.CRIT, hitResult.pos.x, hitResult.pos.y, hitResult.pos.z, 0.0, 0.0, 0.0) + if (!instantBreakable(state, blockPos) || !noRotationForInstant) player.swingHand(Hand.MAIN_HAND) + } } companion object { @TaskCha1nBuilder - fun TaskChainBuilder.breakBlock( + fun uncheckedBreak( blockPos: BlockPos, rotationConfig: IRotationConfig = TaskFlow.rotationSettings, interactionConfig: InteractionConfig = TaskFlow.interactionSettings, @@ -87,8 +110,6 @@ class BreakBlock( sides, collectDrop, noRotationForInstant - ).apply { - required(this) - } + ) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 83577a7ed..0234b85ef 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,6 +1,5 @@ package com.lambda.task.tasks -import com.lambda.event.EventFlow.awaitEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.Blueprint @@ -14,9 +13,7 @@ import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.verify.TargetState import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder -import com.lambda.threading.taskContext -import kotlinx.coroutines.Job +import com.lambda.util.Communication.warn import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -25,9 +22,9 @@ class BuildStructure( private val collectDrops: Boolean = false, private val skipWeakBlocks: Boolean = false, private val pathing: Boolean = true, + private val finishOnDone: Boolean = true, private val limitPerTick: Int = 20 ) : Task() { - private var currentJob: Job? = null private var lastResult: BuildResult? = null init { @@ -37,6 +34,12 @@ class BuildStructure( else -> blueprint.structure } + if (finishOnDone && structure.isEmpty()) { + this@BuildStructure.warn("Structure is empty") + success(Unit) + return@listener + } + val results = structure.entries.fold(emptySet()) { acc, (pos, state) -> val buildContext = BreakContext( pos, @@ -47,49 +50,48 @@ class BuildStructure( acc + BreakResult.Success(buildContext) } - results.sorted().take(limitPerTick).forEach { result -> - if (lastResult == result) return@forEach - if (result !is Resolvable) return@forEach +// results.sorted().take(limitPerTick) + + results.minOrNull()?.let { result -> + if (lastResult == result) return@listener + if (result !is Resolvable) return@listener lastResult = result -// currentJob?.cancel() - currentJob = taskContext { - result.resolve.steps.forEach { it.execute() } - } + cancelSubTasks() + result.resolve.start(this@BuildStructure, false) } } } - override suspend fun onAction() { - awaitEvent { - blueprint.isDone(this) && false - } - } - companion object { @TaskCha1nBuilder - fun TaskChainBuilder.buildStructure( - blueprint: Blueprint, + fun buildStructure( collectDrops: Boolean = false, skipWeakBlocks: Boolean = false, pathing: Boolean = true, + finishOnDone: Boolean = true, + blueprint: () -> Blueprint, ) = BuildStructure( - blueprint, + blueprint(), collectDrops, skipWeakBlocks, - pathing - ).apply { - required(this) - } + pathing, + finishOnDone + ) @TaskCha1nBuilder - fun TaskChainBuilder.breakAndCollectBlock( + fun breakAndCollectBlock( blockPos: BlockPos ) = BuildStructure( blockPos.toStructure(TargetState.Air).toBlueprint(), collectDrops = true - ).apply { - required(this) - } + ) + + @TaskCha1nBuilder + fun breakBlock( + blockPos: BlockPos + ) = BuildStructure( + blockPos.toStructure(TargetState.Air).toBlueprint() + ) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 16385b67a..0f88e0234 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -2,31 +2,33 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.Goal import baritone.api.pathing.goals.GoalXZ -import com.lambda.Lambda.mc -import com.lambda.event.EventFlow.awaitEvent +import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockPos class GoalTask( private val goal: Goal ) : Task() { - override suspend fun onAction() { + + override fun SafeContext.onStart() { BaritoneUtils.setGoalAndPath(goal) + } - awaitEvent { - !BaritoneUtils.isActive + init { + listener { + if (!BaritoneUtils.isActive) { + success(Unit) + } } } companion object { @TaskCha1nBuilder - fun TaskChainBuilder.moveIntoEntityRange(blockPos: BlockPos) = - GoalTask(GoalXZ(blockPos.x, blockPos.z)).apply { - required(this) - } + fun moveIntoEntityRange(blockPos: BlockPos) = + GoalTask(GoalXZ(blockPos.x, blockPos.z)) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt index a04a02cd8..4c5da8f8a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt @@ -4,19 +4,26 @@ import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.task.Ta5kBuilder import com.lambda.task.Task +import com.lambda.task.TaskCha1nBuilder import kotlinx.coroutines.delay -class HelloWorldTask : Task() { +class HelloWorldTask @Ta5kBuilder constructor() : Task() { init { listener { - LOG.info("${this@HelloWorldTask::class.simpleName}: Ticking") + LOG.info("$name: ticking $age") + + if (age > 20) { + success(Unit) + } } } - override suspend fun onAction() { - LOG.info("Hello, World!") - delay(250) - LOG.info("Bye, World! Action completed") + companion object { + @TaskCha1nBuilder + fun Task<*>.helloWorld() = HelloWorldTask() + @TaskCha1nBuilder + fun SubTaskBuilder.helloWorld() = HelloWorldTask().also { tasks.add(it) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt deleted file mode 100644 index 7d16cee32..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/InteractBlock.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.lambda.task.tasks - -import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder -import com.lambda.task.buildChain -import com.lambda.task.tasks.LookAtBlock.Companion.lookAtBlock -import com.lambda.threading.runGameBlocking -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import net.minecraft.util.Hand -import net.minecraft.util.math.BlockPos - -class InteractBlock( - val blockPos: BlockPos -) : Task() { - - override suspend fun onAction() { - buildChain { - lookAtBlock(blockPos) - .withTimeout(3000L) - .onSuccess { context -> - runGameBlocking { - val cast = context.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") - interaction.interactBlock(player, Hand.MAIN_HAND, cast) - } - } - }.run() - } - - companion object { - @TaskCha1nBuilder - fun TaskChainBuilder.interactWithBlock(blockPos: BlockPos) = - InteractBlock(blockPos).apply { - required(this) - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt index 712f181cf..d5087738d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt @@ -1,15 +1,13 @@ package com.lambda.task.tasks +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.material.StackSelection -import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder -import com.lambda.threading.runGameBlocking import com.lambda.util.item.ItemStackUtils.equal import com.lambda.util.primitives.extension.containerSlots import com.lambda.util.primitives.extension.inventorySlots -import kotlinx.coroutines.delay import net.minecraft.item.ItemStack import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot @@ -20,48 +18,40 @@ class InventoryTask( val from: List, private val closeScreen: Boolean = true ) : Task>() { + private val moved = mutableListOf() - // ToDo: Needs smart code to move as efficient as possible. - // Also should handle overflow etc. Should be more generic - override suspend fun onAction(): List { - val moved = mutableListOf() - - from.forEach { - runGameBlocking { - // ToDo: Qickmove will not work in most situations + init { + // ToDo: Needs smart code to move as efficient as possible. + // Also should handle overflow etc. Should be more generic + listener { + from.firstOrNull { it.hasStack() }?.let { val preMove = it.stack.copy() + // ToDo: Quickmove will not work in most situations screen.onSlotClick(it.index, 0, SlotActionType.QUICK_MOVE, player) if (!it.stack.equal(preMove)) { moved.add(preMove) } - } - delay(TaskFlow.itemMoveDelay) // screen.syncState() // ToDo: Needed? + } ?: run { + if (closeScreen) player.closeHandledScreen() + success(moved) + } } - runGameBlocking { - if (closeScreen) player.closeHandledScreen() - } - - return moved } companion object { @TaskCha1nBuilder - inline fun TaskChainBuilder.withdraw(screen: H, selection: StackSelection) = + inline fun withdraw(screen: H, selection: StackSelection) = InventoryTask( screen, selection.filterSlots(screen.containerSlots) - ).apply { - required(this) - } + ) @TaskCha1nBuilder - inline fun TaskChainBuilder.deposit(screen: H, selection: StackSelection) = + inline fun deposit(screen: H, selection: StackSelection) = InventoryTask( screen, selection.filterSlots(screen.inventorySlots) - ).apply { - required(this) - } + ) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt deleted file mode 100644 index 91c34366a..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/LookAtBlock.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.lambda.task.tasks - -import com.lambda.event.EventFlow.awaitEvent -import com.lambda.event.events.RotationEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.InteractionConfig -import com.lambda.interaction.rotation.IRotationConfig -import com.lambda.interaction.rotation.RotationContext -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction - -class LookAtBlock( - val blockPos: BlockPos, - private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - private val sides: Set = emptySet() -) : Task() { - private var context: RotationContext? = null - init { - listener { - context = it.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) - } - } - - override suspend fun onAction(): RotationContext { - awaitEvent { !it.context.isValid } - - return context ?: throw IllegalStateException("Failed to look at block") - } - - companion object { - @TaskCha1nBuilder - fun TaskChainBuilder.lookAtBlock( - blockPos: BlockPos, - rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - sides: Set = emptySet() - ) = LookAtBlock(blockPos, rotationConfig, interactionConfig, sides).apply { - required(this) - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 0fa2f2f49..cb3e25efe 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -1,23 +1,25 @@ package com.lambda.task.tasks -import com.lambda.event.EventFlow.awaitEvent +import com.lambda.event.events.RotationEvent import com.lambda.event.events.ScreenHandlerEvent -import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.InteractionConfig +import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder -import com.lambda.task.buildChain -import com.lambda.task.tasks.LookAtBlock.Companion.lookAtBlock -import com.lambda.threading.runGameBlocking import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.screen.ScreenHandler import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction class OpenContainer( private val blockPos: BlockPos, private val waitForSlotLoad: Boolean = true, + private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + private val sides: Set = emptySet(), ) : Task() { private var screenHandler: H? = null private var slotsLoaded = false @@ -25,39 +27,36 @@ class OpenContainer( init { listener> { screenHandler = it.screenHandler + + if (!waitForSlotLoad) success(it.screenHandler) + } + + listener> { + screenHandler = null } listener { slotsLoaded = true + + screenHandler?.let { success(it) } + } + + listener { event -> + event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } - } - override suspend fun onAction(): H { - buildChain { - lookAtBlock(blockPos) - .withTimeout(2000L) - .onSuccess { request -> - runGameBlocking { - val cast = request.rotation.rayCast(5.0)?.blockResult ?: throw IllegalStateException("Failed to raycast block") - interaction.interactBlock(player, Hand.MAIN_HAND, cast) - } - - awaitEvent { - screenHandler != null && (!waitForSlotLoad || slotsLoaded) - } - } - }.run() - - return screenHandler ?: throw IllegalStateException("Failed to open container") + listener { + if (!it.context.isValid) return@listener + val hitResult = it.context.hitResult?.blockResult ?: return@listener + interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) + } } companion object { @TaskCha1nBuilder - inline fun TaskChainBuilder.openContainer( + inline fun openContainer( blockPos: BlockPos, waitForSlotLoad: Boolean = true - ) = OpenContainer(blockPos, waitForSlotLoad).apply { - required(this) - } + ) = OpenContainer(blockPos, waitForSlotLoad) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index c62585141..a9bbd7d5e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -2,22 +2,16 @@ package com.lambda.task.tasks import com.lambda.task.Task import com.lambda.task.TaskCha1nBuilder -import com.lambda.task.TaskChainBuilder import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos class PlaceContainer( val stack: ItemStack, ) : Task() { - override suspend fun onAction(): BlockPos { - TODO("Not yet implemented") - } companion object { @TaskCha1nBuilder - fun TaskChainBuilder.placeContainer(stack: ItemStack) = - PlaceContainer(stack).apply { - required(this) - } + fun placeContainer(stack: ItemStack) = + PlaceContainer(stack) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 8b3d4966c..6da709b6c 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -1,12 +1,13 @@ package com.lambda.util import com.lambda.context.SafeContext -import com.lambda.util.Communication.info import com.lambda.util.item.ItemUtils.block import com.lambda.util.item.ItemUtils.shulkerBoxes import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.Blocks +import net.minecraft.client.world.ClientWorld +import net.minecraft.fluid.FluidState import net.minecraft.fluid.Fluids import net.minecraft.item.Item import net.minecraft.util.math.* @@ -96,6 +97,8 @@ object BlockUtils { val allSigns = signs + wallSigns + hangingSigns + hangingWallSigns + fun BlockPos.blockState(world: ClientWorld): BlockState = world.getBlockState(this) + fun BlockPos.fluidState(world: ClientWorld): FluidState = world.getFluidState(this) fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) // info("State: $blockState Ticks to break: $ticksNeeded") diff --git a/common/src/main/kotlin/com/lambda/util/Formatting.kt b/common/src/main/kotlin/com/lambda/util/Formatting.kt index e972bf4e3..5c597c39f 100644 --- a/common/src/main/kotlin/com/lambda/util/Formatting.kt +++ b/common/src/main/kotlin/com/lambda/util/Formatting.kt @@ -7,7 +7,7 @@ import java.time.ZonedDateTime import java.time.format.DateTimeFormatter object Formatting { - val Vec3d.asString: String + val Vec3d.string: String get() = asString() fun Vec3d.asString(decimals: Int = 2): String { diff --git a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt index 7a4ff6e4a..d742eecb4 100644 --- a/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt +++ b/common/src/main/kotlin/com/lambda/util/combat/Explosion.kt @@ -1,6 +1,8 @@ package com.lambda.util.combat import com.lambda.context.SafeContext +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.fluidState import com.lambda.util.math.VecUtils.minus import com.lambda.util.math.VecUtils.times import com.lambda.util.world.WorldUtils.getFastEntities @@ -104,8 +106,8 @@ object Explosion { while (intensity > 0) { val blockPos = BlockPos.ofFloored(explosionX, explosionY, explosionZ) - val block = world.getBlockState(blockPos) - val fluid = world.getFluidState(blockPos) + val block = blockPos.blockState(world) + val fluid = blockPos.fluidState(world) if (!world.isInBuildLimit(blockPos)) { break } diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt index 2524f1ab6..7b43045e5 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldUtils.kt @@ -2,6 +2,7 @@ package com.lambda.util.world import com.lambda.context.SafeContext import com.lambda.util.BlockUtils.blockPos +import com.lambda.util.BlockUtils.blockState import com.lambda.util.collections.filterPointer import net.minecraft.block.Block import net.minecraft.block.BlockState @@ -236,7 +237,7 @@ object WorldUtils { iterator: (BlockState, BlockPos, Int) -> Unit = { _, _, _ -> }, ) { iteratePositions(pos, range) { blockPos, index -> - val state = world.getBlockState(blockPos) + val state = blockPos.blockState(world) if (predicate(state, blockPos)) { pointer?.add(state.block) iterator(state, blockPos, index) diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index 8231901f7..90cac89eb 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -31,9 +31,10 @@ "render.LivingEntityRendererMixin", "render.RenderLayersMixin", "render.RenderTickCounterMixin", - "render.VertexBufferMixin", "render.ScreenHandlerMixin", + "render.VertexBufferMixin", "render.WorldRendererMixin", + "world.ClientWorldMixin", "world.WorldMixin" ], "injectors": { From 253bd836a73acf4fb97477a8ec084be1deb21207 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 26 May 2024 04:31:47 +0200 Subject: [PATCH 08/47] Consistent task builder --- .../src/main/kotlin/com/lambda/task/Task.kt | 46 +++++++++---------- .../kotlin/com/lambda/task/TaskBuilder.kt | 7 --- .../com/lambda/task/tasks/AcquireMaterial.kt | 3 +- .../com/lambda/task/tasks/BreakBlock.kt | 4 +- .../com/lambda/task/tasks/BuildStructure.kt | 7 ++- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 3 +- .../com/lambda/task/tasks/HelloWorldTask.kt | 8 +--- .../com/lambda/task/tasks/InventoryTask.kt | 5 +- .../com/lambda/task/tasks/OpenContainer.kt | 3 +- .../com/lambda/task/tasks/PlaceContainer.kt | 3 +- 10 files changed, 35 insertions(+), 54 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/task/TaskBuilder.kt diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 6663e3490..7a163d13f 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,5 +1,6 @@ package com.lambda.task +import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.Event import com.lambda.event.EventFlow @@ -124,6 +125,9 @@ abstract class Task( } } + @DslMarker + annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) + @Ta5kBuilder open fun SafeContext.onStart() {} @@ -135,12 +139,12 @@ abstract class Task( runSafe { onStart() } this.parent?.let { par -> par.subTasks.add(this) - info("${par.name} started this task.") + LOG.info("${par.name} started $name") if (pauseParent && par.isActivated) { - info("Pausing parent ${par.name}") + LOG.info("$name pausing parent ${par.name}") par.deactivate() } - } ?: info("Root started this task") + } ?: LOG.info("Root started $name") activate() return this @@ -149,11 +153,13 @@ abstract class Task( @Ta5kBuilder private fun activate() { subTasks.firstOrNull { !it.isCompleted }?.let { - info("Starting subtask ${it.name}") + LOG.info("$name starting subtask ${it.name}") deactivate() it.start(this) } ?: run { - info("Activated") + if (isActivated) return + + LOG.info("$name activated") state = State.ACTIVATED startListening() } @@ -161,7 +167,7 @@ abstract class Task( @Ta5kBuilder fun deactivate() { - info("Deactivated") + LOG.info("$name deactivated") state = State.DEACTIVATED stopListening() } @@ -172,13 +178,13 @@ abstract class Task( if (executions < repeats) { executions++ - this@Task.info("Repeating task $executions/$repeats...") + LOG.info("Repeating $name $executions/$repeats...") onRepeat(this@Task, result, executions) reset() return } - this@Task.info("Task completed successfully after $attempted retries and $executions executions.") + LOG.info("$name completed successfully after $attempted retries and $executions executions.") state = State.COMPLETED tidyUp() } @@ -187,7 +193,8 @@ abstract class Task( fun cancel() { cancelSubTasks() state = State.CANCELLED - tidyUp() + stopListening() + BaritoneUtils.cancel() runSafe { onCancel() } } @@ -236,14 +243,7 @@ abstract class Task( private fun tidyUp() { stopListening() BaritoneUtils.cancel() - parent?.let { -// it.subTasks.remove(this) - if (it.isDeactivated) { - it.activate() - } else { - info("Parent ${it.name} is activated, not reactivating") - } - } + parent?.activate() } @Ta5kBuilder @@ -369,7 +369,7 @@ abstract class Task( return this } - @TaskCha1nBuilder + @Ta5kBuilder fun withSubTasks(subTaskBuilder: SubTaskBuilder.(Task<*>) -> Unit): Task { with(SubTaskBuilder()) { subTaskBuilder(this@Task) @@ -378,7 +378,7 @@ abstract class Task( return this } - @TaskCha1nBuilder + @Ta5kBuilder inline fun withListener( event: TickEvent.Pre, crossinline action: SafeContext.(Task) -> Unit ): Task { @@ -388,7 +388,7 @@ abstract class Task( return this } - @TaskCha1nBuilder + @Ta5kBuilder fun withName(name: String): Task { this.name = name return this @@ -401,12 +401,12 @@ abstract class Task( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun emptyTask() = object : Task() { override fun SafeContext.onStart() {} } - @TaskCha1nBuilder + @Ta5kBuilder fun buildTask( block: SafeContext.() -> Unit ) = object : Task() { @@ -415,7 +415,7 @@ abstract class Task( } } - @TaskCha1nBuilder + @Ta5kBuilder inline fun buildTaskWithReturn( crossinline block: SafeContext.() -> Unit ) = object : Task() { diff --git a/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt b/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt deleted file mode 100644 index c29b8045e..000000000 --- a/common/src/main/kotlin/com/lambda/task/TaskBuilder.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.lambda.task - -@DslMarker -annotation class TaskCha1nBuilder // Name is used to force the dsl style turquoise (name hash) - -@DslMarker -annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index c66e541f9..f16cfd19f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -5,7 +5,6 @@ import com.lambda.interaction.material.ContainerManager import com.lambda.interaction.material.ContainerManager.findContainerWithSelection import com.lambda.interaction.material.StackSelection import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder class AcquireMaterial( val selection: StackSelection @@ -22,7 +21,7 @@ class AcquireMaterial( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun acquireStack(selection: StackSelection) = AcquireMaterial(selection) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 197967f0a..0f02bbe9e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -9,9 +9,7 @@ import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.Ta5kBuilder import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult @@ -95,7 +93,7 @@ class BreakBlock @Ta5kBuilder constructor( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun uncheckedBreak( blockPos: BlockPos, rotationConfig: IRotationConfig = TaskFlow.rotationSettings, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 0234b85ef..ddb985ba8 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -12,7 +12,6 @@ import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.verify.TargetState import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import com.lambda.util.Communication.warn import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -64,7 +63,7 @@ class BuildStructure( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun buildStructure( collectDrops: Boolean = false, skipWeakBlocks: Boolean = false, @@ -79,7 +78,7 @@ class BuildStructure( finishOnDone ) - @TaskCha1nBuilder + @Ta5kBuilder fun breakAndCollectBlock( blockPos: BlockPos ) = BuildStructure( @@ -87,7 +86,7 @@ class BuildStructure( collectDrops = true ) - @TaskCha1nBuilder + @Ta5kBuilder fun breakBlock( blockPos: BlockPos ) = BuildStructure( diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 0f88e0234..e2900fcd7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -6,7 +6,6 @@ import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockPos @@ -27,7 +26,7 @@ class GoalTask( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun moveIntoEntityRange(blockPos: BlockPos) = GoalTask(GoalXZ(blockPos.x, blockPos.z)) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt index 4c5da8f8a..8e5fe9648 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt @@ -1,13 +1,9 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG -import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.task.Ta5kBuilder import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder -import kotlinx.coroutines.delay class HelloWorldTask @Ta5kBuilder constructor() : Task() { init { @@ -21,9 +17,9 @@ class HelloWorldTask @Ta5kBuilder constructor() : Task() { } companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun Task<*>.helloWorld() = HelloWorldTask() - @TaskCha1nBuilder + @Ta5kBuilder fun SubTaskBuilder.helloWorld() = HelloWorldTask().also { tasks.add(it) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt index d5087738d..0dc70b91f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt @@ -4,7 +4,6 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.material.StackSelection import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import com.lambda.util.item.ItemStackUtils.equal import com.lambda.util.primitives.extension.containerSlots import com.lambda.util.primitives.extension.inventorySlots @@ -40,14 +39,14 @@ class InventoryTask( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder inline fun withdraw(screen: H, selection: StackSelection) = InventoryTask( screen, selection.filterSlots(screen.containerSlots) ) - @TaskCha1nBuilder + @Ta5kBuilder inline fun deposit(screen: H, selection: StackSelection) = InventoryTask( screen, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index cb3e25efe..be4ad83f5 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -7,7 +7,6 @@ import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.screen.ScreenHandler import net.minecraft.util.Hand @@ -53,7 +52,7 @@ class OpenContainer( } companion object { - @TaskCha1nBuilder + @Ta5kBuilder inline fun openContainer( blockPos: BlockPos, waitForSlotLoad: Boolean = true diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index a9bbd7d5e..256054903 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -1,7 +1,6 @@ package com.lambda.task.tasks import com.lambda.task.Task -import com.lambda.task.TaskCha1nBuilder import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos @@ -10,7 +9,7 @@ class PlaceContainer( ) : Task() { companion object { - @TaskCha1nBuilder + @Ta5kBuilder fun placeContainer(stack: ItemStack) = PlaceContainer(stack) } From d1199cd51abf3f9f60999a105b0610cf80e79fcb Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 26 May 2024 04:48:02 +0200 Subject: [PATCH 09/47] Update docs to new architecture --- .../src/main/kotlin/com/lambda/task/Task.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 7a163d13f..a0573839c 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -23,25 +23,21 @@ import org.apache.commons.lang3.time.DurationFormatUtils import java.awt.Color /** - * A [Task] represents a time-critical activity that executes a suspending action function. - * It is designed to automate in-game activities without the need for strict event-based programming, - * thanks to the use of suspending functions which allow for linear coding. + * A [Task] represents a time-critical activity. + * It automates in-game activities through event-based programming, leveraging game events to control task flow. * * [Result] is the type of the result that the task will return when it completes successfully. * In case the task should not return any result, [Unit] can be used as the type. * - * A [Task] can have event listeners, but they are only active while the action function is running. + * A [Task] can have event listeners that are active while the task is running. + * The task will attempt to execute its subtasks sequentially, and can either keep listening + * or stop receiving events while the subtasks are running. * * It supports a builder pattern, allowing you to chain configuration methods like [withDelay], * [withTimeout], [withMaxAttempts], [withRepeats], [onSuccess], [onRetry], [onTimeout], [onFailure], and [onRepeat] * to construct a [Task] instance. * This makes it easy to build complex flows of nested tasks. * - * CAUTION: When implementing the [onAction] function, - * ensure that the function adheres to thread safety measures. - * This includes avoiding write operations on non-synchronized in-game data - * unless explicitly running on the game thread using `runSafeOnGameThread { ... }`. - * * @property delay The delay before the task starts, in milliseconds. * @property timeout The maximum time that the task is allowed to run, in milliseconds. * @property tries The maximum number of attempts to execute the task before it is considered failed. @@ -125,8 +121,12 @@ abstract class Task( } } + /** + * "Typo" in name is used to force the dsl style green + * (color is based on name hash, don't ask me who came up with this) + */ @DslMarker - annotation class Ta5kBuilder // Name is used to force the dsl style yellow (name hash) + annotation class Ta5kBuilder @Ta5kBuilder open fun SafeContext.onStart() {} From 69d31f25fd2eaa5f54097ab88b7a7ff5469f7407 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 27 May 2024 02:46:29 +0200 Subject: [PATCH 10/47] Fix task usecases --- common/build.gradle.kts | 8 +- .../lambda/mixin/MinecraftClientMixin.java | 9 ++- .../src/main/kotlin/com/lambda/core/Loader.kt | 2 + .../interaction/material/ContainerManager.kt | 10 ++- .../interaction/material/MaterialContainer.kt | 20 +++-- .../material/container/CreativeContainer.kt | 7 +- .../material/container/EnderChestContainer.kt | 4 - .../module/modules/debug/ContainerTest.kt | 28 +++++++ .../src/main/kotlin/com/lambda/task/Task.kt | 80 ++++++++++--------- .../com/lambda/task/tasks/AcquireMaterial.kt | 17 ++-- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 13 ++- .../main/kotlin/com/lambda/util/BlockUtils.kt | 1 + common/src/test/kotlin/TaskTests.kt | 14 ++++ 13 files changed, 142 insertions(+), 71 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt create mode 100644 common/src/test/kotlin/TaskTests.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a271651ef..664c4e5bb 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -27,6 +27,12 @@ dependencies { implementation(kotlin("reflect")) // Baritone - modImplementation("baritone-api:baritone-api:1.10.2") +// modImplementation("baritone-api:baritone-api:1.10.2") modImplementation("baritone-api:baritone-unoptimized-fabric:1.10.2") + + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() } diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 5c2452097..3d2e07027 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -14,6 +14,7 @@ import net.minecraft.client.network.ClientPlayerInteractionManager; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -21,6 +22,8 @@ @Mixin(MinecraftClient.class) public class MinecraftClientMixin { + @Shadow @Nullable public Screen currentScreen; + @Inject(method = "tick", at = @At("HEAD")) void onTickPre(CallbackInfo ci) { EventFlow.post(new TickEvent.Pre()); @@ -56,12 +59,12 @@ private void onScreenOpen(@Nullable Screen screen, CallbackInfo ci) { @Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;removed()V", shift = At.Shift.AFTER)) private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { - if (screen == null) return; - if (screen instanceof ScreenHandlerProvider handledScreen) { + if (currentScreen == null) return; + if (currentScreen instanceof ScreenHandlerProvider handledScreen) { EventFlow.post(new ScreenHandlerEvent.Close<>(handledScreen.getScreenHandler())); } - EventFlow.post(new ScreenEvent.Close<>(screen)); + EventFlow.post(new ScreenEvent.Close<>(currentScreen)); } @Redirect(method = "doItemUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;isBreakingBlock()Z")) diff --git a/common/src/main/kotlin/com/lambda/core/Loader.kt b/common/src/main/kotlin/com/lambda/core/Loader.kt index 54a73eba7..b0d684eed 100644 --- a/common/src/main/kotlin/com/lambda/core/Loader.kt +++ b/common/src/main/kotlin/com/lambda/core/Loader.kt @@ -8,6 +8,7 @@ import com.lambda.graphics.renderer.gui.font.LambdaFont import com.lambda.gui.GuiConfigurable import com.lambda.interaction.PlayerPacketManager import com.lambda.interaction.RotationManager +import com.lambda.interaction.material.ContainerManager import com.lambda.module.ModuleRegistry import com.lambda.sound.SoundRegistry import com.lambda.util.Communication.ascii @@ -23,6 +24,7 @@ object Loader { GuiConfigurable, FriendRegistry, SoundRegistry, + ContainerManager ) fun initialize() { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index c87d9d178..a533947d7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -1,10 +1,12 @@ package com.lambda.interaction.material +import com.lambda.core.Loadable import com.lambda.event.events.InteractionEvent import com.lambda.event.events.ScreenHandlerEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.* +import com.lambda.util.BlockUtils.blockEntity import com.lambda.util.Communication.info import com.lambda.util.item.ItemUtils import com.lambda.util.primitives.extension.containerStacks @@ -19,7 +21,7 @@ import net.minecraft.screen.ScreenHandlerType import java.util.TreeSet // ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. -object ContainerManager { +object ContainerManager : Loadable { // ToDo: Maybe use reflection to get all containers? val container = TreeSet().apply { add(CreativeContainer) @@ -34,10 +36,14 @@ object ContainerManager { init { listener { - lastInteractedBlockEntity = world.getBlockEntity(it.blockHitResult.blockPos) + lastInteractedBlockEntity = it.blockHitResult.blockPos.blockEntity(world) } listener> { event -> + // ToDo: ;-; i hate type erasure. + // The listener will be triggered for any H, not just GenericContainerScreenHandler + if (event.screenHandler !is GenericContainerScreenHandler) return@listener + val handler = event.screenHandler when (val block = lastInteractedBlockEntity) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 83ada9673..a2699f85c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -25,16 +25,19 @@ abstract class MaterialContainer( /** * Brings the player into a withdrawal/deposit state. E.g.: move to a chest etc. */ - open fun prepare(): Task<*> = emptyTask() + @Task.Ta5kBuilder + open fun prepare(): Task<*> = emptyTask("EmptyPrepare") /** * Withdraws items from the container to the player's inventory. */ + @Task.Ta5kBuilder abstract fun withdraw(selection: StackSelection): Task<*> /** * Deposits items from the player's inventory into the container. */ + @Task.Ta5kBuilder abstract fun deposit(selection: StackSelection): Task<*> open fun filter(selection: StackSelection) = @@ -64,12 +67,15 @@ abstract class MaterialContainer( selector = { true } count = transferAmount - return TransferResult.Success(emptyTask().withSubTasks { - prepare() - withdraw(this@transfer) - destination.prepare() - deposit(this@transfer) - }) + return TransferResult.Success( + prepare().onSuccess { prep, _ -> + withdraw(this@transfer).onSuccess { with, _ -> + destination.prepare().onSuccess { dest, _ -> + destination.deposit(this@transfer).start(dest) + }.start(with) + }.start(prep) + } + ) } fun List.doShulkerCheck() = diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt index 922584144..e67d4f688 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt @@ -1,5 +1,6 @@ package com.lambda.interaction.material.container +import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task.Companion.buildTask @@ -10,9 +11,9 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { override var stacks = emptyList() override fun available(selection: StackSelection): Int = - if (selection.optimalStack != null) Int.MAX_VALUE else 0 + if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 - override fun deposit(selection: StackSelection) = buildTask { + override fun deposit(selection: StackSelection) = buildTask("CreativeDeposit") { if (!player.isCreative) { // ToDo: Maybe switch gamemode? throw NotInCreativeModeException() @@ -25,7 +26,7 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { } // Withdraws items from the creative menu to the player's main hand - override fun withdraw(selection: StackSelection) = buildTask { + override fun withdraw(selection: StackSelection) = buildTask("CreativeWithdraw") { selection.optimalStack?.let { optimalStack -> if (player.mainHandStack.equal(optimalStack)) return@buildTask diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index 72077ff38..ad3618bb8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -2,14 +2,10 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.task.Task -import com.lambda.task.tasks.AcquireMaterial.Companion.acquireStack -import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer -import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer import net.minecraft.block.Blocks import net.minecraft.item.ItemStack import net.minecraft.item.Items diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt new file mode 100644 index 000000000..dfb594d34 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt @@ -0,0 +1,28 @@ +package com.lambda.module.modules.debug + +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.tasks.AcquireMaterial.Companion.acquire +import com.lambda.util.Communication.info +import net.minecraft.item.Items + +object ContainerTest : Module( + name = "ContainerTest", + description = "Test container", + defaultTags = setOf(ModuleTag.DEBUG) +) { + init { + listener { +// info(task.info) + } + + onEnable { + acquire { + Items.OBSIDIAN.select() + }.start(null) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index a0573839c..f7638c7ea 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,5 +1,6 @@ package com.lambda.task +import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.Event @@ -17,7 +18,6 @@ import com.lambda.util.text.buildText import com.lambda.util.text.color import com.lambda.util.text.literal import com.lambda.util.text.text -import kotlinx.coroutines.* import net.minecraft.text.Text import org.apache.commons.lang3.time.DurationFormatUtils import java.awt.Color @@ -51,8 +51,8 @@ import java.awt.Color abstract class Task( private var delay: Int = 0, private var timeout: Int = Int.MAX_VALUE, - private var tries: Int = 1, - private var repeats: Int = 1, + private var tries: Int = 0, + private var repeats: Int = 0, private var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> }, private var onRetry: SafeContext.(Task) -> Unit = {}, private var onTimeout: SafeContext.(Task) -> Unit = {}, @@ -60,6 +60,8 @@ abstract class Task( private var onException: SafeContext.(Task, Throwable) -> Unit = { _, _ -> }, ) : Nameable { private var parent: Task<*>? = null + private val root: Task<*> get() = parent?.root ?: this + private val depth: Int get() = parent?.depth?.plus(1) ?: 0 private var executions = 0 private var attempted = 0 @@ -90,7 +92,7 @@ abstract class Task( } subTasks.forEach { - literal("\n ") + literal("\n${" ".repeat(depth + 1)}") text(it.info) } } @@ -136,37 +138,35 @@ abstract class Task( this.parent = parent executions++ - runSafe { onStart() } - this.parent?.let { par -> + parent?.let { par -> par.subTasks.add(this) - LOG.info("${par.name} started $name") + LOG.info("$name was started by ${par.name}") if (pauseParent && par.isActivated) { - LOG.info("$name pausing parent ${par.name}") + LOG.info("$name deactivating parent ${par.name}") par.deactivate() } } ?: LOG.info("Root started $name") activate() + LOG.info("\n${root.info.string}") + runSafe { onStart() } + LOG.info("\n${root.info.string}") return this } @Ta5kBuilder private fun activate() { - subTasks.firstOrNull { !it.isCompleted }?.let { - LOG.info("$name starting subtask ${it.name}") - deactivate() - it.start(this) - } ?: run { - if (isActivated) return - - LOG.info("$name activated") - state = State.ACTIVATED - startListening() - } + if (isActivated) return + + LOG.info("$name activated") + state = State.ACTIVATED + startListening() } @Ta5kBuilder fun deactivate() { + if (isDeactivated) return + LOG.info("$name deactivated") state = State.DEACTIVATED stopListening() @@ -174,8 +174,6 @@ abstract class Task( @Ta5kBuilder fun SafeContext.success(result: Result) { - onSuccess(this@Task, result) - if (executions < repeats) { executions++ LOG.info("Repeating $name $executions/$repeats...") @@ -187,6 +185,7 @@ abstract class Task( LOG.info("$name completed successfully after $attempted retries and $executions executions.") state = State.COMPLETED tidyUp() + onSuccess(this@Task, result) } @Ta5kBuilder @@ -219,15 +218,14 @@ abstract class Task( onRetry(this@Task) } reset() +// activate() return } state = State.FAILED logError("Task failed after $attempted attempts with error: ${e.message}") tidyUp() - runSafe { - onException(this@Task, e) - } + runSafe { onException(this@Task, e) } parent?.failure(e) } @@ -368,16 +366,7 @@ abstract class Task( this.onRepeat = action return this } - - @Ta5kBuilder - fun withSubTasks(subTaskBuilder: SubTaskBuilder.(Task<*>) -> Unit): Task { - with(SubTaskBuilder()) { - subTaskBuilder(this@Task) - subTasks.addAll(tasks) - } - return this - } - + @Ta5kBuilder inline fun withListener( event: TickEvent.Pre, crossinline action: SafeContext.(Task) -> Unit @@ -402,25 +391,38 @@ abstract class Task( companion object { @Ta5kBuilder - fun emptyTask() = object : Task() { - override fun SafeContext.onStart() {} + fun emptyTask( + name: String = "EmptyTask", + ) = object : Task() { + init { this.name = name } + override fun SafeContext.onStart() { success(Unit) } } @Ta5kBuilder fun buildTask( + name: String = "Task", block: SafeContext.() -> Unit ) = object : Task() { + init { this.name = name } override fun SafeContext.onStart() { - block() + try { + success(block()) + } catch (e: Throwable) { + failure(e) + } } } @Ta5kBuilder inline fun buildTaskWithReturn( - crossinline block: SafeContext.() -> Unit + crossinline block: SafeContext.() -> R ) = object : Task() { override fun SafeContext.onStart() { - block() + try { + success(block()) + } catch (e: Throwable) { + failure(e) + } } } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index f16cfd19f..812f19c98 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -11,18 +11,17 @@ class AcquireMaterial( ) : Task() { override fun SafeContext.onStart() { findContainerWithSelection(selection)?.let { container -> - emptyTask().withSubTasks { - container.prepare() - container.withdraw(selection) - }.onSuccess { _, _ -> - success(selection) - } - } ?: throw ContainerManager.NoContainerFound(selection) // ToDo: Create crafting path + container.prepare().onSuccess { _, _ -> + container.withdraw(selection).onSuccess { _, _ -> + success(selection) + }.start(this@AcquireMaterial) + }.start(this@AcquireMaterial) + } ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path } companion object { @Ta5kBuilder - fun acquireStack(selection: StackSelection) = - AcquireMaterial(selection) + fun acquire(selection: () -> StackSelection) = + AcquireMaterial(selection()) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index e2900fcd7..0f292e1a6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -1,6 +1,8 @@ package com.lambda.task.tasks import baritone.api.pathing.goals.Goal +import baritone.api.pathing.goals.GoalBlock +import baritone.api.pathing.goals.GoalNear import baritone.api.pathing.goals.GoalXZ import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent @@ -10,7 +12,8 @@ import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockPos class GoalTask( - private val goal: Goal + private val goal: Goal, + private val check: SafeContext.() -> Boolean = { true } ) : Task() { override fun SafeContext.onStart() { @@ -19,7 +22,7 @@ class GoalTask( init { listener { - if (!BaritoneUtils.isActive) { + if (!BaritoneUtils.isActive && check()) { success(Unit) } } @@ -27,7 +30,11 @@ class GoalTask( companion object { @Ta5kBuilder - fun moveIntoEntityRange(blockPos: BlockPos) = + fun moveToXY(blockPos: BlockPos) = GoalTask(GoalXZ(blockPos.x, blockPos.z)) + + @Ta5kBuilder + fun moveIntoEntityRange(blockPos: BlockPos, range: Int = 3) = + GoalTask(GoalNear(blockPos, range)) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 6da709b6c..1882aa6c1 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -99,6 +99,7 @@ object BlockUtils { fun BlockPos.blockState(world: ClientWorld): BlockState = world.getBlockState(this) fun BlockPos.fluidState(world: ClientWorld): FluidState = world.getFluidState(this) + fun BlockPos.blockEntity(world: ClientWorld) = world.getBlockEntity(this) fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) // info("State: $blockState Ticks to break: $ticksNeeded") diff --git a/common/src/test/kotlin/TaskTests.kt b/common/src/test/kotlin/TaskTests.kt new file mode 100644 index 000000000..e47fa80b3 --- /dev/null +++ b/common/src/test/kotlin/TaskTests.kt @@ -0,0 +1,14 @@ +import com.lambda.task.Task.Companion.emptyTask +import kotlin.test.assertEquals + +internal class TaskTests { + fun testSum() { + val task = emptyTask("1").onSuccess { task, unit -> + emptyTask("2").onSuccess { task, unit -> + emptyTask("3").onSuccess { task, unit -> + emptyTask("4") + } + } + } + } +} \ No newline at end of file From c813e30c79ed19b0071dab31b55347a26ae9ae82 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 30 May 2024 16:50:49 +0200 Subject: [PATCH 11/47] Break result optimization --- .../lambda/mixin/render/DebugHudMixin.java | 6 + .../com/lambda/config/InteractionSettings.kt | 1 + .../com/lambda/event/events/RotationEvent.kt | 32 --- .../lambda/interaction/InteractionConfig.kt | 2 + .../interaction/construction/Blueprint.kt | 4 +- .../construction/DynamicBlueprint.kt | 4 +- .../construction/context/BreakContext.kt | 30 +- .../construction/context/BuildContext.kt | 5 +- .../construction/result/BreakResult.kt | 167 ++++++++++- .../construction/result/BuildResult.kt | 77 +++++- .../interaction/construction/result/Rank.kt | 5 +- .../construction/verify/TargetState.kt | 4 +- .../interaction/material/ContainerManager.kt | 8 + .../material/transfer/TransferResult.kt | 18 +- .../interaction/rotation/RotationContext.kt | 2 +- .../visibilty/VisibilityChecker.kt | 49 +++- .../lambda/module/modules/client/TaskFlow.kt | 7 +- .../main/kotlin/com/lambda/task/RootTask.kt | 12 + .../src/main/kotlin/com/lambda/task/Task.kt | 44 +-- .../com/lambda/task/tasks/BreakBlock.kt | 28 +- .../com/lambda/task/tasks/BuildStructure.kt | 261 +++++++++++++++++- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 15 +- .../com/lambda/task/tasks/OpenContainer.kt | 3 +- .../util/DynamicReflectionSerializer.kt | 28 +- .../lambda/util/world/raycast/RayCastUtils.kt | 2 + 25 files changed, 674 insertions(+), 140 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/task/RootTask.kt diff --git a/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java b/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java index 6f97b119c..9c6439c49 100644 --- a/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/DebugHudMixin.java @@ -1,5 +1,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; @@ -15,4 +16,9 @@ public class DebugHudMixin { private void onGetRightText(CallbackInfoReturnable> cir) { DebugInfoHud.addDebugInfo(cir.getReturnValue()); } + + @Inject(method = "getLeftText", at = @At(value = "TAIL")) + private void onGetLeftText(CallbackInfoReturnable> cir) { + RootTask.INSTANCE.addInfo(cir.getReturnValue()); + } } diff --git a/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt index 85f1b810e..2c63b764f 100644 --- a/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt @@ -10,4 +10,5 @@ class InteractionSettings( override val reach by c.setting("Reach", 5.0, 0.1..10.0, 0.1, "Players reach / range", "", vis) override val resolution by c.setting("Resolution", 10, 1..100, 1, "Raycast resolution", "", vis) override val rayCastMask by c.setting("Raycast Mask", RayCastMask.BOTH, "What to raycast against", vis) + override val ignoreRayCast by c.setting("Ignore Raycast", true, "Ignore raycast when looking at blocks", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index 5ac3f4cea..9d9e4319e 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -35,38 +35,6 @@ abstract class RotationEvent : Event { this.context = context } } - - fun lookAtEntity( - rotationConfig: IRotationConfig, - interactionConfig: InteractionConfig, - entity: Entity - ) { - runSafe { - findRotation(rotationConfig, interactionConfig, listOf(entity.boundingBox)) { - entityResult?.entity == entity - }?.let { rotationContext -> - context = rotationContext - } - } - } - - fun lookAtBlock( - blockPos: BlockPos, - rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - interactionConfig: InteractionConfig = TaskFlow.interactionSettings, - sides: Set = emptySet() - ) = runSafe { - val state = blockPos.blockState(world) - val voxelShape = state.getOutlineShape(world, blockPos) - val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } - findRotation(rotationConfig, interactionConfig, boundingBoxes, sides) { - blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) - }?.let { - context = it - return@runSafe it - } - return@runSafe null - } } class Post(val context: RotationContext) : RotationEvent() diff --git a/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt index 3eb58020f..67a1fcaea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt @@ -14,4 +14,6 @@ interface InteractionConfig { val resolution: Int val rayCastMask: RayCastMask + + val ignoreRayCast: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt index 297ec3ab9..87aacb975 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt @@ -13,9 +13,9 @@ import net.minecraft.util.math.Box abstract class Blueprint { abstract val structure: Structure - open fun isDone(safeContext: SafeContext) = + open fun isDone(ctx: SafeContext) = structure.all { (pos, targetState) -> - with(safeContext) { + with(ctx) { targetState.matches(pos.blockState(world), pos, world) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt index ffb3fed46..bc323abb6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -8,8 +8,8 @@ data class DynamicBlueprint( val initial: Structure = emptyMap(), val update: SafeContext.(Structure) -> Structure, ) : Blueprint() { - fun update(safeContext: SafeContext) = - safeContext.update(structure) + fun update(ctx: SafeContext) = + ctx.update(structure) override val structure: Structure by lazy { initial } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index ecdb2e723..48354177e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -1,26 +1,32 @@ package com.lambda.interaction.construction.context +import com.lambda.context.SafeContext +import com.lambda.interaction.rotation.RotationContext +import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d data class BreakContext( -// val hitVec: Vec3d, - val hitPos: BlockPos, - val exposedSides: Int, -// val side: Direction, - override val distance: Double, -// var hand: Hand, -// val pointOfView: Vec3d, -// val blockState: BlockState, + val pov: Vec3d, + val result: BlockHitResult, + val rotation: RotationContext, + val checkedState: BlockState, + val hand: Hand, + val instantBreak: Boolean, ) : BuildContext, ComparableContext { -// override val resultingPos = hitPos -// override fun toBlockHitResult() = BlockHitResult(hitVec, side, hitPos, false) + override val distance: Double by lazy { + result.distanceTo(pov) + } + + fun exposedSides(ctx: SafeContext) = + Direction.entries.filter { + ctx.world.isAir(result.blockPos.offset(it)) + } -// val expectedBlockState = blockState.fluidState.blockState + override val expectedState = checkedState.fluidState.blockState override fun compareTo(other: ComparableContext): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index ff5e91f25..e861dab7e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -1,10 +1,9 @@ package com.lambda.interaction.construction.context -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.math.BlockPos +import net.minecraft.block.BlockState interface BuildContext { val distance: Double + val expectedState: BlockState // val resultingPos: BlockPos -// fun toBlockHitResult(): BlockHitResult } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index b14777dc0..84c3f97d8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -1,7 +1,19 @@ package com.lambda.interaction.construction.result import com.lambda.interaction.construction.context.BreakContext -import com.lambda.task.tasks.BreakBlock.Companion.uncheckedBreak +import com.lambda.interaction.material.ContainerManager.findBestAvailableTool +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.StackSelection.Companion.selectStack +import com.lambda.interaction.material.container.CreativeContainer.transfer +import com.lambda.interaction.material.container.MainHandContainer +import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.tasks.BreakBlock.Companion.breakBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToBlock +import net.minecraft.block.BlockState +import net.minecraft.item.Item +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction sealed class BreakResult : BuildResult() { @@ -9,10 +21,13 @@ sealed class BreakResult : BuildResult() { * Represents a successful break. All checks have been passed. * @param context The context of the break. */ - data class Success(val context: BreakContext) : Resolvable, BreakResult() { + data class Success( + override val blockPos: BlockPos, + val context: BreakContext + ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_SUCCESS - override val resolve = uncheckedBreak(context.hitPos) + override val resolve = breakBlock(context) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -21,4 +36,150 @@ sealed class BreakResult : BuildResult() { } } } + + /** + * Represents a break out of reach. + * @param blockPos The position of the block that is out of reach. + * @param distance The distance to the hit vector. + */ + data class OutOfReach( + override val blockPos: BlockPos, + val distance: Double + ) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_OUT_OF_REACH + + override val resolve = moveToBlock(blockPos) + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is OutOfReach -> distance.compareTo(other.distance) + else -> super.compareTo(other) + } + } + } + + /** + * Represents a break configuration where the hit side is not exposed to air. + * @param blockPos The position of the block that is not exposed. + * @param side The side that is not exposed. + */ + data class NotExposed( + override val blockPos: BlockPos, + val side: Direction + ) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_NOT_EXPOSED + + override val resolve = emptyTask() + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is NotExposed -> blockPos.compareTo(other.blockPos) + else -> super.compareTo(other) + } + } + } + + /** + * The checked break configuration hits on a side not in the player direction. + * @param blockPos The position of the block that is not exposed. + * @param side The side that is not exposed. + */ + data class NotVisible( + override val blockPos: BlockPos, + val side: Direction, + val distance: Double + ) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_NOT_VISIBLE + + override val resolve = emptyTask() + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is NotVisible -> distance.compareTo(other.distance) + else -> super.compareTo(other) + } + } + } + + /** + * The equipped item is not suitable for breaking blocks. + * @param blockState The block state that is being broken. + * @param badItem The item that is being used. + */ + data class ItemCantMine( + override val blockPos: BlockPos, + val blockState: BlockState, + val badItem: Item + ) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_ITEM_CANT_MINE + override val resolve = findBestAvailableTool(blockState) + ?.select() + ?.transfer(MainHandContainer) + ?.solve ?: run { + selectStack { + isItem(badItem).not() + }.transfer(MainHandContainer).solve + } + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is ItemCantMine -> badItem.name.string.compareTo(other.badItem.name.string) + else -> super.compareTo(other) + } + } + } + + /** + * Player has an inefficient tool equipped. + * @param bestTool The best tool for the block state. + */ + data class WrongTool( + override val blockPos: BlockPos, + val context: BreakContext, + val bestTool: Item + ) : Resolvable, BreakResult() { + override val rank = Rank.BREAK_WRONG_TOOL + + override val resolve: Task<*> = + bestTool.select().transfer(MainHandContainer).solve + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is WrongTool -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } + } + + /** + * The block is a liquid and first has to be submerged. + * @param blockPos The position of the block that is a liquid. + */ + data class Submerge( + override val blockPos: BlockPos, + val blockState: BlockState, + val submerge: Set + ) : BreakResult() { + override val rank = Rank.BREAK_SUBMERGE + } + + /** + * The block is blocked by another liquid block that first has to be submerged. + */ + data class BlockedByLiquid( + override val blockPos: BlockPos, + val blockState: BlockState + ) : BreakResult() { + override val rank = Rank.BREAK_IS_BLOCKED_BY_LIQUID + } + + /** + * The player is standing on the block. + */ + data class PlayerOnTop( + override val blockPos: BlockPos, + val blockState: BlockState + ) : BreakResult() { + override val rank = Rank.BREAK_PLAYER_ON_TOP + } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 47e2ed680..9ac564252 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,18 +1,91 @@ package com.lambda.interaction.construction.result +import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockPos + abstract class BuildResult : ComparableResult { + abstract val blockPos: BlockPos /** * The build action is done. */ - data object Done : BuildResult() { + data class Done( + override val blockPos: BlockPos, + ) : BuildResult() { override val rank = Rank.DONE } /** * The build action is ignored. */ - data object Ignored : BuildResult() { + data class Ignored( + override val blockPos: BlockPos, + ) : BuildResult() { override val rank = Rank.IGNORED } + + /** + * The chunk at the target is not loaded. + * @param blockPos The position of the block that is in an unloaded chunk. + */ + data class ChunkNotLoaded( + override val blockPos: BlockPos + ) : Resolvable, BuildResult() { + override val rank = Rank.CHUNK_NOT_LOADED + + override val resolve = moveUntilLoaded(blockPos) + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is ChunkNotLoaded -> blockPos.compareTo(other.blockPos) + else -> super.compareTo(other) + } + } + } + + /** + * The player has no permission to interact with the block. (E.g.: Spectator mode) + * @param blockPos The position of the block that is restricted. + */ + data class Restricted( + override val blockPos: BlockPos + ) : BreakResult() { + override val rank = Rank.BREAK_RESTRICTED + } + + /** + * The block needs server permission to be broken. (Needs op) + * @param blockPos The position of the block that needs permission. + * @param blockState The state of the block that needs permission. + */ + data class NoPermission( + override val blockPos: BlockPos, + val blockState: BlockState + ) : BreakResult() { + override val rank = Rank.BREAK_NO_PERMISSION + } + + /** + * The break target is out of the world border or height limit. + * @param blockPos The position of the block that is out of the world. + */ + data class OutOfWorld( + override val blockPos: BlockPos + ) : BreakResult() { + override val rank = Rank.BREAK_OUT_OF_WORLD + } + + /** + * The block is unbreakable. + * @param blockPos The position of the block that is unbreakable. + * @param blockState The state of the block that is unbreakable. + */ + data class Unbreakable( + override val blockPos: BlockPos, + val blockState: BlockState + ) : BreakResult() { + override val rank = Rank.BREAK_UNBREAKABLE + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt index f969e792a..4369157ea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -14,12 +14,11 @@ enum class Rank { BREAK_NOT_EXPOSED, BREAK_OUT_OF_WORLD, PLACE_OUT_OF_WORLD, - BREAK_CHUNK_NOT_LOADED, - PLACE_CHUNK_NOT_LOADED, + CHUNK_NOT_LOADED, PLACE_NO_INTEGRITY, PLACE_CANT_REPLACE, PLACE_NOT_ITEM_BLOCK, - BREAK_IS_LIQUID, + BREAK_SUBMERGE, BREAK_IS_BLOCKED_BY_LIQUID, BREAK_PLAYER_ON_TOP, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index f4b749b46..82a107367 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -20,14 +20,14 @@ sealed class TargetState : StateMatcher { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.isSolidBlock(world, pos) override fun getStack(world: ClientWorld, pos: BlockPos) = - ItemStack(Items.NETHERRACK) + ItemStack(Items.NETHERRACK) // ToDo: Find any disposable block } data class Support(val direction: Direction) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = pos.offset(direction).blockState(world).isSolidBlock(world, pos.offset(direction)) override fun getStack(world: ClientWorld, pos: BlockPos) = - ItemStack(Items.NETHERRACK) + ItemStack(Items.NETHERRACK) // ToDo: Find any disposable block } data class State(val blockState: BlockState) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index a533947d7..989c16570 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -79,6 +79,14 @@ object ContainerManager : Loadable { ): MaterialContainer? = container.find { it.available(selection) >= selection.count } + fun findContainerWithSelection( + selectionBuilder: StackSelection.() -> Unit + ): MaterialContainer? { + val selection = StackSelection().apply(selectionBuilder) + return container.find { it.available(selection) >= selection.count } + } + + fun findContainerWithStacks( count: Int = StackSelection.DEFAULT_AMOUNT, selection: (ItemStack) -> Boolean, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index de71c9b0a..4512cd7be 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -2,10 +2,20 @@ package com.lambda.interaction.material.transfer import com.lambda.task.Task -sealed class TransferResult { +abstract class TransferResult { + abstract val solve: Task<*> + data class Success( - val task: Task<*> + override val solve: Task<*> ) : TransferResult() - data object NoSpace : TransferResult() // ToDo: Needs inventory space resolver. compressing or disposing - data class MissingItems(val missing: Int) : TransferResult() // ToDo: Find other satisfying permutations + + data object NoSpace : TransferResult() { + // ToDo: Needs inventory space resolver. compressing or disposing + override val solve = Task.emptyTask("NoSpace") + } + + data class MissingItems(val missing: Int) : TransferResult() { + // ToDo: Find other satisfying permutations + override val solve = Task.emptyTask("MissingItems") + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt index 6e95c8d47..94a66c0a6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt @@ -5,8 +5,8 @@ import net.minecraft.util.hit.HitResult data class RotationContext( val rotation: Rotation, val config: IRotationConfig, - val verify: HitResult.() -> Boolean = { true }, val hitResult: HitResult? = null, + val verify: HitResult.() -> Boolean = { true }, ) { val isValid: Boolean get() = hitResult?.verify() == true } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index fcf7930fb..ec6ad01ea 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -6,9 +6,15 @@ import com.lambda.interaction.RotationManager import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationContext +import com.lambda.module.modules.client.TaskFlow +import com.lambda.util.BlockUtils.blockState import com.lambda.util.math.VecUtils.distSq import com.lambda.util.primitives.extension.component6 +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import com.lambda.util.world.raycast.RayCastUtils.entityResult +import net.minecraft.entity.Entity import net.minecraft.util.hit.HitResult +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d @@ -16,10 +22,32 @@ import java.util.* import kotlin.math.pow object VisibilityChecker { - fun SafeContext.findRotation( + fun SafeContext.lookAtEntity( rotationConfig: IRotationConfig, - interact: InteractionConfig, + interactionConfig: InteractionConfig, + entity: Entity + ) = findRotation(listOf(entity.boundingBox), rotationConfig, interactionConfig) { + entityResult?.entity == entity + } + + fun SafeContext.lookAtBlock( + blockPos: BlockPos, + rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + sides: Set = emptySet() + ): RotationContext? { + val state = blockPos.blockState(world) + val voxelShape = state.getOutlineShape(world, blockPos) + val boundingBoxes = voxelShape.boundingBoxes.map { it.offset(blockPos) } + return findRotation(boundingBoxes, rotationConfig, interactionConfig, sides) { + blockResult?.blockPos == blockPos && (blockResult?.side in sides || sides.isEmpty()) + } + } + + fun SafeContext.findRotation( boxes: List, + rotationConfig: IRotationConfig = TaskFlow.rotationSettings, + interact: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet(), verify: HitResult.() -> Boolean, ): RotationContext? { @@ -33,14 +61,14 @@ object VisibilityChecker { ) if (boxes.any { it.contains(eye) }) { - return RotationContext(currentRotation, rotationConfig, verify, currentCast) + return RotationContext(currentRotation, rotationConfig, currentCast, verify) } val validHits = mutableMapOf() val reachSq = interact.reach.pow(2) boxes.forEach { box -> - scanVisibleSurfaces(box, sides, interact.resolution) { vec -> + scanVisibleSurfaces(player.eyePos, box, sides, interact.resolution) { vec -> if (eye distSq vec > reachSq) return@scanVisibleSurfaces val newRotation = eye.rotationTo(vec) @@ -59,25 +87,24 @@ object VisibilityChecker { validHits.keys.mostCenter?.let { optimum -> validHits.minByOrNull { optimum distSq it.key }?.let { closest -> val optimumRotation = eye.rotationTo(closest.key) - return RotationContext(optimumRotation, rotationConfig, verify, closest.value) + return RotationContext(optimumRotation, rotationConfig, closest.value, verify) } } return null } - inline fun SafeContext.scanVisibleSurfaces( + inline fun scanVisibleSurfaces( + eyes: Vec3d, box: Box, sides: Set, resolution: Int, check: (Vec3d) -> Unit, ) { val shrunk = box.expand(-0.005) - box.getVisibleSurfaces(player.eyePos) + box.getVisibleSurfaces(eyes) .forEach { side -> - if (sides.isNotEmpty() && side !in sides) { - return@forEach - } + if (sides.isNotEmpty() && side !in sides) return@forEach val (minX, minY, minZ, maxX, maxY, maxZ) = shrunk.bounds(side) val stepX = (maxX - minX) / resolution val stepY = (maxY - minY) / resolution @@ -93,7 +120,7 @@ object VisibilityChecker { } } - private val Set.mostCenter: Vec3d? + val Set.mostCenter: Vec3d? get() = reduceOrNull { acc, vec3d -> acc.add(vec3d) }?.multiply(1.0 / size.toDouble()) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index 5829f0b06..2613b8766 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -3,6 +3,8 @@ package com.lambda.module.modules.client import com.lambda.config.InteractionSettings import com.lambda.config.RotationSettings import com.lambda.module.Module +import com.lambda.util.BlockUtils.allSigns +import net.minecraft.block.Block object TaskFlow : Module( name = "TaskFlow", @@ -10,9 +12,6 @@ object TaskFlow : Module( ) { val rotationSettings = RotationSettings(this) val interactionSettings = InteractionSettings(this) - private val itemMoveDelaySetting = setting("Item Move Delay", 5, 0..20, unit = "ticks") // val disposables by setting("Disposables", ItemUtils.defaultDisposables) - - val itemMoveDelay: Long - get() = itemMoveDelaySetting.value * 50L + val ignoredBlocks = mutableSetOf().apply { addAll(allSigns) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/RootTask.kt b/common/src/main/kotlin/com/lambda/task/RootTask.kt new file mode 100644 index 000000000..b2825f9ce --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/RootTask.kt @@ -0,0 +1,12 @@ +package com.lambda.task + +object RootTask : Task() { + init { + name = "Root Task" + } + + fun addInfo(debugText: MutableList) { + debugText.add("") + debugText.addAll(info.string.split("\n")) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index f7638c7ea..0182b5252 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -1,6 +1,5 @@ package com.lambda.task -import com.lambda.Lambda import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.Event @@ -10,7 +9,6 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.threading.runSafe import com.lambda.util.BaritoneUtils -import com.lambda.util.Communication.info import com.lambda.util.Communication.logError import com.lambda.util.Communication.warn import com.lambda.util.Nameable @@ -74,6 +72,7 @@ abstract class Task( val isRunning get() = state == State.ACTIVATED || state == State.DEACTIVATED val isFailed get() = state == State.FAILED val isCompleted get() = state == State.COMPLETED + val isRoot get() = parent == null override var name = this::class.simpleName ?: "Task" // ToDo: Better color management @@ -88,13 +87,24 @@ abstract class Task( literal(" Runtime ") color(primaryColor) { - literal(DurationFormatUtils.formatDuration(age * 50L, "HH:mm:ss,SSS")) + literal(DurationFormatUtils.formatDuration(age * 50L, "HH:mm:ss,SSS").dropLast(1)) } - subTasks.forEach { + val display = subTasks.reversed().take(MAX_DEBUG_ENTRIES) + display.forEach { literal("\n${" ".repeat(depth + 1)}") text(it.info) } + + val left = subTasks.size - display.size + if (left > 0) { + literal("\n${" ".repeat(depth + 1)}And ") + color(primaryColor) { + literal("$left") + } + literal(" more...") + } + } val syncListeners = Subscriber() @@ -135,27 +145,24 @@ abstract class Task( @Ta5kBuilder fun start(parent: Task<*>?, pauseParent: Boolean = true): Task { - this.parent = parent executions++ + val owner = parent ?: RootTask + owner.subTasks.add(this) - parent?.let { par -> - par.subTasks.add(this) - LOG.info("$name was started by ${par.name}") - if (pauseParent && par.isActivated) { - LOG.info("$name deactivating parent ${par.name}") - par.deactivate() - } - } ?: LOG.info("Root started $name") + if (pauseParent && owner.isActivated && !owner.isRoot) { + LOG.info("$name deactivating parent ${owner.name}") + owner.deactivate() + } + LOG.info("$name was started by ${owner.name}") + this.parent = owner activate() - LOG.info("\n${root.info.string}") runSafe { onStart() } - LOG.info("\n${root.info.string}") return this } @Ta5kBuilder - private fun activate() { + fun activate() { if (isActivated) return LOG.info("$name activated") @@ -369,7 +376,7 @@ abstract class Task( @Ta5kBuilder inline fun withListener( - event: TickEvent.Pre, crossinline action: SafeContext.(Task) -> Unit + crossinline action: SafeContext.(Task) -> Unit ): Task { listener { action(this@Task) @@ -390,6 +397,9 @@ abstract class Task( } companion object { + val MAX_DEPTH = 20 + const val MAX_DEBUG_ENTRIES = 7 + @Ta5kBuilder fun emptyTask( name: String = "EmptyTask", diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 0f02bbe9e..0177350ad 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -6,8 +6,10 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig +import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces +import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.BlockUtils.instantBreakable @@ -15,20 +17,21 @@ import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState import net.minecraft.entity.ItemEntity -import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction class BreakBlock @Ta5kBuilder constructor( - private val blockPos: BlockPos, + private val ctx: BreakContext, private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), private val collectDrop: Boolean = false, private val noRotationForInstant: Boolean = true, ) : Task() { + val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null val SafeContext.state: BlockState get() = blockPos.blockState(world) + val SafeContext.instant: Boolean get() = instantBreakable(state, blockPos) && noRotationForInstant override fun SafeContext.onStart() { if (state.isAir && !collectDrop) { @@ -40,12 +43,12 @@ class BreakBlock @Ta5kBuilder constructor( init { listener { event -> - if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener - event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + if (instant) return@listener + event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } listener { - if (instantBreakable(state, blockPos) && noRotationForInstant) return@listener + if (instant) return@listener if (!it.context.isValid) return@listener val hitResult = it.context.hitResult?.blockResult ?: return@listener @@ -58,9 +61,9 @@ class BreakBlock @Ta5kBuilder constructor( return@listener } - if (!instantBreakable(state, blockPos) || !noRotationForInstant) return@listener - val shape = state.getOutlineShape(world, blockPos) + if (!instant) return@listener + val shape = state.getOutlineShape(world, blockPos) if (shape.isEmpty) { failure("${blockPos.toShortString()} in state $state has no outline shape") return@listener @@ -87,22 +90,23 @@ class BreakBlock @Ta5kBuilder constructor( private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { mc.particleManager.addBlockBreakingParticles(blockPos, side) -// mc.particleManager.addParticle(ParticleTypes.CRIT, hitResult.pos.x, hitResult.pos.y, hitResult.pos.z, 0.0, 0.0, 0.0) - if (!instantBreakable(state, blockPos) || !noRotationForInstant) player.swingHand(Hand.MAIN_HAND) + if (!instant) { + player.swingHand(ctx.hand) + } } } companion object { @Ta5kBuilder - fun uncheckedBreak( - blockPos: BlockPos, + fun breakBlock( + ctx: BreakContext, rotationConfig: IRotationConfig = TaskFlow.rotationSettings, interactionConfig: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet(), collectDrop: Boolean = false, noRotationForInstant: Boolean = true, ) = BreakBlock( - blockPos, + ctx, rotationConfig, interactionConfig, sides, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index ddb985ba8..77cd6a78c 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,7 +1,9 @@ package com.lambda.task.tasks +import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.RotationManager import com.lambda.interaction.construction.Blueprint import com.lambda.interaction.construction.Blueprint.Companion.toStructure import com.lambda.interaction.construction.DynamicBlueprint @@ -11,10 +13,26 @@ import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.material.ContainerManager.findBestAvailableTool +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.rotation.RotationContext +import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter +import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces +import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.util.Communication.warn +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.Communication.info +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.block.OperatorBlock +import net.minecraft.util.Hand +import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d +import kotlin.math.pow class BuildStructure( private val blueprint: Blueprint, @@ -22,7 +40,8 @@ class BuildStructure( private val skipWeakBlocks: Boolean = false, private val pathing: Boolean = true, private val finishOnDone: Boolean = true, - private val limitPerTick: Int = 20 + private val instantAtOnce: Boolean = true, + private val limitPerTick: Int = 15, ) : Task() { private var lastResult: BuildResult? = null @@ -34,22 +53,49 @@ class BuildStructure( } if (finishOnDone && structure.isEmpty()) { - this@BuildStructure.warn("Structure is empty") + failure("Structure is empty") + return@listener + } + + if (finishOnDone && blueprint.isDone(this)) { + this@BuildStructure.info("Structure is done") success(Unit) return@listener } - val results = structure.entries.fold(emptySet()) { acc, (pos, state) -> - val buildContext = BreakContext( - pos, - Direction.entries.count { world.isAir(pos.offset(it)) }, - player.pos.squaredDistanceTo(pos.toCenterPos()) - ) + val results = structure.entries.fold(mutableSetOf()) { acc, (pos, target) -> + checkRequirements(pos, target)?.let { + acc.add(it) + return@fold acc + } +// checkPlaceResults(pos, target).let { +// if (it.isEmpty()) return@let +// acc.addAll(it) +// return@fold acc +// } + checkBreakResults(pos, target).let { + if (it.isEmpty()) return@let + acc.addAll(it) + return@fold acc + } + acc + } - acc + BreakResult.Success(buildContext) + val instantResults = results.filterIsInstance() + .filter { it.context.instantBreak } + .take(limitPerTick) + + if (instantAtOnce && instantResults.isNotEmpty()) { + cancelSubTasks() + instantResults.forEach { + it.resolve.start(this@BuildStructure, false) + } + lastResult = instantResults.last() + return@listener } -// results.sorted().take(limitPerTick) + val res = results.sorted() + res results.minOrNull()?.let { result -> if (lastResult == result) return@listener @@ -62,6 +108,195 @@ class BuildStructure( } } + private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? { + /* the chunk is not loaded */ + if (!world.isChunkLoaded(pos)) { + return BuildResult.ChunkNotLoaded(pos) + } + + val state = pos.blockState(world) + + /* block is already in the correct state */ + if (target.matches(state, pos, world)) { + return BuildResult.Done(pos) + } + + /* block should be ignored */ + if (state.block in TaskFlow.ignoredBlocks) { + return BuildResult.Ignored(pos) + } + + /* the player is in the wrong game mode to alter the block state */ + if (player.isBlockBreakingRestricted(world, pos, interaction.currentGameMode)) { + return BuildResult.Restricted(pos) + } + + /* the player has no permissions to alter the block state */ + if (state.block is OperatorBlock && !player.isCreativeLevelTwoOp) { + return BuildResult.NoPermission(pos, state) + } + + /* block is outside the world so it cant be altered */ + if (!world.worldBorder.contains(pos) || world.isOutOfHeightLimit(pos)) { + return BuildResult.OutOfWorld(pos) + } + + /* block is unbreakable, so it cant be broken or replaced */ + if (state.getHardness(world, pos) < 0) { + return BuildResult.Unbreakable(pos, state) + } + + return null + } + + private fun SafeContext.checkPlaceResults(pos: BlockPos, target: TargetState): Set { + val acc = mutableSetOf() + + val state = pos.blockState(world) + if (target is TargetState.Air || !state.isReplaceable) return acc + + return acc + } + + private fun SafeContext.checkBreakResults(pos: BlockPos, target: TargetState): Set { + val acc = mutableSetOf() + val state = pos.blockState(world) + + /* is a block that will be destroyed by breaking adjacent blocks */ + if (skipWeakBlocks && state.block.hardness == 0f && !state.isAir) { + acc.add(BuildResult.Ignored(pos)) + return acc + } + + /* player is standing on top of the block */ + val pBox = player.boundingBox + val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) + world.findSupportingBlockPos(player, aabb).orElse(null)?.let { support -> + if (support != pos) return@let + acc.add(BreakResult.PlayerOnTop(pos, state)) + return acc + } + + /* liquid needs to be submerged first to be broken */ + if (!state.fluidState.isEmpty && state.isReplaceable) { + val submerge = checkPlaceResults(pos, TargetState.Solid) + acc.add(BreakResult.Submerge(pos, state, submerge)) + acc.addAll(submerge) + return acc + } + + val adjacentLiquids = Direction.entries.filter { + it != Direction.DOWN && !pos.offset(it).blockState(world).fluidState.isEmpty + } + + /* block has liquids next to it that will leak when broken */ + if (adjacentLiquids.isNotEmpty()) { + acc.add(BreakResult.BlockedByLiquid(pos, state)) + adjacentLiquids.forEach { + val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid) + acc.addAll(submerge) + } + return acc + } + + /* The current selected item cant mine the block */ + Hand.entries.forEach { + val stack = player.getStackInHand(it) + if (stack.isEmpty) return@forEach + if (stack.item.canMine(state, world, pos, player)) return@forEach + acc.add(BreakResult.ItemCantMine(pos, state, stack.item)) + return acc + } + + val eye = player.getCameraPosVec(mc.tickDelta) + + val interact = TaskFlow.interactionSettings + val rotation = TaskFlow.rotationSettings + val currentRotation = RotationManager.currentRotation + val currentCast = currentRotation.rayCast( + interact.reach, + interact.rayCastMask, + eye + ) + + val voxelShape = state.getOutlineShape(world, pos) + val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } + val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } + + /* the player is buried inside the block */ + if (boxes.any { it.contains(eye) }) { + currentCast?.blockResult?.let { blockHit -> + val rotationContext = RotationContext(currentRotation, rotation, currentCast, verify) + val breakContext = BreakContext( + eye, + blockHit, + rotationContext, + state, + player.activeHand, + instantBreakable(state, pos) + ) + acc.add(BreakResult.Success(pos, breakContext)) + return acc + } + } + + val validHits = mutableMapOf() + val reachSq = interact.reach.pow(2) + + boxes.forEach { box -> + // ToDo: Verify needed resolution + scanVisibleSurfaces(eye, box, emptySet(), 2) { vec -> + if (eye distSq vec > reachSq) { + acc.add(BreakResult.OutOfReach(pos, eye.distanceTo(vec))) + return@scanVisibleSurfaces + } + val cast = eye.rotationTo(vec).rayCast( + interact.reach, + interact.rayCastMask, + eye + ) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + validHits[vec] = cast + } + } + + validHits.keys.mostCenter?.let { optimum -> + validHits.minByOrNull { optimum distSq it.key }?.let { closest -> + val optimumRotation = eye.rotationTo(closest.key) + RotationContext(optimumRotation, rotation, closest.value, verify) + } + }?.let { bestRotation -> + val blockHit = bestRotation.hitResult?.blockResult ?: return@let + + val breakContext = BreakContext( + eye, + blockHit, + bestRotation, + state, + player.activeHand, + instantBreakable(state, pos) + ) + + /* player has a better tool for the job available */ + findBestAvailableTool(state)?.let { bestTool -> + var added = false + Hand.entries.forEach { + val stack = player.getStackInHand(it) + if (stack.isEmpty) return@forEach + if (stack.item == bestTool) return@forEach + added = true + acc.add(BreakResult.WrongTool(pos, breakContext, bestTool)) + } + if (added) return acc + } + + acc.add(BreakResult.Success(pos, breakContext)) + } + + return acc + } + companion object { @Ta5kBuilder fun buildStructure( @@ -80,7 +315,7 @@ class BuildStructure( @Ta5kBuilder fun breakAndCollectBlock( - blockPos: BlockPos + blockPos: BlockPos, ) = BuildStructure( blockPos.toStructure(TargetState.Air).toBlueprint(), collectDrops = true @@ -88,7 +323,7 @@ class BuildStructure( @Ta5kBuilder fun breakBlock( - blockPos: BlockPos + blockPos: BlockPos, ) = BuildStructure( blockPos.toStructure(TargetState.Air).toBlueprint() ) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 0f292e1a6..e5d0271d6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -11,9 +11,10 @@ import com.lambda.task.Task import com.lambda.util.BaritoneUtils import net.minecraft.util.math.BlockPos +// ToDo: Custom heuristic goals class GoalTask( private val goal: Goal, - private val check: SafeContext.() -> Boolean = { true } + private val check: SafeContext.() -> Boolean = { false } ) : Task() { override fun SafeContext.onStart() { @@ -22,17 +23,27 @@ class GoalTask( init { listener { - if (!BaritoneUtils.isActive && check()) { + if (!BaritoneUtils.isActive || check()) { success(Unit) } } } companion object { + @Ta5kBuilder + fun moveToBlock(blockPos: BlockPos) = + GoalTask(GoalBlock(blockPos)) + @Ta5kBuilder fun moveToXY(blockPos: BlockPos) = GoalTask(GoalXZ(blockPos.x, blockPos.z)) + @Ta5kBuilder + fun moveUntilLoaded(blockPos: BlockPos) = + GoalTask(GoalBlock(blockPos)) { + world.isPosLoaded(blockPos.x, blockPos.z) + } + @Ta5kBuilder fun moveIntoEntityRange(blockPos: BlockPos, range: Int = 3) = GoalTask(GoalNear(blockPos, range)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index be4ad83f5..53b504e0f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -5,6 +5,7 @@ import com.lambda.event.events.ScreenHandlerEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.world.raycast.RayCastUtils.blockResult @@ -41,7 +42,7 @@ class OpenContainer( } listener { event -> - event.lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } listener { diff --git a/common/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt b/common/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt index 303068242..230241523 100644 --- a/common/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt +++ b/common/src/main/kotlin/com/lambda/util/DynamicReflectionSerializer.kt @@ -1,5 +1,6 @@ package com.lambda.util +import com.lambda.util.DynamicReflectionSerializer.formatFieldValue import com.mojang.serialization.Codec import net.minecraft.block.BlockState import net.minecraft.client.resource.language.TranslationStorage @@ -86,7 +87,7 @@ object DynamicReflectionSerializer { } val fieldValue = field.get(this) val fieldIndent = indent + " ".repeat(INDENT) - builder.appendLine("$fieldIndent${field.name}: ${formatFieldValue(fieldValue)}") + builder.appendLine("$fieldIndent${field.name}: ${fieldValue.formatFieldValue()}") if (currentDepth < maxRecursionDepth && fieldValue != null @@ -105,21 +106,20 @@ object DynamicReflectionSerializer { } } - private fun formatFieldValue(value: Any?): String { - return when (value) { - is String -> "\"$value\"" - is Collection<*> -> "[${value.joinToString(", ") { formatFieldValue(it) }}]" - is Array<*> -> "[${value.joinToString(", ") { formatFieldValue(it) }}]" + private fun Any?.formatFieldValue(): String = + when (this) { + is String -> "\"${this}\"" + is Collection<*> -> "[${joinToString(", ") { it.formatFieldValue() }}]" + is Array<*> -> "[${joinToString(", ") { it.formatFieldValue() }}]" is Map<*, *> -> "{${ - value.entries.joinToString(", ") { (k, v) -> - "${formatFieldValue(k)}: ${formatFieldValue(v)}" + entries.joinToString(", ") { (k, v) -> + "${k.formatFieldValue()}: ${v.formatFieldValue()}" } }}" - is Text -> value.string - is Identifier -> "${value.namespace}:${value.path}" - is NbtCompound -> value.asString() - is RegistryEntry<*> -> "${value.value()}" - else -> value?.toString() ?: "null" + is Text -> string + is Identifier -> "$namespace:$path" + is NbtCompound -> asString() + is RegistryEntry<*> -> "${value()}" + else -> this?.toString() ?: "null" } - } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index 4d2b4de41..60a29c744 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -68,6 +68,8 @@ object RayCastUtils { return this as? BlockHitResult } + fun HitResult.distanceTo(pos: Vec3d) = pos.distanceTo(pos) + val HitResult.orNull get() = entityResult ?: blockResult val HitResult?.orMiss get() = this ?: object : HitResult(mc.player?.eyePos ?: Vec3d.ZERO) { From d1e0c66e7f5bcd34ed969263a8769d3728e07510 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 30 May 2024 18:23:02 +0200 Subject: [PATCH 12/47] Stack selection container transfer fixes --- .../command/commands/TransferCommand.kt | 21 +++++++ .../construction/context/BreakContext.kt | 2 +- .../construction/result/BreakResult.kt | 2 +- .../interaction/material/ContainerManager.kt | 4 ++ .../interaction/material/MaterialContainer.kt | 26 ++++---- .../material/container/ChestContainer.kt | 8 +-- .../material/container/HotbarContainer.kt | 10 ++-- .../material/container/InventoryContainer.kt | 8 +-- .../material/container/MainHandContainer.kt | 59 +++++++++---------- .../com/lambda/module/modules/player/Nuker.kt | 5 +- .../src/main/kotlin/com/lambda/task/Task.kt | 4 +- .../com/lambda/task/tasks/BuildStructure.kt | 16 +++-- .../com/lambda/task/tasks/OpenContainer.kt | 4 +- .../main/kotlin/com/lambda/util/BlockUtils.kt | 4 -- 14 files changed, 96 insertions(+), 77 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt new file mode 100644 index 000000000..f6fc90233 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -0,0 +1,21 @@ +package com.lambda.command.commands + +import com.lambda.brigadier.execute +import com.lambda.command.LambdaCommand +import com.lambda.interaction.material.ContainerManager.transfer +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.MainHandContainer +import com.lambda.util.primitives.extension.CommandBuilder +import net.minecraft.item.Items + +object TransferCommand : LambdaCommand( + name = "transfer", + usage = "transfer ", + description = "Transfer items to a container" +) { + override fun CommandBuilder.create() { + execute { + Items.OBSIDIAN.select().transfer(MainHandContainer).solve.start(null) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 48354177e..3d3eb0397 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -14,7 +14,7 @@ data class BreakContext( val result: BlockHitResult, val rotation: RotationContext, val checkedState: BlockState, - val hand: Hand, + var hand: Hand, val instantBreak: Boolean, ) : BuildContext, ComparableContext { override val distance: Double by lazy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 84c3f97d8..9e05f93ae 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -2,9 +2,9 @@ package com.lambda.interaction.construction.result import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.material.ContainerManager.findBestAvailableTool +import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack -import com.lambda.interaction.material.container.CreativeContainer.transfer import com.lambda.interaction.material.container.MainHandContainer import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 989c16570..7cfb9712b 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -70,6 +70,10 @@ object ContainerManager : Loadable { } } + fun StackSelection.transfer(destination: MaterialContainer) = + findContainerWithSelection(this)?.transfer(this, destination) + ?: throw NoContainerFound(this) + fun findContainer( block: (MaterialContainer) -> Boolean ): MaterialContainer? = container.find(block) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index a2699f85c..3e2977c62 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -52,26 +52,26 @@ abstract class MaterialContainer( open fun spaceLeft(selection: StackSelection) = filter(selection).spaceLeft + stacks.empty * selection.stackSize - fun StackSelection.transfer(destination: MaterialContainer): TransferResult { - val amount = available(this) - if (amount < count) { - return TransferResult.MissingItems(amount - count) + fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { + val amount = available(selection) + if (amount < selection.count) { + return TransferResult.MissingItems(amount - selection.count) } - val space = destination.spaceLeft(this) - if (space == 0) { - return TransferResult.NoSpace - } +// val space = destination.spaceLeft(selection) +// if (space == 0) { +// return TransferResult.NoSpace +// } - val transferAmount = minOf(amount, space) - selector = { true } - count = transferAmount +// val transferAmount = minOf(amount, space) +// selection.selector = { true } +// selection.count = transferAmount return TransferResult.Success( prepare().onSuccess { prep, _ -> - withdraw(this@transfer).onSuccess { with, _ -> + withdraw(selection).onSuccess { with, _ -> destination.prepare().onSuccess { dest, _ -> - destination.deposit(this@transfer).start(dest) + destination.deposit(selection).start(dest) }.start(with) }.start(prep) } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt index 94fdd4456..f19dbdbc2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -30,8 +30,8 @@ data class ChestContainer( override fun withdraw(selection: StackSelection) = openContainer(blockPos) - .withMaxAttempts(3) - .withTimeout(20) +// .withMaxAttempts(3) +// .withTimeout(20) .onSuccess { open, screen -> info("Withdrawing $selection from ${screen.type}") withdraw(screen, selection).start(open) @@ -39,8 +39,8 @@ data class ChestContainer( override fun deposit(selection: StackSelection) = openContainer(blockPos) - .withMaxAttempts(3) - .withTimeout(20) +// .withMaxAttempts(3) +// .withTimeout(20) .onSuccess { open, screen -> info("Depositing $selection to ${screen.type}") deposit(screen, selection).start(open) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt index f55f1053c..504169499 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -4,7 +4,10 @@ import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.util.player.SlotUtils.hotbar +import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider import net.minecraft.item.ItemStack object HotbarContainer : MaterialContainer(Rank.HOTBAR) { @@ -12,11 +15,10 @@ object HotbarContainer : MaterialContainer(Rank.HOTBAR) { get() = mc.player?.hotbar ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection): Task<*> { - TODO("Not yet implemented") - } + override fun withdraw(selection: StackSelection) = emptyTask("HotbarWithdraw") override fun deposit(selection: StackSelection): Task<*> { - TODO("Not yet implemented") + val handledScreen = mc.currentScreen as? ScreenHandlerProvider<*> ?: return emptyTask() + return deposit(handledScreen.screenHandler, selection) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt index 6dc4f7f80..adf41449c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -12,11 +12,7 @@ object InventoryContainer : MaterialContainer(Rank.INVENTORY) { get() = mc.player?.combined ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection): Task<*> { - TODO("Not yet implemented") - } + override fun withdraw(selection: StackSelection) = Task.emptyTask() - override fun deposit(selection: StackSelection): Task<*> { - TODO("Not yet implemented") - } + override fun deposit(selection: StackSelection) = Task.emptyTask() } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index a754c2c2c..06cbd37d0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -5,6 +5,8 @@ import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task +import com.lambda.task.Task.Companion.buildTask +import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.player.SlotUtils.combined import com.lambda.util.player.SlotUtils.hotbar import kotlinx.coroutines.delay @@ -18,38 +20,31 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override fun withdraw(selection: StackSelection): Task<*> { - TODO("Not yet implemented") - } -// InventoryContainer.stacks.filter(selection.selector).take(selection.count).forEach { stack -> -// if (ItemStack.areEqual(stack, player.mainHandStack)) { -// return@forEach -// } -// -// if (ItemStack.areEqual(stack, player.offHandStack)) { -// connection.sendPacket( -// PlayerActionC2SPacket( -// PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, -// BlockPos.ORIGIN, -// Direction.DOWN, -// ), -// ) -// delay(TaskFlow.itemMoveDelay) -// return@forEach -// } -// -// if (stack in player.hotbar) { -// player.inventory.selectedSlot = player.hotbar.indexOf(stack) -// delay(TaskFlow.itemMoveDelay) -// return@forEach -// } -// -// interaction.pickFromInventory(player.combined.indexOf(stack)) -// delay(TaskFlow.itemMoveDelay) -// } -// } + override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromMainHand") + + override fun deposit(selection: StackSelection) = buildTask("DepositToMainHand") { + InventoryContainer.filter(selection).firstOrNull()?.let { stack -> + if (ItemStack.areEqual(stack, player.mainHandStack)) { + return@buildTask + } + + if (ItemStack.areEqual(stack, player.offHandStack)) { + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN, + ), + ) + return@buildTask + } + + if (stack in player.hotbar) { + player.inventory.selectedSlot = player.hotbar.indexOf(stack) + return@buildTask + } - override fun deposit(selection: StackSelection): Task<*> { - TODO("Not yet implemented") + interaction.pickFromInventory(player.combined.indexOf(stack)) + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 1061b8cdc..705a987ee 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -8,9 +8,8 @@ import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BlockUtils.blockPos -import com.lambda.util.BlockUtils.instantBreakable -import com.lambda.util.BlockUtils.safeLiquid import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.KeyCode import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i @@ -23,7 +22,6 @@ object Nuker : Module( ) { private val flatten by setting("Flatten", true) private val onlyBreakInstant by setting("Only Break Instant", true) - private val doNotExposeLiquids by setting("Do Not Expose Liquids", true) private val fillFloor by setting("Fill Floor", false) private val range = Vec3i(4, 4, 4) // TODO: Customizable @@ -42,7 +40,6 @@ object Nuker : Module( .filter { !world.isAir(it) } .filter { !flatten || it.y >= player.blockPos.y } .filter { !onlyBreakInstant || instantBreakable(it.blockState(world), it) } - .filter { !doNotExposeLiquids || safeLiquid(it) } .associateWith { TargetState.Air } // if (fillFloor) { diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 0182b5252..404c15412 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -400,10 +400,12 @@ abstract class Task( val MAX_DEPTH = 20 const val MAX_DEBUG_ENTRIES = 7 + interface EmptyTask + @Ta5kBuilder fun emptyTask( name: String = "EmptyTask", - ) = object : Task() { + ): Task = object : EmptyTask, Task() { init { this.name = name } override fun SafeContext.onStart() { success(Unit) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 77cd6a78c..3812d1040 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -103,6 +103,8 @@ class BuildStructure( lastResult = result cancelSubTasks() + + if (!pathing && result is BreakResult.OutOfReach) return@let result.resolve.start(this@BuildStructure, false) } } @@ -280,15 +282,17 @@ class BuildStructure( /* player has a better tool for the job available */ findBestAvailableTool(state)?.let { bestTool -> - var added = false - Hand.entries.forEach { + Hand.entries.firstOrNull { val stack = player.getStackInHand(it) - if (stack.isEmpty) return@forEach - if (stack.item == bestTool) return@forEach - added = true + stack.item == bestTool + }?.let { hand -> + breakContext.hand = hand + acc.add(BreakResult.Success(pos, breakContext)) + return acc + } ?: run { acc.add(BreakResult.WrongTool(pos, breakContext, bestTool)) + return acc } - if (added) return acc } acc.add(BreakResult.Success(pos, breakContext)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 53b504e0f..053d537ab 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -28,7 +28,7 @@ class OpenContainer( listener> { screenHandler = it.screenHandler - if (!waitForSlotLoad) success(it.screenHandler) + if (!waitForSlotLoad || slotsLoaded) success(it.screenHandler) } listener> { @@ -42,10 +42,12 @@ class OpenContainer( } listener { event -> + if (screenHandler != null) return@listener event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } listener { + if (screenHandler != null) return@listener if (!it.context.isValid) return@listener val hitResult = it.context.hitResult?.blockResult ?: return@listener interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index 1882aa6c1..cc128ab80 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -105,10 +105,6 @@ object BlockUtils { // info("State: $blockState Ticks to break: $ticksNeeded") return ticksNeeded <= 1 && ticksNeeded != 0f } - fun SafeContext.safeLiquid(blockPos: BlockPos) = Direction.entries.all { - if (it == Direction.UP) return@all true - world.getFluidState(blockPos.offset(it)).isEmpty - } val Vec3i.blockPos: BlockPos get() = BlockPos(this) val Block.item: Item get() = asItem() val Vec3d.flooredPos: BlockPos get() = BlockPos(floor(x).toInt(), floor(y).toInt(), floor(z).toInt()) From 8bf37784a5e4413cb92a49424b0d76dcf9eddfac Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 30 May 2024 18:53:51 +0200 Subject: [PATCH 13/47] Break optimizations --- .../interaction/material/ContainerManager.kt | 1 - .../material/container/EnderChestContainer.kt | 2 -- .../material/container/MainHandContainer.kt | 3 -- .../com/lambda/task/tasks/BreakBlock.kt | 30 ++++++------------- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 7cfb9712b..67b07bf6d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -90,7 +90,6 @@ object ContainerManager : Loadable { return container.find { it.available(selection) >= selection.count } } - fun findContainerWithStacks( count: Int = StackSelection.DEFAULT_AMOUNT, selection: (ItemStack) -> Boolean, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index ad3618bb8..730ab4a26 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -6,9 +6,7 @@ import com.lambda.task.Task import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer -import net.minecraft.block.Blocks import net.minecraft.item.ItemStack -import net.minecraft.item.Items import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.util.math.BlockPos diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index 06cbd37d0..ee6a4fca7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -3,13 +3,10 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.module.modules.client.TaskFlow -import com.lambda.task.Task import com.lambda.task.Task.Companion.buildTask import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.player.SlotUtils.combined import com.lambda.util.player.SlotUtils.hotbar -import kotlinx.coroutines.delay import net.minecraft.item.ItemStack import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket import net.minecraft.util.math.BlockPos diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 0177350ad..2e0c400ea 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -8,11 +8,9 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.InteractionConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.rotation.IRotationConfig -import com.lambda.interaction.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState @@ -26,12 +24,13 @@ class BreakBlock @Ta5kBuilder constructor( private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), private val collectDrop: Boolean = false, - private val noRotationForInstant: Boolean = true, + private val dontRotate: Boolean = true, + private val swingHand: Boolean = true, + private val particles: Boolean = true, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null val SafeContext.state: BlockState get() = blockPos.blockState(world) - val SafeContext.instant: Boolean get() = instantBreakable(state, blockPos) && noRotationForInstant override fun SafeContext.onStart() { if (state.isAir && !collectDrop) { @@ -43,12 +42,12 @@ class BreakBlock @Ta5kBuilder constructor( init { listener { event -> - if (instant) return@listener + if (dontRotate) return@listener event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } listener { - if (instant) return@listener + if (dontRotate) return@listener if (!it.context.isValid) return@listener val hitResult = it.context.hitResult?.blockResult ?: return@listener @@ -61,18 +60,9 @@ class BreakBlock @Ta5kBuilder constructor( return@listener } - if (!instant) return@listener + if (!dontRotate) return@listener - val shape = state.getOutlineShape(world, blockPos) - if (shape.isEmpty) { - failure("${blockPos.toShortString()} in state $state has no outline shape") - return@listener - } - - shape.boundingBox - .getVisibleSurfaces(player.eyePos).firstOrNull()?.let { side -> - breakBlock(side) - } + breakBlock(ctx.result.side) } listener { @@ -89,10 +79,8 @@ class BreakBlock @Ta5kBuilder constructor( private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { - mc.particleManager.addBlockBreakingParticles(blockPos, side) - if (!instant) { - player.swingHand(ctx.hand) - } + if (particles) mc.particleManager.addBlockBreakingParticles(blockPos, side) + if (swingHand) player.swingHand(ctx.hand) } } From f28093850c41903052f4cfbf618573b76f5e5229 Mon Sep 17 00:00:00 2001 From: Constructor Date: Thu, 30 May 2024 23:55:46 +0200 Subject: [PATCH 14/47] Placement interaction simulation --- .../construction/context/BreakContext.kt | 10 +- .../construction/context/BuildContext.kt | 8 +- .../construction/context/PlaceContext.kt | 45 ++++++ .../construction/result/BreakResult.kt | 65 -------- .../construction/result/BuildResult.kt | 86 +++++++++- .../construction/result/PlaceResult.kt | 110 +++++++++++++ .../interaction/construction/result/Rank.kt | 11 +- .../lambda/module/modules/debug/BuildTest.kt | 27 ++++ .../com/lambda/task/tasks/BreakBlock.kt | 12 +- .../com/lambda/task/tasks/BuildStructure.kt | 150 ++++++++++++++++-- .../com/lambda/task/tasks/PlaceBlock.kt | 77 +++++++++ .../com/lambda/util/player/MovementUtils.kt | 1 - .../src/main/resources/lambda.accesswidener | 3 +- 13 files changed, 504 insertions(+), 101 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 3d3eb0397..862b070f4 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -6,6 +6,7 @@ import com.lambda.util.world.raycast.RayCastUtils.distanceTo import net.minecraft.block.BlockState import net.minecraft.util.Hand import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d @@ -13,10 +14,13 @@ data class BreakContext( val pov: Vec3d, val result: BlockHitResult, val rotation: RotationContext, - val checkedState: BlockState, - var hand: Hand, + override val checkedState: BlockState, + override var hand: Hand, val instantBreak: Boolean, -) : BuildContext, ComparableContext { +) : BuildContext { + override val resultingPos: BlockPos + get() = result.blockPos + override val distance: Double by lazy { result.distanceTo(pov) } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt index e861dab7e..83cd5f4fa 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/BuildContext.kt @@ -1,9 +1,13 @@ package com.lambda.interaction.construction.context import net.minecraft.block.BlockState +import net.minecraft.util.Hand +import net.minecraft.util.math.BlockPos -interface BuildContext { +interface BuildContext : ComparableContext { val distance: Double val expectedState: BlockState -// val resultingPos: BlockPos + val checkedState: BlockState + val hand: Hand + val resultingPos: BlockPos } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt new file mode 100644 index 000000000..89645c3b2 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -0,0 +1,45 @@ +package com.lambda.interaction.construction.context + +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.rotation.RotationContext +import com.lambda.util.BlockUtils +import net.minecraft.block.BlockState +import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Vec3d + +data class PlaceContext( + val pov: Vec3d, + val result: BlockHitResult, + val rotation: RotationContext, + override val distance: Double, + override val expectedState: BlockState, + override val checkedState: BlockState, + val targetState: TargetState, + override val hand: Hand, + val sneak: Boolean, + val insideBlock: Boolean, +) : BuildContext { + override val resultingPos: BlockPos + get() = result.blockPos.offset(result.side) + + override fun compareTo(other: ComparableContext): Int { + return when (other) { + is PlaceContext -> compareBy { + BlockUtils.fluids.indexOf(it.checkedState.fluidState.fluid) + }.thenByDescending { + it.checkedState.fluidState.level + }.thenBy { + it.hand + }.thenBy { + it.sneak + }.thenBy { + it.distance + }.thenBy { + it.insideBlock + }.compare(this, other) + else -> 1 + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 9e05f93ae..fe30fbc14 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -37,27 +37,6 @@ sealed class BreakResult : BuildResult() { } } - /** - * Represents a break out of reach. - * @param blockPos The position of the block that is out of reach. - * @param distance The distance to the hit vector. - */ - data class OutOfReach( - override val blockPos: BlockPos, - val distance: Double - ) : Resolvable, BreakResult() { - override val rank = Rank.BREAK_OUT_OF_REACH - - override val resolve = moveToBlock(blockPos) - - override fun compareTo(other: ComparableResult): Int { - return when (other) { - is OutOfReach -> distance.compareTo(other.distance) - else -> super.compareTo(other) - } - } - } - /** * Represents a break configuration where the hit side is not exposed to air. * @param blockPos The position of the block that is not exposed. @@ -79,28 +58,6 @@ sealed class BreakResult : BuildResult() { } } - /** - * The checked break configuration hits on a side not in the player direction. - * @param blockPos The position of the block that is not exposed. - * @param side The side that is not exposed. - */ - data class NotVisible( - override val blockPos: BlockPos, - val side: Direction, - val distance: Double - ) : Resolvable, BreakResult() { - override val rank = Rank.BREAK_NOT_VISIBLE - - override val resolve = emptyTask() - - override fun compareTo(other: ComparableResult): Int { - return when (other) { - is NotVisible -> distance.compareTo(other.distance) - else -> super.compareTo(other) - } - } - } - /** * The equipped item is not suitable for breaking blocks. * @param blockState The block state that is being broken. @@ -129,28 +86,6 @@ sealed class BreakResult : BuildResult() { } } - /** - * Player has an inefficient tool equipped. - * @param bestTool The best tool for the block state. - */ - data class WrongTool( - override val blockPos: BlockPos, - val context: BreakContext, - val bestTool: Item - ) : Resolvable, BreakResult() { - override val rank = Rank.BREAK_WRONG_TOOL - - override val resolve: Task<*> = - bestTool.select().transfer(MainHandContainer).solve - - override fun compareTo(other: ComparableResult): Int { - return when (other) { - is WrongTool -> context.compareTo(other.context) - else -> super.compareTo(other) - } - } - } - /** * The block is a liquid and first has to be submerged. * @param blockPos The position of the block that is a liquid. diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 9ac564252..e16efbfe0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,9 +1,18 @@ package com.lambda.interaction.construction.result +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.material.ContainerManager.transfer +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.MainHandContainer +import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.tasks.GoalTask.Companion.moveToBlock import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded -import net.minecraft.block.Block import net.minecraft.block.BlockState +import net.minecraft.item.Item import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction abstract class BuildResult : ComparableResult { abstract val blockPos: BlockPos @@ -51,7 +60,7 @@ abstract class BuildResult : ComparableResult { */ data class Restricted( override val blockPos: BlockPos - ) : BreakResult() { + ) : BuildResult() { override val rank = Rank.BREAK_RESTRICTED } @@ -63,7 +72,7 @@ abstract class BuildResult : ComparableResult { data class NoPermission( override val blockPos: BlockPos, val blockState: BlockState - ) : BreakResult() { + ) : BuildResult() { override val rank = Rank.BREAK_NO_PERMISSION } @@ -73,7 +82,7 @@ abstract class BuildResult : ComparableResult { */ data class OutOfWorld( override val blockPos: BlockPos - ) : BreakResult() { + ) : BuildResult() { override val rank = Rank.BREAK_OUT_OF_WORLD } @@ -85,7 +94,72 @@ abstract class BuildResult : ComparableResult { data class Unbreakable( override val blockPos: BlockPos, val blockState: BlockState - ) : BreakResult() { - override val rank = Rank.BREAK_UNBREAKABLE + ) : BuildResult() { + override val rank = Rank.UNBREAKABLE + } + + /** + * The checked configuration hits on a side not in the player direction. + * @param blockPos The position of the block that is not exposed. + * @param side The side that is not exposed. + */ + data class NotVisible( + override val blockPos: BlockPos, + val side: Direction, + val distance: Double + ) : Resolvable, BuildResult() { + override val rank = Rank.NOT_VISIBLE + + override val resolve = emptyTask() + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is NotVisible -> distance.compareTo(other.distance) + else -> super.compareTo(other) + } + } + } + + /** + * Player has an inefficient tool equipped. + * @param neededItem The best tool for the block state. + */ + data class WrongItem( + override val blockPos: BlockPos, + val context: BuildContext, + val neededItem: Item + ) : Resolvable, BuildResult() { + override val rank = Rank.WRONG_ITEM + + override val resolve: Task<*> = + neededItem.select().transfer(MainHandContainer).solve + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is WrongItem -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } + } + + /** + * Represents a break out of reach. + * @param blockPos The position of the block that is out of reach. + * @param distance The distance to the hit vector. + */ + data class OutOfReach( + override val blockPos: BlockPos, + val distance: Double + ) : Resolvable, BuildResult() { + override val rank = Rank.OUT_OF_REACH + + override val resolve = moveToBlock(blockPos) + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is OutOfReach -> distance.compareTo(other.distance) + else -> super.compareTo(other) + } + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt new file mode 100644 index 000000000..8bd40fedf --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -0,0 +1,110 @@ +package com.lambda.interaction.construction.result + +import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.task.Task +import com.lambda.task.tasks.BuildStructure.Companion.breakBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToBlock +import com.lambda.task.tasks.PlaceBlock.Companion.placeBlock +import net.minecraft.block.BlockState +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack +import net.minecraft.util.math.BlockPos + +/** + * [PlaceResult] represents the result of a placement simulation. + * Holds data about the placement and the result of the simulation. + * Every [BuildResult] can [resolve] its own problem. + * Every [BuildResult] can be compared to another [BuildResult]. + * First based on the context, then based on the [Rank]. + */ +sealed class PlaceResult : BuildResult() { + /** + * Represents a successful placement. All checks have been passed. + * @param context The context of the placement. + */ + data class Success( + override val blockPos: BlockPos, + val context: PlaceContext, + ) : Resolvable, PlaceResult() { + override val rank = Rank.PLACE_SUCCESS + + override val resolve = placeBlock(context) + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is Success -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } + } + + /** + * The calculated placement configuration does not match the simulated outcome. + * @param blockPos The position of the block that is not placed correctly. + * @param expected The expected placement configuration. + * @param simulated The simulated placement configuration. + */ + data class NoIntegrity( + override val blockPos: BlockPos, + val expected: BlockState, + val simulated: ItemPlacementContext + ) : PlaceResult() { + override val rank = Rank.PLACE_NO_INTEGRITY + } + + /** + * The placement configuration cannot replace the block at the target position. + * @param simulated The simulated placement configuration. + */ + data class CantReplace( + override val blockPos: BlockPos, + val simulated: ItemPlacementContext + ) : Resolvable, PlaceResult() { + override val rank = Rank.PLACE_CANT_REPLACE + + override val resolve = breakBlock(simulated.blockPos) + } + + /** + * The placement configuration exceeds the maximum scaffold distance. + * @param simulated The simulated placement configuration. + */ + data class ScaffoldExceeded( + override val blockPos: BlockPos, + val simulated: ItemPlacementContext + ) : PlaceResult() { + override val rank = Rank.PLACE_SCAFFOLD_EXCEEDED + } + + /** + * The interaction with the block is restricted due to the feature being disabled. + */ + data class BlockFeatureDisabled( + override val blockPos: BlockPos, + val itemStack: ItemStack, + ) : PlaceResult() { + override val rank = Rank.PLACE_BLOCK_FEATURE_DISABLED + } + + /** + * The player has no permission to interact with the block. Or the stack cannot be used on the block. + */ + data class IllegalUsage( + override val blockPos: BlockPos + ) : PlaceResult() { + override val rank = Rank.PLACE_ILLEGAL_USAGE + } + + /** + * The item stack is not a block item. + * This will be resolved by analyzing interaction with non-block items. + */ + data class NotItemBlock( + override val blockPos: BlockPos, + val itemStack: ItemStack + ) : Resolvable, PlaceResult() { + override val rank = Rank.PLACE_NOT_ITEM_BLOCK + + override val resolve = Task.emptyTask() // ToDo: analyze interaction with non-block items + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt index 4369157ea..5325d8494 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -4,13 +4,10 @@ enum class Rank { // solvable BREAK_SUCCESS, PLACE_SUCCESS, - BREAK_WRONG_TOOL, + WRONG_ITEM, BREAK_ITEM_CANT_MINE, - PLACE_WRONG_ITEM, - BREAK_NOT_VISIBLE, - PLACE_NOT_VISIBLE, - BREAK_OUT_OF_REACH, - PLACE_OUT_OF_REACH, + NOT_VISIBLE, + OUT_OF_REACH, BREAK_NOT_EXPOSED, BREAK_OUT_OF_WORLD, PLACE_OUT_OF_WORLD, @@ -24,7 +21,7 @@ enum class Rank { // not solvable BREAK_RESTRICTED, - BREAK_UNBREAKABLE, + UNBREAKABLE, BREAK_NO_PERMISSION, PLACE_SCAFFOLD_EXCEEDED, PLACE_BLOCK_FEATURE_DISABLED, diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt new file mode 100644 index 000000000..6cd51d9cd --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt @@ -0,0 +1,27 @@ +package com.lambda.module.modules.debug + +import com.lambda.interaction.construction.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import net.minecraft.block.Blocks + +object BuildTest : Module( + name = "BuildTest", + description = "Test module for build", + defaultTags = setOf(ModuleTag.DEBUG) +) { + init { + onEnable { + buildStructure { + player.blockPos + .offset(player.horizontalFacing) + .offset(player.horizontalFacing) + .toStructure(TargetState.Block(Blocks.NETHERRACK)) + .toBlueprint() + }.start(null) + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 2e0c400ea..62c4d8d63 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -24,7 +24,7 @@ class BreakBlock @Ta5kBuilder constructor( private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), private val collectDrop: Boolean = false, - private val dontRotate: Boolean = true, + private val rotate: Boolean = false, private val swingHand: Boolean = true, private val particles: Boolean = true, ) : Task() { @@ -42,12 +42,12 @@ class BreakBlock @Ta5kBuilder constructor( init { listener { event -> - if (dontRotate) return@listener + if (!rotate) return@listener event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) } listener { - if (dontRotate) return@listener + if (!rotate) return@listener if (!it.context.isValid) return@listener val hitResult = it.context.hitResult?.blockResult ?: return@listener @@ -60,7 +60,7 @@ class BreakBlock @Ta5kBuilder constructor( return@listener } - if (!dontRotate) return@listener + if (rotate) return@listener breakBlock(ctx.result.side) } @@ -92,14 +92,14 @@ class BreakBlock @Ta5kBuilder constructor( interactionConfig: InteractionConfig = TaskFlow.interactionSettings, sides: Set = emptySet(), collectDrop: Boolean = false, - noRotationForInstant: Boolean = true, + rotate: Boolean = false, ) = BreakBlock( ctx, rotationConfig, interactionConfig, sides, collectDrop, - noRotationForInstant + rotate ) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 3812d1040..43399d21f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -9,8 +9,10 @@ import com.lambda.interaction.construction.Blueprint.Companion.toStructure import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.result.Resolvable import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerManager.findBestAvailableTool @@ -20,12 +22,18 @@ import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task +import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.Communication.info import com.lambda.util.math.VecUtils.distSq import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock +import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemUsageContext +import net.minecraft.registry.RegistryKeys import net.minecraft.util.Hand import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos @@ -68,11 +76,11 @@ class BuildStructure( acc.add(it) return@fold acc } -// checkPlaceResults(pos, target).let { -// if (it.isEmpty()) return@let -// acc.addAll(it) -// return@fold acc -// } + checkPlaceResults(pos, target).let { + if (it.isEmpty()) return@let + acc.addAll(it) + return@fold acc + } checkBreakResults(pos, target).let { if (it.isEmpty()) return@let acc.addAll(it) @@ -104,7 +112,7 @@ class BuildStructure( lastResult = result cancelSubTasks() - if (!pathing && result is BreakResult.OutOfReach) return@let + if (!pathing && result is BuildResult.OutOfReach) return@let result.resolve.start(this@BuildStructure, false) } } @@ -154,8 +162,130 @@ class BuildStructure( private fun SafeContext.checkPlaceResults(pos: BlockPos, target: TargetState): Set { val acc = mutableSetOf() - val state = pos.blockState(world) - if (target is TargetState.Air || !state.isReplaceable) return acc + if (target is TargetState.Air || !pos.blockState(world).isReplaceable) return acc + + val interact = TaskFlow.interactionSettings + val rotation = TaskFlow.rotationSettings + + Direction.entries.forEach { neighbor -> + val neighPos = pos.offset(neighbor) + val hitSide = neighbor.opposite + + val voxelShape = neighPos.blockState(world).getOutlineShape(world, pos) + val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } + val verify: HitResult.() -> Boolean = { + blockResult?.blockPos == neighPos && blockResult?.side == hitSide + } + + val eye = player.getCameraPosVec(mc.tickDelta) + val validHits = mutableMapOf() + val reachSq = interact.reach.pow(2) + + boxes.forEach { box -> + // ToDo: Verify needed resolution + scanVisibleSurfaces(eye, box, setOf(hitSide), 2) { vec -> + if (eye distSq vec > reachSq) { + acc.add(BuildResult.OutOfReach(pos, eye.distanceTo(vec))) + return@scanVisibleSurfaces + } + val cast = eye.rotationTo(vec).rayCast( + interact.reach, + interact.rayCastMask, + eye + ) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + validHits[vec] = cast + } + } + + validHits.keys.mostCenter?.let { optimum -> + validHits.minByOrNull { optimum distSq it.key }?.let { closest -> + val optimumRotation = eye.rotationTo(closest.key) + RotationContext(optimumRotation, rotation, closest.value, verify) + } + }?.let { rotation -> + val optimalStack = target.getStack(world, pos) + + val usageContext = ItemUsageContext( + player, + Hand.MAIN_HAND, // ToDo: notice that the hand may have a different item stack and simulation will be wrong + rotation.hitResult?.blockResult, + ) + val cachePos = CachedBlockPosition( + usageContext.world, + usageContext.blockPos, + false + ) + val canBePlacedOn = optimalStack.canPlaceOn( + usageContext.world.registryManager.get(RegistryKeys.BLOCK), + cachePos, + ) + if (!player.abilities.allowModifyWorld && !canBePlacedOn) { + acc.add(PlaceResult.IllegalUsage(pos)) + return@forEach + } + + var context = ItemPlacementContext(usageContext) + + if (!optimalStack.item.isEnabled(world.enabledFeatures)) { + acc.add(PlaceResult.BlockFeatureDisabled(pos, optimalStack)) + return@forEach + } + + if (!context.canPlace()) { + acc.add(PlaceResult.CantReplace(pos, context)) + return@forEach + } + + val blockItem = optimalStack.item as? BlockItem ?: run { + acc.add(PlaceResult.NotItemBlock(pos, optimalStack)) + return@forEach + } + + val checked = blockItem.getPlacementContext(context) + if (checked == null) { + acc.add(PlaceResult.ScaffoldExceeded(pos, context)) + return@forEach + } else { + context = checked + } + + val resultState = blockItem.getPlacementState(context) ?: run { + acc.add(PlaceResult.CantReplace(pos, context)) + return@forEach + } + + if (!target.matches(resultState, pos, world)) { + acc.add(PlaceResult.NoIntegrity(pos, resultState, context)) + return@forEach + } + + val blockHit = rotation.hitResult?.blockResult ?: return@forEach + val hitBlock = blockHit.blockPos.blockState(world).block + val shouldSneak = hitBlock in BlockUtils.interactionBlacklist + + val placeContext = PlaceContext( + eye, + blockHit, + rotation, + eye.distanceTo(blockHit.pos), + resultState, + blockHit.blockPos.blockState(world), + target, + Hand.MAIN_HAND, + shouldSneak, + false + ) + + if (optimalStack.item != player.getStackInHand(placeContext.hand).item) { + acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item)) + return@forEach + } + + acc.add(PlaceResult.Success(pos, placeContext)) + } + } return acc } @@ -249,7 +379,7 @@ class BuildStructure( // ToDo: Verify needed resolution scanVisibleSurfaces(eye, box, emptySet(), 2) { vec -> if (eye distSq vec > reachSq) { - acc.add(BreakResult.OutOfReach(pos, eye.distanceTo(vec))) + acc.add(BuildResult.OutOfReach(pos, eye.distanceTo(vec))) return@scanVisibleSurfaces } val cast = eye.rotationTo(vec).rayCast( @@ -290,7 +420,7 @@ class BuildStructure( acc.add(BreakResult.Success(pos, breakContext)) return acc } ?: run { - acc.add(BreakResult.WrongTool(pos, breakContext, bestTool)) + acc.add(BuildResult.WrongItem(pos, breakContext, bestTool)) return acc } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt new file mode 100644 index 000000000..259b65c5b --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -0,0 +1,77 @@ +package com.lambda.task.tasks + +import com.lambda.Lambda.LOG +import com.lambda.context.SafeContext +import com.lambda.event.events.RotationEvent +import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.task.Task +import com.lambda.util.BlockUtils.blockState +import net.minecraft.block.BlockState + +class PlaceBlock @Ta5kBuilder constructor( + val ctx: PlaceContext, + private val swingHand: Boolean = true, + private val rotate: Boolean = true, + private val waitForConfirmation: Boolean = true, +) : Task() { + private var beginState: BlockState? = null + private val SafeContext.resultingState: BlockState get() = ctx.resultingPos.blockState(world) + + override fun SafeContext.onStart() { + if (ctx.expectedState == resultingState) { + success(Unit) + return + } + beginState = resultingState + } + + init { + listener { event -> + if (!rotate) return@listener + event.context = ctx.rotation + } + + listener { + if (!rotate) return@listener + if (!it.context.isValid) return@listener + + placeBlock() + } + } + + private fun SafeContext.placeBlock() { + val preStack = player.getStackInHand(ctx.hand) + + val actionResult = interaction.interactBlock( + player, + ctx.hand, + ctx.result + ) + + if (actionResult.isAccepted) { + if (actionResult.shouldSwingHand() && swingHand) { + player.swingHand(ctx.hand) + } + + // ToDo: WTF did i do here? i dont remember + if (!player.getStackInHand(ctx.hand).isEmpty && interaction.hasCreativeInventory()) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) + } + } else { + failure("Failed to place block as simulation does not match actual result $actionResult") + } + + LOG.info("Placed $preStack at ${ctx.result.blockPos.toShortString()} (${ctx.result.side}) with expecting state ${ctx.expectedState} and expecting position at ${ctx.resultingPos.toShortString()}") + } + + companion object { + @Ta5kBuilder + fun placeBlock( + ctx: PlaceContext, + swingHand: Boolean = true, + rotate: Boolean = true, + waitForConfirmation: Boolean = true, + ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index b08f54195..a7c935c6f 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -8,7 +8,6 @@ import com.lambda.util.math.MathUtils.toRadian import net.minecraft.client.input.Input import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.Entity -import net.minecraft.util.math.EightWayDirection import net.minecraft.util.math.Vec3d import kotlin.math.cos import kotlin.math.hypot diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 380733fe1..fb789b631 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -47,4 +47,5 @@ accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPa # Other accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; -accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; \ No newline at end of file +accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; +accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; \ No newline at end of file From 08859f6b60c7fcfc2f4dd7c1b4cc6e2581ec7a7c Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 31 May 2024 00:14:11 +0200 Subject: [PATCH 15/47] Place fixes --- .../kotlin/com/lambda/module/modules/debug/BuildTest.kt | 3 +-- .../src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt | 4 ++-- common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt | 6 +++++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt index 6cd51d9cd..f90785335 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt @@ -17,8 +17,7 @@ object BuildTest : Module( onEnable { buildStructure { player.blockPos - .offset(player.horizontalFacing) - .offset(player.horizontalFacing) + .offset(player.horizontalFacing, 2) .toStructure(TargetState.Block(Blocks.NETHERRACK)) .toBlueprint() }.start(null) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 43399d21f..6eb7d910b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -171,8 +171,8 @@ class BuildStructure( val neighPos = pos.offset(neighbor) val hitSide = neighbor.opposite - val voxelShape = neighPos.blockState(world).getOutlineShape(world, pos) - val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } + val voxelShape = neighPos.blockState(world).getOutlineShape(world, neighPos) + val boxes = voxelShape.boundingBoxes.map { it.offset(neighPos) } val verify: HitResult.() -> Boolean = { blockResult?.blockPos == neighPos && blockResult?.side == hitSide } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 259b65c5b..cf2f3ae81 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -3,6 +3,7 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.Task @@ -49,7 +50,8 @@ class PlaceBlock @Ta5kBuilder constructor( ctx.result ) - if (actionResult.isAccepted) { + val match = ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) + if (actionResult.isAccepted && match) { if (actionResult.shouldSwingHand() && swingHand) { player.swingHand(ctx.hand) } @@ -58,6 +60,8 @@ class PlaceBlock @Ta5kBuilder constructor( if (!player.getStackInHand(ctx.hand).isEmpty && interaction.hasCreativeInventory()) { mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) } + + success(Unit) } else { failure("Failed to place block as simulation does not match actual result $actionResult") } From 9bae1883b4ce0d12c73db5bacad5469b39d1a210 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 31 May 2024 01:52:00 +0200 Subject: [PATCH 16/47] Added HighwayTools --- .../command/commands/TransferCommand.kt | 2 +- .../construction/DynamicBlueprint.kt | 37 ++++- .../construction/result/BreakResult.kt | 2 +- .../construction/result/BuildResult.kt | 2 +- .../interaction/material/ContainerManager.kt | 1 - .../lambda/module/modules/debug/BuildTest.kt | 26 ---- .../module/modules/debug/HighwayTools.kt | 133 ++++++++++++++++++ .../com/lambda/module/modules/player/Nuker.kt | 3 +- .../kotlin/com/lambda/module/tag/ModuleTag.kt | 3 +- .../com/lambda/task/tasks/BuildStructure.kt | 23 ++- .../com/lambda/task/tasks/PlaceBlock.kt | 17 ++- .../com/lambda/util/player/MovementUtils.kt | 13 ++ .../com/lambda/util/world/StructureUtils.kt | 65 +++++++++ 13 files changed, 274 insertions(+), 53 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt create mode 100644 common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt create mode 100644 common/src/main/kotlin/com/lambda/util/world/StructureUtils.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index f6fc90233..0cb5a2073 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -15,7 +15,7 @@ object TransferCommand : LambdaCommand( ) { override fun CommandBuilder.create() { execute { - Items.OBSIDIAN.select().transfer(MainHandContainer).solve.start(null) + Items.OBSIDIAN.select().transfer(MainHandContainer)?.solve?.start(null) } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt index bc323abb6..c07f9e161 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -1,17 +1,30 @@ package com.lambda.interaction.construction import com.lambda.context.SafeContext +import com.lambda.threading.runSafe import com.lambda.util.primitives.extension.Structure import net.minecraft.util.math.Vec3i data class DynamicBlueprint( - val initial: Structure = emptyMap(), - val update: SafeContext.(Structure) -> Structure, + val init: SafeContext.(Structure) -> Structure = { emptyMap() }, + val onTick: SafeContext.(Structure) -> Structure = { it }, + val onDone: SafeContext.(Structure) -> Structure? = { null } ) : Blueprint() { - fun update(ctx: SafeContext) = - ctx.update(structure) + fun onTick(ctx: SafeContext) { + structure = ctx.onTick(structure) + } + + fun onDone(ctx: SafeContext): Boolean { + structure = ctx.onDone(structure) ?: return true + return false + } - override val structure: Structure by lazy { initial } + fun create(ctx: SafeContext) { + structure = ctx.init(structure) + } + + override var structure: Structure = emptyMap() + private set companion object { fun offset(offset: Vec3i): SafeContext.(Structure) -> Structure = { @@ -20,8 +33,18 @@ data class DynamicBlueprint( }.toMap() } + fun blueprintOnTick( + init: SafeContext.(Structure) -> Structure = { emptyMap() }, + onTick: SafeContext.(Structure) -> Structure + ) = DynamicBlueprint(init, onTick = onTick) + + fun blueprintOnDone( + init: SafeContext.(Structure) -> Structure = { emptyMap() }, + onDone: SafeContext.(Structure) -> Structure + ) = DynamicBlueprint(init, onDone = onDone) + fun Structure.toBlueprint( - update: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(this, update) + onTick: SafeContext.(Structure) -> Structure + ) = DynamicBlueprint({ emptyMap() }, onTick) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index fe30fbc14..8a2f48ad5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -75,7 +75,7 @@ sealed class BreakResult : BuildResult() { ?.solve ?: run { selectStack { isItem(badItem).not() - }.transfer(MainHandContainer).solve + }.transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error } override fun compareTo(other: ComparableResult): Int { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index e16efbfe0..489302420 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -132,7 +132,7 @@ abstract class BuildResult : ComparableResult { override val rank = Rank.WRONG_ITEM override val resolve: Task<*> = - neededItem.select().transfer(MainHandContainer).solve + neededItem.select().transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 67b07bf6d..984603414 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -72,7 +72,6 @@ object ContainerManager : Loadable { fun StackSelection.transfer(destination: MaterialContainer) = findContainerWithSelection(this)?.transfer(this, destination) - ?: throw NoContainerFound(this) fun findContainer( block: (MaterialContainer) -> Boolean diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt deleted file mode 100644 index f90785335..000000000 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/BuildTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.lambda.module.modules.debug - -import com.lambda.interaction.construction.Blueprint.Companion.toStructure -import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint -import com.lambda.interaction.construction.verify.TargetState -import com.lambda.module.Module -import com.lambda.module.tag.ModuleTag -import com.lambda.task.tasks.BuildStructure.Companion.buildStructure -import net.minecraft.block.Blocks - -object BuildTest : Module( - name = "BuildTest", - description = "Test module for build", - defaultTags = setOf(ModuleTag.DEBUG) -) { - init { - onEnable { - buildStructure { - player.blockPos - .offset(player.horizontalFacing, 2) - .toStructure(TargetState.Block(Blocks.NETHERRACK)) - .toBlueprint() - }.start(null) - } - } -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt new file mode 100644 index 000000000..4a89dcbab --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt @@ -0,0 +1,133 @@ +package com.lambda.module.modules.debug + +import com.lambda.interaction.construction.DynamicBlueprint +import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnDone +import com.lambda.interaction.construction.DynamicBlueprint.Companion.offset +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.util.KeyCode +import com.lambda.util.player.MovementUtils.direction +import com.lambda.util.primitives.extension.Structure +import com.lambda.util.world.StructureUtils.generateDirectionalTube +import net.minecraft.block.Blocks +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.EightWayDirection +import net.minecraft.util.math.Vec3i +import kotlin.math.roundToInt + +object HighwayTools : Module( + name = "HighwayTools", + description = "Auto highway builder", + defaultTags = setOf(ModuleTag.BUILDING), + defaultKeybind = KeyCode.X +) { + private val height by setting("Height", 4, 1..10, 1) + private val width by setting("Width", 6, 1..100, 1) + private val rimHeight by setting("Rim Height", 1, 1..6, 1) + private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") + private val material = Blocks.OBSIDIAN + private val distance by setting("Distance", -1, -1..Int.MAX_VALUE, 100, description = "Distance to build the highway (negative for infinite)") + // ToDo: Fix block setting +// private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") + + private var direction = EightWayDirection.NORTH + private var startPos = BlockPos.ORIGIN + + init { + onEnable { + direction = player.direction() + startPos = player.blockPos + highwayTask.start(null) + } + onDisable { highwayTask.cancel() } + } + + private val highwayTask = buildStructure { + blueprintOnDone({ generateHighway() }) { last -> + val vec = Vec3i(direction.offsetX, 0, direction.offsetZ) + offset(vec).invoke(this, last) + } + } + + private fun generateHighway(): Structure { + val structure = mutableMapOf() + val orthogonal = EightWayDirection.entries[(direction.ordinal + 2).mod(8)] + val center = (width / 2.0).roundToInt() + + // Area to clear + structure += generateDirectionalTube( + orthogonal, + width, + height, + -center, + -1, + ).associateWith { TargetState.Air } + + // Highway + structure += generateDirectionalTube( + orthogonal, + width, + 1, + -center, + -1, + ).associateWith { TargetState.Block(material) } + + // Left rim + structure += generateDirectionalTube( + orthogonal, + 1, + rimHeight, + -center + width - 1, + 0, + ).associateWith { TargetState.Block(material) } + + // Right rim + structure += generateDirectionalTube( + orthogonal, + 1, + rimHeight, + -center, + 0, + ).associateWith { TargetState.Block(material) } + + if (!cornerBlock) { + structure -= generateDirectionalTube( + orthogonal, + 1, + 1, + -center + width - 1, + -1, + ) + + structure -= generateDirectionalTube( + orthogonal, + 1, + 1, + -center, + -1, + ) + + // Remove the left corner +// structure += generateDirectionalTube( +// orthogonal, +// 1, +// 1, +// -center + width - 1, +// -1, +// ).associateWith { TargetState.Support(Direction.UP) } + + // Remove the right corner +// structure += generateDirectionalTube( +// orthogonal, +// 1, +// 1, +// -center, +// -1, +// ).associateWith { TargetState.Support(Direction.UP) } + } + + return structure.map { it.key.add(startPos) to it.value }.toMap() + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 705a987ee..28fd97e6d 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -1,6 +1,7 @@ package com.lambda.module.modules.player import com.lambda.interaction.construction.DynamicBlueprint +import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnTick import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -33,7 +34,7 @@ object Nuker : Module( pathing = false, finishOnDone = false ) { - DynamicBlueprint { _ -> + blueprintOnTick { _ -> val selection = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) .asSequence() .map { it.blockPos } diff --git a/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt b/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt index c5b84bbe5..3fdfa3444 100644 --- a/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt +++ b/common/src/main/kotlin/com/lambda/module/tag/ModuleTag.kt @@ -24,6 +24,7 @@ data class ModuleTag(override val name: String) : Nameable { val WORLD = ModuleTag("World") val MISC = ModuleTag("Misc") val CLIENT = ModuleTag("Client") + val BUILDING = ModuleTag("Building") // Do something with this ? val HIDDEN = ModuleTag("Hidden") @@ -33,6 +34,6 @@ data class ModuleTag(override val name: String) : Nameable { val AUTOMATION = ModuleTag("Automation") val DEBUG = ModuleTag("Debug") - val defaults = listOf(COMBAT, MOVEMENT, RENDER, PLAYER, WORLD, DEBUG, CLIENT) + val defaults = listOf(COMBAT, MOVEMENT, RENDER, PLAYER, WORLD, DEBUG, CLIENT, BUILDING) } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 6eb7d910b..010608f6b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -42,7 +42,7 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import kotlin.math.pow -class BuildStructure( +class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, private val collectDrops: Boolean = false, private val skipWeakBlocks: Boolean = false, @@ -53,25 +53,34 @@ class BuildStructure( ) : Task() { private var lastResult: BuildResult? = null + override fun SafeContext.onStart() { + (blueprint as? DynamicBlueprint)?.create(this) + } + init { listener { - val structure = when (blueprint) { - is DynamicBlueprint -> blueprint.update(this) - else -> blueprint.structure - } + (blueprint as? DynamicBlueprint)?.onTick(this) - if (finishOnDone && structure.isEmpty()) { + if (finishOnDone && blueprint.structure.isEmpty()) { failure("Structure is empty") return@listener } if (finishOnDone && blueprint.isDone(this)) { + if (blueprint is DynamicBlueprint) { + if (!blueprint.onDone(this)) { + this@BuildStructure.info("Structure moved") + return@listener + } + } + this@BuildStructure.info("Structure is done") + cancelSubTasks() success(Unit) return@listener } - val results = structure.entries.fold(mutableSetOf()) { acc, (pos, target) -> + val results = blueprint.structure.entries.fold(mutableSetOf()) { acc, (pos, target) -> checkRequirements(pos, target)?.let { acc.add(it) return@fold acc diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index cf2f3ae81..017199375 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -3,7 +3,6 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.RotationEvent -import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.Task @@ -14,13 +13,14 @@ class PlaceBlock @Ta5kBuilder constructor( val ctx: PlaceContext, private val swingHand: Boolean = true, private val rotate: Boolean = true, - private val waitForConfirmation: Boolean = true, + private val waitForConfirmation: Boolean = false, ) : Task() { private var beginState: BlockState? = null private val SafeContext.resultingState: BlockState get() = ctx.resultingPos.blockState(world) + private val SafeContext.matches get() = ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) override fun SafeContext.onStart() { - if (ctx.expectedState == resultingState) { + if (matches) { success(Unit) return } @@ -39,6 +39,10 @@ class PlaceBlock @Ta5kBuilder constructor( placeBlock() } + +// listener { +// if (matches) success(Unit) +// } } private fun SafeContext.placeBlock() { @@ -50,8 +54,7 @@ class PlaceBlock @Ta5kBuilder constructor( ctx.result ) - val match = ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) - if (actionResult.isAccepted && match) { + if (actionResult.isAccepted && matches) { if (actionResult.shouldSwingHand() && swingHand) { player.swingHand(ctx.hand) } @@ -61,7 +64,7 @@ class PlaceBlock @Ta5kBuilder constructor( mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) } - success(Unit) + if (!waitForConfirmation) success(Unit) } else { failure("Failed to place block as simulation does not match actual result $actionResult") } @@ -75,7 +78,7 @@ class PlaceBlock @Ta5kBuilder constructor( ctx: PlaceContext, swingHand: Boolean = true, rotate: Boolean = true, - waitForConfirmation: Boolean = true, + waitForConfirmation: Boolean = false, ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index a7c935c6f..b1ba0d9b5 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -8,6 +8,7 @@ import com.lambda.util.math.MathUtils.toRadian import net.minecraft.client.input.Input import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.entity.Entity +import net.minecraft.util.math.EightWayDirection import net.minecraft.util.math.Vec3d import kotlin.math.cos import kotlin.math.hypot @@ -82,4 +83,16 @@ object MovementUtils { val Entity.moveDiff get() = Vec3d(this.pos.x - this.prevX, this.pos.y - this.prevY, this.pos.z - this.prevZ) val Entity.moveDelta get() = moveDiff.let { hypot(it.x, it.z) } val Entity.motionDelta get() = hypot(this.velocity.x, this.velocity.z) + + fun Entity.direction(): EightWayDirection { + // Normalize the yaw to be within the range of -180 to 179 degrees + var normalizedYaw = (yaw + 180.0) % 360.0 + if (normalizedYaw < 0) { + normalizedYaw += 360.0 + } + + // Calculate the index of the closest direction + val directionIndex = ((normalizedYaw + 22.5) / 45.0).toInt() % 8 + return EightWayDirection.entries[directionIndex] + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/world/StructureUtils.kt b/common/src/main/kotlin/com/lambda/util/world/StructureUtils.kt new file mode 100644 index 000000000..02c1a43a8 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/world/StructureUtils.kt @@ -0,0 +1,65 @@ +package com.lambda.util.world + +import net.minecraft.block.Blocks +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.EightWayDirection + +object StructureUtils { + val PORTAL = setOf( + BlockPos(0, 0, 0), + BlockPos(0, 1, 0), + BlockPos(0, 2, 0), + BlockPos(0, 3, 0), + BlockPos(1, 3, 0), + BlockPos(2, 3, 0), + BlockPos(3, 3, 0), + BlockPos(1, -1, 0), + BlockPos(2, -1, 0), + BlockPos(3, 0, 0), + BlockPos(3, 1, 0), + BlockPos(3, 2, 0), + BlockPos(3, 3, 0), + ) + val LIGHT_UP = mapOf( + BlockPos(1, 0, 0) to Blocks.FIRE.defaultState, + ) + + /** + * Generates a tube of blocks in the specified direction. + * + * @param direction The direction in which the tube should be generated. + * @param width The width of the tube. + * @param height The height of the tube. + * @param leftRightOffset The offset along the X-axis. + * @param heightOffset The offset along the Y-axis. + * @return A set of BlockPos representing the generated tube. + */ + fun generateDirectionalTube( + direction: EightWayDirection, + width: Int, + height: Int, + leftRightOffset: Int, + heightOffset: Int, + ): Set { + val tube = mutableSetOf() + + val offsetX = leftRightOffset * direction.offsetX + val offsetZ = leftRightOffset * direction.offsetZ + + (heightOffset until heightOffset + height).forEach { y -> + (0 until width).forEach { x -> + (0 until width).forEach { z -> + tube.add( + BlockPos( + offsetX + x * direction.offsetX, + y, + offsetZ + z * direction.offsetZ, + ), + ) + } + } + } + + return tube + } +} From 8c8548cdd34cd8bd8275b221adc93e84570b8e13 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 31 May 2024 23:22:17 +0200 Subject: [PATCH 17/47] Merge fixes --- .../lambda/module/modules/{debug => player}/HighwayTools.kt | 5 ++--- common/src/main/resources/lambda.mixins.common.json | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) rename common/src/main/kotlin/com/lambda/module/modules/{debug => player}/HighwayTools.kt (96%) diff --git a/common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt similarity index 96% rename from common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt rename to common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 4a89dcbab..ea3920ed9 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/debug/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -1,6 +1,5 @@ -package com.lambda.module.modules.debug +package com.lambda.module.modules.player -import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnDone import com.lambda.interaction.construction.DynamicBlueprint.Companion.offset import com.lambda.interaction.construction.verify.TargetState @@ -20,7 +19,7 @@ import kotlin.math.roundToInt object HighwayTools : Module( name = "HighwayTools", description = "Auto highway builder", - defaultTags = setOf(ModuleTag.BUILDING), + defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION), defaultKeybind = KeyCode.X ) { private val height by setting("Height", 4, 1..10, 1) diff --git a/common/src/main/resources/lambda.mixins.common.json b/common/src/main/resources/lambda.mixins.common.json index 8d5af9681..3a95f7468 100644 --- a/common/src/main/resources/lambda.mixins.common.json +++ b/common/src/main/resources/lambda.mixins.common.json @@ -33,9 +33,8 @@ "render.RenderTickCounterMixin", "render.ScreenHandlerMixin", "render.VertexBufferMixin", - "render.WorldRendererMixin" - "world.ClientWorldMixin", - "world.WorldMixin" + "render.WorldRendererMixin", + "world.ClientWorldMixin" ], "injectors": { "defaultRequire": 1 From 054f4542f035e0e53915c133ea9d47c8fba92cb0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 1 Jun 2024 00:59:02 +0200 Subject: [PATCH 18/47] Fix merge conflict --- common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt | 2 +- .../kotlin/com/lambda/module/modules/player/HighwayTools.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index e22ba4b9b..60d65a7eb 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -197,7 +197,7 @@ class VAO( } protected fun finalize() { - runOnGameThread { + runGameScheduled { glDeleteBuffers(ibo) glDeleteBuffers(vbo) glDeleteVertexArrays(vao) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index ea3920ed9..a51bde9a7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -27,7 +27,7 @@ object HighwayTools : Module( private val rimHeight by setting("Rim Height", 1, 1..6, 1) private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") private val material = Blocks.OBSIDIAN - private val distance by setting("Distance", -1, -1..Int.MAX_VALUE, 100, description = "Distance to build the highway (negative for infinite)") + private val distance by setting("Distance", -1, -1..1000000, 100, description = "Distance to build the highway (negative for infinite)") // ToDo: Fix block setting // private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") From 0e93ec028f719d8e3797d9640f85f4b14599d409 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 31 May 2024 19:17:09 -0400 Subject: [PATCH 19/47] do NOT use mojang gl --- common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 60d65a7eb..14eab5083 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -20,7 +20,7 @@ import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer import com.lambda.threading.runGameScheduled -import com.mojang.blaze3d.systems.RenderSystem.drawElements +import org.lwjgl.opengl.GL11C import org.lwjgl.opengl.GL30C.* import java.awt.Color import java.nio.ByteBuffer @@ -171,7 +171,7 @@ class VAO( if (indicesCount <= 0) return bindVertexArray(vao) - drawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT) + glDrawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT, 0) unbindVertexArray() } From 9ec4c1d1bb67d44d20bd6f98de9d4e7478f7a237 Mon Sep 17 00:00:00 2001 From: Kamigen <46357922+Edouard127@users.noreply.github.com> Date: Fri, 31 May 2024 19:17:31 -0400 Subject: [PATCH 20/47] Revert "do NOT use mojang gl" This reverts commit 0e93ec028f719d8e3797d9640f85f4b14599d409. --- common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt index 14eab5083..60d65a7eb 100644 --- a/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt +++ b/common/src/main/kotlin/com/lambda/graphics/buffer/vao/VAO.kt @@ -20,7 +20,7 @@ import com.lambda.graphics.gl.VaoUtils.unbindIndexBuffer import com.lambda.graphics.gl.VaoUtils.unbindVertexArray import com.lambda.graphics.gl.VaoUtils.unbindVertexBuffer import com.lambda.threading.runGameScheduled -import org.lwjgl.opengl.GL11C +import com.mojang.blaze3d.systems.RenderSystem.drawElements import org.lwjgl.opengl.GL30C.* import java.awt.Color import java.nio.ByteBuffer @@ -171,7 +171,7 @@ class VAO( if (indicesCount <= 0) return bindVertexArray(vao) - glDrawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT, 0) + drawElements(drawMode.gl, indicesCount, GL_UNSIGNED_INT) unbindVertexArray() } From 518283f634f517370eb4ff9af54cec3da581ca8c Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 1 Jun 2024 04:17:32 +0200 Subject: [PATCH 21/47] Granular global build settings and tons of fixes --- .../com/lambda/config/InteractionSettings.kt | 14 -- .../com/lambda/config/groups/BuildConfig.kt | 12 ++ .../com/lambda/config/groups/BuildSettings.kt | 17 +++ .../groups}/IRotationConfig.kt | 4 +- .../groups}/InteractionConfig.kt | 8 +- .../config/groups/InteractionSettings.kt | 13 ++ .../config/{ => groups}/RotationSettings.kt | 8 +- .../com/lambda/event/events/RotationEvent.kt | 11 -- .../com/lambda/interaction/RotationManager.kt | 2 +- .../construction/DynamicBlueprint.kt | 9 +- .../construction/result/BreakResult.kt | 2 - .../construction/result/BuildResult.kt | 20 ++- .../construction/result/PlaceResult.kt | 1 - .../interaction/material/MaterialContainer.kt | 29 ++-- .../lambda/interaction/rotation/Rotation.kt | 2 +- .../interaction/rotation/RotationContext.kt | 1 + .../visibilty/VisibilityChecker.kt | 22 +-- .../lambda/module/modules/client/Baritone.kt | 2 +- .../lambda/module/modules/client/TaskFlow.kt | 25 +++- .../module/modules/combat/CrystalAura.kt | 4 +- .../lambda/module/modules/player/Freecam.kt | 2 +- .../module/modules/player/HighwayTools.kt | 27 +++- .../lambda/module/modules/player/Replay.kt | 2 +- .../main/kotlin/com/lambda/task/RootTask.kt | 2 +- .../src/main/kotlin/com/lambda/task/Task.kt | 37 +++--- .../com/lambda/task/tasks/AcquireMaterial.kt | 13 +- .../com/lambda/task/tasks/BreakBlock.kt | 15 +-- .../com/lambda/task/tasks/BuildStructure.kt | 125 +++++++++++------- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 12 +- .../com/lambda/task/tasks/OpenContainer.kt | 4 +- .../com/lambda/task/tasks/PlaceBlock.kt | 18 ++- .../lambda/util/world/raycast/RayCastUtils.kt | 5 +- 32 files changed, 283 insertions(+), 185 deletions(-) delete mode 100644 common/src/main/kotlin/com/lambda/config/InteractionSettings.kt create mode 100644 common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt create mode 100644 common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt rename common/src/main/kotlin/com/lambda/{interaction/rotation => config/groups}/IRotationConfig.kt (88%) rename common/src/main/kotlin/com/lambda/{interaction => config/groups}/InteractionConfig.kt (63%) create mode 100644 common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt rename common/src/main/kotlin/com/lambda/config/{ => groups}/RotationSettings.kt (86%) diff --git a/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt deleted file mode 100644 index 2c63b764f..000000000 --- a/common/src/main/kotlin/com/lambda/config/InteractionSettings.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.lambda.config - -import com.lambda.interaction.InteractionConfig -import com.lambda.util.world.raycast.RayCastMask - -class InteractionSettings( - c: Configurable, - vis: () -> Boolean = { true }, -) : InteractionConfig { - override val reach by c.setting("Reach", 5.0, 0.1..10.0, 0.1, "Players reach / range", "", vis) - override val resolution by c.setting("Resolution", 10, 1..100, 1, "Raycast resolution", "", vis) - override val rayCastMask by c.setting("Raycast Mask", RayCastMask.BOTH, "What to raycast against", vis) - override val ignoreRayCast by c.setting("Ignore Raycast", true, "Ignore raycast when looking at blocks", vis) -} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt new file mode 100644 index 000000000..4eb840970 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -0,0 +1,12 @@ +package com.lambda.config.groups + +interface BuildConfig { + val collectDrops: Boolean + val breakWeakBlocks: Boolean + val pathing: Boolean + val interactLimit: Int + val breakInstantAtOnce: Boolean + val rotateForBreak: Boolean + val swingHand: Boolean + val particlesOnBreak: Boolean +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt new file mode 100644 index 000000000..447f02d72 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -0,0 +1,17 @@ +package com.lambda.config.groups + +import com.lambda.config.Configurable + +class BuildSettings( + c: Configurable, + vis: () -> Boolean = { true } +) : BuildConfig { + override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis) + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", vis) + override val pathing by c.setting("Pathing", true, "Path to blocks", vis) + override val interactLimit by c.setting("Interaction Limit", 15, 1..100, 1, "Max interactions per tick", " i/t", vis) + override val breakInstantAtOnce by c.setting("Break Instant At Once", true, "Break all instant blocks at once", vis) + override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", vis) + override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) + override val particlesOnBreak by c.setting("Particles On Break", true, "Show particles when breaking blocks", vis) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt similarity index 88% rename from common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt rename to common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt index 2f8f1f317..1a42f5da3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/IRotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt @@ -1,4 +1,6 @@ -package com.lambda.interaction.rotation +package com.lambda.config.groups + +import com.lambda.interaction.rotation.RotationMode interface IRotationConfig { /** diff --git a/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt similarity index 63% rename from common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt rename to common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index 67a1fcaea..3514dfe28 100644 --- a/common/src/main/kotlin/com/lambda/interaction/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -1,6 +1,4 @@ -package com.lambda.interaction - -import com.lambda.util.world.raycast.RayCastMask +package com.lambda.config.groups interface InteractionConfig { /** @@ -13,7 +11,5 @@ interface InteractionConfig { */ val resolution: Int - val rayCastMask: RayCastMask - - val ignoreRayCast: Boolean + val useRayCast: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt new file mode 100644 index 000000000..b52f9d9e5 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -0,0 +1,13 @@ +package com.lambda.config.groups + +import com.lambda.config.Configurable +import com.lambda.util.world.raycast.RayCastMask + +class InteractionSettings( + c: Configurable, + vis: () -> Boolean = { true }, +) : InteractionConfig { + override val reach by c.setting("Reach", 4.9, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) + override val useRayCast by c.setting("Raycast", false, "Verify hit vector with ray casting (for very strict ACs)", vis) + override val resolution by c.setting("Resolution", 5, 1..20, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt similarity index 86% rename from common/src/main/kotlin/com/lambda/config/RotationSettings.kt rename to common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index b5f34c005..f591efbba 100644 --- a/common/src/main/kotlin/com/lambda/config/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -1,6 +1,6 @@ -package com.lambda.config +package com.lambda.config.groups -import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.config.Configurable import com.lambda.interaction.rotation.RotationMode import kotlin.math.max import kotlin.math.min @@ -11,8 +11,8 @@ class RotationSettings( vis: () -> Boolean = { true }, ) : IRotationConfig { override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera", vis) - override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", "", vis) - override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", "", vis) + override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", " ticks", vis) + override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) var r1 by c.setting("Turn Speed 1", 70.0, 1.0..180.0, 0.1, "Rotation Speed 1", "", vis) var r2 by c.setting("Turn Speed 2", 110.0, 1.0..180.0, 0.1, "Rotation Speed 2", "", vis) diff --git a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt index 9d9e4319e..9d8d4363f 100644 --- a/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/RotationEvent.kt @@ -3,19 +3,8 @@ package com.lambda.event.events import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable -import com.lambda.interaction.InteractionConfig import com.lambda.interaction.RotationManager -import com.lambda.interaction.rotation.IRotationConfig import com.lambda.interaction.rotation.RotationContext -import com.lambda.interaction.visibilty.VisibilityChecker.findRotation -import com.lambda.module.modules.client.TaskFlow -import com.lambda.threading.runSafe -import com.lambda.util.BlockUtils.blockState -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import com.lambda.util.world.raycast.RayCastUtils.entityResult -import net.minecraft.entity.Entity -import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Direction abstract class RotationEvent : Event { /** diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index 30b2f9bc5..2c387c80e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -2,8 +2,8 @@ package com.lambda.interaction import baritone.utils.PlayerMovementInput import com.lambda.Lambda.mc +import com.lambda.config.groups.RotationSettings import com.lambda.core.Loadable -import com.lambda.config.RotationSettings import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.* diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt index c07f9e161..5a5832a02 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -3,12 +3,13 @@ package com.lambda.interaction.construction import com.lambda.context.SafeContext import com.lambda.threading.runSafe import com.lambda.util.primitives.extension.Structure +import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i data class DynamicBlueprint( val init: SafeContext.(Structure) -> Structure = { emptyMap() }, val onTick: SafeContext.(Structure) -> Structure = { it }, - val onDone: SafeContext.(Structure) -> Structure? = { null } + val onDone: SafeContext.(Structure) -> Structure? = { null }, ) : Blueprint() { fun onTick(ctx: SafeContext) { structure = ctx.onTick(structure) @@ -36,15 +37,15 @@ data class DynamicBlueprint( fun blueprintOnTick( init: SafeContext.(Structure) -> Structure = { emptyMap() }, onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init, onTick = onTick) + ) = DynamicBlueprint(init = init, onTick = onTick) fun blueprintOnDone( init: SafeContext.(Structure) -> Structure = { emptyMap() }, onDone: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init, onDone = onDone) + ) = DynamicBlueprint(init = init, onDone = onDone) fun Structure.toBlueprint( onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint({ emptyMap() }, onTick) + ) = DynamicBlueprint(init = { emptyMap() }, onTick = onTick) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 8a2f48ad5..61b67da63 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -6,10 +6,8 @@ import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MainHandContainer -import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BreakBlock.Companion.breakBlock -import com.lambda.task.tasks.GoalTask.Companion.moveToBlock import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 489302420..360cadc5e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,18 +1,18 @@ package com.lambda.interaction.construction.result -import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.MainHandContainer import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask -import com.lambda.task.tasks.GoalTask.Companion.moveToBlock +import com.lambda.task.tasks.GoalTask.Companion.moveNearBlock import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d abstract class BuildResult : ComparableResult { abstract val blockPos: BlockPos @@ -145,15 +145,25 @@ abstract class BuildResult : ComparableResult { /** * Represents a break out of reach. * @param blockPos The position of the block that is out of reach. - * @param distance The distance to the hit vector. + * @param startVec The start vector of the reach. + * @param hitVec The hit vector of the reach. + * @param reach The maximum reach distance. + * @param side The side that is out of reach. */ data class OutOfReach( override val blockPos: BlockPos, - val distance: Double + val startVec: Vec3d, + val hitVec: Vec3d, + val reach: Double, + val side: Direction, ) : Resolvable, BuildResult() { override val rank = Rank.OUT_OF_REACH - override val resolve = moveToBlock(blockPos) + val distance: Double by lazy { + startVec.distanceTo(hitVec) + } + + override val resolve = moveNearBlock(blockPos, 2) override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 8bd40fedf..f916560ff 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -3,7 +3,6 @@ package com.lambda.interaction.construction.result import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.breakBlock -import com.lambda.task.tasks.GoalTask.Companion.moveToBlock import com.lambda.task.tasks.PlaceBlock.Companion.placeBlock import net.minecraft.block.BlockState import net.minecraft.item.ItemPlacementContext diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 3e2977c62..7ef173974 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -25,21 +25,34 @@ abstract class MaterialContainer( /** * Brings the player into a withdrawal/deposit state. E.g.: move to a chest etc. */ - @Task.Ta5kBuilder - open fun prepare(): Task<*> = emptyTask("EmptyPrepare") + open fun prepare(): Task<*>? = null /** * Withdraws items from the container to the player's inventory. */ - @Task.Ta5kBuilder abstract fun withdraw(selection: StackSelection): Task<*> + @Task.Ta5kBuilder + fun doWithdrawal(selection: StackSelection) = + prepare()?.let { prep -> + prep.onSuccess { _, _ -> + withdraw(selection).start(prep) + } + } ?: withdraw(selection) + /** * Deposits items from the player's inventory into the container. */ - @Task.Ta5kBuilder abstract fun deposit(selection: StackSelection): Task<*> + @Task.Ta5kBuilder + fun doDeposit(selection: StackSelection) = + prepare()?.let { prep -> + prep.onSuccess { _, _ -> + deposit(selection).start(prep) + } + } ?: deposit(selection) + open fun filter(selection: StackSelection) = selection.filterStacks(stacks) @@ -68,12 +81,8 @@ abstract class MaterialContainer( // selection.count = transferAmount return TransferResult.Success( - prepare().onSuccess { prep, _ -> - withdraw(selection).onSuccess { with, _ -> - destination.prepare().onSuccess { dest, _ -> - destination.deposit(selection).start(dest) - }.start(with) - }.start(prep) + doWithdrawal(selection).onSuccess { withdraw, _ -> + destination.doDeposit(selection).start(withdraw) } ) } diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt index 6983e97e1..cf3d35580 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/Rotation.kt @@ -34,9 +34,9 @@ data class Rotation(val yaw: Double, val pitch: Double) { fun rayCast( reach: Double, - mask: RayCastMask = RayCastMask.BOTH, eye: Vec3d? = null, fluids: Boolean = false, + mask: RayCastMask = RayCastMask.BOTH, ) = runSafe { rayCast(eye ?: player.eyePos, vector, reach, mask, fluids) } diff --git a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt index 94a66c0a6..0d55a028a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt +++ b/common/src/main/kotlin/com/lambda/interaction/rotation/RotationContext.kt @@ -1,5 +1,6 @@ package com.lambda.interaction.rotation +import com.lambda.config.groups.IRotationConfig import net.minecraft.util.hit.HitResult data class RotationContext( diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index ec6ad01ea..9a8c58cc2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -1,9 +1,9 @@ package com.lambda.interaction.visibilty import com.lambda.context.SafeContext -import com.lambda.interaction.InteractionConfig +import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.RotationManager -import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.config.groups.IRotationConfig import com.lambda.interaction.rotation.Rotation.Companion.rotationTo import com.lambda.interaction.rotation.RotationContext import com.lambda.module.modules.client.TaskFlow @@ -54,11 +54,7 @@ object VisibilityChecker { val eye = player.getCameraPosVec(mc.tickDelta) val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast( - interact.reach, - interact.rayCastMask, - eye - ) + val currentCast = currentRotation.rayCast(interact.reach, eye) if (boxes.any { it.contains(eye) }) { return RotationContext(currentRotation, rotationConfig, currentCast, verify) @@ -68,16 +64,12 @@ object VisibilityChecker { val reachSq = interact.reach.pow(2) boxes.forEach { box -> - scanVisibleSurfaces(player.eyePos, box, sides, interact.resolution) { vec -> + scanVisibleSurfaces(player.eyePos, box, sides, interact.resolution) { _, vec -> if (eye distSq vec > reachSq) return@scanVisibleSurfaces val newRotation = eye.rotationTo(vec) - val cast = newRotation.rayCast( - interact.reach, - interact.rayCastMask, - eye - ) ?: return@scanVisibleSurfaces + val cast = newRotation.rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces if (!cast.verify()) return@scanVisibleSurfaces validHits[vec] = cast @@ -99,7 +91,7 @@ object VisibilityChecker { box: Box, sides: Set, resolution: Int, - check: (Vec3d) -> Unit, + check: (Direction, Vec3d) -> Unit, ) { val shrunk = box.expand(-0.005) box.getVisibleSurfaces(eyes) @@ -114,7 +106,7 @@ object VisibilityChecker { (0..resolution).forEach { j -> val y = if (stepY != 0.0) minY + stepY * j else minY val z = if (stepZ != 0.0) minZ + stepZ * ((if (stepX != 0.0) j else i)) else minZ - check(Vec3d(x, y, z)) + check(side, Vec3d(x, y, z)) } } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt index 892478d99..177553c41 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt @@ -1,6 +1,6 @@ package com.lambda.module.modules.client -import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.config.groups.IRotationConfig import com.lambda.interaction.rotation.RotationMode import com.lambda.module.Module import com.lambda.module.tag.ModuleTag diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index 2613b8766..b27bdf3c9 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -1,17 +1,32 @@ package com.lambda.module.modules.client -import com.lambda.config.InteractionSettings -import com.lambda.config.RotationSettings +import com.lambda.config.groups.InteractionSettings +import com.lambda.config.groups.BuildSettings +import com.lambda.config.groups.RotationSettings import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag import com.lambda.util.BlockUtils.allSigns import net.minecraft.block.Block object TaskFlow : Module( name = "TaskFlow", - description = "Settings for task automation" + description = "Settings for task automation", + defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.AUTOMATION) ) { - val rotationSettings = RotationSettings(this) - val interactionSettings = InteractionSettings(this) + enum class Page { + BUILD, ROTATION, INTERACTION + } + + private val page by setting("Page", Page.BUILD) + val buildSettings = BuildSettings(this) { + page == Page.BUILD + } + val rotationSettings = RotationSettings(this) { + page == Page.ROTATION + } + val interactionSettings = InteractionSettings(this) { + page == Page.INTERACTION + } // val disposables by setting("Disposables", ItemUtils.defaultDisposables) val ignoredBlocks = mutableSetOf().apply { addAll(allSigns) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index 6bcb86428..b7401ca1b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -1,7 +1,7 @@ package com.lambda.module.modules.combat -import com.lambda.config.InteractionSettings -import com.lambda.config.RotationSettings +import com.lambda.config.groups.InteractionSettings +import com.lambda.config.groups.RotationSettings import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.concurrentListener import com.lambda.event.listener.SafeListener.Companion.listener diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 0a12cb695..efe49f0dd 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -2,7 +2,7 @@ package com.lambda.module.modules.player import baritone.utils.PlayerMovementInput import com.lambda.Lambda.mc -import com.lambda.config.RotationSettings +import com.lambda.config.groups.RotationSettings import com.lambda.event.events.ConnectionEvent import com.lambda.event.events.MovementEvent import com.lambda.event.events.RenderEvent diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index a51bde9a7..36d9d9b07 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -1,11 +1,15 @@ package com.lambda.module.modules.player +import baritone.api.pathing.goals.GoalNear +import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnDone import com.lambda.interaction.construction.DynamicBlueprint.Companion.offset import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.util.BaritoneUtils.primary import com.lambda.util.KeyCode import com.lambda.util.player.MovementUtils.direction import com.lambda.util.primitives.extension.Structure @@ -32,23 +36,32 @@ object HighwayTools : Module( // private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") private var direction = EightWayDirection.NORTH + private var distanceMoved = 0 private var startPos = BlockPos.ORIGIN + private var currentPos = BlockPos.ORIGIN + private var runningTask: Task<*>? = null init { onEnable { direction = player.direction() startPos = player.blockPos - highwayTask.start(null) + val task = getTask() + task.start(null) + runningTask = task } - onDisable { highwayTask.cancel() } + onDisable { runningTask?.cancel() } } - private val highwayTask = buildStructure { - blueprintOnDone({ generateHighway() }) { last -> - val vec = Vec3i(direction.offsetX, 0, direction.offsetZ) - offset(vec).invoke(this, last) + private fun getTask() = + buildStructure { + blueprintOnDone({ generateHighway() }) { last -> + distanceMoved++ + + val vec = Vec3i(direction.offsetX, 0, direction.offsetZ) + currentPos = currentPos.add(vec) + offset(vec).invoke(this, last) + } } - } private fun generateHighway(): Structure { val structure = mutableMapOf() diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index 7c8187529..901ec6307 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -2,7 +2,7 @@ package com.lambda.module.modules.player import com.google.gson.* import com.lambda.brigadier.CommandResult -import com.lambda.config.RotationSettings +import com.lambda.config.groups.RotationSettings import com.lambda.context.SafeContext import com.lambda.sound.SoundManager.playSound import com.lambda.core.TimerManager diff --git a/common/src/main/kotlin/com/lambda/task/RootTask.kt b/common/src/main/kotlin/com/lambda/task/RootTask.kt index b2825f9ce..c4213b712 100644 --- a/common/src/main/kotlin/com/lambda/task/RootTask.kt +++ b/common/src/main/kotlin/com/lambda/task/RootTask.kt @@ -2,7 +2,7 @@ package com.lambda.task object RootTask : Task() { init { - name = "Root Task" + name = "RootTask" } fun addInfo(debugText: MutableList) { diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 404c15412..82f3c6a80 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -74,6 +74,7 @@ abstract class Task( val isCompleted get() = state == State.COMPLETED val isRoot get() = parent == null override var name = this::class.simpleName ?: "Task" + val identifier get() = "$name@${hashCode()}" // ToDo: Better color management private val primaryColor = Color(0, 255, 0, 100) @@ -150,10 +151,10 @@ abstract class Task( owner.subTasks.add(this) if (pauseParent && owner.isActivated && !owner.isRoot) { - LOG.info("$name deactivating parent ${owner.name}") + LOG.info("$identifier deactivating parent ${owner.identifier}") owner.deactivate() } - LOG.info("$name was started by ${owner.name}") + LOG.info("${owner.identifier} started $identifier") this.parent = owner activate() @@ -164,8 +165,6 @@ abstract class Task( @Ta5kBuilder fun activate() { if (isActivated) return - - LOG.info("$name activated") state = State.ACTIVATED startListening() } @@ -174,7 +173,7 @@ abstract class Task( fun deactivate() { if (isDeactivated) return - LOG.info("$name deactivated") + LOG.info("$identifier deactivated") state = State.DEACTIVATED stopListening() } @@ -183,16 +182,17 @@ abstract class Task( fun SafeContext.success(result: Result) { if (executions < repeats) { executions++ - LOG.info("Repeating $name $executions/$repeats...") + LOG.info("Repeating $identifier $executions/$repeats...") onRepeat(this@Task, result, executions) reset() return } - LOG.info("$name completed successfully after $attempted retries and $executions executions.") + LOG.info("$identifier completed successfully after $attempted retries and $executions executions.") state = State.COMPLETED - tidyUp() + stopListening() onSuccess(this@Task, result) + notifyParent() } @Ta5kBuilder @@ -200,8 +200,8 @@ abstract class Task( cancelSubTasks() state = State.CANCELLED stopListening() - BaritoneUtils.cancel() runSafe { onCancel() } + LOG.info("$identifier was cancelled") } @Ta5kBuilder @@ -231,7 +231,7 @@ abstract class Task( state = State.FAILED logError("Task failed after $attempted attempts with error: ${e.message}") - tidyUp() + stopListening() runSafe { onException(this@Task, e) } parent?.failure(e) } @@ -245,10 +245,17 @@ abstract class Task( @Ta5kBuilder open fun SafeContext.onCancel() {} - private fun tidyUp() { - stopListening() - BaritoneUtils.cancel() - parent?.activate() + private fun notifyParent() { + parent?.let { par -> + if (par.isCompleted) { + LOG.info("$identifier notified parent ${par.identifier}") + par.notifyParent() + return@let + } + + LOG.info("$identifier reactivated parent ${par.identifier}") + par.activate() + } } @Ta5kBuilder @@ -398,7 +405,7 @@ abstract class Task( companion object { val MAX_DEPTH = 20 - const val MAX_DEBUG_ENTRIES = 7 + const val MAX_DEBUG_ENTRIES = 15 interface EmptyTask diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index 812f19c98..6bb437480 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -10,13 +10,12 @@ class AcquireMaterial( val selection: StackSelection ) : Task() { override fun SafeContext.onStart() { - findContainerWithSelection(selection)?.let { container -> - container.prepare().onSuccess { _, _ -> - container.withdraw(selection).onSuccess { _, _ -> - success(selection) - }.start(this@AcquireMaterial) - }.start(this@AcquireMaterial) - } ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path + findContainerWithSelection(selection) + ?.doWithdrawal(selection) + ?.onSuccess { _, _ -> + success(selection) + }?.start(this@AcquireMaterial) + ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path } companion object { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 62c4d8d63..b777ee7f7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -5,9 +5,9 @@ import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.InteractionConfig +import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.config.groups.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task @@ -23,10 +23,10 @@ class BreakBlock @Ta5kBuilder constructor( private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, private val sides: Set = emptySet(), - private val collectDrop: Boolean = false, - private val rotate: Boolean = false, - private val swingHand: Boolean = true, - private val particles: Boolean = true, + private val collectDrop: Boolean = TaskFlow.buildSettings.collectDrops, + private val rotate: Boolean = TaskFlow.buildSettings.rotateForBreak, + private val swingHand: Boolean = TaskFlow.buildSettings.swingHand, + private val particles: Boolean = TaskFlow.buildSettings.particlesOnBreak, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null @@ -63,9 +63,6 @@ class BreakBlock @Ta5kBuilder constructor( if (rotate) return@listener breakBlock(ctx.result.side) - } - - listener { if (state.isAir && !collectDrop) success(null) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 010608f6b..6af961c04 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,5 +1,6 @@ package com.lambda.task.tasks +import baritone.api.pathing.goals.GoalNear import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -22,11 +23,13 @@ import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task +import com.lambda.util.BaritoneUtils.primary import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.instantBreakable import com.lambda.util.Communication.info import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.primitives.extension.Structure import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition @@ -35,6 +38,7 @@ import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemUsageContext import net.minecraft.registry.RegistryKeys import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.HitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Box @@ -44,14 +48,16 @@ import kotlin.math.pow class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, - private val collectDrops: Boolean = false, - private val skipWeakBlocks: Boolean = false, - private val pathing: Boolean = true, private val finishOnDone: Boolean = true, - private val instantAtOnce: Boolean = true, - private val limitPerTick: Int = 15, + private val collectDrops: Boolean = TaskFlow.buildSettings.collectDrops, + private val breakWeakBlocks: Boolean = TaskFlow.buildSettings.breakWeakBlocks, + private val pathing: Boolean = TaskFlow.buildSettings.pathing, + private val interactLimit: Int = TaskFlow.buildSettings.interactLimit, + private val instantAtOnce: Boolean = TaskFlow.buildSettings.breakInstantAtOnce, + private val useRayCast: Boolean = TaskFlow.interactionSettings.useRayCast, ) : Task() { - private var lastResult: BuildResult? = null + private var lastTask: Task<*>? = null + private var doneBlueprint: Structure? = null override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create(this) @@ -66,10 +72,14 @@ class BuildStructure @Ta5kBuilder constructor( return@listener } + doneBlueprint?.entries?.take(3)?.lastOrNull()?.let { + primary.customGoalProcess.setGoalAndPath(GoalNear(it.key.up(), 0)) + } + if (finishOnDone && blueprint.isDone(this)) { + doneBlueprint = blueprint.structure if (blueprint is DynamicBlueprint) { if (!blueprint.onDone(this)) { - this@BuildStructure.info("Structure moved") return@listener } } @@ -90,7 +100,7 @@ class BuildStructure @Ta5kBuilder constructor( acc.addAll(it) return@fold acc } - checkBreakResults(pos, target).let { + checkBreakResults(pos).let { if (it.isEmpty()) return@let acc.addAll(it) return@fold acc @@ -100,14 +110,14 @@ class BuildStructure @Ta5kBuilder constructor( val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } - .take(limitPerTick) + .take(interactLimit) if (instantAtOnce && instantResults.isNotEmpty()) { cancelSubTasks() instantResults.forEach { it.resolve.start(this@BuildStructure, false) } - lastResult = instantResults.last() + lastTask = instantResults.last().resolve return@listener } @@ -115,10 +125,10 @@ class BuildStructure @Ta5kBuilder constructor( res results.minOrNull()?.let { result -> - if (lastResult == result) return@listener if (result !is Resolvable) return@listener + if (lastTask?.isCompleted == false) return@listener - lastResult = result + lastTask = result.resolve cancelSubTasks() if (!pathing && result is BuildResult.OutOfReach) return@let @@ -177,13 +187,13 @@ class BuildStructure @Ta5kBuilder constructor( val rotation = TaskFlow.rotationSettings Direction.entries.forEach { neighbor -> - val neighPos = pos.offset(neighbor) + val hitPos = pos.offset(neighbor) val hitSide = neighbor.opposite - val voxelShape = neighPos.blockState(world).getOutlineShape(world, neighPos) - val boxes = voxelShape.boundingBoxes.map { it.offset(neighPos) } + val voxelShape = hitPos.blockState(world).getOutlineShape(world, hitPos) + val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } val verify: HitResult.() -> Boolean = { - blockResult?.blockPos == neighPos && blockResult?.side == hitSide + blockResult?.blockPos == hitPos && blockResult?.side == hitSide } val eye = player.getCameraPosVec(mc.tickDelta) @@ -191,20 +201,28 @@ class BuildStructure @Ta5kBuilder constructor( val reachSq = interact.reach.pow(2) boxes.forEach { box -> - // ToDo: Verify needed resolution - scanVisibleSurfaces(eye, box, setOf(hitSide), 2) { vec -> + val res = if (useRayCast) interact.resolution else 2 + scanVisibleSurfaces(eye, box, setOf(hitSide), res) { side, vec -> if (eye distSq vec > reachSq) { - acc.add(BuildResult.OutOfReach(pos, eye.distanceTo(vec))) + acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) return@scanVisibleSurfaces } - val cast = eye.rotationTo(vec).rayCast( - interact.reach, - interact.rayCastMask, - eye - ) ?: return@scanVisibleSurfaces - if (!cast.verify()) return@scanVisibleSurfaces - validHits[vec] = cast + validHits[vec] = if (useRayCast) { + val cast = eye.rotationTo(vec) + .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + cast + } else { + BlockHitResult( + vec, + side, + hitPos, + false + ) + } + } } @@ -299,12 +317,12 @@ class BuildStructure @Ta5kBuilder constructor( return acc } - private fun SafeContext.checkBreakResults(pos: BlockPos, target: TargetState): Set { + private fun SafeContext.checkBreakResults(pos: BlockPos): Set { val acc = mutableSetOf() val state = pos.blockState(world) /* is a block that will be destroyed by breaking adjacent blocks */ - if (skipWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -354,11 +372,7 @@ class BuildStructure @Ta5kBuilder constructor( val interact = TaskFlow.interactionSettings val rotation = TaskFlow.rotationSettings val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast( - interact.reach, - interact.rayCastMask, - eye - ) + val currentCast = currentRotation.rayCast(interact.reach, eye) val voxelShape = state.getOutlineShape(world, pos) val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } @@ -385,20 +399,27 @@ class BuildStructure @Ta5kBuilder constructor( val reachSq = interact.reach.pow(2) boxes.forEach { box -> - // ToDo: Verify needed resolution - scanVisibleSurfaces(eye, box, emptySet(), 2) { vec -> + val res = if (useRayCast) interact.resolution else 2 + scanVisibleSurfaces(eye, box, emptySet(), res) { side, vec -> if (eye distSq vec > reachSq) { - acc.add(BuildResult.OutOfReach(pos, eye.distanceTo(vec))) + acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) return@scanVisibleSurfaces } - val cast = eye.rotationTo(vec).rayCast( - interact.reach, - interact.rayCastMask, - eye - ) ?: return@scanVisibleSurfaces - if (!cast.verify()) return@scanVisibleSurfaces - - validHits[vec] = cast + + validHits[vec] = if (useRayCast) { + val cast = eye.rotationTo(vec) + .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + cast + } else { + BlockHitResult( + vec, + side, + pos, + false + ) + } } } @@ -443,17 +464,23 @@ class BuildStructure @Ta5kBuilder constructor( companion object { @Ta5kBuilder fun buildStructure( - collectDrops: Boolean = false, - skipWeakBlocks: Boolean = false, - pathing: Boolean = true, finishOnDone: Boolean = true, + collectDrops: Boolean = TaskFlow.buildSettings.collectDrops, + breakWeakBlocks: Boolean = TaskFlow.buildSettings.breakWeakBlocks, + pathing: Boolean = TaskFlow.buildSettings.pathing, + interactLimit: Int = TaskFlow.buildSettings.interactLimit, + instantAtOnce: Boolean = TaskFlow.buildSettings.breakInstantAtOnce, + useRayCast: Boolean = TaskFlow.interactionSettings.useRayCast, blueprint: () -> Blueprint, ) = BuildStructure( blueprint(), + finishOnDone, collectDrops, - skipWeakBlocks, + breakWeakBlocks, pathing, - finishOnDone + interactLimit, + instantAtOnce, + useRayCast ) @Ta5kBuilder diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index e5d0271d6..8ed4d2df6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -9,6 +9,7 @@ import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.task.Task import com.lambda.util.BaritoneUtils +import com.lambda.util.BaritoneUtils.primary import net.minecraft.util.math.BlockPos // ToDo: Custom heuristic goals @@ -23,7 +24,8 @@ class GoalTask( init { listener { - if (!BaritoneUtils.isActive || check()) { + if (goal.isInGoal(player.blockPos) || check()) { + primary.customGoalProcess.goal = null success(Unit) } } @@ -34,6 +36,14 @@ class GoalTask( fun moveToBlock(blockPos: BlockPos) = GoalTask(GoalBlock(blockPos)) + @Ta5kBuilder + fun moveNearBlock(blockPos: BlockPos, range: Int) = + GoalTask(GoalNear(blockPos, range)) + + @Ta5kBuilder + fun moveToBlockUntil(blockPos: BlockPos, check: SafeContext.() -> Boolean) = + GoalTask(GoalBlock(blockPos), check) + @Ta5kBuilder fun moveToXY(blockPos: BlockPos) = GoalTask(GoalXZ(blockPos.x, blockPos.z)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 053d537ab..6e4fb6a75 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -3,8 +3,8 @@ package com.lambda.task.tasks import com.lambda.event.events.RotationEvent import com.lambda.event.events.ScreenHandlerEvent import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.InteractionConfig -import com.lambda.interaction.rotation.IRotationConfig +import com.lambda.config.groups.InteractionConfig +import com.lambda.config.groups.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 017199375..93b4f290e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -10,7 +10,7 @@ import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState class PlaceBlock @Ta5kBuilder constructor( - val ctx: PlaceContext, + private val ctx: PlaceContext, private val swingHand: Boolean = true, private val rotate: Boolean = true, private val waitForConfirmation: Boolean = false, @@ -54,22 +54,26 @@ class PlaceBlock @Ta5kBuilder constructor( ctx.result ) - if (actionResult.isAccepted && matches) { + if (actionResult.isAccepted) { if (actionResult.shouldSwingHand() && swingHand) { player.swingHand(ctx.hand) } - // ToDo: WTF did i do here? i dont remember if (!player.getStackInHand(ctx.hand).isEmpty && interaction.hasCreativeInventory()) { mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) } - if (!waitForConfirmation) success(Unit) + if (!waitForConfirmation && matches) { + LOG.info("Placed $preStack at ${ + ctx.result.blockPos.toShortString() + } (${ctx.result.side}) with expecting state ${ + ctx.expectedState + } and expecting position at ${ctx.resultingPos.toShortString()}") + success(Unit) + } } else { - failure("Failed to place block as simulation does not match actual result $actionResult") + failure("Internal interaction failed with $actionResult") } - - LOG.info("Placed $preStack at ${ctx.result.blockPos.toShortString()} (${ctx.result.side}) with expecting state ${ctx.expectedState} and expecting position at ${ctx.resultingPos.toShortString()}") } companion object { diff --git a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt index 60a29c744..73d54550b 100644 --- a/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/world/raycast/RayCastUtils.kt @@ -53,11 +53,12 @@ object RayCastUtils { // ToDo: Should rather move player hitbox down and check collision fun distanceToGround(maxDist: Double = 100.0) = runSafe { val pos = player.pos.add(0.0, 0.1, 0.0) - val cast = Rotation.DOWN.rayCast(maxDist, RayCastMask.BLOCK, pos, false) ?: return@runSafe maxDist + val cast = Rotation.DOWN.rayCast(maxDist, pos, false, RayCastMask.BLOCK) ?: return@runSafe maxDist return@runSafe max(0.0, pos.y - cast.pos.y) } + val HitResult.entityResult: EntityHitResult? get() { if (type == HitResult.Type.MISS) return null return this as? EntityHitResult @@ -68,7 +69,7 @@ object RayCastUtils { return this as? BlockHitResult } - fun HitResult.distanceTo(pos: Vec3d) = pos.distanceTo(pos) + fun HitResult.distanceTo(pos: Vec3d) = this.pos.distanceTo(pos) val HitResult.orNull get() = entityResult ?: blockResult From 66675f2c59d12e916559205e0052f03af8e88bd0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 1 Jun 2024 06:00:31 +0200 Subject: [PATCH 22/47] Structure slicing for HWT --- .../construction/DynamicBlueprint.kt | 23 ++------- .../module/modules/player/HighwayTools.kt | 47 +++++++++++++------ .../com/lambda/task/tasks/BuildStructure.kt | 43 +++++++---------- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt index 5a5832a02..029537309 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -1,23 +1,15 @@ package com.lambda.interaction.construction import com.lambda.context.SafeContext -import com.lambda.threading.runSafe import com.lambda.util.primitives.extension.Structure -import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Vec3i data class DynamicBlueprint( val init: SafeContext.(Structure) -> Structure = { emptyMap() }, - val onTick: SafeContext.(Structure) -> Structure = { it }, - val onDone: SafeContext.(Structure) -> Structure? = { null }, + val update: SafeContext.(Structure) -> Structure = { it }, ) : Blueprint() { - fun onTick(ctx: SafeContext) { - structure = ctx.onTick(structure) - } - - fun onDone(ctx: SafeContext): Boolean { - structure = ctx.onDone(structure) ?: return true - return false + fun update(ctx: SafeContext) { + structure = ctx.update(structure) } fun create(ctx: SafeContext) { @@ -37,15 +29,10 @@ data class DynamicBlueprint( fun blueprintOnTick( init: SafeContext.(Structure) -> Structure = { emptyMap() }, onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init = init, onTick = onTick) - - fun blueprintOnDone( - init: SafeContext.(Structure) -> Structure = { emptyMap() }, - onDone: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init = init, onDone = onDone) + ) = DynamicBlueprint(init = init, update = onTick) fun Structure.toBlueprint( onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init = { emptyMap() }, onTick = onTick) + ) = DynamicBlueprint(init = { emptyMap() }, update = onTick) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 36d9d9b07..7ea564dfe 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -1,15 +1,14 @@ package com.lambda.module.modules.player import baritone.api.pathing.goals.GoalNear -import com.lambda.interaction.construction.DynamicBlueprint -import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnDone -import com.lambda.interaction.construction.DynamicBlueprint.Companion.offset +import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BaritoneUtils.primary +import com.lambda.util.Communication.info import com.lambda.util.KeyCode import com.lambda.util.player.MovementUtils.direction import com.lambda.util.primitives.extension.Structure @@ -27,11 +26,11 @@ object HighwayTools : Module( defaultKeybind = KeyCode.X ) { private val height by setting("Height", 4, 1..10, 1) - private val width by setting("Width", 6, 1..100, 1) - private val rimHeight by setting("Rim Height", 1, 1..6, 1) + private val width by setting("Width", 6, 1..30, 1) + private val rimHeight by setting("Rim Height", 0, 1..6, 1) private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") private val material = Blocks.OBSIDIAN - private val distance by setting("Distance", -1, -1..1000000, 100, description = "Distance to build the highway (negative for infinite)") + private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway (negative for infinite)") // ToDo: Fix block setting // private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") @@ -45,23 +44,43 @@ object HighwayTools : Module( onEnable { direction = player.direction() startPos = player.blockPos - val task = getTask() - task.start(null) - runningTask = task + currentPos = startPos + buildSlice() + } + onDisable { + runningTask?.cancel() + runningTask = null + distanceMoved = 0 } - onDisable { runningTask?.cancel() } } - private fun getTask() = + private fun buildSlice() { + val blueprint = generateHighway() + .map { it.key.add(currentPos) to it.value } + .toMap() + .toBlueprint() + buildStructure { - blueprintOnDone({ generateHighway() }) { last -> + blueprint + }.apply { + runningTask = this + primary.customGoalProcess.setGoalAndPath(GoalNear(currentPos, 1)) + onSuccess { _, _ -> distanceMoved++ val vec = Vec3i(direction.offsetX, 0, direction.offsetZ) currentPos = currentPos.add(vec) - offset(vec).invoke(this, last) + + if (distanceMoved < distance || distance < 0) { + buildSlice() + } else { + this@HighwayTools.info("Highway built") + disable() + } } + start(null) } + } private fun generateHighway(): Structure { val structure = mutableMapOf() @@ -140,6 +159,6 @@ object HighwayTools : Module( // ).associateWith { TargetState.Support(Direction.UP) } } - return structure.map { it.key.add(startPos) to it.value }.toMap() + return structure } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 6af961c04..54bf65df9 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,6 +1,5 @@ package com.lambda.task.tasks -import baritone.api.pathing.goals.GoalNear import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -23,13 +22,10 @@ import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.util.BaritoneUtils.primary import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.instantBreakable -import com.lambda.util.Communication.info import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.primitives.extension.Structure import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock import net.minecraft.block.pattern.CachedBlockPosition @@ -57,7 +53,6 @@ class BuildStructure @Ta5kBuilder constructor( private val useRayCast: Boolean = TaskFlow.interactionSettings.useRayCast, ) : Task() { private var lastTask: Task<*>? = null - private var doneBlueprint: Structure? = null override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create(this) @@ -65,31 +60,13 @@ class BuildStructure @Ta5kBuilder constructor( init { listener { - (blueprint as? DynamicBlueprint)?.onTick(this) + (blueprint as? DynamicBlueprint)?.update(this) if (finishOnDone && blueprint.structure.isEmpty()) { failure("Structure is empty") return@listener } - doneBlueprint?.entries?.take(3)?.lastOrNull()?.let { - primary.customGoalProcess.setGoalAndPath(GoalNear(it.key.up(), 0)) - } - - if (finishOnDone && blueprint.isDone(this)) { - doneBlueprint = blueprint.structure - if (blueprint is DynamicBlueprint) { - if (!blueprint.onDone(this)) { - return@listener - } - } - - this@BuildStructure.info("Structure is done") - cancelSubTasks() - success(Unit) - return@listener - } - val results = blueprint.structure.entries.fold(mutableSetOf()) { acc, (pos, target) -> checkRequirements(pos, target)?.let { acc.add(it) @@ -125,7 +102,15 @@ class BuildStructure @Ta5kBuilder constructor( res results.minOrNull()?.let { result -> - if (result !is Resolvable) return@listener + if (result !is Resolvable) { + if (result is BuildResult.Done) { + checkDone() + } else { + failure("Failed to resolve build result: $result") + return@listener + } + return@listener + } if (lastTask?.isCompleted == false) return@listener lastTask = result.resolve @@ -137,6 +122,14 @@ class BuildStructure @Ta5kBuilder constructor( } } + private fun SafeContext.checkDone() { + if (!finishOnDone) return + + cancelSubTasks() + success(Unit) + return + } + private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? { /* the chunk is not loaded */ if (!world.isChunkLoaded(pos)) { From ba1ded8e7297a6b4093eaa6f96d79bea1fb5bf76 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sat, 1 Jun 2024 06:25:18 +0200 Subject: [PATCH 23/47] Build in n long slices --- .../module/modules/player/HighwayTools.kt | 33 ++++++++++--------- .../com/lambda/util/player/MovementUtils.kt | 21 ++++++------ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 7ea564dfe..4acbcf4f8 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -10,7 +10,7 @@ import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BaritoneUtils.primary import com.lambda.util.Communication.info import com.lambda.util.KeyCode -import com.lambda.util.player.MovementUtils.direction +import com.lambda.util.player.MovementUtils.octant import com.lambda.util.primitives.extension.Structure import com.lambda.util.world.StructureUtils.generateDirectionalTube import net.minecraft.block.Blocks @@ -31,10 +31,11 @@ object HighwayTools : Module( private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") private val material = Blocks.OBSIDIAN private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway (negative for infinite)") + private val sliceSize by setting("Slice Size", 3, 1..5, 1, description = "Number of slices to build at once") // ToDo: Fix block setting // private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") - private var direction = EightWayDirection.NORTH + private var octant = EightWayDirection.NORTH private var distanceMoved = 0 private var startPos = BlockPos.ORIGIN private var currentPos = BlockPos.ORIGIN @@ -42,7 +43,7 @@ object HighwayTools : Module( init { onEnable { - direction = player.direction() + octant = player.octant startPos = player.blockPos currentPos = startPos buildSlice() @@ -55,22 +56,22 @@ object HighwayTools : Module( } private fun buildSlice() { - val blueprint = generateHighway() - .map { it.key.add(currentPos) to it.value } - .toMap() - .toBlueprint() + distanceMoved += sliceSize + + var structure: Structure = mutableMapOf() + val slice = highwaySlice() + repeat(sliceSize) { + val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) + currentPos = currentPos.add(vec) + structure += slice.map { it.key.add(currentPos) to it.value } + } buildStructure { - blueprint + structure.toBlueprint() }.apply { runningTask = this - primary.customGoalProcess.setGoalAndPath(GoalNear(currentPos, 1)) + primary.customGoalProcess.setGoalAndPath(GoalNear(currentPos, sliceSize)) onSuccess { _, _ -> - distanceMoved++ - - val vec = Vec3i(direction.offsetX, 0, direction.offsetZ) - currentPos = currentPos.add(vec) - if (distanceMoved < distance || distance < 0) { buildSlice() } else { @@ -82,9 +83,9 @@ object HighwayTools : Module( } } - private fun generateHighway(): Structure { + private fun highwaySlice(): Structure { val structure = mutableMapOf() - val orthogonal = EightWayDirection.entries[(direction.ordinal + 2).mod(8)] + val orthogonal = EightWayDirection.entries[(octant.ordinal + 2).mod(8)] val center = (width / 2.0).roundToInt() // Area to clear diff --git a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt index b1ba0d9b5..fe9c1417b 100644 --- a/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/MovementUtils.kt @@ -84,15 +84,16 @@ object MovementUtils { val Entity.moveDelta get() = moveDiff.let { hypot(it.x, it.z) } val Entity.motionDelta get() = hypot(this.velocity.x, this.velocity.z) - fun Entity.direction(): EightWayDirection { - // Normalize the yaw to be within the range of -180 to 179 degrees - var normalizedYaw = (yaw + 180.0) % 360.0 - if (normalizedYaw < 0) { - normalizedYaw += 360.0 + val Entity.octant: EightWayDirection + get() { + // Normalize the yaw to be within the range of -180 to 179 degrees + var normalizedYaw = (yaw + 180.0) % 360.0 + if (normalizedYaw < 0) { + normalizedYaw += 360.0 + } + + // Calculate the index of the closest direction + val directionIndex = ((normalizedYaw + 22.5) / 45.0).toInt() % 8 + return EightWayDirection.entries[directionIndex] } - - // Calculate the index of the closest direction - val directionIndex = ((normalizedYaw + 22.5) / 45.0).toInt() % 8 - return EightWayDirection.entries[directionIndex] - } } \ No newline at end of file From b3a55da996bd8a6f824f8aa431ae2770e218f7c8 Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 2 Jun 2024 02:35:01 +0200 Subject: [PATCH 24/47] Stacktraceing and shulker container --- .../construction/result/BuildResult.kt | 5 +- .../interaction/material/ContainerManager.kt | 12 ++-- .../interaction/material/MaterialContainer.kt | 35 ++++++------ .../material/container/MainHandContainer.kt | 2 +- .../visibilty/VisibilityChecker.kt | 8 +-- .../lambda/module/modules/client/TaskFlow.kt | 6 +- .../src/main/kotlin/com/lambda/task/Task.kt | 20 ++++++- .../com/lambda/task/tasks/BreakBlock.kt | 16 +++--- .../com/lambda/task/tasks/BuildStructure.kt | 57 ++++++++----------- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 5 ++ .../com/lambda/task/tasks/OpenContainer.kt | 4 +- 11 files changed, 94 insertions(+), 76 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 360cadc5e..1629c69b8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,5 +1,6 @@ package com.lambda.interaction.construction.result +import baritone.process.BuilderProcess.GoalAdjacent import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select @@ -7,6 +8,7 @@ import com.lambda.interaction.material.container.MainHandContainer import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.GoalTask.Companion.moveNearBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToGoal import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded import net.minecraft.block.BlockState import net.minecraft.item.Item @@ -105,12 +107,13 @@ abstract class BuildResult : ComparableResult { */ data class NotVisible( override val blockPos: BlockPos, + val hitPos: BlockPos, val side: Direction, val distance: Double ) : Resolvable, BuildResult() { override val rank = Rank.NOT_VISIBLE - override val resolve = emptyTask() + override val resolve = moveToGoal(GoalAdjacent(hitPos, blockPos, true)) override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 984603414..021b1af10 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -23,7 +23,7 @@ import java.util.TreeSet // ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. object ContainerManager : Loadable { // ToDo: Maybe use reflection to get all containers? - val container = TreeSet().apply { + private val container = TreeSet().apply { add(CreativeContainer) add(EnderChestContainer) add(HotbarContainer) @@ -70,23 +70,27 @@ object ContainerManager : Loadable { } } + fun container() = container.flatMap { + setOf(it) + it.shulkerContainer + } + fun StackSelection.transfer(destination: MaterialContainer) = findContainerWithSelection(this)?.transfer(this, destination) fun findContainer( block: (MaterialContainer) -> Boolean - ): MaterialContainer? = container.find(block) + ): MaterialContainer? = container().find(block) fun findContainerWithSelection( selection: StackSelection ): MaterialContainer? = - container.find { it.available(selection) >= selection.count } + container().find { it.available(selection) >= selection.count } fun findContainerWithSelection( selectionBuilder: StackSelection.() -> Unit ): MaterialContainer? { val selection = StackSelection().apply(selectionBuilder) - return container.find { it.available(selection) >= selection.count } + return container().find { it.available(selection) >= selection.count } } fun findContainerWithStacks( diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 7ef173974..29a1156e6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -4,7 +4,6 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ShulkerBoxContainer import com.lambda.interaction.material.transfer.TransferResult import com.lambda.task.Task -import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty import com.lambda.util.item.ItemStackUtils.shulkerBoxContents @@ -18,6 +17,17 @@ abstract class MaterialContainer( ) : Comparable { abstract var stacks: List + val shulkerContainer get() = + stacks.filter { + it.item in ItemUtils.shulkerBoxes + }.map { stack -> + ShulkerBoxContainer( + stack.shulkerBoxContents, + containedIn = this@MaterialContainer, + shulkerStack = stack, + ) + }.toSet() + fun update(stacks: List) { this.stacks = stacks } @@ -53,17 +63,17 @@ abstract class MaterialContainer( } } ?: deposit(selection) - open fun filter(selection: StackSelection) = - selection.filterStacks(stacks) + open fun StackSelection.matchingStacks() = + filterStacks(stacks) - open fun filter(selection: (ItemStack) -> Boolean) = - filter(selection.select()) + open fun matchingStacks(selection: (ItemStack) -> Boolean) = + selection.select().matchingStacks() open fun available(selection: StackSelection) = - filter(selection).count + selection.matchingStacks().count open fun spaceLeft(selection: StackSelection) = - filter(selection).spaceLeft + stacks.empty * selection.stackSize + selection.matchingStacks().spaceLeft + stacks.empty * selection.stackSize fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { val amount = available(selection) @@ -87,17 +97,6 @@ abstract class MaterialContainer( ) } - fun List.doShulkerCheck() = - filter { - it.item in ItemUtils.shulkerBoxes - }.map { stack -> - ShulkerBoxContainer( - stack.shulkerBoxContents, - containedIn = this@MaterialContainer, - shulkerStack = stack, - ) - }.toSet() - enum class Rank { CREATIVE, MAIN_HAND, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index ee6a4fca7..e425c4a97 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -20,7 +20,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromMainHand") override fun deposit(selection: StackSelection) = buildTask("DepositToMainHand") { - InventoryContainer.filter(selection).firstOrNull()?.let { stack -> + selection.matchingStacks().firstOrNull()?.let { stack -> if (ItemStack.areEqual(stack, player.mainHandStack)) { return@buildTask } diff --git a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt index 9a8c58cc2..4ea3dbe99 100644 --- a/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt +++ b/common/src/main/kotlin/com/lambda/interaction/visibilty/VisibilityChecker.kt @@ -32,8 +32,8 @@ object VisibilityChecker { fun SafeContext.lookAtBlock( blockPos: BlockPos, - rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + rotationConfig: IRotationConfig = TaskFlow.rotation, + interactionConfig: InteractionConfig = TaskFlow.interact, sides: Set = emptySet() ): RotationContext? { val state = blockPos.blockState(world) @@ -46,8 +46,8 @@ object VisibilityChecker { fun SafeContext.findRotation( boxes: List, - rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - interact: InteractionConfig = TaskFlow.interactionSettings, + rotationConfig: IRotationConfig = TaskFlow.rotation, + interact: InteractionConfig = TaskFlow.interact, sides: Set = emptySet(), verify: HitResult.() -> Boolean, ): RotationContext? { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index b27bdf3c9..d62aac295 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -18,13 +18,13 @@ object TaskFlow : Module( } private val page by setting("Page", Page.BUILD) - val buildSettings = BuildSettings(this) { + val build = BuildSettings(this) { page == Page.BUILD } - val rotationSettings = RotationSettings(this) { + val rotation = RotationSettings(this) { page == Page.ROTATION } - val interactionSettings = InteractionSettings(this) { + val interact = InteractionSettings(this) { page == Page.INTERACTION } // val disposables by setting("Disposables", ItemUtils.defaultDisposables) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 82f3c6a80..c303e27aa 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -217,7 +217,10 @@ abstract class Task( } @Ta5kBuilder - fun failure(e: Throwable) { + fun failure( + e: Throwable, + stacktrace: MutableList> = mutableListOf() + ) { if (attempted < tries) { attempted++ warn("Failed task with error: ${e.message}, retrying ($attempted/$tries) ...") @@ -230,10 +233,21 @@ abstract class Task( } state = State.FAILED - logError("Task failed after $attempted attempts with error: ${e.message}") stopListening() runSafe { onException(this@Task, e) } - parent?.failure(e) + stacktrace.add(this) + parent?.failure(e, stacktrace) ?: run { + val message = buildString { + stacktrace.firstOrNull()?.let { first -> + append("${first.identifier} failed: ${e.message}\n") + stacktrace.drop(1).forEach { + append(" -> ${it.identifier}\n") + } + } + } + LOG.error(message, e) + logError(message) + } } /** diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index b777ee7f7..500942e1d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -20,13 +20,13 @@ import net.minecraft.util.math.Direction class BreakBlock @Ta5kBuilder constructor( private val ctx: BreakContext, - private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + private val rotationConfig: IRotationConfig = TaskFlow.rotation, + private val interactionConfig: InteractionConfig = TaskFlow.interact, private val sides: Set = emptySet(), - private val collectDrop: Boolean = TaskFlow.buildSettings.collectDrops, - private val rotate: Boolean = TaskFlow.buildSettings.rotateForBreak, - private val swingHand: Boolean = TaskFlow.buildSettings.swingHand, - private val particles: Boolean = TaskFlow.buildSettings.particlesOnBreak, + private val collectDrop: Boolean = TaskFlow.build.collectDrops, + private val rotate: Boolean = TaskFlow.build.rotateForBreak, + private val swingHand: Boolean = TaskFlow.build.swingHand, + private val particles: Boolean = TaskFlow.build.particlesOnBreak, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null @@ -85,8 +85,8 @@ class BreakBlock @Ta5kBuilder constructor( @Ta5kBuilder fun breakBlock( ctx: BreakContext, - rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + rotationConfig: IRotationConfig = TaskFlow.rotation, + interactionConfig: InteractionConfig = TaskFlow.interact, sides: Set = emptySet(), collectDrop: Boolean = false, rotate: Boolean = false, diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 54bf65df9..4a5c2d8e5 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -25,6 +25,7 @@ import com.lambda.task.Task import com.lambda.util.BlockUtils import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.BlockUtils.vecOf import com.lambda.util.math.VecUtils.distSq import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.OperatorBlock @@ -45,12 +46,8 @@ import kotlin.math.pow class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, private val finishOnDone: Boolean = true, - private val collectDrops: Boolean = TaskFlow.buildSettings.collectDrops, - private val breakWeakBlocks: Boolean = TaskFlow.buildSettings.breakWeakBlocks, - private val pathing: Boolean = TaskFlow.buildSettings.pathing, - private val interactLimit: Int = TaskFlow.buildSettings.interactLimit, - private val instantAtOnce: Boolean = TaskFlow.buildSettings.breakInstantAtOnce, - private val useRayCast: Boolean = TaskFlow.interactionSettings.useRayCast, + private val pathing: Boolean = TaskFlow.build.pathing, + private val collectDrops: Boolean = TaskFlow.build.collectDrops, ) : Task() { private var lastTask: Task<*>? = null @@ -87,9 +84,10 @@ class BuildStructure @Ta5kBuilder constructor( val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } - .take(interactLimit) + .sorted() + .take(TaskFlow.build.interactLimit) - if (instantAtOnce && instantResults.isNotEmpty()) { + if (TaskFlow.build.breakInstantAtOnce && instantResults.isNotEmpty()) { cancelSubTasks() instantResults.forEach { it.resolve.start(this@BuildStructure, false) @@ -98,10 +96,9 @@ class BuildStructure @Ta5kBuilder constructor( return@listener } - val res = results.sorted() - res - results.minOrNull()?.let { result -> + if (!pathing && result is BuildResult.OutOfReach) return@let + if (result !is Resolvable) { if (result is BuildResult.Done) { checkDone() @@ -116,7 +113,6 @@ class BuildStructure @Ta5kBuilder constructor( lastTask = result.resolve cancelSubTasks() - if (!pathing && result is BuildResult.OutOfReach) return@let result.resolve.start(this@BuildStructure, false) } } @@ -176,8 +172,8 @@ class BuildStructure @Ta5kBuilder constructor( if (target is TargetState.Air || !pos.blockState(world).isReplaceable) return acc - val interact = TaskFlow.interactionSettings - val rotation = TaskFlow.rotationSettings + val interact = TaskFlow.interact + val rotation = TaskFlow.rotation Direction.entries.forEach { neighbor -> val hitPos = pos.offset(neighbor) @@ -194,14 +190,14 @@ class BuildStructure @Ta5kBuilder constructor( val reachSq = interact.reach.pow(2) boxes.forEach { box -> - val res = if (useRayCast) interact.resolution else 2 + val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 scanVisibleSurfaces(eye, box, setOf(hitSide), res) { side, vec -> if (eye distSq vec > reachSq) { acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) return@scanVisibleSurfaces } - validHits[vec] = if (useRayCast) { + validHits[vec] = if (TaskFlow.interact.useRayCast) { val cast = eye.rotationTo(vec) .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces if (!cast.verify()) return@scanVisibleSurfaces @@ -219,6 +215,11 @@ class BuildStructure @Ta5kBuilder constructor( } } + if (validHits.isEmpty()) { + acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide)))) + return@forEach + } + validHits.keys.mostCenter?.let { optimum -> validHits.minByOrNull { optimum distSq it.key }?.let { closest -> val optimumRotation = eye.rotationTo(closest.key) @@ -315,7 +316,7 @@ class BuildStructure @Ta5kBuilder constructor( val state = pos.blockState(world) /* is a block that will be destroyed by breaking adjacent blocks */ - if (breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + if (TaskFlow.build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { acc.add(BuildResult.Ignored(pos)) return acc } @@ -362,8 +363,8 @@ class BuildStructure @Ta5kBuilder constructor( val eye = player.getCameraPosVec(mc.tickDelta) - val interact = TaskFlow.interactionSettings - val rotation = TaskFlow.rotationSettings + val interact = TaskFlow.interact + val rotation = TaskFlow.rotation val currentRotation = RotationManager.currentRotation val currentCast = currentRotation.rayCast(interact.reach, eye) @@ -392,14 +393,14 @@ class BuildStructure @Ta5kBuilder constructor( val reachSq = interact.reach.pow(2) boxes.forEach { box -> - val res = if (useRayCast) interact.resolution else 2 + val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 scanVisibleSurfaces(eye, box, emptySet(), res) { side, vec -> if (eye distSq vec > reachSq) { acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) return@scanVisibleSurfaces } - validHits[vec] = if (useRayCast) { + validHits[vec] = if (TaskFlow.interact.useRayCast) { val cast = eye.rotationTo(vec) .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces if (!cast.verify()) return@scanVisibleSurfaces @@ -458,22 +459,14 @@ class BuildStructure @Ta5kBuilder constructor( @Ta5kBuilder fun buildStructure( finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlow.buildSettings.collectDrops, - breakWeakBlocks: Boolean = TaskFlow.buildSettings.breakWeakBlocks, - pathing: Boolean = TaskFlow.buildSettings.pathing, - interactLimit: Int = TaskFlow.buildSettings.interactLimit, - instantAtOnce: Boolean = TaskFlow.buildSettings.breakInstantAtOnce, - useRayCast: Boolean = TaskFlow.interactionSettings.useRayCast, + collectDrops: Boolean = TaskFlow.build.collectDrops, + pathing: Boolean = TaskFlow.build.pathing, blueprint: () -> Blueprint, ) = BuildStructure( blueprint(), finishOnDone, - collectDrops, - breakWeakBlocks, pathing, - interactLimit, - instantAtOnce, - useRayCast + collectDrops, ) @Ta5kBuilder diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 8ed4d2df6..c83d56b7c 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -7,6 +7,7 @@ import baritone.api.pathing.goals.GoalXZ import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.BaritoneUtils import com.lambda.util.BaritoneUtils.primary @@ -32,6 +33,10 @@ class GoalTask( } companion object { + @Ta5kBuilder + fun moveToGoal(goal: Goal) = + GoalTask(goal) + @Ta5kBuilder fun moveToBlock(blockPos: BlockPos) = GoalTask(GoalBlock(blockPos)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 6e4fb6a75..7129c925a 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -17,8 +17,8 @@ import net.minecraft.util.math.Direction class OpenContainer( private val blockPos: BlockPos, private val waitForSlotLoad: Boolean = true, - private val rotationConfig: IRotationConfig = TaskFlow.rotationSettings, - private val interactionConfig: InteractionConfig = TaskFlow.interactionSettings, + private val rotationConfig: IRotationConfig = TaskFlow.rotation, + private val interactionConfig: InteractionConfig = TaskFlow.interact, private val sides: Set = emptySet(), ) : Task() { private var screenHandler: H? = null From d3b2fa44d126388ae469c5fee83d5528e75beeef Mon Sep 17 00:00:00 2001 From: Constructor Date: Sun, 2 Jun 2024 03:17:51 +0200 Subject: [PATCH 25/47] Fixed ambiguous container matching --- .../kotlin/com/lambda/config/groups/BuildConfig.kt | 1 - .../kotlin/com/lambda/config/groups/BuildSettings.kt | 1 - .../lambda/interaction/material/MaterialContainer.kt | 12 ++++++------ .../material/container/MainHandContainer.kt | 2 +- .../main/kotlin/com/lambda/task/tasks/BreakBlock.kt | 2 -- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 4eb840970..d13ca2ff6 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -8,5 +8,4 @@ interface BuildConfig { val breakInstantAtOnce: Boolean val rotateForBreak: Boolean val swingHand: Boolean - val particlesOnBreak: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 447f02d72..c3cf399bb 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -13,5 +13,4 @@ class BuildSettings( override val breakInstantAtOnce by c.setting("Break Instant At Once", true, "Break all instant blocks at once", vis) override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", vis) override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) - override val particlesOnBreak by c.setting("Particles On Break", true, "Show particles when breaking blocks", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 29a1156e6..abf65c2d9 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -63,17 +63,17 @@ abstract class MaterialContainer( } } ?: deposit(selection) - open fun StackSelection.matchingStacks() = - filterStacks(stacks) + open fun stacksMatching(selection: StackSelection) = + selection.filterStacks(stacks) - open fun matchingStacks(selection: (ItemStack) -> Boolean) = - selection.select().matchingStacks() + open fun stacksMatching(selection: (ItemStack) -> Boolean) = + stacksMatching(selection.select()) open fun available(selection: StackSelection) = - selection.matchingStacks().count + stacksMatching(selection).count open fun spaceLeft(selection: StackSelection) = - selection.matchingStacks().spaceLeft + stacks.empty * selection.stackSize + stacksMatching(selection).spaceLeft + stacks.empty * selection.stackSize fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { val amount = available(selection) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index e425c4a97..fd31eb194 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -20,7 +20,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromMainHand") override fun deposit(selection: StackSelection) = buildTask("DepositToMainHand") { - selection.matchingStacks().firstOrNull()?.let { stack -> + InventoryContainer.stacksMatching(selection).firstOrNull()?.let { stack -> if (ItemStack.areEqual(stack, player.mainHandStack)) { return@buildTask } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 500942e1d..e1677a265 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -26,7 +26,6 @@ class BreakBlock @Ta5kBuilder constructor( private val collectDrop: Boolean = TaskFlow.build.collectDrops, private val rotate: Boolean = TaskFlow.build.rotateForBreak, private val swingHand: Boolean = TaskFlow.build.swingHand, - private val particles: Boolean = TaskFlow.build.particlesOnBreak, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null @@ -76,7 +75,6 @@ class BreakBlock @Ta5kBuilder constructor( private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { - if (particles) mc.particleManager.addBlockBreakingParticles(blockPos, side) if (swingHand) player.swingHand(ctx.hand) } } From 8a6e8fd23d32ea6f3d662d83267255256ae87715 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 3 Jun 2024 23:04:37 +0200 Subject: [PATCH 26/47] Build simulator and transfer command --- .../brigadier/argument/ItemArguments.kt | 106 +++++ .../com/lambda/command/LambdaCommand.kt | 7 + .../command/commands/TransferCommand.kt | 72 ++- .../construction/result/BreakResult.kt | 14 +- .../construction/result/BuildResult.kt | 30 +- .../construction/result/PlaceResult.kt | 21 +- .../interaction/construction/result/Rank.kt | 4 +- .../construction/simulation/BuildSimulator.kt | 415 ++++++++++++++++++ .../construction/verify/TargetState.kt | 4 +- .../interaction/material/ContainerManager.kt | 7 +- .../interaction/material/MaterialContainer.kt | 7 +- .../interaction/material/StackSelection.kt | 6 +- .../material/container/ChestContainer.kt | 2 + .../material/container/CreativeContainer.kt | 1 + .../material/container/EnderChestContainer.kt | 1 + .../material/container/HotbarContainer.kt | 3 +- .../material/container/InventoryContainer.kt | 1 + .../material/container/MainHandContainer.kt | 1 + .../material/container/OffHandContainer.kt | 36 +- .../material/container/ShulkerBoxContainer.kt | 8 +- .../material/container/StashContainer.kt | 2 + .../module/modules/player/HighwayTools.kt | 3 + .../src/main/kotlin/com/lambda/task/Task.kt | 20 +- .../com/lambda/task/tasks/BuildStructure.kt | 355 +-------------- .../kotlin/com/lambda/task/tasks/GoalTask.kt | 4 + .../com/lambda/task/tasks/PlaceBlock.kt | 5 +- .../com/lambda/task/tasks/PlaceContainer.kt | 36 ++ .../kotlin/com/lambda/util/DebugInfoHud.kt | 3 + 28 files changed, 785 insertions(+), 389 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt diff --git a/common/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt b/common/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt new file mode 100644 index 000000000..b79603703 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2023 The Quilt Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Preserve binary compatibility when moving extensions between files + */ +@file:JvmMultifileClass +@file:JvmName("ArgumentsKt") + +package com.lambda.brigadier.argument + + +import com.lambda.brigadier.* +import com.lambda.brigadier.assumeSourceNotUsed +import net.minecraft.command.CommandRegistryAccess +import net.minecraft.command.argument.ItemPredicateArgumentType +import net.minecraft.command.argument.ItemSlotArgumentType +import net.minecraft.command.argument.ItemStackArgument +import net.minecraft.command.argument.ItemStackArgumentType +import net.minecraft.item.ItemStack +import java.util.function.Predicate + +/** + * Reads the [ItemStack] predicate value from the + * argument in the receiver [ArgumentReader]. + * + * @see ItemPredicateArgumentType.getItemPredicate + */ +@JvmName("valueItemPredicateArg") +@BrigadierDsl +fun DefaultArgumentReader.value(): Predicate { + return ItemPredicateArgumentType.getItemStackPredicate(context.assumeSourceNotUsed(), name) +} + +/** + * Reads the integer value from the + * argument in the receiver [ArgumentReader]. + * + * @see ItemSlotArgumentType.getItemSlot + */ +@JvmName("valueItemSlotArg") +@BrigadierDsl +fun DefaultArgumentReader.value(): Int { + return ItemSlotArgumentType.getItemSlot(context.assumeSourceNotUsed(), name) +} + +/** + * Reads the [ItemStackArgument] value from the + * argument in the receiver [ArgumentReader]. + * + * @see ItemStackArgumentType.getItemStackArgument + */ +@JvmName("valueItemStackArg") +@BrigadierDsl +fun DefaultArgumentReader.value(): ItemStackArgument { + return ItemStackArgumentType.getItemStackArgument(context, name) +} + +/** + * Creates an item predicate argument with [name] as the parameter name. + * + * @param context The command build context + */ +@BrigadierDsl +fun itemPredicate( + name: String, + context: CommandRegistryAccess +): DefaultArgumentConstructor { + return argument(name, ItemPredicateArgumentType.itemPredicate(context)) +} + +/** + * Creates an item slot argument with [name] as the parameter name. + */ +@BrigadierDsl +fun itemSlot( + name: String +): DefaultArgumentConstructor { + return argument(name, ItemSlotArgumentType.itemSlot()) +} + +/** + * Creates an item stack argument with [name] as the parameter name. + * + * @param context The command build context + */ +@BrigadierDsl +fun itemStack( + name: String, + context: CommandRegistryAccess +): DefaultArgumentConstructor { + return argument(name, ItemStackArgumentType.itemStack(context)) +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt b/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt index 000aa4a74..1daabcd88 100644 --- a/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt @@ -4,7 +4,10 @@ import com.lambda.command.CommandManager.dispatcher import com.lambda.util.Nameable import com.lambda.util.primitives.extension.CommandBuilder import com.mojang.brigadier.builder.LiteralArgumentBuilder +import net.minecraft.command.CommandRegistryAccess import net.minecraft.command.CommandSource +import net.minecraft.registry.BuiltinRegistries +import net.minecraft.server.command.CommandManager abstract class LambdaCommand( final override val name: String, @@ -12,6 +15,10 @@ abstract class LambdaCommand( val usage: String = "", val description: String = "", ) : Nameable { + val registry: CommandRegistryAccess by lazy { + CommandManager.createRegistryAccess(BuiltinRegistries.createWrapperLookup()) + } + // ToDo: Include usage and description in the help command init { (listOf(name) + aliases).forEach { diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 0cb5a2073..d4a6eebd6 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -1,12 +1,21 @@ package com.lambda.command.commands -import com.lambda.brigadier.execute +import com.lambda.brigadier.CommandResult +import com.lambda.brigadier.argument.integer +import com.lambda.brigadier.argument.itemStack +import com.lambda.brigadier.argument.string +import com.lambda.brigadier.argument.value +import com.lambda.brigadier.executeWithResult +import com.lambda.brigadier.required import com.lambda.command.LambdaCommand +import com.lambda.interaction.material.ContainerManager +import com.lambda.interaction.material.ContainerManager.containerMatchSelection +import com.lambda.interaction.material.ContainerManager.findContainerWithSelection import com.lambda.interaction.material.ContainerManager.transfer -import com.lambda.interaction.material.StackSelection.Companion.select -import com.lambda.interaction.material.container.MainHandContainer +import com.lambda.interaction.material.StackSelection.Companion.selectStack +import com.lambda.interaction.material.transfer.TransferResult +import com.lambda.util.Communication.info import com.lambda.util.primitives.extension.CommandBuilder -import net.minecraft.item.Items object TransferCommand : LambdaCommand( name = "transfer", @@ -14,8 +23,59 @@ object TransferCommand : LambdaCommand( description = "Transfer items to a container" ) { override fun CommandBuilder.create() { - execute { - Items.OBSIDIAN.select().transfer(MainHandContainer)?.solve?.start(null) + required(itemStack("stack", registry)) { stack -> + required(integer("amount")) { amount -> + required(string("from")) { from -> + suggests { ctx, builder -> + val selection = selectStack(amount(ctx).value()) { + isItem(stack(ctx).value().item) + } + containerMatchSelection(selection).forEach { + builder.suggest("\"${it.name} with ${it.available(selection)}\"") + } + builder.buildFuture() + } + required(string("to")) { to -> + suggests { ctx, builder -> + val selection = selectStack(amount(ctx).value()) { + isItem(stack(ctx).value().item) + } + ContainerManager.container().forEach { + builder.suggest("\"${it.name} with space left ${it.spaceLeft(selection)}\"") + } + builder.buildFuture() + } + executeWithResult { + val selection = selectStack(amount().value()) { + isItem(stack().value().item) + } + val fromContainer = ContainerManager.container().find { + it.name == from().value().split(" with ").firstOrNull() + } ?: return@executeWithResult CommandResult.failure("From container not found") + + val toContainer = ContainerManager.container().find { + it.name == to().value().split(" with ").firstOrNull() + } ?: return@executeWithResult CommandResult.failure("To container not found") + + when (val result = fromContainer.transfer(selection, toContainer)) { + is TransferResult.Success -> { + info("Transferring $selection from ${fromContainer.name} to ${toContainer.name}") + result.solve.start(null) + return@executeWithResult CommandResult.success() + } + is TransferResult.MissingItems -> { + return@executeWithResult CommandResult.failure("Missing items: ${result.missing}") + } + is TransferResult.NoSpace -> { + return@executeWithResult CommandResult.failure("No space in ${toContainer.name}") + } + } + + return@executeWithResult CommandResult.success() + } + } + } + } } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 61b67da63..b86e63bca 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -1,16 +1,21 @@ package com.lambda.interaction.construction.result +import baritone.api.pathing.goals.GoalBlock +import baritone.api.pathing.goals.GoalInverted import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.material.ContainerManager.findBestAvailableTool import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MainHandContainer +import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BreakBlock.Companion.breakBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box import net.minecraft.util.math.Direction sealed class BreakResult : BuildResult() { @@ -112,7 +117,14 @@ sealed class BreakResult : BuildResult() { data class PlayerOnTop( override val blockPos: BlockPos, val blockState: BlockState - ) : BreakResult() { + ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_PLAYER_ON_TOP + + override val resolve = + moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { + val pBox = player.boundingBox + val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) + world.findSupportingBlockPos(player, aabb).orElse(null) != blockPos + } } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 1629c69b8..466c060d5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,6 +1,6 @@ package com.lambda.interaction.construction.result -import baritone.process.BuilderProcess.GoalAdjacent +import baritone.process.BuilderProcess.GoalPlace import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select @@ -12,6 +12,7 @@ import com.lambda.task.tasks.GoalTask.Companion.moveToGoal import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded import net.minecraft.block.BlockState import net.minecraft.item.Item +import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d @@ -85,7 +86,7 @@ abstract class BuildResult : ComparableResult { data class OutOfWorld( override val blockPos: BlockPos ) : BuildResult() { - override val rank = Rank.BREAK_OUT_OF_WORLD + override val rank = Rank.OUT_OF_WORLD } /** @@ -113,7 +114,7 @@ abstract class BuildResult : ComparableResult { ) : Resolvable, BuildResult() { override val rank = Rank.NOT_VISIBLE - override val resolve = moveToGoal(GoalAdjacent(hitPos, blockPos, true)) + override val resolve = moveToGoal(GoalPlace(blockPos)) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -145,6 +146,29 @@ abstract class BuildResult : ComparableResult { } } + /** + * The Player has the wrong item stack selected. + * @param blockPos The position of the block that needs a different tool. + * @param neededStack The best tool for the block state. + */ + data class WrongStack( + override val blockPos: BlockPos, + val context: BuildContext, + val neededStack: ItemStack + ) : Resolvable, BuildResult() { + override val rank = Rank.WRONG_ITEM + + override val resolve: Task<*> = + neededStack.select().transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error + + override fun compareTo(other: ComparableResult): Int { + return when (other) { + is WrongItem -> context.compareTo(other.context) + else -> super.compareTo(other) + } + } + } + /** * Represents a break out of reach. * @param blockPos The position of the block that is out of reach. diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index f916560ff..8ee4a5e19 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -1,13 +1,19 @@ package com.lambda.interaction.construction.result +import baritone.api.pathing.goals.GoalBlock +import baritone.api.pathing.goals.GoalInverted import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.breakBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToGoal +import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil import com.lambda.task.tasks.PlaceBlock.Companion.placeBlock import net.minecraft.block.BlockState import net.minecraft.item.ItemPlacementContext import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.shape.VoxelShape /** * [PlaceResult] represents the result of a placement simulation. @@ -51,6 +57,16 @@ sealed class PlaceResult : BuildResult() { override val rank = Rank.PLACE_NO_INTEGRITY } + data class BlockedByPlayer( + override val blockPos: BlockPos + ) : Resolvable, PlaceResult() { + override val rank = Rank.PLACE_BLOCKED_BY_PLAYER + + override val resolve = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { + !world.canCollide(player, Box(blockPos)) + } + } + /** * The placement configuration cannot replace the block at the target position. * @param simulated The simulated placement configuration. @@ -61,7 +77,10 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE - override val resolve = breakBlock(simulated.blockPos) +// override val resolve = breakBlock(simulated.blockPos) + override val resolve = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { + !world.canCollide(player, Box(blockPos)) + } } /** diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt index 5325d8494..b20cae5f2 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -6,11 +6,11 @@ enum class Rank { PLACE_SUCCESS, WRONG_ITEM, BREAK_ITEM_CANT_MINE, + PLACE_BLOCKED_BY_PLAYER, NOT_VISIBLE, OUT_OF_REACH, BREAK_NOT_EXPOSED, - BREAK_OUT_OF_WORLD, - PLACE_OUT_OF_WORLD, + OUT_OF_WORLD, CHUNK_NOT_LOADED, PLACE_NO_INTEGRITY, PLACE_CANT_REPLACE, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt new file mode 100644 index 000000000..716906ede --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -0,0 +1,415 @@ +package com.lambda.interaction.construction.simulation + +import com.lambda.context.SafeContext +import com.lambda.interaction.RotationManager +import com.lambda.interaction.construction.Blueprint +import com.lambda.interaction.construction.context.BreakContext +import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.construction.result.BreakResult +import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.PlaceResult +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.interaction.material.ContainerManager.findBestAvailableTool +import com.lambda.interaction.rotation.Rotation.Companion.rotationTo +import com.lambda.interaction.rotation.RotationContext +import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter +import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces +import com.lambda.module.modules.client.TaskFlow +import com.lambda.threading.runSafe +import com.lambda.util.BlockUtils +import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.instantBreakable +import com.lambda.util.BlockUtils.vecOf +import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.math.VecUtils.distSq +import com.lambda.util.world.raycast.RayCastUtils.blockResult +import net.minecraft.block.Block +import net.minecraft.block.OperatorBlock +import net.minecraft.block.pattern.CachedBlockPosition +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemUsageContext +import net.minecraft.registry.RegistryKeys +import net.minecraft.util.Hand +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.hit.HitResult +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Box +import net.minecraft.util.math.Direction +import net.minecraft.util.math.Vec3d +import net.minecraft.util.shape.VoxelShape +import kotlin.math.pow +import kotlin.time.measureTime + +object BuildSimulator { + fun Blueprint.simulate(eye: Vec3d) = + runSafe { + structure.entries.flatMap { (pos, target) -> + checkRequirements(pos, target)?.let { + return@flatMap setOf(it) + } + checkPlaceResults(pos, target, eye).let { + if (it.isEmpty()) return@let + return@flatMap it + } + checkBreakResults(pos, eye).let { + if (it.isEmpty()) return@let + return@flatMap it + } + emptySet() + } + } ?: emptySet() + + private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? { + /* the chunk is not loaded */ + if (!world.isChunkLoaded(pos)) { + return BuildResult.ChunkNotLoaded(pos) + } + + val state = pos.blockState(world) + + /* block is already in the correct state */ + if (target.matches(state, pos, world)) { + return BuildResult.Done(pos) + } + + /* block should be ignored */ + if (state.block in TaskFlow.ignoredBlocks) { + return BuildResult.Ignored(pos) + } + + /* the player is in the wrong game mode to alter the block state */ + if (player.isBlockBreakingRestricted(world, pos, interaction.currentGameMode)) { + return BuildResult.Restricted(pos) + } + + /* the player has no permissions to alter the block state */ + if (state.block is OperatorBlock && !player.isCreativeLevelTwoOp) { + return BuildResult.NoPermission(pos, state) + } + + /* block is outside the world so it cant be altered */ + if (!world.worldBorder.contains(pos) || world.isOutOfHeightLimit(pos)) { + return BuildResult.OutOfWorld(pos) + } + + /* block is unbreakable, so it cant be broken or replaced */ + if (state.getHardness(world, pos) < 0) { + return BuildResult.Unbreakable(pos, state) + } + + return null + } + + private fun SafeContext.checkPlaceResults( + pos: BlockPos, + target: TargetState, + eye: Vec3d + ): Set { + val acc = mutableSetOf() + + if (target is TargetState.Air || !pos.blockState(world).isReplaceable) return acc + + val interact = TaskFlow.interact + val rotation = TaskFlow.rotation + + Direction.entries.forEach { neighbor -> + val hitPos = pos.offset(neighbor) + val hitSide = neighbor.opposite + + val voxelShape = hitPos.blockState(world).getOutlineShape(world, hitPos) + if (voxelShape.isEmpty) return@forEach + + val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } + val verify: HitResult.() -> Boolean = { + blockResult?.blockPos == hitPos && blockResult?.side == hitSide + } + + val validHits = mutableMapOf() + val reachSq = interact.reach.pow(2) + + boxes.forEach { box -> + val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 + scanVisibleSurfaces(eye, box, setOf(hitSide), res) { side, vec -> + if (eye distSq vec > reachSq) { +// acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) + return@scanVisibleSurfaces + } + + validHits[vec] = if (TaskFlow.interact.useRayCast) { + val cast = eye.rotationTo(vec) + .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + cast + } else { + BlockHitResult( + vec, + side, + hitPos, + false + ) + } + + } + } + + if (validHits.isEmpty()) { + acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide)))) + return@forEach + } + + validHits.keys.mostCenter?.let { optimum -> + validHits.minByOrNull { optimum distSq it.key }?.let { closest -> + val optimumRotation = eye.rotationTo(closest.key) + RotationContext(optimumRotation, rotation, closest.value, verify) + } + }?.let { rotation -> + val optimalStack = target.getStack(world, pos) + + val usageContext = ItemUsageContext( + player, + Hand.MAIN_HAND, // ToDo: notice that the hand may have a different item stack and simulation will be wrong + rotation.hitResult?.blockResult, + ) + val cachePos = CachedBlockPosition( + usageContext.world, + usageContext.blockPos, + false + ) + val canBePlacedOn = optimalStack.canPlaceOn( + usageContext.world.registryManager.get(RegistryKeys.BLOCK), + cachePos, + ) + if (!player.abilities.allowModifyWorld && !canBePlacedOn) { + acc.add(PlaceResult.IllegalUsage(pos)) + return@forEach + } + + var context = ItemPlacementContext(usageContext) + + if (!optimalStack.item.isEnabled(world.enabledFeatures)) { + acc.add(PlaceResult.BlockFeatureDisabled(pos, optimalStack)) + return@forEach + } + + if (!context.canPlace()) { + acc.add(PlaceResult.CantReplace(pos, context)) + return@forEach + } + + val blockItem = optimalStack.item as? BlockItem ?: run { + acc.add(PlaceResult.NotItemBlock(pos, optimalStack)) + return@forEach + } + + val checked = blockItem.getPlacementContext(context) + if (checked == null) { + acc.add(PlaceResult.ScaffoldExceeded(pos, context)) + return@forEach + } else { + context = checked + } + + val resultState = blockItem.getPlacementState(context) ?: run { + acc.add(PlaceResult.CantReplace(pos, context)) + return@forEach + } + + if (!target.matches(resultState, pos, world)) { + acc.add(PlaceResult.NoIntegrity(pos, resultState, context)) + return@forEach + } + + val blockHit = rotation.hitResult?.blockResult ?: return@forEach + val hitBlock = blockHit.blockPos.blockState(world).block + val shouldSneak = hitBlock in BlockUtils.interactionBlacklist + + val placeContext = PlaceContext( + eye, + blockHit, + rotation, + eye.distanceTo(blockHit.pos), + resultState, + blockHit.blockPos.blockState(world), + target, + Hand.MAIN_HAND, + shouldSneak, + false + ) + + /* player is colliding with the place box */ + if (world.canCollide(player, Box(pos))) { + acc.add(PlaceResult.BlockedByPlayer(pos)) + return@forEach + } + + val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) + if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { + acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack)) + return@forEach + } + + if (optimalStack.item != currentHandStack.item) { + acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item)) + return@forEach + } + + acc.add(PlaceResult.Success(pos, placeContext)) + } + } + + return acc + } + + private fun SafeContext.checkBreakResults(pos: BlockPos, eye: Vec3d): Set { + val acc = mutableSetOf() + val state = pos.blockState(world) + + /* is a block that will be destroyed by breaking adjacent blocks */ + if (TaskFlow.build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { + acc.add(BuildResult.Ignored(pos)) + return acc + } + + /* player is standing on top of the block */ + val pBox = player.boundingBox + val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) + world.findSupportingBlockPos(player, aabb).orElse(null)?.let { support -> + if (support != pos) return@let + acc.add(BreakResult.PlayerOnTop(pos, state)) + return acc + } + + /* liquid needs to be submerged first to be broken */ + if (!state.fluidState.isEmpty && state.isReplaceable) { + val submerge = checkPlaceResults(pos, TargetState.Solid, eye) + acc.add(BreakResult.Submerge(pos, state, submerge)) + acc.addAll(submerge) + return acc + } + + val adjacentLiquids = Direction.entries.filter { + it != Direction.DOWN && !pos.offset(it).blockState(world).fluidState.isEmpty + } + + /* block has liquids next to it that will leak when broken */ + if (adjacentLiquids.isNotEmpty()) { + acc.add(BreakResult.BlockedByLiquid(pos, state)) + adjacentLiquids.forEach { + val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid, eye) + acc.addAll(submerge) + } + return acc + } + + /* The current selected item cant mine the block */ + Hand.entries.forEach { + val stack = player.getStackInHand(it) + if (stack.isEmpty) return@forEach + if (stack.item.canMine(state, world, pos, player)) return@forEach + acc.add(BreakResult.ItemCantMine(pos, state, stack.item)) + return acc + } + + val interact = TaskFlow.interact + val rotation = TaskFlow.rotation + val currentRotation = RotationManager.currentRotation + val currentCast = currentRotation.rayCast(interact.reach, eye) + + val voxelShape = state.getOutlineShape(world, pos) + voxelShape.getClosestPointTo(eye).ifPresent { + // ToDo: Use closest point of shape + } + val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } + val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } + + /* the player is buried inside the block */ + if (boxes.any { it.contains(eye) }) { + currentCast?.blockResult?.let { blockHit -> + val rotationContext = RotationContext(currentRotation, rotation, currentCast, verify) + val breakContext = BreakContext( + eye, + blockHit, + rotationContext, + state, + player.activeHand, + instantBreakable(state, pos) + ) + acc.add(BreakResult.Success(pos, breakContext)) + return acc + } + } + + val validHits = mutableMapOf() + val reachSq = interact.reach.pow(2) + + boxes.forEach { box -> + val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 + scanVisibleSurfaces(eye, box, emptySet(), res) { side, vec -> + if (eye distSq vec > reachSq) { +// acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) + return@scanVisibleSurfaces + } + + validHits[vec] = if (TaskFlow.interact.useRayCast) { + val cast = eye.rotationTo(vec) + .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces + if (!cast.verify()) return@scanVisibleSurfaces + + cast + } else { + BlockHitResult( + vec, + side, + pos, + false + ) + } + } + } + + validHits.keys.mostCenter?.let { optimum -> + validHits.minByOrNull { optimum distSq it.key }?.let { closest -> + val optimumRotation = eye.rotationTo(closest.key) + RotationContext(optimumRotation, rotation, closest.value, verify) + } + }?.let { bestRotation -> + val blockHit = bestRotation.hitResult?.blockResult ?: return@let + + val breakContext = BreakContext( + eye, + blockHit, + bestRotation, + state, + player.activeHand, + instantBreakable(state, pos) + ) + + /* player has a better tool for the job available */ + findBestAvailableTool(state)?.let { bestTool -> + Hand.entries.firstOrNull { + val stack = player.getStackInHand(it) + stack.item == bestTool + }?.let { hand -> + breakContext.hand = hand + acc.add(BreakResult.Success(pos, breakContext)) + return acc + } ?: run { + acc.add(BuildResult.WrongItem(pos, breakContext, bestTool)) + return acc + } + } + + acc.add(BreakResult.Success(pos, breakContext)) + } + + return acc + } + +// private fun SafeContext.playerFitsIn(BlockPos pos) { +// val pBox = player.boundingBox +// val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) +// return world.findSupportingBlockPos(player, aabb).orElse(null) +// } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 82a107367..85f74ec22 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -42,8 +42,10 @@ sealed class TargetState : StateMatcher { block.getPickStack(world, pos, block.defaultState) } data class Stack(val itemStack: ItemStack) : TargetState() { + val copy = itemStack.copy() + override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = - state.block == itemStack.item.block + state.block == copy.item.block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = itemStack diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index 021b1af10..d45d6a1e8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -72,7 +72,7 @@ object ContainerManager : Loadable { fun container() = container.flatMap { setOf(it) + it.shulkerContainer - } + }.sorted() fun StackSelection.transfer(destination: MaterialContainer) = findContainerWithSelection(this)?.transfer(this, destination) @@ -86,6 +86,11 @@ object ContainerManager : Loadable { ): MaterialContainer? = container().find { it.available(selection) >= selection.count } + fun containerMatchSelection( + selection: StackSelection + ): Set = + container().filter { it.available(selection) >= selection.count }.toSet() + fun findContainerWithSelection( selectionBuilder: StackSelection.() -> Unit ): MaterialContainer? { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index abf65c2d9..0f6c63fa0 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -1,9 +1,11 @@ package com.lambda.interaction.material +import com.lambda.Lambda.LOG import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ShulkerBoxContainer import com.lambda.interaction.material.transfer.TransferResult import com.lambda.task.Task +import com.lambda.util.Nameable import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty import com.lambda.util.item.ItemStackUtils.shulkerBoxContents @@ -14,7 +16,7 @@ import net.minecraft.item.ItemStack // ToDo: Make jsonable to persistently store them abstract class MaterialContainer( private val rank: Rank -) : Comparable { +) : Nameable, Comparable { abstract var stacks: List val shulkerContainer get() = @@ -85,11 +87,12 @@ abstract class MaterialContainer( // if (space == 0) { // return TransferResult.NoSpace // } - +// // val transferAmount = minOf(amount, space) // selection.selector = { true } // selection.count = transferAmount + LOG.info("Transferring $selection from $name to ${destination.name}") return TransferResult.Success( doWithdrawal(selection).onSuccess { withdraw, _ -> destination.doDeposit(selection).start(withdraw) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt index afbfd6269..857bbbf71 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/StackSelection.kt @@ -43,10 +43,8 @@ class StackSelection { } val filterStacks: (List) -> List - get() = { stacks -> - // ToDo: count should represent item count not stack count. - // Would need partial selections with permutations - stacks.filter(filterStack).take(count) + get() = { + it.filter(filterStack) } val filterSlots: (List) -> List diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt index f19dbdbc2..4eecc68f5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -17,6 +17,8 @@ data class ChestContainer( override var stacks: List, val blockPos: BlockPos, ) : MaterialContainer(Rank.CHEST) { + override val name = "Chest at ${blockPos.toShortString()}" + override fun prepare() = moveIntoEntityRange(blockPos).onSuccess { _, _ -> // when { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt index e67d4f688..5db705067 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt @@ -9,6 +9,7 @@ import net.minecraft.item.ItemStack data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { override var stacks = emptyList() + override val name = "Creative" override fun available(selection: StackSelection): Int = if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index 730ab4a26..54d0de77e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -12,6 +12,7 @@ import net.minecraft.util.math.BlockPos object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { override var stacks = emptyList() + override val name = "EnderChest" private var placePos: BlockPos? = null override fun prepare(): Task<*> { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt index 504169499..55b727178 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -14,8 +14,9 @@ object HotbarContainer : MaterialContainer(Rank.HOTBAR) { override var stacks: List get() = mc.player?.hotbar ?: emptyList() set(_) {} + override val name = "Hotbar" - override fun withdraw(selection: StackSelection) = emptyTask("HotbarWithdraw") + override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromHotbar") override fun deposit(selection: StackSelection): Task<*> { val handledScreen = mc.currentScreen as? ScreenHandlerProvider<*> ?: return emptyTask() diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt index adf41449c..2eba7bf2a 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -11,6 +11,7 @@ object InventoryContainer : MaterialContainer(Rank.INVENTORY) { override var stacks: List get() = mc.player?.combined ?: emptyList() set(_) {} + override val name = "Inventory" override fun withdraw(selection: StackSelection) = Task.emptyTask() diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index fd31eb194..7733225a3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -16,6 +16,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override var stacks: List get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() set(_) {} + override val name = "MainHand" override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromMainHand") diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt index 7a4257551..984a117b7 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt @@ -4,18 +4,44 @@ import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.buildTask +import com.lambda.task.Task.Companion.emptyTask +import com.lambda.util.player.SlotUtils.combined +import com.lambda.util.player.SlotUtils.hotbar import net.minecraft.item.ItemStack +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction object OffHandContainer : MaterialContainer(Rank.OFF_HAND) { override var stacks: List get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList() set(_) {} + override val name = "OffHand" - override fun withdraw(selection: StackSelection): Task<*> { - TODO("Not yet implemented") - } + override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromOffHand") + + override fun deposit(selection: StackSelection) = buildTask("DepositToOffHand") { + InventoryContainer.stacksMatching(selection).firstOrNull()?.let { stack -> + if (ItemStack.areEqual(stack, player.offHandStack)) { + return@buildTask + } + + if (stack in player.hotbar) { + player.inventory.selectedSlot = player.hotbar.indexOf(stack) + } else { + interaction.pickFromInventory(player.combined.indexOf(stack)) + } - override fun deposit(selection: StackSelection): Task<*> { - TODO("Not yet implemented") + if (ItemStack.areEqual(stack, player.mainHandStack)) { + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN, + ), + ) + } + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt index 48fa3ba59..a26618f3f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -21,12 +21,16 @@ data class ShulkerBoxContainer( private var openScreen: ShulkerBoxScreenHandler? = null private var placePosition: BlockPos? = null + override val name = "${shulkerStack.name.string} in slot $slotInContainer in ${containedIn.name}" + + private val slotInContainer: Int get() = containedIn.stacks.indexOf(shulkerStack) + override fun prepare() = - placeContainer(shulkerStack).onSuccess { _, placePos -> + placeContainer(shulkerStack).onSuccess { place, placePos -> placePosition = placePos openContainer(placePos).onSuccess { _, screen -> openScreen = screen - } + }.start(place) } override fun withdraw(selection: StackSelection): Task<*> { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt index 5e2bb22c2..463a5e10c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/StashContainer.kt @@ -3,6 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.util.math.VecUtils.blockPos import net.minecraft.item.ItemStack import net.minecraft.util.math.Box @@ -13,6 +14,7 @@ data class StashContainer( override var stacks: List get() = chests.flatMap { it.stacks } set(_) {} + override val name = "Stash at ${pos.center.blockPos.toShortString()}" override fun withdraw(selection: StackSelection): Task<*> { TODO("Not yet implemented") diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 4acbcf4f8..e671cee33 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -79,6 +79,9 @@ object HighwayTools : Module( disable() } } + onFailure { _, _ -> + disable() + } start(null) } } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index c303e27aa..68f21a600 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -150,12 +150,12 @@ abstract class Task( val owner = parent ?: RootTask owner.subTasks.add(this) + LOG.info("${owner.identifier} started $identifier") + this.parent = owner if (pauseParent && owner.isActivated && !owner.isRoot) { LOG.info("$identifier deactivating parent ${owner.identifier}") owner.deactivate() } - LOG.info("${owner.identifier} started $identifier") - this.parent = owner activate() runSafe { onStart() } @@ -261,14 +261,14 @@ abstract class Task( private fun notifyParent() { parent?.let { par -> - if (par.isCompleted) { - LOG.info("$identifier notified parent ${par.identifier}") - par.notifyParent() - return@let - } - - LOG.info("$identifier reactivated parent ${par.identifier}") - par.activate() +// if (par.isCompleted) { +// LOG.info("$identifier notified parent ${par.identifier}") +// par.notifyParent() +// return@let +// } + +// LOG.info("$identifier reactivated parent ${par.identifier}") +// par.activate() } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 4a5c2d8e5..9aaf20368 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,5 +1,6 @@ package com.lambda.task.tasks +import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -14,6 +15,8 @@ import com.lambda.interaction.construction.result.BreakResult import com.lambda.interaction.construction.result.BuildResult import com.lambda.interaction.construction.result.PlaceResult import com.lambda.interaction.construction.result.Resolvable +import com.lambda.interaction.construction.simulation.BuildSimulator +import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.ContainerManager.findBestAvailableTool import com.lambda.interaction.rotation.Rotation.Companion.rotationTo @@ -64,23 +67,7 @@ class BuildStructure @Ta5kBuilder constructor( return@listener } - val results = blueprint.structure.entries.fold(mutableSetOf()) { acc, (pos, target) -> - checkRequirements(pos, target)?.let { - acc.add(it) - return@fold acc - } - checkPlaceResults(pos, target).let { - if (it.isEmpty()) return@let - acc.addAll(it) - return@fold acc - } - checkBreakResults(pos).let { - if (it.isEmpty()) return@let - acc.addAll(it) - return@fold acc - } - acc - } + val results = blueprint.simulate(player.getCameraPosVec(mc.tickDelta)) val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } @@ -108,12 +95,13 @@ class BuildStructure @Ta5kBuilder constructor( } return@listener } - if (lastTask?.isCompleted == false) return@listener +// if (lastTask?.isCompleted == false) return@listener lastTask = result.resolve cancelSubTasks() - result.resolve.start(this@BuildStructure, false) + LOG.info("Resolving: $result") + result.resolve.start(this@BuildStructure) } } } @@ -126,335 +114,6 @@ class BuildStructure @Ta5kBuilder constructor( return } - private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? { - /* the chunk is not loaded */ - if (!world.isChunkLoaded(pos)) { - return BuildResult.ChunkNotLoaded(pos) - } - - val state = pos.blockState(world) - - /* block is already in the correct state */ - if (target.matches(state, pos, world)) { - return BuildResult.Done(pos) - } - - /* block should be ignored */ - if (state.block in TaskFlow.ignoredBlocks) { - return BuildResult.Ignored(pos) - } - - /* the player is in the wrong game mode to alter the block state */ - if (player.isBlockBreakingRestricted(world, pos, interaction.currentGameMode)) { - return BuildResult.Restricted(pos) - } - - /* the player has no permissions to alter the block state */ - if (state.block is OperatorBlock && !player.isCreativeLevelTwoOp) { - return BuildResult.NoPermission(pos, state) - } - - /* block is outside the world so it cant be altered */ - if (!world.worldBorder.contains(pos) || world.isOutOfHeightLimit(pos)) { - return BuildResult.OutOfWorld(pos) - } - - /* block is unbreakable, so it cant be broken or replaced */ - if (state.getHardness(world, pos) < 0) { - return BuildResult.Unbreakable(pos, state) - } - - return null - } - - private fun SafeContext.checkPlaceResults(pos: BlockPos, target: TargetState): Set { - val acc = mutableSetOf() - - if (target is TargetState.Air || !pos.blockState(world).isReplaceable) return acc - - val interact = TaskFlow.interact - val rotation = TaskFlow.rotation - - Direction.entries.forEach { neighbor -> - val hitPos = pos.offset(neighbor) - val hitSide = neighbor.opposite - - val voxelShape = hitPos.blockState(world).getOutlineShape(world, hitPos) - val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } - val verify: HitResult.() -> Boolean = { - blockResult?.blockPos == hitPos && blockResult?.side == hitSide - } - - val eye = player.getCameraPosVec(mc.tickDelta) - val validHits = mutableMapOf() - val reachSq = interact.reach.pow(2) - - boxes.forEach { box -> - val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 - scanVisibleSurfaces(eye, box, setOf(hitSide), res) { side, vec -> - if (eye distSq vec > reachSq) { - acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) - return@scanVisibleSurfaces - } - - validHits[vec] = if (TaskFlow.interact.useRayCast) { - val cast = eye.rotationTo(vec) - .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces - if (!cast.verify()) return@scanVisibleSurfaces - - cast - } else { - BlockHitResult( - vec, - side, - hitPos, - false - ) - } - - } - } - - if (validHits.isEmpty()) { - acc.add(BuildResult.NotVisible(pos, hitPos, hitSide, eye.distanceTo(hitPos.vecOf(hitSide)))) - return@forEach - } - - validHits.keys.mostCenter?.let { optimum -> - validHits.minByOrNull { optimum distSq it.key }?.let { closest -> - val optimumRotation = eye.rotationTo(closest.key) - RotationContext(optimumRotation, rotation, closest.value, verify) - } - }?.let { rotation -> - val optimalStack = target.getStack(world, pos) - - val usageContext = ItemUsageContext( - player, - Hand.MAIN_HAND, // ToDo: notice that the hand may have a different item stack and simulation will be wrong - rotation.hitResult?.blockResult, - ) - val cachePos = CachedBlockPosition( - usageContext.world, - usageContext.blockPos, - false - ) - val canBePlacedOn = optimalStack.canPlaceOn( - usageContext.world.registryManager.get(RegistryKeys.BLOCK), - cachePos, - ) - if (!player.abilities.allowModifyWorld && !canBePlacedOn) { - acc.add(PlaceResult.IllegalUsage(pos)) - return@forEach - } - - var context = ItemPlacementContext(usageContext) - - if (!optimalStack.item.isEnabled(world.enabledFeatures)) { - acc.add(PlaceResult.BlockFeatureDisabled(pos, optimalStack)) - return@forEach - } - - if (!context.canPlace()) { - acc.add(PlaceResult.CantReplace(pos, context)) - return@forEach - } - - val blockItem = optimalStack.item as? BlockItem ?: run { - acc.add(PlaceResult.NotItemBlock(pos, optimalStack)) - return@forEach - } - - val checked = blockItem.getPlacementContext(context) - if (checked == null) { - acc.add(PlaceResult.ScaffoldExceeded(pos, context)) - return@forEach - } else { - context = checked - } - - val resultState = blockItem.getPlacementState(context) ?: run { - acc.add(PlaceResult.CantReplace(pos, context)) - return@forEach - } - - if (!target.matches(resultState, pos, world)) { - acc.add(PlaceResult.NoIntegrity(pos, resultState, context)) - return@forEach - } - - val blockHit = rotation.hitResult?.blockResult ?: return@forEach - val hitBlock = blockHit.blockPos.blockState(world).block - val shouldSneak = hitBlock in BlockUtils.interactionBlacklist - - val placeContext = PlaceContext( - eye, - blockHit, - rotation, - eye.distanceTo(blockHit.pos), - resultState, - blockHit.blockPos.blockState(world), - target, - Hand.MAIN_HAND, - shouldSneak, - false - ) - - if (optimalStack.item != player.getStackInHand(placeContext.hand).item) { - acc.add(BuildResult.WrongItem(pos, placeContext, optimalStack.item)) - return@forEach - } - - acc.add(PlaceResult.Success(pos, placeContext)) - } - } - - return acc - } - - private fun SafeContext.checkBreakResults(pos: BlockPos): Set { - val acc = mutableSetOf() - val state = pos.blockState(world) - - /* is a block that will be destroyed by breaking adjacent blocks */ - if (TaskFlow.build.breakWeakBlocks && state.block.hardness == 0f && !state.isAir) { - acc.add(BuildResult.Ignored(pos)) - return acc - } - - /* player is standing on top of the block */ - val pBox = player.boundingBox - val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) - world.findSupportingBlockPos(player, aabb).orElse(null)?.let { support -> - if (support != pos) return@let - acc.add(BreakResult.PlayerOnTop(pos, state)) - return acc - } - - /* liquid needs to be submerged first to be broken */ - if (!state.fluidState.isEmpty && state.isReplaceable) { - val submerge = checkPlaceResults(pos, TargetState.Solid) - acc.add(BreakResult.Submerge(pos, state, submerge)) - acc.addAll(submerge) - return acc - } - - val adjacentLiquids = Direction.entries.filter { - it != Direction.DOWN && !pos.offset(it).blockState(world).fluidState.isEmpty - } - - /* block has liquids next to it that will leak when broken */ - if (adjacentLiquids.isNotEmpty()) { - acc.add(BreakResult.BlockedByLiquid(pos, state)) - adjacentLiquids.forEach { - val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid) - acc.addAll(submerge) - } - return acc - } - - /* The current selected item cant mine the block */ - Hand.entries.forEach { - val stack = player.getStackInHand(it) - if (stack.isEmpty) return@forEach - if (stack.item.canMine(state, world, pos, player)) return@forEach - acc.add(BreakResult.ItemCantMine(pos, state, stack.item)) - return acc - } - - val eye = player.getCameraPosVec(mc.tickDelta) - - val interact = TaskFlow.interact - val rotation = TaskFlow.rotation - val currentRotation = RotationManager.currentRotation - val currentCast = currentRotation.rayCast(interact.reach, eye) - - val voxelShape = state.getOutlineShape(world, pos) - val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } - val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } - - /* the player is buried inside the block */ - if (boxes.any { it.contains(eye) }) { - currentCast?.blockResult?.let { blockHit -> - val rotationContext = RotationContext(currentRotation, rotation, currentCast, verify) - val breakContext = BreakContext( - eye, - blockHit, - rotationContext, - state, - player.activeHand, - instantBreakable(state, pos) - ) - acc.add(BreakResult.Success(pos, breakContext)) - return acc - } - } - - val validHits = mutableMapOf() - val reachSq = interact.reach.pow(2) - - boxes.forEach { box -> - val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2 - scanVisibleSurfaces(eye, box, emptySet(), res) { side, vec -> - if (eye distSq vec > reachSq) { - acc.add(BuildResult.OutOfReach(pos, eye, vec, interact.reach, side)) - return@scanVisibleSurfaces - } - - validHits[vec] = if (TaskFlow.interact.useRayCast) { - val cast = eye.rotationTo(vec) - .rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces - if (!cast.verify()) return@scanVisibleSurfaces - - cast - } else { - BlockHitResult( - vec, - side, - pos, - false - ) - } - } - } - - validHits.keys.mostCenter?.let { optimum -> - validHits.minByOrNull { optimum distSq it.key }?.let { closest -> - val optimumRotation = eye.rotationTo(closest.key) - RotationContext(optimumRotation, rotation, closest.value, verify) - } - }?.let { bestRotation -> - val blockHit = bestRotation.hitResult?.blockResult ?: return@let - - val breakContext = BreakContext( - eye, - blockHit, - bestRotation, - state, - player.activeHand, - instantBreakable(state, pos) - ) - - /* player has a better tool for the job available */ - findBestAvailableTool(state)?.let { bestTool -> - Hand.entries.firstOrNull { - val stack = player.getStackInHand(it) - stack.item == bestTool - }?.let { hand -> - breakContext.hand = hand - acc.add(BreakResult.Success(pos, breakContext)) - return acc - } ?: run { - acc.add(BuildResult.WrongItem(pos, breakContext, bestTool)) - return acc - } - } - - acc.add(BreakResult.Success(pos, breakContext)) - } - - return acc - } - companion object { @Ta5kBuilder fun buildStructure( diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index c83d56b7c..6b752b7ae 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -37,6 +37,10 @@ class GoalTask( fun moveToGoal(goal: Goal) = GoalTask(goal) + @Ta5kBuilder + fun moveToGoalUntil(goal: Goal, check: SafeContext.() -> Boolean) = + GoalTask(goal, check) + @Ta5kBuilder fun moveToBlock(blockPos: BlockPos) = GoalTask(GoalBlock(blockPos)) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 93b4f290e..9e5934a61 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -7,6 +7,7 @@ import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.context.PlaceContext import com.lambda.task.Task import com.lambda.util.BlockUtils.blockState +import com.lambda.util.Communication.info import net.minecraft.block.BlockState class PlaceBlock @Ta5kBuilder constructor( @@ -46,7 +47,7 @@ class PlaceBlock @Ta5kBuilder constructor( } private fun SafeContext.placeBlock() { - val preStack = player.getStackInHand(ctx.hand) + val preStack = player.getStackInHand(ctx.hand).copy() val actionResult = interaction.interactBlock( player, @@ -72,7 +73,7 @@ class PlaceBlock @Ta5kBuilder constructor( success(Unit) } } else { - failure("Internal interaction failed with $actionResult") + info("Internal interaction failed with $actionResult") } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index 256054903..dc81426dd 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -1,12 +1,48 @@ package com.lambda.task.tasks +import com.lambda.context.SafeContext +import com.lambda.interaction.construction.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.result.BuildResult +import com.lambda.interaction.construction.result.PlaceResult +import com.lambda.interaction.construction.result.Resolvable +import com.lambda.interaction.construction.simulation.BuildSimulator.simulate +import com.lambda.interaction.construction.verify.TargetState import com.lambda.task.Task +import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.util.BlockUtils.blockPos import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos class PlaceContainer( val stack: ItemStack, ) : Task() { + override fun SafeContext.onStart() { + val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4) +// .filter { world.isAir(it) } + .map { it.blockPos } +// .filter { world.isAir(it) } + .flatMap { + it.blockPos + .toStructure(TargetState.Stack(stack)) + .toBlueprint() + .simulate(player.getCameraPosVec(mc.tickDelta)) + } + +// val res = results.sorted() +// res + + val useful = results.filterIsInstance() + results.filterIsInstance() + useful.minOrNull()?.let { result -> + buildStructure { + result.blockPos.toStructure(TargetState.Stack(stack)).toBlueprint() + }.onSuccess { _, _ -> + success(result.blockPos) + }.start(this@PlaceContainer) + } ?: { + failure("No valid placement found") + } + } companion object { @Ta5kBuilder diff --git a/common/src/main/kotlin/com/lambda/util/DebugInfoHud.kt b/common/src/main/kotlin/com/lambda/util/DebugInfoHud.kt index e5c3360a6..c735a7b4f 100644 --- a/common/src/main/kotlin/com/lambda/util/DebugInfoHud.kt +++ b/common/src/main/kotlin/com/lambda/util/DebugInfoHud.kt @@ -5,6 +5,7 @@ import com.lambda.Lambda.mc import com.lambda.command.CommandRegistry import com.lambda.event.EventFlow import com.lambda.module.ModuleRegistry +import com.lambda.util.Formatting.asString import net.minecraft.util.Formatting import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.hit.EntityHitResult @@ -36,6 +37,8 @@ object DebugInfoHud { null -> add("Crosshair Target: None") } + add("Eye Pos: ${mc.cameraEntity?.getCameraPosVec(mc.tickDelta)?.asString(3)}") + return } } From 9daee66423a43d9a011b878e40d39831835b51e6 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Jun 2024 01:16:51 +0200 Subject: [PATCH 27/47] Bug fixes --- .../command/commands/TransferCommand.kt | 51 +++++++++++++------ .../construction/result/BreakResult.kt | 11 ++-- .../construction/result/BuildResult.kt | 14 ++--- .../construction/result/PlaceResult.kt | 8 +-- .../interaction/material/MaterialContainer.kt | 39 +++++++------- .../material/container/ChestContainer.kt | 1 + .../material/container/EnderChestContainer.kt | 5 +- .../material/container/InventoryContainer.kt | 5 +- .../material/container/MainHandContainer.kt | 2 +- .../material/container/OffHandContainer.kt | 3 +- .../material/container/ShulkerBoxContainer.kt | 11 ++-- .../material/transfer/TransferResult.kt | 16 +++++- .../src/main/kotlin/com/lambda/task/Task.kt | 28 ++++++++-- .../com/lambda/task/tasks/BuildStructure.kt | 6 +-- 14 files changed, 129 insertions(+), 71 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index d4a6eebd6..b37162528 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -1,17 +1,13 @@ package com.lambda.command.commands -import com.lambda.brigadier.CommandResult -import com.lambda.brigadier.argument.integer -import com.lambda.brigadier.argument.itemStack -import com.lambda.brigadier.argument.string -import com.lambda.brigadier.argument.value +import com.lambda.brigadier.CommandResult.Companion.failure +import com.lambda.brigadier.CommandResult.Companion.success +import com.lambda.brigadier.argument.* import com.lambda.brigadier.executeWithResult import com.lambda.brigadier.required import com.lambda.command.LambdaCommand import com.lambda.interaction.material.ContainerManager import com.lambda.interaction.material.ContainerManager.containerMatchSelection -import com.lambda.interaction.material.ContainerManager.findContainerWithSelection -import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.transfer.TransferResult import com.lambda.util.Communication.info @@ -19,9 +15,11 @@ import com.lambda.util.primitives.extension.CommandBuilder object TransferCommand : LambdaCommand( name = "transfer", - usage = "transfer ", - description = "Transfer items to a container" + usage = "transfer ", + description = "Transfer items from anywhere to anywhere", ) { + private var lastTransfer: TransferResult.Success? = null + override fun CommandBuilder.create() { required(itemStack("stack", registry)) { stack -> required(integer("amount")) { amount -> @@ -41,7 +39,8 @@ object TransferCommand : LambdaCommand( isItem(stack(ctx).value().item) } ContainerManager.container().forEach { - builder.suggest("\"${it.name} with space left ${it.spaceLeft(selection)}\"") + val space = it.spaceLeft(selection) + if (space > 0) builder.suggest("\"${it.name} with $space space left\"") } builder.buildFuture() } @@ -51,31 +50,51 @@ object TransferCommand : LambdaCommand( } val fromContainer = ContainerManager.container().find { it.name == from().value().split(" with ").firstOrNull() - } ?: return@executeWithResult CommandResult.failure("From container not found") + } ?: return@executeWithResult failure("From container not found") val toContainer = ContainerManager.container().find { it.name == to().value().split(" with ").firstOrNull() - } ?: return@executeWithResult CommandResult.failure("To container not found") + } ?: return@executeWithResult failure("To container not found") when (val result = fromContainer.transfer(selection, toContainer)) { is TransferResult.Success -> { info("Transferring $selection from ${fromContainer.name} to ${toContainer.name}") + lastTransfer = result result.solve.start(null) - return@executeWithResult CommandResult.success() + return@executeWithResult success() } is TransferResult.MissingItems -> { - return@executeWithResult CommandResult.failure("Missing items: ${result.missing}") + return@executeWithResult failure("Missing items: ${result.missing}") } is TransferResult.NoSpace -> { - return@executeWithResult CommandResult.failure("No space in ${toContainer.name}") + return@executeWithResult failure("No space in ${toContainer.name}") } } - return@executeWithResult CommandResult.success() + return@executeWithResult success() } } } } } + + required(literal("cancel")) { + executeWithResult { + lastTransfer?.solve?.cancel() ?: run { + return@executeWithResult failure("No transfer to cancel") + } + lastTransfer = null + success() + } + } + + required(literal("undo")) { + executeWithResult { + lastTransfer?.undo ?: run { + return@executeWithResult failure("No transfer to undo") + } + success() + } + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index b86e63bca..64b2ba531 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -8,7 +8,6 @@ import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MainHandContainer -import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BreakBlock.Companion.breakBlock import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil @@ -30,7 +29,7 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_SUCCESS - override val resolve = breakBlock(context) + override val resolve get() = breakBlock(context) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -51,7 +50,7 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_NOT_EXPOSED - override val resolve = emptyTask() + override val resolve get() = emptyTask("Block is not exposed to air.") override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -72,13 +71,13 @@ sealed class BreakResult : BuildResult() { val badItem: Item ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_ITEM_CANT_MINE - override val resolve = findBestAvailableTool(blockState) + override val resolve get() = findBestAvailableTool(blockState) ?.select() ?.transfer(MainHandContainer) ?.solve ?: run { selectStack { isItem(badItem).not() - }.transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error + }.transfer(MainHandContainer)?.solve ?: emptyTask("No item found or space") // ToDo: Should throw error } override fun compareTo(other: ComparableResult): Int { @@ -120,7 +119,7 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_PLAYER_ON_TOP - override val resolve = + override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { val pBox = player.boundingBox val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 466c060d5..66a2e263c 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -47,7 +47,7 @@ abstract class BuildResult : ComparableResult { ) : Resolvable, BuildResult() { override val rank = Rank.CHUNK_NOT_LOADED - override val resolve = moveUntilLoaded(blockPos) + override val resolve get() = moveUntilLoaded(blockPos) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -76,7 +76,7 @@ abstract class BuildResult : ComparableResult { override val blockPos: BlockPos, val blockState: BlockState ) : BuildResult() { - override val rank = Rank.BREAK_NO_PERMISSION + override val rank get() = Rank.BREAK_NO_PERMISSION } /** @@ -114,7 +114,7 @@ abstract class BuildResult : ComparableResult { ) : Resolvable, BuildResult() { override val rank = Rank.NOT_VISIBLE - override val resolve = moveToGoal(GoalPlace(blockPos)) + override val resolve get() = moveToGoal(GoalPlace(blockPos)) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -135,8 +135,8 @@ abstract class BuildResult : ComparableResult { ) : Resolvable, BuildResult() { override val rank = Rank.WRONG_ITEM - override val resolve: Task<*> = - neededItem.select().transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error + override val resolve get() = + neededItem.select().transfer(MainHandContainer)?.solve ?: emptyTask("Item ${neededItem.name.string} not found") // ToDo: Should throw error override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -158,7 +158,7 @@ abstract class BuildResult : ComparableResult { ) : Resolvable, BuildResult() { override val rank = Rank.WRONG_ITEM - override val resolve: Task<*> = + override val resolve get() = neededStack.select().transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error override fun compareTo(other: ComparableResult): Int { @@ -190,7 +190,7 @@ abstract class BuildResult : ComparableResult { startVec.distanceTo(hitVec) } - override val resolve = moveNearBlock(blockPos, 2) + override val resolve get() = moveNearBlock(blockPos, 2) override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 8ee4a5e19..49ec01242 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -33,7 +33,7 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_SUCCESS - override val resolve = placeBlock(context) + override val resolve get() = placeBlock(context) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -62,7 +62,7 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_BLOCKED_BY_PLAYER - override val resolve = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { + override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { !world.canCollide(player, Box(blockPos)) } } @@ -78,7 +78,7 @@ sealed class PlaceResult : BuildResult() { override val rank = Rank.PLACE_CANT_REPLACE // override val resolve = breakBlock(simulated.blockPos) - override val resolve = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { + override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { !world.canCollide(player, Box(blockPos)) } } @@ -123,6 +123,6 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_NOT_ITEM_BLOCK - override val resolve = Task.emptyTask() // ToDo: analyze interaction with non-block items + override val resolve get() = Task.emptyTask() // ToDo: analyze interaction with non-block items } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index 0f6c63fa0..db64795ad 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -45,12 +45,17 @@ abstract class MaterialContainer( abstract fun withdraw(selection: StackSelection): Task<*> @Task.Ta5kBuilder - fun doWithdrawal(selection: StackSelection) = - prepare()?.let { prep -> + fun doWithdrawal(selection: StackSelection): Task<*> { + return prepare()?.let { prep -> prep.onSuccess { _, _ -> withdraw(selection).start(prep) + }.onStart { + LOG.info("${it.identifier} withdrawing [$selection] from [$name]") } - } ?: withdraw(selection) + } ?: withdraw(selection).onStart { + LOG.info("${it.identifier} withdrawing [$selection] from [$name]") + } + } /** * Deposits items from the player's inventory into the container. @@ -58,24 +63,29 @@ abstract class MaterialContainer( abstract fun deposit(selection: StackSelection): Task<*> @Task.Ta5kBuilder - fun doDeposit(selection: StackSelection) = - prepare()?.let { prep -> + fun doDeposit(selection: StackSelection): Task<*> { + return prepare()?.let { prep -> prep.onSuccess { _, _ -> deposit(selection).start(prep) + }.onStart { + LOG.info("${it.identifier} depositing [$selection] to [$name]") } - } ?: deposit(selection) + } ?: deposit(selection).onStart { + LOG.info("${it.identifier} depositing [$selection] to [$name]") + } + } - open fun stacksMatching(selection: StackSelection) = + open fun matchingStacks(selection: StackSelection) = selection.filterStacks(stacks) - open fun stacksMatching(selection: (ItemStack) -> Boolean) = - stacksMatching(selection.select()) + open fun matchingStacks(selection: (ItemStack) -> Boolean) = + matchingStacks(selection.select()) open fun available(selection: StackSelection) = - stacksMatching(selection).count + matchingStacks(selection).count open fun spaceLeft(selection: StackSelection) = - stacksMatching(selection).spaceLeft + stacks.empty * selection.stackSize + matchingStacks(selection).spaceLeft + stacks.empty * selection.stackSize fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { val amount = available(selection) @@ -92,12 +102,7 @@ abstract class MaterialContainer( // selection.selector = { true } // selection.count = transferAmount - LOG.info("Transferring $selection from $name to ${destination.name}") - return TransferResult.Success( - doWithdrawal(selection).onSuccess { withdraw, _ -> - destination.doDeposit(selection).start(withdraw) - } - ) + return TransferResult.Success(selection, from = this, to = destination) } enum class Rank { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt index 4eecc68f5..82865ad76 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -16,6 +16,7 @@ import net.minecraft.util.math.BlockPos data class ChestContainer( override var stacks: List, val blockPos: BlockPos, + val containedInStash: StashContainer? = null, ) : MaterialContainer(Rank.CHEST) { override val name = "Chest at ${blockPos.toShortString()}" diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index 54d0de77e..a63512841 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -3,6 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer @@ -30,7 +31,7 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { // } override fun withdraw(selection: StackSelection): Task<*> { - val pos = placePos ?: return Task.emptyTask() + val pos = placePos ?: return emptyTask("No placePos found for EnderChestContainer") return openContainer(pos) .onSuccess { _, screen -> withdraw(screen, selection) @@ -38,7 +39,7 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { } override fun deposit(selection: StackSelection): Task<*> { - val pos = placePos ?: return Task.emptyTask() + val pos = placePos ?: return emptyTask("No placePos found for EnderChestContainer") return openContainer(pos) .onSuccess { _, screen -> deposit(screen, selection) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt index 2eba7bf2a..c47693fb8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/InventoryContainer.kt @@ -4,6 +4,7 @@ import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.player.SlotUtils.combined import net.minecraft.item.ItemStack @@ -13,7 +14,7 @@ object InventoryContainer : MaterialContainer(Rank.INVENTORY) { set(_) {} override val name = "Inventory" - override fun withdraw(selection: StackSelection) = Task.emptyTask() + override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromInventory") - override fun deposit(selection: StackSelection) = Task.emptyTask() + override fun deposit(selection: StackSelection) = emptyTask("DepositToInventory") } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt index 7733225a3..346ac9993 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/MainHandContainer.kt @@ -21,7 +21,7 @@ object MainHandContainer : MaterialContainer(Rank.MAIN_HAND) { override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromMainHand") override fun deposit(selection: StackSelection) = buildTask("DepositToMainHand") { - InventoryContainer.stacksMatching(selection).firstOrNull()?.let { stack -> + InventoryContainer.matchingStacks(selection).firstOrNull()?.let { stack -> if (ItemStack.areEqual(stack, player.mainHandStack)) { return@buildTask } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt index 984a117b7..95d42af88 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/OffHandContainer.kt @@ -3,7 +3,6 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.Task import com.lambda.task.Task.Companion.buildTask import com.lambda.task.Task.Companion.emptyTask import com.lambda.util.player.SlotUtils.combined @@ -22,7 +21,7 @@ object OffHandContainer : MaterialContainer(Rank.OFF_HAND) { override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromOffHand") override fun deposit(selection: StackSelection) = buildTask("DepositToOffHand") { - InventoryContainer.stacksMatching(selection).firstOrNull()?.let { stack -> + InventoryContainer.matchingStacks(selection).firstOrNull()?.let { stack -> if (ItemStack.areEqual(stack, player.offHandStack)) { return@buildTask } diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt index a26618f3f..c92641c95 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -1,8 +1,10 @@ package com.lambda.interaction.material.container +import com.lambda.Lambda.LOG import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw @@ -27,6 +29,7 @@ data class ShulkerBoxContainer( override fun prepare() = placeContainer(shulkerStack).onSuccess { place, placePos -> + LOG.info("Container placed. Opening now ShulkerBoxContainer") placePosition = placePos openContainer(placePos).onSuccess { _, screen -> openScreen = screen @@ -34,8 +37,8 @@ data class ShulkerBoxContainer( } override fun withdraw(selection: StackSelection): Task<*> { - val open = openScreen ?: return Task.emptyTask() - val place = placePosition ?: return Task.emptyTask() + val open = openScreen ?: return emptyTask("No open screen found for ShulkerBoxContainer") + val place = placePosition ?: return emptyTask("No place position found for ShulkerBoxContainer") info("Withdrawing $selection from ${shulkerStack.name.string}") return withdraw(open, selection).onSuccess { withdraw, _ -> breakAndCollectBlock(place).start(withdraw) @@ -43,8 +46,8 @@ data class ShulkerBoxContainer( } override fun deposit(selection: StackSelection): Task<*> { - val open = openScreen ?: return Task.emptyTask() - val place = placePosition ?: return Task.emptyTask() + val open = openScreen ?: return emptyTask("No open screen found for ShulkerBoxContainer") + val place = placePosition ?: return emptyTask("No place position found for ShulkerBoxContainer") info("Depositing $selection to ${shulkerStack.name.string}") return deposit(open, selection).onSuccess { deposit, _ -> breakAndCollectBlock(place).start(deposit) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index 4512cd7be..12f5427fe 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -1,13 +1,25 @@ package com.lambda.interaction.material.transfer +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection import com.lambda.task.Task abstract class TransferResult { abstract val solve: Task<*> data class Success( - override val solve: Task<*> - ) : TransferResult() + val selection: StackSelection, + val from: MaterialContainer, + val to: MaterialContainer + ) : TransferResult() { + override val solve: Task<*> = from.doWithdrawal(selection).onSuccess { withdraw, _ -> + to.doDeposit(selection).start(withdraw) + } + + val undo = to.doWithdrawal(selection).onSuccess { withdraw, _ -> + from.doDeposit(selection).start(withdraw) + } + } data object NoSpace : TransferResult() { // ToDo: Needs inventory space resolver. compressing or disposing diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 68f21a600..6b86380da 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -51,12 +51,15 @@ abstract class Task( private var timeout: Int = Int.MAX_VALUE, private var tries: Int = 0, private var repeats: Int = 0, + private var onStart: SafeContext.(Task) -> Unit = {}, private var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> }, private var onRetry: SafeContext.(Task) -> Unit = {}, private var onTimeout: SafeContext.(Task) -> Unit = {}, private var onRepeat: SafeContext.(Task, Result, Int) -> Unit = { _, _, _ -> }, private var onException: SafeContext.(Task, Throwable) -> Unit = { _, _ -> }, ) : Nameable { + open var pausable = true + private var parent: Task<*>? = null private val root: Task<*> get() = parent?.root ?: this private val depth: Int get() = parent?.depth?.plus(1) ?: 0 @@ -158,7 +161,10 @@ abstract class Task( } activate() - runSafe { onStart() } + runSafe { + onStart(this@Task) + onStart() + } return this } @@ -172,8 +178,6 @@ abstract class Task( @Ta5kBuilder fun deactivate() { if (isDeactivated) return - - LOG.info("$identifier deactivated") state = State.DEACTIVATED stopListening() } @@ -267,8 +271,10 @@ abstract class Task( // return@let // } -// LOG.info("$identifier reactivated parent ${par.identifier}") -// par.activate() + if (par.isActivated) return@let + + LOG.info("$identifier reactivated parent ${par.identifier}") + par.activate() } } @@ -335,6 +341,18 @@ abstract class Task( return this } + /** + * Sets the action to be performed when the task starts. + * + * @param action The action to be performed. + * @return The task instance with the updated start action. + */ + @Ta5kBuilder + fun onStart(action: SafeContext.(Task) -> Unit): Task { + this.onStart = action + return this + } + /** * Sets the action to be performed when the task completes successfully. * diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 9aaf20368..068abc7b6 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -75,7 +75,7 @@ class BuildStructure @Ta5kBuilder constructor( .take(TaskFlow.build.interactLimit) if (TaskFlow.build.breakInstantAtOnce && instantResults.isNotEmpty()) { - cancelSubTasks() +// cancelSubTasks() instantResults.forEach { it.resolve.start(this@BuildStructure, false) } @@ -98,7 +98,7 @@ class BuildStructure @Ta5kBuilder constructor( // if (lastTask?.isCompleted == false) return@listener lastTask = result.resolve - cancelSubTasks() +// cancelSubTasks() LOG.info("Resolving: $result") result.resolve.start(this@BuildStructure) @@ -109,7 +109,7 @@ class BuildStructure @Ta5kBuilder constructor( private fun SafeContext.checkDone() { if (!finishOnDone) return - cancelSubTasks() +// cancelSubTasks() success(Unit) return } From 24fda9c126d6558fd2f8335f368296a9427561b3 Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Jun 2024 01:24:03 +0200 Subject: [PATCH 28/47] Better error handling --- .../interaction/construction/result/BreakResult.kt | 5 +++-- .../interaction/construction/result/BuildResult.kt | 5 +++-- .../material/container/EnderChestContainer.kt | 5 +++-- .../material/container/ShulkerBoxContainer.kt | 9 +++++---- .../interaction/material/transfer/TransferResult.kt | 5 +++-- common/src/main/kotlin/com/lambda/task/Task.kt | 12 +++++++++--- .../kotlin/com/lambda/task/tasks/PlaceContainer.kt | 2 +- 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index 64b2ba531..ebff569a3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -9,6 +9,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.MainHandContainer import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.Task.Companion.failTask import com.lambda.task.tasks.BreakBlock.Companion.breakBlock import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil import net.minecraft.block.BlockState @@ -50,7 +51,7 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BREAK_NOT_EXPOSED - override val resolve get() = emptyTask("Block is not exposed to air.") + override val resolve get() = failTask("Block is not exposed to air.") override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -77,7 +78,7 @@ sealed class BreakResult : BuildResult() { ?.solve ?: run { selectStack { isItem(badItem).not() - }.transfer(MainHandContainer)?.solve ?: emptyTask("No item found or space") // ToDo: Should throw error + }.transfer(MainHandContainer)?.solve ?: failTask("No item found or space") } override fun compareTo(other: ComparableResult): Int { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index 66a2e263c..b80c091d3 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -7,6 +7,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.MainHandContainer import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.Task.Companion.failTask import com.lambda.task.tasks.GoalTask.Companion.moveNearBlock import com.lambda.task.tasks.GoalTask.Companion.moveToGoal import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded @@ -136,7 +137,7 @@ abstract class BuildResult : ComparableResult { override val rank = Rank.WRONG_ITEM override val resolve get() = - neededItem.select().transfer(MainHandContainer)?.solve ?: emptyTask("Item ${neededItem.name.string} not found") // ToDo: Should throw error + neededItem.select().transfer(MainHandContainer)?.solve ?: failTask("Item ${neededItem.name.string} not found") override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -159,7 +160,7 @@ abstract class BuildResult : ComparableResult { override val rank = Rank.WRONG_ITEM override val resolve get() = - neededStack.select().transfer(MainHandContainer)?.solve ?: emptyTask() // ToDo: Should throw error + neededStack.select().transfer(MainHandContainer)?.solve ?: failTask("Stack ${neededStack.name.string} not found") override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index a63512841..144406a46 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -4,6 +4,7 @@ import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.Task.Companion.failTask import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer @@ -31,7 +32,7 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { // } override fun withdraw(selection: StackSelection): Task<*> { - val pos = placePos ?: return emptyTask("No placePos found for EnderChestContainer") + val pos = placePos ?: return failTask("No placePos found for EnderChestContainer") return openContainer(pos) .onSuccess { _, screen -> withdraw(screen, selection) @@ -39,7 +40,7 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { } override fun deposit(selection: StackSelection): Task<*> { - val pos = placePos ?: return emptyTask("No placePos found for EnderChestContainer") + val pos = placePos ?: return failTask("No placePos found for EnderChestContainer") return openContainer(pos) .onSuccess { _, screen -> deposit(screen, selection) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt index c92641c95..14cf6c73f 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -5,6 +5,7 @@ import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask +import com.lambda.task.Task.Companion.failTask import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw @@ -37,8 +38,8 @@ data class ShulkerBoxContainer( } override fun withdraw(selection: StackSelection): Task<*> { - val open = openScreen ?: return emptyTask("No open screen found for ShulkerBoxContainer") - val place = placePosition ?: return emptyTask("No place position found for ShulkerBoxContainer") + val open = openScreen ?: return failTask("No open screen found for ShulkerBoxContainer") + val place = placePosition ?: return failTask("No place position found for ShulkerBoxContainer") info("Withdrawing $selection from ${shulkerStack.name.string}") return withdraw(open, selection).onSuccess { withdraw, _ -> breakAndCollectBlock(place).start(withdraw) @@ -46,8 +47,8 @@ data class ShulkerBoxContainer( } override fun deposit(selection: StackSelection): Task<*> { - val open = openScreen ?: return emptyTask("No open screen found for ShulkerBoxContainer") - val place = placePosition ?: return emptyTask("No place position found for ShulkerBoxContainer") + val open = openScreen ?: return failTask("No open screen found for ShulkerBoxContainer") + val place = placePosition ?: return failTask("No place position found for ShulkerBoxContainer") info("Depositing $selection to ${shulkerStack.name.string}") return deposit(open, selection).onSuccess { deposit, _ -> breakAndCollectBlock(place).start(deposit) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index 12f5427fe..92f43e694 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -3,6 +3,7 @@ package com.lambda.interaction.material.transfer import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task +import com.lambda.task.Task.Companion.failTask abstract class TransferResult { abstract val solve: Task<*> @@ -23,11 +24,11 @@ abstract class TransferResult { data object NoSpace : TransferResult() { // ToDo: Needs inventory space resolver. compressing or disposing - override val solve = Task.emptyTask("NoSpace") + override val solve = failTask("NoSpace") } data class MissingItems(val missing: Int) : TransferResult() { // ToDo: Find other satisfying permutations - override val solve = Task.emptyTask("MissingItems") + override val solve = failTask("MissingItems") } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 6b86380da..6f6f91ec7 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -439,15 +439,21 @@ abstract class Task( val MAX_DEPTH = 20 const val MAX_DEBUG_ENTRIES = 15 - interface EmptyTask - @Ta5kBuilder fun emptyTask( name: String = "EmptyTask", - ): Task = object : EmptyTask, Task() { + ): Task = object : Task() { init { this.name = name } override fun SafeContext.onStart() { success(Unit) } } + + @Ta5kBuilder + fun failTask( + message: String + ): Task = object : Task() { + init { this.name = "FailTask" } + override fun SafeContext.onStart() { failure(message) } + } @Ta5kBuilder fun buildTask( diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index dc81426dd..16a2d9b89 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -14,7 +14,7 @@ import com.lambda.util.BlockUtils.blockPos import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos -class PlaceContainer( +class PlaceContainer @Ta5kBuilder constructor( val stack: ItemStack, ) : Task() { override fun SafeContext.onStart() { From 623a6ff1dda01ecf3d067731a0242cac0b15daec Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 4 Jun 2024 01:41:42 +0200 Subject: [PATCH 29/47] Fix rim height setting default --- .../kotlin/com/lambda/module/modules/player/HighwayTools.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index e671cee33..916dae420 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -27,7 +27,7 @@ object HighwayTools : Module( ) { private val height by setting("Height", 4, 1..10, 1) private val width by setting("Width", 6, 1..30, 1) - private val rimHeight by setting("Rim Height", 0, 1..6, 1) + private val rimHeight by setting("Rim Height", 1, 0..6, 1) private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") private val material = Blocks.OBSIDIAN private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway (negative for infinite)") From dfacc21ae7a7e363253578eec50c3082b21e30d0 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 02:01:57 +0200 Subject: [PATCH 30/47] Gaussian rotation speed calculation --- .../lambda/config/groups/IRotationConfig.kt | 3 +++ .../lambda/config/groups/RotationSettings.kt | 25 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt index 1a42f5da3..933cfe9f4 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt @@ -25,4 +25,7 @@ interface IRotationConfig { * Ticks to rotate back to the actual rotation. */ val resetTicks: Int + + val mean: Double + val derivation: Double } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index f591efbba..54b4f061f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -2,8 +2,10 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.interaction.rotation.RotationMode -import kotlin.math.max -import kotlin.math.min +import kotlin.math.PI +import kotlin.math.cos +import kotlin.math.ln +import kotlin.math.sqrt import kotlin.random.Random class RotationSettings( @@ -13,19 +15,18 @@ class RotationSettings( override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera", vis) override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", " ticks", vis) override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) + override var mean by c.setting("Mean", 90.0, 1.0..180.0, 0.1, "Average rotation speed", "", vis) + override var derivation by c.setting("Standard Deviation", 10.0, 0.0..100.0, 0.1, "Spread of rotation speeds", "", vis) - var r1 by c.setting("Turn Speed 1", 70.0, 1.0..180.0, 0.1, "Rotation Speed 1", "", vis) - var r2 by c.setting("Turn Speed 2", 110.0, 1.0..180.0, 0.1, "Rotation Speed 2", "", vis) - - override val turnSpeed get() = Random.nextDouble(min(r1, r2), max(r1, r2) + 0.01) + override val turnSpeed get() = nextGaussian(mean, derivation) var speedMultiplier = 1.0 - fun slowdownIf(flag: Boolean) { - speedMultiplier = (if (flag) 0.0 else 1.0) - .coerceIn( - speedMultiplier - 0.3, // slowdown faster - speedMultiplier + 0.15 // accelerate slower - ) + private fun nextGaussian(mean: Double = 0.0, standardDeviation: Double = 1.0): Double { + val u1 = Random.nextDouble() + val u2 = Random.nextDouble() + + val randStdNormal = sqrt(-2.0 * ln(u1)) * cos(2.0 * PI * u2) + return mean + standardDeviation * randStdNormal } } \ No newline at end of file From ebb61f858f489093a21e4e2d84ab284b7a7efeed Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 02:02:13 +0200 Subject: [PATCH 31/47] Fix slots loaded mixin --- .../main/java/com/lambda/mixin/render/ScreenHandlerMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java b/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java index bde96fbf7..9f816cfbb 100644 --- a/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java @@ -14,7 +14,7 @@ @Mixin(ScreenHandler.class) public class ScreenHandlerMixin { - @Inject(method = "updateSlotStacks", at = @At("HEAD")) + @Inject(method = "updateSlotStacks", at = @At("TAIL")) private void onUpdateSlotStacksHead(int revision, List stacks, ItemStack cursorStack, CallbackInfo ci) { EventFlow.post(new ScreenHandlerEvent.Loaded(revision, stacks, cursorStack)); } From ae2c06f46b62f1821a99ef29ed83aa230a8309a8 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 02:29:10 +0200 Subject: [PATCH 32/47] Improved rotation settings. No range but Gaussian distribution --- .../lambda/config/groups/IRotationConfig.kt | 14 ++++++++++++++ .../lambda/config/groups/RotationSettings.kt | 19 ++++++++++--------- .../com/lambda/interaction/RotationManager.kt | 4 ++++ .../lambda/module/modules/client/Baritone.kt | 14 ++------------ .../lambda/module/modules/player/Freecam.kt | 6 ++++-- .../lambda/module/modules/player/Replay.kt | 5 ++--- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt index 933cfe9f4..47ff0a226 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/IRotationConfig.kt @@ -26,6 +26,20 @@ interface IRotationConfig { */ val resetTicks: Int + /** + * If true, rotation will be instant without any transition. If false, rotation will transition over time. + */ + val instant: Boolean + + /** + * The mean (average) value for the Gaussian distribution used to calculate rotation speed. + * This value represents the center of the distribution. + */ val mean: Double + + /** + * The standard deviation for the Gaussian distribution used to calculate rotation speed. + * This value represents the spread or dispersion of the distribution. + */ val derivation: Double } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 54b4f061f..c54ed6928 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -2,10 +2,7 @@ package com.lambda.config.groups import com.lambda.config.Configurable import com.lambda.interaction.rotation.RotationMode -import kotlin.math.PI -import kotlin.math.cos -import kotlin.math.ln -import kotlin.math.sqrt +import kotlin.math.* import kotlin.random.Random class RotationSettings( @@ -15,18 +12,22 @@ class RotationSettings( override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera", vis) override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", " ticks", vis) override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) - override var mean by c.setting("Mean", 90.0, 1.0..180.0, 0.1, "Average rotation speed", "", vis) - override var derivation by c.setting("Standard Deviation", 10.0, 0.0..100.0, 0.1, "Spread of rotation speeds", "", vis) + override var instant by c.setting("Instant Rotation", false, "Instantly rotate", vis) + override var mean by c.setting("Mean", 20.0, 1.0..80.0, 0.1, "Average rotation speed") { vis() && !instant } + override var derivation by c.setting("Standard Deviation", 5.0, 0.0..20.0, 0.1, "Spread of rotation speeds") { vis() && !instant } - override val turnSpeed get() = nextGaussian(mean, derivation) + override val turnSpeed get() = abs(nextGaussian(mean, derivation)) var speedMultiplier = 1.0 - private fun nextGaussian(mean: Double = 0.0, standardDeviation: Double = 1.0): Double { + private fun nextGaussian( + mean: Double = 0.0, + deviation: Double = 1.0 + ): Double { val u1 = Random.nextDouble() val u2 = Random.nextDouble() val randStdNormal = sqrt(-2.0 * ln(u1)) * cos(2.0 * PI * u2) - return mean + standardDeviation * randStdNormal + return mean + deviation * randStdNormal } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt index aeadde7e1..11031eb63 100644 --- a/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/RotationManager.kt @@ -80,6 +80,10 @@ object RotationManager : Loadable { currentRotation = currentContext?.let { context -> val rotationTo = if (keepTicks >= 0) context.rotation else player.rotation + if (context.config.instant) { + return@let rotationTo + } + var speedMultiplier = (context.config as? RotationSettings)?.speedMultiplier ?: 1.0 if (keepTicks < 0) speedMultiplier = 1.0 diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt index 177553c41..4a8393d0a 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/Baritone.kt @@ -1,23 +1,13 @@ package com.lambda.module.modules.client -import com.lambda.config.groups.IRotationConfig -import com.lambda.interaction.rotation.RotationMode +import com.lambda.config.groups.RotationSettings import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.util.math.MathUtils.random object Baritone : Module( name = "Baritone", description = "Baritone configuration", defaultTags = setOf(ModuleTag.CLIENT) ) { - private val r1 by setting("Turn Speed 1", 70.0, 1.0..180.0, 0.1) - private val r2 by setting("Turn Speed 2", 110.0, 1.0..180.0, 0.1) - - val rotation = object : IRotationConfig { - override val rotationMode = RotationMode.SYNC - override val turnSpeed get() = random(r1, r2) - override val keepTicks = 3 - override val resetTicks = 3 - } + val rotation = RotationSettings(this) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 2d1283973..27c3429ab 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -32,10 +32,12 @@ object Freecam : Module( ) { private val speed by setting("Speed", 0.5f, 0.1f..1.0f, 0.1f) private val sprint by setting("Sprint Multiplier", 3.0f, 0.1f..10.0f, 0.1f, description = "Set below 1.0 to fly slower on sprint.") + private val reach by setting("Reach", 5.0, 1.0..100.0, 1.0, "Freecam reach distance") private val rotateToTarget by setting("Rotate to target", true) - private val reach by setting("Reach", 4.0, 1.0..100.0, 0.1) - private val rotationConfig = RotationSettings(this).apply { + private val rotationConfig = RotationSettings(this) { + rotateToTarget + }.apply { rotationMode = RotationMode.LOCK } diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt index ba8f51369..6bc52bd3c 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt @@ -56,15 +56,14 @@ object Replay : Module( private val cycle by setting("Cycle Play Mode", KeyCode.B, description = "REPLAY: Replay the recording once. CONTINUE: Replay the recording and continue recording. LOOP: Loop the recording.") private val check by setting("Set Checkpoint", KeyCode.V, description = "Create a checkpoint while recording.") - private val loops by setting("Loops", -1, -1..10, 1, description = "Number of times to loop the replay. -1 for infinite.", unit = "repeats") + private val loops by setting("Loops", -1, -1..10, 1, description = "Number of times to loop the replay. -1 for infinite.", unit = " repeats") private val velocityCheck by setting("Velocity check", true, description = "Check if the player is moving before starting a recording.") private val cancelOnDeviation by setting("Cancel on deviation", true) private val deviationThreshold by setting("Deviation threshold", 0.1, 0.1..5.0, 0.1, description = "The threshold for the deviation to cancel the replay.") { cancelOnDeviation } private val rotationConfig = RotationSettings(this).apply { rotationMode = RotationMode.LOCK - r1 = 1000.0 - r2 = 1001.0 + instant = true } enum class State { From 26c510a59418cebf0a81aa9d379a723cc0331728 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 02:34:50 +0200 Subject: [PATCH 33/47] Set correct freecam perspective --- .../com/lambda/module/modules/player/Freecam.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt index 27c3429ab..35dbc8eb7 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Freecam.kt @@ -22,6 +22,7 @@ import com.lambda.util.primitives.extension.rotation import com.lambda.util.world.raycast.RayCastUtils.orMiss import com.lambda.util.world.raycast.RayCastUtils.orNull import net.minecraft.client.input.KeyboardInput +import net.minecraft.client.option.Perspective import net.minecraft.entity.Entity import net.minecraft.util.math.Vec3d @@ -32,7 +33,7 @@ object Freecam : Module( ) { private val speed by setting("Speed", 0.5f, 0.1f..1.0f, 0.1f) private val sprint by setting("Sprint Multiplier", 3.0f, 0.1f..10.0f, 0.1f, description = "Set below 1.0 to fly slower on sprint.") - private val reach by setting("Reach", 5.0, 1.0..100.0, 1.0, "Freecam reach distance") + private val reach by setting("Reach", 10.0, 1.0..100.0, 1.0, "Freecam reach distance") private val rotateToTarget by setting("Rotate to target", true) private val rotationConfig = RotationSettings(this) { @@ -41,11 +42,11 @@ object Freecam : Module( rotationMode = RotationMode.LOCK } + private var lastPerspective = Perspective.FIRST_PERSON private var prevPosition: Vec3d = Vec3d.ZERO private var position: Vec3d = Vec3d.ZERO private val interpolatedPosition: Vec3d - get() = - prevPosition.interpolate(position, mc.partialTicks) + get() = prevPosition.interpolate(position, mc.partialTicks) private var rotation: Rotation = Rotation.ZERO private var velocity: Vec3d = Vec3d.ZERO @@ -70,11 +71,17 @@ object Freecam : Module( init { onEnable { + lastPerspective = mc.options.perspective + mc.options.perspective = Perspective.FIRST_PERSON position = player.eyePos rotation = player.rotation velocity = Vec3d.ZERO } + onDisable { + mc.options.perspective = lastPerspective + } + listener(Int.MAX_VALUE) { if (!rotateToTarget) return@listener val target = mc.crosshairTarget?.orNull ?: return@listener From bd00fb4414279c6aecdab84d71b68bfd967218a1 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 03:12:08 +0200 Subject: [PATCH 34/47] Nuker floor filling --- .../com/lambda/module/modules/player/Nuker.kt | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 8a0305957..fc5da0fed 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -1,55 +1,51 @@ package com.lambda.module.modules.player -import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnTick import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.instantBreakable -import com.lambda.util.KeyCode import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Vec3i object Nuker : Module( name = "Nuker", description = "Breaks blocks around you", defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) ) { + private val height by setting("Height", 4, 1..8, 1) + private val width by setting("Width", 4, 1..8, 1) private val flatten by setting("Flatten", true) private val onlyBreakInstant by setting("Only Break Instant", true) private val fillFloor by setting("Fill Floor", false) - private val range = Vec3i(4, 4, 4) // TODO: Customizable - private var task: Task<*> = emptyTask() + private var task = emptyTask() init { onEnable { task = buildStructure( pathing = false, - finishOnDone = false + finishOnDone = false, + cancelOnUnsolvable = false ) { blueprintOnTick { _ -> - val selection = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) + val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) .asSequence() .map { it.blockPos } .filter { !world.isAir(it) } .filter { !flatten || it.y >= player.blockPos.y } - .filter { !onlyBreakInstant || instantBreakable(it.blockState(world), it) } + .filter { !onlyBreakInstant || it.blockState(world).getHardness(world, it) <= 1 } + .filter { it.blockState(world).getHardness(world, it) >= 0 } .associateWith { TargetState.Air } -// if (fillFloor) { -// // ToDo: Use smarter iteration pattern -// val floor = BlockPos.iterateOutwards(player.blockPos, range.x, range.y, range.z) -// .filter { it.y == player.blockPos.down().y } -// .map { it.blockPos } -// .associateWith { TargetState.Solid } -// return@DynamicBlueprint selection + floor -// } + if (fillFloor) { + val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width) + .map { it.blockPos } + .associateWith { TargetState.Solid } + return@blueprintOnTick selection + floor + } selection } From b97f0b12a56eba529c74f37c7f2d6fa25ab89814 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 03:12:22 +0200 Subject: [PATCH 35/47] Remove default hotkey for HWT --- .../kotlin/com/lambda/module/modules/player/HighwayTools.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 916dae420..87329202b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -22,8 +22,7 @@ import kotlin.math.roundToInt object HighwayTools : Module( name = "HighwayTools", description = "Auto highway builder", - defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION), - defaultKeybind = KeyCode.X + defaultTags = setOf(ModuleTag.PLAYER, ModuleTag.AUTOMATION) ) { private val height by setting("Height", 4, 1..10, 1) private val width by setting("Width", 6, 1..30, 1) From 97b1cb1d3650b855435988d69298ebe45469f445 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 03:14:12 +0200 Subject: [PATCH 36/47] Prevent pathing on results --- .../lambda/interaction/construction/result/BreakResult.kt | 2 +- .../lambda/interaction/construction/result/BuildResult.kt | 6 +++--- .../com/lambda/interaction/construction/result/Navigable.kt | 3 +++ .../lambda/interaction/construction/result/PlaceResult.kt | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index ebff569a3..a86973cd5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -117,7 +117,7 @@ sealed class BreakResult : BuildResult() { data class PlayerOnTop( override val blockPos: BlockPos, val blockState: BlockState - ) : Resolvable, BreakResult() { + ) : Navigable, Resolvable, BreakResult() { override val rank = Rank.BREAK_PLAYER_ON_TOP override val resolve get() = diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index b80c091d3..c70250ed6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -45,7 +45,7 @@ abstract class BuildResult : ComparableResult { */ data class ChunkNotLoaded( override val blockPos: BlockPos - ) : Resolvable, BuildResult() { + ) : Navigable, Resolvable, BuildResult() { override val rank = Rank.CHUNK_NOT_LOADED override val resolve get() = moveUntilLoaded(blockPos) @@ -112,7 +112,7 @@ abstract class BuildResult : ComparableResult { val hitPos: BlockPos, val side: Direction, val distance: Double - ) : Resolvable, BuildResult() { + ) : Navigable, Resolvable, BuildResult() { override val rank = Rank.NOT_VISIBLE override val resolve get() = moveToGoal(GoalPlace(blockPos)) @@ -184,7 +184,7 @@ abstract class BuildResult : ComparableResult { val hitVec: Vec3d, val reach: Double, val side: Direction, - ) : Resolvable, BuildResult() { + ) : Navigable, Resolvable, BuildResult() { override val rank = Rank.OUT_OF_REACH val distance: Double by lazy { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt new file mode 100644 index 000000000..729da4bf6 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt @@ -0,0 +1,3 @@ +package com.lambda.interaction.construction.result + +interface Navigable \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 49ec01242..24d127a01 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -59,7 +59,7 @@ sealed class PlaceResult : BuildResult() { data class BlockedByPlayer( override val blockPos: BlockPos - ) : Resolvable, PlaceResult() { + ) : Navigable, Resolvable, PlaceResult() { override val rank = Rank.PLACE_BLOCKED_BY_PLAYER override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { @@ -74,7 +74,7 @@ sealed class PlaceResult : BuildResult() { data class CantReplace( override val blockPos: BlockPos, val simulated: ItemPlacementContext - ) : Resolvable, PlaceResult() { + ) : Navigable, Resolvable, PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE // override val resolve = breakBlock(simulated.blockPos) From a12bcaafeedfbfe0d5338f0145b4125973817cdd Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 03:25:21 +0200 Subject: [PATCH 37/47] Better build settings --- .../com/lambda/config/groups/BuildConfig.kt | 5 ++-- .../com/lambda/config/groups/BuildSettings.kt | 5 ++-- .../lambda/config/groups/RotationSettings.kt | 2 +- .../com/lambda/task/tasks/BuildStructure.kt | 24 ++++++++----------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index d13ca2ff6..37099d353 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -1,11 +1,12 @@ package com.lambda.config.groups interface BuildConfig { + val breakCoolDown: Int + val placeCooldown: Int val collectDrops: Boolean val breakWeakBlocks: Boolean val pathing: Boolean - val interactLimit: Int - val breakInstantAtOnce: Boolean + val breaksPerTick: Int val rotateForBreak: Boolean val swingHand: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index c3cf399bb..d14bb2b20 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -6,11 +6,12 @@ class BuildSettings( c: Configurable, vis: () -> Boolean = { true } ) : BuildConfig { + override val breakCoolDown by c.setting("Break Cooldown", 0, 0..20, 1, "Delay between breaking blocks", " ticks", vis) + override val placeCooldown by c.setting("Place Cooldown", 0, 0..20, 1, "Delay between placing blocks", " ticks", vis) override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis) override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", vis) override val pathing by c.setting("Pathing", true, "Path to blocks", vis) - override val interactLimit by c.setting("Interaction Limit", 15, 1..100, 1, "Max interactions per tick", " i/t", vis) - override val breakInstantAtOnce by c.setting("Break Instant At Once", true, "Break all instant blocks at once", vis) + override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick", "", vis) override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", vis) override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index c54ed6928..13dccb38a 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -12,7 +12,7 @@ class RotationSettings( override var rotationMode by c.setting("Mode", RotationMode.SYNC, "SILENT - server-side rotation, SYNC - server-side rotation; client-side movement, LOCK - Lock camera", vis) override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", " ticks", vis) override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) - override var instant by c.setting("Instant Rotation", false, "Instantly rotate", vis) + override var instant by c.setting("Instant Rotation", true, "Instantly rotate", vis) override var mean by c.setting("Mean", 20.0, 1.0..80.0, 0.1, "Average rotation speed") { vis() && !instant } override var derivation by c.setting("Standard Deviation", 5.0, 0.0..20.0, 0.1, "Spread of rotation speeds") { vis() && !instant } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 068abc7b6..4c2e01806 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -11,10 +11,7 @@ import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.PlaceContext -import com.lambda.interaction.construction.result.BreakResult -import com.lambda.interaction.construction.result.BuildResult -import com.lambda.interaction.construction.result.PlaceResult -import com.lambda.interaction.construction.result.Resolvable +import com.lambda.interaction.construction.result.* import com.lambda.interaction.construction.simulation.BuildSimulator import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState @@ -50,7 +47,8 @@ class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, private val finishOnDone: Boolean = true, private val pathing: Boolean = TaskFlow.build.pathing, - private val collectDrops: Boolean = TaskFlow.build.collectDrops, + val collectDrops: Boolean = TaskFlow.build.collectDrops, + private val cancelOnUnsolvable: Boolean = true, ) : Task() { private var lastTask: Task<*>? = null @@ -72,33 +70,29 @@ class BuildStructure @Ta5kBuilder constructor( val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } .sorted() - .take(TaskFlow.build.interactLimit) + .take(TaskFlow.build.breaksPerTick) - if (TaskFlow.build.breakInstantAtOnce && instantResults.isNotEmpty()) { -// cancelSubTasks() + if (TaskFlow.build.breaksPerTick > 1 && instantResults.isNotEmpty()) { instantResults.forEach { - it.resolve.start(this@BuildStructure, false) + it.resolve.start(this@BuildStructure, pauseParent = false) } lastTask = instantResults.last().resolve return@listener } results.minOrNull()?.let { result -> - if (!pathing && result is BuildResult.OutOfReach) return@let + if (!pathing && result is Navigable) return@let if (result !is Resolvable) { if (result is BuildResult.Done) { checkDone() - } else { + } else if (cancelOnUnsolvable) { failure("Failed to resolve build result: $result") return@listener } return@listener } -// if (lastTask?.isCompleted == false) return@listener - lastTask = result.resolve -// cancelSubTasks() LOG.info("Resolving: $result") result.resolve.start(this@BuildStructure) @@ -120,12 +114,14 @@ class BuildStructure @Ta5kBuilder constructor( finishOnDone: Boolean = true, collectDrops: Boolean = TaskFlow.build.collectDrops, pathing: Boolean = TaskFlow.build.pathing, + cancelOnUnsolvable: Boolean = true, blueprint: () -> Blueprint, ) = BuildStructure( blueprint(), finishOnDone, pathing, collectDrops, + cancelOnUnsolvable ) @Ta5kBuilder From fe82165d3c3056c1194e0ddb3a97990e8f72c3fc Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 03:26:50 +0200 Subject: [PATCH 38/47] Task control flow improvements --- .../lambda/module/modules/client/TaskFlow.kt | 3 +- .../src/main/kotlin/com/lambda/task/Task.kt | 123 ++++++++++-------- .../com/lambda/task/tasks/PlaceContainer.kt | 11 +- 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index d62aac295..c90371467 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -14,10 +14,11 @@ object TaskFlow : Module( defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.AUTOMATION) ) { enum class Page { - BUILD, ROTATION, INTERACTION + TASKS, BUILD, ROTATION, INTERACTION } private val page by setting("Page", Page.BUILD) + val taskCooldown by setting("Task Cooldown", 0, 0..200, 1, " ticks") val build = BuildSettings(this) { page == Page.BUILD } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 6f6f91ec7..067a18939 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -7,10 +7,12 @@ import com.lambda.event.EventFlow import com.lambda.event.Subscriber import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener +import com.lambda.module.modules.client.TaskFlow import com.lambda.threading.runSafe import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.logError import com.lambda.util.Communication.warn +import com.lambda.util.DynamicReflectionSerializer.dynamicString import com.lambda.util.Nameable import com.lambda.util.text.buildText import com.lambda.util.text.color @@ -51,6 +53,7 @@ abstract class Task( private var timeout: Int = Int.MAX_VALUE, private var tries: Int = 0, private var repeats: Int = 0, + private var cooldown: Int = TaskFlow.taskCooldown, private var onStart: SafeContext.(Task) -> Unit = {}, private var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> }, private var onRetry: SafeContext.(Task) -> Unit = {}, @@ -68,7 +71,7 @@ abstract class Task( private var attempted = 0 private val subTasks = mutableListOf>() private var state = State.IDLE - var age: Int = 0 + var age = 0 private val isDeactivated get() = state == State.DEACTIVATED val isActivated get() = state == State.ACTIVATED @@ -81,35 +84,6 @@ abstract class Task( // ToDo: Better color management private val primaryColor = Color(0, 255, 0, 100) - val info: Text - get() = buildText { - literal("Name ") - color(primaryColor) { literal(name) } - - literal(" State ") - color(primaryColor) { literal(state.name) } - - literal(" Runtime ") - color(primaryColor) { - literal(DurationFormatUtils.formatDuration(age * 50L, "HH:mm:ss,SSS").dropLast(1)) - } - - val display = subTasks.reversed().take(MAX_DEBUG_ENTRIES) - display.forEach { - literal("\n${" ".repeat(depth + 1)}") - text(it.info) - } - - val left = subTasks.size - display.size - if (left > 0) { - literal("\n${" ".repeat(depth + 1)}And ") - color(primaryColor) { - literal("$left") - } - literal(" more...") - } - - } val syncListeners = Subscriber() private val concurrentListeners = Subscriber() @@ -120,7 +94,7 @@ abstract class Task( DEACTIVATED, CANCELLED, FAILED, - COMPLETED + COMPLETED, } operator fun plus(other: Task<*>) = subTasks.add(other) @@ -184,6 +158,8 @@ abstract class Task( @Ta5kBuilder fun SafeContext.success(result: Result) { + LOG.info("$identifier completed successfully after $attempted retries and $executions executions.") + if (executions < repeats) { executions++ LOG.info("Repeating $identifier $executions/$repeats...") @@ -192,7 +168,6 @@ abstract class Task( return } - LOG.info("$identifier completed successfully after $attempted retries and $executions executions.") state = State.COMPLETED stopListening() onSuccess(this@Task, result) @@ -201,6 +176,8 @@ abstract class Task( @Ta5kBuilder fun cancel() { + if (state == State.CANCELLED) return + cancelSubTasks() state = State.CANCELLED stopListening() @@ -210,9 +187,7 @@ abstract class Task( @Ta5kBuilder fun cancelSubTasks() { - subTasks - .filter { it.isRunning } - .forEach { it.cancel() } + subTasks.forEach { it.cancel() } } @Ta5kBuilder @@ -232,7 +207,6 @@ abstract class Task( onRetry(this@Task) } reset() -// activate() return } @@ -265,16 +239,16 @@ abstract class Task( private fun notifyParent() { parent?.let { par -> -// if (par.isCompleted) { -// LOG.info("$identifier notified parent ${par.identifier}") -// par.notifyParent() -// return@let -// } - - if (par.isActivated) return@let - - LOG.info("$identifier reactivated parent ${par.identifier}") - par.activate() + when { + par.isCompleted -> { + LOG.info("$identifier completed parent ${par.identifier}") + par.notifyParent() + } + !par.isActivated -> { + LOG.info("$identifier reactivated parent ${par.identifier}") + par.activate() + } + } } } @@ -362,6 +336,15 @@ abstract class Task( @Ta5kBuilder fun onSuccess(action: SafeContext.(Task, Result) -> Unit): Task { this.onSuccess = action + LOG.info("Success action $action set for $identifier") + return this + } + + @Ta5kBuilder + fun thenRun(action: SafeContext.(Task, Result) -> Task<*>): Task { + this.onSuccess = { task, result -> + action(this, task, result).start(task) + } return this } @@ -431,10 +414,6 @@ abstract class Task( class TimeoutException(age: Int, attempts: Int) : Exception("Task timed out after $age ticks and $attempts attempts") - class SubTaskBuilder { - val tasks = mutableListOf>() - } - companion object { val MAX_DEPTH = 20 const val MAX_DEBUG_ENTRIES = 15 @@ -452,7 +431,19 @@ abstract class Task( message: String ): Task = object : Task() { init { this.name = "FailTask" } - override fun SafeContext.onStart() { failure(message) } + override fun SafeContext.onStart() { + failure(message) + } + } + + @Ta5kBuilder + fun failTask( + e: Throwable + ): Task = object : Task() { + init { this.name = "FailTask" } + override fun SafeContext.onStart() { + failure(e) + } } @Ta5kBuilder @@ -483,4 +474,34 @@ abstract class Task( } } } + + val info: Text + get() = buildText { + literal("Name ") + color(primaryColor) { literal(name) } + + literal(" State ") + color(primaryColor) { literal(state.name) } + + literal(" Runtime ") + color(primaryColor) { + literal(DurationFormatUtils.formatDuration(age * 50L, "HH:mm:ss,SSS").dropLast(1)) + } + + val display = subTasks.reversed().take(MAX_DEBUG_ENTRIES) + display.forEach { + literal("\n${" ".repeat(depth + 1)}") + text(it.info) + } + + val left = subTasks.size - display.size + if (left > 0) { + literal("\n${" ".repeat(depth + 1)}And ") + color(primaryColor) { + literal("$left") + } + literal(" more...") + } + + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt index 16a2d9b89..b0fd34c96 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -19,9 +19,7 @@ class PlaceContainer @Ta5kBuilder constructor( ) : Task() { override fun SafeContext.onStart() { val results = BlockPos.iterateOutwards(player.blockPos, 4, 3, 4) -// .filter { world.isAir(it) } .map { it.blockPos } -// .filter { world.isAir(it) } .flatMap { it.blockPos .toStructure(TargetState.Stack(stack)) @@ -32,10 +30,13 @@ class PlaceContainer @Ta5kBuilder constructor( // val res = results.sorted() // res - val useful = results.filterIsInstance() + results.filterIsInstance() - useful.minOrNull()?.let { result -> + val succeeds = results.filterIsInstance() + val wrongStacks = results.filterIsInstance() + (succeeds + wrongStacks).minOrNull()?.let { result -> buildStructure { - result.blockPos.toStructure(TargetState.Stack(stack)).toBlueprint() + result.blockPos + .toStructure(TargetState.Stack(stack)) + .toBlueprint() }.onSuccess { _, _ -> success(result.blockPos) }.start(this@PlaceContainer) From 070c2b7c457cb297f956accfe1aa59ad5333d1f9 Mon Sep 17 00:00:00 2001 From: Constructor Date: Fri, 7 Jun 2024 05:39:22 +0200 Subject: [PATCH 39/47] Material transaction optimizations, task cooldown, drop collection ... --- .../command/commands/TransferCommand.kt | 3 +- .../construction/result/BuildResult.kt | 4 +- .../construction/simulation/BuildSimulator.kt | 12 ++- .../construction/verify/TargetState.kt | 4 +- .../interaction/material/ContainerManager.kt | 3 +- .../interaction/material/MaterialContainer.kt | 36 +------- .../material/container/ChestContainer.kt | 20 ++--- .../material/container/EnderChestContainer.kt | 21 ++--- .../material/container/HotbarContainer.kt | 5 +- .../material/container/ShulkerBoxContainer.kt | 62 +++++++------ .../material/transfer/TransferResult.kt | 10 +-- .../lambda/module/modules/client/TaskFlow.kt | 4 +- .../src/main/kotlin/com/lambda/task/Task.kt | 89 ++++++++++++++----- .../com/lambda/task/tasks/AcquireMaterial.kt | 2 +- .../com/lambda/task/tasks/BreakBlock.kt | 33 +++++-- .../lambda/task/tasks/ContainerTransfer.kt | 20 +++++ .../kotlin/com/lambda/task/tasks/GoalTask.kt | 37 +++++--- .../com/lambda/task/tasks/HelloWorldTask.kt | 4 +- .../com/lambda/task/tasks/InventoryTask.kt | 64 +++++++++---- .../com/lambda/task/tasks/OpenContainer.kt | 4 +- .../com/lambda/task/tasks/PlaceBlock.kt | 29 +++--- .../com/lambda/util/collections/Cacheable.kt | 14 +++ .../com/lambda/util/item/ItemStackUtils.kt | 6 +- .../com/lambda/util/player/SlotUtils.kt | 2 +- 24 files changed, 306 insertions(+), 182 deletions(-) create mode 100644 common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt create mode 100644 common/src/main/kotlin/com/lambda/util/collections/Cacheable.kt diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index b37162528..68ebed04d 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -25,7 +25,8 @@ object TransferCommand : LambdaCommand( required(integer("amount")) { amount -> required(string("from")) { from -> suggests { ctx, builder -> - val selection = selectStack(amount(ctx).value()) { + val count = amount(ctx).value() + val selection = selectStack(count) { isItem(stack(ctx).value().item) } containerMatchSelection(selection).forEach { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index c70250ed6..ec4aae547 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -115,7 +115,9 @@ abstract class BuildResult : ComparableResult { ) : Navigable, Resolvable, BuildResult() { override val rank = Rank.NOT_VISIBLE - override val resolve get() = moveToGoal(GoalPlace(blockPos)) + override val resolve get() = moveToGoal { + GoalPlace(blockPos) + } override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 716906ede..2abdbc2e6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -121,6 +121,11 @@ object BuildSimulator { if (voxelShape.isEmpty) return@forEach val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) } + + if (boxes.all { it.center.distanceTo(eye) > interact.reach + 1 }) { + acc.add(BuildResult.OutOfReach(pos, eye, hitPos.vecOf(hitSide), interact.reach, hitSide)) + return@forEach + } val verify: HitResult.() -> Boolean = { blockResult?.blockPos == hitPos && blockResult?.side == hitSide } @@ -321,9 +326,14 @@ object BuildSimulator { voxelShape.getClosestPointTo(eye).ifPresent { // ToDo: Use closest point of shape } + val boxes = voxelShape.boundingBoxes.map { it.offset(pos) } - val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } + if (boxes.all { it.center.distanceTo(eye) > interact.reach + 1 }) { + acc.add(BuildResult.OutOfReach(pos, eye, pos.toCenterPos(), interact.reach, Direction.UP)) + return acc + } + val verify: HitResult.() -> Boolean = { blockResult?.blockPos == pos } /* the player is buried inside the block */ if (boxes.any { it.contains(eye) }) { currentCast?.blockResult?.let { blockHit -> diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index 85f74ec22..c07398559 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -42,12 +42,12 @@ sealed class TargetState : StateMatcher { block.getPickStack(world, pos, block.defaultState) } data class Stack(val itemStack: ItemStack) : TargetState() { - val copy = itemStack.copy() + val copy: ItemStack = itemStack.copy() override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = state.block == copy.item.block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = - itemStack + copy } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt index d45d6a1e8..ad4d35bbf 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -17,6 +17,7 @@ import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.screen.GenericContainerScreenHandler +import net.minecraft.screen.ScreenHandler import net.minecraft.screen.ScreenHandlerType import java.util.TreeSet @@ -39,7 +40,7 @@ object ContainerManager : Loadable { lastInteractedBlockEntity = it.blockHitResult.blockPos.blockEntity(world) } - listener> { event -> + listener> { event -> // ToDo: ;-; i hate type erasure. // The listener will be triggered for any H, not just GenericContainerScreenHandler if (event.screenHandler !is GenericContainerScreenHandler) return@listener diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index db64795ad..d90ebde78 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -1,6 +1,5 @@ package com.lambda.interaction.material -import com.lambda.Lambda.LOG import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.ShulkerBoxContainer import com.lambda.interaction.material.transfer.TransferResult @@ -34,46 +33,17 @@ abstract class MaterialContainer( this.stacks = stacks } - /** - * Brings the player into a withdrawal/deposit state. E.g.: move to a chest etc. - */ - open fun prepare(): Task<*>? = null - /** * Withdraws items from the container to the player's inventory. */ - abstract fun withdraw(selection: StackSelection): Task<*> - @Task.Ta5kBuilder - fun doWithdrawal(selection: StackSelection): Task<*> { - return prepare()?.let { prep -> - prep.onSuccess { _, _ -> - withdraw(selection).start(prep) - }.onStart { - LOG.info("${it.identifier} withdrawing [$selection] from [$name]") - } - } ?: withdraw(selection).onStart { - LOG.info("${it.identifier} withdrawing [$selection] from [$name]") - } - } + abstract fun withdraw(selection: StackSelection): Task<*> /** * Deposits items from the player's inventory into the container. */ - abstract fun deposit(selection: StackSelection): Task<*> - @Task.Ta5kBuilder - fun doDeposit(selection: StackSelection): Task<*> { - return prepare()?.let { prep -> - prep.onSuccess { _, _ -> - deposit(selection).start(prep) - }.onStart { - LOG.info("${it.identifier} depositing [$selection] to [$name]") - } - } ?: deposit(selection).onStart { - LOG.info("${it.identifier} depositing [$selection] to [$name]") - } - } + abstract fun deposit(selection: StackSelection): Task<*> open fun matchingStacks(selection: StackSelection) = selection.filterStacks(stacks) @@ -90,7 +60,7 @@ abstract class MaterialContainer( fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { val amount = available(selection) if (amount < selection.count) { - return TransferResult.MissingItems(amount - selection.count) + return TransferResult.MissingItems( selection.count - amount) } // val space = destination.spaceLeft(selection) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt index 82865ad76..222f589f5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ChestContainer.kt @@ -2,12 +2,10 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection -import com.lambda.task.tasks.GoalTask.Companion.moveIntoEntityRange import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer import com.lambda.util.Communication.info -import net.minecraft.block.ChestBlock import net.minecraft.item.ItemStack import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.ScreenHandler @@ -20,16 +18,16 @@ data class ChestContainer( ) : MaterialContainer(Rank.CHEST) { override val name = "Chest at ${blockPos.toShortString()}" - override fun prepare() = - moveIntoEntityRange(blockPos).onSuccess { _, _ -> -// when { -// ChestBlock.hasBlockOnTop(world, blockPos) -> breakBlock(blockPos.up()) -// ChestBlock.hasCatOnTop(world, blockPos) -> kill(cat) +// 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() // } - if (ChestBlock.isChestBlocked(world, blockPos)) { - throw ChestBlockedException() - } - } +// } override fun withdraw(selection: StackSelection) = openContainer(blockPos) diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt index 144406a46..4dc768202 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt @@ -3,10 +3,7 @@ package com.lambda.interaction.material.container import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task -import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.Task.Companion.failTask -import com.lambda.task.tasks.InventoryTask.Companion.deposit -import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer import net.minecraft.item.ItemStack import net.minecraft.screen.GenericContainerScreenHandler @@ -17,9 +14,9 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { override val name = "EnderChest" private var placePos: BlockPos? = null - override fun prepare(): Task<*> { - TODO("Not yet implemented") - } +// override fun prepare(): Task<*> { +// TODO("Not yet implemented") +// } // findBlock(Blocks.ENDER_CHEST).onSuccess { pos -> // moveIntoEntityRange(pos) // placePos = pos @@ -32,18 +29,10 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) { // } override fun withdraw(selection: StackSelection): Task<*> { - val pos = placePos ?: return failTask("No placePos found for EnderChestContainer") - return openContainer(pos) - .onSuccess { _, screen -> - withdraw(screen, selection) - } + TODO() } override fun deposit(selection: StackSelection): Task<*> { - val pos = placePos ?: return failTask("No placePos found for EnderChestContainer") - return openContainer(pos) - .onSuccess { _, screen -> - deposit(screen, selection) - } + TODO() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt index 55b727178..c35b165c8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/HotbarContainer.kt @@ -7,7 +7,6 @@ import com.lambda.task.Task import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.util.player.SlotUtils.hotbar -import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider import net.minecraft.item.ItemStack object HotbarContainer : MaterialContainer(Rank.HOTBAR) { @@ -19,7 +18,7 @@ object HotbarContainer : MaterialContainer(Rank.HOTBAR) { override fun withdraw(selection: StackSelection) = emptyTask("WithdrawFromHotbar") override fun deposit(selection: StackSelection): Task<*> { - val handledScreen = mc.currentScreen as? ScreenHandlerProvider<*> ?: return emptyTask() - return deposit(handledScreen.screenHandler, selection) + val handler = mc.player?.currentScreenHandler ?: return emptyTask("NoScreenHandler") + return deposit(handler, selection) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt index 14cf6c73f..53ad4d1dc 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/ShulkerBoxContainer.kt @@ -1,57 +1,63 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.LOG +import com.lambda.context.SafeContext import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task -import com.lambda.task.Task.Companion.emptyTask -import com.lambda.task.Task.Companion.failTask import com.lambda.task.tasks.BuildStructure.Companion.breakAndCollectBlock import com.lambda.task.tasks.InventoryTask.Companion.deposit import com.lambda.task.tasks.InventoryTask.Companion.withdraw import com.lambda.task.tasks.OpenContainer.Companion.openContainer import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer -import com.lambda.util.Communication.info import net.minecraft.item.ItemStack import net.minecraft.screen.ShulkerBoxScreenHandler -import net.minecraft.util.math.BlockPos data class ShulkerBoxContainer( override var stacks: List, val containedIn: MaterialContainer, val shulkerStack: ItemStack, ) : MaterialContainer(Rank.SHULKER_BOX) { - private var openScreen: ShulkerBoxScreenHandler? = null - private var placePosition: BlockPos? = null - override val name = "${shulkerStack.name.string} in slot $slotInContainer in ${containedIn.name}" private val slotInContainer: Int get() = containedIn.stacks.indexOf(shulkerStack) - override fun prepare() = - placeContainer(shulkerStack).onSuccess { place, placePos -> - LOG.info("Container placed. Opening now ShulkerBoxContainer") - placePosition = placePos - openContainer(placePos).onSuccess { _, screen -> - openScreen = screen - }.start(place) - } - - override fun withdraw(selection: StackSelection): Task<*> { - val open = openScreen ?: return failTask("No open screen found for ShulkerBoxContainer") - val place = placePosition ?: return failTask("No place position found for ShulkerBoxContainer") - info("Withdrawing $selection from ${shulkerStack.name.string}") - return withdraw(open, selection).onSuccess { withdraw, _ -> - breakAndCollectBlock(place).start(withdraw) + class Withdraw( + private val selection: StackSelection, + private val shulkerStack: ItemStack + ) : Task() { + override fun SafeContext.onStart() { + placeContainer(shulkerStack).thenRun { _, placePos -> + openContainer(placePos).thenRun { _, screen -> + LOG.info("Opened shulker box screen now withdrawing $selection.") + withdraw(screen, selection).thenRun { _, _ -> + breakAndCollectBlock(placePos).onSuccess { _, _ -> + success(Unit) + } + } + } + }.start(this@Withdraw) } } - override fun deposit(selection: StackSelection): Task<*> { - val open = openScreen ?: return failTask("No open screen found for ShulkerBoxContainer") - val place = placePosition ?: return failTask("No place position found for ShulkerBoxContainer") - info("Depositing $selection to ${shulkerStack.name.string}") - return deposit(open, selection).onSuccess { deposit, _ -> - breakAndCollectBlock(place).start(deposit) + override fun withdraw(selection: StackSelection) = Withdraw(selection, shulkerStack) + + class Deposit( + private val selection: StackSelection, + private val shulkerStack: ItemStack + ) : Task() { + override fun SafeContext.onStart() { + placeContainer(shulkerStack).thenRun { _, placePos -> + openContainer(placePos).thenRun { _, screen -> + deposit(screen, selection).thenRun { _, _ -> + breakAndCollectBlock(placePos).onSuccess { _, _ -> + success(Unit) + } + } + } + }.start(this@Deposit) } } + + override fun deposit(selection: StackSelection) = Deposit(selection, shulkerStack) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index 92f43e694..f2baef0c6 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -4,6 +4,7 @@ import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task import com.lambda.task.Task.Companion.failTask +import com.lambda.task.tasks.ContainerTransfer abstract class TransferResult { abstract val solve: Task<*> @@ -13,13 +14,10 @@ abstract class TransferResult { val from: MaterialContainer, val to: MaterialContainer ) : TransferResult() { - override val solve: Task<*> = from.doWithdrawal(selection).onSuccess { withdraw, _ -> - to.doDeposit(selection).start(withdraw) - } + override val solve = ContainerTransfer(selection, from, to) + val undo = ContainerTransfer(selection, to, from) - val undo = to.doWithdrawal(selection).onSuccess { withdraw, _ -> - from.doDeposit(selection).start(withdraw) - } + override fun toString() = "Transfer of [$selection] from [$from] to [$to]" } data object NoSpace : TransferResult() { diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index c90371467..b42f903e0 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -18,7 +18,9 @@ object TaskFlow : Module( } private val page by setting("Page", Page.BUILD) - val taskCooldown by setting("Task Cooldown", 0, 0..200, 1, " ticks") + val taskCooldown by setting("Task Cooldown", 0, 0..10000, 10, " ms") { + page == Page.TASKS + } val build = BuildSettings(this) { page == Page.BUILD } diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index 067a18939..b22a39934 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -8,6 +8,8 @@ import com.lambda.event.Subscriber import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.modules.client.TaskFlow +import com.lambda.threading.runConcurrent +import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.logError @@ -18,6 +20,7 @@ import com.lambda.util.text.buildText import com.lambda.util.text.color import com.lambda.util.text.literal import com.lambda.util.text.text +import kotlinx.coroutines.delay import net.minecraft.text.Text import org.apache.commons.lang3.time.DurationFormatUtils import java.awt.Color @@ -39,7 +42,7 @@ import java.awt.Color * This makes it easy to build complex flows of nested tasks. * * @property delay The delay before the task starts, in milliseconds. - * @property timeout The maximum time that the task is allowed to run, in milliseconds. + * @property timeout The maximum time that the task is allowed to run, in ticks. * @property tries The maximum number of attempts to execute the task before it is considered failed. * @property executions The number of times the task should be repeated. * @property onSuccess The action to be performed when the task completes successfully. @@ -48,22 +51,22 @@ import java.awt.Color * @property onRepeat The action to be performed each time the task is repeated. * @property onException The action to be performed when the task encounters an exception. */ -abstract class Task( - private var delay: Int = 0, - private var timeout: Int = Int.MAX_VALUE, - private var tries: Int = 0, - private var repeats: Int = 0, - private var cooldown: Int = TaskFlow.taskCooldown, - private var onStart: SafeContext.(Task) -> Unit = {}, - private var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> }, - private var onRetry: SafeContext.(Task) -> Unit = {}, - private var onTimeout: SafeContext.(Task) -> Unit = {}, - private var onRepeat: SafeContext.(Task, Result, Int) -> Unit = { _, _, _ -> }, - private var onException: SafeContext.(Task, Throwable) -> Unit = { _, _ -> }, -) : Nameable { +abstract class Task : Nameable { + open var delay: Int = 0 + open var timeout: Int = Int.MAX_VALUE + open var tries: Int = 0 + open var repeats: Int = 0 + open var cooldown: Int = TaskFlow.taskCooldown + open var onStart: SafeContext.(Task) -> Unit = {} + open var onSuccess: SafeContext.(Task, Result) -> Unit = { _, _ -> } + open var onRetry: SafeContext.(Task) -> Unit = {} + open var onTimeout: SafeContext.(Task) -> Unit = {} + open var onRepeat: SafeContext.(Task, Result, Int) -> Unit = { _, _, _ -> } + open var onException: SafeContext.(Task, Throwable) -> Unit = { _, _ -> } + open var pausable = true - private var parent: Task<*>? = null + var parent: Task<*>? = null private val root: Task<*> get() = parent?.root ?: this private val depth: Int get() = parent?.depth?.plus(1) ?: 0 @@ -77,7 +80,7 @@ abstract class Task( val isActivated get() = state == State.ACTIVATED val isRunning get() = state == State.ACTIVATED || state == State.DEACTIVATED val isFailed get() = state == State.FAILED - val isCompleted get() = state == State.COMPLETED + val isCompleted get() = state == State.COMPLETED || state == State.COOLDOWN val isRoot get() = parent == null override var name = this::class.simpleName ?: "Task" val identifier get() = "$name@${hashCode()}" @@ -94,6 +97,7 @@ abstract class Task( DEACTIVATED, CANCELLED, FAILED, + COOLDOWN, COMPLETED, } @@ -158,8 +162,6 @@ abstract class Task( @Ta5kBuilder fun SafeContext.success(result: Result) { - LOG.info("$identifier completed successfully after $attempted retries and $executions executions.") - if (executions < repeats) { executions++ LOG.info("Repeating $identifier $executions/$repeats...") @@ -168,10 +170,31 @@ abstract class Task( return } - state = State.COMPLETED stopListening() - onSuccess(this@Task, result) + if (cooldown > 0) { + state = State.COOLDOWN + runConcurrent { + delay(cooldown.toLong()) + runGameScheduled { + finish(result) + } + } + } else finish(result) + } + + private fun SafeContext.finish(result: Result) { + state = State.COMPLETED + try { + onSuccess(this@Task, result) + } catch (e: ClassCastException) { + result?.let { + LOG.error("Failed to cast result of $identifier to ${it::class.simpleName}") + } + failure(e) + } + notifyParent() + LOG.info("$identifier completed successfully after $attempted retries and $executions executions.") } @Ta5kBuilder @@ -270,7 +293,7 @@ abstract class Task( /** * Sets the delay before the task starts. * - * @param delay The delay in milliseconds. + * @param delay The delay in ticks. * @return This task instance with the updated delay. */ @Ta5kBuilder @@ -282,7 +305,7 @@ abstract class Task( /** * Sets the timeout for a single attempt of the task * - * @param timeout The timeout in milliseconds + * @param timeout The timeout in ticks. * @return This task instance with the updated timeout. */ @Ta5kBuilder @@ -291,6 +314,20 @@ abstract class Task( return this } + /** + * Sets the cooldown period for the task. + * + * The cooldown period is the time that the task will wait before it the next task can be executed. + * + * @param cooldown The cooldown period in milliseconds. + * @return This task instance with the updated cooldown period. + */ + @Ta5kBuilder + fun withCooldown(cooldown: Int): Task { + this.cooldown = cooldown + return this + } + /** * Sets the maximum number of attempts to execute the task before it is considered failed. * @@ -340,6 +377,14 @@ abstract class Task( return this } + /** + * This method allows you to chain another task that will be started upon the successful completion of the current task. + * The action provided as a parameter will be used to create the next task. + * The context of the action, the current task, and the result of the current task are passed as parameters to the action. + * + * @param action A lambda function that takes the current task and its result as parameters and returns the next task to be started. + * @return The current task instance with the updated success action. + */ @Ta5kBuilder fun thenRun(action: SafeContext.(Task, Result) -> Task<*>): Task { this.onSuccess = { task, result -> diff --git a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt index 6bb437480..008c3d333 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt @@ -11,7 +11,7 @@ class AcquireMaterial( ) : Task() { override fun SafeContext.onStart() { findContainerWithSelection(selection) - ?.doWithdrawal(selection) + ?.withdraw(selection) ?.onSuccess { _, _ -> success(selection) }?.start(this@AcquireMaterial) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index e1677a265..175793faf 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -1,5 +1,6 @@ package com.lambda.task.tasks +import baritone.api.pathing.goals.GoalBlock import com.lambda.context.SafeContext import com.lambda.event.events.RotationEvent import com.lambda.event.events.TickEvent @@ -11,6 +12,10 @@ import com.lambda.config.groups.IRotationConfig import com.lambda.interaction.visibilty.VisibilityChecker.lookAtBlock import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task +import com.lambda.task.tasks.GoalTask.Companion.moveToBlock +import com.lambda.task.tasks.GoalTask.Companion.moveToBlockUntil +import com.lambda.task.tasks.GoalTask.Companion.moveToGoal +import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil import com.lambda.util.BlockUtils.blockState import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState @@ -23,15 +28,22 @@ class BreakBlock @Ta5kBuilder constructor( private val rotationConfig: IRotationConfig = TaskFlow.rotation, private val interactionConfig: InteractionConfig = TaskFlow.interact, private val sides: Set = emptySet(), - private val collectDrop: Boolean = TaskFlow.build.collectDrops, + private var collectDrop: Boolean = TaskFlow.build.collectDrops, private val rotate: Boolean = TaskFlow.build.rotateForBreak, private val swingHand: Boolean = TaskFlow.build.swingHand, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null val SafeContext.state: BlockState get() = blockPos.blockState(world) + override var cooldown = TaskFlow.build.breakCoolDown override fun SafeContext.onStart() { + parent?.let { + if (it is BuildStructure) { + collectDrop = it.collectDrops + } + } + if (state.isAir && !collectDrop) { success(null) return @@ -54,7 +66,7 @@ class BreakBlock @Ta5kBuilder constructor( } listener { - if (state.isAir && !collectDrop) { + if (finish()) { success(null) return@listener } @@ -62,17 +74,28 @@ class BreakBlock @Ta5kBuilder constructor( if (rotate) return@listener breakBlock(ctx.result.side) - if (state.isAir && !collectDrop) success(null) + if (finish()) success(null) } listener { - if (it.entity is ItemEntity + if (collectDrop + && it.entity is ItemEntity && it.entity.pos.isInRange(blockPos.toCenterPos(), 1.0) // && it.entity.stack.item == beginState?.block?.item // ToDo: The item entities are all air?? - ) success(it.entity) + ) { + moveToGoalUntil( + { GoalBlock(it.entity.blockPos) }, + { !world.entities.contains(it.entity) } + ).onSuccess { _, _ -> + success(it.entity) + }.start(this@BreakBlock) +// success(it.entity) + } } } + private fun SafeContext.finish() = state.isAir && !collectDrop + private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { if (swingHand) player.swingHand(ctx.hand) diff --git a/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt b/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt new file mode 100644 index 000000000..6f6391314 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt @@ -0,0 +1,20 @@ +package com.lambda.task.tasks + +import com.lambda.context.SafeContext +import com.lambda.interaction.material.MaterialContainer +import com.lambda.interaction.material.StackSelection +import com.lambda.task.Task + +class ContainerTransfer( + val selection: StackSelection, + val from: MaterialContainer, + val to: MaterialContainer +) : Task() { + override fun SafeContext.onStart() { + from.withdraw(selection).onSuccess { withdraw, _ -> + to.deposit(selection).onSuccess { _, _ -> + success(Unit) + }.start(withdraw) + }.start(this@ContainerTransfer) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt index 6b752b7ae..297648c7e 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt @@ -15,16 +15,17 @@ import net.minecraft.util.math.BlockPos // ToDo: Custom heuristic goals class GoalTask( - private val goal: Goal, + private val goal: () -> Goal, private val check: SafeContext.() -> Boolean = { false } ) : Task() { - override fun SafeContext.onStart() { - BaritoneUtils.setGoalAndPath(goal) - } - init { - listener { + listener { + val goal = goal() + + primary.customGoalProcess.goal = goal + primary.customGoalProcess.path() + if (goal.isInGoal(player.blockPos) || check()) { primary.customGoalProcess.goal = null success(Unit) @@ -34,37 +35,45 @@ class GoalTask( companion object { @Ta5kBuilder - fun moveToGoal(goal: Goal) = + fun moveToGoal(goal: () -> Goal) = GoalTask(goal) + @Ta5kBuilder + fun moveToGoalUntil(goal: () -> Goal, check: SafeContext.() -> Boolean) = + GoalTask(goal) + + @Ta5kBuilder + fun moveToGoal(goal: Goal) = + GoalTask({ goal }) + @Ta5kBuilder fun moveToGoalUntil(goal: Goal, check: SafeContext.() -> Boolean) = - GoalTask(goal, check) + GoalTask({ goal }, check) @Ta5kBuilder fun moveToBlock(blockPos: BlockPos) = - GoalTask(GoalBlock(blockPos)) + GoalTask({ GoalBlock(blockPos) }) @Ta5kBuilder fun moveNearBlock(blockPos: BlockPos, range: Int) = - GoalTask(GoalNear(blockPos, range)) + GoalTask({ GoalNear(blockPos, range) }) @Ta5kBuilder fun moveToBlockUntil(blockPos: BlockPos, check: SafeContext.() -> Boolean) = - GoalTask(GoalBlock(blockPos), check) + GoalTask({ GoalBlock(blockPos) }, check) @Ta5kBuilder fun moveToXY(blockPos: BlockPos) = - GoalTask(GoalXZ(blockPos.x, blockPos.z)) + GoalTask({ GoalXZ(blockPos.x, blockPos.z) }) @Ta5kBuilder fun moveUntilLoaded(blockPos: BlockPos) = - GoalTask(GoalBlock(blockPos)) { + GoalTask({ GoalBlock(blockPos) }) { world.isPosLoaded(blockPos.x, blockPos.z) } @Ta5kBuilder fun moveIntoEntityRange(blockPos: BlockPos, range: Int = 3) = - GoalTask(GoalNear(blockPos, range)) + GoalTask({ GoalNear(blockPos, range) }) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt index 8e5fe9648..3fcf01079 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt @@ -18,8 +18,6 @@ class HelloWorldTask @Ta5kBuilder constructor() : Task() { companion object { @Ta5kBuilder - fun Task<*>.helloWorld() = HelloWorldTask() - @Ta5kBuilder - fun SubTaskBuilder.helloWorld() = HelloWorldTask().also { tasks.add(it) } + fun helloWorld() = HelloWorldTask() } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt index 0dc70b91f..42ab9c0fc 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt @@ -1,10 +1,12 @@ package com.lambda.task.tasks +import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.material.StackSelection import com.lambda.task.Task -import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.item.ItemUtils.defaultDisposables +import com.lambda.util.player.SlotUtils.clickSlot import com.lambda.util.primitives.extension.containerSlots import com.lambda.util.primitives.extension.inventorySlots import net.minecraft.item.ItemStack @@ -14,43 +16,67 @@ import net.minecraft.screen.slot.SlotActionType class InventoryTask( val screen: H, + val selection: StackSelection, val from: List, + val to: List, private val closeScreen: Boolean = true ) : Task>() { private val moved = mutableListOf() + private val selectedFrom = selection.filterSlots(from).filter { it.hasStack() } + private val selectedTo = to.filter { !it.hasStack() } init { // ToDo: Needs smart code to move as efficient as possible. // Also should handle overflow etc. Should be more generic listener { - from.firstOrNull { it.hasStack() }?.let { - val preMove = it.stack.copy() - // ToDo: Quickmove will not work in most situations - screen.onSlotClick(it.index, 0, SlotActionType.QUICK_MOVE, player) - if (!it.stack.equal(preMove)) { - moved.add(preMove) + selectedFrom.firstOrNull()?.let { from -> + val preMove = from.stack.copy() + +// selectedTo.firstOrNull()?.let { to -> +// clickSlot(from.id, 0, SlotActionType.PICKUP) +// clickSlot(to.id, 0, SlotActionType.PICKUP) +// // ToDo: Handle overflow of cursor +// } + + // ToDo: SWAP triangle + val handler = player.currentScreenHandler + handler.inventorySlots.firstOrNull { + it.stack.item in defaultDisposables || it.stack.isEmpty + }?.let { emptySlot -> + clickSlot(emptySlot.id, 0, SlotActionType.SWAP) + clickSlot(from.id, 0, SlotActionType.SWAP) } -// screen.syncState() // ToDo: Needed? - } ?: run { - if (closeScreen) player.closeHandledScreen() - success(moved) + moved.add(preMove) + } ?: finish() + } + + listener { + if (selectedFrom.isEmpty() || moved.sumOf { it.count } >= selection.count) { + finish() } } } + private fun SafeContext.finish() { + if (closeScreen) player.closeHandledScreen() + success(moved) + } + companion object { + @Ta5kBuilder + inline fun moveItems( + screen: H, + selection: StackSelection, + from: List, + to: List, + ) = InventoryTask(screen, selection, from, to) + @Ta5kBuilder inline fun withdraw(screen: H, selection: StackSelection) = - InventoryTask( - screen, - selection.filterSlots(screen.containerSlots) - ) + moveItems(screen, selection, screen.containerSlots, screen.inventorySlots) @Ta5kBuilder inline fun deposit(screen: H, selection: StackSelection) = - InventoryTask( - screen, - selection.filterSlots(screen.inventorySlots) - ) + moveItems(screen, selection, screen.inventorySlots, screen.containerSlots) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt index 7129c925a..5ba0651e7 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -38,7 +38,9 @@ class OpenContainer( listener { slotsLoaded = true - screenHandler?.let { success(it) } + screenHandler?.let { + success(it) + } } listener { event -> diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 9e5934a61..905febf1d 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -3,8 +3,10 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.RotationEvent +import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info @@ -14,11 +16,12 @@ class PlaceBlock @Ta5kBuilder constructor( private val ctx: PlaceContext, private val swingHand: Boolean = true, private val rotate: Boolean = true, - private val waitForConfirmation: Boolean = false, + private val waitForConfirmation: Boolean = true, ) : Task() { private var beginState: BlockState? = null private val SafeContext.resultingState: BlockState get() = ctx.resultingPos.blockState(world) private val SafeContext.matches get() = ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) + override var cooldown = TaskFlow.build.placeCooldown override fun SafeContext.onStart() { if (matches) { @@ -41,14 +44,16 @@ class PlaceBlock @Ta5kBuilder constructor( placeBlock() } + listener { + if (matches) finish() + } + // listener { // if (matches) success(Unit) // } } private fun SafeContext.placeBlock() { - val preStack = player.getStackInHand(ctx.hand).copy() - val actionResult = interaction.interactBlock( player, ctx.hand, @@ -65,25 +70,29 @@ class PlaceBlock @Ta5kBuilder constructor( } if (!waitForConfirmation && matches) { - LOG.info("Placed $preStack at ${ - ctx.result.blockPos.toShortString() - } (${ctx.result.side}) with expecting state ${ - ctx.expectedState - } and expecting position at ${ctx.resultingPos.toShortString()}") - success(Unit) + finish() } } else { info("Internal interaction failed with $actionResult") } } + private fun SafeContext.finish() { + LOG.info("Placed at ${ + ctx.result.blockPos.toShortString() + } (${ctx.result.side}) with expecting state ${ + ctx.expectedState + } and expecting position at ${ctx.resultingPos.toShortString()}") + success(Unit) + } + companion object { @Ta5kBuilder fun placeBlock( ctx: PlaceContext, swingHand: Boolean = true, rotate: Boolean = true, - waitForConfirmation: Boolean = false, + waitForConfirmation: Boolean = true, ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/collections/Cacheable.kt b/common/src/main/kotlin/com/lambda/util/collections/Cacheable.kt new file mode 100644 index 000000000..8f68e40cb --- /dev/null +++ b/common/src/main/kotlin/com/lambda/util/collections/Cacheable.kt @@ -0,0 +1,14 @@ +package com.lambda.util.collections + +import kotlin.reflect.KProperty + +class Cacheable private constructor(private val getter: (K) -> V) { + private val cache = mutableMapOf() + + operator fun getValue(thisRef: K, property: KProperty<*>) = + cache.getOrPut(thisRef) { getter(thisRef) } + + companion object { + fun cacheable(getter: (K) -> V) = Cacheable(getter) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt index 0049b6d18..aa15c663a 100644 --- a/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt @@ -1,5 +1,6 @@ package com.lambda.util.item +import com.lambda.util.collections.Cacheable.Companion.cacheable import net.minecraft.inventory.Inventories import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack @@ -39,14 +40,15 @@ object ItemStackUtils { return listOf(copyWithCount(maxCount), copyWithCount(remainder)) } - val ItemStack.shulkerBoxContents: List get() = - BlockItem.getBlockEntityNbt(this)?.takeIf { + val ItemStack.shulkerBoxContents: List by cacheable { stack -> + BlockItem.getBlockEntityNbt(stack)?.takeIf { it.contains("Items", NbtElement.LIST_TYPE.toInt()) }?.let { val list = DefaultedList.ofSize(27, ItemStack.EMPTY) Inventories.readNbt(it, list) list } ?: emptyList() + } /** * Checks if the given item stacks are equal, including the item count and NBT. diff --git a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt index a1902c5c8..ce1a94dd7 100644 --- a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -10,7 +10,7 @@ object SlotUtils { val ClientPlayerEntity.storage: List get() = inventory.main.subList(9, 36) val ClientPlayerEntity.hotbarAndStorage: List get() = inventory.main.subList(0, 36) val ClientPlayerEntity.combined: List get() = inventory.main + inventory.armor + inventory.offHand - val ClientPlayerEntity.offhand: ItemStack get() = inventory.offHand.first() + val ClientPlayerEntity.offhand: ItemStack get() = offHandStack fun SafeContext.clickSlot( slotId: Int, From a854f4097b18b8107857a271a69e40986c0f0a97 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 03:18:17 +0200 Subject: [PATCH 40/47] Fix creative related issues and more clear transfer command --- .../lambda/command/commands/TransferCommand.kt | 15 +++++++++++---- .../com/lambda/config/groups/RotationSettings.kt | 4 ++-- .../construction/simulation/BuildSimulator.kt | 2 +- .../interaction/material/MaterialContainer.kt | 2 +- .../material/container/CreativeContainer.kt | 3 +++ .../material/transfer/TransferResult.kt | 2 +- .../lambda/module/modules/player/HighwayTools.kt | 2 ++ .../kotlin/com/lambda/task/tasks/BreakBlock.kt | 1 + .../src/main/kotlin/com/lambda/util/BlockUtils.kt | 3 +-- common/src/main/resources/lambda.accesswidener | 3 ++- 10 files changed, 25 insertions(+), 12 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 68ebed04d..f134fbb46 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -30,7 +30,9 @@ object TransferCommand : LambdaCommand( isItem(stack(ctx).value().item) } containerMatchSelection(selection).forEach { - builder.suggest("\"${it.name} with ${it.available(selection)}\"") + val available = it.available(selection) + val availableMsg = if (available == Int.MAX_VALUE) "∞" else available.toString() + builder.suggest("\"${it.name} with $availableMsg\"") } builder.buildFuture() } @@ -41,7 +43,8 @@ object TransferCommand : LambdaCommand( } ContainerManager.container().forEach { val space = it.spaceLeft(selection) - if (space > 0) builder.suggest("\"${it.name} with $space space left\"") + val spaceMsg = if (space == Int.MAX_VALUE) "∞" else space.toString() + if (space > 0) builder.suggest("\"${it.name} with $spaceMsg space left\"") } builder.buildFuture() } @@ -59,9 +62,11 @@ object TransferCommand : LambdaCommand( when (val result = fromContainer.transfer(selection, toContainer)) { is TransferResult.Success -> { - info("Transferring $selection from ${fromContainer.name} to ${toContainer.name}") + info("$result started.") lastTransfer = result - result.solve.start(null) + result.solve.onSuccess { _, _ -> + info("$lastTransfer completed.") + }.start(null) return@executeWithResult success() } is TransferResult.MissingItems -> { @@ -84,6 +89,7 @@ object TransferCommand : LambdaCommand( lastTransfer?.solve?.cancel() ?: run { return@executeWithResult failure("No transfer to cancel") } + info("$lastTransfer cancelled") lastTransfer = null success() } @@ -94,6 +100,7 @@ object TransferCommand : LambdaCommand( lastTransfer?.undo ?: run { return@executeWithResult failure("No transfer to undo") } + info("Undoing $lastTransfer") success() } } diff --git a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt index 13dccb38a..e464bca50 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/RotationSettings.kt @@ -13,8 +13,8 @@ class RotationSettings( override val keepTicks by c.setting("Keep Rotation", 3, 1..10, 1, "Ticks to keep rotation", " ticks", vis) override val resetTicks by c.setting("Reset Rotation", 3, 1..10, 1, "Ticks before rotation is reset", " ticks", vis) override var instant by c.setting("Instant Rotation", true, "Instantly rotate", vis) - override var mean by c.setting("Mean", 20.0, 1.0..80.0, 0.1, "Average rotation speed") { vis() && !instant } - override var derivation by c.setting("Standard Deviation", 5.0, 0.0..20.0, 0.1, "Spread of rotation speeds") { vis() && !instant } + override var mean by c.setting("Mean", 20.0, 1.0..80.0, 0.1, "Average rotation speed", unit = "°") { vis() && !instant } + override var derivation by c.setting("Standard Deviation", 5.0, 0.0..20.0, 0.1, "Spread of rotation speeds", unit = "°") { vis() && !instant } override val turnSpeed get() = abs(nextGaussian(mean, derivation)) diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 2abdbc2e6..77b7505a5 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -397,7 +397,7 @@ object BuildSimulator { ) /* player has a better tool for the job available */ - findBestAvailableTool(state)?.let { bestTool -> + if (!player.isCreative) findBestAvailableTool(state)?.let { bestTool -> Hand.entries.firstOrNull { val stack = player.getStackInHand(it) stack.item == bestTool diff --git a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt index d90ebde78..f268250ec 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -76,11 +76,11 @@ abstract class MaterialContainer( } enum class Rank { - CREATIVE, MAIN_HAND, OFF_HAND, HOTBAR, INVENTORY, + CREATIVE, SHULKER_BOX, ENDER_CHEST, CHEST, diff --git a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt index 5db705067..abf90f900 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/container/CreativeContainer.kt @@ -1,6 +1,7 @@ package com.lambda.interaction.material.container import com.lambda.Lambda.mc +import com.lambda.interaction.construction.result.ComparableResult import com.lambda.interaction.material.MaterialContainer import com.lambda.interaction.material.StackSelection import com.lambda.task.Task.Companion.buildTask @@ -14,6 +15,8 @@ data object CreativeContainer : MaterialContainer(Rank.CREATIVE) { override fun available(selection: StackSelection): Int = if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 + override fun spaceLeft(selection: StackSelection) = Int.MAX_VALUE + override fun deposit(selection: StackSelection) = buildTask("CreativeDeposit") { if (!player.isCreative) { // ToDo: Maybe switch gamemode? diff --git a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt index f2baef0c6..23bd5a5a8 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt @@ -17,7 +17,7 @@ abstract class TransferResult { override val solve = ContainerTransfer(selection, from, to) val undo = ContainerTransfer(selection, to, from) - override fun toString() = "Transfer of [$selection] from [$from] to [$to]" + override fun toString() = "Transfer of [$selection] from [${from.name}] to [${to.name}]" } data object NoSpace : TransferResult() { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 87329202b..9e7c1604f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -7,6 +7,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.util.BaritoneUtils import com.lambda.util.BaritoneUtils.primary import com.lambda.util.Communication.info import com.lambda.util.KeyCode @@ -51,6 +52,7 @@ object HighwayTools : Module( runningTask?.cancel() runningTask = null distanceMoved = 0 + BaritoneUtils.cancel() } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 175793faf..652d8db1b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -98,6 +98,7 @@ class BreakBlock @Ta5kBuilder constructor( private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { + if (player.isCreative) interaction.blockBreakingCooldown = 0 if (swingHand) player.swingHand(ctx.hand) } } diff --git a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt index cc128ab80..581e60c7a 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -102,8 +102,7 @@ object BlockUtils { fun BlockPos.blockEntity(world: ClientWorld) = world.getBlockEntity(this) fun SafeContext.instantBreakable(blockState: BlockState, blockPos: BlockPos): Boolean { val ticksNeeded = 1 / blockState.calcBlockBreakingDelta(player, world, blockPos) -// info("State: $blockState Ticks to break: $ticksNeeded") - return ticksNeeded <= 1 && ticksNeeded != 0f + return (ticksNeeded <= 1 && ticksNeeded != 0f) || player.isCreative } val Vec3i.blockPos: BlockPos get() = BlockPos(this) val Block.item: Item get() = asItem() diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index fb789b631..79683133f 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -48,4 +48,5 @@ accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPa # Other accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; -accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; \ No newline at end of file +accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; +accessible field net/minecraft/client/network/ClientPlayerInteractionManager blockBreakingCooldown I \ No newline at end of file From c3dbc56728f2b0e5642c9a0d75adba78ff4e13c9 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 04:16:21 +0200 Subject: [PATCH 41/47] Dont override cancelled and correct place break cooldown unit --- .../src/main/kotlin/com/lambda/config/groups/BuildSettings.kt | 4 ++-- common/src/main/kotlin/com/lambda/task/Task.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index d14bb2b20..5387e1f66 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -6,8 +6,8 @@ class BuildSettings( c: Configurable, vis: () -> Boolean = { true } ) : BuildConfig { - override val breakCoolDown by c.setting("Break Cooldown", 0, 0..20, 1, "Delay between breaking blocks", " ticks", vis) - override val placeCooldown by c.setting("Place Cooldown", 0, 0..20, 1, "Delay between placing blocks", " ticks", vis) + override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms", vis) + override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms", vis) override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis) override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", vis) override val pathing by c.setting("Pathing", true, "Path to blocks", vis) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index b22a39934..b8eacd38a 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -176,7 +176,7 @@ abstract class Task : Nameable { runConcurrent { delay(cooldown.toLong()) runGameScheduled { - finish(result) + if (isRunning) finish(result) } } } else finish(result) From 0e0880e375fa3444d365d7d87fb0b430188decd4 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 04:17:35 +0200 Subject: [PATCH 42/47] Fix cooldown --- common/src/main/kotlin/com/lambda/task/Task.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index b8eacd38a..fb7320879 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -176,7 +176,7 @@ abstract class Task : Nameable { runConcurrent { delay(cooldown.toLong()) runGameScheduled { - if (isRunning) finish(result) + if (state == State.COOLDOWN) finish(result) } } } else finish(result) From 9fc5ebe7bb124deb082231d2c18bc67409c82595 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 21:58:49 +0200 Subject: [PATCH 43/47] Pathing strategies and more options --- .../com/lambda/config/groups/BuildConfig.kt | 3 + .../com/lambda/config/groups/BuildSettings.kt | 3 + .../construction/result/BreakResult.kt | 9 +-- .../construction/result/BuildResult.kt | 21 +++--- .../construction/result/Navigable.kt | 6 +- .../construction/result/PlaceResult.kt | 16 ++--- .../interaction/construction/result/Rank.kt | 10 +-- .../construction/simulation/BuildSimulator.kt | 4 +- .../lambda/module/modules/client/TaskFlow.kt | 8 +-- .../module/modules/player/HighwayTools.kt | 7 +- .../src/main/kotlin/com/lambda/task/Task.kt | 1 - .../com/lambda/task/tasks/BreakBlock.kt | 3 +- .../com/lambda/task/tasks/BuildStructure.kt | 70 +++++++------------ .../com/lambda/task/tasks/PlaceBlock.kt | 49 ++++++++----- 14 files changed, 98 insertions(+), 112 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 37099d353..37979863e 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -3,10 +3,13 @@ package com.lambda.config.groups interface BuildConfig { val breakCoolDown: Int val placeCooldown: Int + val placeConfirmation: Boolean + val breakConfirmation: Boolean val collectDrops: Boolean val breakWeakBlocks: Boolean val pathing: Boolean val breaksPerTick: Int val rotateForBreak: Boolean + val rotateForPlace: Boolean val swingHand: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 5387e1f66..d010e53da 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -8,10 +8,13 @@ class BuildSettings( ) : BuildConfig { override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms", vis) override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms", vis) + override val placeConfirmation by c.setting("Place Confirmation", false, "Wait for block placement confirmation", vis) + override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation", vis) override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis) override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", vis) override val pathing by c.setting("Pathing", true, "Path to blocks", vis) override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick", "", vis) override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", vis) + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", vis) override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt index a86973cd5..c457cd560 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BreakResult.kt @@ -117,14 +117,9 @@ sealed class BreakResult : BuildResult() { data class PlayerOnTop( override val blockPos: BlockPos, val blockState: BlockState - ) : Navigable, Resolvable, BreakResult() { + ) : Navigable, BreakResult() { override val rank = Rank.BREAK_PLAYER_ON_TOP - override val resolve get() = - moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { - val pBox = player.boundingBox - val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ) - world.findSupportingBlockPos(player, aabb).orElse(null) != blockPos - } + override val goal = GoalInverted(GoalBlock(blockPos)) } } diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt index ec4aae547..43e1d8e88 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt @@ -1,16 +1,13 @@ package com.lambda.interaction.construction.result +import baritone.api.pathing.goals.GoalBlock +import baritone.api.pathing.goals.GoalNear import baritone.process.BuilderProcess.GoalPlace import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.material.ContainerManager.transfer import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.interaction.material.container.MainHandContainer -import com.lambda.task.Task -import com.lambda.task.Task.Companion.emptyTask import com.lambda.task.Task.Companion.failTask -import com.lambda.task.tasks.GoalTask.Companion.moveNearBlock -import com.lambda.task.tasks.GoalTask.Companion.moveToGoal -import com.lambda.task.tasks.GoalTask.Companion.moveUntilLoaded import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.item.ItemStack @@ -45,10 +42,10 @@ abstract class BuildResult : ComparableResult { */ data class ChunkNotLoaded( override val blockPos: BlockPos - ) : Navigable, Resolvable, BuildResult() { + ) : Navigable, BuildResult() { override val rank = Rank.CHUNK_NOT_LOADED - override val resolve get() = moveUntilLoaded(blockPos) + override val goal = GoalBlock(blockPos) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -112,12 +109,10 @@ abstract class BuildResult : ComparableResult { val hitPos: BlockPos, val side: Direction, val distance: Double - ) : Navigable, Resolvable, BuildResult() { + ) : Navigable, BuildResult() { override val rank = Rank.NOT_VISIBLE - override val resolve get() = moveToGoal { - GoalPlace(blockPos) - } + override val goal = GoalPlace(blockPos) override fun compareTo(other: ComparableResult): Int { return when (other) { @@ -186,14 +181,14 @@ abstract class BuildResult : ComparableResult { val hitVec: Vec3d, val reach: Double, val side: Direction, - ) : Navigable, Resolvable, BuildResult() { + ) : Navigable, BuildResult() { override val rank = Rank.OUT_OF_REACH val distance: Double by lazy { startVec.distanceTo(hitVec) } - override val resolve get() = moveNearBlock(blockPos, 2) + override val goal = GoalNear(blockPos, 2) override fun compareTo(other: ComparableResult): Int { return when (other) { diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt index 729da4bf6..0a075b2fd 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Navigable.kt @@ -1,3 +1,7 @@ package com.lambda.interaction.construction.result -interface Navigable \ No newline at end of file +import baritone.api.pathing.goals.Goal + +interface Navigable { + val goal: Goal +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt index 24d127a01..9847ba568 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/PlaceResult.kt @@ -1,5 +1,6 @@ package com.lambda.interaction.construction.result +import baritone.api.pathing.goals.Goal import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted import com.lambda.interaction.construction.context.PlaceContext @@ -59,12 +60,10 @@ sealed class PlaceResult : BuildResult() { data class BlockedByPlayer( override val blockPos: BlockPos - ) : Navigable, Resolvable, PlaceResult() { + ) : Navigable, PlaceResult() { override val rank = Rank.PLACE_BLOCKED_BY_PLAYER - override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { - !world.canCollide(player, Box(blockPos)) - } + override val goal = GoalInverted(GoalBlock(blockPos)) } /** @@ -74,13 +73,10 @@ sealed class PlaceResult : BuildResult() { data class CantReplace( override val blockPos: BlockPos, val simulated: ItemPlacementContext - ) : Navigable, Resolvable, PlaceResult() { + ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE -// override val resolve = breakBlock(simulated.blockPos) - override val resolve get() = moveToGoalUntil(GoalInverted(GoalBlock(blockPos))) { - !world.canCollide(player, Box(blockPos)) - } + override val resolve = breakBlock(blockPos) } /** @@ -123,6 +119,6 @@ sealed class PlaceResult : BuildResult() { ) : Resolvable, PlaceResult() { override val rank = Rank.PLACE_NOT_ITEM_BLOCK - override val resolve get() = Task.emptyTask() // ToDo: analyze interaction with non-block items + override val resolve get() = TODO("Not expected") } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt index b20cae5f2..795d0d573 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt @@ -10,17 +10,17 @@ enum class Rank { NOT_VISIBLE, OUT_OF_REACH, BREAK_NOT_EXPOSED, - OUT_OF_WORLD, CHUNK_NOT_LOADED, - PLACE_NO_INTEGRITY, PLACE_CANT_REPLACE, - PLACE_NOT_ITEM_BLOCK, - BREAK_SUBMERGE, - BREAK_IS_BLOCKED_BY_LIQUID, BREAK_PLAYER_ON_TOP, + PLACE_NOT_ITEM_BLOCK, // not solvable + OUT_OF_WORLD, BREAK_RESTRICTED, + PLACE_NO_INTEGRITY, + BREAK_SUBMERGE, + BREAK_IS_BLOCKED_BY_LIQUID, UNBREAKABLE, BREAK_NO_PERMISSION, PLACE_SCAFFOLD_EXCEEDED, diff --git a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt index 77b7505a5..50ffd268e 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt @@ -126,10 +126,10 @@ object BuildSimulator { acc.add(BuildResult.OutOfReach(pos, eye, hitPos.vecOf(hitSide), interact.reach, hitSide)) return@forEach } + val verify: HitResult.() -> Boolean = { blockResult?.blockPos == hitPos && blockResult?.side == hitSide } - val validHits = mutableMapOf() val reachSq = interact.reach.pow(2) @@ -251,7 +251,7 @@ object BuildSimulator { val currentHandStack = player.getStackInHand(Hand.MAIN_HAND) if (target is TargetState.Stack && !target.itemStack.equal(currentHandStack)) { - acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack)) + acc.add(BuildResult.WrongStack(pos, placeContext, target.copy)) return@forEach } diff --git a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt index b42f903e0..4e336bde6 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/client/TaskFlow.kt @@ -14,13 +14,10 @@ object TaskFlow : Module( defaultTags = setOf(ModuleTag.CLIENT, ModuleTag.AUTOMATION) ) { enum class Page { - TASKS, BUILD, ROTATION, INTERACTION + BUILD, ROTATION, INTERACTION, TASKS } private val page by setting("Page", Page.BUILD) - val taskCooldown by setting("Task Cooldown", 0, 0..10000, 10, " ms") { - page == Page.TASKS - } val build = BuildSettings(this) { page == Page.BUILD } @@ -30,6 +27,9 @@ object TaskFlow : Module( val interact = InteractionSettings(this) { page == Page.INTERACTION } + val taskCooldown by setting("Task Cooldown", 0, 0..10000, 10, unit = " ms") { + page == Page.TASKS + } // val disposables by setting("Disposables", ItemUtils.defaultDisposables) val ignoredBlocks = mutableSetOf().apply { addAll(allSigns) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 9e7c1604f..e8849125b 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -1,6 +1,5 @@ package com.lambda.module.modules.player -import baritone.api.pathing.goals.GoalNear import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.Module @@ -8,9 +7,7 @@ import com.lambda.module.tag.ModuleTag import com.lambda.task.Task import com.lambda.task.tasks.BuildStructure.Companion.buildStructure import com.lambda.util.BaritoneUtils -import com.lambda.util.BaritoneUtils.primary import com.lambda.util.Communication.info -import com.lambda.util.KeyCode import com.lambda.util.player.MovementUtils.octant import com.lambda.util.primitives.extension.Structure import com.lambda.util.world.StructureUtils.generateDirectionalTube @@ -67,11 +64,9 @@ object HighwayTools : Module( structure += slice.map { it.key.add(currentPos) to it.value } } - buildStructure { + runningTask = buildStructure { structure.toBlueprint() }.apply { - runningTask = this - primary.customGoalProcess.setGoalAndPath(GoalNear(currentPos, sliceSize)) onSuccess { _, _ -> if (distanceMoved < distance || distance < 0) { buildSlice() diff --git a/common/src/main/kotlin/com/lambda/task/Task.kt b/common/src/main/kotlin/com/lambda/task/Task.kt index fb7320879..3b1be0c76 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -373,7 +373,6 @@ abstract class Task : Nameable { @Ta5kBuilder fun onSuccess(action: SafeContext.(Task, Result) -> Unit): Task { this.onSuccess = action - LOG.info("Success action $action set for $identifier") return this } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 652d8db1b..f08f8711b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -35,7 +35,8 @@ class BreakBlock @Ta5kBuilder constructor( val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null val SafeContext.state: BlockState get() = blockPos.blockState(world) - override var cooldown = TaskFlow.build.breakCoolDown + override var cooldown = Int.MAX_VALUE + get() = maxOf(TaskFlow.build.breakCoolDown, TaskFlow.taskCooldown) override fun SafeContext.onStart() { parent?.let { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 4c2e01806..500ac9fbc 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,47 +1,28 @@ package com.lambda.task.tasks +import baritone.api.pathing.goals.Goal +import baritone.api.pathing.goals.GoalNear import com.lambda.Lambda.LOG +import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.interaction.RotationManager import com.lambda.interaction.construction.Blueprint import com.lambda.interaction.construction.Blueprint.Companion.toStructure import com.lambda.interaction.construction.DynamicBlueprint import com.lambda.interaction.construction.StaticBlueprint.Companion.toBlueprint -import com.lambda.interaction.construction.context.BreakContext -import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.construction.result.* -import com.lambda.interaction.construction.simulation.BuildSimulator import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState -import com.lambda.interaction.material.ContainerManager.findBestAvailableTool -import com.lambda.interaction.rotation.Rotation.Companion.rotationTo -import com.lambda.interaction.rotation.RotationContext -import com.lambda.interaction.visibilty.VisibilityChecker.mostCenter -import com.lambda.interaction.visibilty.VisibilityChecker.scanVisibleSurfaces import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.util.BlockUtils -import com.lambda.util.BlockUtils.blockState -import com.lambda.util.BlockUtils.instantBreakable -import com.lambda.util.BlockUtils.vecOf -import com.lambda.util.math.VecUtils.distSq -import com.lambda.util.world.raycast.RayCastUtils.blockResult -import net.minecraft.block.OperatorBlock -import net.minecraft.block.pattern.CachedBlockPosition -import net.minecraft.item.BlockItem -import net.minecraft.item.ItemPlacementContext -import net.minecraft.item.ItemUsageContext -import net.minecraft.registry.RegistryKeys -import net.minecraft.util.Hand -import net.minecraft.util.hit.BlockHitResult -import net.minecraft.util.hit.HitResult +import com.lambda.threading.runConcurrent +import com.lambda.threading.runGameScheduled +import com.lambda.util.BaritoneUtils +import net.minecraft.block.BlockState +import net.minecraft.util.math.BlockBox import net.minecraft.util.math.BlockPos -import net.minecraft.util.math.Box -import net.minecraft.util.math.Direction -import net.minecraft.util.math.Vec3d -import kotlin.math.pow +import net.minecraft.world.chunk.WorldChunk class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, @@ -50,7 +31,10 @@ class BuildStructure @Ta5kBuilder constructor( val collectDrops: Boolean = TaskFlow.build.collectDrops, private val cancelOnUnsolvable: Boolean = true, ) : Task() { - private var lastTask: Task<*>? = null + + abstract class PathingStrategy { + + } override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create(this) @@ -76,34 +60,34 @@ class BuildStructure @Ta5kBuilder constructor( instantResults.forEach { it.resolve.start(this@BuildStructure, pauseParent = false) } - lastTask = instantResults.last().resolve return@listener } results.minOrNull()?.let { result -> - if (!pathing && result is Navigable) return@let + when (result) { + is BuildResult.Done -> checkDone() + is Resolvable -> { + LOG.info("Resolving: $result") + result.resolve.start(this@BuildStructure) + if (pathing) { + BaritoneUtils.setGoalAndPath(GoalNear(result.blockPos, 2)) + } + } + is Navigable -> { + if (pathing) BaritoneUtils.setGoalAndPath(result.goal) + } + else -> { + if (!cancelOnUnsolvable) return@listener - if (result !is Resolvable) { - if (result is BuildResult.Done) { - checkDone() - } else if (cancelOnUnsolvable) { failure("Failed to resolve build result: $result") - return@listener } - return@listener } - lastTask = result.resolve - - LOG.info("Resolving: $result") - result.resolve.start(this@BuildStructure) } } } private fun SafeContext.checkDone() { if (!finishOnDone) return - -// cancelSubTasks() success(Unit) return } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 905febf1d..1fd3e5781 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -3,7 +3,7 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.RotationEvent -import com.lambda.event.events.TickEvent +import com.lambda.event.events.WorldEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.interaction.construction.context.PlaceContext import com.lambda.module.modules.client.TaskFlow @@ -14,43 +14,53 @@ import net.minecraft.block.BlockState class PlaceBlock @Ta5kBuilder constructor( private val ctx: PlaceContext, - private val swingHand: Boolean = true, - private val rotate: Boolean = true, - private val waitForConfirmation: Boolean = true, + private val swingHand: Boolean, + private val rotate: Boolean, + private val waitForConfirmation: Boolean, ) : Task() { private var beginState: BlockState? = null - private val SafeContext.resultingState: BlockState get() = ctx.resultingPos.blockState(world) - private val SafeContext.matches get() = ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) - override var cooldown = TaskFlow.build.placeCooldown + override var cooldown = Int.MAX_VALUE + get() = maxOf(TaskFlow.build.placeCooldown, TaskFlow.taskCooldown) + override var timeout = 20 + private var placed = false + + private val SafeContext.resultingState: BlockState get() = + ctx.resultingPos.blockState(world) + private val SafeContext.matches get() = + ctx.targetState.matches(ctx.resultingPos.blockState(world), ctx.resultingPos, world) override fun SafeContext.onStart() { if (matches) { - success(Unit) + finish() return } beginState = resultingState + + if (!rotate) placeBlock() } init { listener { event -> + if (placed) return@listener if (!rotate) return@listener event.context = ctx.rotation } listener { + if (placed) return@listener if (!rotate) return@listener if (!it.context.isValid) return@listener placeBlock() } - listener { - if (matches) finish() - } + listener { + if (it.pos != ctx.resultingPos) return@listener -// listener { -// if (matches) success(Unit) -// } + if (ctx.targetState.matches(it.state, it.pos, world)) { + finish() + } + } } private fun SafeContext.placeBlock() { @@ -69,8 +79,9 @@ class PlaceBlock @Ta5kBuilder constructor( mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) } - if (!waitForConfirmation && matches) { - finish() + if (matches) { + placed = true + if (!waitForConfirmation) finish() } } else { info("Internal interaction failed with $actionResult") @@ -90,9 +101,9 @@ class PlaceBlock @Ta5kBuilder constructor( @Ta5kBuilder fun placeBlock( ctx: PlaceContext, - swingHand: Boolean = true, - rotate: Boolean = true, - waitForConfirmation: Boolean = true, + swingHand: Boolean = TaskFlow.build.swingHand, + rotate: Boolean = TaskFlow.build.rotateForPlace, + waitForConfirmation: Boolean = TaskFlow.build.placeConfirmation, ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) } } \ No newline at end of file From c50ad7bfde83975988a7c2b10a76b9bbd4a688f7 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 22:21:03 +0200 Subject: [PATCH 44/47] Advanced break place settings --- .../com/lambda/config/groups/BuildConfig.kt | 3 +- .../com/lambda/config/groups/BuildSettings.kt | 49 ++++++++++++++----- .../lambda/config/groups/InteractionConfig.kt | 1 + .../config/groups/InteractionSettings.kt | 1 + .../com/lambda/task/tasks/BreakBlock.kt | 20 ++++---- .../com/lambda/task/tasks/PlaceBlock.kt | 2 +- 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 37979863e..3c86835ac 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -3,13 +3,12 @@ package com.lambda.config.groups interface BuildConfig { val breakCoolDown: Int val placeCooldown: Int - val placeConfirmation: Boolean val breakConfirmation: Boolean + val placeConfirmation: Boolean val collectDrops: Boolean val breakWeakBlocks: Boolean val pathing: Boolean val breaksPerTick: Int val rotateForBreak: Boolean val rotateForPlace: Boolean - val swingHand: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index d010e53da..cc35d34c4 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -6,15 +6,42 @@ class BuildSettings( c: Configurable, vis: () -> Boolean = { true } ) : BuildConfig { - override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms", vis) - override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms", vis) - override val placeConfirmation by c.setting("Place Confirmation", false, "Wait for block placement confirmation", vis) - override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation", vis) - override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks", vis) - override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)", vis) - override val pathing by c.setting("Pathing", true, "Path to blocks", vis) - override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick", "", vis) - override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking", vis) - override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing", vis) - override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) + enum class Page { + BREAK, PLACE, PATHING + } + + val page by c.setting("Build Page", Page.BREAK, "Current page", vis) + + override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms") { + vis() && page == Page.BREAK + } + override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { + vis() && page == Page.BREAK + } + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { + vis() && page == Page.BREAK + } + override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick") { + vis() && page == Page.BREAK + } + override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { + vis() && page == Page.BREAK + } + + override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms") { + vis() && page == Page.PLACE + } + override val placeConfirmation by c.setting("Place Confirmation", false, "Wait for block placement confirmation") { + vis() && page == Page.PLACE + } + override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { + vis() && page == Page.PLACE + } + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { + vis() && page == Page.PLACE + } + + override val pathing by c.setting("Pathing", true, "Path to blocks") { + vis() && page == Page.PATHING + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt index 3514dfe28..919478485 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -12,4 +12,5 @@ interface InteractionConfig { val resolution: Int val useRayCast: Boolean + val swingHand: Boolean } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt index b52f9d9e5..1ddb53ea6 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -10,4 +10,5 @@ class InteractionSettings( override val reach by c.setting("Reach", 4.9, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) override val useRayCast by c.setting("Raycast", false, "Verify hit vector with ray casting (for very strict ACs)", vis) override val resolution by c.setting("Resolution", 5, 1..20, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } + override val swingHand by c.setting("Swing Hand", true, "Swing hand on interactions", vis) } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index f08f8711b..17d2df206 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -25,12 +25,12 @@ import net.minecraft.util.math.Direction class BreakBlock @Ta5kBuilder constructor( private val ctx: BreakContext, - private val rotationConfig: IRotationConfig = TaskFlow.rotation, - private val interactionConfig: InteractionConfig = TaskFlow.interact, - private val sides: Set = emptySet(), - private var collectDrop: Boolean = TaskFlow.build.collectDrops, - private val rotate: Boolean = TaskFlow.build.rotateForBreak, - private val swingHand: Boolean = TaskFlow.build.swingHand, + private val rotationConfig: IRotationConfig, + private val interactionConfig: InteractionConfig, + private val sides: Set, + private var collectDrop: Boolean, + private val rotate: Boolean, + private val swingHand: Boolean, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos private var beginState: BlockState? = null @@ -111,15 +111,17 @@ class BreakBlock @Ta5kBuilder constructor( rotationConfig: IRotationConfig = TaskFlow.rotation, interactionConfig: InteractionConfig = TaskFlow.interact, sides: Set = emptySet(), - collectDrop: Boolean = false, - rotate: Boolean = false, + collectDrop: Boolean = TaskFlow.build.collectDrops, + rotate: Boolean = TaskFlow.build.rotateForBreak, + swingHand: Boolean = TaskFlow.interact.swingHand, ) = BreakBlock( ctx, rotationConfig, interactionConfig, sides, collectDrop, - rotate + rotate, + swingHand ) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt index 1fd3e5781..80fb69945 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -101,7 +101,7 @@ class PlaceBlock @Ta5kBuilder constructor( @Ta5kBuilder fun placeBlock( ctx: PlaceContext, - swingHand: Boolean = TaskFlow.build.swingHand, + swingHand: Boolean = TaskFlow.interact.swingHand, rotate: Boolean = TaskFlow.build.rotateForPlace, waitForConfirmation: Boolean = TaskFlow.build.placeConfirmation, ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) From 3a577cfcfabf3ace751a35e953744c4892c503ed Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 22:52:36 +0200 Subject: [PATCH 45/47] Working drop collection --- .../module/modules/network/PacketLimiter.kt | 4 +-- .../com/lambda/task/tasks/BreakBlock.kt | 34 +++++++++++++++---- .../com/lambda/task/tasks/BuildStructure.kt | 7 ---- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt b/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt index 4a2006399..cc9dcb725 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/network/PacketLimiter.kt @@ -17,12 +17,12 @@ object PacketLimiter : Module( defaultTags = setOf(ModuleTag.NETWORK) ) { private var packetQueue = LimitedDecayQueue(99, 1000) - private val limit by setting("Limit", 99, 1..100, 1, "The maximum amount of packets to send per given time interval").apply { + private val limit by setting("Limit", 99, 1..100, 1, "The maximum amount of packets to send per given time interval", unit = " packets").apply { onValueChange { _, to -> packetQueue.setMaxSize(to) } } - private val interval by setting("Duration", 1000L, 1L..1000L, 50L, "The interval / duration in milliseconds to limit packets for").apply { + private val interval by setting("Duration", 1000L, 1L..1000L, 50L, "The interval / duration in milliseconds to limit packets for", unit = " ms").apply { onValueChange { _, to -> packetQueue.setInterval(to) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt index 17d2df206..6d91f1906 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -16,10 +16,17 @@ import com.lambda.task.tasks.GoalTask.Companion.moveToBlock import com.lambda.task.tasks.GoalTask.Companion.moveToBlockUntil import com.lambda.task.tasks.GoalTask.Companion.moveToGoal import com.lambda.task.tasks.GoalTask.Companion.moveToGoalUntil +import com.lambda.util.BaritoneUtils import com.lambda.util.BlockUtils.blockState +import com.lambda.util.BlockUtils.item +import com.lambda.util.item.ItemUtils.defaultDisposables +import com.lambda.util.player.SlotUtils.clickSlot +import com.lambda.util.player.SlotUtils.hotbarAndStorage +import com.lambda.util.primitives.extension.inventorySlots import com.lambda.util.world.raycast.RayCastUtils.blockResult import net.minecraft.block.BlockState import net.minecraft.entity.ItemEntity +import net.minecraft.screen.slot.SlotActionType import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -37,6 +44,7 @@ class BreakBlock @Ta5kBuilder constructor( val SafeContext.state: BlockState get() = blockPos.blockState(world) override var cooldown = Int.MAX_VALUE get() = maxOf(TaskFlow.build.breakCoolDown, TaskFlow.taskCooldown) + private var drop: ItemEntity? = null override fun SafeContext.onStart() { parent?.let { @@ -67,6 +75,23 @@ class BreakBlock @Ta5kBuilder constructor( } listener { + drop?.let { itemDrop -> + if (!world.entities.contains(itemDrop)) { + success(itemDrop) + return@listener + } + + if (player.hotbarAndStorage.none { it.isEmpty }) { + player.currentScreenHandler.inventorySlots.firstOrNull { + it.stack.item in defaultDisposables + }?.let { + clickSlot(it.index, 1, SlotActionType.THROW) + } + } + + BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) + } + if (finish()) { success(null) return@listener @@ -80,17 +105,12 @@ class BreakBlock @Ta5kBuilder constructor( listener { if (collectDrop + && drop == null && it.entity is ItemEntity && it.entity.pos.isInRange(blockPos.toCenterPos(), 1.0) // && it.entity.stack.item == beginState?.block?.item // ToDo: The item entities are all air?? ) { - moveToGoalUntil( - { GoalBlock(it.entity.blockPos) }, - { !world.entities.contains(it.entity) } - ).onSuccess { _, _ -> - success(it.entity) - }.start(this@BreakBlock) -// success(it.entity) + drop = it.entity } } } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt index 500ac9fbc..539baaf3c 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt @@ -1,9 +1,7 @@ package com.lambda.task.tasks -import baritone.api.pathing.goals.Goal import baritone.api.pathing.goals.GoalNear import com.lambda.Lambda.LOG -import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listener @@ -16,13 +14,8 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task -import com.lambda.threading.runConcurrent -import com.lambda.threading.runGameScheduled import com.lambda.util.BaritoneUtils -import net.minecraft.block.BlockState -import net.minecraft.util.math.BlockBox import net.minecraft.util.math.BlockPos -import net.minecraft.world.chunk.WorldChunk class BuildStructure @Ta5kBuilder constructor( private val blueprint: Blueprint, From d564d26985cfee92a6668c0bbb47d4bf88a39c55 Mon Sep 17 00:00:00 2001 From: Constructor Date: Mon, 10 Jun 2024 23:03:56 +0200 Subject: [PATCH 46/47] Corner support blocks --- .../com/lambda/config/groups/BuildSettings.kt | 40 +++++-------------- .../module/modules/player/HighwayTools.kt | 37 ++++++++--------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index cc35d34c4..eb900586f 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -12,36 +12,16 @@ class BuildSettings( val page by c.setting("Build Page", Page.BREAK, "Current page", vis) - override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms") { - vis() && page == Page.BREAK - } - override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { - vis() && page == Page.BREAK - } - override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { - vis() && page == Page.BREAK - } - override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick") { - vis() && page == Page.BREAK - } - override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { - vis() && page == Page.BREAK - } + override val breakCoolDown by c.setting("Break Cooldown", 0, 0..1000, 1, "Delay between breaking blocks", " ms") { vis() && page == Page.BREAK } + override val breakConfirmation by c.setting("Break Confirmation", false, "Wait for block break confirmation") { vis() && page == Page.BREAK } + override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() && page == Page.BREAK } + override val breaksPerTick by c.setting("Instant Breaks Per Tick", 10, 1..30, 1, "Maximum instant block breaks per tick") { vis() && page == Page.BREAK } + override val rotateForBreak by c.setting("Rotate For Break", false, "Rotate towards block while breaking") { vis() && page == Page.BREAK } - override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms") { - vis() && page == Page.PLACE - } - override val placeConfirmation by c.setting("Place Confirmation", false, "Wait for block placement confirmation") { - vis() && page == Page.PLACE - } - override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { - vis() && page == Page.PLACE - } - override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { - vis() && page == Page.PLACE - } + override val placeCooldown by c.setting("Place Cooldown", 0, 0..1000, 1, "Delay between placing blocks", " ms") { vis() && page == Page.PLACE } + override val placeConfirmation by c.setting("Place Confirmation", false, "Wait for block placement confirmation") { vis() && page == Page.PLACE } + override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { vis() && page == Page.PLACE } + override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() && page == Page.PLACE } - override val pathing by c.setting("Pathing", true, "Path to blocks") { - vis() && page == Page.PATHING - } + override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.PATHING } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index e8849125b..216264d14 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -13,6 +13,7 @@ import com.lambda.util.primitives.extension.Structure import com.lambda.util.world.StructureUtils.generateDirectionalTube import net.minecraft.block.Blocks import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction import net.minecraft.util.math.EightWayDirection import net.minecraft.util.math.Vec3i import kotlin.math.roundToInt @@ -139,26 +140,26 @@ object HighwayTools : Module( -center, -1, ) - - // Remove the left corner -// structure += generateDirectionalTube( -// orthogonal, -// 1, -// 1, -// -center + width - 1, -// -1, -// ).associateWith { TargetState.Support(Direction.UP) } - - // Remove the right corner -// structure += generateDirectionalTube( -// orthogonal, -// 1, -// 1, -// -center, -// -1, -// ).associateWith { TargetState.Support(Direction.UP) } } + // Support for the left corner + structure += generateDirectionalTube( + orthogonal, + 1, + 1, + -center + width - 1, + -1, + ).associateWith { TargetState.Solid } + + // Support for the right corner + structure += generateDirectionalTube( + orthogonal, + 1, + 1, + -center, + -1, + ).associateWith { TargetState.Solid } + return structure } } \ No newline at end of file From daf9b13c21ece5304924dd28bbe34fad53a385fe Mon Sep 17 00:00:00 2001 From: Constructor Date: Tue, 11 Jun 2024 19:34:12 +0200 Subject: [PATCH 47/47] Bring back block setting --- .../kotlin/com/lambda/module/modules/player/HighwayTools.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt index 216264d14..11cf9d200 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt @@ -27,11 +27,9 @@ object HighwayTools : Module( private val width by setting("Width", 6, 1..30, 1) private val rimHeight by setting("Rim Height", 1, 0..6, 1) private val cornerBlock by setting("Corner Block", false, description = "Include corner blocks in the highway") - private val material = Blocks.OBSIDIAN private val distance by setting("Distance", -1, -1..1000000, 1, description = "Distance to build the highway (negative for infinite)") private val sliceSize by setting("Slice Size", 3, 1..5, 1, description = "Number of slices to build at once") - // ToDo: Fix block setting -// private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") + private val material by setting("Material", Blocks.OBSIDIAN, description = "Material to build the highway with") private var octant = EightWayDirection.NORTH private var distanceMoved = 0