1 farming
forgejo-actions edited this page 2026-07-06 16:45:10 +00:00

Farming

What This System Is

Farming connects generated crop item families, authored scene fields, runtime economy checks, and server-owned tile state. Authors configure crop source records and farming_fields; the game server owns planting, watering, harvesting, growth, persistence, and replicated FarmingTileStateWire updates.

How Authors Use It

Authors usually touch two source areas:

  • assets/gameplay/game_data_item_families.json for generated crop families and crop sheet assets.
  • Scene farming_fields for where crops can be planted.

The farming_crops item-family generator creates four item records per crop plus a crop catalog row:

  • produce item: farming.crop.<crop_id>
  • seed bag item: farming.seed_bag.<crop_id>
  • planted seed item: farming.seeds.<crop_id>
  • crop sign item: farming.sign.<crop_id>
  • crop catalog id: crops.<crop_id>

Each generated crop catalog row points back to the crop sheet asset, seed item, planted seed item, produce item, sign item, and yield rule. The generated game-data aggregate records provenance back to the item-family manifest so authoring and validation can jump to the source family entry instead of the aggregate.

Authors use the farming editor tab and scene farming surfaces to inspect or place fields. A field records its id, display name, layer index, rectangular bounds, default soil, irrigation rule, and allowed_crop_ids. An empty allowed_crop_ids list means any catalog crop can be planted there; a non-empty list is an allow-list of crop catalog ids such as crops.barley.

Rules And Invariants

Crop IDs must remain stable once fields, generated item IDs, inventory items, or server flows reference them. Changing barley changes the generated item IDs and crop catalog ID, so treat crop ID renames like item ID migrations.

The coupling is:

  • item-family crop entry owns the crop id, label, and source art;
  • generated crop catalog row owns seed/planted-seed/produce/sign references and yield metadata;
  • scene field owns where the crop may be planted;
  • server authority checks the field, allowed crop id, tile coordinate, existing tile state, and crop economy metadata.

Authors should edit crop family entries and scene fields, not persisted tile state. Runtime tile state is keyed by realm, zone, scene, field, layer, tile x, and tile y. The server revision-checks writes so stale updates fail instead of overwriting newer tile state.

Server actions use FarmingActionWire:

  • Till: sets the tile to tilled.
  • Plant: requires a crop id allowed by the field, sets planted, and consumes one seed item.
  • Water: marks the tile watered for the day.
  • Harvest: requires a mature crop and grants produce according to crop economy metadata.
  • Clear: resets the tile to empty.

Growth ticks use FarmingGrowthTickInput and crop growth rules. Rain, manual watering, or irrigated tiles can advance crop growth; growth returns FarmingGrowthTickResult with deltas and audit records.

Runtime Replication

FarmingTileStateWire carries the authoritative crop id, stage, watering counters, last growth tick id, and revision. Every accepted farming action or growth tick produces FarmingTileDelta values. Deltas are sent through the server_farming_tile_deltas realtime stream message and are also included in realtime bootstrap when a zone has persisted farming state.

Client reducers ignore stale farming revisions, so if a preview looks out of date, check whether the server emitted a newer revision and whether the client received ServerFarmingTileDeltas.

Validation And Debugging

Use farming validation and scene validation when a crop family entry, crop catalog row, seed item, planted seed item, produce item, optional sign item, field definition, or allowed crop id changes. Validation reports missing crop item references from the game-data snapshot and invalid field references from scene validation.

After adding or changing a crop:

cargo run -p aether --bin aether-generated-artifact -- --write game_data_catalog_aggregate
cargo run --bin aether-bake -- --fail-on-diagnostics

For runtime mismatches, compare these in order:

  1. The crop entry in game_data_item_families.json.
  2. The generated item IDs and crops.<crop_id> row in the aggregate.
  3. The field's allowed_crop_ids.
  4. The server action request and economy plan.
  5. Persisted tile state revision in scene_farming_tile_state.
  6. Realtime FarmingTileDelta packets and client reducer state.

Examples

Adding a new crop named sunflower means adding a crop_id and source sheet to the farming_crops family. The generator creates farming.seed_bag.sunflower, farming.seeds.sunflower, farming.crop.sunflower, farming.sign.sunflower, and crops.sunflower. A field that should only allow sunflowers lists crops.sunflower; a general field leaves allowed_crop_ids empty.

If planting is rejected, first check that the request crop id is crops.sunflower and that the field allow-list includes that exact id. If harvest is rejected, check that the tile stage is mature and that the crop economy metadata has a produce item and quantity.