-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathversion.ts
More file actions
114 lines (95 loc) · 3.63 KB
/
Copy pathversion.ts
File metadata and controls
114 lines (95 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Schema version management for `*.world.json` and `*.prefab.json` files.
//
// The current version number lives in `./types.ts` as `WORLD_SCHEMA_VERSION`.
// When the schema changes in a breaking way, bump that constant and add a
// migration step here. `migrateWorldData` and `migratePrefabData` are called
// by the loader before handing data to the rest of the pipeline.
import { WORLD_SCHEMA_VERSION, WorldData, PrefabData, LightData, Vec3Lit } from './types';
// Migrate a parsed world document to the current schema version. Returns the
// same object (mutated in place) for convenience. Logs a warning when the
// input is from a future version — in that case we proceed anyway and hope
// the extra fields are ignored, since we can't forward-migrate.
export function migrateWorldData(raw: WorldData): WorldData {
const from = raw.schemaVersion | 0;
if (from === WORLD_SCHEMA_VERSION) {
return raw;
}
if (from > WORLD_SCHEMA_VERSION) {
// Future version — we may be missing fields. Warn and continue.
return raw;
}
// from < WORLD_SCHEMA_VERSION. Apply migration steps in order.
if (from < 2) {
migrateV1ToV2(raw);
}
raw.schemaVersion = WORLD_SCHEMA_VERSION;
return raw;
}
// v1 → v2: lights become a top-level array.
//
// In v1 a light was an ordinary entity carrying `userData.kind = "point_light"`
// plus `range`, `color` ("r, g, b"), and `intensity` strings — a convention each
// game had to know about. Lift those entities into `world.lights` and drop them
// from `entities`, so the editor (and any other consumer) sees a light as a
// light. Worlds with no such entities just gain an empty array.
function migrateV1ToV2(raw: WorldData): void {
if (!raw.lights) raw.lights = [];
const kept: typeof raw.entities = [];
for (let i = 0; i < raw.entities.length; i++) {
const e = raw.entities[i];
const kind = e.userData ? e.userData['kind'] : undefined;
if (kind !== 'point_light') {
kept.push(e);
continue;
}
const light: LightData = {
id: e.id,
name: e.name,
kind: 'point',
position: [
e.transform.position[0],
e.transform.position[1],
e.transform.position[2],
],
color: parseColor(e.userData['color']),
intensity: parseNumber(e.userData['intensity'], 1),
range: parseNumber(e.userData['range'], 12),
};
raw.lights.push(light);
}
raw.entities = kept;
}
// userData values are strings; "1.0, 0.85, 0.55" is the colour convention.
function parseColor(s: string | undefined): Vec3Lit {
if (s === undefined || s.length === 0) return [1, 1, 1];
const parts = s.split(',');
if (parts.length !== 3) return [1, 1, 1];
const r = parseFloat(parts[0]);
const g = parseFloat(parts[1]);
const b = parseFloat(parts[2]);
if (r !== r || g !== g || b !== b) return [1, 1, 1];
return [r, g, b];
}
function parseNumber(s: string | undefined, fallback: number): number {
if (s === undefined || s.length === 0) return fallback;
const v = parseFloat(s);
return v === v ? v : fallback;
}
export function migratePrefabData(raw: PrefabData): PrefabData {
// Prefabs saved before 2026-07-15 are missing `schemaVersion` and `bounds`
// outright — serializePrefab dropped both (fixed the same day). Backfill
// bounds with a degenerate AABB so those files still load; consumers treat
// a zero-size bounds as "unknown".
if (!raw.bounds) {
raw.bounds = { min: [0, 0, 0], max: [0, 0, 0] };
}
const from = raw.schemaVersion | 0;
if (from === WORLD_SCHEMA_VERSION) {
return raw;
}
if (from > WORLD_SCHEMA_VERSION) {
return raw;
}
raw.schemaVersion = WORLD_SCHEMA_VERSION;
return raw;
}