Unravel Engine C++ Reference
Loading...
Searching...
No Matches
script_component.cpp
Go to the documentation of this file.
1#include "script_component.h"
2#include <dotnetpp/dotnetpp.h>
3
5#include <engine/engine.h>
6#include <engine/events.h>
7#include <engine/play_mode.h>
9namespace unravel
10{
11
12namespace
13{
14
15
16auto to_managed_contact_point(const manifold_point& manifold, bool use_b = false) -> dotnetpp_backend::managed_interface::manifold_point
17{
19 if(use_b)
20 {
21 result.point = {manifold.b.x, manifold.b.y, manifold.b.z};
22 result.normal = {manifold.normal_on_b.x, manifold.normal_on_b.y, manifold.normal_on_b.z};
23 result.distance = manifold.distance;
24 result.impulse = manifold.impulse;
25 }
26 else
27 {
28 result.point = {manifold.a.x, manifold.a.y, manifold.a.z};
29 result.normal = {manifold.normal_on_a.x, manifold.normal_on_a.y, manifold.normal_on_a.z};
30 result.distance = manifold.distance;
31 result.impulse = manifold.impulse;
32 }
33 return result;
34}
35
36auto to_managed_contact_points(const std::vector<manifold_point>& manifolds, bool use_b = false) -> std::vector<dotnetpp_backend::managed_interface::manifold_point>
37{
38 std::vector<dotnetpp_backend::managed_interface::manifold_point> result;
39 result.reserve(manifolds.size());
40 for(const auto& manifold : manifolds)
41 {
42 result.push_back(to_managed_contact_point(manifold, use_b));
43 }
44 return result;
45}
46
47auto engine_script_cache() -> const script_system::engine_script_cache&
48{
49 const auto& ctx = engine::context();
50 return ctx.get_cached<script_system>().get_cache();
51}
52} // namespace
53
54void script_component::on_create_component(entt::registry& r, entt::entity e)
55{
56 entt::handle entity(r, e);
57
58 auto& component = entity.get<script_component>();
59 component.set_owner(entity);
60}
61
62void script_component::on_destroy_component(entt::registry& r, entt::entity e)
63{
64}
65
76
78{
79 safe_foreach(script_components_,
80 [&](auto& script)
81 {
82 if(script.pinned)
83 {
84 auto obj = script.pinned->get_object();
86 }
87 });
88
90}
91
93{
94 safe_foreach(script_components_,
95 [&](auto& script)
96 {
97 auto obj = script.pinned->get_object();
98 enable(script, true);
99 });
100}
101
103{
104 safe_foreach(script_components_,
105 [&](auto& script)
106 {
107 auto obj = script.pinned->get_object();
108 disable(script, true);
109 });
110}
111
112void script_component::on_sensor_enter(entt::handle other, const std::vector<manifold_point>& manifolds)
113{
114 auto managed_manifolds = to_managed_contact_points(manifolds, true);
115 safe_foreach(script_components_,
116 [&](auto& script)
117 {
118 auto obj = script.pinned->get_object();
119 on_sensor_enter(obj, other, managed_manifolds);
120 });
121}
122
123void script_component::on_sensor_exit(entt::handle other, const std::vector<manifold_point>& manifolds)
124{
125 auto managed_manifolds = to_managed_contact_points(manifolds, true);
126 safe_foreach(script_components_,
127 [&](auto& script)
128 {
129 auto obj = script.pinned->get_object();
130 on_sensor_exit(obj, other, managed_manifolds);
131 });
132}
133
134void script_component::on_collision_enter(entt::handle b, const std::vector<manifold_point>& manifolds, bool use_b)
135{
136 auto managed_manifolds = to_managed_contact_points(manifolds, use_b);
137 safe_foreach(script_components_,
138 [&](auto& script)
139 {
140 auto obj = script.pinned->get_object();
141 on_collision_enter(obj, b, managed_manifolds);
142 });
143}
144
145void script_component::on_collision_exit(entt::handle b, const std::vector<manifold_point>& manifolds, bool use_b)
146{
147 auto managed_manifolds = to_managed_contact_points(manifolds, use_b);
148 safe_foreach(script_components_,
149 [&](auto& script)
150 {
151 auto obj = script.pinned->get_object();
152 on_collision_exit(obj, b, managed_manifolds);
153 });
154}
155
156void script_component::enable(script_object& script_obj, bool check_order)
157{
158 if(script_obj.is_enabled() || script_obj.is_marked_for_destroy())
159 {
160 return;
161 }
162
163 script_obj.state->active = true;
164
165 if(check_order)
166 {
167 if(!script_obj.is_create_called())
168 {
169 return;
170 }
171 }
172
173 auto obj = script_obj.pinned->get_object();
174 if(!obj.valid())
175 {
176 APPLOG_WARNING("Script component already destroyed");
177 return;
178 }
179
180 try
181 {
182 auto method = dotnet::make_method_invoker<void()>(engine_script_cache().on_enable_method, false);
183 method(obj);
184 }
185 catch(const dotnet::exception& e)
186 {
188 }
189}
190
191void script_component::disable(script_object& script_obj, bool check_order)
192{
193 if(script_obj.is_disabled() || script_obj.is_marked_for_destroy())
194 {
195 return;
196 }
197
198 script_obj.state->active = 0;
199
200 if(check_order)
201 {
202 if(!script_obj.is_create_called())
203 {
204 return;
205 }
206 }
207 auto obj = script_obj.pinned->get_object();
208 if(!obj.valid())
209 {
210 APPLOG_WARNING("Script component already destroyed");
211 return;
212 }
213 try
214 {
215 auto method = dotnet::make_method_invoker<void()>(engine_script_cache().on_disable_method, false);
216 method(obj);
217 }
218 catch(const dotnet::exception& e)
219 {
221 }
222}
223
224void script_component::create(script_object& script_obj)
225{
226 if(script_obj.is_create_called())
227 {
228 return;
229 }
230
231 script_obj.state->create_called = true;
232 auto obj = script_obj.pinned->get_object();
233 if(!obj.valid())
234 {
235 APPLOG_WARNING("Script component already destroyed");
236 return;
237 }
238
239 try
240 {
241 auto method = dotnet::make_method_invoker<void()>(engine_script_cache().on_create_method, false);
242 method(obj);
243 }
244 catch(const dotnet::exception& e)
245 {
247 }
248}
249void script_component::start(script_object& script_obj)
250{
251 if(script_obj.is_start_called())
252 {
253 return;
254 }
255 script_obj.state->start_called = true;
256 auto obj = script_obj.pinned->get_object();
257 if(!obj.valid())
258 {
259 APPLOG_WARNING("Script component already destroyed");
260 return;
261 }
262 try
263 {
264 auto method = dotnet::make_method_invoker<void()>(engine_script_cache().on_start_method, false);
265 method(obj);
266 }
267 catch(const dotnet::exception& e)
268 {
270 }
271}
272
273void script_component::destroy(script_object& script_obj)
274{
275 auto obj = script_obj.pinned->get_object();
276 if(!obj.valid())
277 {
278 APPLOG_WARNING("Script component already destroyed");
279 return;
280 }
281 try
282 {
283 auto method = dotnet::make_method_invoker<void()>(engine_script_cache().on_destroy_method, false);
284 method(obj);
285 }
286 catch(const dotnet::exception& e)
287 {
289 }
290}
291
292void script_component::set_entity(const dotnet::object& obj, entt::handle e)
293{
294 if(!obj.valid())
295 {
296 return;
297 }
298 try
299 {
300 auto method =
301 dotnet::make_method_invoker<void(entt::entity)>(engine_script_cache().set_entity_method, false);
302 method(obj, e.entity());
303 }
304 catch(const dotnet::exception& e)
305 {
307 }
308}
309
310void script_component::on_sensor_enter(const dotnet::object& obj, entt::handle other, const std::vector<dotnetpp_backend::managed_interface::manifold_point>& manifolds)
311{
312 try
313 {
314 auto method = dotnet::make_method_invoker<void(
315 entt::entity, const std::vector<dotnetpp_backend::managed_interface::manifold_point>&)>(
316 engine_script_cache().on_sensor_enter_method, false);
317 method(obj, other.entity(), manifolds);
318 }
319 catch(const dotnet::exception& e)
320 {
322 }
323}
324
325void script_component::on_sensor_exit(const dotnet::object& obj, entt::handle other, const std::vector<dotnetpp_backend::managed_interface::manifold_point>& manifolds)
326{
327 try
328 {
329 auto method = dotnet::make_method_invoker<void(
330 entt::entity, const std::vector<dotnetpp_backend::managed_interface::manifold_point>&)>(
331 engine_script_cache().on_sensor_exit_method, false);
332 method(obj, other.entity(), manifolds);
333 }
334 catch(const dotnet::exception& e)
335 {
337 }
338}
339
340void script_component::on_collision_enter(const dotnet::object& obj,
341 entt::handle other,
342 const std::vector<dotnetpp_backend::managed_interface::manifold_point>& manifolds)
343{
344 try
345 {
346 auto method = dotnet::make_method_invoker<void(
347 entt::entity, const std::vector<dotnetpp_backend::managed_interface::manifold_point>&)>(
348 engine_script_cache().on_collision_enter_method, false);
349 method(obj, other.entity(), manifolds);
350 }
351 catch(const dotnet::exception& e)
352 {
354 }
355}
356
357void script_component::on_collision_exit(const dotnet::object& obj,
358 entt::handle other,
359 const std::vector<dotnetpp_backend::managed_interface::manifold_point>& manifolds)
360{
361 try
362 {
363 auto method = dotnet::make_method_invoker<void(
364 entt::entity, const std::vector<dotnetpp_backend::managed_interface::manifold_point>&)>(
365 engine_script_cache().on_collision_exit_method, false);
366 method(obj, other.entity(), manifolds);
367 }
368 catch(const dotnet::exception& e)
369 {
371 }
372}
373
375{
376 auto& ctx = engine::context();
377 auto& play = ctx.get_cached<play_mode>();
378
379 // Call destroy on marked script components before erasing
380 if(play.is_simulation_running())
381 {
382 for(auto& script : script_components_)
383 {
384 if(script.is_marked_for_destroy())
385 {
387 }
388 }
389 }
390
391 // Now erase the marked components
392 size_t erased = std::erase_if(script_components_,
393 [](const auto& rhs)
394 {
395 return rhs.is_marked_for_destroy();
396 });
397
398 erased += std::erase_if(native_components_,
399 [](const auto& rhs)
400 {
401 return rhs.is_marked_for_destroy();
402 });
403}
404
406{
407 while(!script_components_to_create_.empty())
408 {
409 auto comps = std::move(script_components_to_create_);
410 script_components_to_create_.clear();
411
412 for(auto& script : comps)
413 {
414 if(!script.is_marked_for_destroy())
415 {
416 create(script);
417 }
418 }
419 }
420}
421
423{
424 while(!script_components_to_start_.empty())
425 {
426 auto comps = std::move(script_components_to_start_);
427 script_components_to_start_.clear();
428
429 for(auto& script : comps)
430 {
431 if(!script.is_marked_for_destroy())
432 {
433 start(script);
434 }
435 }
436 }
437}
438
443
445{
446 auto& ctx = engine::context();
447 auto& sys = ctx.get_cached<script_system>();
448 auto& play = ctx.get_cached<play_mode>();
449
450 if(play.is_simulation_running() && sys.is_create_called())
451 {
453
454 if(get_owner().all_of<active_component>())
455 {
456 enable(script_obj, false);
457 }
458 else
459 {
460 disable(script_obj, false);
461 }
462 }
463}
464
465
467{
468 auto obj = type.new_instance();
469 return add_script_component(obj);
470}
471
473{
474 script_object script_obj(obj);
475 return add_script_component(script_obj);
476}
477
478auto script_component::add_script_component(const script_object& script_obj, bool process_callbacks) -> script_object
479{
480 script_components_.emplace_back(script_obj);
481 script_components_to_create_.emplace_back(script_obj);
482 script_components_to_start_.emplace_back(script_obj);
483
484 auto obj = script_obj.pinned->get_object();
485
486 set_entity(obj, get_owner());
487
488 if(process_callbacks)
489 {
490 process_pending_actions(script_obj);
491 }
492
493 return script_obj;
494}
495
497{
498 for(auto& comp : comps)
499 {
500 if(comp.pinned)
501 {
502 add_script_component(comp, false);
503 }
504 }
505}
506
508{
509 for(auto& comp : comps)
510 {
511 if(comp.pinned)
512 {
513 auto obj = comp.pinned->get_object();
514 const auto& type = obj.get_type();
515 if(get_script_component(type).pinned)
516 {
517 continue;
518 }
519
521 }
522 }
523}
524
526{
527 auto obj = type.new_instance();
528 auto& script_obj = native_components_.emplace_back(obj);
529
530 set_entity(obj, get_owner());
531
532 return script_obj;
533}
534
535auto script_component::get_script_components(const dotnet::type& type) -> std::vector<dotnet::object>
536{
537 std::vector<dotnet::object> result;
538 for(const auto& component : script_components_)
539 {
540 auto obj = component.pinned->get_object();
541 const auto& comp_type = obj.get_type();
542
543 if(comp_type.equals(type) || comp_type.is_derived_from(type))
544 {
545 // Validate the object is still valid before adding
546 if(!component.is_marked_for_destroy())
547 {
548 // Since the handle is pinned, the object pointer remains valid
549 result.emplace_back(obj);
550 }
551 }
552 }
553
554 return result;
555}
556
558{
559 auto it = std::find_if(std::begin(script_components_),
560 std::end(script_components_),
561 [&](const auto& component)
562 {
563 auto obj = component.pinned->get_object();
564 const auto& comp_type = obj.get_type();
565 return comp_type.equals(type) || comp_type.is_derived_from(type);
566 });
567
568 if(it != std::end(script_components_))
569 {
570 return *it;
571 }
572
573 return {};
574}
575
577{
578 auto it = std::find_if(std::begin(native_components_),
579 std::end(native_components_),
580 [&](const auto& component)
581 {
582 auto obj = component.pinned->get_object();
583 const auto& comp_type = obj.get_type();
584 return comp_type.equals(type) || comp_type.is_derived_from(type);
585 });
586
587 if(it != std::end(native_components_))
588 {
589 return *it;
590 }
591
592 return {};
593}
594
595auto script_component::remove_script_component(const dotnet::object& obj) -> bool
596{
597 auto checker = [&](const auto& rhs)
598 {
599 return rhs.pinned->get_object().equals(obj);
600 };
601 std::erase_if(script_components_to_create_, checker);
602
603 std::erase_if(script_components_to_start_, checker);
604
605 auto it = std::find_if(std::begin(script_components_), std::end(script_components_), checker);
606
607 if(it != std::end(script_components_))
608 {
609 auto& script_obj = *it;
610
611 if(script_obj.state)
612 {
613
614 // already queued for destruction
615 if(script_obj.state->marked_for_destroy)
616 {
617 APPLOG_WARNING("Script component already queued for destruction");
618 return true;
619 }
620 set_entity(obj, {});
621
622 script_obj.state->marked_for_destroy = true;
623 return true;
624 }
625 return false;
626 }
627
628 return false;
629}
630
631auto script_component::remove_script_component(const dotnet::type& type) -> bool
632{
633 auto checker = [&](const auto& rhs)
634 {
635 return rhs.pinned->get_object().get_type().equals(type);
636 };
637 std::erase_if(script_components_to_create_, checker);
638
639 std::erase_if(script_components_to_start_, checker);
640
641 auto it = std::find_if(std::begin(script_components_), std::end(script_components_), checker);
642
643 if(it != std::end(script_components_))
644 {
645 auto& script_obj = *it;
646
647 if(script_obj.state)
648 {
649 // already queued for destruction
650 if(script_obj.state->marked_for_destroy)
651 {
652 APPLOG_WARNING("Script component already queued for destruction");
653 return true;
654 }
655 auto obj = script_obj.pinned->get_object();
656 set_entity(obj, {});
657
658 script_obj.state->marked_for_destroy = true;
659 return true;
660 }
661
662 return false;
663
664 }
665
666 return false;
667}
668
669auto script_component::remove_native_component(const dotnet::object& obj) -> bool
670{
671 auto it = std::find_if(std::begin(native_components_),
672 std::end(native_components_),
673 [&](const auto& rhs)
674 {
675 return rhs.pinned->get_object().equals(obj);
676 });
677
678 if(it != std::end(native_components_))
679 {
680 auto& script_obj = *it;
681
682 set_entity(obj, {});
683
684 script_obj.state->marked_for_destroy = true;
685 return true;
686 }
687
688 return false;
689}
690
691auto script_component::remove_native_component(const dotnet::type& type) -> bool
692{
693 auto it = std::find_if(std::begin(native_components_),
694 std::end(native_components_),
695 [&](const auto& rhs)
696 {
697 return rhs.pinned->get_object().get_type().equals(type);
698 });
699
700 if(it != std::end(native_components_))
701 {
702 auto& script_obj = *it;
703
704 auto obj = script_obj.pinned->get_object();
705 set_entity(obj, {});
706
707 script_obj.state->marked_for_destroy = true;
708 return true;
709 }
710
711 return false;
712}
713
715{
716 return script_components_;
717}
718
720{
721 return !script_components_.empty();
722}
723
724auto script_component::has_script_components(const std::string& type_name) const -> bool
725{
726 return std::any_of(std::begin(script_components_), std::end(script_components_), [&](const auto& component)
727 {
728 return component.pinned->get_object().get_type().get_name() == type_name;
729 });
730}
731
733{
734 if(!obj.pinned)
735 {
736 return {};
737 }
738
739 auto object = obj.pinned->get_object();
740 const auto& type = object.get_type();
741 try
742 {
743 auto attrs = type.get_attributes();
744 for(auto& attr : attrs)
745 {
746 if(attr.get_type().get_fullname() == "Unravel.Core.ScriptSourceFileAttribute")
747 {
748 auto invoker = dotnet::make_property_invoker<std::string>(attr.get_type(), "Path");
749 return invoker.get_value(attr);
750 }
751 }
752 auto prop = type.get_property("SourceFilePath");
753 auto invoker = dotnet::make_property_invoker<std::string>(prop);
754 return invoker.get_value(object);
755 }
756 catch(const dotnet::exception& e)
757 {
758 return {};
759 }
760}
761} // namespace unravel
entt::handle b
auto get_owner() const noexcept -> entt::const_handle
Gets the owner of the component.
void set_owner(entt::handle owner)
Sets the owner of the component.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
auto get_script_source_location(const script_object &obj) const -> std::string
void on_collision_enter(entt::handle other, const std::vector< manifold_point > &manifolds, bool use_b)
auto get_script_component(const dotnet::type &type) -> script_object
void add_missing_script_components(const script_components_t &comps)
void on_collision_exit(entt::handle other, const std::vector< manifold_point > &manifolds, bool use_b)
void on_sensor_enter(entt::handle other, const std::vector< manifold_point > &manifolds)
auto remove_native_component(const dotnet::object &obj) -> bool
auto remove_script_component(const dotnet::object &obj) -> bool
void process_pending_actions_create(script_object script_obj)
void add_script_components(const script_components_t &comps)
static void on_destroy_component(entt::registry &r, entt::entity e)
Called when the component is destroyed.
auto get_script_components() const -> const script_components_t &
void on_sensor_exit(entt::handle other, const std::vector< manifold_point > &manifolds)
std::vector< script_object > script_components_t
auto add_script_component(const dotnet::type &type) -> script_object
auto has_script_components() const -> bool
static void on_create_component(entt::registry &r, entt::entity e)
Called when the component is created.
auto add_native_component(const dotnet::type &type) -> script_object
auto get_native_component(const dotnet::type &type) -> script_object
#define APPLOG_WARNING(...)
Definition logging.h:19
texture_job_type type
entt::handle entity
static auto context() -> rtti::context &
Definition engine.cpp:111
Owns play-mode state and orchestrates the splash -> running lifecycle.
Definition play_mode.h:17
auto is_create_called() const -> bool
static void log_exception(const dotnet::exception &e, const hpp::source_location &loc=hpp::source_location::current())