From fdd530a3e7fbdb095fef550d6eaebe15fd27cd8b Mon Sep 17 00:00:00 2001 From: tom-boullay Date: Wed, 27 May 2026 11:39:44 +0200 Subject: [PATCH] fix(map): prevent visual loading from blocking gameplay --- src/world/GameMap.tsx | 46 +++++++++---------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/src/world/GameMap.tsx b/src/world/GameMap.tsx index c027e46..9b85441 100644 --- a/src/world/GameMap.tsx +++ b/src/world/GameMap.tsx @@ -24,13 +24,16 @@ import { CloudSystem } from "@/world/clouds/CloudSystem"; import { GeneratedMapNodeInstance } from "@/world/map-generated/GeneratedMapNodeInstance"; import { isGeneratedMapModelName } from "@/world/map-generated/generatedMapModelConfig"; import { MapInstancingSystem } from "@/world/map-instancing/MapInstancingSystem"; -import { isInstancedMapNodeName } from "@/world/map-instancing/mapInstancingConfig"; import { VegetationSystem } from "@/world/vegetation/VegetationSystem"; import { WaterSystem } from "@/world/water/WaterSystem"; import { WorldPlane } from "@/world/WorldPlane"; import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading"; import { logger } from "@/utils/core/Logger"; import { loadMapSceneData } from "@/utils/map/loadMapSceneData"; +import { + getTerrainMapNode, + isRuntimeSingleMapNode, +} from "@/utils/map/mapRuntimeClassification"; import { logModelLoadError } from "@/utils/three/modelLoadLogger"; import type { MapNode } from "@/types/editor/editor"; import type { OctreeReadyHandler } from "@/types/three/three"; @@ -40,16 +43,6 @@ interface LoadedMapNode { modelUrl: string | null; } -const MAP_STRUCTURE_NODE_NAMES = new Set(["Scene", "blocking", "terrain"]); -const LITE_MAP_SKIPPED_NODE_NAMES = new Set([ - "arbre", - "buisson", - "champdeble", - "champdesoja", - "champsdetournesol", - "sapin", -]); - interface ErrorBoundaryProps { children: ReactNode; fallback: ReactNode; @@ -121,7 +114,7 @@ export function GameMap({ const [terrainNode, setTerrainNode] = useState(null); const [mapLoaded, setMapLoaded] = useState(false); const [settledMapNodeCount, setSettledMapNodeCount] = useState(0); - const mapReady = mapLoaded && settledMapNodeCount >= renderMapNodes.length; + const mapReady = mapLoaded; const handleMapNodeSettled = useCallback((index: number) => { if (settledMapNodesRef.current.has(index)) return; @@ -169,7 +162,9 @@ export function GameMap({ status: "loading", }); - const visibleMapNodes = sceneData.mapNodes.filter(liteMap); + const visibleMapNodes = sceneData.mapNodes.filter( + isRuntimeSingleMapNode, + ); const skippedMapNodeCount = sceneData.mapNodes.length - visibleMapNodes.length; @@ -189,7 +184,7 @@ export function GameMap({ const modelUrl = sceneData.models.get(node.name); return { node, modelUrl: modelUrl ?? null }; }); - const loadedTerrainNode = loadedCollisionNodes[0]?.node ?? null; + const loadedTerrainNode = getTerrainMapNode(sceneData.mapNodes); const missingModelCount = loadedMapNodes.filter( (mapNode) => mapNode.modelUrl === null, ).length; @@ -299,29 +294,6 @@ function HiddenMapNode({ onSettled }: { onSettled: () => void }): null { return null; } -/** - * Temporary development-only map reducer. - * - * TODO: replace this with a real map performance pass: merged static geometry, - * instancing for repeated props, LOD, and/or zone-based loading. For now this - * keeps the app usable on local machines by not rendering the densest exported - * nodes from map.json. - */ -function liteMap(node: MapNode): boolean { - if (MAP_STRUCTURE_NODE_NAMES.has(node.name)) { - return false; - } - - if (node.type === "Mesh") { - return false; - } - - return ( - !LITE_MAP_SKIPPED_NODE_NAMES.has(node.name) && - !isInstancedMapNodeName(node.name) - ); -} - function MapNodeInstance({ node, modelUrl,