.-') _ (`-.) _ .-') .-') _
( OO ). ( (OO )( \( -O ) ( OO) )
(_)---\_)_.` \ ,------. .-'),-----. ,--. ,--. / '._
/ _ |(__...--'' | /`. '( OO' .-. ' | | | | |'--...__)
\ :``. | / | | | / | |/ | | | | | | | .-')'--. .--'
'..`''.)| |_.' | | |_.' |\_) | |\| | | |_|( OO ) | |
.-._) \| .___.' | . '.' \ | | | | | | | `-' / | |
\ /| | | |\ \ `' '-' '(' '-'(_.-' | |
`-----' `--' `--' '--' `-----' `-----' `--'
a minecraft wheat farm daemon
node index.js
plant. harvest. restock. repeat.
sprout is a mineflayer bot that operates a wheat farm autonomously. connects to a server, scans the farmland, and runs a three-phase loop: plant empty plots, wait for growth, harvest and replant. chests are discovered via item-frame labels — no hardcoded positions.
npm install
node index.js| step | detail |
|---|---|
| 1. | set the server address from the terminal menu |
| 2. | place item frames on chests: wheat item → wheat chest, seeds item → seed chest |
| 3. | select start |
note: on Paper servers (1.20.4+), chest interaction requires
/op Sproutdue to a server-side packet validation bug (mineflayer#3459). vanilla servers work without op.
flowchart LR
subgraph Bot
TUI[terminal menu<br/>config + status]
F[FarmTick loop<br/>500ms–2000ms]
M[manageInventory<br/>wheat deposit<br/>seed restock]
S[scanFarm<br/>33x33 bounding box<br/>3s cooldown]
C[scanChests<br/>item-frame labels]
end
subgraph Server
MC[Minecraft server<br/>1.20.6]
end
subgraph Dashboard
W[HTTP :8080<br/>SSE events]
H[HTML canvas<br/>map + stats]
end
TUI --> F
F --> S & M
C --> M
MC <--> F
W <--> F
H --> W
stateDiagram-v2
state "FarmTick" as T {
[*] --> Phase1 : empty plots exist
state Phase1 {
direction LR
p1[sort by distance] --> p2[navigate to plot]
p2 --> p3[place seed<br/>raw block_place packet]
p3 --> p4[mark stage=0 locally]
p4 --> p1 : more plots
}
Phase1 --> Phase2 : all plots occupied
state Phase2 {
direction LR
w1[manageInventory<br/>wheat deposit + seed restock] --> w2[wait 2000ms]
}
Phase2 --> Phase3 : mature crops detected
state Phase3 {
direction LR
h1[sort mature by distance] --> h2[navigate reach=4]
h2 --> h3[break crop<br/>raw block_dig instant]
h3 --> h4[place seed<br/>raw block_place]
h4 --> h5[mark stage=0 locally]
h5 --> h1 : more mature
}
Phase3 --> Phase1 : batch complete
Phase2 --> Phase2 : rescan after 2s
}
scanFarm() performs an O(n³) bounding-box scan from a centroid (farmCenter) that is recomputed after every scan. initial position is the bot's location at start; subsequent scans recenter on the mean x/z of all discovered farmland blocks. this keeps the farm visible even when the bot walks to distant chests.
// centroid self-corrects as new farmland is discovered
const avgX = plots.reduce((s, p) => s + p.pos.x, 0) / plots.length
const avgZ = plots.reduce((s, p) => s + p.pos.z, 0) / plots.length
farmCenter = new Vec3(Math.floor(avgX), center.y, Math.floor(avgZ))scanChests() iterates all loaded item_frame / glow_item_frame entities, reads the item in slot 8 (1.20.6+ slot format via itemCount), resolves the attached chest block via 6-direction neighbor search, and filters type === 'right' to deduplicate double-chest halves. chests are stored as deduplicated arrays (seedChests[], wheatChests[]).
crops are instant-break (hardness 0). sprout bypasses mineflayer's bot.dig() — which performs a lookAt, sends block_dig status=0, waits for an elapsed timer, then sends status=2 — and instead sends both packets back-to-back with a swingArm(). same for seed placement (block_place written directly without waitForBlockUpdate). this eliminates ~100ms of per-call latency and matches the server's natural tick processing.
bot._client.write('block_dig', { status: 0, location: crop.position, face: 1 })
bot._client.write('block_dig', { status: 2, location: crop.position, face: 1 })
bot.swingArm()inventory size targets are derived from the plot count. the bot maintains exactly plots.length seeds in inventory — any surplus is deposited to the seed chest on the next chest trip. wheat is deposited to the wheat chest when any stack reaches 64. chest contents for the dashboard are read from chest.containerItems() during actual chest operations (no extra chest opens for UI).
an HTTP server on :8080 pushes SSE events at 60fps. the payload includes plot state (x, z, stage), chest positions, bot location, inventory summary, and cumulative stats. the browser renders a canvas 2D map with colored plot squares (brown→green→gold for stage 0→7), diamond markers for chests, and a dot for the bot. zero framework dependencies — vanilla canvas API + EventSource.
open http://localhost:8080 while sprout runs.
- mineflayer — minecraft protocol client
- mineflayer-pathfinder — A* navigation
- minecraft-data, prismarine-block, vec3 — transitive, resolved by mineflayer
zero runtime deps beyond the mineflayer stack.
ISC