feat: add authenticated realtime presence
This commit is contained in:
@@ -99,6 +99,13 @@ import {
|
||||
type RobotSpec,
|
||||
type RobotView,
|
||||
} from "./robots.ts";
|
||||
import {
|
||||
createScenePeers,
|
||||
type ScenePeersOptions,
|
||||
} from "../realtime/scenePeers.ts";
|
||||
import type { EntityPoseSnapshot } from "../realtime/types.ts";
|
||||
|
||||
export type OfficeRealtimePeersOptions = Omit<ScenePeersOptions, "project" | "groundAt">;
|
||||
|
||||
/** Shared empty, so a pack with no robots does not allocate one per call. */
|
||||
const NO_ROBOTS: readonly RobotView[] = [];
|
||||
@@ -222,6 +229,8 @@ export interface OfficeSceneOptions {
|
||||
robots?: readonly RobotSpec[];
|
||||
/** Optional local walk actor. Constructed inactive unless `walker.active` says otherwise. */
|
||||
walker?: OfficeWalkerOptions;
|
||||
/** Full-depth-only authoritative remote actors/vehicles in local metre coordinates. */
|
||||
realtimePeers?: OfficeRealtimePeersOptions;
|
||||
/** Defaults to false — the lid comes off, because that is the whole view. */
|
||||
showCeilings?: boolean;
|
||||
/** Fade the walls you are looking through. Defaults to true. */
|
||||
@@ -306,6 +315,10 @@ export interface OfficeScene extends StageScene {
|
||||
* The plan panel and the ceiling lights both consume it that way.
|
||||
*/
|
||||
robots(): readonly RobotView[];
|
||||
upsertRemoteSnapshot(snapshot: EntityPoseSnapshot): boolean;
|
||||
removeRemoteEntity(id: string): boolean;
|
||||
clearRemoteEntities(): void;
|
||||
remoteEntityCount(): number;
|
||||
}
|
||||
|
||||
export function createOfficeScene(office: Office, options: OfficeSceneOptions): OfficeScene {
|
||||
@@ -509,6 +522,18 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
|
||||
: null;
|
||||
const officeWalker = options.walker ? createOfficeWalker(plan, options.walker) : null;
|
||||
if (officeWalker) scene.add(officeWalker.root);
|
||||
// Live occupancy is excluded from the public-depth build rather than hidden.
|
||||
// Geographic callbacks are required by the generic adapter but harmless here:
|
||||
// an office subscription sends validated local poses in metre coordinates.
|
||||
const realtimePeers = depth === "full" && options.realtimePeers
|
||||
? createScenePeers({
|
||||
...options.realtimePeers,
|
||||
project: () => [0, 0],
|
||||
groundAt: () => 0,
|
||||
localSceneUnitsPerMetre: options.realtimePeers.localSceneUnitsPerMetre ?? 1,
|
||||
})
|
||||
: null;
|
||||
if (realtimePeers) scene.add(realtimePeers.root);
|
||||
if (robots) {
|
||||
scene.add(robots.group);
|
||||
/**
|
||||
@@ -830,6 +855,18 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
|
||||
setWalkers(walkers) {
|
||||
luminaires.setWalkers(walkers);
|
||||
},
|
||||
upsertRemoteSnapshot: (snapshot) => {
|
||||
// The generic peer adapter can also project geographic poses, but an
|
||||
// office must never render a stale city stream or another office's local
|
||||
// coordinates at its origin during an interest handoff.
|
||||
if (snapshot.pose.space !== "local") return false;
|
||||
const cell = snapshot.pose.cell;
|
||||
if (!("officeId" in cell) || cell.officeId !== office.id) return false;
|
||||
return realtimePeers?.upsert(snapshot) ?? false;
|
||||
},
|
||||
removeRemoteEntity: (id) => realtimePeers?.remove(id) ?? false,
|
||||
clearRemoteEntities: () => { realtimePeers?.clear(); },
|
||||
remoteEntityCount: () => realtimePeers?.count() ?? 0,
|
||||
// Stepping back out to the city should retire the hover with it, or the
|
||||
// detail card for whoever the pointer was over survives the journey.
|
||||
onExit: () => kit.resetPick(),
|
||||
@@ -840,6 +877,7 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
|
||||
kit.tick(dt);
|
||||
officeWalker?.tick(dt);
|
||||
if (walking && officeWalker) kit.setPose(officeWalker.followPose());
|
||||
realtimePeers?.tick(Date.now());
|
||||
updateOcclusion();
|
||||
// Robots first: the lights above them should respond to where they are
|
||||
// *now*, not to where they were last frame.
|
||||
@@ -849,6 +887,7 @@ export function createOfficeScene(office: Office, options: OfficeSceneOptions):
|
||||
dispose() {
|
||||
mediaSurfaces.dispose();
|
||||
officeWalker?.dispose();
|
||||
realtimePeers?.dispose();
|
||||
robots?.dispose();
|
||||
luminaires.dispose();
|
||||
if (horizonPlane) {
|
||||
|
||||
Reference in New Issue
Block a user