refactor: Serializers include available location attributes

This commit is contained in:
2026-07-09 03:04:48 +05:30
parent fadea41e6f
commit 6cf099821b
4 changed files with 102 additions and 0 deletions

View File

@@ -72,6 +72,7 @@ export class ScenarioLoader {
}
// Save location record linked to the world instance
world.addLocation(location);
this.coreRepo.saveLocation(location, world.id);
}
}
@@ -102,6 +103,7 @@ export class ScenarioLoader {
}
// Save entity record linked to the world instance
world.addEntity(entity);
this.coreRepo.saveEntity(entity, world.id);
// Seed initial memory buffer history

View File

@@ -84,6 +84,21 @@ describe("Talking Room Demo Scenario Test (Tier 1)", () => {
expect(beta!.locationId).toBe("white-room");
expect(beta!.aliases.get(alphaId)).toBe("the person in the Alpha jumpsuit");
// Verify subjective world state serializes the location attributes (epistemic inclusion)
const { serializeSubjectiveWorldState } = await import("@omnia/core");
const subjectiveState = serializeSubjectiveWorldState(world!, alphaId);
expect(subjectiveState).toContain("You are at location: white-room");
expect(subjectiveState).toContain("Location attributes:");
expect(subjectiveState).toContain("description: A pristine, featureless room");
expect(subjectiveState).toContain("lighting: Bright, uniform illumination");
// Verify objective world state serializes locations (physics awareness)
const { serializeObjectiveWorldState } = await import("@omnia/core");
const objectiveState = serializeObjectiveWorldState(world!);
expect(objectiveState).toContain("Locations:");
expect(objectiveState).toContain("- Location [ID: white-room]:");
expect(objectiveState).toContain("description: A pristine, featureless room");
// 7. Assert initial pre-seeded memories
const alphaMemories = bufferRepo.listForOwner(alphaId);
expect(alphaMemories).toHaveLength(1);

View File

@@ -4,6 +4,8 @@ import { AttributableObject, AttributeVisibility } from "./attribute.js";
import { Entity } from "./entity.js";
import { WorldState } from "./world.js";
class GenericObject extends AttributableObject {}
export class SQLiteRepository {
private db: Database.Database;
@@ -195,6 +197,9 @@ export class SQLiteRepository {
for (const entity of worldState.entities.values()) {
this.saveEntity(entity, worldState.id);
}
for (const location of worldState.locations.values()) {
this.saveLocation(location, worldState.id);
}
});
saveWorldTx();
}
@@ -288,6 +293,25 @@ export class SQLiteRepository {
const worldState = new WorldState(id, startTime);
this.reconstituteAttributes(worldState);
// Reconstitute all locations belonging to this world
const locationRows = this.db
.prepare(
`
SELECT id, location_id, connections_json FROM objects WHERE type = 'location' AND world_id = ?
`,
)
.all(id) as { id: string; location_id: string | null; connections_json: string | null }[];
for (const row of locationRows) {
const loc = new GenericObject(row.id);
(loc as { parentId?: string | null }).parentId = row.location_id;
if (row.connections_json) {
(loc as { connections?: unknown[] }).connections = JSON.parse(row.connections_json);
}
this.reconstituteAttributes(loc);
worldState.addLocation(loc);
}
// Reconstitute all entities belonging to this world
const entityRows = this.db
.prepare(

View File

@@ -8,6 +8,7 @@ export class WorldState extends AttributableObject {
* Universe's current state (distinct from how it started)
*/
readonly entities: Map<string, Entity> = new Map();
readonly locations: Map<string, AttributableObject> = new Map();
readonly clock: WorldClock;
constructor(id?: string, startTime?: Date) {
@@ -27,6 +28,19 @@ export class WorldState extends AttributableObject {
getEntity(id: string): Entity | undefined {
return this.entities.get(id);
}
addLocation(location: AttributableObject): void {
if (this.locations.has(location.id)) {
throw new Error(
`Location with ID ${location.id} already exists in the world`,
);
}
this.locations.set(location.id, location);
}
getLocation(id: string): AttributableObject | undefined {
return this.locations.get(id);
}
}
/**
@@ -43,6 +57,45 @@ export function serializeObjectiveWorldState(worldState: WorldState): string {
lines.push(worldAttrsStr.split("\n").map(l => " " + l).join("\n"));
}
// Serialize locations and their attributes/portals
lines.push("Locations:");
if (worldState.locations.size > 0) {
for (const loc of worldState.locations.values()) {
lines.push(` - Location [ID: ${loc.id}]:`);
const parentId = (loc as { parentId?: string | null }).parentId;
if (parentId) {
lines.push(` * Parent Location ID: ${parentId}`);
}
if (loc.attributes.size > 0) {
const locAttrsStr = serializeAttributes(Array.from(loc.attributes.values()));
lines.push(locAttrsStr.split("\n").map(l => " " + l).join("\n"));
} else {
lines.push(" * (No attributes)");
}
const connections = (loc as { connections?: unknown[] }).connections as {
targetId: string;
portalName?: string;
portalStateDescriptor?: string;
visionProp: number;
soundProp: number;
bidirectional: boolean;
}[] | undefined;
if (connections && connections.length > 0) {
lines.push(" * Connections:");
for (const conn of connections) {
const portalStr = conn.portalName ? ` via ${conn.portalName} (${conn.portalStateDescriptor || "normal"})` : "";
lines.push(` -> To: ${conn.targetId}${portalStr} (Vision: ${conn.visionProp}, Sound: ${conn.soundProp})`);
}
}
}
} else {
lines.push(" (No locations)");
}
// Serialize entities and their attributes
lines.push("Entities:");
if (worldState.entities.size > 0) {
@@ -129,6 +182,14 @@ export function serializeSubjectiveWorldState(
lines.push("What you perceive around you:");
if (viewer.locationId) {
lines.push(` You are at location: ${viewer.locationId}`);
const location = worldState.getLocation(viewer.locationId);
if (location) {
const locVisible = location.getVisibleAttributesFor(viewerId);
if (locVisible.length > 0) {
lines.push(" Location attributes:");
lines.push(serializeVisibleAttributes(locVisible).split("\n").map((l) => " " + l).join("\n"));
}
}
} else {
lines.push(" You are not located anywhere in particular.");
}