8#include "BlueprintEditor.h"
21#include "../json_helper.h"
25#ifndef _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
26#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
28#include <experimental/filesystem>
30namespace fs = std::experimental::filesystem;
48 , m_HasUnsavedChanges(
false)
49 , m_AssetRootPath(
"Blueprints")
52 , m_ShowMigrationDialog(
false)
101 std::cout <<
"[BlueprintEditor] Initializing Runtime Editor mode\n";
108 std::cout <<
"[BlueprintEditor] Initializing Standalone Editor mode\n";
232 std::cerr <<
"BlueprintEditor: " <<
m_LastError << std::endl;
237 std::cout <<
"BlueprintEditor: Scanning assets directory: " <<
m_AssetRootPath << std::endl;
244 std::cout <<
"BlueprintEditor: Asset scan complete" << std::endl;
249 std::cerr <<
"BlueprintEditor: " <<
m_LastError << std::endl;
253 catch (
const std::exception&
e)
255 m_LastError = std::string(
"Error scanning assets: ") +
e.what();
256 std::cerr <<
"BlueprintEditor: " <<
m_LastError << std::endl;
263 auto node = std::make_shared<AssetNode>(
271 for (
const auto&
entry : fs::directory_iterator(path))
280 if (fs::is_directory(
entry.path()))
286 else if (fs::is_regular_file(
entry.path()))
289 if (
entry.path().extension() ==
".json")
291 auto fileNode = std::make_shared<AssetNode>(
306 std::sort(
node->children.begin(),
node->children.end(),
307 [](
const std::shared_ptr<AssetNode>& a,
const std::shared_ptr<AssetNode>& b)
309 if (a->isDirectory != b->isDirectory)
310 return a->isDirectory > b->isDirectory;
311 return a->name < b->name;
314 catch (
const std::exception&
e)
316 std::cerr <<
"BlueprintEditor: Error scanning directory " << path <<
": " <<
e.what() << std::endl;
331 std::string
filename = fs::path(filepath).filename().string();
334 if (
j.contains(
"type"))
336 std::string type =
j[
"type"].get<std::string>();
339 if (
j.contains(
"blueprintType"))
341 std::string blueprintType =
j[
"blueprintType"].get<std::string>();
342 if (type != blueprintType)
344 std::cerr <<
"[BlueprintEditor] WARNING: type (" << type
345 <<
") != blueprintType (" << blueprintType <<
") in " <<
filename << std::endl;
353 if (
j.contains(
"blueprintType"))
355 std::string type =
j[
"blueprintType"].get<std::string>();
356 std::cerr <<
"[BlueprintEditor] WARNING: Using 'blueprintType' field (missing 'type') in "
363 std::cerr <<
"[BlueprintEditor] WARNING: No type information found in " <<
filename
364 <<
", using structural detection (detected: " <<
detectedType <<
")" << std::endl;
368 if (
j.contains(
"data"))
370 const json& data =
j[
"data"];
371 if (data.contains(
"rootNodeId") && data.contains(
"nodes"))
374 return "BehaviorTree";
376 if (data.contains(
"components"))
379 return "EntityPrefab";
384 if (
j.contains(
"rootNodeId") &&
j.contains(
"nodes"))
387 return "BehaviorTree";
390 if (
j.contains(
"states") ||
j.contains(
"initialState"))
396 if (
j.contains(
"components"))
399 return "EntityBlueprint";
402 std::cerr <<
"[BlueprintEditor] WARNING: Could not determine type for " <<
filename
403 <<
", defaulting to Generic" << std::endl;
406 catch (
const std::exception&
e)
408 std::string
filename = fs::path(filepath).filename().string();
409 std::cerr <<
"Error detecting asset type in " <<
filename <<
": " <<
e.what() << std::endl;
416 std::vector<AssetMetadata>
assets;
430 if (!
node->isDirectory)
441 assets.push_back(metadata);
454 std::vector<AssetMetadata>
filtered;
458 if (
asset.type == type)
470 std::vector<AssetMetadata>
results;
501 metadata.
name = fs::path(filepath).filename().string();
509 catch (
const std::exception&
e)
512 metadata.
errorMessage = std::string(
"Error loading asset: ") +
e.what();
513 std::cerr <<
"BlueprintEditor: " << metadata.
errorMessage << std::endl;
532 if (
j.contains(
"type"))
535 metadata.
type = type;
537 if (type ==
"EntityBlueprint" || type ==
"EntityPrefab")
541 else if (type ==
"BehaviorTree")
545 else if (type ==
"HFSM")
556 else if (
j.contains(
"blueprintType"))
559 metadata.
type = type;
561 std::string
filename = fs::path(filepath).filename().string();
562 std::cerr <<
"[ParseAssetMetadata] Warning: Using deprecated 'blueprintType' field in " <<
filename << std::endl;
564 if (type ==
"BehaviorTree")
568 else if (type ==
"HFSM")
572 else if (type ==
"EntityBlueprint" || type ==
"EntityPrefab")
583 else if (
j.contains(
"data"))
585 const json& data =
j[
"data"];
586 if (data.contains(
"rootNodeId") && data.contains(
"nodes"))
588 metadata.
type =
"BehaviorTree";
591 else if (data.contains(
"components"))
593 metadata.
type =
"EntityPrefab";
598 metadata.
type =
"Generic";
604 else if (
j.contains(
"rootNodeId") &&
j.contains(
"nodes"))
606 metadata.
type =
"BehaviorTree";
609 else if (
j.contains(
"states") ||
j.contains(
"initialState"))
611 metadata.
type =
"HFSM";
614 else if (
j.contains(
"components"))
616 metadata.
type =
"EntityBlueprint";
621 metadata.
type =
"Generic";
628 catch (
const std::exception&
e)
631 metadata.
errorMessage = std::string(
"JSON Parse Error: ") +
e.what();
632 std::cerr <<
"BlueprintEditor: " << metadata.
errorMessage << std::endl;
642 if (
j.contains(
"data") &&
j[
"data"].contains(
"components") &&
j[
"data"][
"components"].is_array())
644 const auto& components =
j[
"data"][
"components"];
648 for (
size_t i = 0;
i < components.size(); ++
i)
650 const auto&
comp = components[
i];
651 if (
comp.contains(
"type") &&
comp[
"type"].is_string())
659 else if (
j.contains(
"components") &&
j[
"components"].is_array())
661 const auto& components =
j[
"components"];
665 for (
size_t i = 0;
i < components.size(); ++
i)
667 const auto&
comp = components[
i];
668 if (
comp.contains(
"type") &&
comp[
"type"].is_string())
680 metadata.
description =
"Behavior Tree AI Definition";
683 if (
j.contains(
"data"))
685 const json& data =
j[
"data"];
686 if (data.contains(
"nodes") && data[
"nodes"].is_array())
688 const auto& nodes = data[
"nodes"];
692 for (
size_t i = 0;
i < nodes.size(); ++
i)
694 const auto&
node = nodes[
i];
695 if (
node.contains(
"type") &&
node[
"type"].is_string())
698 if (
node.contains(
"name") &&
node[
"name"].is_string())
711 if (data.contains(
"rootNodeId"))
713 int rootId = data[
"rootNodeId"].get<
int>();
718 else if (
j.contains(
"nodes") &&
j[
"nodes"].is_array())
720 const auto& nodes =
j[
"nodes"];
724 for (
size_t i = 0;
i < nodes.size(); ++
i)
726 const auto&
node = nodes[
i];
727 if (
node.contains(
"type") &&
node[
"type"].is_string())
730 if (
node.contains(
"name") &&
node[
"name"].is_string())
742 if (
j.contains(
"rootNodeId"))
744 int rootId =
j[
"rootNodeId"].get<
int>();
753 metadata.
description =
"Hierarchical Finite State Machine";
756 if (
j.contains(
"states") &&
j[
"states"].is_array())
758 const auto& states =
j[
"states"];
762 for (
size_t i = 0;
i < states.size(); ++
i)
764 const auto& state = states[
i];
765 if (state.contains(
"name") && state[
"name"].is_string())
775 if (
j.contains(
"initialState"))
792 catch (
const std::exception&)
809 std::cout <<
"BlueprintEditor: Entity " << entityId <<
" created (total: "
821 std::cout <<
"BlueprintEditor: Entity " << entityId <<
" destroyed (total: "
841 std::cout <<
"BlueprintEditor: Selected entity " << entityId << std::endl;
857 std::cout <<
"BlueprintEditor: Selected asset " <<
assetPath << std::endl;
867 std::cout <<
"BlueprintEditor: Opening graph " <<
assetPath <<
" in Node Graph Editor" << std::endl;
875 std::cerr <<
"BlueprintEditor: Cannot open asset type '" <<
assetType
876 <<
"' in Node Graph Editor (only BehaviorTree and HFSM supported)" << std::endl;
886 std::cerr <<
"BlueprintEditor: Failed to load graph from " <<
assetPath << std::endl;
892 std::cout <<
"BlueprintEditor: Graph loaded with ID " << graphId << std::endl;
900 const std::string& description,
901 const std::string& category)
905 m_LastError =
"No blueprint loaded to save as template";
928 std::cout <<
"Template saved: " << name <<
" (" <<
tpl.id <<
")" << std::endl;
947 std::cout <<
"Template applied: " <<
templateId << std::endl;
959 std::cout <<
"Template deleted: " <<
templateId << std::endl;
966 std::cout <<
"Templates reloaded" << std::endl;
1030 std::cout <<
"BlueprintEditor: Initializing plugins..." << std::endl;
1041 std::cout <<
"BlueprintEditor: " <<
m_Plugins.size() <<
" plugins registered" << std::endl;
1046 std::string type =
plugin->GetBlueprintType();
1048 std::cout <<
"BlueprintEditor: Registered plugin: " << type << std::endl;
1056 return it->second.get();
1064 if (
blueprint.contains(
"blueprintType"))
1066 std::string type =
blueprint[
"blueprintType"].get<std::string>();
1096 std::cerr <<
"BlueprintEditor: Directory not found: " <<
directory << std::endl;
1100 for (
const auto&
entry : fs::recursive_directory_iterator(
directory))
1102 if (fs::is_regular_file(
entry.path()) &&
entry.path().extension() ==
".json")
1108 catch (
const std::exception&
e)
1110 std::cerr <<
"BlueprintEditor: Error scanning directory: " <<
e.what() << std::endl;
1118 std::cout <<
"BlueprintEditor: Starting migration..." << std::endl;
1127 for (
const auto& path :
files)
1134 std::cerr <<
"Failed to load: " << path << std::endl;
1142 std::cout <<
"Skipping (already v2): " << path << std::endl;
1148 std::string
backupPath = path +
".v1.backup";
1151 fs::copy_file(path,
backupPath, fs::copy_options::overwrite_existing);
1153 catch (
const std::exception&
e)
1155 std::cerr <<
"Failed to create backup for " << path <<
": " <<
e.what() << std::endl;
1164 std::ofstream
file(path);
1165 if (!
file.is_open())
1167 std::cerr <<
"Failed to open file for writing: " << path << std::endl;
1175 std::cout <<
"Migrated: " << path << std::endl;
1178 catch (
const std::exception&
e)
1180 std::cerr <<
"Migration failed for " << path <<
": " <<
e.what() << std::endl;
1185 std::cout <<
"Migration complete: " <<
successCount <<
" success, "
ComponentTypeID GetComponentTypeID_Static()
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...
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
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)
class CommandStack * m_CommandStack
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
void SelectAsset(const std::string &assetPath)
void SetSelectedEntity(uint64_t entityId)
std::string m_AssetRootPath
class CommandStack * GetCommandStack()
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)
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()
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.
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)