35#include <hpp/uuid.hpp>
45fs::path app_deploy_cfg =
"app:/deploy/deploy.cfg";
46fs::path app_deploy_file =
"deploy/deploy.cfg";
47fs::path app_settings_cfg =
"app:/settings/settings.cfg";
48fs::path app_editor_cfg =
"app:/editor/editor.cfg";
49fs::path app_project_cfg =
"app:/project.cfg";
50fs::path editor_cfg = fs::persistent_path() /
"unravel" /
"editor.cfg";
51fs::path agents_template_path =
"editor:/data/project/AGENTS.template";
52fs::path claude_template_path =
"editor:/data/project/CLAUDE.template";
54auto seed_project_agent_files(
const fs::path& project_path) ->
bool
57 bool agents_ok =
false;
59 const fs::path agents_dst = project_path /
"AGENTS.md";
60 if(fs::exists(agents_template, err))
62 fs::copy_file(agents_template, agents_dst, fs::copy_options::overwrite_existing, err);
65 APPLOG_WARNING(
"Failed to seed AGENTS.md into {}: {}", project_path.string(), err.message());
74 APPLOG_WARNING(
"Agent instructions template missing: {}", agents_template.string());
77 const fs::path claude_dst = project_path /
"CLAUDE.md";
78 if(fs::exists(claude_template, err))
80 fs::copy_file(claude_template, claude_dst, fs::copy_options::overwrite_existing, err);
83 APPLOG_WARNING(
"Failed to seed CLAUDE.md into {}: {}", project_path.string(), err.message());
103 project_settings_ = {};
104 deploy_settings_ = {};
105 project_editor_settings_ = {};
137 if(!fs::exists(project_path, err))
139 APPLOG_ERROR(
"Project directory doesn't exist {0}", project_path.string());
143 APPLOG_TRACE(
"Opening project directory {0}", project_path.string());
158 set_name(project_path.filename().string());
160 save_editor_settings();
168 aw.
watch_assets(ctx,
"app:/",
true, [&ls](
size_t completed,
size_t total,
const std::string& job) ->
void
170 ls.progress(completed, total, job);
178 ls.begin_module(
"Scripting");
181 ls.begin_module(
"Project Info");
202 const bool had_info_file = load_project_info();
207 project_info_.engine_version_created = current;
208 project_info_.project_guid = hpp::to_string(
generate_uuid());
209 APPLOG_INFO(
"No project.cfg found - creating project signature "
210 "(engine {}, guid {}).",
212 project_info_.project_guid);
216 APPLOG_WARNING(
"Project was last opened with an older engine ({} < {}). "
217 "Proceeding - data loss is possible if the on-disk "
218 "format has diverged.",
219 project_info_.engine_version_opened.to_string(),
220 current.to_string());
223 project_info_.engine_version_opened = current;
227 ls.begin_module(
"Project Settings");
228 load_project_settings();
229 save_project_settings(ctx);
231 ls.begin_module(
"Deploy Settings");
232 load_deploy_settings();
233 save_deploy_settings();
235 ls.begin_module(
"Project Editor Settings");
236 load_project_editor_settings();
238 ls.begin_module(
"Scene");
240 auto opened_scene = project_editor_settings_.scene.opened_scene;
241 auto startup_scene = project_settings_.standalone.startup_scene;
243 bool scene_loaded =
false;
248 if(!scene_loaded && startup_scene)
257 auto& ev = ctx.get_cached<
events>();
314 return project_editor_settings_;
319 return project_info_;
324 return project_info_;
333 const fs::path info_path = project_path /
"project.cfg";
336 if(!fs::exists(info_path, ec) || ec)
338 report.
status = project_compat::no_info_file;
346 APPLOG_WARNING(
"project.cfg at {} exists but could not be parsed. "
347 "It will be regenerated on open.",
349 report.
status = project_compat::no_info_file;
356 report.
status = project_compat::engine_older;
360 report.
status = project_compat::ok;
368 if(fs::exists(project_path, err) && !fs::is_empty(project_path, err))
370 APPLOG_ERROR(
"Project directory already exists and is not empty {0}", project_path.string());
374 fs::create_directories(project_path, err);
378 APPLOG_ERROR(
"Failed to create project directory {0}", project_path.string());
382 seed_project_agent_files(project_path);
391 if(!has_open_project())
393 APPLOG_WARNING(
"Cannot regenerate agent files: no project is open");
397 return seed_project_agent_files(project_path);
400void project_manager::fixup_editor_settings_on_save()
407 if(std::find_if(std::begin(rp),
411 return project_path.generic_string() == prj;
414 rp.emplace_back(std::move(project_path));
417 std::sort(std::begin(rp),
419 [](
const auto& lhs_path,
const auto& rhs_path)
422 auto lhs_time = fs::last_write_time(lhs_path / app_deploy_file, ec);
423 auto rhs_time = fs::last_write_time(rhs_path / app_deploy_file, ec);
425 return lhs_time > rhs_time;
429void project_manager::fixup_editor_settings_on_load()
436 auto iter = std::begin(
items);
437 while(iter !=
items.end())
441 if(!fs::exists(item, err))
443 iter =
items.erase(iter);
456 const fs::path config = editor_cfg;
457 if(!fs::exists(config, err))
463 APPLOG_INFO(
"Loading editor settings {}", config.string());
466 fixup_editor_settings_on_load();
473 fixup_editor_settings_on_save();
476 fs::create_directories(editor_cfg.parent_path(), err);
478 const fs::path config = editor_cfg;
484 return project_name_;
489 project_name_ =
name;
494 return project_settings_;
499 return deploy_settings_;
504 return editor_settings_;
532 parser.
set_optional<std::string>(
"p",
"project",
"",
"Project folder to open.");
538 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
541 if(parser.try_get(
"project", project) && !project.empty())
543 if(project ==
"recent")
545 const auto&
items = editor_settings_.projects.recent_projects;
548 fs::path project_path =
items.front();
549 return open_project(ctx, project_path);
554 fs::path project_path = project;
555 return open_project(ctx, project_path);
564 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
void set_optional(const std::string &name, const std::string &alternative, T defaultValue, const std::string &description="", bool dominant=false)
void watch_assets(rtti::context &ctx, const std::string &protocol, bool wait=false, const on_wait_progress_t &on_progress=nullptr)
void unwatch_assets(rtti::context &ctx, const std::string &protocol)
auto regenerate_agent_files() -> bool
void save_editor_settings()
auto inspect_project(const fs::path &project_path) const -> project_compat_report
void load_editor_settings()
auto init(rtti::context &ctx, const cmd_line::parser &parser) -> bool
auto get_project_editor_settings() -> project_editor_settings &
auto get_deploy_settings() -> deploy_settings &
auto get_project_info() -> project_info &
auto load_project_info() -> bool
project_manager(rtti::context &ctx, cmd_line::parser &parser)
void save_deploy_settings()
void load_project_settings()
void set_name(const std::string &name)
auto has_open_project() const -> bool
auto get_editor_settings() -> editor_settings &
auto open_project(rtti::context &ctx, const fs::path &project_path) -> bool
void load_deploy_settings()
auto deinit(rtti::context &ctx) -> bool
void create_project(rtti::context &ctx, const fs::path &project_path)
auto get_settings() -> settings &
void load_project_editor_settings()
void save_project_editor_settings()
void save_project_settings(rtti::context &ctx)
auto get_name() const -> const std::string &
void close_project(rtti::context &ctx)
#define APPLOG_WARNING(...)
#define APPLOG_ERROR(...)
#define APPLOG_TRACE(...)
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_compiled_directory(const std::string &prefix={}) -> std::string
auto get_meta_directory(const std::string &prefix={}) -> std::string
bool add_path_protocol(const std::string &protocol, const path &dir)
Allows us to map a protocol to a specific directory. A path protocol gives the caller the ability to ...
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
Hash specialization for batch_key to enable use in std::unordered_map.
auto atomic_save_to_file(const fs::path &key, const asset_handle< T > &obj) -> bool
auto generate_uuid() -> hpp::uuid
void load_from_file(const std::string &absolute_path, animation_clip &obj)
auto is_from_older_engine(const version::engine_version &on_disk, const version::engine_version &running) -> bool
auto get_current() -> engine_version
auto add(Args &&... args) -> T &
Manages the entity-component-system (ECS) operations for the ACE framework.
void unload_scene()
Unloads the current scene.
static auto open_scene_from_asset(rtti::context &ctx, const asset_handle< scene_prefab > &asset) -> bool
static auto new_scene(rtti::context &ctx) -> bool
static void generate_script_workspace()
std::vector< fs::path > recent_projects
struct unravel::editor_settings::projects_settings projects
struct unravel::editor_settings::debugger_settings debugger
hpp::event< void(rtti::context &)> on_project_opened
hpp::event< void(rtti::context &, const std::string &protocol, uint64_t version)> on_script_recompile
void begin_module(const std::string &module_name)
version::engine_version engine_version_opened
void set_debug_config(const std::string &address, uint32_t port, uint32_t loglevel)
auto load_app_domain(rtti::context &ctx, bool recompile) -> bool
void set_cache_directory(const fs::path &cache_dir)
Sets the directory for persistent thumbnail caching.
hpp::event< void(rtti::context &)> on_close_project
System responsible for managing user interface components and rendering.