From cf632225b7b45b9f030899bd646743033f351b34 Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Tue, 15 Sep 2026 16:16:24 -0700 Subject: [PATCH 1/3] adds stripped state pre processor --- .../state/StrippedStateProcessor.kt | 82 +++++++++++++++++++ src/main/resources/lambda.accesswidener | 1 + 2 files changed, 83 insertions(+) create mode 100644 src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt new file mode 100644 index 000000000..67f824cd3 --- /dev/null +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt @@ -0,0 +1,82 @@ +/* + * 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.construction.simulation.processing.preprocessors.state + +import com.lambda.context.SafeContext +import com.lambda.interaction.construction.simulation.processing.PreProcessingInfoAccumulator +import com.lambda.interaction.construction.simulation.processing.StateProcessor +import com.lambda.util.item.ItemUtils +import net.minecraft.block.Block +import net.minecraft.block.BlockState +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.AxeItem +import net.minecraft.item.Item +import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos + +@Suppress("unused") +object StrippedStateProcessor : StateProcessor { + // [AxeItem.STRIPPED_BLOCKS] maps unstripped to stripped, we need the other direction + private val unstrippedToStripped: Map by lazy { + AxeItem.STRIPPED_BLOCKS.entries.associate { (from, to) -> to to from } + } + + override fun acceptsState(state: BlockState, targetState: BlockState): Boolean { + val unstrippedVariant = unstrippedToStripped[targetState.block] ?: return false + return state.isReplaceable || state.block == unstrippedVariant + } + + context(safeContext: SafeContext) + override fun PreProcessingInfoAccumulator.preProcess(state: BlockState, targetState: BlockState, pos: BlockPos) { + val unstrippedVariant = unstrippedToStripped[targetState.block] ?: return + val player = safeContext.player + + // Every branch below depends on the current inventory, so this must never be cached. + noCaching() + + if (state.isReplaceable){ + val sourceItem = unstrippedVariant.asItem() + if (!player.carries(sourceItem)) return + + setExpectedState(unstrippedVariant.withAxisOf(targetState)) + setItem(sourceItem) + } else if (state.block == unstrippedVariant) { + val axe = player.findAxe() ?: return + setItem(axe) + setPlacing(false) + // InteractSim refuses to interact while sneaking, so ask for it explicitly. + setSneak(false) + return + } + } + + private fun Block.withAxisOf(targetState: BlockState) = defaultState.let { placed -> + if (Properties.AXIS in placed && Properties.AXIS in targetState) { + placed.with(Properties.AXIS, targetState.get(Properties.AXIS)) + } else placed + } + + private fun PlayerEntity.findAxe(): Item? { + val held = mainHandStack.item + if (held in ItemUtils.axes) return held + return inventory.mainStacks.firstOrNull { it.item in ItemUtils.axes }?.item + } + + private fun PlayerEntity.carries(item: Item) = + inventory.mainStacks.any { it.item == item } +} diff --git a/src/main/resources/lambda.accesswidener b/src/main/resources/lambda.accesswidener index c446edbb0..f887bb3c4 100644 --- a/src/main/resources/lambda.accesswidener +++ b/src/main/resources/lambda.accesswidener @@ -150,6 +150,7 @@ transitive-accessible method net/minecraft/item/DebugStickItem cycle (Lnet/minec transitive-accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; transitive-accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; transitive-accessible method net/minecraft/block/AbstractBlock getPickStack (Lnet/minecraft/world/WorldView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/item/ItemStack; +transitive-accessible field net/minecraft/item/AxeItem STRIPPED_BLOCKS Ljava/util/Map; transitive-accessible field net/minecraft/client/gui/screen/ingame/HandledScreen focusedSlot Lnet/minecraft/screen/slot/Slot; transitive-accessible field net/minecraft/registry/SimpleRegistry frozen Z transitive-accessible field net/minecraft/client/gui/screen/ingame/AbstractSignEditScreen messages [Ljava/lang/String; From 0dd1156941afc6a19a3ebee95872d0f8c65ef394 Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Tue, 15 Sep 2026 16:38:25 -0700 Subject: [PATCH 2/3] adds stripped state pre processor --- .../processing/preprocessors/state/StrippedStateProcessor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt index 67f824cd3..cee29514c 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt @@ -50,6 +50,9 @@ object StrippedStateProcessor : StateProcessor { noCaching() if (state.isReplaceable){ + // Don't use unstripped logs if you already have stripped ones + if (player.carries(targetState.block.asItem())) return + val sourceItem = unstrippedVariant.asItem() if (!player.carries(sourceItem)) return @@ -61,7 +64,6 @@ object StrippedStateProcessor : StateProcessor { setPlacing(false) // InteractSim refuses to interact while sneaking, so ask for it explicitly. setSneak(false) - return } } From 7d852ab97a19bb8347af3b71a2606d91fab5318c Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Wed, 16 Sep 2026 08:33:04 -0700 Subject: [PATCH 3/3] Adds setting for toggling stripping logs --- .../kotlin/com/lambda/config/blocks/BuildConfig.kt | 1 + .../com/lambda/config/blocks/BuildSettings.kt | 1 + .../simulation/processing/PlacementProcessor.kt | 5 +++++ .../simulation/processing/ProcessorRegistry.kt | 13 ++++++++----- .../preprocessors/state/StrippedStateProcessor.kt | 4 ++++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt b/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt index 7e98fc835..807f46b1f 100644 --- a/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt +++ b/src/main/kotlin/com/lambda/config/blocks/BuildConfig.kt @@ -34,6 +34,7 @@ interface BuildConfig { val collectDrops: Boolean val spleefEntities: Boolean val cautionDoubleBlocks: Boolean + val stripLogs: Boolean val maxPendingActions: Int val actionTimeout: Int val maxBuildDependencies: Int diff --git a/src/main/kotlin/com/lambda/config/blocks/BuildSettings.kt b/src/main/kotlin/com/lambda/config/blocks/BuildSettings.kt index b03efbe5c..63102e414 100644 --- a/src/main/kotlin/com/lambda/config/blocks/BuildSettings.kt +++ b/src/main/kotlin/com/lambda/config/blocks/BuildSettings.kt @@ -38,6 +38,7 @@ class BuildSettings(override val c: Config) : BuildConfig, ConfigBlock { @Group(GENERAL_GROUP) override val collectDrops by c.setting("Collect All Drops", false, "Collect all drops when breaking blocks") @Group(GENERAL_GROUP) override val spleefEntities by c.setting("Spleef Entities", false, "Breaks blocks beneath entities blocking placements to get them out of the way") @Group(GENERAL_GROUP) override val cautionDoubleBlocks by c.setting("Caution Double Blocks", true, "Prevents spamming double blocks like doors, chests, etc when configured to interact more than once per tick") + @Group(GENERAL_GROUP) override val stripLogs by c.setting("Strip Logs", true, "Builds stripped blocks by placing the unstripped variant and stripping it with an axe when the stripped item isn't carried") @Group(GENERAL_GROUP) override val maxPendingActions by c.setting("Max Pending Actions", 59, 1..60, 1, "The maximum count of pending interactions to allow before pausing future interactions") @Group(GENERAL_GROUP) override val actionTimeout by c.setting("Action Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") @Group(GENERAL_GROUP) override val maxBuildDependencies by c.setting("Max Sim Dependencies", 3, 0..10, 1, "Maximum dependency build results") diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/PlacementProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/PlacementProcessor.kt index 841ca28c9..332ef30ad 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/PlacementProcessor.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/PlacementProcessor.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.simulation.processing +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import net.minecraft.block.BlockState import net.minecraft.util.math.BlockPos @@ -27,6 +28,10 @@ import net.minecraft.util.math.BlockPos * unnecessary to scan all of them, for example. */ interface StateProcessor { + /** Checked before [acceptsState], so a disabled processor still falls back to breaking. */ + context(_: AutomatedSafeContext) + fun isEnabled(): Boolean = true + fun acceptsState(state: BlockState, targetState: BlockState): Boolean context(safeContext: SafeContext) diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/ProcessorRegistry.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/ProcessorRegistry.kt index 3ec075142..9aaf2aadd 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/ProcessorRegistry.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/ProcessorRegistry.kt @@ -18,7 +18,6 @@ package com.lambda.interaction.construction.simulation.processing import com.lambda.context.AutomatedSafeContext -import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.interaction.construction.simulation.SimDsl import com.lambda.interaction.construction.verify.TargetState @@ -144,15 +143,19 @@ object ProcessorRegistry : Loadable { return PreProcessingData(preProcessingInfo, pos) } - context(safeContext: SafeContext) + context(_: AutomatedSafeContext) private fun preProcess(pos: BlockPos, state: BlockState, targetState: BlockState, itemStack: ItemStack) = PreProcessingInfoAccumulator(targetState, itemStack.item).run { var stateProcessing = false stateProcessors.forEach { processor -> - if (processor.acceptsState(state, targetState)) { - with(processor) { preProcess(state, targetState, pos) } - stateProcessing = true + if (!processor.acceptsState(state, targetState)) return@forEach + if (!processor.isEnabled()) { + // The verdict now hinges on a setting that can be toggled at any time. + noCaching() + return@forEach } + with(processor) { preProcess(state, targetState, pos) } + stateProcessing = true } if (!omitInteraction) { if (state.block != expectedState.block) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt index cee29514c..ee405aef5 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/processing/preprocessors/state/StrippedStateProcessor.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.construction.simulation.processing.preprocessors.state +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.interaction.construction.simulation.processing.PreProcessingInfoAccumulator import com.lambda.interaction.construction.simulation.processing.StateProcessor @@ -36,6 +37,9 @@ object StrippedStateProcessor : StateProcessor { AxeItem.STRIPPED_BLOCKS.entries.associate { (from, to) -> to to from } } + context(automatedSafeContext: AutomatedSafeContext) + override fun isEnabled() = automatedSafeContext.buildConfig.stripLogs + override fun acceptsState(state: BlockState, targetState: BlockState): Boolean { val unstrippedVariant = unstrippedToStripped[targetState.block] ?: return false return state.isReplaceable || state.block == unstrippedVariant