feat: Implemented Delta generator for time advancement

This commit is contained in:
2026-07-06 20:11:59 +05:30
parent 6d8c3cee4f
commit de0510636d
9 changed files with 243 additions and 18 deletions

View File

@@ -1,7 +1,22 @@
import { AttributableObject } from "./attribute.js";
export class Entity extends AttributableObject {
constructor(id?: string) {
locationId: string | null = null;
constructor(id?: string, locationId?: string | null) {
super(id);
this.locationId = locationId ?? null;
}
override serialize(): string {
const lines: string[] = [];
if (this.locationId) {
lines.push(`* Location ID: ${this.locationId}`);
}
const selfSerialized = super.serialize();
if (selfSerialized) {
lines.push(selfSerialized);
}
return lines.join("\n");
}
}

View File

@@ -21,6 +21,7 @@ export class SQLiteRepository {
type TEXT NOT NULL,
world_id TEXT,
clock_iso TEXT,
location_id TEXT,
FOREIGN KEY (world_id) REFERENCES objects(id) ON DELETE CASCADE
);
@@ -48,6 +49,13 @@ export class SQLiteRepository {
} catch {
// Column already exists, ignore error
}
// Safely add location_id column if it does not exist in an existing database
try {
this.db.exec("ALTER TABLE objects ADD COLUMN location_id TEXT;");
} catch {
// Column already exists, ignore error
}
}
save(obj: AttributableObject, type: string, worldId?: string): void {
@@ -57,19 +65,25 @@ export class SQLiteRepository {
clockIso = obj.clock.get().toISOString();
}
let locationId: string | null = null;
if (obj instanceof Entity) {
locationId = obj.locationId;
}
// 1. Insert or ignore the object in the objects table
this.db
.prepare(
`
INSERT INTO objects (id, type, world_id, clock_iso)
VALUES (?, ?, ?, ?)
INSERT INTO objects (id, type, world_id, clock_iso, location_id)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
type = excluded.type,
world_id = excluded.world_id,
clock_iso = excluded.clock_iso
clock_iso = excluded.clock_iso,
location_id = excluded.location_id
`,
)
.run(obj.id, type, worldId || null, clockIso);
.run(obj.id, type, worldId || null, clockIso, locationId);
// Get current attributes from db to delete the ones that are no longer present
const existingAttrs = this.db
@@ -155,16 +169,16 @@ export class SQLiteRepository {
const objRow = this.db
.prepare(
`
SELECT type FROM objects WHERE id = ?
SELECT type, location_id FROM objects WHERE id = ?
`,
)
.get(id) as { type: string } | undefined;
.get(id) as { type: string; location_id: string | null } | undefined;
if (!objRow || objRow.type !== "entity") {
return null;
}
const entity = new Entity(id);
const entity = new Entity(id, objRow.location_id);
this.reconstituteAttributes(entity);
return entity;
}
@@ -190,13 +204,13 @@ export class SQLiteRepository {
const entityRows = this.db
.prepare(
`
SELECT id FROM objects WHERE type = 'entity' AND world_id = ?
SELECT id, location_id FROM objects WHERE type = 'entity' AND world_id = ?
`,
)
.all(id) as { id: string }[];
.all(id) as { id: string; location_id: string | null }[];
for (const row of entityRows) {
const entity = new Entity(row.id);
const entity = new Entity(row.id, row.location_id);
this.reconstituteAttributes(entity);
worldState.addEntity(entity);
}

View File

@@ -126,7 +126,7 @@ describe("SQLiteRepository Unit Tests (Tier 1)", () => {
world.addAttribute("location", "Outer Rim", AttributeVisibility.PUBLIC);
// 2. Setup Entities and add attributes with ACL grants
const alice = new Entity("alice");
const alice = new Entity("alice", "location-a");
alice.addAttribute("name", "Alice Smith", AttributeVisibility.PUBLIC);
// Secret attribute visible only to 'bob'
alice.addAttribute("diaries", "Private thoughts", AttributeVisibility.PRIVATE, new Set(["bob"]));
@@ -156,6 +156,8 @@ describe("SQLiteRepository Unit Tests (Tier 1)", () => {
expect(loadedBob).toBeDefined();
// 7. Assert entity attributes and access controls are preserved
expect(loadedAlice!.locationId).toBe("location-a");
expect(loadedBob!.locationId).toBeNull();
expect(loadedAlice!.attributes.get("name")?.getValue()).toBe("Alice Smith");
expect(loadedAlice!.attributes.get("name")?.visibility).toBe(AttributeVisibility.PUBLIC);
@@ -184,7 +186,7 @@ describe("Serializer Unit Tests (Tier 1)", () => {
const world = new WorldState("world-1");
world.addAttribute("gravity", "9.8", AttributeVisibility.PUBLIC);
const alice = new Entity("alice");
const alice = new Entity("alice", "location-a");
alice.addAttribute("role", "warrior", AttributeVisibility.PUBLIC);
world.addEntity(alice);
@@ -196,6 +198,7 @@ describe("Serializer Unit Tests (Tier 1)", () => {
expect(result).toContain(" * gravity: 9.8 (Visibility: PUBLIC)");
expect(result).toContain("Entities:");
expect(result).toContain(" - Entity [ID: alice]:");
expect(result).toContain(" * Location ID: location-a");
expect(result).toContain(" * role: warrior (Visibility: PUBLIC)");
expect(result).toContain(" - Entity [ID: bob]:");
expect(result).toContain(" * (No attributes)");