diff --git a/build.gradle.kts b/build.gradle.kts index c4f760579..a0c3c0627 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -196,7 +196,7 @@ dependencies { modCompileOnly("maven.modrinth:litematica:$litematicaVersion") // DevLogin -// modRuntimeOnly("com.ptsmods:devlogin:3.5.1:fabric") + modRuntimeOnly("com.ptsmods:devlogin:3.5.1:fabric") // Test implementations testImplementation(kotlin("test")) diff --git a/gradle.properties b/gradle.properties index 08d727a15..abbaeef67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,7 +35,7 @@ baritoneVersion=1.14.0 discordIPCVersion=6f6b6cce17 classGraphVersion=4.8.184 ktorVersion=3.3.3 -jacksonVersion=3.1.1 +jacksonVersion=3.1.4 mockkVersion=1.14.7 spairVersion=1.90.0 diff --git a/src/main/java/com/lambda/mixin/MinecraftClientMixin.java b/src/main/java/com/lambda/mixin/MinecraftClientMixin.java index 768ece4c2..d20a73c7a 100644 --- a/src/main/java/com/lambda/mixin/MinecraftClientMixin.java +++ b/src/main/java/com/lambda/mixin/MinecraftClientMixin.java @@ -21,7 +21,7 @@ import com.lambda.event.events.*; import com.lambda.gui.DearImGui; import com.lambda.gui.components.ClickGuiLayout; -import com.lambda.interaction.handlers.TimerHandler; +import com.lambda.interaction.handler.handlers.TimerHandler; import com.lambda.module.modules.movement.BetterFirework; import com.lambda.module.modules.player.Interact; import com.lambda.module.modules.player.InventoryMove; @@ -141,7 +141,8 @@ 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)) + @SuppressWarnings("DiscouragedShift") + @Inject(method = "setScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/Screen;removed()V", shift = At.Shift.BEFORE)) private void onScreenRemove(@Nullable Screen screen, CallbackInfo ci) { if (currentScreen == null) return; if (currentScreen instanceof ScreenHandlerProvider> handledScreen) { diff --git a/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java b/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java index dbdb9a9d2..5630fb0b2 100644 --- a/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java +++ b/src/main/java/com/lambda/mixin/entity/ClientPlayInteractionManagerMixin.java @@ -20,9 +20,10 @@ import com.lambda.event.EventFlow; import com.lambda.event.events.InventoryEvent; import com.lambda.event.events.PlayerEvent; -import com.lambda.interaction.handlers.BaritoneHandler; -import com.lambda.interaction.managers.inventory.InventoryManager; -import com.lambda.interaction.managers.rotating.RotationManager; +import com.lambda.interaction.handler.handlers.BaritoneHandler; +import com.lambda.interaction.manager.managers.inventory.InventoryManager; +import com.lambda.interaction.manager.managers.rotating.RotationManager; +import com.lambda.module.modules.client.Client; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; @@ -111,6 +112,17 @@ public void clickSlotHead(int syncId, int slotId, int button, SlotActionType act if (EventFlow.post(click).isCanceled()) ci.cancel(); } + @Inject(method = "clickSlot", at = @At("TAIL")) + public void clickSlotTail(int syncId, int slotId, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo ci) { + if (syncId != player.currentScreenHandler.syncId) return; + EventFlow.post(new PlayerEvent.SlotClick.Post(syncId, slotId, button, actionType, player.currentScreenHandler)); + } + + @ModifyExpressionValue(method = "clickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandler;getRevision()I")) + private int modifyClickSlotRevision(int originalRevision) { + return Client.INSTANCE.getAvoidInventoryDesync() ? -1 : originalRevision; + } + /** * Posts {@link InventoryEvent.HotbarSlot.Update} and returns the event value as the selected slot *
{@code
@@ -144,7 +156,7 @@ private void cancelBlockBreakingPre(CallbackInfo ci) {
@WrapMethod(method = "createPlayer(Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/stat/StatHandler;Lnet/minecraft/client/recipebook/ClientRecipeBook;)Lnet/minecraft/client/network/ClientPlayerEntity;")
private ClientPlayerEntity wrapCreatePlayer(ClientWorld world, StatHandler statHandler, ClientRecipeBook recipeBook, Operation original) {
var player = original.call(world, statHandler, recipeBook);
- InventoryManager.INSTANCE.setScreenHandler(player.playerScreenHandler);
+ InventoryManager.onSetScreenHandler(player.playerScreenHandler);
return player;
}
}
diff --git a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
index c8bd52b47..136c9d2d4 100644
--- a/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/ClientPlayerEntityMixin.java
@@ -19,8 +19,8 @@
import com.lambda.event.EventFlow;
import com.lambda.event.events.*;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.movement.NoJumpCooldown;
import com.lambda.module.modules.movement.elytrafly.ElytraFly;
import com.lambda.module.modules.player.PortalGui;
@@ -56,7 +56,7 @@
import java.util.Objects;
import static com.lambda.Lambda.getMc;
-import static com.lambda.interaction.managers.rotating.Rotation.dist;
+import static com.lambda.interaction.manager.managers.rotating.Rotation.dist;
@Mixin(value = ClientPlayerEntity.class, priority = Integer.MAX_VALUE)
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity {
diff --git a/src/main/java/com/lambda/mixin/entity/EntityMixin.java b/src/main/java/com/lambda/mixin/entity/EntityMixin.java
index 2c31d2ff8..99fb142f6 100644
--- a/src/main/java/com/lambda/mixin/entity/EntityMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/EntityMixin.java
@@ -20,8 +20,8 @@
import com.lambda.event.EventFlow;
import com.lambda.event.events.EntityEvent;
import com.lambda.event.events.PlayerEvent;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.movement.elytrafly.ElytraFly;
import com.lambda.module.modules.render.NoRender;
import com.lambda.util.math.Vec2d;
diff --git a/src/main/java/com/lambda/mixin/entity/HandledScreensMixin.java b/src/main/java/com/lambda/mixin/entity/HandledScreensMixin.java
index 079ea9411..106e0bbb0 100644
--- a/src/main/java/com/lambda/mixin/entity/HandledScreensMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/HandledScreensMixin.java
@@ -17,7 +17,7 @@
package com.lambda.mixin.entity;
-import com.lambda.interaction.managers.inventory.InventoryManager;
+import com.lambda.interaction.manager.managers.inventory.InventoryManager;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreens;
import net.minecraft.screen.ScreenHandler;
diff --git a/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java b/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
index 1f3caf618..9af7e6302 100644
--- a/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/LivingEntityMixin.java
@@ -20,9 +20,9 @@
import com.lambda.Lambda;
import com.lambda.event.EventFlow;
import com.lambda.event.events.MovementEvent;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.handlers.GlideHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.handler.handlers.GlideHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.movement.Velocity;
import com.lambda.module.modules.movement.elytrafly.ElytraFly;
import com.lambda.module.modules.render.ViewModel;
@@ -49,7 +49,6 @@
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends EntityMixin {
-
@Unique private final LivingEntity lambda$instance = (LivingEntity) (Object) this;
@Definition(id = "getJumpVelocity", method = "Lnet/minecraft/entity/LivingEntity;getJumpVelocity()F")
diff --git a/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java b/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java
index 8a0fc6d0d..c7722b2bc 100644
--- a/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/PlayerEntityMixin.java
@@ -20,9 +20,9 @@
import com.lambda.Lambda;
import com.lambda.event.EventFlow;
import com.lambda.event.events.MovementEvent;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.handlers.GlideHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.handler.handlers.GlideHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.player.Reach;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
diff --git a/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java b/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
index 26c4c0a91..e538912bd 100644
--- a/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
+++ b/src/main/java/com/lambda/mixin/entity/PlayerInventoryMixin.java
@@ -18,7 +18,7 @@
package com.lambda.mixin.entity;
import com.lambda.Lambda;
-import com.lambda.interaction.managers.hotbar.HotbarManager;
+import com.lambda.interaction.manager.managers.hotbar.HotbarManager;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.entity.player.PlayerInventory;
import org.objectweb.asm.Opcodes;
diff --git a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java
index 9f83518dd..a85af677e 100644
--- a/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java
+++ b/src/main/java/com/lambda/mixin/network/ClientPlayNetworkHandlerMixin.java
@@ -23,8 +23,8 @@
import com.lambda.event.events.InventoryEvent;
import com.lambda.event.events.PlayerEvent;
import com.lambda.event.events.WorldEvent;
-import com.lambda.interaction.managers.inventory.InventoryManager;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.manager.managers.inventory.InventoryManager;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.movement.Velocity;
import com.lambda.module.modules.render.NoRender;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
@@ -134,6 +134,11 @@ private void wrapOnInventory(InventoryS2CPacket packet, Operation original
InventoryManager.onInventoryUpdate(packet, original);
}
+ @WrapMethod(method = "onSetCursorItem")
+ private void wrapOnSetCursorItem(SetCursorItemS2CPacket packet, Operation original) {
+ InventoryManager.onCursorUpdate(packet, original);
+ }
+
@WrapMethod(method = "sendChatMessage(Ljava/lang/String;)V")
void onSendMessage(String content, Operation original) {
var event = new ChatEvent.Send(content);
diff --git a/src/main/java/com/lambda/mixin/render/CameraMixin.java b/src/main/java/com/lambda/mixin/render/CameraMixin.java
index 38f629489..e6ca7231f 100644
--- a/src/main/java/com/lambda/mixin/render/CameraMixin.java
+++ b/src/main/java/com/lambda/mixin/render/CameraMixin.java
@@ -17,7 +17,7 @@
package com.lambda.mixin.render;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.render.CameraTweaks;
import com.lambda.module.modules.render.FreeLook;
import com.lambda.module.modules.render.Freecam;
diff --git a/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java b/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
index dc18dc35a..86ffe2d5e 100644
--- a/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
+++ b/src/main/java/com/lambda/mixin/render/LivingEntityRendererMixin.java
@@ -18,8 +18,8 @@
package com.lambda.mixin.render;
import com.lambda.Lambda;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.lambda.module.modules.render.Nametags;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
diff --git a/src/main/java/com/lambda/mixin/render/PlayerListHudMixin.java b/src/main/java/com/lambda/mixin/render/PlayerListHudMixin.java
index eefaba6f1..f6e090956 100644
--- a/src/main/java/com/lambda/mixin/render/PlayerListHudMixin.java
+++ b/src/main/java/com/lambda/mixin/render/PlayerListHudMixin.java
@@ -17,7 +17,7 @@
package com.lambda.mixin.render;
-import com.lambda.interaction.handlers.FriendHandler;
+import com.lambda.interaction.handler.handlers.FriendHandler;
import com.lambda.module.modules.render.ExtraTab;
import com.lambda.util.text.TextBuilder;
import com.lambda.util.text.TextDslKt;
diff --git a/src/main/java/com/lambda/mixin/world/DirectionMixin.java b/src/main/java/com/lambda/mixin/world/DirectionMixin.java
index 765248bb3..da8698f2e 100644
--- a/src/main/java/com/lambda/mixin/world/DirectionMixin.java
+++ b/src/main/java/com/lambda/mixin/world/DirectionMixin.java
@@ -17,8 +17,8 @@
package com.lambda.mixin.world;
-import com.lambda.interaction.handlers.BaritoneHandler;
-import com.lambda.interaction.managers.rotating.RotationManager;
+import com.lambda.interaction.handler.handlers.BaritoneHandler;
+import com.lambda.interaction.manager.managers.rotating.RotationManager;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Direction;
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/BlockArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/BlockArguments.kt
index bafbb787a..9c0034f3f 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/BlockArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/BlockArguments.kt
@@ -37,9 +37,8 @@ import java.util.function.Predicate
* @see BlockPredicateArgumentType.getBlockPredicate
*/
@BrigadierDsl
-fun DefaultArgumentReader.value(): Predicate {
- return BlockPredicateArgumentType.getBlockPredicate(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): Predicate =
+ BlockPredicateArgumentType.getBlockPredicate(context.assumeSourceNotUsed(), name)
/**
* Reads the [BlockStateArgument] value of the argument in
@@ -48,9 +47,8 @@ fun DefaultArgumentReader.value(): Predicate.value(): BlockStateArgument {
- return BlockStateArgumentType.getBlockState(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): BlockStateArgument =
+ BlockStateArgumentType.getBlockState(context.assumeSourceNotUsed(), name)
/**
* Creates a block predicate argument with [name] as the parameter name.
@@ -62,9 +60,8 @@ fun DefaultArgumentReader.value(): BlockStateArgument {
fun blockPredicate(
name: String,
registryAccess: CommandRegistryAccess,
-): DefaultArgumentConstructor {
- return argument(name, BlockPredicateArgumentType.blockPredicate(registryAccess))
-}
+): DefaultArgumentConstructor =
+ argument(name, BlockPredicateArgumentType.blockPredicate(registryAccess))
/**
* Creates a block state argument with [name] as the parameter name.
@@ -76,6 +73,5 @@ fun blockPredicate(
fun blockState(
name: String,
registryAccess: CommandRegistryAccess,
-): DefaultArgumentConstructor {
- return argument(name, BlockStateArgumentType.blockState(registryAccess))
-}
+): DefaultArgumentConstructor =
+ argument(name, BlockStateArgumentType.blockState(registryAccess))
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/GeometricArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/GeometricArguments.kt
index 6c2117456..065bb8e0c 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/GeometricArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/GeometricArguments.kt
@@ -167,9 +167,8 @@ fun rotation(name: String): DefaultArgumentConstructor swizzle(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, SwizzleArgumentType.swizzle())
-}
+): DefaultArgumentConstructor =
+ argument(name, SwizzleArgumentType.swizzle())
/**
* Creates a block pos argument with [name] as the parameter name.
@@ -177,9 +176,8 @@ fun swizzle(
@BrigadierDsl
fun blockPos(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, BlockPosArgumentType.blockPos())
-}
+): DefaultArgumentConstructor =
+ argument(name, BlockPosArgumentType.blockPos())
/**
* Creates a column pos argument with [name] as the parameter name.
@@ -187,9 +185,8 @@ fun blockPos(
@BrigadierDsl
fun columnPos(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, ColumnPosArgumentType.columnPos())
-}
+): DefaultArgumentConstructor =
+ argument(name, ColumnPosArgumentType.columnPos())
/**
* Creates a vec2 argument with [name] as the parameter name.
@@ -200,9 +197,8 @@ fun columnPos(
fun vec2(
name: String,
centerIntegers: Boolean = false,
-): DefaultArgumentConstructor {
- return argument(name, Vec2ArgumentType.vec2(centerIntegers))
-}
+): DefaultArgumentConstructor =
+ argument(name, Vec2ArgumentType.vec2(centerIntegers))
/**
* Creates a vec3 argument with [name] as the parameter name.
@@ -213,6 +209,5 @@ fun vec2(
fun vec3(
name: String,
centerIntegers: Boolean = false,
-): DefaultArgumentConstructor {
- return argument(name, Vec3ArgumentType.vec3(centerIntegers))
-}
+): DefaultArgumentConstructor =
+ argument(name, Vec3ArgumentType.vec3(centerIntegers))
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/IdentifierArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/IdentifierArguments.kt
index c88a39cb3..d2a3b0218 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/IdentifierArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/IdentifierArguments.kt
@@ -27,8 +27,6 @@ import com.lambda.brigadier.assumeSourceNotUsed
import net.minecraft.advancement.AdvancementEntry
import net.minecraft.command.argument.IdentifierArgumentType
import net.minecraft.command.argument.RegistryKeyArgumentType
-import net.minecraft.loot.condition.LootCondition
-import net.minecraft.loot.function.LootFunction
import net.minecraft.recipe.RecipeEntry
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.util.Identifier
@@ -41,9 +39,8 @@ import net.minecraft.util.Identifier
*/
@JvmName("valueIdentifierArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Identifier {
- return IdentifierArgumentType.getIdentifier(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): Identifier =
+ IdentifierArgumentType.getIdentifier(context.assumeSourceNotUsed(), name)
/**
* Reads the [Identifier] value from the
@@ -53,22 +50,16 @@ fun DefaultArgumentReader.value(): Identifier {
* @see RegistryKeyArgumentType.getAdvancementEntry
*/
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- DefaultArgumentDescriptor<
- IdentifierArgumentType
- >
- >.asAdvancement(): AdvancementEntry {
- return RegistryKeyArgumentType.getAdvancementEntry(context, name)
-}
+fun ArgumentReader>.asAdvancement(): AdvancementEntry =
+ RegistryKeyArgumentType.getAdvancementEntry(context, name)
-/**
- * Reads the [Identifier] value from the
- * argument in the receiver [ArgumentReader]
- * as a [LootCondition].
- *
- * @see IdentifierArgumentType.getPredicateArgument
- */
+///**
+// * Reads the [Identifier] value from the
+// * argument in the receiver [ArgumentReader]
+// * as a [LootCondition].
+// *
+// * @see IdentifierArgumentType.getPredicateArgument
+// */
//@BrigadierDsl
//fun ArgumentReader<
// ServerCommandSource,
@@ -79,13 +70,13 @@ fun ArgumentReader<
// return IdentifierArgumentType.getPredicateArgument(context, name)
//}
-/**
- * Reads the [Identifier] value from the
- * argument in the receiver [ArgumentReader]
- * as a [LootFunction].
- *
- * @see IdentifierArgumentType.getItemModifierArgument
- */
+///**
+// * Reads the [Identifier] value from the
+// * argument in the receiver [ArgumentReader]
+// * as a [LootFunction].
+// *
+// * @see IdentifierArgumentType.getItemModifierArgument
+// */
//@BrigadierDsl
//fun ArgumentReader<
// ServerCommandSource,
@@ -104,14 +95,8 @@ fun ArgumentReader<
* @see IdentifierArgumentType.getRecipeArgument
*/
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- DefaultArgumentDescriptor<
- IdentifierArgumentType
- >
- >.asRecipe(): RecipeEntry<*> {
- return RegistryKeyArgumentType.getRecipeEntry(context, name)
-}
+fun ArgumentReader>.asRecipe(): RecipeEntry<*> =
+ RegistryKeyArgumentType.getRecipeEntry(context, name)
/**
* Creates an identifier argument with [name] as the parameter name.
@@ -119,6 +104,5 @@ fun ArgumentReader<
@BrigadierDsl
fun identifier(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, IdentifierArgumentType.identifier())
-}
+): DefaultArgumentConstructor =
+ argument(name, IdentifierArgumentType.identifier())
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt
index 846cdb2e3..ecaa559ef 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/ItemArguments.kt
@@ -46,9 +46,8 @@ import java.util.function.Predicate
*/
@JvmName("valueItemPredicateArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Predicate {
- return ItemPredicateArgumentType.getItemStackPredicate(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): Predicate =
+ ItemPredicateArgumentType.getItemStackPredicate(context.assumeSourceNotUsed(), name)
/**
* Reads the integer value from the
@@ -58,9 +57,8 @@ fun DefaultArgumentReader.value(): Predicate.value(): Int {
- return ItemSlotArgumentType.getItemSlot(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): Int =
+ ItemSlotArgumentType.getItemSlot(context.assumeSourceNotUsed(), name)
/**
* Reads the [ItemStackArgument] value from the
@@ -70,9 +68,8 @@ fun DefaultArgumentReader.value(): Int {
*/
@JvmName("valueItemStackArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): ItemStackArgument {
- return ItemStackArgumentType.getItemStackArgument(context, name)
-}
+fun DefaultArgumentReader.value(): ItemStackArgument =
+ ItemStackArgumentType.getItemStackArgument(context, name)
/**
* Creates an item predicate argument with [name] as the parameter name.
@@ -83,9 +80,8 @@ fun DefaultArgumentReader.value(): ItemStackArgument {
fun itemPredicate(
name: String,
context: CommandRegistryAccess,
-): DefaultArgumentConstructor {
- return argument(name, ItemPredicateArgumentType.itemPredicate(context))
-}
+): DefaultArgumentConstructor =
+ argument(name, ItemPredicateArgumentType.itemPredicate(context))
/**
* Creates an item slot argument with [name] as the parameter name.
@@ -93,9 +89,8 @@ fun itemPredicate(
@BrigadierDsl
fun itemSlot(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, ItemSlotArgumentType.itemSlot())
-}
+): DefaultArgumentConstructor =
+ argument(name, ItemSlotArgumentType.itemSlot())
/**
* Creates an item stack argument with [name] as the parameter name.
@@ -106,6 +101,5 @@ fun itemSlot(
fun itemStack(
name: String,
context: CommandRegistryAccess,
-): DefaultArgumentConstructor {
- return argument(name, ItemStackArgumentType.itemStack(context))
-}
+): DefaultArgumentConstructor =
+ argument(name, ItemStackArgumentType.itemStack(context))
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/MiscArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/MiscArguments.kt
index 3deb92a56..7a2c1f498 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/MiscArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/MiscArguments.kt
@@ -48,9 +48,8 @@ object LiteralDescriptor : ArgumentDescriptor>
*/
@JvmName("valueTimeArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Int {
- return IntegerArgumentType.getInteger(context, name)
-} // TimeArgumentType does not provide an accessor, defaulting to int
+fun DefaultArgumentReader.value(): Int =
+ IntegerArgumentType.getInteger(context, name) // TimeArgumentType does not provide an accessor, defaulting to int
/**
* Reads the [UUID] value from the
@@ -60,9 +59,8 @@ fun DefaultArgumentReader.value(): Int {
*/
@JvmName("valueUuidArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): UUID {
- return UuidArgumentType.getUuid(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): UUID =
+ UuidArgumentType.getUuid(context.assumeSourceNotUsed(), name)
/**
* Creates a time argument with [name] as the parameter name.
@@ -73,9 +71,8 @@ fun DefaultArgumentReader.value(): UUID {
fun time(
name: String,
minimumTicks: Int = 0,
-): DefaultArgumentConstructor {
- return argument(name, TimeArgumentType.time(minimumTicks))
-}
+): DefaultArgumentConstructor =
+ argument(name, TimeArgumentType.time(minimumTicks))
/**
* Creates a UUID argument with [name] as the parameter name.
@@ -83,9 +80,8 @@ fun time(
@BrigadierDsl
fun uuid(
name: String,
-): RequiredArgumentConstructor> {
- return argument(name, UuidArgumentType.uuid())
-}
+): RequiredArgumentConstructor> =
+ argument(name, UuidArgumentType.uuid())
/**
* Creates a literal argument with [name] as the literal.
@@ -98,6 +94,5 @@ fun uuid(
@BrigadierDsl
fun literal(
name: String,
-): ArgumentConstructor, LiteralDescriptor> {
- return ArgumentConstructor(LiteralArgumentBuilder.literal(name), name, LiteralDescriptor)
-}
+): ArgumentConstructor, LiteralDescriptor> =
+ ArgumentConstructor(LiteralArgumentBuilder.literal(name), name, LiteralDescriptor)
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/NBTArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/NBTArguments.kt
index 08122cc82..41a7c13ac 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/NBTArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/NBTArguments.kt
@@ -45,9 +45,8 @@ import net.minecraft.nbt.NbtElement
*/
@JvmName("valueNbtCompoundArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): NbtCompound {
- return NbtCompoundArgumentType.getNbtCompound(context, name)
-}
+fun DefaultArgumentReader.value(): NbtCompound =
+ NbtCompoundArgumentType.getNbtCompound(context, name)
/**
* Reads the [NbtElement] value from the
@@ -57,9 +56,8 @@ fun DefaultArgumentReader.value(): NbtCompound {
*/
@JvmName("valueNbtElementArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): NbtElement {
- return NbtElementArgumentType.getNbtElement(context, name)
-}
+fun DefaultArgumentReader.value(): NbtElement =
+ NbtElementArgumentType.getNbtElement(context, name)
/**
* Reads the [NbtPath] value from the
@@ -69,9 +67,8 @@ fun DefaultArgumentReader.value(): NbtElement {
*/
@JvmName("valueNbtPathArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): NbtPath {
- return NbtPathArgumentType.getNbtPath(context.assumeSourceNotUsed(), name)
-}
+fun DefaultArgumentReader.value(): NbtPath =
+ NbtPathArgumentType.getNbtPath(context.assumeSourceNotUsed(), name)
/**
* Creates a nbt compound argument with [name] as the parameter name.
@@ -79,9 +76,8 @@ fun DefaultArgumentReader.value(): NbtPath {
@BrigadierDsl
fun nbtCompound(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, NbtCompoundArgumentType.nbtCompound())
-}
+): DefaultArgumentConstructor =
+ argument(name, NbtCompoundArgumentType.nbtCompound())
/**
* Creates an NBT element argument with [name] as the parameter name.
@@ -89,9 +85,8 @@ fun nbtCompound(
@BrigadierDsl
fun nbtElement(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, NbtElementArgumentType.nbtElement())
-}
+): DefaultArgumentConstructor =
+ argument(name, NbtElementArgumentType.nbtElement())
/**
* Creates an NBT path argument with [name] as the parameter name.
@@ -99,6 +94,5 @@ fun nbtElement(
@BrigadierDsl
fun nbtPath(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, NbtPathArgumentType.nbtPath())
-}
+): DefaultArgumentConstructor =
+ argument(name, NbtPathArgumentType.nbtPath())
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt
index 102021f26..36169393c 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/PlayerArguments.kt
@@ -65,14 +65,8 @@ object ListPlayerArgumentDescriptor : ArgumentDescriptor
*/
@JvmName("valueGameProfileArg")
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- DefaultArgumentDescriptor<
- GameProfileArgumentType
- >
- >.value(): Collection {
- return GameProfileArgumentType.getProfileArgument(context, name)
-}
+fun ArgumentReader>.value(): Collection =
+ GameProfileArgumentType.getProfileArgument(context, name)
/**
* Reads the [Team] value from the
@@ -82,14 +76,8 @@ fun ArgumentReader<
*/
@JvmName("valueTeamArg")
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- DefaultArgumentDescriptor<
- TeamArgumentType
- >
- >.value(): Team {
- return TeamArgumentType.getTeam(context, name)
-}
+fun ArgumentReader>.value(): Team =
+ TeamArgumentType.getTeam(context, name)
/**
* Reads the [ServerPlayerEntity] value from the
@@ -99,12 +87,8 @@ fun ArgumentReader<
*/
@JvmName("valuePlayerArg")
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- SinglePlayerArgumentDescriptor
- >.value(): ServerPlayerEntity {
- return EntityArgumentType.getPlayer(context, name)
-}
+fun ArgumentReader.value(): ServerPlayerEntity =
+ EntityArgumentType.getPlayer(context, name)
/**
* Reads the collection of players from the argument in
@@ -116,12 +100,8 @@ fun ArgumentReader<
*/
@JvmName("requiredPlayerArg")
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- ListPlayerArgumentDescriptor
- >.required(): Collection {
- return EntityArgumentType.getPlayers(context, name)
-}
+fun ArgumentReader.required(): Collection =
+ EntityArgumentType.getPlayers(context, name)
/**
* Reads the collection of players from the argument in
@@ -133,12 +113,8 @@ fun ArgumentReader<
*/
@JvmName("optionalPlayerArg")
@BrigadierDsl
-fun ArgumentReader<
- ServerCommandSource,
- ListPlayerArgumentDescriptor
- >.optional(): Collection {
- return EntityArgumentType.getOptionalPlayers(context, name)
-}
+fun ArgumentReader.optional(): Collection =
+ EntityArgumentType.getOptionalPlayers(context, name)
/**
* Creates a game profile argument with [name] as the parameter name.
@@ -146,9 +122,8 @@ fun ArgumentReader<
@BrigadierDsl
fun gameProfile(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, GameProfileArgumentType.gameProfile())
-}
+): DefaultArgumentConstructor =
+ argument(name, GameProfileArgumentType.gameProfile())
/**
* Creates a team argument with [name] as the parameter name.
@@ -156,9 +131,8 @@ fun gameProfile(
@BrigadierDsl
fun team(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, TeamArgumentType.team())
-}
+): DefaultArgumentConstructor =
+ argument(name, TeamArgumentType.team())
/**
* Creates a player selector argument with [name] as the parameter name.
@@ -166,12 +140,8 @@ fun team(
@BrigadierDsl
fun player(
name: String,
-): RequiredArgumentConstructor<
- S,
- SinglePlayerArgumentDescriptor
- > {
- return argument(name, EntityArgumentType.player(), SinglePlayerArgumentDescriptor)
-}
+): RequiredArgumentConstructor =
+ argument(name, EntityArgumentType.player(), SinglePlayerArgumentDescriptor)
/**
* Creates a multiple player selector argument with [name] as the parameter name.
@@ -179,9 +149,5 @@ fun player(
@BrigadierDsl
fun players(
name: String,
-): RequiredArgumentConstructor<
- S,
- ListPlayerArgumentDescriptor
- > {
- return argument(name, EntityArgumentType.players(), ListPlayerArgumentDescriptor)
-}
+): RequiredArgumentConstructor =
+ argument(name, EntityArgumentType.players(), ListPlayerArgumentDescriptor)
diff --git a/src/main/kotlin/com/lambda/brigadier/argument/PrimitiveArguments.kt b/src/main/kotlin/com/lambda/brigadier/argument/PrimitiveArguments.kt
index 6544a31f3..258676e5c 100644
--- a/src/main/kotlin/com/lambda/brigadier/argument/PrimitiveArguments.kt
+++ b/src/main/kotlin/com/lambda/brigadier/argument/PrimitiveArguments.kt
@@ -42,9 +42,8 @@ import com.mojang.brigadier.arguments.LongArgumentType
*/
@JvmName("valueBoolArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Boolean {
- return BoolArgumentType.getBool(context, name)
-}
+fun DefaultArgumentReader.value(): Boolean =
+ BoolArgumentType.getBool(context, name)
/**
* Reads the boolean value from the argument in
@@ -54,9 +53,8 @@ fun DefaultArgumentReader.value(): Boolean {
*/
@JvmName("valueDoubleArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Double {
- return DoubleArgumentType.getDouble(context, name)
-}
+fun DefaultArgumentReader.value(): Double =
+ DoubleArgumentType.getDouble(context, name)
/**
* Reads the float value from the argument in
@@ -66,9 +64,8 @@ fun DefaultArgumentReader.value(): Double {
*/
@JvmName("valueFloatArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Float {
- return FloatArgumentType.getFloat(context, name)
-}
+fun DefaultArgumentReader.value(): Float =
+ FloatArgumentType.getFloat(context, name)
/**
* Reads the integer value from the argument in
@@ -78,9 +75,8 @@ fun DefaultArgumentReader.value(): Float {
*/
@JvmName("valueIntArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Int {
- return IntegerArgumentType.getInteger(context, name)
-}
+fun DefaultArgumentReader.value(): Int =
+ IntegerArgumentType.getInteger(context, name)
/**
* Reads the long value from the argument in
@@ -90,9 +86,8 @@ fun DefaultArgumentReader.value(): Int {
*/
@JvmName("valueLongArg")
@BrigadierDsl
-fun DefaultArgumentReader.value(): Long {
- return LongArgumentType.getLong(context, name)
-}
+fun DefaultArgumentReader.value(): Long =
+ LongArgumentType.getLong(context, name)
/**
* Creates a boolean argument with [name] as the parameter name.
@@ -100,9 +95,8 @@ fun DefaultArgumentReader.value(): Long {
@BrigadierDsl
fun boolean(
name: String,
-): DefaultArgumentConstructor {
- return argument(name, BoolArgumentType.bool())
-}
+): DefaultArgumentConstructor =
+ argument(name, BoolArgumentType.bool())
/**
* Creates a double argument with [name] as the parameter name.
@@ -115,9 +109,8 @@ fun double(
name: String,
min: Double = -Double.MAX_VALUE,
max: Double = Double.MAX_VALUE,
-): DefaultArgumentConstructor {
- return argument(name, DoubleArgumentType.doubleArg(min, max))
-}
+): DefaultArgumentConstructor =
+ argument(name, DoubleArgumentType.doubleArg(min, max))
/**
* Creates a float argument with [name] as the parameter name.
@@ -130,9 +123,8 @@ fun float(
name: String,
min: Float = -Float.MAX_VALUE,
max: Float = Float.MAX_VALUE,
-): DefaultArgumentConstructor {
- return argument(name, FloatArgumentType.floatArg(min, max))
-}
+): DefaultArgumentConstructor =
+ argument(name, FloatArgumentType.floatArg(min, max))
/**
* Creates an integer argument with [name] as the parameter name.
@@ -145,9 +137,8 @@ fun integer(
name: String,
min: Int = -Int.MAX_VALUE,
max: Int = Int.MAX_VALUE,
-): DefaultArgumentConstructor {
- return argument(name, IntegerArgumentType.integer(min, max))
-}
+): DefaultArgumentConstructor =
+ argument(name, IntegerArgumentType.integer(min, max))
/**
* Creates a long argument with [name] as the parameter name.
@@ -160,6 +151,5 @@ fun long(
name: String,
min: Long = -Long.MAX_VALUE,
max: Long = Long.MAX_VALUE,
-): DefaultArgumentConstructor {
- return argument(name, LongArgumentType.longArg(min, max))
-}
+): DefaultArgumentConstructor =
+ argument(name, LongArgumentType.longArg(min, max))
diff --git a/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt b/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt
index 51e93f749..ccd2212ab 100644
--- a/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt
+++ b/src/main/kotlin/com/lambda/command/commands/BuildCommand.kt
@@ -28,10 +28,10 @@ import com.lambda.command.LambdaCommand
import com.lambda.config.automation.AutomationConfig
import com.lambda.interaction.construction.StructureRegistry
import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
-import com.lambda.interaction.construction.blueprint.StaticBlueprint.Companion.toBlueprint
-import com.lambda.task.RootTask.run
+import com.lambda.interaction.construction.blueprint.toBlueprint
+import com.lambda.task.start
import com.lambda.task.tasks.BuildTask
-import com.lambda.task.tasks.BuildTask.Companion.build
+import com.lambda.task.tasks.build
import com.lambda.threading.runSafe
import com.lambda.util.CommunicationUtils.info
import com.lambda.util.extension.CommandBuilder
@@ -62,12 +62,12 @@ object BuildCommand : LambdaCommand(
.loadStructureByRelativePath(Path.of(pathString))
.let { template ->
info("Building structure $pathString with dimensions ${template.size.toShortString()} created by ${template.author}")
- lastBuildTask = with(AutomationConfig.Companion.DEFAULT) {
+ lastBuildTask = with(AutomationConfig.DEFAULT) {
template.toStructure()
.move(player.blockPos)
.toBlueprint()
.build()
- .run()
+ .start()
}
return@executeWithResult success()
diff --git a/src/main/kotlin/com/lambda/command/commands/FriendCommand.kt b/src/main/kotlin/com/lambda/command/commands/FriendCommand.kt
index 87f4b8454..9b9441535 100644
--- a/src/main/kotlin/com/lambda/command/commands/FriendCommand.kt
+++ b/src/main/kotlin/com/lambda/command/commands/FriendCommand.kt
@@ -32,7 +32,7 @@ import com.lambda.brigadier.required
import com.lambda.command.CommandRegistry.prefix
import com.lambda.command.LambdaCommand
import com.lambda.config.categories.FriendCategory
-import com.lambda.interaction.handlers.FriendHandler
+import com.lambda.interaction.handler.handlers.FriendHandler
import com.lambda.interaction.handlers.FriendImporter
import com.lambda.network.mojang.getProfile
import com.lambda.threading.runIO
diff --git a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt
index 33adbaee7..b3d9392ee 100644
--- a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt
+++ b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt
@@ -28,12 +28,13 @@ import com.lambda.brigadier.executeWithResult
import com.lambda.brigadier.required
import com.lambda.command.LambdaCommand
import com.lambda.config.automation.AutomationConfig
-import com.lambda.interaction.handlers.ContainerHandler
-import com.lambda.interaction.handlers.ContainerHandler.findContainersWithMaterial
-import com.lambda.interaction.handlers.ContainerHandler.findContainersWithSpace
-import com.lambda.interaction.material.StackSelection.Companion.selectStack
-import com.lambda.task.RootTask
+import com.lambda.interaction.container.selection.containerSelection
+import com.lambda.interaction.container.selection.select
+import com.lambda.interaction.container.selection.stackSelection
+import com.lambda.interaction.handler.handlers.ContainerHandler.findContainers
import com.lambda.task.Task
+import com.lambda.task.start
+import com.lambda.task.tasks.transfer
import com.lambda.threading.runSafeAutomated
import com.lambda.util.CommunicationUtils.info
import com.lambda.util.extension.CommandBuilder
@@ -51,60 +52,64 @@ object TransferCommand : LambdaCommand(
required(integer("amount", 1)) { amount ->
required(string("from")) { from ->
suggests { ctx, builder ->
- val selection = selectStack(amount(ctx).value()) {
- isItem(stack(ctx).value().item)
- }
+ val selection = stack(ctx).value().item.select(amount(ctx).value())
AutomationConfig.DEFAULT.runSafeAutomated {
- val containers = selection.findContainersWithMaterial()
+ val containers =
+ findContainers(
+ containerSelection {
+ hasStack(selection)
+ }
+ ).toList()
val indexedContainers = containers.withIndex()
suggestMatching(
indexedContainers,
builder,
- { (index, container) ->
- "\"${index + 1}. ${container.name}\""
- },
- { (_, container) ->
- container.description(selection)
- }
+ { (index, container) -> "\"${index + 1}. ${container.name}\"" },
+ { (_, container) -> container.descriptionAndStock(selection) }
)
} ?: builder.buildFuture()
}
required(string("to")) { to ->
suggests { ctx, builder ->
- val selection = selectStack(amount(ctx).value()) {
- isItem(stack(ctx).value().item)
- }
+ val selection = stack(ctx).value().item.select(amount(ctx).value())
AutomationConfig.DEFAULT.runSafeAutomated {
- val containers = selection.findContainersWithSpace()
+ val containers =
+ findContainers(
+ containerSelection {
+ hasSpace(selection)
+ }
+ ).toList()
val indexedContainers = containers.withIndex()
suggestMatching(
indexedContainers,
builder,
- { (index, container) ->
- "\"${index + 1}. ${container.name}\""
- },
- { (_, container) ->
- container.description(selection)
- }
+ { (index, container) -> "\"${index + 1}. ${container.name}\"" },
+ { (_, container) -> container.descriptionAndStock(selection) }
)
} ?: builder.buildFuture()
}
executeWithResult {
- val selection = selectStack(amount().value()) {
- isItem(stack().value().item)
- }
AutomationConfig.DEFAULT.runSafeAutomated {
- val fromContainer = ContainerHandler.containers().find {
- it.name == from().value().split(".").last().trim()
- } ?: return@executeWithResult failure("From container not found")
+ val fromSelection =
+ containerSelection {
+ predicate { it.name == from().value().split(".").first().trim() }
+ }
- val toContainer = ContainerHandler.containers().find {
- it.name == to().value().split(".").last().trim()
- } ?: return@executeWithResult failure("To container not found")
+ val toSelection =
+ containerSelection {
+ predicate { it.name == to().value().split(".").first().trim() }
+ }
- fromContainer.transferByTask(selection, toContainer).execute(RootTask)
+ lastContainerTransfer =
+ transfer(
+ stackSelection(amount().value()) {
+ isItem(stack().value().item)
+ },
+ fromSelection,
+ toSelection
+ ).start()
}
return@executeWithResult success()
}
@@ -115,9 +120,11 @@ object TransferCommand : LambdaCommand(
required(literal("cancel")) {
executeWithResult {
- lastContainerTransfer?.cancel() ?: run {
- return@executeWithResult failure("No transfer to cancel")
- }
+ lastContainerTransfer
+ ?.cancel()
+ ?: run {
+ return@executeWithResult failure("No transfer to cancel")
+ }
this@TransferCommand.info("$lastContainerTransfer cancelled")
lastContainerTransfer = null
success()
diff --git a/src/main/kotlin/com/lambda/config/ConfigCategory.kt b/src/main/kotlin/com/lambda/config/ConfigCategory.kt
index c490a65e1..35ec67f6c 100644
--- a/src/main/kotlin/com/lambda/config/ConfigCategory.kt
+++ b/src/main/kotlin/com/lambda/config/ConfigCategory.kt
@@ -25,7 +25,7 @@ import com.lambda.config.migration.ConfigMigrationHandler
import com.lambda.core.Loadable
import com.lambda.event.events.ClientEvent
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
-import com.lambda.interaction.handlers.BaritoneHandler.primary
+import com.lambda.interaction.handler.handlers.BaritoneHandler.primary
import com.lambda.threading.runIO
import com.lambda.util.CommunicationUtils.info
import com.lambda.util.CommunicationUtils.logError
diff --git a/src/main/kotlin/com/lambda/config/ConfigEditor.kt b/src/main/kotlin/com/lambda/config/ConfigEditor.kt
index 8700d9889..df332bd5d 100644
--- a/src/main/kotlin/com/lambda/config/ConfigEditor.kt
+++ b/src/main/kotlin/com/lambda/config/ConfigEditor.kt
@@ -21,7 +21,7 @@ package com.lambda.config
import com.lambda.config.entries.Property
import com.lambda.config.entries.Setting
-import com.lambda.config.entries.Setting.Companion.onValueChange
+import com.lambda.config.entries.onValueChange
import com.lambda.context.SafeContext
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
diff --git a/src/main/kotlin/com/lambda/config/automation/AutomationConfig.kt b/src/main/kotlin/com/lambda/config/automation/AutomationConfig.kt
index 4272fdf96..d90420e89 100644
--- a/src/main/kotlin/com/lambda/config/automation/AutomationConfig.kt
+++ b/src/main/kotlin/com/lambda/config/automation/AutomationConfig.kt
@@ -31,6 +31,9 @@ import com.lambda.config.categories.AutomationCategory
import com.lambda.context.Automated
import com.lambda.module.Module
+@DslMarker
+private annotation class AutomationConfigMarker
+
open class AutomationConfig(
name: String,
configCategory: ConfigCategory = AutomationCategory
@@ -55,20 +58,17 @@ open class AutomationConfig(
private const val HOTBAR_TAB = "Hotbar"
private const val EAT_TAB = "Eat"
- @DslMarker
- private annotation class AutomationConfigMarker
-
- @AutomationConfigMarker
- context(module: Module)
- fun IMutableAutomationConfig.setDefaultAutomationConfig(
- name: String = module.name
- ) = AutomationConfig("$name Automation Config").also { this.defaultAutomationConfig = it }
-
- @AutomationConfigMarker
- fun IMutableAutomationConfig.setDefaultAutomationConfig(
- name: String
- ) = AutomationConfig("$name Automation Config").also { this.defaultAutomationConfig = it }
-
val DEFAULT = AutomationConfig("Default")
}
-}
\ No newline at end of file
+}
+
+@AutomationConfigMarker
+context(module: Module)
+fun IMutableAutomationConfig.setDefaultAutomationConfig(
+ name: String = module.name
+) = AutomationConfig("$name Automation Config").also { this.defaultAutomationConfig = it }
+
+@AutomationConfigMarker
+fun IMutableAutomationConfig.setDefaultAutomationConfig(
+ name: String
+) = AutomationConfig("$name Automation Config").also { this.defaultAutomationConfig = it }
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/config/blocks/BreakSettings.kt b/src/main/kotlin/com/lambda/config/blocks/BreakSettings.kt
index e44e1ed4f..1449738a6 100644
--- a/src/main/kotlin/com/lambda/config/blocks/BreakSettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/BreakSettings.kt
@@ -28,7 +28,7 @@ import com.lambda.config.blocks.BreakConfig.SwapMode
import com.lambda.config.blocks.BreakConfig.SwingMode
import com.lambda.config.blocks.BreakConfig.WhitelistMode
import com.lambda.config.blocks.BuildConfig.SwingType
-import com.lambda.config.entries.Setting.Companion.onValueChangeUnsafe
+import com.lambda.config.entries.onValueChangeUnsafe
import com.lambda.config.hide
import com.lambda.config.withEdits
import com.lambda.event.events.TickEvent
diff --git a/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt b/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt
index 7e98fc835..6e7922008 100644
--- a/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt
@@ -17,8 +17,8 @@
package com.lambda.config.blocks
-import com.lambda.interaction.managers.rotating.Rotation.Companion.dist
-import com.lambda.interaction.managers.rotating.RotationManager
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.dist
+import com.lambda.interaction.manager.managers.rotating.RotationManager
import com.lambda.util.Describable
import com.lambda.util.NamedEnum
import com.lambda.util.math.distSq
diff --git a/src/main/kotlin/com/lambda/config/blocks/EatConfig.kt b/src/main/kotlin/com/lambda/config/blocks/EatConfig.kt
index 1e0e5714b..548540934 100644
--- a/src/main/kotlin/com/lambda/config/blocks/EatConfig.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/EatConfig.kt
@@ -19,7 +19,8 @@ package com.lambda.config.blocks
import com.lambda.context.Automated
import com.lambda.context.AutomatedSafeContext
-import com.lambda.interaction.material.StackSelection.Companion.selectStack
+import com.lambda.interaction.container.selection.StackAndSlot
+import com.lambda.interaction.container.selection.stackSelection
import com.lambda.threading.runSafe
import com.lambda.util.Describable
import com.lambda.util.NamedEnum
@@ -55,17 +56,17 @@ interface EatConfig {
@Suppress("unused")
enum class SelectionPriority(
- val comparator: Comparator,
+ val comparator: Comparator>,
override val displayName: String,
override val description: String
): NamedEnum, Describable {
LeastNutritious(
- compareBy { it.item.nutrition },
+ compareBy { it.stack.item.nutrition },
"Least Nutritious",
"Eats food items with the least nutritional value."
),
MostNutritious(
- compareByDescending { it.item.nutrition },
+ compareByDescending { it.stack.item.nutrition },
"Most Nutritious",
"Eats food items with the most nutritional value."
)
@@ -94,14 +95,17 @@ interface EatConfig {
} ?: false
context(c: Automated)
- fun selector() = selectStack(sorter = c.eatConfig.selectionPriority.comparator) {
- when(this@Reason) {
- None -> any()
- Hunger -> isOneOfItems(c.eatConfig.nutritiousFood)
- Damage -> isOneOfItems(c.eatConfig.regenerationFood)
- Fire -> isOneOfItems(c.eatConfig.resistanceFood)
- } and if (c.eatConfig.ignoreBadFood) isNoneOfItems(c.eatConfig.badFood) else any()
- }
+ fun selector() =
+ stackSelection {
+ when(this@Reason) {
+ Hunger -> ofAnyItems(c.eatConfig.nutritiousFood)
+ Damage -> ofAnyItems(c.eatConfig.regenerationFood)
+ Fire -> ofAnyItems(c.eatConfig.resistanceFood)
+ else -> {}
+ }
+ if (c.eatConfig.ignoreBadFood) noneOfItems(c.eatConfig.badFood)
+ sortedWith(c.eatConfig.selectionPriority.comparator)
+ }
}
companion object {
diff --git a/src/main/kotlin/com/lambda/config/blocks/EntityColorSettings.kt b/src/main/kotlin/com/lambda/config/blocks/EntityColorSettings.kt
index 4545e7d1c..ea27b3e0b 100644
--- a/src/main/kotlin/com/lambda/config/blocks/EntityColorSettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/EntityColorSettings.kt
@@ -20,7 +20,7 @@ package com.lambda.config.blocks
import com.lambda.config.Config
import com.lambda.config.ConfigBlock
import com.lambda.context.SafeContext
-import com.lambda.interaction.handlers.FriendHandler.isFriend
+import com.lambda.interaction.handler.handlers.FriendHandler.isFriend
import com.lambda.util.EntityUtils.EntityGroup
import com.lambda.util.EntityUtils.entityGroup
import com.lambda.util.extension.blockColor
diff --git a/src/main/kotlin/com/lambda/config/blocks/HotbarSettings.kt b/src/main/kotlin/com/lambda/config/blocks/HotbarSettings.kt
index ce76c94c3..3c8b1e9c2 100644
--- a/src/main/kotlin/com/lambda/config/blocks/HotbarSettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/HotbarSettings.kt
@@ -28,5 +28,5 @@ class HotbarSettings(override val c: Config) : HotbarConfig, ConfigBlock {
override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks")
override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 }
override val swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks")
- override val tickStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Input.Post), ALL_STAGES.toSet(), "The sub-tick timing at which hotbar actions are performed", displayClassName = true)
+ override val tickStageMask by c.setting("Hotbar Stage Mask", setOf(TickEvent.Pre, TickEvent.Input.Post), ALL_STAGES.toSet(), "The sub-tick timing at which hotbar actions are performed", displayClassName = true)
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/config/blocks/InventoryConfig.kt b/src/main/kotlin/com/lambda/config/blocks/InventoryConfig.kt
index 6a0c26116..c63843619 100644
--- a/src/main/kotlin/com/lambda/config/blocks/InventoryConfig.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/InventoryConfig.kt
@@ -17,68 +17,12 @@
package com.lambda.config.blocks
-import com.lambda.context.SafeContext
import com.lambda.event.events.TickEvent
-import com.lambda.interaction.material.ContainerSelection
-import com.lambda.interaction.material.StackSelection
-import com.lambda.interaction.material.container.MaterialContainer
-import com.lambda.util.Describable
-import com.lambda.util.NamedEnum
import net.minecraft.item.Item
interface InventoryConfig {
val tickStageMask: Collection
val disposables: Collection-
- val swapWithDisposables: Boolean
- val providerPriority: Priority
- val storePriority: Priority
-
- val accessShulkerBoxes: Boolean
- val accessChests: Boolean
- val accessEnderChest: Boolean
- val accessStashes: Boolean
-
- val containerSelection: ContainerSelection
- get() = ContainerSelection.selectContainer {
- val allowedContainers = buildSet {
- addAll(MaterialContainer.Rank.entries)
- if (!accessShulkerBoxes) remove(MaterialContainer.Rank.ShulkerBox)
- if (!accessEnderChest) remove(MaterialContainer.Rank.EnderChest)
- if (!accessChests) remove(MaterialContainer.Rank.Chest)
- if (!accessStashes) remove(MaterialContainer.Rank.Stash)
- }
- ofAnyType(*allowedContainers.toTypedArray())
- }
-
- enum class Priority(
- override val displayName: String,
- override val description: String
- ) : NamedEnum, Describable {
- WithMinItems("With Min Items", "Pick containers with the fewest matching items (or least space) first; useful for topping off or clearing leftovers."),
- WithMaxItems("With Max Items", "Pick containers with the most matching items (or most space) first; ideal for bulk moves with fewer transfers.");
-
- context(_: SafeContext)
- fun materialComparator(selection: StackSelection) =
- when (this) {
- WithMaxItems -> compareBy
{ it.rank }
- .thenByDescending { it.materialAvailable(selection) }
- .thenBy { it.name }
-
- WithMinItems -> compareBy { it.rank }
- .thenBy { it.materialAvailable(selection) }
- .thenBy { it.name }
- }
-
- context(_: SafeContext)
- fun spaceComparator(selection: StackSelection) =
- when (this) {
- WithMaxItems -> compareBy { it.rank }
- .thenByDescending { it.spaceAvailable(selection) }
- .thenBy { it.name }
-
- WithMinItems -> compareBy { it.rank }
- .thenBy { it.spaceAvailable(selection) }
- .thenBy { it.name }
- }
- }
+ val allowedContainers: Collection
+ val enderChestSearchRadius: Int
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/config/blocks/InventorySettings.kt b/src/main/kotlin/com/lambda/config/blocks/InventorySettings.kt
index ee64c7e13..84f059189 100644
--- a/src/main/kotlin/com/lambda/config/blocks/InventorySettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/InventorySettings.kt
@@ -21,22 +21,17 @@ import com.lambda.config.Config
import com.lambda.config.ConfigBlock
import com.lambda.config.Group
import com.lambda.event.events.TickEvent.Companion.ALL_STAGES
+import com.lambda.interaction.container.ContainerType
import com.lambda.util.item.ItemUtils
class InventorySettings(override val c: Config) : InventoryConfig, ConfigBlock {
companion object {
- private const val CONTAINER_GROUP = "Container"
- private const val ACCESS_GROUP = "Access"
+ const val ENDER_CHEST_GROUP = "Ender Chest"
}
override val tickStageMask by c.setting("Inventory Stage Mask", ALL_STAGES.toSet(), description = "The sub-tick timing at which inventory actions are performed", displayClassName = true)
- @Group(CONTAINER_GROUP) override val disposables by c.setting("Disposables", ItemUtils.defaultDisposables, description = "Items that will be ignored when checking for a free slot")
- @Group(CONTAINER_GROUP) override val swapWithDisposables by c.setting("Swap With Disposables", true, "Swap items with disposable ones")
- @Group(CONTAINER_GROUP) override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from")
- @Group(CONTAINER_GROUP) override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to")
+ override val disposables by c.setting("Disposables", ItemUtils.DEFAULT_DISPOSABLES, description = "Items that will be ignored when checking for a free slot")
+ override val allowedContainers by c.setting("Allowed Containers", ContainerType.entries, description = "What containers are accessible")
- @Group(ACCESS_GROUP) override val accessShulkerBoxes by c.setting("Access Shulker Boxes", false, "Allow access to the player's shulker boxes")
- @Group(ACCESS_GROUP) override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests")
- @Group(ACCESS_GROUP) override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest")
- @Group(ACCESS_GROUP) override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes")
+ @Group(ENDER_CHEST_GROUP) override val enderChestSearchRadius by c.setting("Initial Search Radius", 10, 0..20, unit = " blocks", description = "Initial search radius to scan for an already placed ender chest. If one isn't found, one will be placed. If an ender chest isn't found in the inventory, it scans the rest of the loaded area to find an already placed one")
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/config/blocks/RotationConfig.kt b/src/main/kotlin/com/lambda/config/blocks/RotationConfig.kt
index a20530cf4..3f13dfa0b 100644
--- a/src/main/kotlin/com/lambda/config/blocks/RotationConfig.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/RotationConfig.kt
@@ -18,8 +18,8 @@
package com.lambda.config.blocks
import com.lambda.event.events.TickEvent
-import com.lambda.interaction.managers.rotating.RotationManager
-import com.lambda.interaction.managers.rotating.RotationMode
+import com.lambda.interaction.manager.managers.rotating.RotationManager
+import com.lambda.interaction.manager.managers.rotating.RotationMode
interface RotationConfig {
/**
diff --git a/src/main/kotlin/com/lambda/config/blocks/RotationSettings.kt b/src/main/kotlin/com/lambda/config/blocks/RotationSettings.kt
index cdaaf397c..c927e65a5 100644
--- a/src/main/kotlin/com/lambda/config/blocks/RotationSettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/RotationSettings.kt
@@ -21,7 +21,7 @@ import com.lambda.config.Config
import com.lambda.config.ConfigBlock
import com.lambda.event.events.TickEvent
import com.lambda.event.events.TickEvent.Companion.ALL_STAGES
-import com.lambda.interaction.managers.rotating.RotationMode
+import com.lambda.interaction.manager.managers.rotating.RotationMode
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.cos
diff --git a/src/main/kotlin/com/lambda/config/blocks/TargetingSettings.kt b/src/main/kotlin/com/lambda/config/blocks/TargetingSettings.kt
index d29bf56af..071f78564 100644
--- a/src/main/kotlin/com/lambda/config/blocks/TargetingSettings.kt
+++ b/src/main/kotlin/com/lambda/config/blocks/TargetingSettings.kt
@@ -22,10 +22,10 @@ import com.lambda.config.ConfigBlock
import com.lambda.config.hide
import com.lambda.config.withEdits
import com.lambda.context.SafeContext
-import com.lambda.interaction.handlers.FriendHandler.isFriend
-import com.lambda.interaction.managers.rotating.Rotation.Companion.dist
-import com.lambda.interaction.managers.rotating.Rotation.Companion.rotation
-import com.lambda.interaction.managers.rotating.Rotation.Companion.rotationTo
+import com.lambda.interaction.handler.handlers.FriendHandler.isFriend
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.dist
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.rotation
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.rotationTo
import com.lambda.threading.runSafe
import com.lambda.util.extension.fullHealth
import com.lambda.util.math.distSq
diff --git a/src/main/kotlin/com/lambda/config/categories/FriendCategory.kt b/src/main/kotlin/com/lambda/config/categories/FriendCategory.kt
index 3484222a8..130c1e786 100644
--- a/src/main/kotlin/com/lambda/config/categories/FriendCategory.kt
+++ b/src/main/kotlin/com/lambda/config/categories/FriendCategory.kt
@@ -18,7 +18,7 @@
package com.lambda.config.categories
import com.lambda.config.ConfigCategory
-import com.lambda.interaction.handlers.FriendHandler
+import com.lambda.interaction.handler.handlers.FriendHandler
import com.lambda.threading.runIO
import com.lambda.util.FolderRegistry
import java.io.File
diff --git a/src/main/kotlin/com/lambda/config/entries/Setting.kt b/src/main/kotlin/com/lambda/config/entries/Setting.kt
index e68c0def7..17777d42a 100644
--- a/src/main/kotlin/com/lambda/config/entries/Setting.kt
+++ b/src/main/kotlin/com/lambda/config/entries/Setting.kt
@@ -31,6 +31,7 @@ import com.lambda.config.ConfigEntry
import com.lambda.config.EntryCore
import com.lambda.config.EntryLayer
import com.lambda.config.MultipleLayerType
+import com.lambda.config.entries.Setting.ValueListener
import com.lambda.context.SafeContext
import com.lambda.gui.dsl.ImGuiBuilder
import com.lambda.threading.runSafe
@@ -229,42 +230,40 @@ abstract class Setting(
override fun toString() = "Setting $name: $value"
- companion object {
- /**
- * Will only register changes of the variable, not the content of the variable!
- * E.g., if the variable is a list, it will only register if the list reference changes, not if the content of the list changes.
- */
- @ConfigEntryDsl
- fun , T> S.onValueChange(block: SafeContext.(from: T, to: T) -> Unit) =
- apply {
- listeners.add(ValueListener(true) { from, to ->
- runSafe { block(from, to) }
- })
- }
-
- @ConfigEntryDsl
- fun , T> S.onValueChangeUnsafe(block: (from: T, to: T) -> Unit) =
- apply { listeners.add(ValueListener(true, block)) }
+ class ValueListener(val requiresValueChange: Boolean, val execute: (from: T, to: T) -> Unit)
+}
- @ConfigEntryDsl
- fun , T> S.onValueSet(block: SafeContext.(from: T, to: T) -> Unit) =
- apply {
- listeners.add(ValueListener(false) { from, to ->
- runSafe { block(from, to) }
- })
- }
+/**
+ * Will only register changes of the variable, not the content of the variable!
+ * E.g., if the variable is a list, it will only register if the list reference changes, not if the content of the list changes.
+ */
+@ConfigEntryDsl
+fun , T> S.onValueChange(block: SafeContext.(from: T, to: T) -> Unit) =
+ apply {
+ listeners.add(ValueListener(true) { from, to ->
+ runSafe { block(from, to) }
+ })
+ }
- @ConfigEntryDsl
- fun , T> S.onValueSetUnsafe(block: (from: T, to: T) -> Unit) =
- apply { listeners.add(ValueListener(false, block)) }
+@ConfigEntryDsl
+fun , T> S.onValueChangeUnsafe(block: (from: T, to: T) -> Unit) =
+ apply { listeners.add(ValueListener(true, block)) }
- @ConfigEntryDsl
- fun , T> S.disabled(predicate: () -> Boolean) =
- apply { disabled = predicate }
+@ConfigEntryDsl
+fun , T> S.onValueSet(block: SafeContext.(from: T, to: T) -> Unit) =
+ apply {
+ listeners.add(ValueListener(false) { from, to ->
+ runSafe { block(from, to) }
+ })
}
- class ValueListener(val requiresValueChange: Boolean, val execute: (from: T, to: T) -> Unit)
-}
+@ConfigEntryDsl
+fun , T> S.onValueSetUnsafe(block: (from: T, to: T) -> Unit) =
+ apply { listeners.add(ValueListener(false, block)) }
+
+@ConfigEntryDsl
+fun , T> S.disabled(predicate: () -> Boolean) =
+ apply { disabled = predicate }
class SettingEntryLayer, U>(
override val parent: EntryLayer.Multiple>,
diff --git a/src/main/kotlin/com/lambda/config/serializers/EnderChestContainerSerializer.kt b/src/main/kotlin/com/lambda/config/serializers/EnderChestContainerSerializer.kt
new file mode 100644
index 000000000..08c442617
--- /dev/null
+++ b/src/main/kotlin/com/lambda/config/serializers/EnderChestContainerSerializer.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+@file:Suppress("unused")
+
+package com.lambda.config.serializers
+
+import com.lambda.config.Deserializer
+import com.lambda.config.JsonOps
+import com.lambda.config.Serializer
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.container.containers.external.EnderChestContainer
+import net.minecraft.item.ItemStack
+import tools.jackson.core.JsonGenerator
+import tools.jackson.core.JsonParser
+import tools.jackson.databind.DeserializationContext
+import tools.jackson.databind.SerializationContext
+import tools.jackson.databind.node.ArrayNode
+import tools.jackson.databind.node.ObjectNode
+
+object EnderChestContainerSerializer : Serializer(EnderChestContainer::class.java) {
+ override fun serialize(container: EnderChestContainer, gen: JsonGenerator, ctxt: SerializationContext) {
+ gen.writeStartObject()
+ gen.writeStringProperty("Type", ContainerType.EnderChest.name)
+
+ gen.writeArrayPropertyStart("Stacks")
+ container.stacks.forEach { stack ->
+ if (stack.isEmpty) {
+ gen.writeNull()
+ } else {
+ val encoded = ItemStack.CODEC
+ .encodeStart(JsonOps.UNCOMPRESSED, stack)
+ .result()
+ if (encoded.isPresent) {
+ gen.writePOJO(encoded.get())
+ } else {
+ gen.writeNull()
+ }
+ }
+ }
+ gen.writeEndArray()
+
+ gen.writeEndObject()
+ }
+}
+
+object EnderChestContainerDeserializer : Deserializer(EnderChestContainer::class.java) {
+ override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): EnderChestContainer? {
+ throw initFromJsonException("EnderChestContainer")
+ }
+
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext, intoValue: EnderChestContainer): EnderChestContainer {
+ val root = p.readValueAsTree()
+ val stackArray = root.get("Stacks") as? ArrayNode
+ val decodedStacks =
+ stackArray?.mapNotNull { element ->
+ if (element.isNull) {
+ ItemStack.EMPTY
+ } else {
+ ItemStack.CODEC
+ .parse(JsonOps.UNCOMPRESSED, element)
+ .result()
+ .orElse(ItemStack.EMPTY)
+ }
+ } ?: emptyList()
+
+ val stacks =
+ if (decodedStacks.size < 27) {
+ decodedStacks + List(27 - decodedStacks.size) { ItemStack.EMPTY }
+ } else decodedStacks
+
+ EnderChestContainer.update(stacks)
+ EnderChestContainer.scanStacksForNestedContainers()
+ return EnderChestContainer
+ }
+}
diff --git a/src/main/kotlin/com/lambda/config/serializers/PlacedContainerSerializer.kt b/src/main/kotlin/com/lambda/config/serializers/PlacedContainerSerializer.kt
new file mode 100644
index 000000000..ce2872caf
--- /dev/null
+++ b/src/main/kotlin/com/lambda/config/serializers/PlacedContainerSerializer.kt
@@ -0,0 +1,131 @@
+@file:Suppress("unused")
+
+package com.lambda.config.serializers
+
+import com.lambda.config.FallbackDeserializer
+import com.lambda.config.FallbackSerializer
+import com.lambda.config.JsonOps
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.container.PlacedContainer
+import com.lambda.interaction.container.containers.external.ChestContainer
+import com.lambda.interaction.container.containers.external.DoubleChestContainer
+import com.lambda.interaction.container.containers.external.PlacedShulkerBoxContainer
+import com.lambda.util.world.Dimension
+import net.minecraft.block.Blocks
+import net.minecraft.item.ItemStack
+import net.minecraft.registry.Registries
+import net.minecraft.util.Identifier
+import net.minecraft.util.math.BlockPos
+import tools.jackson.core.JsonGenerator
+import tools.jackson.core.JsonParser
+import tools.jackson.databind.DeserializationContext
+import tools.jackson.databind.SerializationContext
+import tools.jackson.databind.node.ArrayNode
+import tools.jackson.databind.node.ObjectNode
+
+object PlacedContainerSerializer : FallbackSerializer(PlacedContainer::class.java) {
+ override fun serialize(container: PlacedContainer, gen: JsonGenerator, ctxt: SerializationContext) {
+ gen.writeStartObject()
+ gen.writeStringProperty("Type", container.type.name)
+ gen.writeStringProperty("Dimension", container.dimension.name)
+ gen.writePOJOProperty("Pos", BlockPos.CODEC.encodeStart(JsonOps.UNCOMPRESSED, container.pos).orThrow)
+
+ when (container) {
+ is PlacedShulkerBoxContainer -> {
+ val blockId = Registries.BLOCK.getId(container.block).toString()
+ gen.writeStringProperty("Block", blockId)
+ }
+ is DoubleChestContainer -> {
+ gen.writeBooleanProperty("Double", true)
+ gen.writePOJOProperty("Left Pos", BlockPos.CODEC.encodeStart(JsonOps.UNCOMPRESSED, container.leftPos).orThrow)
+ gen.writePOJOProperty("Right Pos", BlockPos.CODEC.encodeStart(JsonOps.UNCOMPRESSED, container.rightPos).orThrow)
+ }
+ else -> {}
+ }
+
+ gen.writeArrayPropertyStart("Stacks")
+ container.stacks.forEach { stack ->
+ if (stack.isEmpty) {
+ gen.writeNull()
+ } else {
+ val encoded = ItemStack.CODEC
+ .encodeStart(JsonOps.UNCOMPRESSED, stack)
+ .result()
+ if (encoded.isPresent) {
+ gen.writePOJO(encoded.get())
+ } else {
+ gen.writeNull()
+ }
+ }
+ }
+ gen.writeEndArray()
+ gen.writeEndObject()
+ }
+}
+
+object PlacedContainerDeserializer : FallbackDeserializer(PlacedContainer::class.java) {
+ override fun deserialize(p: JsonParser, ctxt: DeserializationContext): PlacedContainer? {
+ val root = p.readValueAsTree() ?: return null
+
+ val typeName = root.get("Type")?.asString() ?: return null
+ val type = ContainerType.entries.find { it.name == typeName } ?: return null
+ val dimension = root.get("Dimension")?.asString()
+ ?.takeUnless { it.isBlank() }
+ ?.let { name -> Dimension.entries.firstOrNull { it.name == name } }
+ ?: Dimension.Unknown
+
+ val posNode = root.get("Pos") ?: return null
+ val pos = BlockPos.CODEC.parse(JsonOps.UNCOMPRESSED, posNode)
+ .result()
+ .orElse(null)
+ ?: return null
+
+ val stackArray = root.get("Stacks") as? ArrayNode
+ val decodedStacks =
+ stackArray?.mapNotNull { element ->
+ if (element.isNull) {
+ ItemStack.EMPTY
+ } else {
+ ItemStack.CODEC
+ .parse(JsonOps.UNCOMPRESSED, element)
+ .result()
+ .orElse(ItemStack.EMPTY)
+ }
+ } ?: emptyList()
+
+ fun padStacks(expectedSize: Int): List =
+ if (decodedStacks.size < expectedSize) {
+ decodedStacks + List(expectedSize - decodedStacks.size) { ItemStack.EMPTY }
+ } else decodedStacks
+
+ val container =
+ when (type) {
+ ContainerType.PlacedShulkerBox -> {
+ val blockId = root.get("Block")?.asString()
+ val block = blockId?.let { Registries.BLOCK.get(Identifier.of(it)) } ?: Blocks.SHULKER_BOX
+ PlacedShulkerBoxContainer(pos, block, null, padStacks(27), dimension)
+ }
+ ContainerType.Chest -> {
+ if (root.get("Double")?.asBoolean() == true) {
+ val leftPosNode = root.get("Left Pos") ?: return null
+ val leftPos = BlockPos.CODEC.parse(JsonOps.UNCOMPRESSED, leftPosNode)
+ .result()
+ .orElse(null)
+ ?: return null
+ val rightPosNode = root.get("Right Pos") ?: return null
+ val rightPos = BlockPos.CODEC.parse(JsonOps.UNCOMPRESSED, rightPosNode)
+ .result()
+ .orElse(null)
+ ?: return null
+ DoubleChestContainer(pos, leftPos, rightPos, padStacks(54), dimension = dimension)
+ } else {
+ ChestContainer(pos, padStacks(27), dimension = dimension)
+ }
+ }
+ else -> return null
+ }
+
+ container.scanStacksForNestedContainers()
+ return container
+ }
+}
diff --git a/src/main/kotlin/com/lambda/config/settings/StringSetting.kt b/src/main/kotlin/com/lambda/config/settings/StringSetting.kt
index cb379f4ec..026ee1a0a 100644
--- a/src/main/kotlin/com/lambda/config/settings/StringSetting.kt
+++ b/src/main/kotlin/com/lambda/config/settings/StringSetting.kt
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
+@file:Suppress("unused")
+
package com.lambda.config.settings
import com.lambda.brigadier.argument.greedyString
@@ -44,7 +46,6 @@ class StringSetting(
var multiline: Boolean = false,
var flags: Int = ImGuiInputTextFlags.None,
) : Setting(name, description, defaultValue, layer, config, visibility) {
-
override fun ImGuiBuilder.buildLayout() {
if (multiline) {
inputTextMultiline(name, ::value, flags = flags)
diff --git a/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt b/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt
index 6dc59d4cf..bc01d1ef2 100644
--- a/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt
+++ b/src/main/kotlin/com/lambda/config/settings/collections/CollectionSetting.kt
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
+@file:Suppress("unused")
+
package com.lambda.config.settings.collections
import com.lambda.config.Config
diff --git a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
index 00629b99f..5b069fd72 100644
--- a/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
+++ b/src/main/kotlin/com/lambda/config/settings/complex/KeybindSetting.kt
@@ -15,6 +15,8 @@
* along with this program. If not, see .
*/
+@file:Suppress("unused")
+
package com.lambda.config.settings.complex
import com.fasterxml.jackson.annotation.JsonIncludeProperties
@@ -218,26 +220,23 @@ class KeybindSetting(
}
}
- @Suppress("unused")
- companion object {
- @ConfigEntryDsl
- fun KeybindSetting.onPress(block: SafeContext.(ButtonEvent) -> Unit) = apply { pressListeners.add(block) }
+ @ConfigEntryDsl
+ fun onPress(block: SafeContext.(ButtonEvent) -> Unit) = apply { pressListeners.add(block) }
- @ConfigEntryDsl
- fun KeybindSetting.onPressUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafePressListeners.add(block) }
+ @ConfigEntryDsl
+ fun onPressUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafePressListeners.add(block) }
- @ConfigEntryDsl
- fun KeybindSetting.onRepeat(block: SafeContext.(ButtonEvent) -> Unit) = apply { repeatListeners.add(block) }
+ @ConfigEntryDsl
+ fun onRepeat(block: SafeContext.(ButtonEvent) -> Unit) = apply { repeatListeners.add(block) }
- @ConfigEntryDsl
- fun KeybindSetting.onRepeatUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafeRepeatListeners.add(block) }
+ @ConfigEntryDsl
+ fun onRepeatUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafeRepeatListeners.add(block) }
- @ConfigEntryDsl
- fun KeybindSetting.onRelease(block: SafeContext.(ButtonEvent) -> Unit) = apply { releaseListeners.add(block) }
+ @ConfigEntryDsl
+ fun onRelease(block: SafeContext.(ButtonEvent) -> Unit) = apply { releaseListeners.add(block) }
- @ConfigEntryDsl
- fun KeybindSetting.onReleaseUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafeReleaseListeners.add(block) }
- }
+ @ConfigEntryDsl
+ fun onReleaseUnsafe(block: (ButtonEvent) -> Unit) = apply { unsafeReleaseListeners.add(block) }
}
@Suppress("unused")
diff --git a/src/main/kotlin/com/lambda/context/AutomatedSafeContext.kt b/src/main/kotlin/com/lambda/context/AutomatedSafeContext.kt
index 96bb48367..3d59c2143 100644
--- a/src/main/kotlin/com/lambda/context/AutomatedSafeContext.kt
+++ b/src/main/kotlin/com/lambda/context/AutomatedSafeContext.kt
@@ -17,7 +17,14 @@
package com.lambda.context
-class AutomatedSafeContext(
- safeContext: SafeContext,
- automated: Automated
-) : IAutomatedSafeContext, SafeContext by safeContext, Automated by automated
\ No newline at end of file
+interface AutomatedSafeContext : SafeContext, Automated {
+ companion object {
+ fun create(
+ safeContext: SafeContext,
+ automated: Automated
+ ): AutomatedSafeContext =
+ object : AutomatedSafeContext,
+ SafeContext by safeContext,
+ Automated by automated {}
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/event/EventFlow.kt b/src/main/kotlin/com/lambda/event/EventFlow.kt
index 91db873f4..e21723c90 100644
--- a/src/main/kotlin/com/lambda/event/EventFlow.kt
+++ b/src/main/kotlin/com/lambda/event/EventFlow.kt
@@ -301,8 +301,7 @@ object EventFlow {
* @return `true` if the listener should not be notified, `false` otherwise.
*/
private fun shouldNotNotify(listener: Listener, event: Event) =
- (listener.owner is Muteable &&
- (listener.owner as Muteable).isMuted &&
+ ((listener.owner as? Muteable)?.isMuted == true &&
!listener.alwaysListen) ||
(event is ICancellable && event.isCanceled())
}
diff --git a/src/main/kotlin/com/lambda/event/events/ContainerEvent.kt b/src/main/kotlin/com/lambda/event/events/ContainerEvent.kt
index 2c0031cb1..0bfdb1899 100644
--- a/src/main/kotlin/com/lambda/event/events/ContainerEvent.kt
+++ b/src/main/kotlin/com/lambda/event/events/ContainerEvent.kt
@@ -19,14 +19,14 @@ package com.lambda.event.events
import com.lambda.event.callback.Cancellable
import com.lambda.event.callback.ICancellable
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.Container
import net.minecraft.screen.slot.Slot
sealed class ContainerEvent {
data class Transfer(
val fromSlot: Slot,
val toSlot: Slot,
- val from: MaterialContainer,
- val to: MaterialContainer
+ val from: Container,
+ val to: Container
) : ICancellable by Cancellable()
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt b/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt
index 1498eaa59..9a21f9fcf 100644
--- a/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt
+++ b/src/main/kotlin/com/lambda/event/events/PlayerEvent.kt
@@ -142,5 +142,13 @@ sealed class PlayerEvent {
val button: Int,
val action: SlotActionType,
val screenHandler: ScreenHandler,
- ) : ICancellable by Cancellable()
+ ) : ICancellable by Cancellable() {
+ data class Post(
+ val syncId: Int,
+ val slot: Int,
+ val button: Int,
+ val action: SlotActionType,
+ val screenHandler: ScreenHandler,
+ ) : Event
+ }
}
diff --git a/src/main/kotlin/com/lambda/event/events/RotationEvent.kt b/src/main/kotlin/com/lambda/event/events/RotationEvent.kt
index c6f6f2a8a..c807c825e 100644
--- a/src/main/kotlin/com/lambda/event/events/RotationEvent.kt
+++ b/src/main/kotlin/com/lambda/event/events/RotationEvent.kt
@@ -18,7 +18,7 @@
package com.lambda.event.events
import com.lambda.event.Event
-import com.lambda.interaction.managers.rotating.RotationRequest
+import com.lambda.interaction.manager.managers.rotating.RotationRequest
import net.minecraft.client.input.Input
sealed class RotationEvent {
diff --git a/src/main/kotlin/com/lambda/graphics/mc/renderer/ChunkedRenderer.kt b/src/main/kotlin/com/lambda/graphics/mc/renderer/ChunkedRenderer.kt
index d7aebfca2..767c56ca6 100644
--- a/src/main/kotlin/com/lambda/graphics/mc/renderer/ChunkedRenderer.kt
+++ b/src/main/kotlin/com/lambda/graphics/mc/renderer/ChunkedRenderer.kt
@@ -63,7 +63,7 @@ class ChunkedRenderer(
private val uploadQueue = ConcurrentLinkedDeque<() -> Unit>()
init {
- owner.listenUnsafe { event ->
+ owner.listenUnsafe(alwaysListen = true) { event ->
val pos = event.pos
val world = mc.world ?: return@listenUnsafe
world.getWorldChunk(pos)?.chunkData?.markDirty()
@@ -77,9 +77,9 @@ class ChunkedRenderer(
if (zInChunk == 15) world.getWorldChunk(pos.south())?.chunkData?.markDirty()
}
- owner.listenUnsafe { event -> event.chunk.chunkData.markDirty() }
- owner.listenUnsafe { chunkMap.remove(it.chunk.chunkKey)?.clearData() }
- owner.listenUnsafe { clear() }
+ owner.listenUnsafe(alwaysListen = true) { event -> event.chunk.chunkData.markDirty() }
+ owner.listenUnsafe(alwaysListen = true) { chunkMap.remove(it.chunk.chunkKey)?.clearData() }
+ owner.listenUnsafe(alwaysListen = true) { clear() }
owner.listenConcurrentlyUnsafe {
if (pauseUpdates()) return@listenConcurrentlyUnsafe
diff --git a/src/main/kotlin/com/lambda/graphics/text/FontHandler.kt b/src/main/kotlin/com/lambda/graphics/text/FontHandler.kt
index edb196076..197e207fc 100644
--- a/src/main/kotlin/com/lambda/graphics/text/FontHandler.kt
+++ b/src/main/kotlin/com/lambda/graphics/text/FontHandler.kt
@@ -20,7 +20,7 @@ package com.lambda.graphics.text
import com.lambda.Lambda.LOG
import com.lambda.config.Config
import com.lambda.config.categories.FontCategory
-import com.lambda.config.entries.Setting.Companion.onValueChangeUnsafe
+import com.lambda.config.entries.onValueChangeUnsafe
import com.lambda.core.Loadable
import com.lambda.event.events.ClientEvent
import com.lambda.event.listener.SafeListener.Companion.listen
diff --git a/src/main/kotlin/com/lambda/gui/MenuBar.kt b/src/main/kotlin/com/lambda/gui/MenuBar.kt
index 2dc94faec..df50b2a31 100644
--- a/src/main/kotlin/com/lambda/gui/MenuBar.kt
+++ b/src/main/kotlin/com/lambda/gui/MenuBar.kt
@@ -40,10 +40,10 @@ import com.lambda.imgui.flag.ImGuiCol
import com.lambda.imgui.flag.ImGuiStyleVar
import com.lambda.imgui.flag.ImGuiWindowFlags
import com.lambda.imgui.type.ImBoolean
-import com.lambda.interaction.handlers.BaritoneHandler
+import com.lambda.interaction.handler.handlers.BaritoneHandler
import com.lambda.module.ModuleRegistry
import com.lambda.module.ModuleRegistry.moduleNameMap
-import com.lambda.module.tag.ModuleTag
+import com.lambda.module.ModuleTag
import com.lambda.network.LambdaAPI
import com.lambda.threading.runSafe
import com.lambda.util.CommunicationUtils.info
diff --git a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt
index f4e708bd8..973f570a0 100644
--- a/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt
+++ b/src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt
@@ -21,8 +21,7 @@ import com.lambda.Lambda.mc
import com.lambda.config.Config
import com.lambda.config.Tab
import com.lambda.config.categories.GuiCategory
-import com.lambda.config.entries.Setting.Companion.onValueChange
-import com.lambda.config.settings.complex.KeybindSetting.Companion.onPressUnsafe
+import com.lambda.config.entries.onValueChange
import com.lambda.core.Loadable
import com.lambda.event.events.GuiEvent
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
@@ -47,10 +46,10 @@ import com.lambda.imgui.flag.ImGuiCond
import com.lambda.imgui.flag.ImGuiHoveredFlags
import com.lambda.imgui.flag.ImGuiWindowFlags
import com.lambda.module.ModuleRegistry
+import com.lambda.module.ModuleTag
+import com.lambda.module.ModuleTag.Companion.shownTags
import com.lambda.module.modules.client.Client
import com.lambda.module.modules.combat.autodisconnect.AutoDisconnectScreen
-import com.lambda.module.tag.ModuleTag
-import com.lambda.module.tag.ModuleTag.Companion.shownTags
import com.lambda.sound.LambdaSound
import com.lambda.sound.SoundHandler.play
import com.lambda.util.Describable
@@ -137,11 +136,9 @@ object ClickGuiLayout : Loadable, Config(
const val BASE_SCALE = 100
const val BASE_SCALE_MULTI = 1.8
- fun deviceScaleMultiplier() = try {
- val monitorWidth = mc.window.monitor!!.currentVideoMode!!.width.toDouble()
- (monitorWidth / 1920.0).coerceIn(0.5, 4.0)
- } catch (_: Throwable) {
- 1.0
+ fun deviceScaleMultiplier(): Double {
+ val monitorWidth = mc.window.monitor?.currentVideoMode?.width?.toDouble() ?: return 1.0
+ return (monitorWidth / 1920.0).coerceIn(0.5, 4.0)
}
// General
@@ -157,8 +154,7 @@ object ClickGuiLayout : Loadable, Config(
mc.window.setIcon(mc.defaultResourcePack, icon)
}
}
- @JvmStatic
- @Tab(GENERAL_TAB) val setLambdaWindowTitle by setting("Set Lambda Window Title", true).onValueChange { _, _ -> mc.updateWindowTitle() }
+ @Tab(GENERAL_TAB) @JvmStatic val setLambdaWindowTitle by setting("Set Lambda Window Title", true).onValueChange { _, _ -> mc.updateWindowTitle() }
@Tab(GENERAL_TAB) val lambdaTitleAppendixName by setting("Append Username", true) { setLambdaWindowTitle }.onValueChange { _, _ -> mc.updateWindowTitle() }
@Tab(GENERAL_TAB) val backgroundBlur by setting("Background Blur", true)
@Tab(GENERAL_TAB) val backgroundDarkening by setting("Background Darkening", true)
diff --git a/src/main/kotlin/com/lambda/gui/components/HudGuiLayout.kt b/src/main/kotlin/com/lambda/gui/components/HudGuiLayout.kt
index b070261d3..908805e6f 100644
--- a/src/main/kotlin/com/lambda/gui/components/HudGuiLayout.kt
+++ b/src/main/kotlin/com/lambda/gui/components/HudGuiLayout.kt
@@ -123,9 +123,11 @@ object HudGuiLayout : Loadable, Config(
if (!isLocked) {
activeDragHudName?.let { drag ->
- if (mouseDown) updateDragAndSnapping(
- drag, lastBounds[drag]!!, dragOffsetX, dragOffsetY, pendingPositions, snapOverlays
- )
+ lastBounds[drag]?.let { bounds ->
+ if (mouseDown) updateDragAndSnapping(
+ drag, bounds, dragOffsetX, dragOffsetY, pendingPositions, snapOverlays
+ )
+ }
drawDragGrid()
}
}
diff --git a/src/main/kotlin/com/lambda/interaction/construction/blueprint/StaticBlueprint.kt b/src/main/kotlin/com/lambda/interaction/construction/blueprint/StaticBlueprint.kt
index f06de623e..f5e6e3176 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/blueprint/StaticBlueprint.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/blueprint/StaticBlueprint.kt
@@ -23,8 +23,6 @@ data class StaticBlueprint(
override val structure: Structure,
) : Blueprint() {
override fun toString() = "Static Blueprint at ${center?.toShortString()}"
-
- companion object {
- fun Structure.toBlueprint() = StaticBlueprint(this)
- }
}
+
+fun Structure.toBlueprint() = StaticBlueprint(this)
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
index 046855d72..ee00b43f4 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt
@@ -44,9 +44,24 @@ import net.minecraft.util.math.Vec3d
context(automatedSafeContext: AutomatedSafeContext)
fun Structure.sim(
pov: Vec3d = automatedSafeContext.player.eyePos
-) = BuildSimulator.sim(this, pov)
+) = sim(this, pov)
-class BuildSimulator private constructor(
+@JvmName("simExt")
+@SimDsl
+context(automatedSafeContext: AutomatedSafeContext)
+fun sim(
+ structure: Structure,
+ pov: Vec3d = automatedSafeContext.player.eyePos
+): Set =
+ with(automatedSafeContext) {
+ runBlocking(Dispatchers.Default) {
+ val results = ConcurrentSet()
+ with(BuildSimulator(structure, pov, results)) { sim() }
+ results
+ }
+ }
+
+class BuildSimulator internal constructor(
private val structure: Structure,
private val pov: Vec3d,
private val resultsCollection: MutableSet
@@ -66,20 +81,4 @@ class BuildSimulator private constructor(
}
}
}
-
- companion object {
- @SimDsl
- context(automatedSafeContext: AutomatedSafeContext)
- internal fun sim(
- structure: Structure,
- pov: Vec3d = automatedSafeContext.player.eyePos
- ): Set =
- with(automatedSafeContext) {
- runBlocking(Dispatchers.Default) {
- val results = ConcurrentSet()
- with(BuildSimulator(structure, pov, results)) { sim() }
- results
- }
- }
- }
}
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt
index 2ecda1e9b..4b6ee893d 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/Sim.kt
@@ -21,7 +21,7 @@ import com.lambda.context.AutomatedSafeContext
import com.lambda.interaction.construction.simulation.processing.PreProcessingData
import com.lambda.interaction.construction.simulation.result.BuildResult
import com.lambda.interaction.construction.simulation.result.results.GenericResult
-import com.lambda.interaction.managers.rotating.Rotation.Companion.rotationTo
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.rotationTo
import com.lambda.util.math.distSq
import com.lambda.util.math.vec3d
import com.lambda.util.player.CheckedHit
@@ -84,7 +84,7 @@ abstract class Sim : Results {
dependencyStack.pop()
}
- protected abstract suspend fun AutomatedSafeContext.sim()
+ abstract suspend fun AutomatedSafeContext.sim()
/**
* Scans a [voxelShape] on the given [sides] at the [pos] from the [pov].
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt
index 2222f6791..d3b142bcf 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BreakContext.kt
@@ -19,8 +19,8 @@ package com.lambda.interaction.construction.simulation.context
import com.lambda.context.Automated
import com.lambda.graphics.mc.RenderBuilder
-import com.lambda.interaction.managers.rotating.RotationRequest
-import com.lambda.interaction.material.StackSelection
+import com.lambda.interaction.container.selection.StackSelection
+import com.lambda.interaction.manager.managers.rotating.RotationRequest
import com.lambda.threading.runSafe
import com.lambda.util.BlockUtils.emptyState
import net.minecraft.block.BlockState
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BuildContext.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BuildContext.kt
index 676d99b47..de4669021 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BuildContext.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/BuildContext.kt
@@ -20,7 +20,7 @@ package com.lambda.interaction.construction.simulation.context
import com.lambda.config.blocks.ActionConfig
import com.lambda.context.Automated
import com.lambda.interaction.construction.simulation.result.Drawable
-import com.lambda.interaction.managers.rotating.RotationRequest
+import com.lambda.interaction.manager.managers.rotating.RotationRequest
import com.lambda.threading.runSafe
import net.minecraft.block.BlockState
import net.minecraft.util.hit.BlockHitResult
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt
index a431da618..6ab4617b6 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/context/InteractContext.kt
@@ -20,8 +20,8 @@ package com.lambda.interaction.construction.simulation.context
import com.lambda.context.Automated
import com.lambda.graphics.mc.RenderBuilder
import com.lambda.interaction.construction.simulation.processing.PreProcessingInfo
-import com.lambda.interaction.managers.interacting.InteractRequest
-import com.lambda.interaction.managers.rotating.RotationRequest
+import com.lambda.interaction.manager.managers.interacting.InteractRequest
+import com.lambda.interaction.manager.managers.rotating.RotationRequest
import net.minecraft.block.BlockState
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/FarmlandStateProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/FarmlandStateProcessor.kt
index b4586a199..c6817df2f 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/FarmlandStateProcessor.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/FarmlandStateProcessor.kt
@@ -75,8 +75,8 @@ object FarmlandStateProcessor : StateProcessor {
}
private fun ClientPlayerEntity.findHoe(): Item? =
- mainHandStack.item.takeIf { it in ItemUtils.hoes }
- ?: hotbarAndInventoryStacks.firstOrNull { it.item in ItemUtils.hoes }?.item
+ mainHandStack.item.takeIf { it in ItemUtils.HOES }
+ ?: hotbarAndInventoryStacks.firstOrNull { it.item in ItemUtils.HOES }?.item
private fun ClientPlayerEntity.carries(item: Item) =
hotbarAndInventoryStacks.any { it.item == item }
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Contextual.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Contextual.kt
index 9f25ed94f..fd67a73dd 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Contextual.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Contextual.kt
@@ -21,8 +21,8 @@ import com.lambda.config.blocks.ActionConfig
import com.lambda.interaction.construction.simulation.context.BreakContext
import com.lambda.interaction.construction.simulation.context.BuildContext
import com.lambda.interaction.construction.simulation.context.InteractContext
-import com.lambda.interaction.managers.hotbar.HotbarManager
-import com.lambda.interaction.managers.rotating.RotationManager
+import com.lambda.interaction.manager.managers.hotbar.HotbarManager
+import com.lambda.interaction.manager.managers.rotating.RotationManager
import com.lambda.threading.runSafe
import com.lambda.util.BlockUtils
import net.minecraft.block.Blocks
@@ -36,8 +36,8 @@ interface Contextual : ComparableResult {
override fun compareResult(other: ComparableResult) = runSafe {
when (other) {
is Contextual -> compareBy {
- if (it is InteractContext) BlockUtils.fluids.indexOf(it.cachedState.fluidState.fluid)
- else BlockUtils.fluids.size - 1
+ if (it is InteractContext) BlockUtils.FLUIDS.indexOf(it.cachedState.fluidState.fluid)
+ else BlockUtils.FLUIDS.size - 1
}.thenByDescending {
if (it is InteractContext && it.cachedState.fluidState.level != 0) it.blockPos.y
else Int.MIN_VALUE
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt
index 533123ef0..496faa8cc 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt
@@ -17,13 +17,13 @@
package com.lambda.interaction.construction.simulation.result
-import com.lambda.context.AutomatedSafeContext
+import com.lambda.context.Automated
import com.lambda.task.Task
/**
* Represents a [BuildResult] with a resolvable [Task]
*/
interface Resolvable {
- context(task: Task<*>, _: AutomatedSafeContext)
- fun resolve()
+ context(_: Automated)
+ fun resolve(): Task<*>
}
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt
index 58b684006..ce2cb1f35 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt
@@ -19,7 +19,7 @@ package com.lambda.interaction.construction.simulation.result.results
import baritone.api.pathing.goals.GoalBlock
import baritone.api.pathing.goals.GoalInverted
-import com.lambda.context.AutomatedSafeContext
+import com.lambda.context.Automated
import com.lambda.graphics.mc.RenderBuilder
import com.lambda.graphics.util.DirectionMask.mask
import com.lambda.interaction.construction.simulation.context.BreakContext
@@ -31,12 +31,11 @@ import com.lambda.interaction.construction.simulation.result.Drawable
import com.lambda.interaction.construction.simulation.result.Navigable
import com.lambda.interaction.construction.simulation.result.Rank
import com.lambda.interaction.construction.simulation.result.Resolvable
-import com.lambda.interaction.handlers.BaritoneHandler
-import com.lambda.interaction.handlers.ContainerHandler.transferByTask
-import com.lambda.interaction.material.StackSelection.Companion.selectStack
-import com.lambda.interaction.material.container.containers.HotbarContainer
-import com.lambda.task.Task
-import com.lambda.task.wrappers.softFail
+import com.lambda.interaction.container.containers.HotbarContainer
+import com.lambda.interaction.container.selection.select
+import com.lambda.interaction.container.selection.stackSelection
+import com.lambda.interaction.handler.handlers.BaritoneHandler
+import com.lambda.task.tasks.transfer
import net.minecraft.block.BlockState
import net.minecraft.item.Item
import net.minecraft.util.math.BlockPos
@@ -118,12 +117,10 @@ sealed class BreakResult : BuildResult() {
) : Resolvable, BreakResult() {
override val rank = Rank.BreakItemCantMine
- context(task: Task<*>, _: AutomatedSafeContext)
- override fun resolve() {
- selectStack {
- isItem(badItem).not()
- }.transferByTask(HotbarContainer)?.softFail()?.execute(task)
- }
+ context(_: Automated)
+ override fun resolve() =
+ stackSelection { notItem(badItem) }
+ .transfer(toSelection = HotbarContainer.select())
override fun compareResult(other: ComparableResult) =
when (other) {
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt
index 54b8fd2fd..b02445f0e 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt
@@ -18,7 +18,7 @@
package com.lambda.interaction.construction.simulation.result.results
import baritone.api.pathing.goals.GoalNear
-import com.lambda.context.AutomatedSafeContext
+import com.lambda.context.Automated
import com.lambda.graphics.mc.RenderBuilder
import com.lambda.interaction.construction.simulation.result.BuildResult
import com.lambda.interaction.construction.simulation.result.ComparableResult
@@ -26,12 +26,11 @@ import com.lambda.interaction.construction.simulation.result.Drawable
import com.lambda.interaction.construction.simulation.result.Navigable
import com.lambda.interaction.construction.simulation.result.Rank
import com.lambda.interaction.construction.simulation.result.Resolvable
-import com.lambda.interaction.handlers.BaritoneHandler
-import com.lambda.interaction.handlers.ContainerHandler.transferByTask
-import com.lambda.interaction.material.StackSelection
-import com.lambda.interaction.material.container.containers.HotbarContainer
-import com.lambda.task.Task
-import com.lambda.task.wrappers.softFail
+import com.lambda.interaction.container.containers.HotbarContainer
+import com.lambda.interaction.container.selection.StackSelection
+import com.lambda.interaction.container.selection.select
+import com.lambda.interaction.handler.handlers.BaritoneHandler
+import com.lambda.task.tasks.transfer
import net.minecraft.client.data.TextureMap.side
import net.minecraft.item.ItemStack
import net.minecraft.util.math.BlockPos
@@ -99,10 +98,9 @@ sealed class GenericResult : BuildResult() {
override val rank = Rank.WrongItem
private val color = Color(3, 252, 169, 25)
- context(task: Task<*>, _: AutomatedSafeContext)
- override fun resolve() {
- neededSelection.transferByTask(HotbarContainer)?.softFail()?.execute(task)
- }
+ context(_: Automated)
+ override fun resolve() =
+ neededSelection.transfer(toSelection = HotbarContainer.select())
override fun RenderBuilder.render() {
val center = pos.toCenterPos()
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt
index 6c45686a3..c0946491d 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/InteractResult.kt
@@ -27,7 +27,7 @@ import com.lambda.interaction.construction.simulation.result.Dependent
import com.lambda.interaction.construction.simulation.result.Drawable
import com.lambda.interaction.construction.simulation.result.Navigable
import com.lambda.interaction.construction.simulation.result.Rank
-import com.lambda.interaction.handlers.BaritoneHandler
+import com.lambda.interaction.handler.handlers.BaritoneHandler
import net.minecraft.block.BlockState
import net.minecraft.entity.Entity
import net.minecraft.item.Item
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt
index efa0cbaf6..62d15fb0f 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/PreSimResult.kt
@@ -24,7 +24,7 @@ import com.lambda.interaction.construction.simulation.result.ComparableResult
import com.lambda.interaction.construction.simulation.result.Drawable
import com.lambda.interaction.construction.simulation.result.Navigable
import com.lambda.interaction.construction.simulation.result.Rank
-import com.lambda.interaction.handlers.BaritoneHandler
+import com.lambda.interaction.handler.handlers.BaritoneHandler
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import java.awt.Color
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/BreakSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/BreakSim.kt
index 682d3f0b0..69adb7cbb 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/BreakSim.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/BreakSim.kt
@@ -28,15 +28,15 @@ import com.lambda.interaction.construction.simulation.result.results.BreakResult
import com.lambda.interaction.construction.simulation.result.results.GenericResult
import com.lambda.interaction.construction.simulation.sim
import com.lambda.interaction.construction.verify.TargetState
-import com.lambda.interaction.handlers.ContainerHandler.findContainersWithMaterial
-import com.lambda.interaction.managers.hotbar.HotbarManager
-import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest
-import com.lambda.interaction.managers.rotating.RotationManager
-import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer
-import com.lambda.interaction.material.StackSelection
-import com.lambda.interaction.material.StackSelection.Companion.EVERYTHING
-import com.lambda.interaction.material.StackSelection.Companion.selectStack
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.containers.HotbarContainer
+import com.lambda.interaction.container.selection.StackAndSlot
+import com.lambda.interaction.container.selection.StackSelection
+import com.lambda.interaction.container.selection.select
+import com.lambda.interaction.container.selection.stackSelection
+import com.lambda.interaction.handler.handlers.ContainerHandler.findStack
+import com.lambda.interaction.manager.managers.hotbar.HotbarManager
+import com.lambda.interaction.manager.managers.rotating.RotationManager
+import com.lambda.interaction.manager.managers.rotating.rotationRequest
import com.lambda.util.BlockUtils.blockState
import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
import com.lambda.util.BlockUtils.instantBreakable
@@ -138,56 +138,33 @@ class BreakSim internal constructor(simInfo: SimInfo)
}
private fun AutomatedSafeContext.getSwapStack(): Pair? {
- // Stack size 0 to account for attacking with an empty hand. Empty slots have stack size 0
- val stackSelection = selectStack(
- count = 0,
- sorter = compareByDescending {
- it.canBreak(CachedBlockPosition(world, pos, false))
- }.thenByDescending {
- state.calcItemBlockBreakingDelta(pos, it)
- }.thenByDescending {
- it.inventoryIndex == HotbarManager.serverSlot
- }
- ) {
- EVERYTHING
- .andIf(breakConfig.efficientOnly) {
- isEfficientForBreaking(state)
- }.andIf(breakConfig.suitableToolsOnly) {
- isSuitableForBreaking(state)
- }.andIf(breakConfig.forceSilkTouch) {
- hasEnchantment(Enchantments.SILK_TOUCH)
- }.andIf(breakConfig.avoidSilkTouch) {
- hasEnchantment(Enchantments.SILK_TOUCH).not()
- }.andIf(breakConfig.forceFortunePickaxe) {
- hasEnchantment(Enchantments.FORTUNE)
+ val stackSelection =
+ stackSelection(1) {
+ countEmptyAsOne()
+ alwaysWholeStacks()
+ if (breakConfig.efficientOnly) isEfficientForBreaking(state)
+ if (breakConfig.suitableToolsOnly) isSuitableForBreaking(state)
+ if (breakConfig.forceSilkTouch) withEnchantment(Enchantments.SILK_TOUCH)
+ if (breakConfig.avoidSilkTouch) withoutEnchantment(Enchantments.SILK_TOUCH)
+ if (breakConfig.forceFortunePickaxe) withEnchantment(Enchantments.FORTUNE)
+ sortedWith {
+ compareByDescending> {
+ it.stack.canBreak(CachedBlockPosition(world, pos, false))
+ }.thenByDescending {
+ state.calcItemBlockBreakingDelta(pos, it.stack)
+ }.thenByDescending {
+ it.stack.inventoryIndex == HotbarManager.serverSlot
+ }
}
- }
-
- val silentSwapSelection = selectContainer {
- ofAnyType(MaterialContainer.Rank.Hotbar)
- }
-
- val hotbarCandidates = stackSelection
- .findContainersWithMaterial(silentSwapSelection)
- .flatMap { it.matchingStacks(stackSelection) }
- if (hotbarCandidates.isEmpty()) {
- result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack))
- return null
- }
+ }
- var bestStack = ItemStack.EMPTY
- var bestBreakDelta = -1f
- hotbarCandidates.forEach { stack ->
- val breakDelta = state.calcItemBlockBreakingDelta(pos, stack)
- if (breakDelta > bestBreakDelta ||
- (stack == player.mainHandStack && breakDelta >= bestBreakDelta)
- ) {
- bestBreakDelta = breakDelta
- bestStack = stack
+ val bestStack = findStack(stackSelection, HotbarContainer.select())
+ ?: run {
+ result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack))
+ return null
}
- }
- return if (bestBreakDelta == -1f) null
- else Pair(bestStack, stackSelection)
+
+ return Pair(bestStack, stackSelection)
}
private suspend fun AutomatedSafeContext.affectsFluids(): Boolean {
diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/InteractSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/InteractSim.kt
index df60f11ff..74eaccae2 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/InteractSim.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/sims/InteractSim.kt
@@ -27,19 +27,17 @@ import com.lambda.interaction.construction.simulation.result.results.GenericResu
import com.lambda.interaction.construction.simulation.result.results.InteractResult
import com.lambda.interaction.construction.simulation.sim
import com.lambda.interaction.construction.verify.TargetState
-import com.lambda.interaction.handlers.ContainerHandler.findContainersWithMaterial
-import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest
-import com.lambda.interaction.managers.rotating.Rotation
-import com.lambda.interaction.managers.rotating.Rotation.Companion.rotation
-import com.lambda.interaction.managers.rotating.RotationManager
-import com.lambda.interaction.material.ContainerSelection.Companion.selectContainer
-import com.lambda.interaction.material.StackSelection
-import com.lambda.interaction.material.StackSelection.Companion.select
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.containers.HotbarContainer
+import com.lambda.interaction.container.selection.select
+import com.lambda.interaction.container.selection.stackSelection
+import com.lambda.interaction.handler.handlers.ContainerHandler.findSlot
+import com.lambda.interaction.manager.managers.rotating.Rotation
+import com.lambda.interaction.manager.managers.rotating.Rotation.Companion.rotation
+import com.lambda.interaction.manager.managers.rotating.RotationManager
+import com.lambda.interaction.manager.managers.rotating.rotationRequest
import com.lambda.util.BlockUtils.blockState
import com.lambda.util.EntityUtils.getPositionsWithinHitboxXZ
import com.lambda.util.PlaceDirection
-import com.lambda.util.item.ItemStackUtils.inventoryIndex
import com.lambda.util.item.ItemUtils.blockItem
import com.lambda.util.math.MathUtils.floorToInt
import com.lambda.util.math.minus
@@ -60,7 +58,6 @@ import net.minecraft.entity.Entity
import net.minecraft.item.BlockItem
import net.minecraft.item.Item
import net.minecraft.item.ItemPlacementContext
-import net.minecraft.item.Items
import net.minecraft.screen.slot.Slot
import net.minecraft.state.property.Properties
import net.minecraft.util.Hand
@@ -205,17 +202,20 @@ class InteractSim internal constructor(simInfo: InteractSimInfo)
supervisorScope.cancel()
return null
}
- val stackSelection = item?.select()?.apply { if (item == Items.AIR) count = 0 }
- ?: StackSelection.selectStack(0, sorter = compareByDescending { it.inventoryIndex == player.inventory.selectedSlot })
- val containerSelection = selectContainer { ofAnyType(MaterialContainer.Rank.Hotbar) }
- val container = stackSelection.findContainersWithMaterial(containerSelection).firstOrNull() ?: run {
- result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack))
- return null
- }
- return stackSelection.filterSlots(container.slots).run {
- firstOrNull { it.index == player.inventory.selectedSlot }
- ?: firstOrNull()
- }
+
+ val stackSelection =
+ stackSelection(1) {
+ countEmptyAsOne()
+ alwaysWholeStacks()
+ item?.let { isItem(it) }
+ sortedWith { compareByDescending { it.slot?.index == player.inventory.selectedSlot } }
+ }
+
+ return findSlot(stackSelection, HotbarContainer.select())
+ ?: run {
+ result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack))
+ null
+ }
}
private suspend fun AutomatedSafeContext.simRotation(
diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
index 9f30d19e2..7c5d72c22 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/verify/StateMatcher.kt
@@ -34,7 +34,9 @@ interface StateMatcher {
context(automatedSafeContext: AutomatedSafeContext)
fun getStack(pos: BlockPos): ItemStack
+
context(automatedSafeContext: AutomatedSafeContext)
fun getState(pos: BlockPos): BlockState
+
fun isEmpty(): Boolean
}
diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
index 91cb32184..e2451a63f 100644
--- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
+++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt
@@ -19,7 +19,8 @@ package com.lambda.interaction.construction.verify
import com.lambda.context.AutomatedSafeContext
import com.lambda.context.SafeContext
-import com.lambda.interaction.handlers.ContainerHandler.findDisposable
+import com.lambda.interaction.container.selection.stackSelection
+import com.lambda.interaction.handler.handlers.ContainerHandler.findStack
import com.lambda.util.BlockUtils.blockState
import com.lambda.util.BlockUtils.emptyState
import com.lambda.util.BlockUtils.isEmpty
@@ -86,9 +87,12 @@ sealed class TargetState : StateMatcher {
context(automatedSafeContext: AutomatedSafeContext)
override fun getStack(pos: BlockPos) =
with(automatedSafeContext) {
- findDisposable()?.stacks?.firstOrNull {
- it.item in inventoryConfig.disposables && it.item.block !in replace
- } ?: ItemStack(Items.NETHERRACK)
+ findStack(
+ stackSelection {
+ ofAnyItems(inventoryConfig.disposables)
+ predicate { stack, _ -> stack.item.block !in replace }
+ }
+ ) ?: ItemStack(Items.NETHERRACK)
}
context(_: AutomatedSafeContext)
@@ -113,9 +117,8 @@ sealed class TargetState : StateMatcher {
context(automatedSafeContext: AutomatedSafeContext)
override fun getStack(pos: BlockPos) =
with(automatedSafeContext) {
- findDisposable()?.stacks?.firstOrNull {
- it.item in inventoryConfig.disposables
- } ?: ItemStack(Items.NETHERRACK)
+ findStack(stackSelection { ofAnyItems(inventoryConfig.disposables) })
+ ?: ItemStack(Items.NETHERRACK)
}
context(_: AutomatedSafeContext)
@@ -176,12 +179,12 @@ sealed class TargetState : StateMatcher {
ignoredProperties: Collection>
) = state.block == block
- context(automatedSafeContext: AutomatedSafeContext)
+ context(_: AutomatedSafeContext)
override fun getStack(pos: BlockPos): ItemStack = itemStack
context(_: AutomatedSafeContext)
override fun getState(pos: BlockPos): BlockState = block.defaultState
- override fun isEmpty() = false
+ override fun isEmpty() = block.defaultState.isEmpty
}
}
diff --git a/src/main/kotlin/com/lambda/interaction/container/Container.kt b/src/main/kotlin/com/lambda/interaction/container/Container.kt
new file mode 100644
index 000000000..ed2875296
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/Container.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.context.Automated
+import com.lambda.context.AutomatedSafeContext
+import com.lambda.context.SafeContext
+import com.lambda.event.EventFlow.post
+import com.lambda.event.events.ContainerEvent
+import com.lambda.interaction.container.containers.CursorContainer
+import com.lambda.interaction.container.containers.external.ShulkerBoxContainer
+import com.lambda.interaction.container.selection.StackAndSlot
+import com.lambda.interaction.container.selection.StackSelection
+import com.lambda.interaction.container.selection.mutate
+import com.lambda.interaction.manager.managers.inventory.InvRequestBuilder
+import com.lambda.interaction.manager.managers.inventory.InventoryRequest
+import com.lambda.interaction.manager.managers.inventory.inventoryRequest
+import com.lambda.task.Task.Ta5kBuilder
+import com.lambda.util.Nameable
+import com.lambda.util.item.ItemStackUtils.count
+import com.lambda.util.item.ItemStackUtils.emptySpace
+import com.lambda.util.item.ItemStackUtils.shulkerBoxStacks
+import com.lambda.util.item.ItemStackUtils.spaceLeft
+import com.lambda.util.item.ItemUtils.SHULKER_BOXES
+import com.lambda.util.item.ItemUtils.toItemCount
+import com.lambda.util.text.TextBuilder
+import com.lambda.util.text.TextDsl
+import com.lambda.util.text.buildText
+import com.lambda.util.text.highlighted
+import com.lambda.util.text.literal
+import com.lambda.util.text.text
+import com.lambda.util.world.Dimension
+import net.minecraft.component.DataComponentTypes
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.slot.Slot
+import net.minecraft.text.Text
+
+@Suppress("unused")
+abstract class Container(
+ val type: ContainerType
+) : Nameable, Comparable {
+ override val name: String
+ get() = description.string
+
+ abstract val slots: List
+ abstract var stacks: List
+ val storedContainers = mutableMapOf()
+
+ open val dimension = Dimension.Unknown
+
+ open val swapMethodPriority = 0
+ abstract val description: Text
+
+ context(automated: Automated)
+ open val replaceSorter
+ get() = compareByDescending> {
+ it.stack.isEmpty
+ }.thenByDescending {
+ it.stack.item in automated.inventoryConfig.disposables
+ }.thenByDescending {
+ !it.stack.item.components.contains(DataComponentTypes.TOOL)
+ }.thenByDescending {
+ !it.stack.item.components.contains(DataComponentTypes.FOOD)
+ }.thenByDescending {
+ !it.stack.item.components.contains(DataComponentTypes.CONSUMABLE)
+ }.thenByDescending {
+ it.stack.isStackable
+ }
+
+ open val isAccessed get() = true
+
+ context(_: SafeContext)
+ fun descriptionAndStock(selection: StackSelection) =
+ buildText {
+ text(description)
+ stock(selection)
+ }
+
+ @TextDsl
+ context(_: SafeContext)
+ fun TextBuilder.stock(selection: StackSelection) {
+ literal("\n")
+ literal("Contains ")
+ val available = count(selection)
+ highlighted(if (available == Int.MAX_VALUE) "∞" else available.toItemCount())
+ literal(" of ")
+ highlighted("${selection.optimalStack?.name?.string}")
+ literal("\n")
+ literal("Could store ")
+ val left = spaceLeft(selection)
+ highlighted(if (left == Int.MAX_VALUE) "∞" else left.toItemCount())
+ literal(" of ")
+ highlighted("${selection.optimalStack?.name?.string}")
+ }
+
+ fun update(slots: List) {
+ this.stacks = slots
+ }
+
+ fun scanStacksForNestedContainers() {
+ stacks.forEachIndexed { index, stack ->
+ if (stack.item in SHULKER_BOXES) {
+ storedContainers[index] =
+ ShulkerBoxContainer(
+ stack.name.string,
+ stack.item,
+ stack.shulkerBoxStacks,
+ this,
+ index
+ ).also { it.scanStacksForNestedContainers() }
+ } else storedContainers.remove(index)
+ }
+ }
+
+ @Ta5kBuilder
+ context(automated: Automated)
+ open fun access(): OpenContainerTask<*>? = null
+
+ open fun count(selection: StackSelection) =
+ slots
+ .takeUnless { it.isEmpty() }
+ ?.let {
+ selection.filter(it, false).count(selection.countEmptyAsOne)
+ } ?: selection.filter(stacks, false).count(selection.countEmptyAsOne)
+
+ open fun spaceLeft(selection: StackSelection) =
+ slots
+ .takeUnless { it.isEmpty() }
+ ?.let {
+ selection.filter(it, false).spaceLeft + selection.emptySpace(it)
+ } ?: (selection.filter(stacks, false).spaceLeft + stacks.emptySpace)
+
+ open fun findSlots(selection: StackSelection) = selection.filter(slots)
+
+ fun findSlot(selection: StackSelection) = findSlots(selection).firstOrNull()
+
+ open fun findStacks(selection: StackSelection) = selection.filter(stacks)
+
+ fun findStack(selection: StackSelection) = findStacks(selection).firstOrNull()
+
+ context(automated: Automated)
+ open val isAccessible: Boolean
+ get() = type in automated.inventoryConfig.allowedContainers
+
+ open fun requiredContainers(toContainer: Container): List =
+ if (swapMethodPriority == 0 && toContainer.swapMethodPriority == 0) listOf(CursorContainer)
+ else emptyList()
+
+ context(_: Automated)
+ fun canSwapWith(toContainer: Container): Boolean =
+ requiredContainers(toContainer).all { it.isAccessible }
+
+ context(_: Automated)
+ fun findMoveSlots(
+ selection: StackSelection,
+ toContainer: Container,
+ toStackSelection: StackSelection = StackSelection.EVERYTHING
+ ): Pair {
+ if (!canSwapWith(toContainer)) return Pair(null, null)
+ val fromSlot = selection.bestMatch(slots)
+ val toSlot =
+ fromSlot?.let {
+ toContainer.findReplaceSlot(it.stack, toStackSelection)
+ } ?: toContainer.findReplaceSlot(toStackSelection)
+ val validToSlot =
+ toSlot?.takeIf {
+ it.stack.isEmpty ||
+ fromSlot == null ||
+ fromSlot.canInsert(it.stack)
+ }
+ return Pair(fromSlot, validToSlot)
+ }
+
+ context(_: Automated)
+ open fun findReplaceSlot(
+ selection: StackSelection = StackSelection.EVERYTHING
+ ) = selection
+ .mutate { sortedWith(replaceSorter) }
+ .bestMatch(slots)
+
+ context(_: Automated)
+ open fun findReplaceSlot(
+ incomingStack: ItemStack,
+ selection: StackSelection = StackSelection.EVERYTHING
+ ) = findReplaceSlot(selection.mutate { canInsert(incomingStack) })
+
+ context(automatedSafeContext: AutomatedSafeContext)
+ internal fun swapRequest(fromSlot: Slot, toSlot: Slot, toContainer: Container): InventoryRequest? {
+ if (!canSwapWith(toContainer)) return null
+ val transferEvent = ContainerEvent.Transfer(fromSlot, toSlot, this@Container, toContainer)
+ if (transferEvent.post().isCanceled()) return null
+ return automatedSafeContext.inventoryRequest {
+ if (swapMethodPriority > toContainer.swapMethodPriority) swap(fromSlot, toSlot)
+ else with(toContainer) { swap(toSlot, fromSlot) }
+ }
+ }
+
+ context(automatedSafeContext: AutomatedSafeContext)
+ internal fun swap(fromSlot: Slot, toSlot: Slot, toContainer: Container): Boolean =
+ swapRequest(fromSlot, toSlot, toContainer)?.submit()?.done ?: false
+
+ context(safeContext: SafeContext)
+ protected open fun InvRequestBuilder.swap(fromHere: Slot, toSlot: Slot) {
+ if (fromHere.stack.isEmpty) moveSlot(toSlot.id, fromHere.id)
+ else {
+ moveSlot(fromHere.id, toSlot.id)
+ if (!toSlot.stack.isEmpty) pickup(fromHere.id)
+ }
+ }
+
+ override fun compareTo(other: Container) =
+ compareBy { it.type }
+ .compare(this, other)
+
+ open fun haveMatchingInventories(other: Container) =
+ this == other ||
+ (this is PlacedContainer &&
+ other is PlacedContainer &&
+ this::class == other::class &&
+ dimension == other.dimension &&
+ pos == other.pos)
+}
diff --git a/src/main/kotlin/com/lambda/interaction/container/ContainerDslMarker.kt b/src/main/kotlin/com/lambda/interaction/container/ContainerDslMarker.kt
new file mode 100644
index 000000000..9269e464e
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/ContainerDslMarker.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+@DslMarker
+annotation class ContainerDslMarker
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/ContainerSerializer.kt b/src/main/kotlin/com/lambda/interaction/container/ContainerSerializer.kt
new file mode 100644
index 000000000..ba46de0a6
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/ContainerSerializer.kt
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.Lambda.LOG
+import com.lambda.Lambda.mapper
+import com.lambda.Lambda.mc
+import com.lambda.interaction.container.containers.external.EnderChestContainer
+import com.lambda.util.FolderRegistry
+import com.lambda.util.StringUtils.sanitizeForFilename
+import com.lambda.util.extension.dim
+import com.lambda.util.world.Dimension
+import net.minecraft.util.WorldSavePath
+import net.minecraft.util.math.BlockPos
+import net.minecraft.util.math.ChunkPos
+import java.net.InetSocketAddress
+import java.nio.file.Files
+import java.nio.file.Path
+import java.util.*
+import kotlin.io.path.createDirectories
+import kotlin.io.path.deleteIfExists
+import kotlin.io.path.exists
+import kotlin.io.path.notExists
+
+object ContainerSerializer {
+ private const val ENDER_CHEST_FILE = "ender_chest.json"
+
+ private val index =
+ mutableMapOf>>>()
+
+ val serializedContainers =
+ sequence {
+ val worldDir = currentWorldDirectory() ?: return@sequence
+ ensureIndex()
+
+ index.forEach { (dimension, chunks) ->
+ val dimensionDir = worldDir.resolve(dimension.name)
+ chunks.forEach { (_, byType) ->
+ byType.forEach { (type, positions) ->
+ positions.forEach { pos ->
+ val file = dimensionDir.resolve(containerFileName(pos, type))
+ readContainer(file)?.let { yield(it) }
+ }
+ }
+ }
+ }
+ }
+
+ @Volatile
+ private var indexed = false
+ private var indexedDirectory: Path? = null
+
+ fun serializedByDimensions(vararg dimensions: Dimension) =
+ sequence {
+ val worldDir = currentWorldDirectory() ?: return@sequence
+ ensureIndex()
+
+ index.forEach { (dimension, chunks) ->
+ if (dimension !in dimensions) return@forEach
+ val dimensionDir = worldDir.resolve(dimension.name)
+ chunks.forEach { (_, byType) ->
+ byType.forEach { (type, positions) ->
+ positions.forEach { pos ->
+ val file = dimensionDir.resolve(containerFileName(pos, type))
+ readContainer(file)?.let { yield(it) }
+ }
+ }
+ }
+ }
+ }
+
+ fun prepareIndex() {
+ synchronized(this) {
+ index.clear()
+ indexed = false
+ indexedDirectory = null
+ }
+ ensureIndex()
+ }
+
+ fun reset() {
+ synchronized(this) {
+ index.clear()
+ indexed = false
+ indexedDirectory = null
+ }
+ clearEnderChest()
+ }
+
+ fun savedIn(chunk: ChunkPos): Map {
+ val dimension = mc.world?.dim ?: return emptyMap()
+ return savedIn(dimension, chunk)
+ }
+
+ fun savedIn(dimension: Dimension, chunk: ChunkPos): Map {
+ ensureIndex()
+ val byType = index[dimension]?.get(chunk) ?: return emptyMap()
+ return buildMap {
+ byType.forEach { (type, positions) ->
+ positions.forEach { put(it, type) }
+ }
+ }
+ }
+
+ @Synchronized
+ fun saveContainer(container: PlacedContainer) {
+ ensureIndex()
+ val worldDir = currentWorldDirectory() ?: return
+ val dir = ensureDirectory(worldDir.resolve(container.dimension.name)) ?: return
+ val file = dir.resolve(containerFileName(container.pos, container.type))
+ mapper.writeValue(file, container)
+ record(container.dimension, container.pos, container.type)
+ }
+
+ @Synchronized
+ fun removeContainer(pos: BlockPos) {
+ val worldDir = currentWorldDirectory() ?: return
+ val dimension = mc.world?.dim ?: return
+ ensureIndex()
+ val type = removeRecord(dimension, pos) ?: return
+ worldDir.resolve(dimension.name).resolve(containerFileName(pos, type)).deleteIfExists()
+ }
+
+ fun saveEnderChest() {
+ val dir = ensureDirectory(currentWorldDirectory()) ?: return
+ val file = dir.resolve(ENDER_CHEST_FILE).toFile()
+ mapper.writeValue(file, EnderChestContainer)
+ }
+
+ fun loadEnderChest() {
+ clearEnderChest()
+ val dir = currentWorldDirectory() ?: return
+ val file = dir.resolve(ENDER_CHEST_FILE)
+ if (!file.exists()) return
+ runCatching {
+ mapper.readValue(file.toFile(), EnderChestContainer::class.java)
+ }.onFailure {
+ LOG.warn("Failed to load ender chest from $file", it)
+ }
+ }
+
+ private fun clearEnderChest() {
+ EnderChestContainer.update(emptyList())
+ EnderChestContainer.storedContainers.clear()
+ }
+
+ private fun ensureDirectory(dir: Path?): Path? {
+ if (dir == null) return null
+ if (dir.notExists()) dir.createDirectories()
+ return dir
+ }
+
+ private fun currentWorldDirectory(): Path? {
+ if (mc.world == null) return null
+ val name =
+ mc.server
+ ?.getSavePath(WorldSavePath.ROOT)
+ ?.normalize()
+ ?.fileName
+ ?.toString()
+ ?: mc.server?.saveProperties?.levelName
+ ?: mc.currentServerEntry?.address
+ ?: (mc.networkHandler?.connection?.address as? InetSocketAddress)?.hostName
+ ?: "unknown"
+ val folder = name
+ .sanitizeForFilename()
+ .takeUnless {
+ it.isBlank() ||
+ it == "." ||
+ it == ".."
+ }
+ ?: "unknown"
+ return FolderRegistry.containers.resolve(folder)
+ }
+
+ private fun ensureIndex() {
+ val worldDir = currentWorldDirectory() ?: return
+ if (indexed && indexedDirectory == worldDir) return
+ synchronized(this) {
+ if (indexed && indexedDirectory == worldDir) return
+ index.clear()
+ indexed = false
+ indexedDirectory = worldDir
+
+ if (worldDir.notExists()) {
+ indexed = true
+ return
+ }
+ val dimensionDirectories =
+ runCatching {
+ Files.newDirectoryStream(worldDir).use { stream ->
+ stream.filter { Files.isDirectory(it) }
+ }
+ }.onFailure {
+ LOG.warn("Failed to list saved container dimensions", it)
+ }.getOrDefault(emptyList())
+
+ var migrated = 0
+ dimensionDirectories.forEach dimensionLoop@{ dir ->
+ val dimension = Dimension.entries.firstOrNull { it.name == dir.fileName.toString() }
+ ?: return@dimensionLoop
+ val files =
+ runCatching {
+ Files.newDirectoryStream(dir, "*.json").use { stream -> stream.toList() }
+ }.onFailure {
+ LOG.warn("Failed to list saved containers in ${dir.fileName}", it)
+ }.getOrDefault(emptyList())
+
+ files.forEach fileLoop@{ path ->
+ runCatching {
+ val name = path.fileName.toString()
+ if (name == ENDER_CHEST_FILE) return@fileLoop
+ val parts = name.removeSuffix(".json").split('_')
+ val chunkX = parts[0].toIntOrNull() ?: return@fileLoop
+ val chunkZ = parts[1].toIntOrNull() ?: return@fileLoop
+ val pos =
+ BlockPos(
+ parts.getOrNull(2)?.toIntOrNull() ?: return@fileLoop,
+ parts.getOrNull(3)?.toIntOrNull() ?: return@fileLoop,
+ parts.getOrNull(4)?.toIntOrNull() ?: return@fileLoop
+ )
+ if (chunkX != pos.x shr 4 || chunkZ != pos.z shr 4) return@fileLoop
+
+ val named = parts
+ .getOrNull(5)
+ ?.let { token ->
+ ContainerType.entries.firstOrNull { it.name == token }
+ }
+ val type = named
+ ?: run {
+ val stored = readContainer(path)
+ if (stored == null) {
+ LOG.warn("Dropping unreadable container file $name")
+ Files.deleteIfExists(path)
+ return@fileLoop
+ }
+ Files.move(path, dir.resolve(containerFileName(pos, stored.type)))
+ migrated++
+ stored.type
+ }
+ record(dimension, pos, type)
+ }.onFailure {
+ LOG.warn("Failed to index saved container $path", it)
+ }
+ }
+ }
+ if (migrated > 0) LOG.info("Named $migrated saved container(s) with their container type")
+ indexed = true
+ }
+ }
+
+ private fun readContainer(file: Path) =
+ runCatching {
+ mapper.readValue(file.toFile(), PlacedContainer::class.java)
+ }.onFailure {
+ LOG.warn("Failed to read container from ${file.fileName}", it)
+ }.getOrNull()
+
+ private fun record(dimension: Dimension, pos: BlockPos, type: ContainerType) {
+ index.getOrPut(dimension) { HashMap() }
+ .getOrPut(ChunkPos(pos.x shr 4, pos.z shr 4)) { EnumMap(ContainerType::class.java) }
+ .getOrPut(type) { HashSet() }
+ .add(pos)
+ }
+
+ private fun removeRecord(dimension: Dimension, pos: BlockPos): ContainerType? {
+ val chunk = ChunkPos(pos.x shr 4, pos.z shr 4)
+ val chunks = index[dimension] ?: return null
+ val byType = chunks[chunk] ?: return null
+ val entry = byType.entries.firstOrNull { pos in it.value } ?: return null
+ entry.value.remove(pos)
+ if (entry.value.isEmpty()) byType.remove(entry.key)
+ if (byType.isEmpty()) chunks.remove(chunk)
+ if (chunks.isEmpty()) index.remove(dimension)
+ return entry.key
+ }
+
+ private fun containerFileName(pos: BlockPos, type: ContainerType): String {
+ val chunkX = pos.x shr 4
+ val chunkZ = pos.z shr 4
+ return "${chunkX}_${chunkZ}_${pos.x}_${pos.y}_${pos.z}_${type.name}.json"
+ }
+}
diff --git a/src/main/kotlin/com/lambda/interaction/container/ContainerType.kt b/src/main/kotlin/com/lambda/interaction/container/ContainerType.kt
new file mode 100644
index 000000000..cfc76c5f5
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/ContainerType.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+enum class ContainerType {
+ OffHand,
+ Hotbar,
+ Cursor,
+ Inventory,
+ Armor,
+ Creative,
+ ShulkerBox,
+ EnderChest,
+ PlacedShulkerBox,
+ PlacedEnderChest,
+ Chest,
+ Stash
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/context/IAutomatedSafeContext.kt b/src/main/kotlin/com/lambda/interaction/container/ExternalContainer.kt
similarity index 89%
rename from src/main/kotlin/com/lambda/context/IAutomatedSafeContext.kt
rename to src/main/kotlin/com/lambda/interaction/container/ExternalContainer.kt
index a41eee489..500d2f6e2 100644
--- a/src/main/kotlin/com/lambda/context/IAutomatedSafeContext.kt
+++ b/src/main/kotlin/com/lambda/interaction/container/ExternalContainer.kt
@@ -15,6 +15,6 @@
* along with this program. If not, see .
*/
-package com.lambda.context
+package com.lambda.interaction.container
-interface IAutomatedSafeContext : SafeContext, Automated
\ No newline at end of file
+interface ExternalContainer
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/NestedContainer.kt b/src/main/kotlin/com/lambda/interaction/container/NestedContainer.kt
new file mode 100644
index 000000000..3b18a6700
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/NestedContainer.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.context.Automated
+
+abstract class NestedContainer(type: ContainerType) : Container(type), ExternalContainer {
+ abstract val containedIn: Container
+ abstract val index: Int
+
+ override val dimension get() = containedIn.dimension
+
+ context(_: Automated)
+ override val isAccessible: Boolean
+ get() = super.isAccessible && containedIn.isAccessible
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt b/src/main/kotlin/com/lambda/interaction/container/OpenContainerTask.kt
similarity index 70%
rename from src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt
rename to src/main/kotlin/com/lambda/interaction/container/OpenContainerTask.kt
index 7bad81182..27cc94c61 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/container/OpenContainerTask.kt
@@ -15,13 +15,13 @@
* along with this program. If not, see .
*/
-package com.lambda.interaction.material.container
+package com.lambda.interaction.container
-import com.lambda.context.AutomatedSafeContext
import com.lambda.task.Task
-import com.lambda.task.wrappers.TaskSupplier
+import net.minecraft.text.Text
-interface ExternalContainer {
- context(_: AutomatedSafeContext)
- fun accessThen(exitAfter: Boolean = true, taskSupplier: TaskSupplier): Task<*>?
+abstract class OpenContainerTask @Ta5kBuilder internal constructor(
+ description: Text
+) : Task() {
+ override val name = "accessing container: ${description.string}"
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/OpenedContainerContext.kt b/src/main/kotlin/com/lambda/interaction/container/OpenedContainerContext.kt
new file mode 100644
index 000000000..dfb78544b
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/OpenedContainerContext.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.context.Automated
+import com.lambda.task.Task
+import com.lambda.task.Task.Ta5kBuilder
+import com.lambda.task.tasks.wrappers.actionTask
+
+interface OpenedContainerContext {
+ @Ta5kBuilder
+ context(automated: Automated)
+ fun close(): Task<*>?
+}
+
+open class BasicOpenedContainerContext(
+ private val isAccessed: () -> Boolean
+) : OpenedContainerContext {
+ context(_: Automated)
+ final override fun close() =
+ if (!isAccessed()) null
+ else actionTask { player.closeHandledScreen() }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/PlacedContainer.kt b/src/main/kotlin/com/lambda/interaction/container/PlacedContainer.kt
new file mode 100644
index 000000000..2383ae0af
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/PlacedContainer.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.interaction.container.containers.external.StashContainer
+import com.lambda.util.world.Dimension
+import net.minecraft.util.math.BlockPos
+
+abstract class PlacedContainer(
+ type: ContainerType,
+ override val dimension: Dimension
+) : Container(type), ExternalContainer {
+ abstract val pos: BlockPos
+ abstract val stash: StashContainer?
+ open val positions: Set = setOf(pos)
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/SlotIdentity.kt b/src/main/kotlin/com/lambda/interaction/container/SlotIdentity.kt
new file mode 100644
index 000000000..5753ef868
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/SlotIdentity.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container
+
+import com.lambda.context.SafeContext
+import com.lambda.interaction.handler.handlers.ContainerHandler
+import net.minecraft.block.entity.EnderChestBlockEntity
+import net.minecraft.entity.player.PlayerInventory
+import net.minecraft.inventory.SimpleInventory
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.slot.Slot
+import net.minecraft.util.math.BlockPos
+
+sealed interface SlotIdentity {
+ data class Player(val index: Int) : SlotIdentity
+ data class Placed(val positions: Set, val index: Int) : SlotIdentity
+ data class Ender(val index: Int) : SlotIdentity
+
+ context(safeContext: SafeContext)
+ fun resolve(): Slot? {
+ val handler = safeContext.player.currentScreenHandler
+ val (key, ender) = currentContainerAnchor()
+ return resolveIn(handler.slots, key, ender)
+ }
+
+ fun resolveIn(
+ slots: List,
+ containerKey: Set?,
+ enderOpen: Boolean
+ ): Slot? =
+ when (this) {
+ is Player ->
+ slots.firstOrNull { it.inventory is PlayerInventory && it.index == index }
+ is Placed ->
+ if (containerKey != null && containerKey == positions)
+ slots.firstOrNull { it.inventory is SimpleInventory && it.index == index }
+ else null
+ is Ender ->
+ if (enderOpen)
+ slots.firstOrNull { it.inventory is SimpleInventory && it.index == index }
+ else null
+ }
+
+ context(safeContext: SafeContext)
+ fun holds(stack: ItemStack): Boolean {
+ val live = resolve()?.stack ?: return false
+ if (live.isEmpty || stack.isEmpty) return false
+ return live.item == stack.item && live.name.string == stack.name.string
+ }
+}
+
+fun Slot.identity(): SlotIdentity? {
+ if (inventory is PlayerInventory) return SlotIdentity.Player(index)
+ if (inventory !is SimpleInventory) return null
+ val (key, ender) = currentContainerAnchor()
+ if (key != null) return SlotIdentity.Placed(key, index)
+ if (ender) return SlotIdentity.Ender(index)
+ return null
+}
+
+fun currentContainerAnchor(): Pair?, Boolean> =
+ ContainerHandler.accessedPlacedContainer?.positions to
+ (ContainerHandler.lastInteractedBlockEntity is EnderChestBlockEntity)
diff --git a/src/main/kotlin/com/lambda/interaction/container/containers/ArmorContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/ArmorContainer.kt
new file mode 100644
index 000000000..2a76c37ab
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/ArmorContainer.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container.containers
+
+import com.lambda.Lambda.mc
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
+import com.lambda.util.player.SlotUtils.armorSlots
+import com.lambda.util.player.SlotUtils.armorStacks
+import com.lambda.util.text.buildText
+import com.lambda.util.text.literal
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.slot.Slot
+
+object ArmorContainer : Container(ContainerType.Armor) {
+ override val slots: List
+ get() = mc.player?.armorSlots ?: emptyList()
+ override var stacks: List
+ get() = mc.player?.armorStacks ?: emptyList()
+ set(_) {}
+
+ override val description = buildText { literal("Armor") }
+
+ override val isAccessed get() = mc.player?.currentScreenHandler?.syncId == 0
+}
diff --git a/src/main/kotlin/com/lambda/interaction/container/containers/CreativeContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/CreativeContainer.kt
new file mode 100644
index 000000000..6f9d1181d
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/CreativeContainer.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container.containers
+
+import com.lambda.Lambda.mc
+import com.lambda.context.SafeContext
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.container.selection.StackSelection
+import com.lambda.interaction.manager.managers.inventory.InvRequestBuilder
+import com.lambda.util.text.buildText
+import com.lambda.util.text.literal
+import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.inventory.SingleStackInventory
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.PlayerScreenHandler
+import net.minecraft.screen.slot.Slot
+
+@Suppress("unused")
+data object CreativeContainer : Container(ContainerType.Creative) {
+ override val slots = emptyList()
+ override var stacks = emptyList()
+
+ override val swapMethodPriority = 11
+
+ override val description = buildText { literal("Creative") }
+
+ override fun count(selection: StackSelection): Int =
+ if (mc.player?.isCreative == true && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else -1
+
+ override fun spaceLeft(selection: StackSelection): Int =
+ if (mc.player?.isCreative == true && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else -1
+
+ context(safeContext: SafeContext)
+ override fun InvRequestBuilder.swap(fromHere: Slot, toSlot: Slot) {
+ clickCreativeStack(fromHere.stack, toSlot.id)
+ safeContext.player.currentScreenHandler.slots.getOrNull(toSlot.id)?.stack = fromHere.stack
+ }
+
+ override fun findSlots(selection: StackSelection) =
+ listOfNotNull(
+ selection.optimalStack?.let { stack ->
+ Slot(
+ object : SingleStackInventory {
+ override fun getStack() = stack
+ override fun setStack(stack: ItemStack?) {}
+ override fun markDirty() {}
+ override fun canPlayerUse(player: PlayerEntity?) = false
+ },
+ 0, 0, 0
+ )
+ }
+ )
+
+ override fun findStacks(selection: StackSelection) =
+ listOfNotNull(selection.optimalStack)
+
+ private val correctScreenHandler
+ get() = mc.player?.currentScreenHandler is PlayerScreenHandler || mc.player?.currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler
+}
diff --git a/src/main/kotlin/com/lambda/interaction/container/containers/CursorContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/CursorContainer.kt
new file mode 100644
index 000000000..b19df139d
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/CursorContainer.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container.containers
+
+import com.lambda.Lambda.mc
+import com.lambda.context.SafeContext
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.manager.managers.inventory.InvRequestBuilder
+import com.lambda.util.text.buildText
+import com.lambda.util.text.literal
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.inventory.SingleStackInventory
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.ScreenHandler
+import net.minecraft.screen.slot.Slot
+
+object CursorContainer : Container(ContainerType.Cursor) {
+ override val slots: List
+ get() =
+ mc.player?.currentScreenHandler?.let {
+ listOf(CursorSlot(it))
+ } ?: emptyList()
+
+ override var stacks: List
+ get() =
+ mc.player?.currentScreenHandler?.cursorStack?.let {
+ listOf(it)
+ } ?: emptyList()
+ set(_) {}
+
+ override val swapMethodPriority = 12
+
+ override val description = buildText { literal("Cursor") }
+
+ context(safeContext: SafeContext)
+ override fun InvRequestBuilder.swap(fromHere: Slot, toSlot: Slot) {
+ pickup(toSlot.id, 0)
+ }
+
+ class CursorSlot(val screenHandler: ScreenHandler) : Slot(
+ object : SingleStackInventory {
+ override fun getStack(): ItemStack = screenHandler.cursorStack
+ override fun setStack(stack: ItemStack?) {
+ if (stack != null) screenHandler.cursorStack = stack
+ }
+ override fun markDirty() {}
+ override fun canPlayerUse(player: PlayerEntity?) = true
+ },
+ 0, 0, 0
+ ) {
+ override fun canInsert(stack: ItemStack): Boolean = true
+ override fun canTakeItems(playerEntity: PlayerEntity?): Boolean = true
+ }
+}
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/HotbarContainer.kt
similarity index 71%
rename from src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt
rename to src/main/kotlin/com/lambda/interaction/container/containers/HotbarContainer.kt
index f2dc5d3f4..3e5ba3744 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/HotbarContainer.kt
@@ -15,22 +15,22 @@
* along with this program. If not, see .
*/
-package com.lambda.interaction.material.container.containers
+package com.lambda.interaction.container.containers
import com.lambda.Lambda.mc
import com.lambda.context.SafeContext
-import com.lambda.interaction.managers.inventory.InventoryRequest
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.manager.managers.inventory.InvRequestBuilder
import com.lambda.util.player.SlotUtils.hotbarSlots
import com.lambda.util.player.SlotUtils.hotbarStacks
import com.lambda.util.text.buildText
import com.lambda.util.text.literal
import net.minecraft.screen.slot.Slot
-object HotbarContainer : MaterialContainer(Rank.Hotbar) {
- context(safeContext: SafeContext)
+object HotbarContainer : Container(ContainerType.Hotbar) {
override val slots: List
- get() = safeContext.player.hotbarSlots
+ get() = mc.player?.hotbarSlots ?: emptyList()
override var stacks
get() = mc.player?.hotbarStacks ?: emptyList()
set(_) {}
@@ -40,7 +40,7 @@ object HotbarContainer : MaterialContainer(Rank.Hotbar) {
override val description = buildText { literal("Hotbar") }
context(safeContext: SafeContext)
- override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) {
- swap(toSlot.id, safeContext.player.hotbarSlots.indexOf(fromHere))
+ override fun InvRequestBuilder.swap(fromHere: Slot, toSlot: Slot) {
+ swapWithHotbar(toSlot.id, safeContext.player.hotbarSlots.indexOf(fromHere))
}
}
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/InventoryContainer.kt
similarity index 78%
rename from src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt
rename to src/main/kotlin/com/lambda/interaction/container/containers/InventoryContainer.kt
index 10867bdb2..f1c56e1cf 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/InventoryContainer.kt
@@ -15,11 +15,11 @@
* along with this program. If not, see .
*/
-package com.lambda.interaction.material.container.containers
+package com.lambda.interaction.container.containers
import com.lambda.Lambda.mc
-import com.lambda.context.SafeContext
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
import com.lambda.util.player.SlotUtils.inventorySlots
import com.lambda.util.player.SlotUtils.inventoryStacks
import com.lambda.util.text.buildText
@@ -27,10 +27,9 @@ import com.lambda.util.text.literal
import net.minecraft.item.ItemStack
import net.minecraft.screen.slot.Slot
-object InventoryContainer : MaterialContainer(Rank.Inventory) {
- context(safeContext: SafeContext)
+object InventoryContainer : Container(ContainerType.Inventory) {
override val slots: List
- get() = safeContext.player.inventorySlots
+ get() = mc.player?.inventorySlots ?: emptyList()
override var stacks: List
get() = mc.player?.inventoryStacks ?: emptyList()
set(_) {}
diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/OffhandContainer.kt
similarity index 73%
rename from src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt
rename to src/main/kotlin/com/lambda/interaction/container/containers/OffhandContainer.kt
index 2ae235e42..b2b5fefb6 100644
--- a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/OffhandContainer.kt
@@ -15,22 +15,22 @@
* along with this program. If not, see .
*/
-package com.lambda.interaction.material.container.containers
+package com.lambda.interaction.container.containers
import com.lambda.Lambda.mc
import com.lambda.context.SafeContext
-import com.lambda.interaction.managers.inventory.InventoryRequest
-import com.lambda.interaction.material.container.MaterialContainer
+import com.lambda.interaction.container.Container
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.manager.managers.inventory.InvRequestBuilder
import com.lambda.util.player.SlotUtils.offHandSlots
import com.lambda.util.text.buildText
import com.lambda.util.text.literal
import net.minecraft.item.ItemStack
import net.minecraft.screen.slot.Slot
-object OffHandContainer : MaterialContainer(Rank.OffHand) {
- context(safeContext: SafeContext)
+object OffhandContainer : Container(ContainerType.OffHand) {
override val slots: List
- get() = safeContext.player.offHandSlots
+ get() = mc.player?.offHandSlots ?: emptyList()
override var stacks: List
get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList()
set(_) {}
@@ -40,7 +40,7 @@ object OffHandContainer : MaterialContainer(Rank.OffHand) {
override val description = buildText { literal("OffHand") }
context(_: SafeContext)
- override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) {
- swap(toSlot.id, 40)
+ override fun InvRequestBuilder.swap(fromHere: Slot, toSlot: Slot) {
+ swapWithHotbar(toSlot.id, 40)
}
}
diff --git a/src/main/kotlin/com/lambda/interaction/container/containers/external/ChestContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/external/ChestContainer.kt
new file mode 100644
index 000000000..ae38f8a45
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/external/ChestContainer.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.lambda.interaction.container.containers.external
+
+import com.lambda.Lambda.mc
+import com.lambda.context.Automated
+import com.lambda.context.SafeContext
+import com.lambda.interaction.container.BasicOpenedContainerContext
+import com.lambda.interaction.container.ContainerType
+import com.lambda.interaction.container.OpenContainerTask
+import com.lambda.interaction.container.OpenedContainerContext
+import com.lambda.interaction.container.PlacedContainer
+import com.lambda.interaction.handler.handlers.ContainerHandler.lastInteractedBlockEntity
+import com.lambda.task.Task.Ta5kBuilder
+import com.lambda.task.tasks.openContainer
+import com.lambda.util.extension.containerSlots
+import com.lambda.util.extension.dim
+import com.lambda.util.player.SlotUtils.typeSafe
+import com.lambda.util.text.buildText
+import com.lambda.util.text.highlighted
+import com.lambda.util.text.literal
+import com.lambda.util.world.Dimension
+import net.minecraft.block.ChestBlock
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.ScreenHandlerType
+import net.minecraft.screen.slot.Slot
+import net.minecraft.util.math.BlockPos
+
+data class ChestContainer(
+ override val pos: BlockPos,
+ override var stacks: List,
+ override val stash: StashContainer? = null,
+ override val dimension: Dimension = mc.world?.dim ?: Dimension.Unknown
+) : PlacedContainer(ContainerType.Chest, dimension) {
+ override val slots
+ get(): List =
+ if (isAccessed) mc.player?.currentScreenHandler?.containerSlots ?: emptyList()
+ else emptyList()
+
+ override val description =
+ buildText {
+ literal("Chest at ")
+ highlighted(pos.toShortString())
+ stash?.let { stash ->
+ literal(" (contained in ")
+ highlighted(stash.name)
+ literal(")")
+ }
+ }
+
+ override val isAccessed
+ get() = lastInteractedBlockEntity?.let { blockEntity ->
+ blockEntity.pos == pos &&
+ blockEntity.cachedState.block is ChestBlock &&
+ mc.player?.currentScreenHandler?.typeSafe == ScreenHandlerType.GENERIC_9X3
+ } ?: false
+
+ @Ta5kBuilder
+ context(automated: Automated)
+ override fun access() =
+ if (isAccessed) null
+ else AccessChestTask(automated)
+
+ inner class AccessChestTask @Ta5kBuilder internal constructor(
+ automated: Automated
+ ) : OpenContainerTask(description), Automated by automated {
+ override fun SafeContext.onStart() {
+ openContainer(pos)
+ .onSuccess { success(BasicOpenedContainerContext(::isAccessed)) }
+ .start()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/lambda/interaction/container/containers/external/DoubleChestContainer.kt b/src/main/kotlin/com/lambda/interaction/container/containers/external/DoubleChestContainer.kt
new file mode 100644
index 000000000..4b5dd9d14
--- /dev/null
+++ b/src/main/kotlin/com/lambda/interaction/container/containers/external/DoubleChestContainer.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2026 Lambda
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see