fix(editor): preserve hierarchical map saves

This commit is contained in:
tom-boullay
2026-05-27 09:47:01 +02:00
parent ab100c683f
commit d38ad242d6
5 changed files with 121 additions and 17 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
import type { SceneData } from "@/types/editor/editor";
import { parseMapNodes } from "@/utils/map/mapNodeValidation";
import { parseMapData } from "@/utils/map/mapNodeValidation";
const MAP_JSON_PATH = "/map.json";
@@ -18,7 +18,7 @@ export async function createSceneDataFromFiles(
}
const mapPayload: unknown = JSON.parse(await mapFile.text());
const mapNodes = parseMapNodes(mapPayload);
const { mapNodes, mapTree } = parseMapData(mapPayload);
const models = new Map<string, string>();
for (const [path, file] of fileMap.entries()) {
@@ -31,7 +31,7 @@ export async function createSceneDataFromFiles(
}
}
return { mapNodes, models };
return { mapNodes, models, mapTree };
}
function getProjectRelativePath(file: File): string {
+19 -8
View File
@@ -1,5 +1,9 @@
import type { MapNode, SceneData } from "@/types/editor/editor";
import { parseMapNodes } from "@/utils/map/mapNodeValidation";
import type {
HierarchicalMapNode,
MapNode,
SceneData,
} from "@/types/editor/editor";
import { parseMapData } from "@/utils/map/mapNodeValidation";
const MAP_JSON_PATH = "/map.json";
const MODEL_FILE_NAMES = ["model.glb", "model.gltf"];
@@ -21,8 +25,12 @@ export async function loadMapSceneData(): Promise<SceneData | null> {
}
loadingPromise = loadMapSceneDataInternal();
cachedSceneData = await loadingPromise;
loadingPromise = null;
try {
cachedSceneData = await loadingPromise;
} finally {
loadingPromise = null;
}
return cachedSceneData;
}
@@ -45,9 +53,9 @@ async function loadMapSceneDataInternal(): Promise<SceneData | null> {
}
const mapPayload: unknown = await response.json();
const mapNodes = parseMapNodes(mapPayload);
const { mapNodes, mapTree } = parseMapData(mapPayload);
const deduplicatedNodes = deduplicateMapNodes(mapNodes);
return createSceneData(deduplicatedNodes);
return createSceneData(deduplicatedNodes, mapTree);
}
function createPositionKey(node: MapNode): string {
@@ -84,9 +92,12 @@ function deduplicateMapNodes(nodes: MapNode[]): MapNode[] {
return result;
}
async function createSceneData(mapNodes: MapNode[]): Promise<SceneData> {
async function createSceneData(
mapNodes: MapNode[],
mapTree: HierarchicalMapNode | HierarchicalMapNode[],
): Promise<SceneData> {
const models = await loadMapModelUrls(mapNodes);
return { mapNodes, models };
return { mapNodes, models, mapTree };
}
async function loadMapModelUrls(
+23 -4
View File
@@ -1,5 +1,10 @@
import type { HierarchicalMapNode, MapNode } from "../../types/editor/editor";
export interface ParsedMapNodes {
mapNodes: MapNode[];
mapTree: HierarchicalMapNode | HierarchicalMapNode[];
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
@@ -46,15 +51,19 @@ function isHierarchicalMapNode(value: unknown): value is HierarchicalMapNode {
);
}
function flattenMapNode(node: HierarchicalMapNode): MapNode[] {
function flattenMapNode(node: HierarchicalMapNode, path: number[]): MapNode[] {
const mapNode: MapNode = {
name: node.name,
type: node.type,
position: node.position,
rotation: node.rotation,
scale: node.scale,
sourcePath: path,
};
const childNodes = node.children?.flatMap(flattenMapNode) ?? [];
const childNodes =
node.children?.flatMap((child, index) =>
flattenMapNode(child, [...path, index]),
) ?? [];
if (node.role === "group") {
return childNodes;
@@ -64,12 +73,22 @@ function flattenMapNode(node: HierarchicalMapNode): MapNode[] {
}
export function parseMapNodes(value: unknown): MapNode[] {
return parseMapData(value).mapNodes;
}
export function parseMapData(value: unknown): ParsedMapNodes {
if (Array.isArray(value) && value.every(isHierarchicalMapNode)) {
return value.flatMap(flattenMapNode);
return {
mapNodes: value.flatMap((node, index) => flattenMapNode(node, [index])),
mapTree: value,
};
}
if (isHierarchicalMapNode(value)) {
return flattenMapNode(value);
return {
mapNodes: flattenMapNode(value, []),
mapTree: value,
};
}
throw new Error("Invalid map node data");