8#include "BlueprintEditor.h"
24#include "../TaskSystem/TaskGraphLoader.h"
25#include "../Core/AssetManager.h"
26#include "../json_helper.h"
27#include "../system/system_utils.h"
31#ifndef _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
32#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
34#include <experimental/filesystem>
36namespace fs = std::experimental::filesystem;
54 , m_HasUnsavedChanges(
false)
55 , m_AssetRootPath(
"Blueprints")
56 , m_GamedataRootPath(
"Gamedata")
59 , m_ShowMigrationDialog(
false)
111 std::cout <<
"[BlueprintEditor] Initializing Runtime Editor mode\n";
118 std::cout <<
"[BlueprintEditor] Initializing Standalone Editor mode\n";
253 std::cerr <<
"BlueprintEditor: " <<
m_LastError << std::endl;
259 auto virtualRoot = std::make_shared<AssetNode>(
"Assets",
".",
true);
261 std::cout <<
"BlueprintEditor: Scanning assets directory: " <<
m_AssetRootPath << std::endl;
269 std::cout <<
"BlueprintEditor: Blueprints scan complete" << std::endl;
273 std::cerr <<
"BlueprintEditor: Blueprints directory not found: "
277 catch (
const std::exception&
e)
279 m_LastError = std::string(
"Error scanning blueprints: ") +
e.what();
280 std::cerr <<
"BlueprintEditor: " <<
m_LastError << std::endl;
286 std::cout <<
"BlueprintEditor: Scanning gamedata directory: "
294 std::cout <<
"BlueprintEditor: Gamedata scan complete" << std::endl;
298 std::cerr <<
"BlueprintEditor: Gamedata directory not found: "
302 catch (
const std::exception&
e)
304 std::cerr <<
"BlueprintEditor: Error scanning gamedata: " <<
e.what() << std::endl;
317 auto node = std::make_shared<AssetNode>(
325 for (
const auto&
entry : fs::directory_iterator(path))
334 if (fs::is_directory(
entry.path()))
340 else if (fs::is_regular_file(
entry.path()))
343 const std::string
ext =
entry.path().extension().string();
344 if (
ext ==
".json" ||
ext ==
".ats")
346 auto fileNode = std::make_shared<AssetNode>(
361 std::sort(
node->children.begin(),
node->children.end(),
362 [](
const std::shared_ptr<AssetNode>&
a,
const std::shared_ptr<AssetNode>&
b)
364 if (a->isDirectory != b->isDirectory)
365 return a->isDirectory > b->isDirectory;
366 return a->name < b->name;
369 catch (
const std::exception&
e)
371 std::cerr <<
"BlueprintEditor: Error scanning directory " << path <<
": " <<
e.what() << std::endl;
383 const std::string
ext = fs::path(filepath).extension().string();
390 if (
j.contains(
"graphType"))
391 return j[
"graphType"].get<std::string>();
401 std::string
filename = fs::path(filepath).filename().string();
407 if (
j.contains(
"graphType") &&
j[
"graphType"].is_string())
409 return j[
"graphType"].get<std::string>();
413 if (
j.contains(
"type"))
415 std::string type =
j[
"type"].get<std::string>();
418 if (
j.contains(
"blueprintType"))
420 std::string blueprintType =
j[
"blueprintType"].get<std::string>();
421 if (type != blueprintType)
423 std::cerr <<
"[BlueprintEditor] WARNING: type (" << type
424 <<
") != blueprintType (" << blueprintType <<
") in " <<
filename << std::endl;
432 if (
j.contains(
"blueprintType"))
434 std::string type =
j[
"blueprintType"].get<std::string>();
435 std::cerr <<
"[BlueprintEditor] WARNING: Using 'blueprintType' field (missing 'type') in "
442 std::cerr <<
"[BlueprintEditor] WARNING: No type information found in " <<
filename
443 <<
", using structural detection (detected: " <<
detectedType <<
")" << std::endl;
447 if (
j.contains(
"data"))
449 const json& data =
j[
"data"];
450 if (data.contains(
"rootNodeId") && data.contains(
"nodes"))
453 return "BehaviorTree";
455 if (data.contains(
"components"))
458 return "EntityPrefab";
463 if (
j.contains(
"rootNodeId") &&
j.contains(
"nodes"))
466 return "BehaviorTree";
469 if (
j.contains(
"states") ||
j.contains(
"initialState"))
476 if (
j.contains(
"schema_version") ||
j.contains(
"ExecConnections") ||
j.contains(
"DataConnections"))
479 return "VisualScript";
482 if (
j.contains(
"components"))
485 return "EntityBlueprint";
488 std::cerr <<
"[BlueprintEditor] WARNING: Could not determine type for " <<
filename
489 <<
", defaulting to Generic" << std::endl;
492 catch (
const std::exception&
e)
494 std::string
filename = fs::path(filepath).filename().string();
495 std::cerr <<
"Error detecting asset type in " <<
filename <<
": " <<
e.what() << std::endl;
507 std::cout <<
"[BlueprintEditor] PreloadATSGraphs: scanning "
519 std::vector<std::shared_ptr<AssetNode>>
stack;
521 while (!
stack.empty())
526 if (!
node->isDirectory)
528 const std::string&
p =
node->fullPath;
529 if (
p.size() > 4 &&
p.substr(
p.size() - 4) ==
".ats")
545 std::vector<std::string> errors;
549 std::cout <<
"[BlueprintEditor] PreloadATSGraphs: OK " << path << std::endl;
554 std::cerr <<
"[BlueprintEditor] PreloadATSGraphs: FAIL " << path << std::endl;
555 for (
const auto&
err : errors)
556 std::cerr <<
" " <<
err << std::endl;
561 std::cout <<
"[BlueprintEditor] PreloadATSGraphs complete: "
562 <<
loaded <<
" loaded, " << failed <<
" failed" << std::endl;
567 std::vector<AssetMetadata>
assets;
581 if (!
node->isDirectory)
592 assets.push_back(metadata);
605 std::vector<AssetMetadata>
filtered;
609 if (
asset.type == type)
621 std::vector<AssetMetadata>
results;
662 metadata.
name = fs::path(filepath).filename().string();
670 catch (
const std::exception&
e)
673 metadata.
errorMessage = std::string(
"Error loading asset: ") +
e.what();
674 std::cerr <<
"BlueprintEditor: " << metadata.
errorMessage << std::endl;
696 if (
j.contains(
"type"))
699 metadata.
type = type;
701 if (type ==
"EntityBlueprint" || type ==
"EntityPrefab")
705 else if (type ==
"BehaviorTree")
709 else if (type ==
"HFSM")
720 else if (
j.contains(
"blueprintType"))
723 metadata.
type = type;
725 std::string
filename = fs::path(filepath).filename().string();
726 std::cerr <<
"[ParseAssetMetadata] Warning: Using deprecated 'blueprintType' field in " <<
filename << std::endl;
728 if (type ==
"BehaviorTree")
732 else if (type ==
"HFSM")
736 else if (type ==
"EntityBlueprint" || type ==
"EntityPrefab")
747 else if (
j.contains(
"data"))
749 const json& data =
j[
"data"];
750 if (data.contains(
"rootNodeId") && data.contains(
"nodes"))
752 metadata.
type =
"BehaviorTree";
755 else if (data.contains(
"components"))
757 metadata.
type =
"EntityPrefab";
762 metadata.
type =
"Generic";
768 else if (
j.contains(
"rootNodeId") &&
j.contains(
"nodes"))
770 metadata.
type =
"BehaviorTree";
773 else if (
j.contains(
"states") ||
j.contains(
"initialState"))
775 metadata.
type =
"HFSM";
778 else if (
j.contains(
"components"))
780 metadata.
type =
"EntityBlueprint";
785 metadata.
type =
"Generic";
792 catch (
const std::exception&
e)
795 metadata.
errorMessage = std::string(
"JSON Parse Error: ") +
e.what();
796 std::cerr <<
"BlueprintEditor: " << metadata.
errorMessage << std::endl;
806 if (
j.contains(
"data") &&
j[
"data"].contains(
"components") &&
j[
"data"][
"components"].is_array())
808 const auto& components =
j[
"data"][
"components"];
812 for (
size_t i = 0;
i < components.size(); ++
i)
814 const auto&
comp = components[
i];
815 if (
comp.contains(
"type") &&
comp[
"type"].is_string())
823 else if (
j.contains(
"components") &&
j[
"components"].is_array())
825 const auto& components =
j[
"components"];
829 for (
size_t i = 0;
i < components.size(); ++
i)
831 const auto&
comp = components[
i];
832 if (
comp.contains(
"type") &&
comp[
"type"].is_string())
844 metadata.
description =
"Behavior Tree AI Definition";
847 if (
j.contains(
"data"))
849 const json& data =
j[
"data"];
850 if (data.contains(
"nodes") && data[
"nodes"].is_array())
852 const auto& nodes = data[
"nodes"];
856 for (
size_t i = 0;
i < nodes.size(); ++
i)
858 const auto&
node = nodes[
i];
859 if (
node.contains(
"type") &&
node[
"type"].is_string())
862 if (
node.contains(
"name") &&
node[
"name"].is_string())
865 metadata.
nodes.push_back(nodeName +
" (" + nodeType +
")");
869 metadata.
nodes.push_back(nodeType);
875 if (data.contains(
"rootNodeId"))
877 int rootId = data[
"rootNodeId"].get<
int>();
882 else if (
j.contains(
"nodes") &&
j[
"nodes"].is_array())
884 const auto& nodes =
j[
"nodes"];
888 for (
size_t i = 0;
i < nodes.size(); ++
i)
890 const auto&
node = nodes[
i];
891 if (
node.contains(
"type") &&
node[
"type"].is_string())
894 if (
node.contains(
"name") &&
node[
"name"].is_string())
897 metadata.
nodes.push_back(nodeName +
" (" + nodeType +
")");
901 metadata.
nodes.push_back(nodeType);
906 if (
j.contains(
"rootNodeId"))
908 int rootId =
j[
"rootNodeId"].get<
int>();
917 metadata.
description =
"Hierarchical Finite State Machine";
920 if (
j.contains(
"states") &&
j[
"states"].is_array())
922 const auto& states =
j[
"states"];
926 for (
size_t i = 0;
i < states.size(); ++
i)
928 const auto& state = states[
i];
929 if (state.contains(
"name") && state[
"name"].is_string())
939 if (
j.contains(
"initialState"))
956 catch (
const std::exception&)
973 std::cout <<
"BlueprintEditor: Entity " << entityId <<
" created (total: "
985 std::cout <<
"BlueprintEditor: Entity " << entityId <<
" destroyed (total: "
1005 std::cout <<
"BlueprintEditor: Selected entity " << entityId << std::endl;
1027 std::cout <<
"BlueprintEditor: Selected asset " <<
assetPath << std::endl;
1080 SYSTEM_LOG <<
"BlueprintEditor: Graph loaded with ID " << graphId <<
"\n";
1088 const std::string& description,
1089 const std::string& category)
1093 m_LastError =
"No blueprint loaded to save as template";
1116 std::cout <<
"Template saved: " << name <<
" (" <<
tpl.id <<
")" << std::endl;
1135 std::cout <<
"Template applied: " <<
templateId << std::endl;
1147 std::cout <<
"Template deleted: " <<
templateId << std::endl;
1154 std::cout <<
"Templates reloaded" << std::endl;
1218 std::cout <<
"BlueprintEditor: Initializing plugins..." << std::endl;
1229 std::cout <<
"BlueprintEditor: " <<
m_Plugins.size() <<
" plugins registered" << std::endl;
1234 std::string type =
plugin->GetBlueprintType();
1236 std::cout <<
"BlueprintEditor: Registered plugin: " << type << std::endl;
1244 return it->second.get();
1252 if (
blueprint.contains(
"blueprintType"))
1254 std::string type =
blueprint[
"blueprintType"].get<std::string>();
1284 std::cerr <<
"BlueprintEditor: Directory not found: " <<
directory << std::endl;
1288 for (
const auto&
entry : fs::recursive_directory_iterator(
directory))
1290 if (fs::is_regular_file(
entry.path()) &&
entry.path().extension() ==
".json")
1296 catch (
const std::exception&
e)
1298 std::cerr <<
"BlueprintEditor: Error scanning directory: " <<
e.what() << std::endl;
1306 std::cout <<
"BlueprintEditor: Starting migration..." << std::endl;
1316 for (
const auto& path :
files)
1323 std::cerr <<
"Failed to load: " << path << std::endl;
1334 std::cout <<
"Skipping (already current format): " << path << std::endl;
1340 std::string
backupPath = path +
".pre_v5.backup";
1343 fs::copy_file(path,
backupPath, fs::copy_options::overwrite_existing);
1345 catch (
const std::exception&
e)
1347 std::cerr <<
"Failed to create backup for " << path <<
": " <<
e.what() << std::endl;
1356 std::cout <<
"[Migration] v1->v2: " << path << std::endl;
1363 std::cout <<
"[Migration] v2->v5 (subgraph): " << path << std::endl;
1367 std::ofstream
file(path);
1368 if (!
file.is_open())
1370 std::cerr <<
"Failed to open file for writing: " << path << std::endl;
1378 std::cout <<
"Migrated: " << path << std::endl;
1381 catch (
const std::exception&
e)
1383 std::cerr <<
"Migration failed for " << path <<
": " <<
e.what() << std::endl;
1388 std::cout <<
"Migration complete: " <<
successCount <<
" success, "
1404 if (!
file.is_open())
1406 std::cout <<
"[BlueprintEditor] Config file not found: " <<
configPath
1407 <<
", creating default config" << std::endl;
1412 m_Config[
"editor_mode"] =
"standalone";
1414 m_Config[
"window"] = json::object();
1415 m_Config[
"window"][
"width"] = 1920;
1416 m_Config[
"window"][
"height"] = 1080;
1417 m_Config[
"window"][
"maximized"] =
true;
1419 m_Config[
"panels"] = json::object();
1420 m_Config[
"panels"][
"asset_browser"] = json::object();
1421 m_Config[
"panels"][
"asset_browser"][
"visible"] =
true;
1422 m_Config[
"panels"][
"asset_browser"][
"width"] = 400;
1423 m_Config[
"panels"][
"node_graph"] = json::object();
1424 m_Config[
"panels"][
"node_graph"][
"visible"] =
true;
1425 m_Config[
"panels"][
"inspector"] = json::object();
1426 m_Config[
"panels"][
"inspector"][
"visible"] =
true;
1427 m_Config[
"panels"][
"inspector"][
"width"] = 400;
1429 m_Config[
"layout"] = json::object();
1430 m_Config[
"layout"][
"mode"] =
"fixed";
1431 m_Config[
"layout"][
"asset_browser_width"] = 400.0f;
1432 m_Config[
"layout"][
"inspector_width"] = 400.0f;
1433 m_Config[
"layout"][
"min_panel_width"] = 200.0f;
1434 m_Config[
"layout"][
"splitter_size"] = 8.0f;
1443 std::cout <<
"[BlueprintEditor] Loaded config from: " <<
configPath << std::endl;
1446 catch (
const std::exception&
e)
1448 std::cerr <<
"[BlueprintEditor] Error loading config: " <<
e.what() << std::endl;
1458 if (!
file.is_open())
1460 std::cerr <<
"[BlueprintEditor] Failed to open config file for writing: "
1468 std::cout <<
"[BlueprintEditor] Saved config to: " <<
configPath << std::endl;
1471 catch (
const std::exception&
e)
1473 std::cerr <<
"[BlueprintEditor] Error saving config: " <<
e.what() << std::endl;
ComponentTypeID GetComponentTypeID_Static()
Editor mode and capabilities management.
Phase 8 — Migrates legacy blueprint data to the flat-dictionary subgraph format.
Central manager for the multi-graph tab system.
AssetID LoadTaskGraph(const std::string &path, std::vector< std::string > &outErrors)
Loads a TaskGraphTemplate from path and caches it.
static AssetManager & Get()
Returns the singleton AssetManager instance.
BlueprintEditorPlugin - Base interface for blueprint type plugins Each plugin handles a specific blue...
BlueprintEditor Singleton Backend Manages all business logic, state, and data for the Blueprint Edito...
void InvalidateAssetMetadataCache()
std::vector< AssetMetadata > SearchAssets(const std::string &query) const
void RegisterPlugin(std::unique_ptr< class BlueprintEditorPlugin > plugin)
void ParseEntityBlueprint(const json &j, AssetMetadata &metadata)
void MigrateAllBlueprints()
std::vector< std::string > ScanBlueprintFiles(const std::string &directory)
static BlueprintEditor & Instance()
std::map< std::string, std::unique_ptr< class BlueprintEditorPlugin > > m_Plugins
bool LoadBlueprint(const std::string &filepath)
std::string DetectAssetType(const std::string &filepath)
std::shared_ptr< AssetNode > ScanDirectory(const std::string &path)
std::vector< AssetMetadata > GetAssetsByType(const std::string &type) const
Blueprint::CommandStack * m_CommandStack
std::vector< uint64_t > m_RuntimeEntities
void NewBlueprint(const std::string &name, const std::string &description="")
void NotifyEntityDestroyed(uint64_t entityId)
std::string GetNextRedoDescription() const
bool HasBlueprint() const
void SetAssetRootPath(const std::string &path)
std::string GetLastCommandDescription() const
std::string m_CurrentFilepath
void NotifyEntityCreated(uint64_t entityId)
void ParseHFSM(const json &j, AssetMetadata &metadata)
bool DeleteTemplate(const std::string &templateId)
AssetMetadata GetAssetMetadata(const std::string &filepath)
bool IsAssetValid(const std::string &filepath) const
bool LoadConfig(const std::string &configPath="blueprint_editor_config.json")
void SelectAsset(const std::string &assetPath)
void SetSelectedEntity(uint64_t entityId)
bool SaveConfig(const std::string &configPath="blueprint_editor_config.json")
std::string m_AssetRootPath
Blueprint::CommandStack * GetCommandStack()
std::string m_GamedataRootPath
bool SaveBlueprintAs(const std::string &filepath)
class BlueprintEditorPlugin * GetPlugin(const std::string &type)
void OpenGraphInEditor(const std::string &assetPath)
void InitializeStandaloneEditor()
bool ApplyTemplate(const std::string &templateId)
std::shared_ptr< AssetNode > m_AssetTreeRoot
void ParseAssetMetadata(const std::string &filepath, AssetMetadata &metadata)
bool SaveCurrentAsTemplate(const std::string &name, const std::string &description, const std::string &category)
class BlueprintEditorPlugin * DetectPlugin(const json &blueprint)
std::vector< AssetMetadata > GetAllAssets() const
void CollectAllAssets(const std::shared_ptr< AssetNode > &node, std::vector< AssetMetadata > &assets) const
void ParseBehaviorTree(const json &j, AssetMetadata &metadata)
std::string m_SelectedAssetPath
void InitializeRuntimeEditor()
uint64_t m_SelectedEntity
Blueprint::EntityBlueprint m_CurrentBlueprint
void Update(float deltaTime)
std::map< std::string, AssetMetadata > m_AssetMetadataCache
BlueprintMigrator - Converts v1 blueprints to v2 format Handles automatic position calculation and st...
CommandStack - Manages undo/redo command history Maintains two stacks for undo and redo operations.
std::string GetNextRedoDescription() const
std::string GetLastCommandDescription() const
static EditorContext & Get()
void InitializeStandalone()
static EntityInspectorManager & Get()
static EnumCatalogManager & Get()
int LoadGraph(const std::string &filepath)
static NodeGraphManager & Get()
Converts legacy blueprint JSON to the Phase 8 subgraph flat-dict format.
static TabManager & Get()
Returns the global singleton instance.
std::string OpenFileInTab(const std::string &filePath)
Opens a file in a new tab.
void Initialize(const std::string &templatesPath="Blueprints/Templates")
std::string GetLastError() const
static TemplateManager & Get()
BlueprintTemplate CreateTemplateFromBlueprint(const json &blueprint, const std::string &name, const std::string &description, const std::string &category, const std::string &author="User")
std::string GetString(const json &j, const std::string &key, const std::string &defaultValue="")
Safely get a string value from JSON.
bool LoadJsonFromFile(const std::string &filepath, json &j)
Load and parse a JSON file.
< Provides AssetID and INVALID_ASSET_ID
void WorldBridge_UnregisterTaskCallback()
Unregisters the TaskExecutionBridge so that TaskSystem no longer publishes live state to the editor p...
uint32_t AssetID
Opaque asset identifier: 32-bit FNV-1a hash of the asset file path.
static const AssetID INVALID_ASSET_ID
Sentinel value indicating an invalid / unloaded asset.
BlueprintTemplate - Template metadata and data Stores a complete blueprint that can be reused as a te...
static EntityBlueprint LoadFromFile(const std::string &filepath)
bool SaveToFile(const std::string &filepath) const
static EntityBlueprint FromJson(const json &j)