jengine-homestead
A homestead’s rules as plain data: a plot to till, plant and build on, crops grown from timestamps (offline included), inventory and crafting, resource nodes that respawn on a clock, and a community project everyone builds.
| Kind | Project package: linked into the builds of the projects that list it |
| Source | packages/jengine-homestead |
| Version | 0.1.0 |
| Entry point | jengine_homestead::HomesteadPackage |
Using it
Section titled “Using it”List it in the top-level "packages" array of project.jengine:
{ "packages": [ { "name": "jengine-homestead", "source": { "type": "path", "path": "../../packages/jengine-homestead" } } ]}Then rebuild the editor viewport for the project (jengine viewport-build projects/<name>, or the
editor’s Rebuild viewport banner). Native and web builds link it on their own.
From packages/jengine-homestead/README.md.
A homestead’s rules as plain data: a plot of ground to till, plant and build on, crops that grow from timestamps (so a field grows while its farmer is away), bags of stacked items, crafting from recipes, resource nodes everyone shares that respawn on a clock, and a community project a whole server builds together.
It was written for Hearthwood (projects/hearthwood), a cozy village MMO
whose server rooms run these rules for every player’s homestead, the shared
woods and the village bridge. Nothing in it knows a server, a scene or a
particular game, so another farming game can use it as it is - Thornwick’s
fields could be Plots and its barley a CropDef; a single-player farm could
keep one Plot in a save.
What is in it
Section titled “What is in it”| Module | Holds |
|---|---|
catalog | the data: ItemDef, CropDef, ObjectDef, RecipeDef, NodeDef in one Catalog, read from JSON (Catalog::from_json) and checked whole - a recipe naming an item that does not exist is refused at load, with what is wrong |
inventory | Inventory: slots of Stacks. Every change is all or nothing - add puts in everything or nothing, take takes everything asked or nothing - so a craft or trade that fails half way cannot eat half its inputs |
growth | GrowthRules, Wet, Planting: how far a crop has grown at any time t, worked out from when it was planted and when its soil was wet. Full speed while wet, dry_rate while dry; wet for water_secs after a watering, for as long as a sprinkler stands next to it, and all of any day it rains (rains_on(day), a hash of the day and a seed - the same weather everywhere) |
plot | Plot: a grid of grass / tilled / path, fixed cells for the game’s own layout, crops by cell, Placed objects with footprints. till, plant, water, harvest, can_place, place, pick_up, state (grass / tilled / watered / path at a moment), solid_cells. Every refusal is a PlotError that says why |
crafting | craft (exactly the inputs, or nothing), start + Job + collect for recipes that take time - the output is due at a timestamp, so it is ready when its maker comes back |
nodes | Node: a tree, rock or bush. hit gives what it gives after its hits and is then gone for everyone until respawn_secs later; refresh says the moment it is back, once |
project | Project: a shared build. contribute takes no more than is wanted and finishes the project exactly once - the gift that fills the last need is told so, every later one is refused - so the moment of completion can be an event without two players both setting it off |
Time is a number the caller passes in. Every rule takes at in seconds
and nothing reads a clock: ask about a crop a week later and the answer is
the same as watching it all week (a test sums a crop’s growth second by
second against one question an hour later). That is what lets a server keep
one clock for its world - wall time in production, a clock a test moves by
hand.
Everything serializes. A whole Plot is one JSON document (Hearthwood
stores one per player), Nodes and Project likewise, and Inventory and
Job live in a player’s saved state.
Using it
Section titled “Using it”As a library - a server room or a game’s own package calls it:
use jengine_homestead::{Catalog, GrowthRules, Plot};
let catalog = Catalog::from_json(include_str!("content.json"))?;let rules = GrowthRules::default();let mut plot = Plot::new(26, 18, fixed_cells);plot.till(8, 12)?;plot.plant(&catalog, 8, 12, "turnip_seeds", now)?;plot.water(8, 12, &rules, now)?;// ... an hour later, after a restart, nobody having ticked anything:let (crop, stage, ripe) = plot.crop_state(&catalog, 8, 12, &rules, now + 3600.0).unwrap();To list it in a project (it changes nothing in a scene - see below):
{ "packages": [ { "name": "jengine-homestead", "source": { "type": "path", "path": "../../packages/jengine-homestead" } }] }Components and script commands
Section titled “Components and script commands”None. The package’s JEnginePackage registers no components, systems or
script commands: Hearthwood’s rules run on its server, and its client draws
what the server sends. It is a package so a project can list it and a
client-side system (a single-player farm, a preview of growth) can be added
later without moving the rules.
cargo test -p jengine-homestead: growth at full and dry speed, growth across
an “offline” gap equal to watching it, rain by the day and the same
everywhere, overlapping waterings counted once, sprinklers watering from when
they were placed, a regrowing crop; tilling, planting, watering and harvesting
with every refusal; placement overlap and footprints off the edge refused;
crafting that takes exactly its inputs and keeps them when the bag is full;
timed jobs finishing offline; nodes gone for everyone and back on the clock;
a project that takes only what it wants and completes once, and survives
being stored. Hearthwood’s own tests (cargo test -p hearthwood-world) play
these rules through real rooms and the real scene.