#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h" #include "MetaCoreFoundation/MetaCoreProject.h" #include "MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h" #include "MetaCoreScene/MetaCoreScene.h" #include "MetaCoreScene/MetaCoreSceneSerializer.h" #include #include #include #include #include namespace { [[nodiscard]] MetaCore::MetaCoreSceneDocument MetaCoreBuildPilotSceneDocument() { MetaCore::MetaCoreSceneDocument document; document.Name = "Main"; MetaCore::MetaCoreScene scene; MetaCore::MetaCoreGameObject mainCamera = scene.CreateGameObjectWithId(1, "Main Camera"); mainCamera.AddComponent().IsPrimary = true; mainCamera.GetComponent().Position = glm::vec3(0.0F, 2.4F, 7.0F); mainCamera.GetComponent().RotationEulerDegrees = glm::vec3(-16.0F, 0.0F, 0.0F); MetaCore::MetaCoreGameObject keyLight = scene.CreateGameObjectWithId(2, "Directional Light"); keyLight.AddComponent(); keyLight.GetComponent().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F); MetaCore::MetaCoreGameObject cube = scene.CreateGameObjectWithId(3, "Runtime Cube"); cube.AddComponent().BaseColor = glm::vec3(0.4F, 0.6F, 0.9F); cube.GetComponent().Position = glm::vec3(0.0F, 0.5F, 0.0F); MetaCore::MetaCoreGameObject valve = scene.CreateGameObjectWithId(4, "Runtime Valve"); valve.AddComponent().BaseColor = glm::vec3(0.9F, 0.55F, 0.25F); valve.GetComponent().Position = glm::vec3(-2.0F, 0.5F, 0.0F); valve.GetComponent().Scale = glm::vec3(0.65F, 0.65F, 0.65F); MetaCore::MetaCoreGameObject tank = scene.CreateGameObjectWithId(5, "Runtime Tank"); tank.AddComponent().BaseColor = glm::vec3(0.35F, 0.65F, 0.90F); tank.GetComponent().Position = glm::vec3(2.0F, 0.5F, 0.0F); tank.GetComponent().Scale = glm::vec3(1.25F, 1.25F, 1.25F); MetaCore::MetaCoreGameObject alarm = scene.CreateGameObjectWithId(6, "Runtime Alarm Light"); alarm.AddComponent().Intensity = 0.0F; alarm.GetComponent().Position = glm::vec3(0.0F, 3.0F, 0.0F); MetaCore::MetaCoreIdGenerator::EnsureAbove(6); document.GameObjects = scene.CaptureSnapshot().GameObjects; return document; } [[nodiscard]] MetaCore::MetaCoreUiDocument MetaCoreBuildPilotUiDocument() { MetaCore::MetaCoreUiDocument document; document.Name = "RuntimeDataHud"; document.ReferenceWidth = 1280; document.ReferenceHeight = 720; document.RootNodeIds = {"runtime.root"}; MetaCore::MetaCoreUiNodeDocument root; root.Id = "runtime.root"; root.Name = "Runtime Root"; root.Type = MetaCore::MetaCoreUiNodeType::Panel; root.Children = {"runtime.status"}; root.RectTransform.AnchorMin = glm::vec3(0.0F, 0.0F, 0.0F); root.RectTransform.AnchorMax = glm::vec3(1.0F, 1.0F, 0.0F); root.RectTransform.Size = glm::vec3(0.0F, 0.0F, 0.0F); root.Style.BackgroundColor = glm::vec3(0.0F, 0.0F, 0.0F); MetaCore::MetaCoreUiNodeDocument status; status.Id = "runtime.status"; status.Name = "Runtime Status"; status.Type = MetaCore::MetaCoreUiNodeType::Text; status.ParentId = "runtime.root"; status.Text = "RuntimeData waiting"; status.RectTransform.Position = glm::vec3(24.0F, 24.0F, 0.0F); status.RectTransform.Size = glm::vec3(560.0F, 40.0F, 0.0F); status.Style.FontSize = 18.0F; status.Style.TextColor = glm::vec3(0.88F, 0.94F, 1.0F); document.Nodes = {root, status}; return document; } [[nodiscard]] const MetaCore::MetaCoreGameObjectData* MetaCoreFindSceneObject( const MetaCore::MetaCoreSceneDocument& sceneDocument, MetaCore::MetaCoreId objectId ) { const auto iterator = std::find_if( sceneDocument.GameObjects.begin(), sceneDocument.GameObjects.end(), [objectId](const MetaCore::MetaCoreGameObjectData& objectData) { return objectData.Id == objectId; } ); return iterator == sceneDocument.GameObjects.end() ? nullptr : &(*iterator); } [[nodiscard]] bool MetaCoreValidatePilotRuntimeDocuments( const MetaCore::MetaCoreSceneDocument& sceneDocument, const MetaCore::MetaCoreUiDocument& uiDocument, const MetaCore::MetaCoreRuntimeBindingsDocument& bindings, std::string& error ) { for (const MetaCore::MetaCoreSceneBindingDefinition& binding : bindings.Bindings) { const MetaCore::MetaCoreGameObjectData* targetObject = MetaCoreFindSceneObject(sceneDocument, binding.TargetObjectId); if (targetObject == nullptr) { error = "Scene binding target object does not exist: " + binding.BindingId; return false; } const bool targetHasRequiredComponent = binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition || ((binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible || binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor) && targetObject->MeshRenderer.has_value()) || ((binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::LightIntensity || binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::LightColor) && targetObject->Light.has_value()); if (!targetHasRequiredComponent) { error = "Scene binding target object is missing the required component: " + binding.BindingId; return false; } } for (const MetaCore::MetaCoreUiBindingDefinition& binding : bindings.UiBindings) { if (binding.Target != MetaCore::MetaCoreRuntimeUiBindingTarget::Text) { continue; } const auto nodeIterator = std::find_if( uiDocument.Nodes.begin(), uiDocument.Nodes.end(), [&](const MetaCore::MetaCoreUiNodeDocument& node) { return node.Id == binding.TargetNodeId; } ); if (nodeIterator == uiDocument.Nodes.end() || nodeIterator->Type != MetaCore::MetaCoreUiNodeType::Text) { error = "UI binding target Text node does not exist: " + binding.BindingId; return false; } } return true; } } // namespace int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Usage: MetaCoreRuntimeConfigTool [--tcp]\n"; return 1; } const bool generateTcpConfig = argc >= 3 && std::string_view(argv[2]) == "--tcp"; MetaCore::MetaCoreTypeRegistry registry; MetaCore::MetaCoreRegisterFoundationGeneratedTypes(registry); MetaCore::MetaCoreRegisterSceneGeneratedTypes(registry); MetaCore::MetaCoreRegisterRuntimeDataGeneratedTypes(registry); const std::filesystem::path runtimeDirectory = std::filesystem::absolute(argv[1]).lexically_normal(); const std::filesystem::path projectRoot = runtimeDirectory.parent_path(); const std::filesystem::path runtimeDirectoryRelative = MetaCore::MetaCoreBuildRuntimeDirectoryRelativePath(projectRoot, runtimeDirectory); const std::filesystem::path sceneRelativePath = std::filesystem::path("Scenes") / "Main.mcscene.json"; const std::filesystem::path uiRelativePath = std::filesystem::path("Assets") / "UI" / "Hud.mcui.json"; MetaCore::MetaCoreRuntimeDataSourcesDocument sources; sources.Sources.push_back(MetaCore::MetaCoreDataSourceDefinition{ generateTcpConfig ? "tcp-source" : "replay-source", generateTcpConfig ? "tcp" : "file_replay", generateTcpConfig ? "Tcp Source" : "Replay Source", generateTcpConfig ? std::vector{ MetaCore::MetaCoreDataSourceSetting{"host", "127.0.0.1"}, MetaCore::MetaCoreDataSourceSetting{"port", "7001"} } : std::vector{ // 动态计算相对于项目根目录的播放流文件路径,替代原先硬编码的 TestProject MetaCore::MetaCoreDataSourceSetting{"file_path", (runtimeDirectoryRelative / "RuntimeReplay.mcstream").generic_string()} }, true, 1000 }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "cube.position", generateTcpConfig ? "tcp-source" : "replay-source", "cube.position", MetaCore::MetaCoreRuntimeValueType::Vec3 }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "cube.visible", generateTcpConfig ? "tcp-source" : "replay-source", "cube.visible", MetaCore::MetaCoreRuntimeValueType::Bool }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "cube.base_color", generateTcpConfig ? "tcp-source" : "replay-source", "cube.base_color", MetaCore::MetaCoreRuntimeValueType::Vec3 }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "valve.visible", generateTcpConfig ? "tcp-source" : "replay-source", "valve.visible", MetaCore::MetaCoreRuntimeValueType::Bool }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "tank.base_color", generateTcpConfig ? "tcp-source" : "replay-source", "tank.base_color", MetaCore::MetaCoreRuntimeValueType::Vec3 }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "alarm.intensity", generateTcpConfig ? "tcp-source" : "replay-source", "alarm.intensity", MetaCore::MetaCoreRuntimeValueType::Double }); sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{ "runtime.status", generateTcpConfig ? "tcp-source" : "replay-source", "runtime.status", MetaCore::MetaCoreRuntimeValueType::String }); MetaCore::MetaCoreRuntimeBindingsDocument bindings; bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.cube.position", "cube.position", 3, MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.cube.visible", "cube.visible", 3, MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.cube.base_color", "cube.base_color", 3, MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.valve.visible", "valve.visible", 4, MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.tank.base_color", "tank.base_color", 5, MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{ "binding.alarm.intensity", "alarm.intensity", 6, MetaCore::MetaCoreRuntimeBindingTarget::LightIntensity, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); bindings.UiBindings.push_back(MetaCore::MetaCoreUiBindingDefinition{ "binding.runtime.status", "runtime.status", "runtime.status", MetaCore::MetaCoreRuntimeUiBindingTarget::Text, MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue }); const MetaCore::MetaCoreSceneDocument sceneDocument = MetaCoreBuildPilotSceneDocument(); const MetaCore::MetaCoreUiDocument uiDocument = MetaCoreBuildPilotUiDocument(); std::string validationError; if (!MetaCoreValidatePilotRuntimeDocuments(sceneDocument, uiDocument, bindings, validationError)) { std::cerr << "Generated runtime config is invalid: " << validationError << '\n'; return 1; } const std::filesystem::path scenePath = projectRoot / sceneRelativePath; const std::filesystem::path uiPath = projectRoot / uiRelativePath; std::filesystem::create_directories(runtimeDirectory); if (!MetaCore::MetaCoreWriteRuntimeDataSourcesDocument(runtimeDirectory / "DataSources.mcruntime", sources, registry)) { std::cerr << "Failed to write DataSources.mcruntime\n"; return 1; } if (!MetaCore::MetaCoreWriteRuntimeBindingsDocument(runtimeDirectory / "Bindings.mcruntime", bindings, registry)) { std::cerr << "Failed to write Bindings.mcruntime\n"; return 1; } MetaCore::MetaCoreRuntimeProjectDocument runtimeProjectDocument = MetaCore::MetaCoreBuildDefaultRuntimeProjectDocument(runtimeDirectoryRelative); runtimeProjectDocument.StartupScenePath = sceneRelativePath; runtimeProjectDocument.StartupUiPath = uiRelativePath; if (!MetaCore::MetaCoreWriteRuntimeProjectDocument(runtimeDirectory / "ProjectRuntime.mcruntimecfg", runtimeProjectDocument, registry)) { std::cerr << "Failed to write ProjectRuntime.mcruntimecfg\n"; return 1; } MetaCore::MetaCoreProjectFileDocument projectDocument; projectDocument.Name = generateTcpConfig ? "MetaCoreTcpRuntimeDataPilot" : "MetaCoreRuntimeDataPilot"; projectDocument.RuntimeDirectory = runtimeDirectoryRelative; projectDocument.UiDirectory = std::filesystem::path("Assets") / "UI"; projectDocument.BuildDirectory = std::filesystem::path("Build"); projectDocument.StartupScenePath = sceneRelativePath; projectDocument.ScenePaths = {sceneRelativePath}; if (!MetaCore::MetaCoreWriteProjectFile(MetaCore::MetaCoreGetProjectFilePath(projectRoot), projectDocument)) { std::cerr << "Failed to write MetaCore.project.json\n"; return 1; } std::filesystem::create_directories(scenePath.parent_path()); if (!MetaCore::MetaCoreSceneSerializer::SaveSceneToJson(scenePath, sceneDocument, registry)) { std::cerr << "Failed to write Main.mcscene.json\n"; return 1; } std::filesystem::create_directories(uiPath.parent_path()); if (!MetaCore::MetaCoreSceneSerializer::SaveUiToJson(uiPath, uiDocument, registry)) { std::cerr << "Failed to write Hud.mcui.json\n"; return 1; } if (!generateTcpConfig) { std::ofstream replayFile(runtimeDirectory / "RuntimeReplay.mcstream", std::ios::trunc); if (!replayFile) { std::cerr << "Failed to write RuntimeReplay.mcstream\n"; return 1; } replayFile << "# emit_after_seconds data_point_id value_type payload\n" << "0.00 cube.position vec3 0.0 0.5 0.0\n" << "0.00 cube.visible bool true\n" << "0.00 cube.base_color vec3 0.4 0.6 0.9\n" << "0.00 valve.visible bool true\n" << "0.00 tank.base_color vec3 0.35 0.65 0.90\n" << "0.00 alarm.intensity double 0.0\n" << "0.00 runtime.status string RuntimeData replay started\n" << "0.50 cube.position vec3 1.5 0.5 0.0\n" << "0.50 cube.base_color vec3 0.8 0.6 0.5\n" << "0.50 tank.base_color vec3 0.20 0.80 0.25\n" << "0.50 alarm.intensity double 2.0\n" << "0.50 runtime.status string Replay frame 0.50s active\n" << "1.00 cube.visible bool false\n" << "1.00 valve.visible bool false\n" << "1.00 runtime.status string Replay toggled visibility\n" << "1.50 cube.position vec3 -1.5 0.5 0.0\n" << "1.50 cube.visible bool true\n" << "1.50 valve.visible bool true\n" << "1.50 alarm.intensity double 0.0\n" << "1.50 runtime.status string Replay restored scene\n"; } std::cout << "Runtime config generated at " << runtimeDirectory.string() << " scene=" << scenePath.string() << " ui=" << uiPath.string() << " mode=" << (generateTcpConfig ? "tcp" : "file_replay") << '\n'; return 0; }