Unravel Engine C++ Reference
Loading...
Searching...
No Matches
script_system.cpp
Go to the documentation of this file.
1#include "script_system.h"
5#include <engine/ecs/ecs.h>
6#include <engine/engine.h>
7#include <engine/events.h>
12
13
14#include <monopp/mono_exception.h>
15#include <monopp/mono_field_invoker.h>
16#include <monopp/mono_internal_call.h>
17#include <monopp/mono_logger.h>
18#include <monopp/mono_method_invoker.h>
19#include <monopp/mono_property_invoker.h>
20
21
24#include <logging/logging.h>
25#include <seq/seq.h>
27
28namespace unravel
29{
30namespace
31{
32
33enum class recompile_command : int
34{
35 none,
36 compile_at_schedule,
37 compile_now,
38};
39
40std::chrono::milliseconds check_interval(50);
41std::atomic_bool initted{};
42
43std::atomic<recompile_command> needs_recompile{};
44std::mutex container_mutex;
45std::vector<std::string> needs_to_recompile;
46std::atomic<uint64_t> compilation_version{};
47
48std::atomic_bool debug_mode{true};
49
50auto print_assembly_info(const mono::mono_assembly& assembly)
51{
52 std::stringstream ss;
53 auto refs = assembly.dump_references();
54
55 ss << fmt::format(" ----- References -----");
56
57 for(const auto& ref : refs)
58 {
59 ss << fmt::format("\n{}", ref);
60 }
61
62 APPLOG_TRACE("\n{}", ss.str());
63
64 auto types = assembly.get_types();
65
66 ss = {};
67 ss << fmt::format(" ----- Types -----");
68
69 for(const auto& type : types)
70 {
71 ss << fmt::format("\n{}", type.get_fullname());
72 ss << fmt::format("\n sizeof {}", type.get_sizeof());
73 ss << fmt::format("\n alignof {}", type.get_alignof());
74
75 {
76 auto attribs = type.get_attributes();
77 for(const auto& attrib : attribs)
78 {
79 ss << fmt::format("\n - Attribute : {}", attrib.get_type().get_fullname());
80 }
81 }
82
83 auto fields = type.get_fields();
84 for(const auto& field : fields)
85 {
86 ss << fmt::format("\n - Field : {}", field.get_name());
87
88 auto attribs = field.get_attributes();
89 for(const auto& attrib : attribs)
90 {
91 ss << fmt::format("\n -- Attribute : {}", attrib.get_type().get_fullname());
92 }
93 }
94
95 auto properties = type.get_properties();
96 for(const auto& prop : properties)
97 {
98 ss << fmt::format("\n - Property : {}", prop.get_name());
99
100 auto attribs = prop.get_attributes();
101 for(const auto& attrib : attribs)
102 {
103 ss << fmt::format("\n -- Attribute : {}", attrib.get_type().get_fullname());
104 }
105 }
106 }
107 APPLOG_TRACE("\n{}", ss.str());
108}
109
110} // namespace
111
112void script_system::log_exception(const mono::mono_exception& e, const hpp::source_location& loc)
113{
114 auto frame = mono::extract_relevant_stack_frame(e.what());
115 if(!frame.file_name.empty())
116 {
117 APPLOG_ERROR_LOC(frame.file_name.c_str(), frame.line, frame.function_name.c_str(), e.what());
118 }
119 else
120 {
121 APPLOG_ERROR_LOC(loc.file_name(), int(loc.line()), loc.function_name(), e.what());
122 }
123}
124
125void script_system::copy_compiled_lib(const fs::path& from, const fs::path& to)
126{
127 auto from_debug_info = from;
128 from_debug_info.concat(".mdb");
129 auto from_comments_xml = from;
130 from_comments_xml.replace_extension(".xml");
131
132 auto to_debug_info = to;
133 to_debug_info.concat(".mdb");
134 auto to_comments_xml = to;
135 to_comments_xml.replace_extension(".xml");
136
137 fs::error_code er;
138 fs::copy_file(from, to, fs::copy_options::overwrite_existing, er);
139 fs::copy_file(from_debug_info, to_debug_info, fs::copy_options::overwrite_existing, er);
140 fs::copy_file(from_comments_xml, to_comments_xml, fs::copy_options::overwrite_existing, er);
141
142 fs::remove(from, er);
143 fs::remove(from_debug_info, er);
144 fs::remove(from_comments_xml, er);
145}
146
147auto script_system::find_mono(const rtti::context& ctx) -> mono::compiler_paths
148{
149 bool is_deploy_mode = ctx.has<deploy>();
150
151 mono::compiler_paths result;
152
153 if(is_deploy_mode)
154 {
155 auto mono_dir = fs::resolve_protocol("engine:/mono");
156 result.assembly_dir = fs::absolute(mono_dir / "lib").string();
157 result.config_dir = fs::absolute(mono_dir / "etc").string();
158 }
159 else
160 {
161 const auto& names = mono::get_common_library_names();
162 const auto& library_paths = mono::get_common_library_paths();
163 const auto& config_paths = mono::get_common_config_paths();
164
165 for(size_t i = 0; i < library_paths.size(); ++i)
166 {
167 const auto& library_path = library_paths.at(i);
168 const auto& config_path = config_paths.at(i);
169 std::vector<std::string> paths{library_path};
170 auto found_library = fs::find_library(names, paths);
171
172 if(!found_library.empty())
173 {
174 result.assembly_dir = fs::path(library_path).make_preferred().string();
175 result.config_dir = fs::path(config_path).make_preferred().string();
176
177 break;
178 }
179 }
180 }
181
182 {
183 const auto& names = mono::get_common_executable_names();
184 const auto& paths = mono::get_common_executable_paths();
185
186 result.msc_executable = fs::find_program(names, paths).make_preferred().string();
187 }
188
189 APPLOG_TRACE("MONO_PATHS:");
190 APPLOG_TRACE("Assembly path - {}", result.assembly_dir);
191 APPLOG_TRACE("Config path - {}", result.config_dir);
192
193 return result;
194}
195
196auto validate_paths(const mono::compiler_paths& paths) -> bool
197{
198 return !paths.assembly_dir.empty() && !paths.config_dir.empty();
199}
200
202{
203 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
204
205 auto& ev = ctx.get_cached<events>();
206 ev.on_frame_update.connect(sentinel_, this, &script_system::on_frame_update);
207 ev.on_frame_fixed_update.connect(sentinel_, this, &script_system::on_frame_fixed_update);
208 ev.on_frame_update.connect(sentinel_, -100000, this, &script_system::on_frame_late_update);
209 ev.on_play_begin.connect(sentinel_, -1000, this, &script_system::on_play_begin);
210 ev.on_play_end.connect(sentinel_, 1000, this, &script_system::on_play_end);
211 ev.on_pause.connect(sentinel_, 100, this, &script_system::on_pause);
212 ev.on_resume.connect(sentinel_, -100, this, &script_system::on_resume);
213 ev.on_skip_next_frame.connect(sentinel_, -100, this, &script_system::on_skip_next_frame);
214
215 auto mono_paths = find_mono(ctx);
216
217 if(!validate_paths(mono_paths))
218 {
219 auto& error = ctx.add<init_error>();
220 error.category = "Mono C#";
221 error.msg = "Failed to locate Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
222
223 return false;
224 }
225
226 debug_config_.enable_debugging = true;
227
228 mono::set_log_handler("info",
229 [](const std::string& msg)
230 {
231 APPLOG_INFO("{}", msg);
232 });
233 mono::set_log_handler("trace",
234 [](const std::string& msg)
235 {
236 APPLOG_TRACE("{}", msg);
237 });
238 mono::set_log_handler("warning",
239 [](const std::string& msg)
240 {
241 APPLOG_WARNING("{}", msg);
242 });
243 mono::set_log_handler("error",
244 [](const std::string& msg)
245 {
246 APPLOG_ERROR("{}", msg);
247 });
248
249 if(mono::init(mono_paths, debug_config_))
250 {
251 bind_internal_calls(ctx);
252
253 mono::mono_domain::set_assemblies_path(fs::resolve_protocol(ex::get_compiled_directory("engine")).string());
254
255 try
256 {
257 if(!load_engine_domain(ctx, true))
258 {
259 return false;
260 }
261 }
262 catch(const mono::mono_exception& e)
263 {
264 log_exception(e);
265 return false;
266 }
267
268 initted = true;
269 return true;
270 }
271
272 auto& error = ctx.add<init_error>();
273 error.category = "Mono C#";
274 error.msg = "Failed to initialize Mono C#. Please install it from - https://www.mono-project.com/download/stable/";
275
276 return false;
277}
278
280{
281 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
282
283 unload_app_domain();
284 unload_engine_domain();
285
286 mono::shutdown();
287
288 return true;
289}
290
291void script_system::set_debug_config(const std::string& address, uint32_t port, uint32_t loglevel)
292{
293 debug_config_.address = address;
294 debug_config_.port = port;
295 debug_config_.loglevel = loglevel;
296}
297
298auto script_system::load_engine_domain(rtti::context& ctx, bool recompile) -> bool
299{
300 bool is_deploy_mode = ctx.has<deploy>();
301
302 if(!is_deploy_mode && recompile)
303 {
304 bool debug = false;
305#ifndef NDEBUG
306 debug = true;
307#endif
308
309 if(!create_compilation_job(ctx, "engine", debug).get())
310 {
311 return false;
312 }
313 }
314
315 domain_ = std::make_unique<mono::mono_domain>("Unravel.Engine");
316 mono::mono_domain::set_current_domain(domain_.get());
317
318 auto engine_script_lib = fs::resolve_protocol(get_lib_compiled_key("engine"));
319 auto engine_script_lib_temp = fs::resolve_protocol(get_lib_temp_compiled_key("engine"));
320
321 copy_compiled_lib(engine_script_lib_temp, engine_script_lib);
322
323 auto assembly = domain_->get_assembly(engine_script_lib.string());
324 // print_assembly_info(assembly);
325
326 cache_.update_manager_type = assembly.get_type("Unravel.Core", "SystemManager");
327
328 // Cache methods to avoid repeated allocations every frame
329 cache_.update_method = cache_.update_manager_type.get_method("internal_n2m_update", 1);
330 cache_.fixed_update_method = cache_.update_manager_type.get_method("internal_n2m_fixed_update", 1);
331 cache_.late_update_method = cache_.update_manager_type.get_method("internal_n2m_late_update", 0);
332
333 return true;
334}
336{
337 cache_ = {};
338 domain_.reset();
339 mono::mono_domain::set_current_domain(nullptr);
340}
341
342auto script_system::load_app_domain(rtti::context& ctx, bool recompile) -> bool
343{
344 bool is_deploy_mode = ctx.has<deploy>();
345
346 bool result = true;
347
348 if(!is_deploy_mode && recompile)
349 {
350 result &= create_compilation_job(ctx, "app", get_script_debug_mode()).get();
351
352 has_compilation_errors_ = !result;
353 }
354
355 app_domain_ = std::make_unique<mono::mono_domain>("Unravel.App");
356 mono::mono_domain::set_current_domain(app_domain_.get());
357
358 auto app_script_lib = fs::resolve_protocol(get_lib_compiled_key("app"));
359 auto app_script_lib_temp = fs::resolve_protocol(get_lib_temp_compiled_key("app"));
360
361 copy_compiled_lib(app_script_lib_temp, app_script_lib);
362
363 if(!is_deploy_mode)
364 {
365 auto& am = ctx.get_cached<asset_manager>();
366 auto assets = am.get_assets<script>("app");
367 // assets include the empty asset
368 if(assets.size() <= 1)
369 {
370 return result;
371 }
372 }
373
374 try
375 {
376 auto assembly = app_domain_->get_assembly(app_script_lib.string());
377 // print_assembly_info(assembly);
378
379 auto engine_script_lib = fs::resolve_protocol(get_lib_compiled_key("engine"));
380 auto engine_assembly = domain_->get_assembly(engine_script_lib.string());
381
382 auto comp_type = engine_assembly.get_type("Unravel.Core", "ScriptComponent");
383
384 app_cache_.scriptable_component_types.clear();
385 if(!has_compilation_errors_)
386 {
387 app_cache_.scriptable_component_types = assembly.get_types_derived_from(comp_type);
388 }
389 }
390 catch(const mono::mono_exception& e)
391 {
392 log_exception(e);
393 result = false;
394 }
395
396 return result;
397}
399{
400 app_cache_ = {};
401 app_domain_.reset();
402 mono::mono_domain::set_current_domain(domain_.get());
403}
404
405void script_system::on_create_component(entt::registry& r, entt::entity e)
406{
407}
408void script_system::on_destroy_component(entt::registry& r, entt::entity e)
409{
410 auto& comp = r.get<script_component>(e);
411 comp.destroy();
412}
413
414void script_system::on_create_active_component(entt::registry& r, entt::entity e)
415{
416 if(auto comp = r.try_get<script_component>(e))
417 {
418 comp->enable();
419 }
420}
421void script_system::on_destroy_active_component(entt::registry& r, entt::entity e)
422{
423 if(auto comp = r.try_get<script_component>(e))
424 {
425 comp->disable();
426 }
427}
428
429void script_system::on_play_begin(hpp::span<const entt::handle> entities)
430{
431 if(!app_domain_ || !domain_)
432 {
433 return;
434 }
435 try
436 {
437 create_call_ = call_progress::started;
438
439 for(auto entity : entities)
440 {
441 if(auto comp = entity.try_get<script_component>())
442 {
443 comp->create();
444 }
445 }
446 create_call_ = call_progress::finished;
447
448 for(auto entity : entities)
449 {
450 if(auto comp = entity.try_get<script_component>())
451 {
452 if(entity.all_of<active_component>())
453 {
454 comp->enable();
455 }
456 else
457 {
458 comp->disable();
459 }
460 }
461 }
462 }
463 catch(const mono::mono_exception& e)
464 {
465 log_exception(e);
466 }
467}
468
469void script_system::on_play_begin(entt::registry& entities)
470{
471 if(!app_domain_ || !domain_)
472 {
473 return;
474 }
475 try
476 {
477 create_call_ = call_progress::started;
478
479 entities.view<script_component>().each(
480 [&](auto e, auto&& comp)
481 {
482 comp.create();
483 });
484
485 create_call_ = call_progress::finished;
486
487 entities.view<script_component>().each(
488 [&](auto e, auto&& comp)
489 {
490 if(entities.all_of<active_component>(e))
491 {
492 comp.enable();
493 }
494 else
495 {
496 comp.disable();
497 }
498 });
499 }
500 catch(const mono::mono_exception& e)
501 {
502 log_exception(e);
503 }
504}
505
507{
508 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
509
510 if(!app_domain_ || !domain_)
511 {
512 return;
513 }
514
515 auto& ec = ctx.get_cached<ecs>();
516 auto& scn = ec.get_scene();
517 auto& registry = *scn.registry;
518
519 registry.on_construct<script_component>().connect<&on_create_component>();
520 registry.on_destroy<script_component>().connect<&on_destroy_component>();
521
522 registry.on_construct<active_component>().connect<&on_create_active_component>();
523 registry.on_destroy<active_component>().connect<&on_destroy_active_component>();
524
525 on_play_begin(registry);
526
527 elapsed_time_ = {};
528}
529
530void script_system::on_play_end(rtti::context& ctx)
531{
532 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
533
534 auto& ec = ctx.get_cached<ecs>();
535 auto& scn = ec.get_scene();
536 auto& registry = *scn.registry;
537
538 seq::scope::stop_all("script");
539
540 // try
541 // {
542 // registry.view<script_component>().each(
543 // [&](auto e, auto&& comp)
544 // {
545 // comp.destroy();
546 // });
547 // }
548 // catch(const mono::mono_exception& e)
549 // {
550 // log_exception(e);
551 // }
552
553 registry.on_construct<active_component>().disconnect<&on_create_active_component>();
554 registry.on_destroy<active_component>().disconnect<&on_destroy_active_component>();
555
556 registry.on_construct<script_component>().disconnect<&on_create_component>();
557 registry.on_destroy<script_component>().disconnect<&on_destroy_component>();
558
559 elapsed_time_ = {};
560}
561
562void script_system::on_pause(rtti::context& ctx)
563{
564}
565
566void script_system::on_resume(rtti::context& ctx)
567{
568}
569
570void script_system::on_skip_next_frame(rtti::context& ctx)
571{
572 delta_t step(1.0f / 60.0f);
573 on_frame_update(ctx, step);
574}
575void script_system::on_frame_update(rtti::context& ctx, delta_t dt)
576{
577 APP_SCOPE_PERF("Script/System Update");
578
579 auto& ev = ctx.get_cached<events>();
580 if(!ev.is_playing)
581 {
582 check_for_recompile(ctx, dt, true);
583 }
584
585 is_updating_ = true;
586
587 try
588 {
589 if(!app_domain_ || !domain_)
590 {
591 return;
592 }
593
594 auto& ec = ctx.get_cached<ecs>();
595 auto& scn = ec.get_scene();
596 auto& registry = *scn.registry;
597
598 registry.view<script_component>().each(
599 [&](auto e, auto&& comp)
600 {
601 comp.process_pending_deletions();
602
603 if(ev.is_playing && registry.all_of<active_component>(e))
604 {
605 comp.start();
606 }
607 });
608
609 struct update_data
610 {
611 float time{};
612 float delta_time{};
613 float time_scale{};
614 uint64_t frame_count{};
615 };
616
617 if(ev.is_playing)
618 {
619 auto& sim = ctx.get_cached<simulation>();
620 auto time_scale = sim.get_time_scale();
621
622 update_data data;
623 data.time = elapsed_time_.count();
624 data.delta_time = dt.count();
625 data.time_scale = time_scale;
626 data.frame_count = sim.get_frame();
627
628 // Use cached method to avoid repeated allocations
629 auto method_thunk = mono::make_method_invoker<void(update_data)>(cache_.update_method);
630 method_thunk(data);
631
632 elapsed_time_ += dt;
633 }
634 }
635 catch(const mono::mono_exception& e)
636 {
637 log_exception(e);
638 }
639 is_updating_ = false;
640}
641
642void script_system::on_frame_fixed_update(rtti::context& ctx, delta_t dt)
643{
644 // APP_SCOPE_PERF("Script/System Fixed Update");
645
646 auto& ev = ctx.get_cached<events>();
647
648 try
649 {
650 if(!app_domain_ || !domain_)
651 {
652 return;
653 }
654
655 auto& ec = ctx.get_cached<ecs>();
656 auto& scn = ec.get_scene();
657 auto& registry = *scn.registry;
658
659 registry.view<script_component>().each(
660 [&](auto e, auto&& comp)
661 {
662 comp.process_pending_deletions();
663 });
664
665 struct update_data
666 {
667 float fixed_delta_time{};
668 };
669
670 if(ev.is_playing && dt > delta_t::zero())
671 {
672 auto& sim = ctx.get_cached<simulation>();
673 auto time_scale = sim.get_time_scale();
674
675 update_data data;
676 data.fixed_delta_time = dt.count();
677
678 // Use cached method to avoid repeated allocations
679 auto method_thunk = mono::make_method_invoker<void(update_data)>(cache_.fixed_update_method);
680 method_thunk(data);
681 }
682 }
683 catch(const mono::mono_exception& e)
684 {
685 log_exception(e);
686 }
687}
688
689void script_system::on_frame_late_update(rtti::context& ctx, delta_t dt)
690{
691 APP_SCOPE_PERF("Script/System Late Update");
692
693 auto& ev = ctx.get_cached<events>();
694
695 try
696 {
697 if(!app_domain_ || !domain_)
698 {
699 return;
700 }
701
702 auto& ec = ctx.get_cached<ecs>();
703 auto& scn = ec.get_scene();
704 auto& registry = *scn.registry;
705
706 if(ev.is_playing && dt > delta_t::zero())
707 {
708 // Use cached method to avoid repeated allocations
709 auto method_thunk = mono::make_method_invoker<void()>(cache_.late_update_method);
710 method_thunk();
711 }
712 }
713 catch(const mono::mono_exception& e)
714 {
715 log_exception(e);
716 }
717}
718
719auto script_system::get_all_scriptable_components() const -> const std::vector<mono::mono_type>&
720{
721 return app_cache_.scriptable_component_types;
722}
723
724auto script_system::get_engine_assembly() const -> mono::mono_assembly
725{
726 auto engine_script_lib = fs::resolve_protocol(get_lib_compiled_key("engine"));
727 return domain_->get_assembly(engine_script_lib.string());
728}
729
730auto script_system::get_app_assembly() const -> mono::mono_assembly
731{
732 auto app_script_lib = fs::resolve_protocol(get_lib_compiled_key("app"));
733 return app_domain_->get_assembly(app_script_lib.string());
734}
735
737{
738 return create_call_ == call_progress::finished;
739}
741{
742 return is_updating_;
743}
744
746{
747 return mono::is_debugger_attached();
748}
749
750void script_system::check_for_recompile(rtti::context& ctx, delta_t dt, bool emit_callback)
751{
752 time_since_last_check_ += dt;
753
754 if(time_since_last_check_ >= check_interval || needs_recompile == recompile_command::compile_now)
755 {
756 time_since_last_check_ = {};
757
758 recompile_command should_recompile = needs_recompile.exchange(recompile_command::none);
759
760 if(should_recompile != recompile_command::none)
761 {
762 auto container = []()
763 {
764 std::lock_guard<std::mutex> lock(container_mutex);
765 auto result = std::move(needs_to_recompile);
766 return result;
767 }();
768
769 compilation_jobs_.clear();
770
771 compilation_version++;
772
773 auto current_version = compilation_version.load();
774 for(const auto& protocol : container)
775 {
776 auto job = create_compilation_job(ctx, protocol, get_script_debug_mode())
777 .then(tpp::this_thread::get_id(),
778 [this, &ctx, protocol, emit_callback, current_version](auto f)
779 {
780 if(!emit_callback)
781 {
782 return;
783 }
784 auto& ev = ctx.get_cached<events>();
785
786 if(ev.is_playing)
787 {
788 return;
789 }
790
791 if(compilation_version > current_version)
792 {
793 return;
794 }
795
796 has_compilation_errors_ = !f.get();
797 if(!has_compilation_errors_)
798 {
799 ev.on_script_recompile(ctx, protocol, current_version);
800 }
801 });
802
803 compilation_jobs_.emplace_back(std::move(job));
804 }
805 }
806 }
807}
808
810{
811 APPLOG_TRACE("Waiting for script compilation...");
812
813 check_for_recompile(ctx, 100s, false);
814
815 auto jobs = std::move(compilation_jobs_);
816
817 for(auto& job : jobs)
818 {
819 job.wait();
820 }
821}
822
823auto script_system::create_compilation_job(rtti::context& ctx,
824 const std::string& protocol,
825 bool debug) -> tpp::job_future<bool>
826{
827 uint32_t flags = 0;
828 if(debug)
829 {
831 }
832
833 auto& thr = ctx.get_cached<threader>();
834 auto& am = ctx.get_cached<asset_manager>();
835
836 return thr.pool->schedule(
837 "Compiling " + ex::get_type<script_library>(),
838 [&am, flags, protocol]()
839 {
840 auto key = get_lib_data_key(protocol);
841 auto output = get_lib_temp_compiled_key(protocol);
842
844 });
845}
846void script_system::set_needs_recompile(const std::string& protocol, bool now)
847{
848 if(!initted)
849 {
850 return;
851 }
852 needs_recompile = now ? recompile_command::compile_now : recompile_command::compile_at_schedule;
853 {
854 std::lock_guard<std::mutex> lock(container_mutex);
855 if(std::find(std::begin(needs_to_recompile), std::end(needs_to_recompile), protocol) ==
856 std::end(needs_to_recompile))
857 {
858 needs_to_recompile.emplace_back(protocol);
859 }
860 }
861}
862
864{
865 return debug_mode;
866}
867
869{
870 debug_mode = debug;
871}
872
873auto script_system::get_lib_name(const std::string& protocol) -> std::string
874{
875 return protocol + "-script.dll";
876}
877
878auto script_system::get_lib_data_key(const std::string& protocol) -> std::string
879{
880 std::string output = get_lib_name(protocol + ex::get_data_directory() + "/" + protocol);
881 return output;
882}
883
884auto script_system::get_lib_temp_compiled_key(const std::string& protocol) -> std::string
885{
886 std::string output = get_lib_name(protocol + ex::get_compiled_directory() + "/temp-" + protocol);
887 return output;
888}
889
890auto script_system::get_lib_compiled_key(const std::string& protocol) -> std::string
891{
892 std::string output = get_lib_name(protocol + ex::get_compiled_directory() + "/" + protocol);
893 return output;
894}
895
896void script_system::on_sensor_enter(entt::handle sensor, entt::handle other)
897{
898 if(!other || !sensor)
899 {
900 return;
901 }
902 auto comp = sensor.try_get<script_component>();
903 if(!comp)
904 {
905 return;
906 }
907
908 try
909 {
910 comp->on_sensor_enter(other);
911 }
912 catch(const mono::mono_exception& e)
913 {
914 log_exception(e);
915 }
916}
917
918void script_system::on_sensor_exit(entt::handle sensor, entt::handle other)
919{
920 if(!other || !sensor)
921 {
922 return;
923 }
924
925 auto comp = sensor.try_get<script_component>();
926 if(!comp)
927 {
928 return;
929 }
930
931 try
932 {
933 comp->on_sensor_exit(other);
934 }
935 catch(const mono::mono_exception& e)
936 {
937 log_exception(e);
938 }
939}
940
941void script_system::on_collision_enter(entt::handle a, entt::handle b, const std::vector<manifold_point>& manifolds)
942{
943 if(!a || !b)
944 {
945 return;
946 }
947
948 try
949 {
950 {
951 auto comp = a.try_get<script_component>();
952 if(comp)
953 {
954 comp->on_collision_enter(b, manifolds, true);
955 }
956 }
957
958 {
959 auto comp = b.try_get<script_component>();
960 if(comp)
961 {
962 comp->on_collision_enter(a, manifolds, false);
963 }
964 }
965 }
966 catch(const mono::mono_exception& e)
967 {
968 log_exception(e);
969 }
970}
971
972void script_system::on_collision_exit(entt::handle a, entt::handle b, const std::vector<manifold_point>& manifolds)
973{
974 if(!a || !b)
975 {
976 return;
977 }
978
979 try
980 {
981 {
982 auto comp = a.try_get<script_component>();
983 if(comp)
984 {
985 comp->on_collision_exit(b, manifolds, true);
986 }
987 }
988
989 {
990 auto comp = b.try_get<script_component>();
991 if(comp)
992 {
993 comp->on_collision_exit(a, manifolds, false);
994 }
995 }
996 }
997 catch(const mono::mono_exception& e)
998 {
999 log_exception(e);
1000 }
1001}
1002
1004{
1005 return has_compilation_errors_;
1006}
1007
1008} // namespace unravel
entt::handle b
manifold_type type
entt::handle a
Manages assets, including loading, unloading, and storage.
auto get_assets(const std::string &group={}) const -> std::vector< asset_handle< T > >
Gets all assets in a specified group.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
std::chrono::duration< float > delta_t
uint32_t frame
Definition graphics.cpp:21
bool initted
Definition graphics.cpp:20
#define APPLOG_WARNING(...)
Definition logging.h:19
#define APPLOG_ERROR(...)
Definition logging.h:20
#define APPLOG_INFO(...)
Definition logging.h:18
#define APPLOG_ERROR_LOC(FILE_LOC, LINE_LOC, FUNC_LOC,...)
Definition logging.h:38
#define APPLOG_TRACE(...)
Definition logging.h:17
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_type() -> const std::string &
auto get_compiled_directory(const std::string &prefix={}) -> std::string
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
void stop_all(const std::string &scope)
Stops all actions within the specified scope.
Definition seq.cpp:154
Hash specialization for batch_key to enable use in std::unordered_map.
auto compile< script_library >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto validate_paths(const mono::compiler_paths &paths) -> bool
#define APP_SCOPE_PERF(name)
Create a scoped performance timer that only accepts string literals.
Definition profiler.h:160
entt::entity entity
auto get_cached() -> T &
Definition context.hpp:49
Manages the entity-component-system (ECS) operations for the ACE framework.
Definition ecs.h:12
auto get_scene() -> scene &
Gets the current scene.
Definition ecs.cpp:30
hpp::event< void(rtti::context &, delta_t)> on_frame_update
Definition events.h:16
std::string category
Definition engine.h:13
static auto is_debugger_attached() -> bool
static auto get_lib_name(const std::string &protocol) -> std::string
auto is_create_called() const -> bool
static auto get_lib_data_key(const std::string &protocol) -> std::string
static auto get_script_debug_mode() -> bool
auto is_update_called() const -> bool
static auto get_lib_compiled_key(const std::string &protocol) -> std::string
static auto get_lib_temp_compiled_key(const std::string &protocol) -> std::string
static void set_script_debug_mode(bool debug)
auto load_engine_domain(rtti::context &ctx, bool recompile) -> bool
auto get_app_assembly() const -> mono::mono_assembly
auto has_compilation_errors() const -> bool
void on_collision_enter(entt::handle a, entt::handle b, const std::vector< manifold_point > &manifolds)
auto init(rtti::context &ctx) -> bool
static void on_create_component(entt::registry &r, entt::entity e)
Called when a physics component is created.
static void set_needs_recompile(const std::string &protocol, bool now=false)
void set_debug_config(const std::string &address, uint32_t port, uint32_t loglevel)
static void copy_compiled_lib(const fs::path &from, const fs::path &to)
static void on_destroy_active_component(entt::registry &r, entt::entity e)
auto load_app_domain(rtti::context &ctx, bool recompile) -> bool
static auto find_mono(const rtti::context &ctx) -> mono::compiler_paths
void on_sensor_exit(entt::handle sensor, entt::handle other)
void on_sensor_enter(entt::handle sensor, entt::handle other)
static void log_exception(const mono::mono_exception &e, const hpp::source_location &loc=hpp::source_location::current())
auto get_engine_assembly() const -> mono::mono_assembly
static void on_create_active_component(entt::registry &r, entt::entity e)
void on_play_begin(rtti::context &ctx)
Called when playback begins.
auto deinit(rtti::context &ctx) -> bool
auto get_all_scriptable_components() const -> const std::vector< mono::mono_type > &
void on_collision_exit(entt::handle a, entt::handle b, const std::vector< manifold_point > &manifolds)
void wait_for_jobs_to_finish(rtti::context &ctx)
static void on_destroy_component(entt::registry &r, entt::entity e)
Called when a physics component is destroyed.