feat(protocol2): presence stream — online population + region transitions (Part B ph.2)
Overlay BridgePresence (new): - presence.online sweep over online PlayerMobiles: total plus per-facet and per-region counts, emitted only when the population changes. - region.enter real-time from EventSink.OnEnterRegion (player-filtered), the cheap location signal PLAN.md prefers over Movement. - PresenceSweepSeconds (30s); wired into [bridge reload|sweepnow|status. Sidecar: - GET /online serves the latest presence.online snapshot from the event store (survives restart); population time series via /history?kind=presence.online. Docs: INTEGRATION.md presence events + /online endpoint; PROTOCOL_2 ph.2 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:
@@ -77,6 +77,7 @@ pub async fn serve(addr: &str, state: AppState) -> anyhow::Result<()> {
|
||||
// and survive an outage with the last-known snapshot (docs/PROTOCOL_2.md §12.2).
|
||||
.route("/guilds", get(guilds))
|
||||
.route("/governors", get(governors))
|
||||
.route("/online", get(online))
|
||||
.route_layer(middleware::from_fn_with_state(state.clone(), gate));
|
||||
|
||||
let app = Router::new()
|
||||
@@ -729,6 +730,26 @@ async fn governors(State(st): State<AppState>) -> impl IntoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// The current online population: total plus per-facet and per-region counts. This is the most
|
||||
/// recent `presence.online` snapshot from the event store (so it survives a sidecar restart); the
|
||||
/// live `presence.online` stream keeps it current, and `GET /history?kind=presence.online` gives the
|
||||
/// population time series. Returns `count: 0` if the shard has not reported one yet.
|
||||
async fn online(State(st): State<AppState>) -> impl IntoResponse {
|
||||
match st.store.recent(Some("presence.online"), 1).await {
|
||||
Ok(mut events) => match events.pop() {
|
||||
Some(latest) => (StatusCode::OK, Json(latest)),
|
||||
None => (
|
||||
StatusCode::OK,
|
||||
Json(json!({"kind": "presence.online", "count": 0, "byFacet": {}, "byRegion": {}})),
|
||||
),
|
||||
},
|
||||
Err(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({"error": e.to_string()})),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// ---- websocket ----
|
||||
|
||||
async fn ws_upgrade(ws: WebSocketUpgrade, State(state): State<AppState>) -> impl IntoResponse {
|
||||
|
||||
Reference in New Issue
Block a user