Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
788bcc9
rename material directory to inventory and rename MaterialContainer t…
beanbag44 Jul 6, 2026
be3257d
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Jul 8, 2026
b961cf2
rework StackSelection. Uses builder dsl and the final product is immu…
beanbag44 Jul 8, 2026
3b043d9
immutable container selections with builder and general cleanup
beanbag44 Jul 9, 2026
a38c0bf
should be finished container transfer task setup. Need to finish exte…
beanbag44 Jul 12, 2026
840550a
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Jul 14, 2026
4017227
lotta changes, still a lotta broken stuff
beanbag44 Jul 15, 2026
8b4957e
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Jul 16, 2026
cf1c069
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Jul 28, 2026
bc1f372
cleanup, still broken. Need to rework task chaining
beanbag44 Jul 28, 2026
678f149
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Aug 23, 2026
4b3d4b4
initial merge
beanbag44 Aug 23, 2026
d2966cf
idk, a bunch of stuff
beanbag44 Aug 28, 2026
d008a36
cleanup. Last thing left is returning slots for picked up items or so…
beanbag44 Aug 29, 2026
9453645
fix memory leak (?) and general bugs
beanbag44 Aug 29, 2026
3dae2b6
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Aug 29, 2026
e555c36
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Aug 29, 2026
ccd3a08
better container caching, etc
beanbag44 Aug 31, 2026
4152230
fixes n stuff
beanbag44 Sep 1, 2026
e393250
remove inventory package and move container up
beanbag44 Sep 3, 2026
b7dfcde
checkpoint
beanbag44 Sep 8, 2026
8d0c65f
fix all syntax errors but most things are broken/could be written better
beanbag44 Sep 10, 2026
d9efa6d
improvements
beanbag44 Sep 12, 2026
e710f2f
cleanup
beanbag44 Sep 14, 2026
9b92f5f
more improvements
beanbag44 Sep 15, 2026
4b0c4d0
try to move away from direct slot retrieval so containers can be filt…
beanbag44 Sep 15, 2026
fed568c
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Sep 15, 2026
7b70bc4
cleanup/fixes
beanbag44 Sep 15, 2026
b1f7de7
big checkpoint
beanbag44 Oct 2, 2026
1a51af5
update jackson version
beanbag44 Oct 2, 2026
c18a7c3
better
beanbag44 Oct 4, 2026
565ab83
refactor
beanbag44 Oct 4, 2026
5d96dd9
cleanup
beanbag44 Oct 6, 2026
4181e4d
Merge branch '1.21.11' into improvement/containers-and-stack-selections
beanbag44 Oct 6, 2026
a59c6bf
fixes
beanbag44 Oct 6, 2026
f81a93d
move builder entry functions out of companion objects
beanbag44 Oct 6, 2026
4b71ca6
cleanup and perform as many moves in each tick as possible
beanbag44 Oct 7, 2026
fdd02cb
hopefully fix lots of issues with inventory desync and also fix huge …
beanbag44 Oct 7, 2026
ff9c5a5
better anti desync
beanbag44 Oct 9, 2026
8a5f9a0
fix cursor desync and an edge case InventoryTweaks showed up
beanbag44 Oct 9, 2026
e9035ad
smaller debug logs
beanbag44 Oct 9, 2026
048bfd0
move open and close callbacks into open functions rather than params
beanbag44 Oct 10, 2026
d12ba63
fixes and improvements
beanbag44 Oct 10, 2026
5f90cf4
only place shulker in original slot if the slot is empty
beanbag44 Oct 10, 2026
afcdd71
whoops, fixed it in the wrong place
beanbag44 Oct 10, 2026
7d78caf
cleaner AutomatedSafeContext
beanbag44 Oct 10, 2026
c2150e9
cleaner InventoryConfig interface
beanbag44 Oct 10, 2026
30f16e9
improvements
beanbag44 Oct 15, 2026
f2eadcd
cleanup
beanbag44 Oct 15, 2026
3b5a14d
more cleanup
beanbag44 Oct 15, 2026
4442ff5
formatting
beanbag44 Oct 15, 2026
138aa9a
more cleanup
beanbag44 Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
183 changes: 183 additions & 0 deletions src/gametest/kotlin/com/lambda/BuildPreTickGameTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

@file:Suppress("unused")

package com.lambda

import com.lambda.Lambda.LOG
import com.lambda.Lambda.mc
import com.lambda.config.automation.AutomationConfig
import com.lambda.context.Automated
import com.lambda.interaction.construction.blueprint.Blueprint.Companion.toStructure
import com.lambda.interaction.construction.verify.TargetState
import com.lambda.task.RootTask
import com.lambda.task.Task
import com.lambda.task.start
import com.lambda.task.tasks.build
import com.lambda.threading.runSafe
import net.fabricmc.fabric.api.client.gametest.v1.FabricClientGameTest
import net.fabricmc.fabric.api.client.gametest.v1.context.ClientGameTestContext
import net.fabricmc.fabric.api.client.gametest.v1.context.TestServerContext
import net.minecraft.block.Blocks
import net.minecraft.client.gui.screen.world.WorldCreator
import net.minecraft.util.math.BlockPos
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean

