feat(protocol2): house registry board (Part B ph.3)

Overlay BridgeHousing (new): a diff sweep over BaseHouse.AllHouses ->
house.update / house.remove (owner, region, location, decay level, co-owners,
friends, placement price), complementing the existing house.decay transition
feed. HousingSweepSeconds (300s); wired into [bridge reload|sweepnow|status.
Stock ServUO has no "for sale" flag, so this is an owner->houses registry;
price is the placement value, not a listing.

Sidecar: houses board table with upsert/delete/all; main routes house.update/
remove into it; GET /houses served from the store.

Docs: INTEGRATION.md house.* events + /houses endpoint; PROTOCOL_2 ph.3 built.

Verified: sidecar cargo check clean; overlay compiles in the full ServUO
Scripts tree (0 errors, 0 warnings). Live run pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 08:05:47 -05:00
parent d47170581d
commit b858d526b8
7 changed files with 274 additions and 1 deletions

View File

@@ -246,6 +246,40 @@ impl Store {
.await?;
Ok(parse_json_column(rows))
}
// ---- house registry (Protocol 2.0) ----
/// Upserts one house's latest state, keyed by serial. Fed from `house.update`.
pub async fn upsert_house(&self, serial: &str, name: Option<&str>, json: &str, t: i64) -> anyhow::Result<()> {
sqlx::query(
"INSERT INTO houses (serial, name, json, updated_t) VALUES (?, ?, ?, ?)
ON CONFLICT(serial) DO UPDATE SET name = excluded.name, json = excluded.json, updated_t = excluded.updated_t",
)
.bind(serial)
.bind(name)
.bind(json)
.bind(t)
.execute(&self.pool)
.await?;
Ok(())
}
/// Drops one house from the registry. Fed from `house.remove` (demolished / traded away).
pub async fn delete_house(&self, serial: &str) -> anyhow::Result<()> {
sqlx::query("DELETE FROM houses WHERE serial = ?")
.bind(serial)
.execute(&self.pool)
.await?;
Ok(())
}
/// The full house registry: every house's latest snapshot, ordered by name then serial.
pub async fn houses_all(&self) -> anyhow::Result<Vec<Value>> {
let rows = sqlx::query("SELECT json FROM houses ORDER BY name, serial")
.fetch_all(&self.pool)
.await?;
Ok(parse_json_column(rows))
}
}
fn parse_json_column(rows: Vec<sqlx::sqlite::SqliteRow>) -> Vec<Value> {
@@ -297,4 +331,11 @@ CREATE TABLE IF NOT EXISTS governors (
json TEXT NOT NULL,
updated_t INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS houses (
serial TEXT PRIMARY KEY,
name TEXT,
json TEXT NOT NULL,
updated_t INTEGER NOT NULL
);
"#;