diff --git a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 73a885d2b..5d13f1dfb 100644 --- a/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/common/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -57,7 +57,7 @@ private void onStartup(CallbackInfo ci) { 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 ScreenHandlerEvent.Open(handledScreen.getScreenHandler())); } EventFlow.post(new ScreenEvent.Open<>(screen)); @@ -67,7 +67,7 @@ private void onScreenOpen(@Nullable Screen screen, CallbackInfo ci) { private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { if (currentScreen == null) return; if (currentScreen instanceof ScreenHandlerProvider handledScreen) { - EventFlow.post(new ScreenHandlerEvent.Close<>(handledScreen.getScreenHandler())); + EventFlow.post(new ScreenHandlerEvent.Close(handledScreen.getScreenHandler())); } EventFlow.post(new ScreenEvent.Close<>(currentScreen)); diff --git a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java index 1ac004285..0dd8307ec 100644 --- a/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -8,6 +8,7 @@ import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.screen.slot.SlotActionType; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; @@ -37,6 +38,13 @@ public void interactBlockHead(final ClientPlayerEntity player, final Hand hand, EventFlow.post(new InteractionEvent.Block(client.world, hitResult)); } + @Inject(method = "clickSlot", at = @At("HEAD"), cancellable = true) + public void clickSlotHead(int syncId, int slotId, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo ci) { + if (syncId != player.currentScreenHandler.syncId) return; + var click = new InteractionEvent.SlotClick(syncId, slotId, button, actionType, player.currentScreenHandler); + if (EventFlow.post(click).isCanceled()) ci.cancel(); + } + @Inject(method = "attackEntity", at = @At("HEAD"), cancellable = true) void onAttackPre(PlayerEntity player, Entity target, CallbackInfo ci) { if (EventFlow.post(new AttackEvent.Pre(target)).isCanceled()) ci.cancel(); diff --git a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java index 7be591ef1..04406efef 100644 --- a/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java +++ b/common/src/main/java/com/lambda/mixin/entity/EntityMixin.java @@ -3,10 +3,12 @@ import com.lambda.Lambda; import com.lambda.event.EventFlow; import com.lambda.event.events.EntityEvent; +import com.lambda.event.events.WorldEvent; import com.lambda.interaction.RotationManager; import com.lambda.util.math.Vec2d; import net.minecraft.entity.Entity; import net.minecraft.entity.MovementType; +import net.minecraft.entity.data.TrackedData; import net.minecraft.util.math.Vec3d; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -70,4 +72,11 @@ float fixDirectionPitch2(Entity entity) { private void changeLookDirection(double cursorDeltaX, double cursorDeltaY, CallbackInfo ci) { if (EventFlow.post(new EntityEvent.ChangeLookDirection(cursorDeltaX, cursorDeltaY)).isCanceled()) ci.cancel(); } -} \ No newline at end of file + + @Inject(method = "onTrackedDataSet(Lnet/minecraft/entity/data/TrackedData;)V", at = @At("TAIL")) + public void onTrackedDataSet(TrackedData data, CallbackInfo ci) { + Entity entity = (Entity) (Object) this; + + EventFlow.post(new WorldEvent.EntityUpdate(entity, data)); + } +} 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 9f816cfbb..dacf0050f 100644 --- a/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java +++ b/common/src/main/java/com/lambda/mixin/render/ScreenHandlerMixin.java @@ -1,7 +1,6 @@ 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; @@ -16,6 +15,6 @@ public class ScreenHandlerMixin { @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)); + EventFlow.post(new ScreenHandlerEvent.Update(revision, stacks, cursorStack)); } } diff --git a/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt b/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt index 96ba26a83..4a5f5f10c 100644 --- a/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/LambdaCommand.kt @@ -22,7 +22,7 @@ abstract class LambdaCommand( // ToDo: Include usage and description in the help command init { (listOf(name) + aliases).forEach { - val argument = LiteralArgumentBuilder.literal(it) + val argument = LiteralArgumentBuilder.literal(it.lowercase()) argument.create() dispatcher.register(argument) } diff --git a/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt new file mode 100644 index 000000000..a065cf754 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt @@ -0,0 +1,47 @@ +package com.lambda.command.commands + +import com.lambda.brigadier.argument.literal +import com.lambda.brigadier.execute +import com.lambda.brigadier.required +import com.lambda.command.LambdaCommand +import com.lambda.interaction.construction.Blueprint.Companion.toStructure +import com.lambda.interaction.construction.DynamicBlueprint.Companion.toBlueprint +import com.lambda.interaction.construction.verify.TargetState +import com.lambda.task.tasks.BuildTask.Companion.build +import com.lambda.threading.runSafe +import com.lambda.util.extension.CommandBuilder +import net.minecraft.block.Blocks +import net.minecraft.util.math.BlockBox + +object BuildCommand : LambdaCommand( + name = "Build", + description = "Builds a structure", + usage = "build " +) { + override fun CommandBuilder.create() { + required(literal("place")) { + execute { + runSafe { + val materials = setOf( + TargetState.Block(Blocks.NETHERRACK), + TargetState.Block(Blocks.AIR), + TargetState.Block(Blocks.COBBLESTONE), + TargetState.Block(Blocks.AIR), + ) + val facing = player.horizontalFacing + val pos = player.blockPos.add(facing.vector.multiply(2)) + + BlockBox.create(pos, pos.add(facing.rotateYClockwise().vector.multiply(3))) + .toStructure(TargetState.Block(Blocks.NETHERRACK)) + .toBlueprint { + it.mapValues { (_, _) -> + materials.elementAt((System.currentTimeMillis() / 5000).toInt() % materials.size) + } + } + .build(finishOnDone = false) + .start(null) + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt b/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt new file mode 100644 index 000000000..772f2d7c0 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/command/commands/TaskCommand.kt @@ -0,0 +1,24 @@ +package com.lambda.command.commands + +import com.lambda.brigadier.argument.literal +import com.lambda.brigadier.execute +import com.lambda.brigadier.required +import com.lambda.command.LambdaCommand +import com.lambda.task.RootTask +import com.lambda.util.Communication.info +import com.lambda.util.extension.CommandBuilder + +object TaskCommand : LambdaCommand( + name = "task", + usage = "task ", + description = "Control tasks" +) { + override fun CommandBuilder.create() { + required(literal("cancel")) { + execute { + this@TaskCommand.info("Cancelling all tasks") + RootTask.cancel() + } + } + } +} \ No newline at end of file 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 d66f41e6b..79b552910 100644 --- a/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/common/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -18,7 +18,7 @@ object TransferCommand : LambdaCommand( usage = "transfer ", description = "Transfer items from anywhere to anywhere", ) { - private var lastTransfer: TransferResult.Success? = null + private var lastTransfer: TransferResult.Transfer? = null override fun CommandBuilder.create() { required(itemStack("stack", registry)) { stack -> @@ -61,10 +61,10 @@ object TransferCommand : LambdaCommand( } ?: return@executeWithResult failure("To container not found") when (val result = fromContainer.transfer(selection, toContainer)) { - is TransferResult.Success -> { + is TransferResult.Transfer -> { info("$result started.") lastTransfer = result - result.solve.onSuccess { _, _ -> + result.onSuccess { _, _ -> info("$lastTransfer completed.") }.start(null) return@executeWithResult success() @@ -86,7 +86,7 @@ object TransferCommand : LambdaCommand( required(literal("cancel")) { executeWithResult { - lastTransfer?.solve?.cancel() ?: run { + lastTransfer?.cancel() ?: run { return@executeWithResult failure("No transfer to cancel") } info("$lastTransfer cancelled") @@ -94,15 +94,5 @@ object TransferCommand : LambdaCommand( success() } } - - required(literal("undo")) { - executeWithResult { - 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/BuildConfig.kt b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index 1ae1fbb6c..e9edf3f23 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -1,8 +1,6 @@ package com.lambda.config.groups interface BuildConfig { - val breakCoolDown: Int - val placeCooldown: Int val breakConfirmation: Boolean val placeConfirmation: Boolean val collectDrops: Boolean @@ -11,5 +9,4 @@ interface BuildConfig { val breaksPerTick: Int val rotateForBreak: Boolean val rotateForPlace: Boolean - val pingTimeout: 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 3ce4c91f2..98ffe549d 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -7,23 +7,21 @@ class BuildSettings( vis: () -> Boolean = { true } ) : BuildConfig { enum class Page { - BREAK, PLACE, GENERAL + GENERAL, BREAK, PLACE } - private val page by c.setting("Build Page", Page.BREAK, "Current page", vis) + private val page by c.setting("Build Page", Page.GENERAL, "Current page", vis) + + override val pathing by c.setting("Pathing", true, "Path to blocks") { vis() && page == Page.GENERAL } - 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 rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() && page == Page.BREAK } + override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") { 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", true, "Wait for block placement confirmation") { 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.GENERAL } - override val pingTimeout by c.setting("Ping Timeout", true, "Timeout on high ping") { vis() && page == Page.GENERAL } } 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 919478485..0cd7fcb85 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionConfig.kt @@ -1,5 +1,7 @@ package com.lambda.config.groups +import com.lambda.core.PingManager + interface InteractionConfig { /** * Maximum distance to interact. @@ -13,4 +15,12 @@ interface InteractionConfig { val useRayCast: Boolean val swingHand: Boolean + val inScopeThreshold: Int + val pingTimeout: Boolean + + val scopeThreshold: Int get() = if (pingTimeout) { + (PingManager.lastPing / 50L).toInt() + } else { + inScopeThreshold + } } \ 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 e90ce0028..f23768066 100644 --- a/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt +++ b/common/src/main/kotlin/com/lambda/config/groups/InteractionSettings.kt @@ -1,14 +1,22 @@ package com.lambda.config.groups import com.lambda.config.Configurable +import com.lambda.config.groups.BuildSettings.Page +import com.lambda.core.PingManager +import com.lambda.module.modules.client.TaskFlow +import com.lambda.util.world.raycast.RayCastMask class InteractionSettings( c: Configurable, - defaultReach: Double = 4.9, + defaultReach: Double = 4.6, vis: () -> Boolean = { true }, ) : InteractionConfig { override val reach by c.setting("Reach", defaultReach, 0.1..10.0, 0.1, "Players reach / range", " blocks", vis) override val useRayCast by c.setting("Raycast", true, "Verify hit vector with ray casting (for very strict ACs)", vis) - override val resolution by c.setting("Resolution", 20, 1..40, 1, "How many raycast checks per surface (will be squared)") { vis() && useRayCast } + override val resolution by c.setting("Resolution", 4, 1..40, 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) + override val pingTimeout by c.setting("Ping Timeout", false, "Timeout on high ping", vis) + override val inScopeThreshold by c.setting("Constant Timeout", 1, 0..20, 1, "How many ticks to wait after target box is in rotation scope"," ticks") { + vis() && !pingTimeout + } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt b/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt index deca6c695..13719148c 100644 --- a/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/InteractionEvent.kt @@ -4,6 +4,8 @@ import com.lambda.event.Event import com.lambda.event.callback.Cancellable import com.lambda.event.callback.ICancellable import net.minecraft.client.world.ClientWorld +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.slot.SlotActionType import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -39,4 +41,12 @@ sealed class InteractionEvent : Event { val progress: Float, ) : BreakingProgress() } + + data class SlotClick( + val syncId: Int, + val slot: Int, + val button: Int, + val action: SlotActionType, + val screenHandler: ScreenHandler, + ) : ScreenHandlerEvent(), ICancellable by Cancellable() } 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 6e6cf8352..0f80e89f0 100644 --- a/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/ScreenHandlerEvent.kt @@ -5,9 +5,10 @@ import net.minecraft.item.ItemStack import net.minecraft.screen.ScreenHandler sealed class ScreenHandlerEvent : Event { - class Open(val screenHandler: H) : ScreenHandlerEvent() - class Close(val screenHandler: H) : ScreenHandlerEvent() - data class Loaded( + class Open(val screenHandler: ScreenHandler) : ScreenHandlerEvent() + class Close(val screenHandler: ScreenHandler) : ScreenHandlerEvent() + + data class Update( val revision: Int, val stacks: List, val cursorStack: ItemStack, diff --git a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt index f1fab7628..1d120f23c 100644 --- a/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt +++ b/common/src/main/kotlin/com/lambda/event/events/WorldEvent.kt @@ -6,6 +6,7 @@ import com.lambda.event.callback.ICancellable import net.minecraft.block.BlockState import net.minecraft.client.world.ClientWorld import net.minecraft.entity.Entity +import net.minecraft.entity.data.TrackedData import net.minecraft.util.math.BlockPos import net.minecraft.util.shape.VoxelShape import net.minecraft.world.chunk.WorldChunk @@ -36,5 +37,10 @@ abstract class WorldEvent : Event { val entity: Entity ) : WorldEvent(), ICancellable by Cancellable() + class EntityUpdate( + val entity: Entity, + val data: TrackedData<*>, + ) : WorldEvent(), ICancellable by Cancellable() + class Collision(val pos: BlockPos, val state: BlockState, var shape: VoxelShape) : WorldEvent() -} \ 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 8d9ca25e6..6678755db 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt @@ -21,6 +21,8 @@ abstract class Blueprint { } companion object { + fun emptyStructure(): Structure = emptyMap() + fun Box.toStructure(targetState: TargetState): Structure = BlockPos.stream(this) .map { it.blockPos } 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 12227c181..61686acce 100644 --- a/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt +++ b/common/src/main/kotlin/com/lambda/interaction/construction/DynamicBlueprint.kt @@ -26,13 +26,9 @@ data class DynamicBlueprint( }.toMap() } - fun blueprintOnTick( - init: SafeContext.(Structure) -> Structure = { emptyMap() }, - onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init = init, update = onTick) - fun Structure.toBlueprint( + init: SafeContext.(Structure) -> Structure = { this@toBlueprint }, onTick: SafeContext.(Structure) -> Structure - ) = DynamicBlueprint(init = { emptyMap() }, update = onTick) + ) = DynamicBlueprint(init = init, update = onTick) } } 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 deleted file mode 100644 index 7ab3c2182..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/construction/context/ComparableContext.kt +++ /dev/null @@ -1,7 +0,0 @@ -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 index 19d2647de..aeccc87e5 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,7 +9,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.Companion.failTask import com.lambda.task.tasks.BreakBlock.Companion.breakBlock import net.minecraft.block.BlockState import net.minecraft.item.Item @@ -23,16 +22,20 @@ sealed class BreakResult : BuildResult() { * Represents a successful break. All checks have been passed. * @param context The context of the break. */ - data class Success( + data class Break( override val blockPos: BlockPos, val context: BreakContext - ) : Resolvable, Drawable, BreakResult() { + ) : Drawable, BreakResult() { override val rank = Rank.BREAK_SUCCESS private val color = Color(222, 0, 0, 100) var collectDrop = false - override val resolve get() = breakBlock(context, collectDrop = collectDrop) + override fun SafeContext.onStart() { + breakBlock(context, collectDrop = collectDrop).onSuccess { _, _ -> + success(Unit) + }.start(this@Break) + } override fun SafeContext.buildRenderer() { withPos(context.resultingPos, color, context.result.side) @@ -40,7 +43,7 @@ sealed class BreakResult : BuildResult() { override fun compareTo(other: ComparableResult): Int { return when (other) { - is Success -> context.compareTo(other.context) + is Break -> context.compareTo(other.context) else -> super.compareTo(other) } } @@ -54,11 +57,13 @@ sealed class BreakResult : BuildResult() { data class NotExposed( override val blockPos: BlockPos, val side: Direction - ) : Resolvable, Drawable, BreakResult() { + ) : Drawable, BreakResult() { override val rank = Rank.BREAK_NOT_EXPOSED private val color = Color(46, 0, 0, 30) - override val resolve get() = failTask("Block is not exposed to air.") + override fun SafeContext.onStart() { + failure("Block is not exposed to air.") + } override fun SafeContext.buildRenderer() { withPos(blockPos, color, side) @@ -81,20 +86,24 @@ sealed class BreakResult : BuildResult() { override val blockPos: BlockPos, val blockState: BlockState, val badItem: Item - ) : Resolvable, Drawable, BreakResult() { + ) : Drawable, BreakResult() { override val rank = Rank.BREAK_ITEM_CANT_MINE private val color = Color(255, 0, 0, 100) override val pausesParent get() = true - override val resolve get() = findBestAvailableTool(blockState) - ?.select() - ?.transfer(MainHandContainer) - ?.solve ?: run { - selectStack { - isItem(badItem).not() - }.transfer(MainHandContainer)?.solve ?: failTask("No item found or space") - } + override fun SafeContext.onStart() { + findBestAvailableTool(blockState) + ?.select() + ?.transfer(MainHandContainer) + ?.onSuccess { _, _ -> + success(Unit) + }?.start(this@ItemCantMine) ?: run { + selectStack { + isItem(badItem).not() + }.transfer(MainHandContainer)?.start(this@ItemCantMine) ?: failure("No item found or space") + } + } override fun SafeContext.buildRenderer() { withPos(blockPos, color) 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 7c0113731..3e12b0acb 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 @@ -8,6 +8,7 @@ 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.failTask import net.minecraft.block.BlockState import net.minecraft.item.Item @@ -18,7 +19,7 @@ import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d import java.awt.Color -abstract class BuildResult : ComparableResult { +abstract class BuildResult : ComparableResult, Task() { abstract val blockPos: BlockPos open val pausesParent = false @@ -140,12 +141,12 @@ abstract class BuildResult : ComparableResult { val distance: Double ) : Navigable, Drawable, BuildResult() { override val rank = Rank.NOT_VISIBLE - private val color = Color(46, 0, 0, 30) + private val color = Color(46, 0, 0, 80) override val goal = GoalPlace(blockPos) override fun SafeContext.buildRenderer() { - withPos(blockPos, color, side) + withBox(Box(blockPos), color) } override fun compareTo(other: ComparableResult): Int { @@ -164,17 +165,22 @@ abstract class BuildResult : ComparableResult { override val blockPos: BlockPos, val context: BuildContext, val neededItem: Item - ) : Resolvable, Drawable, BuildResult() { + ) : Drawable, BuildResult() { override val rank = Rank.WRONG_ITEM private val color = Color(3, 252, 169, 100) override val pausesParent get() = true - override val resolve get() = - neededItem.select().transfer(MainHandContainer)?.solve ?: failTask("Item ${neededItem.name.string} not found") + override fun SafeContext.onStart() { + neededItem.select() + .transfer(MainHandContainer) + ?.onSuccess { _, _ -> + success(Unit) + }?.start(this@WrongItem) ?: failure("Item ${neededItem.name.string} not found") + } override fun SafeContext.buildRenderer() { - withPos(blockPos, color) + withBox(Box(blockPos), color) } override fun compareTo(other: ComparableResult): Int { @@ -194,14 +200,19 @@ abstract class BuildResult : ComparableResult { override val blockPos: BlockPos, val context: BuildContext, val neededStack: ItemStack - ) : Resolvable, Drawable, BuildResult() { + ) : Drawable, BuildResult() { override val rank = Rank.WRONG_ITEM private val color = Color(3, 252, 169, 100) override val pausesParent get() = true - override val resolve get() = - neededStack.select().transfer(MainHandContainer)?.solve ?: failTask("Stack ${neededStack.name.string} not found") + override fun SafeContext.onStart() { + neededStack.select() + .transfer(MainHandContainer) + ?.onSuccess { _, _ -> + success(Unit) + }?.start(this@WrongStack) ?: failTask("Stack ${neededStack.name.string} not found") + } override fun SafeContext.buildRenderer() { withPos(blockPos, color) 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 7c79a30d6..f1d281ddc 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 @@ -4,7 +4,7 @@ import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted import com.lambda.context.SafeContext import com.lambda.interaction.construction.context.PlaceContext -import com.lambda.task.tasks.BuildStructure.Companion.breakBlock +import com.lambda.task.tasks.BuildTask.Companion.breakBlock import com.lambda.task.tasks.PlaceBlock.Companion.placeBlock import net.minecraft.block.BlockState import net.minecraft.item.ItemPlacementContext @@ -24,14 +24,18 @@ sealed class PlaceResult : BuildResult() { * Represents a successful placement. All checks have been passed. * @param context The context of the placement. */ - data class Success( + data class Place( override val blockPos: BlockPos, - val context: PlaceContext, - ) : Resolvable, Drawable, PlaceResult() { + val context: PlaceContext + ) : Drawable, PlaceResult() { override val rank = Rank.PLACE_SUCCESS private val color = Color(35, 188, 254, 100) - override val resolve get() = placeBlock(context) + override fun SafeContext.onStart() { + placeBlock(context).onSuccess { _, _ -> + success(Unit) + }.start(this@Place) + } override fun SafeContext.buildRenderer() { val hitPos = context.result.blockPos @@ -43,7 +47,7 @@ sealed class PlaceResult : BuildResult() { override fun compareTo(other: ComparableResult): Int { return when (other) { - is Success -> context.compareTo(other.context) + is Place -> context.compareTo(other.context) else -> super.compareTo(other) } } @@ -83,10 +87,14 @@ sealed class PlaceResult : BuildResult() { data class CantReplace( override val blockPos: BlockPos, val simulated: ItemPlacementContext - ) : Resolvable, PlaceResult() { + ) : PlaceResult() { override val rank = Rank.PLACE_CANT_REPLACE - override val resolve = breakBlock(blockPos) + override fun SafeContext.onStart() { + breakBlock(blockPos).onSuccess { _, _ -> + success(Unit) + }.start(this@CantReplace) + } } /** @@ -126,9 +134,7 @@ sealed class PlaceResult : BuildResult() { data class NotItemBlock( override val blockPos: BlockPos, val itemStack: ItemStack - ) : Resolvable, PlaceResult() { + ) : PlaceResult() { override val rank = Rank.PLACE_NOT_ITEM_BLOCK - - override val resolve get() = TODO("Not expected") } } 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 deleted file mode 100644 index 9bfb609c2..000000000 --- a/common/src/main/kotlin/com/lambda/interaction/construction/result/Resolvable.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.lambda.interaction.construction.result - -import com.lambda.task.Task - -interface Resolvable { - val resolve: Task<*> -} \ No newline at end of file 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 29af0d786..b0ab48954 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 @@ -241,7 +241,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.copy)) + acc.add(BuildResult.WrongStack(pos, placeContext, target.itemStack)) return@forEach } @@ -250,7 +250,7 @@ object BuildSimulator { return@forEach } - acc.add(PlaceResult.Success(pos, placeContext)) + acc.add(PlaceResult.Place(pos, placeContext)) } } @@ -336,7 +336,7 @@ object BuildSimulator { player.activeHand, instantBreakable(state, pos) ) - acc.add(BreakResult.Success(pos, breakContext)) + acc.add(BreakResult.Break(pos, breakContext)) return acc } } @@ -392,7 +392,7 @@ object BuildSimulator { stack.item == bestTool }?.let { hand -> breakContext.hand = hand - acc.add(BreakResult.Success(pos, breakContext)) + acc.add(BreakResult.Break(pos, breakContext)) return acc } ?: run { acc.add(BuildResult.WrongItem(pos, breakContext, bestTool)) @@ -400,7 +400,7 @@ object BuildSimulator { } } - acc.add(BreakResult.Success(pos, breakContext)) + acc.add(BreakResult.Break(pos, breakContext)) } return acc 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 13e02ca70..7238654a0 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.interaction.material.ContainerManager.findDisposable import com.lambda.util.BlockUtils.blockState import com.lambda.util.item.ItemUtils.block import net.minecraft.block.BlockState @@ -20,15 +21,15 @@ 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) // ToDo: Find any disposable block + findDisposable()?.stacks?.firstOrNull() ?: ItemStack(Items.NETHERRACK) } 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)) - || pos.blockState(world).isSolidBlock(world, pos) + || state.isSolidBlock(world, pos) override fun getStack(world: ClientWorld, pos: BlockPos) = - ItemStack(Items.NETHERRACK) // ToDo: Find any disposable block + findDisposable()?.stacks?.firstOrNull() ?: ItemStack(Items.NETHERRACK) } data class State(val blockState: BlockState) : TargetState() { override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = @@ -43,12 +44,12 @@ sealed class TargetState : StateMatcher { block.getPickStack(world, pos, block.defaultState) } data class Stack(val itemStack: ItemStack) : TargetState() { - val copy: ItemStack = itemStack.copy() + private val block = itemStack.item.block override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) = - state.block == copy.item.block + state.block == block override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack = - copy + 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 index 483f43f1f..da9d5fa7d 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/ContainerManager.kt @@ -6,7 +6,9 @@ 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.BlockUtils.blockEntity +import com.lambda.util.BlockUtils.item import com.lambda.util.Communication.info import com.lambda.util.item.ItemUtils import com.lambda.util.extension.containerStacks @@ -18,10 +20,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 org.reflections.util.ClasspathHelper.forPackage -import java.util.* // ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. object ContainerManager : Loadable { @@ -38,9 +37,7 @@ object ContainerManager : Loadable { lastInteractedBlockEntity = it.blockHitResult.blockPos.blockEntity(world) } - listener> { event -> - // ToDo: ;-; i hate type erasure. - // The listener will be triggered for any H, not just GenericContainerScreenHandler + listener { event -> if (event.screenHandler !is GenericContainerScreenHandler) return@listener val handler = event.screenHandler @@ -116,7 +113,9 @@ object ContainerManager : Loadable { it.second }?.first -// fun SafeContext.nextDisposable() = player.combined.firstOrNull { it.item in TaskFlow.disposables } + fun findDisposable() = container().find { container -> + TaskFlow.disposables.any { container.available(it.item.select()) >= 0 } + } class NoContainerFound(selection: StackSelection): Exception("No container found matching $selection") } 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 f268250ec..b439a4b61 100644 --- a/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt +++ b/common/src/main/kotlin/com/lambda/interaction/material/MaterialContainer.kt @@ -72,7 +72,7 @@ abstract class MaterialContainer( // selection.selector = { true } // selection.count = transferAmount - return TransferResult.Success(selection, from = this, to = destination) + return TransferResult.Transfer(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 222f589f5..c4e4d5b45 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,7 +30,7 @@ data class ChestContainer( // } override fun withdraw(selection: StackSelection) = - openContainer(blockPos) + openContainer(blockPos) // .withMaxAttempts(3) // .withTimeout(20) .onSuccess { open, screen -> @@ -39,7 +39,7 @@ data class ChestContainer( } override fun deposit(selection: StackSelection) = - openContainer(blockPos) + openContainer(blockPos) // .withMaxAttempts(3) // .withTimeout(20) .onSuccess { open, screen -> 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 53ad4d1dc..332a26403 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,17 +1,15 @@ 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.tasks.BuildStructure.Companion.breakAndCollectBlock +import com.lambda.task.tasks.BuildTask.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 net.minecraft.item.ItemStack -import net.minecraft.screen.ShulkerBoxScreenHandler data class ShulkerBoxContainer( override var stacks: List, @@ -27,10 +25,9 @@ data class ShulkerBoxContainer( 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 { _, _ -> + placeContainer(shulkerStack).thenRun(this@Withdraw) { _, placePos -> + openContainer(placePos).thenRun(this@Withdraw) { _, screen -> + withdraw(screen, selection).thenRun(this@Withdraw) { _, _ -> breakAndCollectBlock(placePos).onSuccess { _, _ -> success(Unit) } @@ -47,9 +44,9 @@ data class ShulkerBoxContainer( private val shulkerStack: ItemStack ) : Task() { override fun SafeContext.onStart() { - placeContainer(shulkerStack).thenRun { _, placePos -> - openContainer(placePos).thenRun { _, screen -> - deposit(screen, selection).thenRun { _, _ -> + placeContainer(shulkerStack).thenRun(this@Deposit) { _, placePos -> + openContainer(placePos).thenRun(this@Deposit) { _, screen -> + deposit(screen, selection).thenRun(this@Deposit) { _, _ -> breakAndCollectBlock(placePos).onSuccess { _, _ -> success(Unit) } 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 23bd5a5a8..42f6f03e7 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,32 +1,38 @@ package com.lambda.interaction.material.transfer +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.failTask -import com.lambda.task.tasks.ContainerTransfer -abstract class TransferResult { - abstract val solve: Task<*> - - data class Success( +abstract class TransferResult : Task() { + data class Transfer( val selection: StackSelection, val from: MaterialContainer, val to: MaterialContainer ) : TransferResult() { - override val solve = ContainerTransfer(selection, from, to) - val undo = ContainerTransfer(selection, to, from) + override fun SafeContext.onStart() { + from.withdraw(selection).thenRun(this@Transfer) { _, _ -> + to.deposit(selection).onSuccess { _, _ -> + success(Unit) + } + }.start(this@Transfer) + } override fun toString() = "Transfer of [$selection] from [${from.name}] to [${to.name}]" } data object NoSpace : TransferResult() { // ToDo: Needs inventory space resolver. compressing or disposing - override val solve = failTask("NoSpace") + override fun SafeContext.onStart() { + failure("No space left in the target container") + } } data class MissingItems(val missing: Int) : TransferResult() { // ToDo: Find other satisfying permutations - override val solve = failTask("MissingItems") + override fun SafeContext.onStart() { + failure("Missing $missing items") + } } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/module/hud/TaskFlow.kt b/common/src/main/kotlin/com/lambda/module/hud/TaskFlow.kt new file mode 100644 index 000000000..be056b02c --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/hud/TaskFlow.kt @@ -0,0 +1,19 @@ +package com.lambda.module.hud + +import com.lambda.module.HudModule +import com.lambda.module.tag.ModuleTag +import com.lambda.util.math.Vec2d + +object TaskFlow : HudModule( + name = "TaskFlow", + defaultTags = setOf(ModuleTag.CLIENT), +) { + override val width = 50.0 + override val height = 50.0 + + init { + onRender { + font.build("TaskFlow", Vec2d.ZERO) + } + } +} \ No newline at end of file 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 da1b5ee09..ae1b2faf7 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 @@ -2,6 +2,7 @@ package com.lambda.module.modules.debug import com.lambda.Lambda.LOG import com.lambda.event.events.PacketEvent +import com.lambda.event.events.ScreenHandlerEvent import com.lambda.event.listener.SafeListener.Companion.listener import com.lambda.module.Module import com.lambda.module.tag.ModuleTag @@ -17,6 +18,18 @@ object InventoryDebug : Module( defaultTags = setOf(ModuleTag.DEBUG) ) { init { + listener { + info("Opened screen handler: ${it.screenHandler::class.simpleName}") + } + + listener { + info("Closed screen handler: ${it.screenHandler::class.simpleName}") + } + + listener { + info("Updated screen handler: ${it.revision}, ${it.stacks}, ${it.cursorStack}") + } + listener { when (val packet = it.packet) { is UpdateSelectedSlotS2CPacket, is InventoryS2CPacket -> { 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 13f430993..3bfe808b9 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 @@ -5,7 +5,7 @@ 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.task.tasks.BuildTask.Companion.build import com.lambda.util.BaritoneUtils import com.lambda.util.Communication.info import com.lambda.util.player.MovementUtils.octant @@ -60,25 +60,21 @@ object HighwayTools : Module( repeat(sliceSize) { val vec = Vec3i(octant.offsetX, 0, octant.offsetZ) currentPos = currentPos.add(vec) - structure += slice.map { it.key.add(currentPos) to it.value } + structure = structure.plus(slice.map { it.key.add(currentPos) to it.value }) } - runningTask = buildStructure { + runningTask = build { structure.toBlueprint() - }.apply { - onSuccess { _, _ -> - if (distanceMoved < distance || distance < 0) { - buildSlice() - } else { - this@HighwayTools.info("Highway built") - disable() - } - } - onFailure { _, _ -> + }.onSuccess { _, _ -> + if (distanceMoved < distance || distance < 0) { + buildSlice() + } else { + this@HighwayTools.info("Highway built") disable() } - start(null) - } + }.onFailure { _, _ -> + disable() + }.start(null) } private fun highwaySlice(): Structure { diff --git a/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt new file mode 100644 index 000000000..6cc6ebd64 --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -0,0 +1,91 @@ +package com.lambda.module.modules.player + +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.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.task.Task +import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock +import com.lambda.task.tasks.OpenContainer.Companion.openContainer +import com.lambda.task.tasks.PlaceContainer.Companion.placeContainer +import com.lambda.util.item.ItemUtils.shulkerBoxes +import com.lambda.util.player.SlotUtils.clickSlot +import com.lambda.util.player.SlotUtils.hotbar +import net.minecraft.item.ItemStack +import net.minecraft.item.Items +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket +import net.minecraft.screen.ScreenHandler +import net.minecraft.screen.slot.SlotActionType +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction + +object InventoryTweaks : Module( + name = "InventoryTweaks", + defaultTags = setOf(ModuleTag.PLAYER) +) { + private val instantShulker by setting("Instant Shulker", true, description = "Right-click shulker boxes in your inventory to instantly place them and open them.") + private val instantEChest by setting("Instant Ender-Chest", true, description = "Right-click ender chests in your inventory to instantly place them and open them.") + private var placedPos: BlockPos? = null + private var lastPlace: Task<*>? = null + private var lastBreak: Task<*>? = null + private var lastOpenScreen: ScreenHandler? = null + + init { + listener { + if (it.action != SlotActionType.PICKUP || it.button != 1) return@listener + val stack = it.screenHandler.getSlot(it.slot).stack + if (!(instantShulker && stack.item in shulkerBoxes) && !(instantEChest && stack.item == Items.ENDER_CHEST)) return@listener + it.cancel() + move(it.slot, stack) + + player.closeScreen() + + lastPlace = placeContainer(stack).thenRun(null) { _, placePos -> + placedPos = placePos + openContainer(placePos).onSuccess { _, screenHandler -> + lastOpenScreen = screenHandler + } + }.start(null) + } + + listener { event -> + if (event.screenHandler != lastOpenScreen) return@listener + lastOpenScreen = null + placedPos?.let { + lastBreak = breakAndCollectBlock(it).start(null) + placedPos = null + } + } + + onDisable { + lastPlace?.cancel() + lastBreak?.cancel() + } + } + + private fun SafeContext.move(index: Int, stack: ItemStack) { + if (stack == player.mainHandStack) { + return + } + + if (stack == player.offHandStack) { + connection.sendPacket( + PlayerActionC2SPacket( + PlayerActionC2SPacket.Action.SWAP_ITEM_WITH_OFFHAND, + BlockPos.ORIGIN, + Direction.DOWN, + ), + ) + return + } + + if (stack in player.hotbar) { + player.inventory.selectedSlot = player.hotbar.indexOf(stack) + return + } + + clickSlot(index, player.inventory.selectedSlot, SlotActionType.SWAP) + } +} \ 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 fc5da0fed..c60ea9205 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,11 +1,12 @@ package com.lambda.module.modules.player -import com.lambda.interaction.construction.DynamicBlueprint.Companion.blueprintOnTick +import com.lambda.interaction.construction.Blueprint.Companion.emptyStructure +import com.lambda.interaction.construction.DynamicBlueprint.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.Companion.emptyTask -import com.lambda.task.tasks.BuildStructure.Companion.buildStructure +import com.lambda.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos import com.lambda.util.BlockUtils.blockState import net.minecraft.util.math.BlockPos @@ -25,12 +26,8 @@ object Nuker : Module( init { onEnable { - task = buildStructure( - pathing = false, - finishOnDone = false, - cancelOnUnsolvable = false - ) { - blueprintOnTick { _ -> + task = emptyStructure() + .toBlueprint { val selection = BlockPos.iterateOutwards(player.blockPos, width, height, width) .asSequence() .map { it.blockPos } @@ -44,12 +41,16 @@ object Nuker : Module( val floor = BlockPos.iterateOutwards(player.blockPos.down(), width, 0, width) .map { it.blockPos } .associateWith { TargetState.Solid } - return@blueprintOnTick selection + floor + return@toBlueprint selection + floor } selection } - } + .build( + pathing = false, + finishOnDone = false, + cancelOnUnsolvable = false + ) task.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 a7f0e5efc..9117bffb8 100644 --- a/common/src/main/kotlin/com/lambda/task/Task.kt +++ b/common/src/main/kotlin/com/lambda/task/Task.kt @@ -64,22 +64,21 @@ abstract class Task : Nameable { open var pausable = true - var parent: Task<*>? = null + 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 - val subTasks = mutableListOf>() + private val subTasks = mutableListOf>() private var state = State.IDLE var age = 0 - private val isDeactivated get() = state == State.DEACTIVATED - val isActivated get() = state == State.ACTIVATED - val isRunning get() = state == State.ACTIVATED || state == State.DEACTIVATED + private val isWaiting get() = state == State.WAITING + private val isRunning get() = state == State.RUNNING val isFailed get() = state == State.FAILED val isCompleted get() = state == State.COMPLETED || state == State.COOLDOWN - val isRoot get() = parent == null + private val isRoot get() = parent == null override var name = this::class.simpleName ?: "Task" val identifier get() = "$name@${hashCode()}" @@ -91,16 +90,14 @@ abstract class Task : Nameable { enum class State { IDLE, - ACTIVATED, - DEACTIVATED, + RUNNING, + WAITING, CANCELLED, FAILED, COOLDOWN, COMPLETED, } - operator fun plus(other: Task<*>) = subTasks.add(other) - init { listener { parent?.let { @@ -131,7 +128,7 @@ abstract class Task : Nameable { LOG.info("${owner.identifier} started $identifier") this.parent = owner - if (pauseParent && owner.isActivated && !owner.isRoot) { + if (pauseParent && owner.isRunning && !owner.isRoot) { LOG.info("$identifier deactivating parent ${owner.identifier}") owner.deactivate() } @@ -146,15 +143,15 @@ abstract class Task : Nameable { @Ta5kBuilder fun activate() { - if (isActivated) return - state = State.ACTIVATED + if (isRunning) return + state = State.RUNNING startListening() } @Ta5kBuilder fun deactivate() { - if (isDeactivated) return - state = State.DEACTIVATED + if (isWaiting) return + state = State.WAITING stopListening() } @@ -268,7 +265,7 @@ abstract class Task : Nameable { LOG.info("$identifier completed parent ${par.identifier}") par.notifyParent() } - !par.isActivated -> { + !par.isRunning -> { LOG.info("$identifier reactivated parent ${par.identifier}") par.activate() } @@ -386,9 +383,9 @@ abstract class Task : Nameable { * @return The current task instance with the updated success action. */ @Ta5kBuilder - fun thenRun(action: SafeContext.(Task, Result) -> Task<*>): Task { + fun thenRun(owner: Task<*>?, action: SafeContext.(Task, Result) -> Task<*>): Task { this.onSuccess = { task, result -> - action(this, task, result).start(task) + action(this, task, result).start(owner) } 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 43e9416a0..43cf19c17 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BreakBlock.kt @@ -14,11 +14,10 @@ import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.BaritoneUtils import com.lambda.util.BlockUtils.blockState +import com.lambda.util.extension.inventorySlots import com.lambda.util.item.ItemUtils.block import com.lambda.util.player.SlotUtils.clickSlot import com.lambda.util.player.SlotUtils.hotbarAndStorage -import com.lambda.util.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 @@ -27,41 +26,51 @@ import net.minecraft.util.math.Direction class BreakBlock @Ta5kBuilder constructor( private val ctx: BreakContext, - private val rotationConfig: IRotationConfig, - private val interactionConfig: InteractionConfig, + private val rotation: IRotationConfig, + private val interact: InteractionConfig, private val sides: Set, private val collectDrop: Boolean, private val rotate: Boolean, private val swingHand: Boolean, ) : Task() { val blockPos: BlockPos get() = ctx.result.blockPos + private var beginState: BlockState? = null - val SafeContext.state: BlockState get() = blockPos.blockState(world) - override var cooldown = Int.MAX_VALUE - get() = maxOf(TaskFlow.build.breakCoolDown, TaskFlow.taskCooldown) - set(value) = run { field = value } + val SafeContext.blockState: BlockState get() = + blockPos.blockState(world) + private var drop: ItemEntity? = null + private var state = State.BREAKING + private var isValid = false + + enum class State { + BREAKING, COLLECTING + } override fun SafeContext.onStart() { - if (state.isAir && !collectDrop) { + if (done()) { success(null) return } - beginState = state + beginState = blockState + + if (!rotate || ctx.instantBreak) { + breakBlock(ctx.result.side) + } } init { listener { event -> - if (!rotate) return@listener - event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + if (state != State.BREAKING) return@listener + if (!rotate || ctx.instantBreak) return@listener + event.context = lookAtBlock(blockPos, rotation, interact, sides) } listener { - if (!rotate) return@listener - if (!it.context.isValid) return@listener - val hitResult = it.context.hitResult?.blockResult ?: return@listener + if (state != State.BREAKING) return@listener + if (!rotate || ctx.instantBreak) return@listener - breakBlock(hitResult.side) + isValid = it.context.isValid } listener { @@ -80,32 +89,32 @@ class BreakBlock @Ta5kBuilder constructor( } BaritoneUtils.setGoalAndPath(GoalBlock(itemDrop.blockPos)) - } + } ?: BaritoneUtils.cancel() - if (finish()) { - success(null) - return@listener + if (isValid || !rotate || ctx.instantBreak) { + breakBlock(ctx.result.side) } - if (rotate) return@listener - - breakBlock(ctx.result.side) - if (finish()) success(null) + if (done()) { + state = State.COLLECTING + if (!collectDrop) { + success(null) + } + } } - listener { + 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?? + && it.entity.pos.isInRange(blockPos.toCenterPos(), 0.5) + ) { drop = it.entity } } } - private fun SafeContext.finish() = state.isAir && !collectDrop + private fun SafeContext.done() = blockState.isAir && !collectDrop private fun SafeContext.breakBlock(side: Direction) { if (interaction.updateBlockBreakingProgress(blockPos, side)) { diff --git a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt similarity index 59% rename from common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt rename to common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 7e38e25c9..1ae7d7e62 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/BuildStructure.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -1,6 +1,5 @@ package com.lambda.task.tasks -import baritone.api.pathing.goals.GoalNear import com.lambda.Lambda.LOG import com.lambda.context.SafeContext import com.lambda.event.events.RenderEvent @@ -16,21 +15,22 @@ import com.lambda.interaction.construction.verify.TargetState import com.lambda.module.modules.client.TaskFlow import com.lambda.task.Task import com.lambda.util.BaritoneUtils -import com.lambda.util.Communication.info +import com.lambda.util.extension.Structure import net.minecraft.util.math.BlockPos -class BuildStructure @Ta5kBuilder constructor( +class BuildTask @Ta5kBuilder constructor( private val blueprint: Blueprint, private val finishOnDone: Boolean = true, private val pathing: Boolean = TaskFlow.build.pathing, + private val stayInRange: Boolean = true, + private val forceSilkTouch: Boolean = false, val collectDrops: Boolean = TaskFlow.build.collectDrops, - private val cancelOnUnsolvable: Boolean = true, + private val cancelOnUnsolvable: Boolean = false, ) : Task() { private var previousResults = setOf() - private var lastResult: BuildResult? = null - private var lastTask: Task<*>? = null - - abstract class PathingStrategy + private val placeTimeout = 15 + private val breakTimeout = 15 + private val pending = mutableListOf() override fun SafeContext.onStart() { (blueprint as? DynamicBlueprint)?.create(this) @@ -39,13 +39,20 @@ class BuildStructure @Ta5kBuilder constructor( init { listener { previousResults.filterIsInstance().forEach { res -> - with(res) { - buildRenderer() - } + with(res) { buildRenderer() } } } listener { + pending.removeIf { + if (it.age > placeTimeout) { + it.cancel() + true + } else { + it.isCompleted + } + } + (blueprint as? DynamicBlueprint)?.update(this) if (finishOnDone && blueprint.structure.isEmpty()) { @@ -57,55 +64,40 @@ class BuildStructure @Ta5kBuilder constructor( previousResults = results val result = results.minOrNull() ?: return@listener - lastResult?.let { - if (it.compareTo(result) == 0) return@listener -// if (it.pausesParent && lastTask?.isCompleted != true) return@listener - if (collectDrops && it is BreakResult.Success && lastTask?.isCompleted != true) { - return@listener - } - info("${it.rank.name}${if (it.pausesParent) " pauses" else ""} -> ${result.rank.name} (${lastTask?.identifier})") - - lastTask?.cancel() - } - - val instantResults = results.filterIsInstance() + val instantResults = results.filterIsInstance() .filter { it.context.instantBreak } .sorted() .take(TaskFlow.build.breaksPerTick) if (TaskFlow.build.breaksPerTick > 1 && instantResults.isNotEmpty()) { instantResults.forEach { - lastResult = it - lastTask = it.resolve.start(this@BuildStructure, pauseParent = false) + pending.add(it) + it.start(this@BuildTask, pauseParent = false) } return@listener } + if (pending.isNotEmpty()) { + return@listener + } + when (result) { is BuildResult.Done, is BuildResult.Unbreakable -> { if (!finishOnDone) return@listener success(Unit) } - is Resolvable -> { - LOG.info("Resolving: $result") - - if (result is BreakResult.Success) { - result.collectDrop = collectDrops - } - - lastResult = result - lastTask = result.resolve.start(this@BuildStructure, pauseParent = result.pausesParent) - if (pathing) { - BaritoneUtils.setGoalAndPath(GoalNear(result.blockPos, 3)) - } - } is Navigable -> { if (pathing) BaritoneUtils.setGoalAndPath(result.goal) } else -> { - if (!cancelOnUnsolvable) return@listener + LOG.info("Resolving: $result") - failure("Failed to resolve build result: $result") + if (result is BreakResult.Break) { + result.collectDrop = collectDrops + } + + pending.add(result) + result.start(this@BuildTask, pauseParent = result.pausesParent) } } } @@ -113,32 +105,76 @@ class BuildStructure @Ta5kBuilder constructor( companion object { @Ta5kBuilder - fun buildStructure( + fun build( finishOnDone: Boolean = true, - collectDrops: Boolean = TaskFlow.build.collectDrops, pathing: Boolean = TaskFlow.build.pathing, + stayInRange: Boolean = true, + forceSilkTouch: Boolean = false, + collectDrops: Boolean = TaskFlow.build.collectDrops, cancelOnUnsolvable: Boolean = true, blueprint: () -> Blueprint, - ) = BuildStructure( + ) = BuildTask( blueprint(), finishOnDone, pathing, + stayInRange, + forceSilkTouch, collectDrops, cancelOnUnsolvable ) + @Ta5kBuilder + fun Structure.build( + finishOnDone: Boolean = true, + pathing: Boolean = TaskFlow.build.pathing, + stayInRange: Boolean = true, + forceSilkTouch: Boolean = false, + collectDrops: Boolean = TaskFlow.build.collectDrops, + cancelOnUnsolvable: Boolean = true, + ) = BuildTask( + toBlueprint(), + finishOnDone, + pathing, + stayInRange, + forceSilkTouch, + collectDrops, + cancelOnUnsolvable + ) + + @Ta5kBuilder + fun Blueprint.build( + finishOnDone: Boolean = true, + pathing: Boolean = TaskFlow.build.pathing, + stayInRange: Boolean = true, + forceSilkTouch: Boolean = false, + collectDrops: Boolean = TaskFlow.build.collectDrops, + cancelOnUnsolvable: Boolean = true, + ) = BuildTask( + this, + finishOnDone, + pathing, + stayInRange, + forceSilkTouch, + collectDrops, + cancelOnUnsolvable + ) + @Ta5kBuilder fun breakAndCollectBlock( blockPos: BlockPos, - ) = BuildStructure( + withSilkTouch: Boolean = false, + stayInRange: Boolean = false, + ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), + forceSilkTouch = withSilkTouch, + stayInRange = stayInRange, collectDrops = true ) @Ta5kBuilder fun breakBlock( blockPos: BlockPos, - ) = BuildStructure( + ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint() ) } diff --git a/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt b/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt deleted file mode 100644 index 6f6391314..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/ContainerTransfer.kt +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 297648c7e..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/GoalTask.kt +++ /dev/null @@ -1,79 +0,0 @@ -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 -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 -import net.minecraft.util.math.BlockPos - -// ToDo: Custom heuristic goals -class GoalTask( - private val goal: () -> Goal, - private val check: SafeContext.() -> Boolean = { false } -) : Task() { - - init { - listener { - val goal = goal() - - primary.customGoalProcess.goal = goal - primary.customGoalProcess.path() - - if (goal.isInGoal(player.blockPos) || check()) { - primary.customGoalProcess.goal = null - success(Unit) - } - } - } - - companion object { - @Ta5kBuilder - 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) - - @Ta5kBuilder - 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) }) - - @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) }) - } -} \ 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 deleted file mode 100644 index 3fcf01079..000000000 --- a/common/src/main/kotlin/com/lambda/task/tasks/HelloWorldTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.lambda.task.tasks - -import com.lambda.Lambda.LOG -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listener -import com.lambda.task.Task - -class HelloWorldTask @Ta5kBuilder constructor() : Task() { - init { - listener { - LOG.info("$name: ticking $age") - - if (age > 20) { - success(Unit) - } - } - } - - companion object { - @Ta5kBuilder - 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 abbd6badd..bcd6c459f 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/InventoryTask.kt @@ -6,79 +6,67 @@ 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.util.item.ItemUtils.block -import com.lambda.util.item.ItemUtils.defaultDisposables -import com.lambda.util.player.SlotUtils.clickSlot import com.lambda.util.extension.containerSlots import com.lambda.util.extension.inventorySlots -import net.minecraft.item.ItemStack +import com.lambda.util.item.ItemUtils.block +import com.lambda.util.player.SlotUtils import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot import net.minecraft.screen.slot.SlotActionType -class InventoryTask( - val screen: H, - val selection: StackSelection, +class InventoryTask( + val screen: ScreenHandler, + private val selector: 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() } +) : Task() { + private val selectedFrom get() = selector.filterSlots(from).filter { it.hasStack() } + private val selectedTo = to.filter { it.stack.isEmpty } + to.filter { it.stack.item.block in TaskFlow.disposables } + private val transactions = mutableListOf() + + override fun SafeContext.onStart() { + selectedFrom.zip(selectedTo).forEach { (from, to) -> + transactions.add(SlotUtils.Transaction(to.id, 0, SlotActionType.SWAP)) + transactions.add(SlotUtils.Transaction(from.id, 0, SlotActionType.SWAP)) + + // ToDo: Handle overflow of cursor for PICKUP + } + } init { // ToDo: Needs smart code to move as efficient as possible. // Also should handle overflow etc. Should be more generic listener { - selectedFrom.firstOrNull()?.let { from -> - val preMove = from.stack.copy() + val moved = selector.filterSlots(to) + .filter { it.hasStack() } + .sumOf { it.stack.count } >= selector.count -// 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.block in TaskFlow.disposables || it.stack.isEmpty - }?.let { emptySlot -> - clickSlot(emptySlot.id, 0, SlotActionType.SWAP) - clickSlot(from.id, 0, SlotActionType.SWAP) - } - moved.add(preMove) - } ?: finish() - } - - listener { - if (selectedFrom.isEmpty() || moved.sumOf { it.count } >= selection.count) { - finish() + if (transactions.isEmpty() || moved) { + if (closeScreen) player.closeHandledScreen() + success(Unit) } - } - } - private fun SafeContext.finish() { - if (closeScreen) player.closeHandledScreen() - success(moved) + transactions.removeFirst().click() + } } companion object { @Ta5kBuilder - inline fun moveItems( - screen: H, + fun moveItems( + screen: ScreenHandler, selection: StackSelection, from: List, to: List, - ) = InventoryTask(screen, selection, from, to) + closeScreen: Boolean = true + ) = InventoryTask(screen, selection, from, to, closeScreen) @Ta5kBuilder - inline fun withdraw(screen: H, selection: StackSelection) = + fun withdraw(screen: ScreenHandler, selection: StackSelection) = moveItems(screen, selection, screen.containerSlots, screen.inventorySlots) @Ta5kBuilder - inline fun deposit(screen: H, selection: StackSelection) = + fun deposit(screen: ScreenHandler, selection: StackSelection) = moveItems(screen, selection, screen.inventorySlots, screen.containerSlots) } } 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 7e1685292..a5fa9b655 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt @@ -14,29 +14,43 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class OpenContainer( +class OpenContainer( private val blockPos: BlockPos, private val waitForSlotLoad: Boolean = true, - private val rotationConfig: IRotationConfig = TaskFlow.rotation, - private val interactionConfig: InteractionConfig = TaskFlow.interact, + private val rotate: Boolean, + private val rotation: IRotationConfig = TaskFlow.rotation, + private val interact: InteractionConfig = TaskFlow.interact, private val sides: Set = emptySet(), -) : Task() { - private var screenHandler: H? = null - private var slotsLoaded = false +) : Task() { + private var screenHandler: ScreenHandler? = null + private var state = State.SCOPING + private var inScope = 0 + + override var timeout = 50 + + enum class State { + SCOPING, OPENING, SLOT_LOADING + } init { - listener> { + listener { + if (state != State.OPENING) return@listener + screenHandler = it.screenHandler + state = State.SLOT_LOADING - if (!waitForSlotLoad || slotsLoaded) success(it.screenHandler) + if (!waitForSlotLoad) success(it.screenHandler) } - listener> { + listener { + if (screenHandler != it.screenHandler) return@listener + + state = State.SCOPING screenHandler = null } - listener { - slotsLoaded = true + listener { + if (state != State.SLOT_LOADING) return@listener screenHandler?.let { success(it) @@ -44,23 +58,30 @@ class OpenContainer( } listener { event -> - if (screenHandler != null) return@listener - event.context = lookAtBlock(blockPos, rotationConfig, interactionConfig, sides) + if (!rotate) return@listener + event.context = lookAtBlock(blockPos, rotation, interact, sides) } listener { - if (screenHandler != null) return@listener + if (!rotate) return@listener + if (state != State.SCOPING) return@listener if (!it.context.isValid) return@listener - val hitResult = it.context.hitResult?.blockResult ?: return@listener - interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) + + if (inScope++ >= interact.scopeThreshold) { + val hitResult = it.context.hitResult?.blockResult ?: return@listener + interaction.interactBlock(player, Hand.MAIN_HAND, hitResult) + + state = State.OPENING + } } } companion object { @Ta5kBuilder - inline fun openContainer( + fun openContainer( blockPos: BlockPos, - waitForSlotLoad: Boolean = true - ) = OpenContainer(blockPos, waitForSlotLoad) + waitForSlotLoad: Boolean = true, + rotate: Boolean = true, + ) = OpenContainer(blockPos, waitForSlotLoad, rotate) } } \ 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 65a5c550d..355f70e4b 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceBlock.kt @@ -1,35 +1,38 @@ package com.lambda.task.tasks import com.lambda.Lambda.LOG +import com.lambda.config.groups.InteractionConfig import com.lambda.context.SafeContext -import com.lambda.core.PingManager 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.blockplace.PlaceInteraction.placeBlock 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.warn import net.minecraft.block.BlockState class PlaceBlock @Ta5kBuilder constructor( private val ctx: PlaceContext, - private val swingHand: Boolean, private val rotate: Boolean, + private val interact: InteractionConfig = TaskFlow.interact, private val waitForConfirmation: Boolean, ) : Task() { private var beginState: BlockState? = null - override var cooldown = Int.MAX_VALUE - get() = maxOf(TaskFlow.build.placeCooldown, TaskFlow.taskCooldown) - override var timeout = 50 - private var inScope = 0 + private var state = State.ROTATING + private var findOutIfNeeded = 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) + enum class State { + ROTATING, PLACING, CONFIRMING + } + override fun SafeContext.onStart() { if (matches) { finish() @@ -37,29 +40,32 @@ class PlaceBlock @Ta5kBuilder constructor( } beginState = resultingState - if (!rotate) placeBlock() + if (!rotate) { + placeBlock() + } } init { listener { event -> + if (state != State.ROTATING) return@listener if (!rotate) return@listener event.context = ctx.rotation } - listener { + listener { event -> + if (state != State.ROTATING) return@listener if (!rotate) return@listener - if (!it.context.isValid) return@listener - - if (TaskFlow.build.pingTimeout) { - val threshold = PingManager.lastPing / 50L - if (++inScope >= threshold) { - inScope = 0 - placeBlock() - } - return@listener - } + if (event.context != ctx.rotation) return@listener + if (!event.context.isValid) return@listener - placeBlock() + state = State.PLACING + } + + listener { + if (state != State.PLACING) return@listener + + if (findOutIfNeeded) placeBlock() + findOutIfNeeded = true } listener { @@ -72,7 +78,29 @@ class PlaceBlock @Ta5kBuilder constructor( } private fun SafeContext.placeBlock() { - placeBlock(ctx.result, ctx.hand, swingHand) + val actionResult = interaction.interactBlock( + player, + ctx.hand, + ctx.result + ) + + if (actionResult.isAccepted) { + if (actionResult.shouldSwingHand() && interact.swingHand) { + player.swingHand(ctx.hand) + } + + if (!player.getStackInHand(ctx.hand).isEmpty && interaction.hasCreativeInventory()) { + mc.gameRenderer.firstPersonRenderer.resetEquipProgress(ctx.hand) + } + + state = State.CONFIRMING + + if (matches) { + if (!waitForConfirmation) finish() + } + } else { + warn("Internal interaction failed with $actionResult") + } } private fun SafeContext.finish() { @@ -88,9 +116,9 @@ class PlaceBlock @Ta5kBuilder constructor( @Ta5kBuilder fun placeBlock( ctx: PlaceContext, - swingHand: Boolean = TaskFlow.interact.swingHand, rotate: Boolean = TaskFlow.build.rotateForPlace, waitForConfirmation: Boolean = TaskFlow.build.placeConfirmation, - ) = PlaceBlock(ctx, swingHand, rotate, waitForConfirmation) + interact: InteractionConfig = TaskFlow.interact, + ) = PlaceBlock(ctx, rotate, interact, waitForConfirmation) } } 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 b0fd34c96..669032f13 100644 --- a/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/common/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt @@ -3,16 +3,21 @@ 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.context.PlaceContext 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.task.tasks.BuildTask.Companion.build import com.lambda.util.BlockUtils.blockPos +import com.lambda.util.item.ItemUtils.shulkerBoxes +import net.minecraft.block.ChestBlock +import net.minecraft.entity.mob.ShulkerEntity import net.minecraft.item.ItemStack +import net.minecraft.item.Items import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction class PlaceContainer @Ta5kBuilder constructor( val stack: ItemStack, @@ -30,10 +35,15 @@ class PlaceContainer @Ta5kBuilder constructor( // val res = results.sorted() // res - val succeeds = results.filterIsInstance() - val wrongStacks = results.filterIsInstance() + val succeeds = results.filterIsInstance().filter { + canBeOpened(stack, it.blockPos, it.context.result.side) + } + val wrongStacks = results.filterIsInstance().filter { + val result = (it.context as? PlaceContext)?.result ?: return@filter false + canBeOpened(stack, it.blockPos, result.side) + } (succeeds + wrongStacks).minOrNull()?.let { result -> - buildStructure { + build { result.blockPos .toStructure(TargetState.Stack(stack)) .toBlueprint() @@ -45,9 +55,28 @@ class PlaceContainer @Ta5kBuilder constructor( } } + private fun SafeContext.canBeOpened( + itemStack: ItemStack, + blockPos: BlockPos, + direction: Direction, + ) = when (itemStack.item) { + Items.ENDER_CHEST -> { + !ChestBlock.isChestBlocked(world, blockPos) + } + in shulkerBoxes -> { + val box = ShulkerEntity + .calculateBoundingBox(direction, 0.0f, 0.5f) + .offset(blockPos) + .contract(1.0E-6) + world.isSpaceEmpty(box) + } + else -> false + } + companion object { @Ta5kBuilder - fun placeContainer(stack: ItemStack) = - PlaceContainer(stack) + fun placeContainer( + stack: ItemStack, + ) = PlaceContainer(stack) } } \ No newline at end of file diff --git a/common/src/main/kotlin/com/lambda/threading/Threading.kt b/common/src/main/kotlin/com/lambda/threading/Threading.kt index 05e04d093..a3df8e905 100644 --- a/common/src/main/kotlin/com/lambda/threading/Threading.kt +++ b/common/src/main/kotlin/com/lambda/threading/Threading.kt @@ -62,7 +62,7 @@ inline fun taskContext(crossinline block: suspend () -> Unit) = * * @param block The block of code to be executed within the safe context. */ -inline fun runSafeConcurrent(crossinline block: SafeContext.() -> Unit) { +inline fun runSafeConcurrent(crossinline block: suspend SafeContext.() -> Unit) { EventFlow.lambdaScope.launch { 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 6741d5cc5..93c4811cb 100644 --- a/common/src/main/kotlin/com/lambda/util/BlockUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/BlockUtils.kt @@ -14,7 +14,7 @@ import net.minecraft.util.math.* import net.minecraft.world.BlockView object BlockUtils { - private val shulkerBlocks = shulkerBoxes.map { it.block } + val shulkerBlocks = shulkerBoxes.map { it.block } val interactionBlacklist = mutableSetOf( Blocks.CHEST, diff --git a/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt b/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt deleted file mode 100644 index dec109bfe..000000000 --- a/common/src/main/kotlin/com/lambda/util/TransformedObservable.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.lambda.util - -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -abstract class TransformedObservable(initialValue: T) : ReadWriteProperty { - private var value = initialValue - - override fun getValue(thisRef: Any?, property: KProperty<*>) = value - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - val oldValue = this.value - this.value = transform(value) - if (oldValue != this.value) onChange(oldValue, this.value) - } - - protected open fun transform(value: T): T = value - protected open fun onChange(oldValue: T, newValue: T) {} - - override fun toString(): String = "TransformedObservableProperty(value=$value)" -} 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 ce1a94dd7..c1cc9d4e9 100644 --- a/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/common/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -1,6 +1,7 @@ package com.lambda.util.player import com.lambda.context.SafeContext +import com.lambda.threading.runSafe import net.minecraft.client.network.ClientPlayerEntity import net.minecraft.item.ItemStack import net.minecraft.screen.slot.SlotActionType @@ -27,4 +28,16 @@ object SlotUtils { player, ) } + + data class Transaction( + val slotId: Int, + val button: Int, + val actionType: SlotActionType, + ) { + fun click() { + runSafe { + clickSlot(slotId, button, actionType) + } + } + } } \ No newline at end of file