diff --git a/Standalone/Research/WorldAssemblerM3/Linux133/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux133/REPORT.md new file mode 100644 index 0000000..bed33eb --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux133/REPORT.md @@ -0,0 +1,7 @@ +# L133 — native floating islands + +Stopped after the second route design. The native v9 plan binds the owner’s resolved mouths and passes raw route audit, strict layout (0 issues), 3/3 interface audits, shell (0 floor violations), and ripple (3/3). The candidate dispatches FI natively. + +The composed FI walk still fails: 2 capsule collisions, 40 unsupported centers, 0 grade failures; mouth binding passes. The return lane is carved by `passage/0-1/0` near `(19.755, 9.605, -733.553) m` (4.25 m from its 4.5 m tube). At `passage/1-2/0`, the approach has collisions at `(14.402, -10.552, -732.556) m` and `(14.242, -10.712, -732.556) m`; the departure landing probe `(19.166, -14.650, -733.331) m` gets 0/5 support probes. The exact endpoints alone are not enough to place a route clear of both composed passage volumes and retain a supported landing. + +No captures, timing matrix, compiler determinism matrix, baseline update, or UE host build were run after the required composed walk gate failed. The owner’s default import remains on its legacy FI fallback. Logs and candidate fixtures are under `Saved/Linux/l133/`. diff --git a/Standalone/Research/WorldAssemblerM3/Linux133/l133-native-fi-v9.patch b/Standalone/Research/WorldAssemblerM3/Linux133/l133-native-fi-v9.patch new file mode 100644 index 0000000..cae3424 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux133/l133-native-fi-v9.patch @@ -0,0 +1,511 @@ +diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +index cb2b2dd..5a94ab8 100644 +--- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h ++++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +@@ -496,8 +496,9 @@ namespace VoxelForgeCore + // continuous floor while relaxing the spline chord tolerance and bounding shell work. + // Version 226 unifies the passage influence reach in metres and converts it to core + // voxels for every broadphase and proof bound. Version 227 uses the exact smooth-max +- // support width/6 when deriving that reach. +- inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 227; ++ // support width/6 when deriving that reach. Version 228 permits the native FI route ++ // planner to bind its fixed endpoints to the resolved passage mouths. ++ inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 228; + + enum class FieldChannel : std::uint32_t + { +@@ -788,6 +789,15 @@ namespace VoxelForgeCore + std::uint64_t contentHash = 0; + }; + ++ // The two resolved inter-strate walk points owned by a FloatingIslands slot. ++ // The route planner treats these as fixed player-capsule centers and places ++ // the island chain between them. ++ struct FloatingIslandRouteEndpointsV1 ++ { ++ std::array arrivalSubvoxels{}; ++ std::array departureSubvoxels{}; ++ }; ++ + struct WorldAssemblyFeatureGroupBoundsV1 + { + std::array minimumSubvoxels{}; +@@ -1377,6 +1387,9 @@ namespace VoxelForgeCore + // Planner output is ordinary reviewed data; clients can load it without re-running the planner. + VFCORE_API std::optional PlanFloatingIslandCluster( + const FloatingIslandRecipeV1& recipe, std::string& error); ++ VFCORE_API std::optional PlanFloatingIslandCluster( ++ const FloatingIslandRecipeV1& recipe, ++ const FloatingIslandRouteEndpointsV1& endpoints, std::string& error); + VFCORE_API FloatingIslandBoundsAuditResult AuditFloatingIslandBounds( + const FloatingIslandFieldV1& field, std::uint32_t boxCount = 256, + std::uint64_t seed = 0x49534c414e444231ull); +diff --git a/Standalone/Core/src/FloatingIslandsInternal.h b/Standalone/Core/src/FloatingIslandsInternal.h +index 33d97d6..42f855a 100644 +--- a/Standalone/Core/src/FloatingIslandsInternal.h ++++ b/Standalone/Core/src/FloatingIslandsInternal.h +@@ -285,7 +285,7 @@ namespace floating_island_detail + ? recipe.ownerVoxelSizeMicrons / ScaleGcd : 0; + const std::uint32_t ExpectedDenominator = ScaleGcd > 0 + ? recipe.coreVoxelSizeMicrons / ScaleGcd : 0; +- if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 8 ++ if (recipe.schemaVersion != 1 || recipe.geometryVersion < 1 || recipe.geometryVersion > 9 + || (recipe.geometryVersion == 1 + && (recipe.crustDepthBonusQ16 != 0 || recipe.rootDepthRatioQ16 != 0)) + || recipe.unitsPerVoxel != Q +@@ -1473,7 +1473,8 @@ namespace floating_island_detail + static_cast(anchorIsland->centerSubvoxels[0]), + static_cast(anchorIsland->centerSubvoxels[1])); + if(anchorDistance>recipe.anchorPlacementRadiusSubvoxels +- ||recipe.anchorRootBottomZSubvoxels>=anchorIsland->bottomZSubvoxels ++ ||(recipe.geometryVersion<8 ++ &&recipe.anchorRootBottomZSubvoxels>=anchorIsland->bottomZSubvoxels) + ||recipe.anchorMouthOffsetSubvoxels+recipe.routeConstraints.capsuleRadiusSubvoxels + >=anchorIsland->radiusSubvoxels) + {error="floating-island anchor island or its mouth leaves the passage radius or rock envelope";return false;} +@@ -1607,7 +1608,7 @@ namespace floating_island_detail + ||to.ordinal==recipe.anchorIslandOrdinal); + const bool westernSupportConnector=WorldAssemblyEvaluatorVersion>=178 + &&((from.ordinal==0&&to.ordinal==1)||(from.ordinal==1&&to.ordinal==0)); +- const double routeDeckLift=recipe.geometryVersion>=2 ++ const double routeDeckLift=recipe.geometryVersion>=9?0.0:recipe.geometryVersion>=2 + ?(WorldAssemblyEvaluatorVersion>=178 + ?(westernSupportConnector + ?(WorldAssemblyEvaluatorVersion>=181?-1.0 +@@ -2264,6 +2265,335 @@ std::optional PlanFloatingIslandCluster( + return manifest; + } + ++std::optional PlanFloatingIslandCluster( ++ const FloatingIslandRecipeV1& inputRecipe, ++ const FloatingIslandRouteEndpointsV1& endpoints, std::string& error) ++{ ++ FloatingIslandManifestV1 manifest; ++ manifest.recipe = inputRecipe; ++ auto& recipe = manifest.recipe; ++ recipe.geometryVersion = 9; ++ recipe.primaryIslandCount = 9; ++ recipe.anchorIslandOrdinal = recipe.primaryIslandCount - 1; ++ constexpr std::int64_t step = 192 * floating_island_detail::Q; ++ recipe.spacingSubvoxels = step; ++ if (recipe.routeConstraints.version == 0) ++ { ++ recipe.routeConstraints.version = 1; ++ recipe.routeConstraints.capsuleRadiusSubvoxels = 348; ++ recipe.routeConstraints.capsuleHeightSubvoxels = 1802; ++ recipe.routeConstraints.maximumGradeQ16 = 17560; ++ recipe.routeConstraints.sweepSpacingSubvoxels = 128; ++ recipe.routeConstraints.supportProbeRadiusSubvoxels = 230; ++ recipe.routeConstraints.supportSearchDepthSubvoxels = 512; ++ recipe.routeConstraints.minimumClearanceSubvoxels = 128; ++ recipe.routeConstraints.minimumSupportProbes = 3; ++ } ++ for (const auto& value : endpoints.arrivalSubvoxels) ++ if (!floating_island_detail::ValidScalar(value)) ++ { error = "resolved floating-island arrival mouth is outside the fixed-point range"; return std::nullopt; } ++ for (const auto& value : endpoints.departureSubvoxels) ++ if (!floating_island_detail::ValidScalar(value)) ++ { error = "resolved floating-island departure mouth is outside the fixed-point range"; return std::nullopt; } ++ ++ const double qPerMetre = static_cast(floating_island_detail::Q) ++ * 1000000.0 / recipe.coreVoxelSizeMicrons; ++ const double dx = static_cast(endpoints.departureSubvoxels[0] ++ - endpoints.arrivalSubvoxels[0]); ++ const double dy = static_cast(endpoints.departureSubvoxels[1] ++ - endpoints.arrivalSubvoxels[1]); ++ const double endpointDistance = std::hypot(dx, dy); ++ const std::int64_t maximumMouthOffset = 64 * floating_island_detail::Q ++ - recipe.routeConstraints.capsuleRadiusSubvoxels - floating_island_detail::Q; ++ const std::int64_t desiredMouthOffset = std::max(recipe.anchorMouthOffsetSubvoxels, ++ static_cast(std::llround(qPerMetre * 6.6))); ++ recipe.anchorMouthOffsetSubvoxels = std::min({desiredMouthOffset, ++ maximumMouthOffset, static_cast(std::llround(endpointDistance))}); ++ if (recipe.anchorMouthOffsetSubvoxels <= 0) ++ { error = "resolved island mouths cannot provide a bounded departure approach"; return std::nullopt; } ++ recipe.anchorRootRadiusSubvoxels = std::max(recipe.anchorRootRadiusSubvoxels, ++ recipe.anchorMouthOffsetSubvoxels + recipe.routeConstraints.capsuleRadiusSubvoxels ++ + floating_island_detail::Q); ++ recipe.anchorIslandMinimumRadiusSubvoxels = std::max(recipe.minimumRadiusSubvoxels, ++ recipe.anchorMouthOffsetSubvoxels + recipe.routeConstraints.capsuleRadiusSubvoxels ++ + floating_island_detail::Q + 1); ++ // Keep the resolved fixed mouth on a capsule-grade ramp at the anchor island lip. ++ recipe.topFlattenQ16 = std::max(recipe.topFlattenQ16, 52429); ++ recipe.bridgeHalfWidthSubvoxels = std::max(recipe.bridgeHalfWidthSubvoxels, ++ recipe.routeConstraints.capsuleRadiusSubvoxels + 2 * floating_island_detail::Q); ++ if (!floating_island_detail::ValidateRecipe(recipe, error)) return std::nullopt; ++ ++ auto& islands = manifest.plan.islands; ++ auto& edges = manifest.plan.clusterEdges; ++ auto& route = manifest.plan.route; ++ constexpr std::uint32_t count = 9; ++ double tangentX = endpointDistance > 1.0 ++ ? dx / endpointDistance : 1.0; ++ double tangentY = endpointDistance > 1.0 ++ ? dy / endpointDistance : 0.0; ++ if (endpointDistance <= 1.0) ++ { ++ const double angle = static_cast(floating_island_detail::UnitQ16( ++ recipe.seed, 0x900000000ull, 0x524f555445ull)) ++ * (2.0 * floating_island_detail::Pi / 65536.0); ++ tangentX = std::cos(angle); ++ tangentY = std::sin(angle); ++ } ++ const double normalX = -tangentY; ++ const double normalY = tangentX; ++ const auto localPoint = [&](double along, double side) ++ { ++ return std::array{ ++ floating_island_detail::RoundQ( ++ static_cast(endpoints.arrivalSubvoxels[0]) ++ + tangentX * along + normalX * side), ++ floating_island_detail::RoundQ( ++ static_cast(endpoints.arrivalSubvoxels[1]) ++ + tangentY * along + normalY * side)}; ++ }; ++ std::array, count> primaryXY{}; ++ primaryXY[0] = {endpoints.arrivalSubvoxels[0], endpoints.arrivalSubvoxels[1]}; ++ primaryXY[1] = localPoint(0.0, static_cast(step)); ++ primaryXY[2] = localPoint(static_cast(step), static_cast(step)); ++ primaryXY[3] = localPoint(static_cast(step), 0.0); ++ primaryXY[4] = localPoint(static_cast(step), -static_cast(step)); ++ primaryXY[5] = localPoint(0.0, -static_cast(step)); ++ primaryXY[6] = localPoint(-static_cast(step), -static_cast(step)); ++ // Keep the return lane outside the arrival passage's spiral and on the negative ++ // side of the first connector, so the two bridges cannot cross in plan view. ++ // Keep the final approach radial at the passage mouth. This lets the shared ++ // passage resolver keep its authored route inside the placement disk. ++ primaryXY[7] = localPoint(-0.85 * static_cast(step), ++ -0.23 * static_cast(step)); ++ const double departureRadial = std::hypot( ++ static_cast(endpoints.departureSubvoxels[0]), ++ static_cast(endpoints.departureSubvoxels[1])); ++ const double anchorScale = departureRadial > 1.0 ++ ? std::max(0.0, (departureRadial - recipe.anchorMouthOffsetSubvoxels) / departureRadial) ++ : 0.0; ++ primaryXY[8] = { ++ floating_island_detail::RoundQ(endpoints.departureSubvoxels[0] * anchorScale), ++ floating_island_detail::RoundQ(endpoints.departureSubvoxels[1] * anchorScale)}; ++ ++ std::array cumulative{}; ++ for (std::uint32_t i = 1; i < count; ++i) ++ { ++ const double segmentX = static_cast(primaryXY[i][0] - primaryXY[i - 1][0]); ++ const double segmentY = static_cast(primaryXY[i][1] - primaryXY[i - 1][1]); ++ const double length = std::hypot(segmentX, segmentY); ++ if (!(length > 0.0) || length > static_cast(256 * floating_island_detail::Q)) ++ { error = "resolved island mouths require an unsupported serpentine segment length"; return std::nullopt; } ++ cumulative[i] = cumulative[i - 1] + length; ++ } ++ const double totalLength = cumulative.back(); ++ if (!(totalLength > 0.0)) ++ { error = "resolved floating-island mouths do not define a route"; return std::nullopt; } ++ const std::int64_t capsuleHalf = recipe.routeConstraints.capsuleHeightSubvoxels / 2; ++ const std::int64_t routeOffset = capsuleHalf ++ + recipe.routeConstraints.minimumClearanceSubvoxels ++ + floating_island_detail::Q / 8; ++ const std::int64_t startTop = endpoints.arrivalSubvoxels[2] - routeOffset; ++ const std::int64_t endTop = endpoints.departureSubvoxels[2] - routeOffset; ++ const std::uint64_t anchorSite = 0x700000000ull + recipe.anchorIslandOrdinal; ++ auto anchorShape = floating_island_detail::MakeIsland(recipe, ++ recipe.anchorIslandOrdinal, primaryXY.back()[0], primaryXY.back()[1], endTop, anchorSite); ++ anchorShape.radiusSubvoxels = std::max(anchorShape.radiusSubvoxels, ++ recipe.anchorIslandMinimumRadiusSubvoxels); ++ anchorShape.topHalfSubvoxels = std::max(1, ++ anchorShape.radiusSubvoxels / 5); ++ anchorShape.centerSubvoxels[2] = endTop - anchorShape.topHalfSubvoxels; ++ std::int64_t anchorTop = endTop; ++ for (int iteration = 0; iteration < 4; ++iteration) ++ { ++ anchorShape.topZSubvoxels = anchorTop; ++ anchorShape.centerSubvoxels[2] = anchorTop - anchorShape.topHalfSubvoxels; ++ const double mouthSurface = floating_island_detail::IslandTopSurface(recipe, ++ anchorShape, static_cast(endpoints.departureSubvoxels[0]), ++ static_cast(endpoints.departureSubvoxels[1])); ++ anchorTop = endTop + floating_island_detail::RoundQ( ++ static_cast(anchorTop) - mouthSurface); ++ } ++ std::array tops{}; ++ for (std::uint32_t i = 0; i < count; ++i) ++ { ++ const double t = cumulative[i] / totalLength; ++ tops[i] = floating_island_detail::RoundQ(static_cast(startTop) ++ + static_cast(anchorTop - startTop) * t); ++ } ++ ++ islands.reserve(count); ++ for (std::uint32_t i = 0; i < count; ++i) ++ { ++ const std::uint64_t site = 0x700000000ull + i; ++ auto island = floating_island_detail::MakeIsland(recipe, i, ++ primaryXY[i][0], primaryXY[i][1], tops[i], site); ++ if (i == recipe.anchorIslandOrdinal) ++ { ++ island.radiusSubvoxels = std::max(island.radiusSubvoxels, ++ recipe.anchorIslandMinimumRadiusSubvoxels); ++ } ++ const std::int64_t interiorBottom = recipe.layoutMinimumZSubvoxels ++ + recipe.boundarySealSubvoxels; ++ while (island.radiusSubvoxels > recipe.minimumRadiusSubvoxels ++ && floating_island_detail::IslandBottom(recipe, tops[i], island.radiusSubvoxels) ++ < interiorBottom) ++ --island.radiusSubvoxels; ++ const std::int64_t requiredRadius = i == recipe.anchorIslandOrdinal ++ ? recipe.anchorIslandMinimumRadiusSubvoxels : recipe.minimumRadiusSubvoxels; ++ if (island.radiusSubvoxels < requiredRadius ++ || floating_island_detail::IslandBottom(recipe, tops[i], island.radiusSubvoxels) ++ < interiorBottom) ++ { ++ error = "resolved-mouth island " + std::to_string(i) + " cannot fit above sealed bedrock" ++ + " (radius=" + std::to_string(island.radiusSubvoxels) ++ + ", required=" + std::to_string(requiredRadius) ++ + ", top=" + std::to_string(tops[i]) ++ + ", bottom=" + std::to_string(floating_island_detail::IslandBottom( ++ recipe, tops[i], island.radiusSubvoxels)) ++ + ", interior_bottom=" + std::to_string(interiorBottom) ++ + ", anchor_top=" + std::to_string(anchorTop) ++ + ", mouth_offset=" + std::to_string(recipe.anchorMouthOffsetSubvoxels) + ")"; ++ return std::nullopt; ++ } ++ island.topHalfSubvoxels = std::max(1, island.radiusSubvoxels / 5); ++ island.topZSubvoxels = tops[i]; ++ island.centerSubvoxels[2] = tops[i] - island.topHalfSubvoxels; ++ island.bottomZSubvoxels = floating_island_detail::IslandBottom(recipe, ++ island.topZSubvoxels, island.radiusSubvoxels); ++ floating_island_detail::SetIslandBounds(recipe, island); ++ islands.push_back(std::move(island)); ++ } ++ for (std::uint32_t i = 0; i + 1 < count; ++i) ++ edges.push_back(floating_island_detail::MakeConnector(recipe, ++ islands[i], islands[i + 1], 0x600000000ull + i)); ++ ++ FloatingIslandFieldV1 planningField(recipe, manifest.plan); ++ if (!planningField.isValid()) ++ { error = "resolved-mouth island field did not pass the Core field contract"; return std::nullopt; } ++ const std::int64_t routeRadius = 4 * floating_island_detail::Q; ++ std::uint32_t ordinal = 0; ++ for (std::uint32_t segment = 0; segment + 1 < count; ++segment) ++ { ++ const double segmentX = static_cast(primaryXY[segment + 1][0] ++ - primaryXY[segment][0]); ++ const double segmentY = static_cast(primaryXY[segment + 1][1] ++ - primaryXY[segment][1]); ++ const double length = std::hypot(segmentX, segmentY); ++ const std::uint32_t pieces = static_cast(std::max(1.0, ++ std::ceil(length / (4.0 * floating_island_detail::Q)))); ++ for (std::uint32_t piece = segment == 0 ? 0 : 1; piece <= pieces; ++piece) ++ { ++ const double t = static_cast(piece) / pieces; ++ const auto x = floating_island_detail::RoundQ( ++ static_cast(primaryXY[segment][0]) + segmentX * t); ++ const auto y = floating_island_detail::RoundQ( ++ static_cast(primaryXY[segment][1]) + segmentY * t); ++ const auto surface = floating_island_detail::FindCapsuleFootingTopZ( ++ recipe, manifest.plan, x, y); ++ if (!surface) ++ { error = "resolved-mouth island chain has no capsule-width support along a connector"; return std::nullopt; } ++ WorldAssemblyRoutePointV1 point; ++ point.stableFeatureId = floating_island_detail::StableId(recipe.seed, ++ 0x800000000ull + ordinal, 0x524f555445ull); ++ point.positionSubvoxels = {x, y, *surface + routeOffset}; ++ point.radiusSubvoxels = routeRadius; ++ if (segment == 0 && piece == 0) ++ point.positionSubvoxels = endpoints.arrivalSubvoxels; ++ route.push_back(point); ++ ++ordinal; ++ } ++ } ++ const double mouthLegX = static_cast(endpoints.departureSubvoxels[0] ++ - primaryXY.back()[0]); ++ const double mouthLegY = static_cast(endpoints.departureSubvoxels[1] ++ - primaryXY.back()[1]); ++ const double mouthLegLength = std::hypot(mouthLegX, mouthLegY); ++ const std::uint32_t mouthLegPieces = static_cast(std::max(1.0, ++ std::ceil(mouthLegLength / (4.0 * floating_island_detail::Q)))); ++ for (std::uint32_t piece = 1; piece <= mouthLegPieces; ++piece) ++ { ++ const double t = static_cast(piece) / mouthLegPieces; ++ const auto x = floating_island_detail::RoundQ(static_cast(primaryXY.back()[0]) ++ + mouthLegX * t); ++ const auto y = floating_island_detail::RoundQ(static_cast(primaryXY.back()[1]) ++ + mouthLegY * t); ++ const auto surface = floating_island_detail::FindCapsuleFootingTopZ( ++ recipe, manifest.plan, x, y); ++ if (!surface) ++ { error = "resolved island passage mouth has no capsule-width support along its anchor landing at x=" ++ + std::to_string(x) + ", y=" + std::to_string(y) + ", piece=" ++ + std::to_string(piece) + "/" + std::to_string(mouthLegPieces) ++ + ", anchor_radius=" + std::to_string(anchorShape.radiusSubvoxels); return std::nullopt; } ++ WorldAssemblyRoutePointV1 point; ++ point.stableFeatureId = floating_island_detail::StableId(recipe.seed, ++ 0x800000000ull + ordinal, 0x524f555445ull); ++ point.positionSubvoxels = {x, y, *surface + routeOffset}; ++ point.radiusSubvoxels = routeRadius; ++ if (piece == mouthLegPieces) ++ point.positionSubvoxels = endpoints.departureSubvoxels; ++ route.push_back(point); ++ ++ordinal; ++ } ++ ++ const double maximumGrade = floating_island_detail::Q16ToDouble( ++ recipe.routeConstraints.maximumGradeQ16) * 0.95; ++ std::vector baseHeights; ++ std::vector routeDistance(route.size(), 0.0); ++ baseHeights.reserve(route.size()); ++ for (std::size_t i = 0; i < route.size(); ++i) ++ { ++ baseHeights.push_back(route[i].positionSubvoxels[2]); ++ if (i > 0) ++ { ++ const double dx = static_cast(route[i].positionSubvoxels[0] ++ - route[i - 1].positionSubvoxels[0]); ++ const double dy = static_cast(route[i].positionSubvoxels[1] ++ - route[i - 1].positionSubvoxels[1]); ++ routeDistance[i] = routeDistance[i - 1] + std::hypot(dx, dy); ++ } ++ } ++ for (std::size_t i = 0; i < route.size(); ++i) ++ { ++ double profile = static_cast(baseHeights[i]); ++ std::size_t profileSource = i; ++ for (std::size_t j = 0; j < route.size(); ++j) ++ { ++ const double pathDistance = std::abs(routeDistance[i] - routeDistance[j]); ++ const double candidate = static_cast(baseHeights[j]) ++ - maximumGrade * pathDistance; ++ if (candidate > profile) ++ { ++ profile = candidate; ++ profileSource = j; ++ } ++ } ++ const std::int64_t plannedHeight = floating_island_detail::RoundQ(profile); ++ const bool fixedEndpoint = i == 0 || i + 1 == route.size(); ++ if (fixedEndpoint && plannedHeight != baseHeights[i]) ++ { ++ error = "resolved fixed island mouth endpoint " + std::to_string(i) ++ + " needs " + std::to_string(plannedHeight - baseHeights[i]) ++ + " extra subvoxels from route sample " + std::to_string(profileSource) ++ + " at z=" + std::to_string(baseHeights[profileSource]) ++ + " and path distance=" + std::to_string(static_cast( ++ std::abs(routeDistance[i] - routeDistance[profileSource]))) ++ + " (arrival_z=" ++ + std::to_string(endpoints.arrivalSubvoxels[2]) + ", departure_z=" ++ + std::to_string(endpoints.departureSubvoxels[2]) + ", path_length=" ++ + std::to_string(static_cast(routeDistance.back())) + ")"; ++ return std::nullopt; ++ } ++ route[i].positionSubvoxels[2] = plannedHeight; ++ } ++ if (!manifest.finalize(error)) return std::nullopt; ++ const std::string encoded = manifest.ToJson(); ++ const auto roundTrip = FloatingIslandManifestV1::FromJson(encoded, error); ++ if (!roundTrip || roundTrip->ToJson() != encoded ++ || roundTrip->contentHash != manifest.contentHash) ++ { if (error.empty()) error = "resolved-mouth floating-island plan did not canonical-round-trip"; return std::nullopt; } ++ return manifest; ++} ++ + FloatingIslandFieldV1::FloatingIslandFieldV1(const FloatingIslandRecipeV1& recipe, + const FloatingIslandPlanV1& plan) noexcept : recipe_(&recipe), plan_(&plan) + {} +diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp +index a0cb0a7..468e315 100644 +--- a/Standalone/Source/StreamBenchmarkHeadless.cpp ++++ b/Standalone/Source/StreamBenchmarkHeadless.cpp +@@ -1826,12 +1826,59 @@ int main(int argc,char** argv) + else if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype( + *ownerWorld,ownerJsonRoot,error,{},&worldAssembly)) + {std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;} +- if(((!useExportedCorePlan&&options.FloatingIslandsRecipe) +- ||(options.NativeOwnerFloatingIslandsRecipe +- &&worldAssembly.floatingIslands.empty())) +- &&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly( +- *ownerWorld,ownerJsonRoot,worldAssembly,error)) +- {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;} ++ const bool attachFloatingIslandPlan=(!useExportedCorePlan&&options.FloatingIslandsRecipe) ++ ||(options.NativeOwnerFloatingIslandsRecipe&&worldAssembly.floatingIslands.empty()); ++ if(attachFloatingIslandPlan) ++ { ++ const FStrateSlot* islandSlot=nullptr; ++ if(ownerWorld->Manager) ++ for(const FStrateSlot& slot:ownerWorld->Manager->GetLayout()) ++ if(slot.Definition&&slot.Definition->GeneratorType==ECaveGeneratorType::FloatingIslands) ++ {islandSlot=&slot;break;} ++ if(!islandSlot) ++ {std::fprintf(stderr,"Floating-island endpoint resolution found no FloatingIslands strate.\n");return 2;} ++ VoxelForgeCore::ResolvedLayoutSnapshotV3 mouthLayout; ++ if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot( ++ *ownerWorld,worldAssembly,"floating-islands",mouthLayout,error)) ++ {std::fprintf(stderr,"Pre-plan passage layout failed: %s\n",error.c_str());return 2;} ++ VoxelForgeCore::FloatingIslandRouteEndpointsV1 endpoints; ++ bool hasArrival=false,hasDeparture=false; ++ for(const auto& passage:mouthLayout.passages) ++ { ++ const std::size_t lastSlash=passage.stableId.rfind('/'); ++ if(lastSlash==std::string::npos||passage.stableId.substr(lastSlash+1)!="0")continue; ++ if(passage.lowerStrateIndex==islandSlot->StrateIndex&&!hasArrival) ++ { ++ endpoints.arrivalSubvoxels=passage.lowerMouthSubvoxels; ++ hasArrival=true; ++ } ++ if(passage.upperStrateIndex==islandSlot->StrateIndex&&!hasDeparture) ++ { ++ endpoints.departureSubvoxels=passage.upperMouthSubvoxels; ++ hasDeparture=true; ++ } ++ } ++ if(!hasArrival||!hasDeparture) ++ {std::fprintf(stderr,"Resolved passage layout does not provide both FI route mouths (arrival=%s departure=%s).\n", ++ hasArrival?"yes":"no",hasDeparture?"yes":"no");return 2;} ++ std::printf("[FloatingIslandResolvedMouths] strate=%d arrival_m=(%.3f,%.3f,%.3f) departure_m=(%.3f,%.3f,%.3f) status=PASS\n", ++ islandSlot->StrateIndex, ++ endpoints.arrivalSubvoxels[0]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel), ++ endpoints.arrivalSubvoxels[1]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel), ++ endpoints.arrivalSubvoxels[2]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel), ++ endpoints.departureSubvoxels[0]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel), ++ endpoints.departureSubvoxels[1]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel), ++ endpoints.departureSubvoxels[2]*ownerWorld->CoreVoxelSizeMicrons ++ /(1.0e6*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)); ++ if(!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly( ++ *ownerWorld,ownerJsonRoot,worldAssembly,endpoints,error)) ++ {std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;} ++ } + if(!options.WorldAssemblyManifestOutputPath.empty()) + { + const std::string manifestText=worldAssembly.ToJson(); +diff --git a/Standalone/Source/WorldAssemblyPrototype.h b/Standalone/Source/WorldAssemblyPrototype.h +index dbe2a7c..9790f2f 100644 +--- a/Standalone/Source/WorldAssemblyPrototype.h ++++ b/Standalone/Source/WorldAssemblyPrototype.h +@@ -923,7 +923,8 @@ namespace StandaloneWorldAssemblyPrototype + } + + inline bool AttachFloatingIslandWorldAssembly(FStandaloneOwnerWorld& owner, +- VFJson::Value& root,VoxelForgeCore::WorldAssemblyManifest& assembly,std::string& error) ++ VFJson::Value& root,VoxelForgeCore::WorldAssemblyManifest& assembly, ++ const VoxelForgeCore::FloatingIslandRouteEndpointsV1& endpoints,std::string& error) + { + if(!owner.Manager) + {error="floating-island assembly needs the resolved standalone strate manager";return false;} +@@ -996,7 +997,7 @@ namespace StandaloneWorldAssemblyPrototype + recipe.anchorRootBottomZSubvoxels=recipe.layoutMinimumZSubvoxels + +recipe.boundarySealSubvoxels; + +- auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,error); ++ auto planned=VoxelForgeCore::PlanFloatingIslandCluster(recipe,endpoints,error); + if(!planned)return false; + const VoxelForgeCore::FloatingIslandFieldV1 routeField( + planned->recipe,planned->plan); +@@ -1217,6 +1218,7 @@ namespace StandaloneWorldAssemblyPrototype + :it->manifest.plan.route.size()-1; + std::size_t resolvedRoutePointCount=it->manifest.plan.route.size(); + if(it->manifest.recipe.geometryVersion>=5 ++ &&it->manifest.recipe.geometryVersion<9 + &&!it->manifest.plan.route.empty() + &&it->manifest.recipe.anchorIslandOrdinalmanifest.plan.islands.size()) + {