Unravel Engine C++ Reference
Loading...
Searching...
No Matches
asset_watcher.cpp
Go to the documentation of this file.
1#include "asset_watcher.h"
2#include "engine/ui/ui_tree.h"
10#include <engine/ecs/ecs.h>
11#include <engine/ecs/prefab.h>
12#include <engine/events.h>
20
21
24
27
28#include <filesystem/watcher.h>
29#include <graphics/graphics.h>
30#include <logging/logging.h>
31
32#include <fstream>
33#include <set>
34
35namespace unravel
36{
37namespace
38{
39using namespace std::literals;
40
41template<typename T>
42auto get_job_name() -> std::string
43{
44 return fmt::format("Compiling {}", ex::get_type<T>());
45}
46
47template<typename T>
48auto checking_dependencies_job_name() -> std::string
49{
50 return fmt::format("Checking dependencies of {}", ex::get_type<T>());
51}
52
54auto needs_recompilation(const fs::path& source_file_path, const fs::path& compiled_output_path) -> bool
55{
56 fs::error_code err;
57
58 // If output doesn't exist, we need to compile
59 if(!fs::exists(compiled_output_path, err) || err)
60 {
61 return true;
62 }
63
64 // Check if manifest exists
65 auto manifest_path = asset_compiler::get_manifest_path(compiled_output_path);
66 if(!fs::exists(manifest_path, err) || err)
67 {
68 // APPLOG_TRACE("Manifest missing for {}, cannot check if recompilation is needed",
69 // compiled_output_path.string());
70 return true;
71 }
72
73 // Load manifest and check if source has changed
74 asset_compiler::asset_manifest manifest;
75 if(!asset_compiler::load_manifest(manifest_path, manifest))
76 {
77 APPLOG_TRACE("Failed to load manifest for {}, recompilation needed", compiled_output_path.string());
78 return true;
79 }
80
81 // Check if source file has changed
82 if(asset_compiler::is_source_file_changed(source_file_path, manifest))
83 {
84 APPLOG_TRACE("Source file changed for {}, recompilation needed", compiled_output_path.string());
85 return true;
86 }
87
88 // APPLOG_TRACE("Asset {} is up to date, skipping compilation", compiled_output_path.string());
89 return false;
90}
91
92template<typename T>
93void resolve_includes(const fs::path& file_path, std::set<fs::path>& processed_files)
94{
95}
96
97template<>
98void resolve_includes<gfx::shader>(const fs::path& file_path, std::set<fs::path>& processed_files)
99{
100 if(!processed_files.insert(file_path).second)
101 {
102 return; // Avoid processing the same file multiple times
103 }
104
105 std::ifstream file(file_path);
106 if(!file.is_open())
107 {
108 // std::cerr << "Failed to open file: " << filePath << '\n';
109 return;
110 }
111
112 const std::string include_keyword = "#include";
113 const std::string bgfx_include_path = "/path/to/bgfx/include"; // Assuming bgfx include path is known
114
115 std::string line;
116 while(std::getline(file, line))
117 {
118 // Trim leading whitespace
119 line.erase(0, line.find_first_not_of(" \t"));
120
121 // Check for #include directive
122 if(line.compare(0, include_keyword.length(), include_keyword) == 0)
123 {
124 // Find the start and end of the include path
125 size_t start = line.find_first_of("\"<") + 1;
126 size_t end = line.find_last_of("\">");
127
128 if(start == std::string::npos || end == std::string::npos || start >= end)
129 {
130 // std::cerr << "Invalid include directive: " << line << '\n';
131 continue;
132 }
133
134 std::string include_path = line.substr(start, end - start);
135 fs::path resolved_path;
136
137 if(line[start - 1] == '<' && line[end] == '>')
138 {
139 // Resolve system include path (e.g., bgfx_shader.sh)
140 // resolvedPath = fs::path(bgfxIncludePath) / includePath;
141 continue;
142 }
143 else
144 {
145 // Resolve local include path relative to the current file
146 resolved_path = file_path.parent_path() / include_path;
147 }
148
149 resolved_path = fs::absolute(resolved_path);
150 // std::cout << "Resolved include: " << resolvedPath << '\n';
151
152 // Recurse into the included file
153 resolve_includes<gfx::shader>(resolved_path, processed_files);
154 }
155 }
156}
157
158auto extract_href_from_link_tag(hpp::string_view link_tag) -> hpp::string_view
159{
160 size_t href_pos = link_tag.find("href=");
161 if(href_pos == std::string::npos)
162 {
163 return {};
164 }
165
166 href_pos += 5; // Move past "href="
167
168 // Find the quote character (either " or ')
169 char quote_char = link_tag[href_pos];
170 if(quote_char != '"' && quote_char != '\'')
171 {
172 return {};
173 }
174
175 href_pos++; // Move past the opening quote
176 size_t href_end = link_tag.find(quote_char, href_pos);
177 if(href_end == std::string::npos)
178 {
179 return {};
180 }
181
182 return link_tag.substr(href_pos, href_end - href_pos);
183}
184
185auto resolve_ui_tree_dependency_path(const fs::path& href_value, const fs::path& base_file_path) -> fs::path
186{
187 if(fs::has_known_protocol(href_value))
188 {
189 // Handle protocol paths like "engine:/data/ui/rml.rcss"
190 return fs::resolve_protocol(href_value);
191 }
192
193 // Resolve relative path
194 return fs::absolute(base_file_path.parent_path() / href_value);
195}
196
197template<>
198void resolve_includes<ui_tree>(const fs::path& file_path, std::set<fs::path>& processed_files)
199{
200 if(!processed_files.insert(file_path).second)
201 {
202 return; // Avoid processing the same file multiple times
203 }
204
205 std::ifstream file(file_path);
206 if(!file.is_open())
207 {
208 return;
209 }
210
211 std::string content_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
212 hpp::string_view content(content_str);
213 // Parse HTML/RML content to find <link> tags with href attributes
214 size_t pos = 0;
215 while((pos = content.find("<link", pos)) != std::string::npos)
216 {
217 // Find the end of the link tag
218 size_t tag_end = content.find('>', pos);
219 if(tag_end == std::string::npos)
220 {
221 break;
222 }
223
224 hpp::string_view link_tag = content.substr(pos, tag_end - pos + 1);
225 hpp::string_view href_value = extract_href_from_link_tag(link_tag);
226
227 if(!href_value.empty())
228 {
229 fs::path resolved_path = resolve_ui_tree_dependency_path(fs::path(href_value), file_path);
230
231 // Recurse into the included file if it's a supported dependency format
232 const auto& supported_deps = ex::get_suported_dependencies_formats<ui_tree>();
233 auto extension = resolved_path.extension().string();
234 if(std::find(supported_deps.begin(), supported_deps.end(), extension) != supported_deps.end())
235 {
236 resolve_includes<ui_tree>(resolved_path, processed_files);
237 }
238 }
239
240 pos = tag_end + 1;
241 }
242}
243
244template<typename T>
245auto has_depencency(const fs::path& file, const fs::path& dep_to_check) -> bool
246{
247 std::set<fs::path> dependecies;
248 resolve_includes<T>(file, dependecies);
249
250 return dependecies.contains(dep_to_check);
251}
252
253auto remove_meta_tag(const fs::path& synced_path) -> fs::path
254{
255 return fs::replace(synced_path, ".meta", "");
256}
257
258auto remove_meta_tag(const std::vector<fs::path>& synced_paths) -> std::vector<fs::path>
259{
260 std::decay_t<decltype(synced_paths)> reduced;
261 reduced.reserve(synced_paths.size());
262 for(const auto& synced_path : synced_paths)
263 {
264 reduced.emplace_back(remove_meta_tag(synced_path));
265 }
266 return reduced;
267}
268
269void unwatch(std::vector<uint64_t>& watchers)
270{
271 for(const auto& id : watchers)
272 {
274 }
275 watchers.clear();
276}
277
278auto get_asset_key(const fs::path& path) -> std::string
279{
281 auto data_key = fs::convert_to_protocol(p);
282 auto key =
283 fs::replace(data_key.generic_string(), ex::get_compiled_directory(), ex::get_data_directory()).generic_string();
284 return key;
285}
286
287auto get_meta_key(const fs::path& path) -> std::string
288{
290 auto data_key = fs::convert_to_protocol(p);
291 auto key =
292 fs::replace(data_key.generic_string(), ex::get_compiled_directory(), ex::get_meta_directory()).generic_string();
293 return key + ".meta";
294}
295
296auto check_files_integrity(const std::string& key, const fs::path& entry_path) -> bool
297{
298 fs::error_code ec;
299 auto key_path = fs::resolve_protocol(key);
300
301 if(!fs::exists(key_path, ec))
302 {
303 APPLOG_WARNING("{} does not exist. Cleaning up cached...", key);
304 fs::remove(entry_path, ec);
305
306 auto meta = get_meta_key(entry_path);
307 auto meta_path = fs::resolve_protocol(meta);
308 if(fs::exists(meta_path, ec))
309 {
310 APPLOG_WARNING("{} does not exist. Cleaning up meta...", key);
311 fs::remove(meta_path, ec);
312 }
313
314 return false;
315 }
316
317 return true;
318}
319
320template<typename T>
321auto watch_assets(rtti::context& ctx, const fs::path& dir, const fs::pattern_filter& filter, bool reload_async)
322 -> uint64_t
323{
324 auto& am = ctx.get_cached<asset_manager>();
325 auto& ts = ctx.get_cached<threader>();
326 auto& tm = ctx.get_cached<thumbnail_manager>();
327 auto& em = ctx.get_cached<editing_manager>();
328
329 fs::path watch_dir = fs::path(dir).make_preferred();
330
331 auto callback = [&am, &ts, &tm, &em](const auto& entries, bool is_initial_list)
332 {
333 std::set<hpp::uuid> changed;
334 std::set<hpp::uuid> removed;
335
336 for(const auto& entry : entries)
337 {
338 auto key = get_asset_key(entry.path);
339
341
342 if(entry.type == fs::file_type::regular)
343 {
345 {
346 removed.emplace(am.get_asset<T>(key).uid());
347 am.unload_asset<T>(key);
348
349 if constexpr(std::is_same<T, script>::value)
350 {
352 }
353 }
355 {
356 auto old_key = get_asset_key(entry.last_path);
357 am.rename_asset<T>(old_key, key);
358
359 if constexpr(std::is_same<T, script>::value)
360 {
362 }
363 }
364 else // created or modified
365 {
366 if(check_files_integrity(key, entry.path))
367 {
368 load_flags flags = is_initial_list ? load_flags::standard : load_flags::reload;
369 auto asset = am.get_asset<T>(key, flags);
370
371 changed.emplace(asset.uid());
372 }
373
374 if constexpr(std::is_same<T, script>::value)
375 {
377 }
378 }
379 }
380 }
381
382 if(!changed.empty() || !removed.empty())
383 {
384 tpp::invoke(tpp::main_thread::get_id(),
385 [&tm, &em, &am, changed, removed]()
386 {
387 for(const auto& uid : removed)
388 {
389 tm.remove_thumbnail(uid);
390 }
391
392 for(const auto& uid : changed)
393 {
394 tm.regenerate_thumbnail(uid);
395
396 if constexpr(std::is_same<T, prefab>::value)
397 {
398 auto asset = am.get_asset<T>(uid);
399 em.on_prefab_updated(asset);
400 }
401 }
402 });
403 }
404 };
405
406 return fs::watcher::watch(watch_dir, filter, true, true, 500ms, callback);
407}
408
409template<typename T>
410auto watch_assets_depenencies(rtti::context& ctx, const fs::path& dir, const fs::pattern_filter& filter) -> uint64_t
411{
412 auto& am = ctx.get_cached<asset_manager>();
413 auto& ts = ctx.get_cached<threader>();
414
415 fs::path watch_dir = fs::path(dir).make_preferred();
416
417 auto callback = [&am, &ts](const auto& entries, bool is_initial_list)
418 {
419 if(is_initial_list)
420 {
421 return;
422 }
423
424 for(const auto& entry : entries)
425 {
427
428 if(entry.type == fs::file_type::regular)
429 {
431 {
432 }
434 {
435 }
436 else // created or modified
437 {
438 auto task = ts.pool->schedule(checking_dependencies_job_name<T>(),
439 [&am, entry]()
440 {
441 auto assets = am.get_assets<T>();
442 for(const auto& asset : assets)
443 {
444 auto meta = am.get_metadata(asset.uid());
445 auto absolute_path = fs::resolve_protocol(meta.location);
446
447 if(has_depencency<T>(absolute_path, entry.path))
448 {
449 fs::watcher::touch(absolute_path, false);
450 }
451 }
452 });
453 }
454 }
455 }
456 };
457
458 return fs::watcher::watch(watch_dir, filter, true, true, 500ms, callback);
459}
460
461template<typename T>
462static void add_to_syncer(rtti::context& ctx,
463 fs::syncer& syncer,
464 const fs::syncer::on_entry_removed_t& on_removed,
465 const fs::syncer::on_entry_renamed_t& on_renamed)
466{
467 auto& ts = ctx.get_cached<threader>();
468 auto& am = ctx.get_cached<asset_manager>();
469
470 auto on_modified =
471 [&ts, &am](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
472 {
473 auto paths = remove_meta_tag(synced_paths);
474
475 for(const auto& output : paths)
476 {
477 // Check if recompilation is needed based on manifest
478 if(is_initial_listing && !needs_recompilation(ref_path, output))
479 {
480 continue;
481 }
482
483 auto key = get_asset_key(output);
484 if(check_files_integrity(key, output))
485 {
486 auto task = ts.pool->schedule(get_job_name<T>(),
487 [&am, ref_path, output]()
488 {
489 asset_compiler::compile<T>(am, ref_path, output);
490 });
491 }
492 }
493 };
494
495 for(const auto& type : ex::get_suported_formats<T>())
496 {
497 syncer.set_mapping(type + ".meta",
498 {".asset"},
499 on_modified,
500 on_modified,
501 on_removed,
502 on_renamed);
503 }
504}
505
506template<typename T>
507static void watch_synced(rtti::context& ctx, std::vector<uint64_t>& watchers, const fs::path& dir)
508{
509 for(const auto& type : ex::get_suported_formats<T>())
510 {
511 const auto watch_id = watch_assets<T>(ctx, dir, fs::pattern_filter("*" + type + ".asset"), true);
512 watchers.push_back(watch_id);
513 }
514}
515
516template<>
517void add_to_syncer<gfx::shader>(rtti::context& ctx,
518 fs::syncer& syncer,
519 const fs::syncer::on_entry_removed_t& on_removed,
520 const fs::syncer::on_entry_renamed_t& on_renamed)
521{
522 auto& ts = ctx.get_cached<threader>();
523 auto& am = ctx.get_cached<asset_manager>();
524
525 auto on_modified =
526 [&ts, &am](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
527 {
528 auto paths = remove_meta_tag(synced_paths);
529 if(paths.empty())
530 {
531 return;
532 }
533 const auto& platform_supported = gfx::get_renderer_platform_supported_filename_extensions();
534
535 for(const auto& output : paths)
536 {
537 auto it =
538 std::find(std::begin(platform_supported), std::end(platform_supported), output.extension().string());
539
540 if(it == std::end(platform_supported))
541 {
542 continue;
543 }
544
545 // Check if recompilation is needed based on manifest
546 if(is_initial_listing && !needs_recompilation(ref_path, output))
547 {
548 continue;
549 }
550
551 auto key = get_asset_key(output);
552 if(check_files_integrity(key, output))
553 {
554 auto task = ts.pool->schedule(get_job_name<gfx::shader>(),
555 [&am, ref_path, output]()
556 {
557 asset_compiler::compile<gfx::shader>(am, ref_path, output);
558 });
559 }
560 }
561 };
562
564 {
565 syncer.set_mapping(type + ".meta",
566 {".asset.dx11",
567 ".asset.dx12",
568 ".asset.gl",
569 ".asset.spirv"},
570 on_modified,
571 on_modified,
572 on_removed,
573 on_renamed);
574 }
575}
576
577template<>
578void watch_synced<gfx::shader>(rtti::context& ctx, std::vector<uint64_t>& watchers, const fs::path& dir)
579{
580 const auto& renderer_extension = gfx::get_current_renderer_filename_extension();
582 {
583 const auto watch_id =
584 watch_assets<gfx::shader>(ctx, dir, fs::pattern_filter("*" + type + ".asset" + renderer_extension), true);
585 watchers.push_back(watch_id);
586 }
587}
588
589} // namespace
590
591void asset_watcher::setup_directory(rtti::context& ctx, fs::syncer& syncer)
592{
593 const auto on_dir_modified =
594 [](const std::string& ext, const auto& /*ref_path*/, const auto& /*synced_paths*/, bool /*is_initial_listing*/)
595 {
596
597 };
598 const auto on_dir_removed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
599 {
600 for(const auto& synced_path : synced_paths)
601 {
602 fs::error_code err;
603 fs::remove_all(synced_path, err);
604 }
605 };
606
607 const auto on_dir_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
608 {
609 for(const auto& synced_path : synced_paths)
610 {
611 fs::error_code err;
612 fs::rename(synced_path.first, synced_path.second, err);
613 }
614 };
615 syncer.set_directory_mapping(on_dir_modified, on_dir_modified, on_dir_removed, on_dir_renamed);
616}
617
618void asset_watcher::setup_meta_syncer(rtti::context& ctx,
619 std::vector<uint64_t>& watchers,
620 fs::syncer& syncer,
621 const fs::path& data_dir,
622 const fs::path& meta_dir,
623 bool wait)
624{
625 setup_directory(ctx, syncer);
626 auto& am = ctx.get_cached<asset_manager>();
627
628 const auto on_file_removed = [&am](const std::string& ext, const auto& ref_path, const auto& synced_paths)
629 {
630 for(const auto& synced_path : synced_paths)
631 {
632 fs::error_code err;
633 fs::remove_all(synced_path, err);
634
635 am.remove_asset_info_for_path(ref_path);
636 }
637 };
638
639 const auto on_file_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
640 {
641 for(const auto& synced_path : synced_paths)
642 {
643 fs::error_code err;
644 fs::rename(synced_path.first, synced_path.second, err);
645 }
646 };
647
648 const auto on_file_modified =
649 [&am](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
650 {
651 for(const auto& synced_path : synced_paths)
652 {
653 asset_meta meta;
654 fs::error_code err;
655 if(fs::exists(synced_path, err))
656 {
657 load_from_file(synced_path.string(), meta);
658 }
659
660 if(meta.uid.is_nil())
661 {
662 auto key = fs::convert_to_protocol(ref_path).generic_string();
663 meta = am.generate_metadata(key);
664 }
665 meta.uid = am.add_asset_info_for_path(ref_path, meta, true);
666
667 save_to_file(synced_path.string(), meta);
668 }
669 };
670
671 for(const auto& asset_set : ex::get_all_formats())
672 {
673 for(const auto& type : asset_set)
674 {
675 syncer.set_mapping(type, {".meta"}, on_file_modified, on_file_modified, on_file_removed, on_file_renamed);
676 }
677 }
678
680 {
681 auto id = watch_assets_depenencies<gfx::shader>(ctx, data_dir, fs::pattern_filter("*" + dep_ex));
682 watchers.emplace_back(id);
683 }
684
685 for(const auto& dep_ex : ex::get_suported_dependencies_formats<ui_tree>())
686 {
687 auto id = watch_assets_depenencies<ui_tree>(ctx, data_dir, fs::pattern_filter("*" + dep_ex));
688 watchers.emplace_back(id);
689 }
690
691 syncer.sync(data_dir, meta_dir);
692
693 if(wait)
694 {
695 auto& ts = ctx.get_cached<threader>();
696 ts.pool->wait_all();
697 }
698}
699
700void asset_watcher::setup_cache_syncer(rtti::context& ctx,
701 std::vector<uint64_t>& watchers,
702 fs::syncer& syncer,
703 const fs::path& meta_dir,
704 const fs::path& cache_dir,
705 bool wait)
706{
707 setup_directory(ctx, syncer);
708
709 auto on_removed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
710 {
711 for(const auto& synced_path : synced_paths)
712 {
713 auto synced_asset = remove_meta_tag(synced_path);
714
715 auto manifest_path = asset_compiler::get_manifest_path(synced_asset);
716 fs::error_code err;
717 fs::remove_all(manifest_path, err);
718 fs::remove_all(synced_asset, err);
719 }
720 };
721
722 auto on_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
723 {
724 for(const auto& synced_path : synced_paths)
725 {
726 auto synced_old_asset = remove_meta_tag(synced_path.first);
727 auto synced_new_asset = remove_meta_tag(synced_path.second);
728 auto manifest_old_path = asset_compiler::get_manifest_path(synced_old_asset);
729 auto manifest_new_path = asset_compiler::get_manifest_path(synced_new_asset);
730 fs::error_code err;
731 fs::rename(manifest_old_path, manifest_new_path, err);
732 fs::rename(synced_old_asset, synced_new_asset, err);
733 }
734 };
735
736 add_to_syncer<gfx::texture>(ctx, syncer, on_removed, on_renamed);
737 add_to_syncer<gfx::shader>(ctx, syncer, on_removed, on_renamed);
738 add_to_syncer<mesh>(ctx, syncer, on_removed, on_renamed);
739 add_to_syncer<material>(ctx, syncer, on_removed, on_renamed);
740 add_to_syncer<animation_clip>(ctx, syncer, on_removed, on_renamed);
741 add_to_syncer<prefab>(ctx, syncer, on_removed, on_renamed);
742 add_to_syncer<scene_prefab>(ctx, syncer, on_removed, on_renamed);
743 add_to_syncer<physics_material>(ctx, syncer, on_removed, on_renamed);
744 add_to_syncer<audio_clip>(ctx, syncer, on_removed, on_renamed);
745 add_to_syncer<font>(ctx, syncer, on_removed, on_renamed);
746 add_to_syncer<script>(ctx, syncer, on_removed, on_renamed);
747 add_to_syncer<ui_tree>(ctx, syncer, on_removed, on_renamed);
748 add_to_syncer<style_sheet>(ctx, syncer, on_removed, on_renamed);
749
750 syncer.sync(meta_dir, cache_dir);
751
752 if(wait)
753 {
754 auto& ts = ctx.get_cached<threader>();
755 ts.pool->wait_all();
756 }
757
758 watch_synced<gfx::texture>(ctx, watchers, cache_dir);
759 watch_synced<gfx::shader>(ctx, watchers, cache_dir);
760 watch_synced<mesh>(ctx, watchers, cache_dir);
761 watch_synced<material>(ctx, watchers, cache_dir);
762 watch_synced<animation_clip>(ctx, watchers, cache_dir);
763 watch_synced<prefab>(ctx, watchers, cache_dir);
764 watch_synced<scene_prefab>(ctx, watchers, cache_dir);
765 watch_synced<physics_material>(ctx, watchers, cache_dir);
766 watch_synced<audio_clip>(ctx, watchers, cache_dir);
767 watch_synced<font>(ctx, watchers, cache_dir);
768 watch_synced<script>(ctx, watchers, cache_dir);
769 watch_synced<ui_tree>(ctx, watchers, cache_dir);
770 watch_synced<style_sheet>(ctx, watchers, cache_dir);
771}
772
776
780
781void asset_watcher::on_os_event(rtti::context& ctx, os::event& e)
782{
783 if(e.type == os::events::window)
784 {
785 if(e.window.type == os::window_event_id::focus_lost)
786 {
787 if(!os::window::is_any_focused())
788 {
789 // APPLOG_TRACE("Application lost focus");
791 }
792 }
793 if(e.window.type == os::window_event_id::focus_gained)
794 {
795 if(os::window::is_any_focused())
796 {
797 // APPLOG_TRACE("Application gained focus");
799 }
800 }
801 }
802}
803
805{
806 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
807
808 auto& ev = ctx.get_cached<events>();
809 ev.on_os_event.connect(sentinel_, 1000, this, &asset_watcher::on_os_event);
810
811 watch_assets(ctx, "engine:/", true);
812
813 return true;
814}
815
817{
818 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
819
820 unwatch_assets(ctx, "engine:/");
821 return true;
822}
823
824void asset_watcher::watch_assets(rtti::context& ctx, const std::string& protocol, bool wait)
825{
826 auto& w = watched_protocols_[protocol];
827
828 auto data_protocol = ex::get_data_directory_no_slash(protocol);
829 auto meta_protocol = ex::get_meta_directory_no_slash(protocol);
830 auto cache_protocol = ex::get_compiled_directory_no_slash(protocol);
831
832 setup_meta_syncer(ctx,
833 w.watchers,
834 w.meta_syncer,
835 fs::resolve_protocol(data_protocol),
836 fs::resolve_protocol(meta_protocol),
837 wait);
838
839 setup_cache_syncer(ctx,
840 w.watchers,
841 w.cache_syncer,
842 fs::resolve_protocol(meta_protocol),
843 fs::resolve_protocol(cache_protocol),
844 wait);
845}
846
847void asset_watcher::unwatch_assets(rtti::context& ctx, const std::string& protocol)
848{
849 auto& w = watched_protocols_[protocol];
850
851 unwatch(w.watchers);
852 w.meta_syncer.unsync();
853 w.cache_syncer.unsync();
854
855 watched_protocols_.erase(protocol);
856
857 auto& am = ctx.get_cached<asset_manager>();
858 am.unload_group(protocol);
859}
860
861} // namespace unravel
manifold_type type
A filter that combines include and exclude patterns for file/directory filtering.
void set_mapping(const std::string &ref_ext, const std::vector< std::string > &synced_ext, on_entry_created_t on_entry_created, on_entry_modified_t on_entry_modified, on_entry_removed_t on_entry_removed, on_entry_renamed_t on_entry_renamed)
Remaps a specific extension of the reference directory to extensions of the synced directory....
Definition syncer.cpp:24
std::function< void(const std::string &, const rename_pair_t &, const std::vector< rename_pair_t > &)> on_entry_renamed_t
Definition syncer.h:21
void sync(const fs::path &reference_dir, const fs::path &synced_dir)
Start syncing the synced_dir with reference to the reference_dir i.e changes that occur in the refere...
Definition syncer.cpp:90
void set_directory_mapping(on_entry_created_t on_entry_created, on_entry_modified_t on_entry_modified, on_entry_removed_t on_entry_removed, on_entry_renamed_t on_entry_renamed)
Definition syncer.cpp:40
std::function< void(const std::string &, const fs::path &, const std::vector< fs::path > &)> on_entry_removed_t
Definition syncer.h:20
static void unwatch(std::uint64_t key)
Un-watches a previously registered file or directory.
Definition watcher.cpp:426
static void resume()
Definition watcher.cpp:491
static void pause()
Definition watcher.cpp:475
static void touch(const fs::path &path, bool recursive, fs::file_time_type time=fs::now())
Sets the last modification time of a file or directory. by default sets the time to the current time.
Definition watcher.cpp:436
static auto watch(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, clock_t::duration poll_interval, notify_callback callback) -> std::uint64_t
Watches a file or directory for modification and call back the specified std::function....
Definition watcher.cpp:416
Manages assets, including loading, unloading, and storage.
void unload_group(const std::string &group)
Unloads all assets in a specified group.
auto deinit(rtti::context &ctx) -> bool
void watch_assets(rtti::context &ctx, const std::string &protocol, bool wait=false)
auto init(rtti::context &ctx) -> bool
void unwatch_assets(rtti::context &ctx, const std::string &protocol)
#define APPLOG_WARNING(...)
Definition logging.h:19
#define APPLOG_TRACE(...)
Definition logging.h:17
auto get_suported_formats< gfx::shader >() -> const std::vector< std::string > &
auto get_all_formats() -> const std::vector< std::vector< std::string > > &
auto get_compiled_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_type() -> const std::string &
auto get_meta_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_compiled_directory(const std::string &prefix={}) -> std::string
auto get_suported_dependencies_formats< gfx::shader >() -> const std::vector< std::string > &
auto get_meta_directory(const std::string &prefix={}) -> std::string
auto get_suported_formats() -> const std::vector< std::string > &
auto get_data_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_suported_dependencies_formats() -> const std::vector< std::string > &
path extract_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path reduce_trailing_extensions(const path &_path)
another.
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
bool has_known_protocol(const path &_path)
Checks whether the path has a known protocol.
path replace(const path &_path, const path &_sequence, const path &_new_sequence)
Replacing any occurences of the specified path sequence with another.
auto to_string(const watcher::entry &e) -> std::string
Definition watcher.cpp:617
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
auto get_renderer_platform_supported_filename_extensions() -> const std::vector< std::string > &
auto get_current_renderer_filename_extension() -> const std::string &
void end(encoder *_encoder)
Definition graphics.cpp:271
auto start(seq_action action, const seq_scope_policy &scope_policy, hpp::source_location location) -> seq_id_t
Starts a new action.
Definition seq.cpp:8
auto get_manifest_path(const fs::path &compiled_asset_path) -> fs::path
Generate manifest file path from compiled asset path.
auto compile< gfx::shader >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto is_source_file_changed(const fs::path &source_path, const asset_manifest &manifest) -> bool
Check if source file has changed compared to manifest.
auto load_manifest(const fs::path &manifest_path, asset_manifest &manifest) -> bool
Load manifest from file.
auto compile(asset_manager &am, const fs::path &key, const fs::path &output_key, uint32_t flags=0) -> bool
auto get_job_name() -> std::string
void load_from_file(const std::string &absolute_path, animation_clip &obj)
void save_to_file(const std::string &absolute_path, const animation_clip &obj)
auto get_cached() -> T &
Definition context.hpp:49
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
Definition events.h:34
static void set_needs_recompile(const std::string &protocol, bool now=false)