1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/src/realtime/types.ts
T

235 lines
5.8 KiB
TypeScript

/** JSON-safe contracts for an eventual server-authoritative Tera session. */
export type RealtimeActorKind = "humanoid" | "dog" | "crow";
export type RealtimeVehicleKind = "model-x";
export type RealtimeAircraftKind = "electric-vtail";
export type RealtimeMemberRole = "visitor" | "member" | "admin";
export type RealtimeRevocationReason =
| "signed-out"
| "membership-revoked"
| "session-replaced"
| "protocol-violation"
| "server-shutdown";
export interface CaliforniaTileInterest {
kind: "california-tile";
/** Discrete authored world grid, not web-map tiles. */
x: number;
y: number;
level: number;
}
export interface CityInterest {
kind: "city";
cityId: "bay-area" | "socal";
}
export interface OfficeInterest {
kind: "office";
officeId: string;
}
export interface FloorInterest {
kind: "floor";
officeId: string;
floorId: string;
}
export interface RoomInterest {
kind: "room";
officeId: string;
floorId: string;
roomId: string;
}
export type InterestCell =
| CaliforniaTileInterest
| CityInterest
| OfficeInterest
| FloorInterest
| RoomInterest;
/** Monotonic within one server epoch and bounded to an unsigned 32-bit integer. */
export type RealtimeSequence = number;
/** Unix epoch milliseconds, serialized as a safe integer. */
export type RealtimeTimestamp = number;
export interface GeographicPose {
space: "geographic";
lat: number;
lng: number;
altitudeM: number;
headingDeg: number;
pitchDeg: number;
}
export interface LocalPose {
space: "local";
/** The smallest spatial cell whose coordinates define this pose. */
cell: CityInterest | OfficeInterest | FloorInterest | RoomInterest;
xM: number;
yM: number;
zM: number;
headingDeg: number;
pitchDeg: number;
}
export type SpatialPose = GeographicPose | LocalPose;
export interface PoseVelocity {
xMps: number;
yMps: number;
zMps: number;
yawDegPerSec: number;
}
interface EntityPoseSnapshotBase {
sequence: RealtimeSequence;
timestampMs: RealtimeTimestamp;
pose: SpatialPose;
velocity: PoseVelocity;
}
export interface ActorPoseSnapshot extends EntityPoseSnapshotBase {
entity: "actor";
actorId: string;
kind: RealtimeActorKind;
}
export interface VehiclePoseSnapshot extends EntityPoseSnapshotBase {
entity: "vehicle";
vehicleId: string;
kind: RealtimeVehicleKind;
driverActorId: string | null;
steering: number;
wheelRadians: number;
}
/** Authoritative state needed to reproduce a piloted aircraft on another client. */
export interface AircraftPoseSnapshot extends EntityPoseSnapshotBase {
entity: "aircraft";
aircraftId: string;
kind: RealtimeAircraftKind;
pilotActorId: string;
rollDeg: number;
throttle: number;
rollInput: number;
pitchInput: number;
yawInput: number;
fanRadians: number;
}
export type EntityPoseSnapshot = ActorPoseSnapshot | VehiclePoseSnapshot | AircraftPoseSnapshot;
export interface JoinRequest {
type: "join-request";
protocolVersion: 1;
requestId: string;
actorId: string;
resumeToken: string | null;
lastReceivedSequence: RealtimeSequence | null;
interests: readonly InterestCell[];
}
export interface JoinGrant {
type: "join-grant";
protocolVersion: 1;
requestId: string;
sessionId: string;
actorId: string;
role: RealtimeMemberRole;
serverEpoch: string;
serverTimeMs: RealtimeTimestamp;
nextSequence: RealtimeSequence;
resumeToken: string;
interests: readonly InterestCell[];
initial: readonly EntityPoseSnapshot[];
}
export interface ResumeRequest {
type: "resume-request";
protocolVersion: 1;
requestId: string;
sessionId: string;
resumeToken: string;
serverEpoch: string;
lastReceivedSequence: RealtimeSequence;
interests: readonly InterestCell[];
}
export interface ResumeGrant {
type: "resume-grant";
protocolVersion: 1;
requestId: string;
sessionId: string;
serverEpoch: string;
serverTimeMs: RealtimeTimestamp;
nextSequence: RealtimeSequence;
resumeToken: string;
/** True means queued deltas bridge the requested sequence without a gap. */
continuous: boolean;
snapshot: readonly EntityPoseSnapshot[];
}
export interface PoseDelta {
type: "pose-delta";
protocolVersion: 1;
serverEpoch: string;
sequence: RealtimeSequence;
timestampMs: RealtimeTimestamp;
updates: readonly EntityPoseSnapshot[];
removedEntityIds: readonly string[];
}
export interface MembershipRevoked {
type: "membership-revoked";
protocolVersion: 1;
serverEpoch: string;
sequence: RealtimeSequence;
timestampMs: RealtimeTimestamp;
sessionId: string;
actorId: string;
reason: RealtimeRevocationReason;
reconnectAllowed: boolean;
}
export type ClientRealtimeMessage = JoinRequest | ResumeRequest;
export type ServerRealtimeMessage = JoinGrant | ResumeGrant | PoseDelta | MembershipRevoked;
export type RealtimeValidationResult<T> =
| { ok: true; value: T }
| { ok: false; error: string };
export interface MotionLimits {
maximumHorizontalSpeedMps: number;
maximumVerticalSpeedMps: number;
maximumTurnRateDegPerSec: number;
/** Allows quantization and packet jitter without permitting teleportation. */
positionSlackM: number;
minimumDeltaSeconds?: number;
maximumDeltaSeconds?: number;
}
export type MotionValidationResult =
| { ok: true; horizontalSpeedMps: number; verticalSpeedMps: number; turnRateDegPerSec: number }
| {
ok: false;
reason: "identity-mismatch" | "space-mismatch" | "time-invalid" | "speed-exceeded";
};
export interface InterpolationOptions {
interpolationDelayMs?: number;
maximumExtrapolationMs?: number;
capacity?: number;
}
export type BufferedSampleMode = "exact" | "interpolated" | "extrapolated" | "held";
export interface BufferedPoseSample {
mode: BufferedSampleMode;
pose: SpatialPose;
velocity: PoseVelocity;
timestampMs: RealtimeTimestamp;
sourceSequence: RealtimeSequence;
}