#include "OwnerWorldLoader.h" #include "StandaloneCoreScheduler.h" #include "StreamBenchmarkPipeline.h" #include "WorldAssemblyPrototype.h" #include "../Core/src/StandaloneSnapshotAdapter.h" #include #include #include #include #include #include #include #include #include #include #include namespace { struct FOptions { std::filesystem::path DataPath; int WorkerCount=8; int ClipRadius=3; int MaxClipLevel=8; int MaxLevel=-1; int FarHorizonLOD8InteriorStepVoxels=0; bool WorldAssemblerPrototype=false; bool StreamBenchmark=false; bool StreamPipeline=false; bool StreamHash=false; bool ProfileColumnOverlap=false; bool ForceGenerate=false; bool HasCamera=false; FVector CameraMeters=FVector::ZeroVector; }; bool ParseInteger(std::string_view text,int& output) { if(text.empty())return false; char* end=nullptr; const std::string value(text); const long parsed=std::strtol(value.c_str(),&end,10); if(end==value.c_str()||*end!='\0'||parsedINT32_MAX)return false; output=static_cast(parsed); return true; } bool ParseCamera(std::string_view text,FVector& meters) { const std::string values(text); float x=0.0f,y=0.0f,z=0.0f,yaw=0.0f,pitch=0.0f; if(std::sscanf(values.c_str(),"%f,%f,%f,%f,%f",&x,&y,&z,&yaw,&pitch)!=5) return false; meters={x,y,z}; return true; } bool ParseOptions(int argc,char** argv,FOptions& options) { const std::filesystem::path executable=std::filesystem::absolute(argv[0]); options.DataPath=executable.parent_path().parent_path()/"data/owner_world.json"; for(int index=1;index(arg.size()),arg.data());return false;} options.HasCamera=true; } else if(arg.starts_with("--workers=")) {if(!readInt("--workers=",options.WorkerCount))return false;} else if(arg.starts_with("--clip-radius=")) {if(!readInt("--clip-radius=",options.ClipRadius))return false;} else if(arg.starts_with("--max-clip-level=")) {if(!readInt("--max-clip-level=",options.MaxClipLevel))return false;} else if(arg.starts_with("--max-level=")) {if(!readInt("--max-level=",options.MaxLevel))return false;} else if(arg.starts_with("--far-horizon-lod8-step=")) {if(!readInt("--far-horizon-lod8-step=",options.FarHorizonLOD8InteriorStepVoxels))return false;} else if(arg.starts_with("--process-affinity=")||arg.starts_with("--process-priority=") ||arg.starts_with("--frame-limit=")) { // These viewer switches are accepted for command-line parity. The caller pins // this headless process with taskset, and a headless run has no frame limiter. } else if(!arg.empty()&&arg.front()!='-')options.DataPath=std::filesystem::path(arg); else {std::fprintf(stderr,"Unknown option: %.*s\n",static_cast(arg.size()),arg.data());return false;} } options.WorkerCount=std::clamp(options.WorkerCount,1,16); options.ClipRadius=std::clamp(options.ClipRadius,1,64); options.MaxClipLevel=std::clamp(options.MaxClipLevel,0,8); options.FarHorizonLOD8InteriorStepVoxels=std::max(0,options.FarHorizonLOD8InteriorStepVoxels); if(!options.WorldAssemblerPrototype||!options.StreamBenchmark||!options.StreamPipeline) { std::fprintf(stderr,"This headless target runs the world-assembler streaming pipeline; pass --world-assembler-prototype --stream-benchmark --stream-pipeline.\n"); return false; } return true; } } int main(int argc,char** argv) { using Clock=std::chrono::steady_clock; const auto coldStart=Clock::now(); FOptions options; if(!ParseOptions(argc,argv,options))return 2; std::printf("[HeadlessConfig] data=%s workers=%d clip_radius=%d max_clip_level=%d max_level=%d hash=%s column_overlap=%s force_generate=%s\n", options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel, options.MaxLevel,options.StreamHash?"on":"off",options.ProfileColumnOverlap?"on":"off", options.ForceGenerate?"on":"off"); std::printf("[ColdTimeline] stage=owner_data_load begin_ms=%.3f\n", std::chrono::duration(Clock::now()-coldStart).count()); std::string error; auto ownerWorld=std::make_shared(); VFJson::Value ownerJsonRoot; { std::ifstream jsonFile(options.DataPath,std::ios::binary); if(!jsonFile) {std::fprintf(stderr,"Owner data load failed: cannot open %s\n",options.DataPath.string().c_str());return 2;} std::string ownerJsonText{std::istreambuf_iterator(jsonFile),std::istreambuf_iterator{}}; if(!jsonFile&&!jsonFile.eof()) {std::fprintf(stderr,"Owner data load failed: read error for %s\n",options.DataPath.string().c_str());return 2;} if(!VFJson::Parser(std::move(ownerJsonText)).Parse(ownerJsonRoot,error)) {std::fprintf(stderr,"Owner data load failed: %s\n",error.c_str());return 2;} } if(!StandaloneOwnerLoader::Resolve(ownerJsonRoot,*ownerWorld,error)) {std::fprintf(stderr,"Owner data load failed: %s\n",error.c_str());return 2;} std::printf("[ColdTimeline] stage=owner_data_load end_ms=%.3f\n", std::chrono::duration(Clock::now()-coldStart).count()); const int seed=ownerWorld->Seed; const int exportedMaxClipLevel=ownerWorld->MaxClipLevel; ownerWorld->ClipRadius=options.ClipRadius; ownerWorld->Settings->ClipRadius=options.ClipRadius; ownerWorld->MaxClipLevel=options.MaxClipLevel; ownerWorld->Settings->MaxClipLevel=options.MaxClipLevel; if(exportedMaxClipLevel!=options.MaxClipLevel) std::printf("Standalone max clip level override: exported=%d -> max_level=%d.\n", exportedMaxClipLevel,options.MaxClipLevel); FVector cameraVoxels=ownerWorld->ViewerStartVoxels; if(options.HasCamera) cameraVoxels=FVector(options.CameraMeters.X*4.0f,options.CameraMeters.Y*4.0f,options.CameraMeters.Z*4.0f); const auto planStart=Clock::now(); if(!StandaloneWorldAssemblyPrototype::AttachRound1WorldAssemblyPrototype( *ownerWorld,ownerJsonRoot,error,{})) {std::fprintf(stderr,"World assembler prototype setup failed: %s\n",error.c_str());return 2;} std::printf("[WorldAssemblerPlanTiming] total_ms=%.3f status=PASS\n", std::chrono::duration(Clock::now()-planStart).count()); auto snapshot=VoxelForgeCore::MakeStandaloneSnapshotFromParsedJson(ownerJsonRoot,seed, options.ClipRadius,options.MaxClipLevel,false,false,error); if(!snapshot) {std::fprintf(stderr,"Shape interval build failed: %s\n",error.c_str());return 2;} FStandaloneCoreScheduler scheduler; return StandaloneStreamBenchmark::RunPipelinedStreamingBenchmark(ownerWorld,snapshot, cameraVoxels,options.WorkerCount,options.MaxLevel,VoxelForgeCore::DetailVariant::coherent, scheduler,options.StreamHash,false,false,false,false,coldStart, options.ProfileColumnOverlap,options.FarHorizonLOD8InteriorStepVoxels); }