/**
* Pins down the [BuildTask][com.lambda.task.tasks.BuildTask] pre-tick hook contract:
*
* 1. The callback fires every tick with a sane status snapshot (remaining work, statically
* known materials, idle collect state).
* 2. A veto (`true`) stalls the whole tick: while vetoed the target block stays untouched.
* 3. Releasing the veto lets the build finish normally.
*/
@Suppress("UnstableApiUsage")
object BuildPreTickGameTest : FabricClientGameTest {
private const val PLAYER = "Steve"
private const val VETO_TICKS = 20
private const val BUILD_TIMEOUT = 20 * 30

private data class Seen(
val pending: Int,
val collecting: Boolean,
val drops: Int,
val pickups: Int
)

private val failures = mutableListOf<String>()

override fun runTest(context: ClientGameTestContext) {
failures.clear()

val singleplayer = context.worldBuilder()
.adjustSettings { it.gameMode = WorldCreator.Mode.SURVIVAL }
.create()

try {
singleplayer.clientWorld.waitForChunksDownload()
runScenario(context, singleplayer.server)
} finally {
runCatching { context.runOnClient<RuntimeException> { RootTask.cancel() } }
runCatching { singleplayer.close() }
}

if (failures.isEmpty()) {
LOG.info("Build pre-tick game test passed")
} else {
LOG.error("Build pre-tick game test failed:")
failures.forEach { LOG.error(" FAIL $it") }
}
check(failures.isEmpty()) {
"${failures.size} build pre-tick expectations failed:\n" + failures.joinToString("\n") { " - $it" }
}
}

private fun runScenario(context: ClientGameTestContext, server: TestServerContext) {
val auto: Automated = AutomationConfig.DEFAULT
val center = BlockPos(0, -60, 0)
val target = BlockPos(1, -60, 0)

prepareWorld(context, server, center)
server.runCommand("/clear $PLAYER")
server.runCommand("/give $PLAYER minecraft:dirt 64")

val seen = CopyOnWriteArrayList<Seen>()
val veto = AtomicBoolean(true)
var error: String? = null
var task: Task<*>? = null
context.runOnClient<RuntimeException> {
runSafe {
task = with(auto) {
target.toStructure(TargetState.Block(Blocks.DIRT))
.build(finishOnDone = true, collectDrops = false)
.onPreTick {
seen += Seen(
it.pendingInteractions,
it.collecting,
it.dropsPending.size,
it.pickups.size
)
veto.get()
}
}.onFailure { error = it.message }.start()
}
}
val build = task
if (build == null) {
fail("could not start the build task")
return
}

// 1 + 2. While vetoed the hook must fire every tick but nothing may build.
context.waitTicks(VETO_TICKS)
if (build.state != Task.State.Running) {
fail("vetoed build should still be running but was ${build.state} ($error)")
return
}
if (seen.isEmpty()) {
fail("pre-tick hook never fired during the veto window")
return
}
val first = seen.first()
if (first.collecting) fail("status should not report collecting with collectDrops=false")
if (first.drops != 0) fail("status should report no pending drops but had ${first.drops}")
if (!isAir(context, target)) {
fail("vetoed build must not place anything")
return
}

// 3. Releasing the veto lets the build finish.
veto.set(false)
try {
context.waitFor({ build.state != Task.State.Running && build.state != Task.State.Paused }, BUILD_TIMEOUT)
} catch (_: Throwable) {
fail("build did not finish within ${BUILD_TIMEOUT} ticks of releasing the veto (state=${build.state}, $error)")
return
}
if (build.state != Task.State.Completed) {
fail("build should have completed but was ${build.state} ($error)")
return
}
val placed = context.computeOnClient<Boolean, RuntimeException> {
mc.world?.getBlockState(target)?.isOf(Blocks.DIRT) ?: false
}
if (!placed) fail("dirt should be placed after the veto was released")
}

private fun isAir(context: ClientGameTestContext, pos: BlockPos) =
context.computeOnClient<Boolean, RuntimeException> {
mc.world?.getBlockState(pos)?.isAir ?: false
}

private fun prepareWorld(context: ClientGameTestContext, server: TestServerContext, center: BlockPos) {
server.runCommand("/gamemode survival $PLAYER")
server.runCommand("/difficulty peaceful")
server.runCommand("/time set noon")
server.runCommand("/weather clear")
server.runCommand("/fill ${center.x - 5} ${center.y - 1} ${center.z - 5} ${center.x + 5} ${center.y - 1} ${center.z + 5} minecraft:stone")
server.runCommand("/fill ${center.x - 5} ${center.y} ${center.z - 5} ${center.x + 5} ${center.y + 4} ${center.z + 5} minecraft:air")
server.runCommand("/tp $PLAYER ${center.x + 0.5} ${center.y} ${center.z + 0.5} 0 0")
context.waitTicks(10)
}

private fun fail(message: String) {
failures += message
LOG.warn("FAIL [build-pretick] $message")
}
}
Loading