Player Homesteads, Housing, And Property Auctions
What This System Is
Housing is authored once and instantiated many times. assets/gameplay/housing_catalog.json defines reusable areas, plot templates, auction policy, and placeable furnishings. The starter Lantern Meadows area uses assets/scenes/lantern_meadows.scene and has no authored instance cap, so the server may add districts as ownership demand grows without copying the scene or catalog content.
Rules And Invariants
A property is always identified by the full HousingPropertyKey:
area id + district instance id + plot id
Scene IDs and plot IDs alone are not property identities. Different instances reuse the same authored scene and plot definitions while retaining separate owners, auctions, residents, and decoration layouts. ensure_housing_district_capacity returns an existing district below the area's target occupancy or initializes the next instance. max_instances = 0 means intentionally unbounded expansion; a positive value provides a hard operational cap.
Realm state belongs in housing_district_state, keyed by (realm_id, area_id, instance_id). Its JSON payload and mirrored revision are updated with optimistic concurrency, preventing two game-server processes from silently overwriting the same district. Character gameplay state stores only owned-property and active-bid references; shared ownership and auction state must never be embedded in a character row.
An active housing visit adds the full property key to the character's persistent travel state. Realtime replication derives a logical scope from that address, so players in different plots or district instances cannot see each other even when they reuse the same hosted scene and coordinates. The scope is loaded during cross-server handoff before the destination session becomes visible.
Auction transaction boundary
Auctions enforce a minimum bid, minimum raise, ownership limit, closing time, and optional anti-sniping extensions. Bid preparation calculates only the incremental escrow charge when a bidder raises an existing bid. The game-server housing adapter locks the district, derives the balance from authoritative currency storage, consumes that currency, and revision-checks the district update in one database transaction. Replayed request IDs are idempotent.
Settlement deterministically selects the highest bid, using placement time and player ID as stable tie breakers. The winner becomes the owner, losing escrow is returned, and the auction is removed. The adapter refunds losing character UUIDs and commits the new owner under the same row lock and SQL transaction. This two-phase plan/commit split lets the domain remain deterministic while the server owns locks, balances, retries, and audit records.
Browsing a district also advances expired auctions. Auctions with bids settle and refund under the transaction above; auctions without bids reopen with a fresh closing window. Area allocation is protected by a realm-and-area advisory lock, so concurrent game-server processes cannot create the same next district.
Bid, outbid, winner, and escrow-refund messages are inserted into the persistent player inbox inside the same SQL transaction as the housing mutation. Stable dedupe keys make command replay safe, while typed housing actions retain the full area, district instance, and plot address for one-click return.
Ownership and build mode
Owners may grant co-owner, decorator, resident, or visitor roles. Only owners, co-owners, and decorators can mutate placement state. Place, move, rotate, and store commands use clone-then-commit behavior and require the expected district revision. Validation checks property-local build bounds, rotated furnishing footprints, same-layer overlap, and per-property furnishing limits before changing state. Furnishings may reference inventory items, gameplay assemblies, and persistent world-object definitions.
Owners also select a revision-checked visitor policy: private, residents, party, or public. Visit permission is resolved by the server from current ownership, resident records, and live party membership. Directory projections expose whether the viewer may enter and the current local visitor count, but never let the client authorize itself.
The server catalog loader treats housing as required content and validates those cross-catalog references at startup. Character projections expose only the district and plot information needed by the viewer; the authoritative state remains server-side.
Player Flow
Open the World Map and choose Housing. The housing surface reuses the journal layout and provides:
- Area and district browsing, including the authored scene and entry route in the replicated area projection.
- Plot auction status, adjustable revision-checked bids, leading-bid feedback, and current ownership.
- Build mode for owners, co-owners, and decorators with furnishing selection, grid positioning, rotation, placement, and storage.
- Visit or Go Home controls for permitted properties, an owner access-policy control, live presence counts, and a Return action while visiting.
- Immediate server feedback. Accepted commands publish the new district revision; rejected stale, overlapping, out-of-bounds, unauthorized, or inventory-invalid commands leave the projection unchanged and surface the authority message.
The UI only keeps selection and preview coordinates locally. It sends typed HousingCommand requests through the normal gameplay-action channel, and ownership, escrow, inventory, permissions, and placement state change only inside the server transaction.
Visit and return commands use the ordinary authoritative scene-transfer machinery. The first visit captures the exact source zone, scene, entry point, and tile. Returning clears the housing scope and restores that exact tile; cross-instance transfers persist the new scope before handoff and roll it back if preparation fails.
How Authors Use It
Open a housing scene and expand Scene → Detailed Scene Tools → Scene Lighting → Housing Area & District Preview. The preview reports plots per instance, capacity policy, auction timings, property-local build bounds, home/farming hooks, and available furnishing definitions. To create another neighborhood style, add one area and one scene; do not duplicate it for each live district.
Validation And Debugging
When changing the catalog:
- Keep area, plot, and furnishing IDs stable after shipping.
- Treat
target_occupancy_percentas the expansion threshold and use a positivemax_instancesonly when operations need a hard ceiling. - Keep placement coordinates local to a plot so layouts survive district routing changes.
- Run the world-domain housing tests and game-server catalog/schema checks before publishing.
Transport code must continue wrapping the existing bid, settlement, district projection, and placement transactions rather than duplicating their rules in packet handlers.
Examples
Lantern Meadows authors four plot templates once. District instance 0 can own plot.a through plot.d; when it reaches the configured 90 percent occupancy target, the realm initializes instance 1 from the same scene and plot definitions. area.lantern_meadows + 1 + plot.a is therefore a different property from area.lantern_meadows + 0 + plot.a, even though both use the same art and build bounds.
A player with a 12,000-gold escrowed bid who raises to 13,000 is charged only another 1,000. A bid within the final five minutes extends the closing time until the configured extension limit is reached.