wip(assembler): M3 round 4 -- plans built inside Unreal from the owner's strates; no import needed
SurfaceWorld and FloatingIslands plans generate at startup from the resolved seed, strate types and heights (hashed, cached). Imported JSON is an opt-in override (off by default); an invalid override shows an on-screen notice and falls back to legacy. BuildHost on staged owner assets: SurfaceWorld cross-system checks, islands at owner height (route, collision, anchors, BP edits), seed 137501, W1/W8/W16, Core-off legacy. XY-column cache verified (hashes kept); island LOD0 p95 91 ms vs 133 ms sprint deadline. Not exercised: season-seed path (no Season in staged settings), rendered notice pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
48fa028086
commit
111cf3d262
21 files changed
+1934
-391
No files matched your search
@@ -342,6 +342,11 @@ namespace world_assembly_detail
|
||||
HashWord(hash, static_cast<std::uint32_t>(stratum.strateIndex), 4);
|
||||
HashWord(hash, static_cast<std::uint32_t>(stratum.topChunkZ), 4);
|
||||
HashWord(hash, static_cast<std::uint32_t>(stratum.bottomChunkZ), 4);
|
||||
if (stratum.hasRecipeOverride)
|
||||
{
|
||||
HashWord(hash, 0x5354524154455250ull, 8);
|
||||
HashWord(hash, HashRecipe(stratum.recipeOverride), 8);
|
||||
}
|
||||
HashWord(hash, static_cast<std::uint64_t>(stratum.capHeightSubvoxels), 8);
|
||||
HashWord(hash, static_cast<std::uint64_t>(stratum.capUndulationSubvoxels), 8);
|
||||
HashWord(hash, static_cast<std::uint64_t>(stratum.capWavelengthSubvoxels), 8);
|
||||
@@ -554,6 +559,65 @@ namespace world_assembly_detail
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::string RecipeJson(const WorldAssemblyRecipeV1& recipe)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "{\"schema_version\":" << recipe.schemaVersion
|
||||
<< ",\"evaluator_version\":" << recipe.evaluatorVersion
|
||||
<< ",\"units_per_voxel\":" << recipe.unitsPerVoxel
|
||||
<< ",\"seed\":" << recipe.seed
|
||||
<< ",\"biome_cell_size_q\":" << recipe.biomeCellSizeSubvoxels
|
||||
<< ",\"biome_warp_q\":" << recipe.biomeWarpSubvoxels
|
||||
<< ",\"biome_warp_wavelength_q\":" << recipe.biomeWarpWavelengthSubvoxels
|
||||
<< ",\"biome_border_blend_q\":" << recipe.biomeBorderBlendSubvoxels;
|
||||
if (recipe.evaluatorVersion >= 2)
|
||||
out << ",\"climate_relief_wavelength_q\":" << recipe.biomeClimateReliefWavelengthSubvoxels
|
||||
<< ",\"climate_moisture_wavelength_q\":" << recipe.biomeClimateMoistureWavelengthSubvoxels
|
||||
<< ",\"climate_relief_contrast_q16\":" << recipe.biomeClimateReliefContrastQ16;
|
||||
out << ",\"content_hash\":\"" << HashText(recipe.contentHash) << "\",\"biomes\":[";
|
||||
for (std::size_t i = 0; i < recipe.biomes.size(); ++i)
|
||||
{
|
||||
const auto& biome = recipe.biomes[i];
|
||||
if (i) out << ',';
|
||||
out << "{\"id\":" << biome.stableId << ",\"base_height_q\":" << biome.baseHeightSubvoxels
|
||||
<< ",\"continent_amplitude_q\":" << biome.continentAmplitudeSubvoxels
|
||||
<< ",\"continent_wavelength_q\":" << biome.continentWavelengthSubvoxels
|
||||
<< ",\"mountain_amplitude_q\":" << biome.mountainAmplitudeSubvoxels
|
||||
<< ",\"mountain_wavelength_q\":" << biome.mountainWavelengthSubvoxels
|
||||
<< ",\"detail_amplitude_q\":" << biome.detailAmplitudeSubvoxels
|
||||
<< ",\"detail_wavelength_q\":" << biome.detailWavelengthSubvoxels;
|
||||
if (recipe.evaluatorVersion >= 2)
|
||||
out << ",\"relief_min_q16\":" << biome.reliefMinimumQ16
|
||||
<< ",\"relief_max_q16\":" << biome.reliefMaximumQ16
|
||||
<< ",\"moisture_min_q16\":" << biome.moistureMinimumQ16
|
||||
<< ",\"moisture_max_q16\":" << biome.moistureMaximumQ16
|
||||
<< ",\"relief_strength_q16\":" << biome.reliefStrengthQ16
|
||||
<< ",\"relief_contrast_q16\":" << biome.reliefContrastQ16
|
||||
<< ",\"relief_wavelength_q\":" << biome.reliefWavelengthSubvoxels
|
||||
<< ",\"height_warp_strength_q\":" << biome.heightWarpStrengthSubvoxels
|
||||
<< ",\"height_warp_wavelength_q\":" << biome.heightWarpWavelengthSubvoxels
|
||||
<< ",\"beach_width_q\":" << biome.beachWidthSubvoxels
|
||||
<< ",\"cliff_strength_q16\":" << biome.cliffStrengthQ16
|
||||
<< ",\"cliff_slope_threshold_q16\":" << biome.cliffSlopeThresholdQ16
|
||||
<< ",\"cliff_sharpness_q16\":" << biome.cliffSharpnessQ16
|
||||
<< ",\"cliff_sample_distance_q\":" << biome.cliffSampleDistanceSubvoxels
|
||||
<< ",\"cliff_max_displacement_q\":" << biome.cliffMaximumDisplacementSubvoxels
|
||||
<< ",\"cap_enabled\":" << (biome.hasCapProfile ? "true" : "false")
|
||||
<< ",\"cap_height_q\":" << biome.capHeightSubvoxels
|
||||
<< ",\"cap_warp_strength_q\":" << biome.capWarpStrengthSubvoxels
|
||||
<< ",\"cap_warp_wavelength_q\":" << biome.capWarpWavelengthSubvoxels
|
||||
<< ",\"cap_undulation_q\":" << biome.capUndulationSubvoxels
|
||||
<< ",\"cap_wavelength_q\":" << biome.capWavelengthSubvoxels
|
||||
<< ",\"cap_detail_amplitude_q\":" << biome.capDetailAmplitudeSubvoxels
|
||||
<< ",\"cap_detail_wavelength_q\":" << biome.capDetailWavelengthSubvoxels
|
||||
<< ",\"cap_ridge_amplitude_q\":" << biome.capRidgeAmplitudeSubvoxels
|
||||
<< ",\"cap_ridge_wavelength_q\":" << biome.capRidgeWavelengthSubvoxels;
|
||||
out << '}';
|
||||
}
|
||||
out << "]}";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
inline bool ParsePlan(const VFJson::Value& value, WorldAssemblyPlanV1& plan,
|
||||
std::string& error)
|
||||
{
|
||||
@@ -573,6 +637,12 @@ namespace world_assembly_detail
|
||||
|| !ReadHash(item.Get("content_hash"), stratum.contentHash)
|
||||
|| !item.Get("features").IsArray() || !item.Get("route").IsArray())
|
||||
{ error = "world assembly stratum is missing a required fixed-scale field"; return false; }
|
||||
const VFJson::Value& recipeOverrideValue = item.Get("recipe_override");
|
||||
if (!std::holds_alternative<std::nullptr_t>(recipeOverrideValue.Data))
|
||||
{
|
||||
if (!ParseRecipe(recipeOverrideValue, stratum.recipeOverride, error)) return false;
|
||||
stratum.hasRecipeOverride = true;
|
||||
}
|
||||
const auto readOptional = [&item](const char* name, std::int64_t& output)
|
||||
{
|
||||
const VFJson::Value& field = item.Get(name);
|
||||
@@ -1402,6 +1472,13 @@ bool WorldAssemblyManifest::finalize(std::string& error)
|
||||
std::vector<std::int32_t> strateIds;
|
||||
for (auto& stratum : plan.strates)
|
||||
{
|
||||
if (stratum.hasRecipeOverride)
|
||||
{
|
||||
if (!world_assembly_detail::ValidateRecipe(stratum.recipeOverride, error)) return false;
|
||||
if (stratum.recipeOverride.seed != recipe.seed)
|
||||
{ error = "SurfaceWorld stratum recipe seed does not match the world assembly season seed"; return false; }
|
||||
stratum.recipeOverride.contentHash = world_assembly_detail::HashRecipe(stratum.recipeOverride);
|
||||
}
|
||||
if (!world_assembly_detail::ValidateStratum(stratum, error)) return false;
|
||||
if (recipe.evaluatorVersion >= 4)
|
||||
{
|
||||
@@ -1460,16 +1537,25 @@ bool WorldAssemblyManifest::validate(std::string& error) const
|
||||
const std::uint64_t expectedPlan = plan.contentHash;
|
||||
const std::uint64_t expectedManifest = contentHash;
|
||||
std::vector<std::uint64_t> expectedStrates;
|
||||
for (const auto& stratum : plan.strates) expectedStrates.push_back(stratum.contentHash);
|
||||
std::vector<std::uint64_t> expectedStratumRecipes;
|
||||
for (const auto& stratum : plan.strates)
|
||||
{
|
||||
expectedStrates.push_back(stratum.contentHash);
|
||||
expectedStratumRecipes.push_back(stratum.hasRecipeOverride
|
||||
? stratum.recipeOverride.contentHash : 0);
|
||||
}
|
||||
std::vector<std::uint64_t> expectedIslandManifests;
|
||||
for (const auto& islands : floatingIslands) expectedIslandManifests.push_back(islands.manifest.contentHash);
|
||||
if (!copy.finalize(error)) return false;
|
||||
if (expectedRecipe != copy.recipe.contentHash || expectedPlan != copy.plan.contentHash
|
||||
|| expectedManifest != copy.contentHash || expectedStrates.size() != copy.plan.strates.size()
|
||||
|| expectedStratumRecipes.size() != copy.plan.strates.size()
|
||||
|| expectedIslandManifests.size() != copy.floatingIslands.size())
|
||||
{ error = "world assembly recipe, plan or manifest content hash does not match its fields"; return false; }
|
||||
for (std::size_t i = 0; i < expectedStrates.size(); ++i)
|
||||
if (expectedStrates[i] != copy.plan.strates[i].contentHash)
|
||||
if (expectedStrates[i] != copy.plan.strates[i].contentHash
|
||||
|| expectedStratumRecipes[i] != (copy.plan.strates[i].hasRecipeOverride
|
||||
? copy.plan.strates[i].recipeOverride.contentHash : 0))
|
||||
{ error = "world assembly stratum content hash does not match its fields"; return false; }
|
||||
for (std::size_t i = 0; i < expectedIslandManifests.size(); ++i)
|
||||
if (expectedIslandManifests[i] != copy.floatingIslands[i].manifest.contentHash)
|
||||
@@ -1485,62 +1571,8 @@ std::string WorldAssemblyManifest::ToJson() const
|
||||
std::ostringstream out;
|
||||
out << "{\"schema_version\":" << value.schemaVersion
|
||||
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(value.contentHash)
|
||||
<< "\",\"recipe\":{\"schema_version\":" << value.recipe.schemaVersion
|
||||
<< ",\"evaluator_version\":" << value.recipe.evaluatorVersion
|
||||
<< ",\"units_per_voxel\":" << value.recipe.unitsPerVoxel
|
||||
<< ",\"seed\":" << value.recipe.seed
|
||||
<< ",\"biome_cell_size_q\":" << value.recipe.biomeCellSizeSubvoxels
|
||||
<< ",\"biome_warp_q\":" << value.recipe.biomeWarpSubvoxels
|
||||
<< ",\"biome_warp_wavelength_q\":" << value.recipe.biomeWarpWavelengthSubvoxels
|
||||
<< ",\"biome_border_blend_q\":" << value.recipe.biomeBorderBlendSubvoxels
|
||||
;
|
||||
if (value.recipe.evaluatorVersion >= 2)
|
||||
out << ",\"climate_relief_wavelength_q\":" << value.recipe.biomeClimateReliefWavelengthSubvoxels
|
||||
<< ",\"climate_moisture_wavelength_q\":" << value.recipe.biomeClimateMoistureWavelengthSubvoxels
|
||||
<< ",\"climate_relief_contrast_q16\":" << value.recipe.biomeClimateReliefContrastQ16;
|
||||
out
|
||||
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(value.recipe.contentHash)
|
||||
<< "\",\"biomes\":[";
|
||||
for (std::size_t i = 0; i < value.recipe.biomes.size(); ++i)
|
||||
{
|
||||
const auto& biome = value.recipe.biomes[i];
|
||||
if (i) out << ',';
|
||||
out << "{\"id\":" << biome.stableId << ",\"base_height_q\":" << biome.baseHeightSubvoxels
|
||||
<< ",\"continent_amplitude_q\":" << biome.continentAmplitudeSubvoxels
|
||||
<< ",\"continent_wavelength_q\":" << biome.continentWavelengthSubvoxels
|
||||
<< ",\"mountain_amplitude_q\":" << biome.mountainAmplitudeSubvoxels
|
||||
<< ",\"mountain_wavelength_q\":" << biome.mountainWavelengthSubvoxels
|
||||
<< ",\"detail_amplitude_q\":" << biome.detailAmplitudeSubvoxels
|
||||
<< ",\"detail_wavelength_q\":" << biome.detailWavelengthSubvoxels;
|
||||
if (value.recipe.evaluatorVersion >= 2)
|
||||
out << ",\"relief_min_q16\":" << biome.reliefMinimumQ16
|
||||
<< ",\"relief_max_q16\":" << biome.reliefMaximumQ16
|
||||
<< ",\"moisture_min_q16\":" << biome.moistureMinimumQ16
|
||||
<< ",\"moisture_max_q16\":" << biome.moistureMaximumQ16
|
||||
<< ",\"relief_strength_q16\":" << biome.reliefStrengthQ16
|
||||
<< ",\"relief_contrast_q16\":" << biome.reliefContrastQ16
|
||||
<< ",\"relief_wavelength_q\":" << biome.reliefWavelengthSubvoxels
|
||||
<< ",\"height_warp_strength_q\":" << biome.heightWarpStrengthSubvoxels
|
||||
<< ",\"height_warp_wavelength_q\":" << biome.heightWarpWavelengthSubvoxels
|
||||
<< ",\"beach_width_q\":" << biome.beachWidthSubvoxels
|
||||
<< ",\"cliff_strength_q16\":" << biome.cliffStrengthQ16
|
||||
<< ",\"cliff_slope_threshold_q16\":" << biome.cliffSlopeThresholdQ16
|
||||
<< ",\"cliff_sharpness_q16\":" << biome.cliffSharpnessQ16
|
||||
<< ",\"cliff_sample_distance_q\":" << biome.cliffSampleDistanceSubvoxels
|
||||
<< ",\"cliff_max_displacement_q\":" << biome.cliffMaximumDisplacementSubvoxels
|
||||
<< ",\"cap_enabled\":" << (biome.hasCapProfile ? "true" : "false")
|
||||
<< ",\"cap_height_q\":" << biome.capHeightSubvoxels
|
||||
<< ",\"cap_warp_strength_q\":" << biome.capWarpStrengthSubvoxels
|
||||
<< ",\"cap_warp_wavelength_q\":" << biome.capWarpWavelengthSubvoxels
|
||||
<< ",\"cap_undulation_q\":" << biome.capUndulationSubvoxels
|
||||
<< ",\"cap_wavelength_q\":" << biome.capWavelengthSubvoxels
|
||||
<< ",\"cap_detail_amplitude_q\":" << biome.capDetailAmplitudeSubvoxels
|
||||
<< ",\"cap_detail_wavelength_q\":" << biome.capDetailWavelengthSubvoxels
|
||||
<< ",\"cap_ridge_amplitude_q\":" << biome.capRidgeAmplitudeSubvoxels
|
||||
<< ",\"cap_ridge_wavelength_q\":" << biome.capRidgeWavelengthSubvoxels;
|
||||
out << '}';
|
||||
}
|
||||
out << "]},\"plan\":{\"schema_version\":" << value.plan.schemaVersion
|
||||
<< "\",\"recipe\":" << world_assembly_detail::RecipeJson(value.recipe)
|
||||
<< ",\"plan\":{\"schema_version\":" << value.plan.schemaVersion
|
||||
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(value.plan.contentHash)
|
||||
<< "\",\"strates\":[";
|
||||
for (std::size_t i = 0; i < value.plan.strates.size(); ++i)
|
||||
@@ -1563,6 +1595,9 @@ std::string WorldAssemblyManifest::ToJson() const
|
||||
<< ",\"cap_ridge_wavelength_q\":" << stratum.capRidgeWavelengthSubvoxels
|
||||
<< ",\"water_surface_q\":" << stratum.waterSurfaceSubvoxels
|
||||
<< ",\"water_enabled\":" << (stratum.waterEnabled ? "true" : "false");
|
||||
if (stratum.hasRecipeOverride)
|
||||
out << ",\"recipe_override\":"
|
||||
<< world_assembly_detail::RecipeJson(stratum.recipeOverride);
|
||||
out
|
||||
<< ",\"content_hash\":\"" << world_assembly_detail::HashText(stratum.contentHash)
|
||||
<< "\",\"features\":[";
|
||||
@@ -1626,7 +1661,7 @@ std::string WorldAssemblyManifest::ToJson() const
|
||||
|
||||
Heightfield2D::Heightfield2D(const WorldAssemblyRecipeV1& recipe,
|
||||
const WorldAssemblyStratumPlanV1& plan) noexcept
|
||||
: recipe_(&recipe), plan_(&plan)
|
||||
: recipe_(plan.hasRecipeOverride ? &plan.recipeOverride : &recipe), plan_(&plan)
|
||||
{}
|
||||
|
||||
bool Heightfield2D::isValid() const noexcept
|
||||
|
||||
Reference in new issue
Block a user