eris 1.4.0
A WorldForge client library.
Entity.cpp
1#include <utility>
2
3#ifdef HAVE_CONFIG_H
4 #include "config.h"
5#endif
6
7#include "Entity.h"
8#include "Connection.h"
9#include "TypeInfo.h"
10#include "LogStream.h"
11#include "Exceptions.h"
12#include "Avatar.h"
13#include "Task.h"
14
15#include <wfmath/atlasconv.h>
16#include <Atlas/Objects/Entity.h>
17#include <Atlas/Objects/Operation.h>
18#include <Atlas/Objects/BaseObject.h>
19
20#include <algorithm>
21#include <set>
22#include <cassert>
23
24using namespace Atlas::Objects::Operation;
25using Atlas::Objects::Root;
26using Atlas::Objects::Entity::RootEntity;
27using Atlas::Message::Element;
28using Atlas::Message::ListType;
29using Atlas::Message::MapType;
30using Atlas::Objects::smart_static_cast;
31using Atlas::Objects::smart_dynamic_cast;
32
33using WFMath::TimeStamp;
34using WFMath::TimeDiff;
35
36namespace Eris {
37
38Entity::Entity(std::string id, TypeInfo* ty) :
39 m_type(ty),
40 m_location(nullptr),
41 m_id(std::move(id)),
42 m_stamp(-1.0f),
43 m_visible(false),
44 m_waitingForParentBind(false),
45 m_angularMag(0),
46 m_updateLevel(0),
47 m_hasBBox(false),
48 m_moving(false),
49 m_recentlyCreated(false)
50{
51 assert(!m_id.empty());
52 m_orientation.identity();
53
54
55 if (m_type) {
56 m_type->PropertyChanges.connect(sigc::mem_fun(this, &Entity::typeInfo_PropertyChanges));
57 }
58}
59
60Entity::~Entity()
61{
62 shutdown();
63}
64
65void Entity::shutdown() {
66 setLocation(nullptr);
67
68 for (auto& child: m_contents) {
69 //Release all children.
70 child->setLocation(nullptr, false);
71 }
72 m_contents.clear();
73
74 //Delete any lingering tasks.
75 for (auto& entry : m_tasks) {
76 TaskRemoved(entry.first, entry.second.get());
77 }
78}
79
80void Entity::init(const RootEntity& ge, bool fromCreateOp)
81{
82 // setup initial state
83 firstSight(ge);
84
85 if (fromCreateOp)
86 {
87 m_recentlyCreated = true;
88 }
89}
90
91
92Entity* Entity::getTopEntity()
93{
94 if (m_waitingForParentBind) {
95 return nullptr;
96 }
97 if (!m_location) {
98 return this;
99 }
100 return m_location->getTopEntity();
101}
102
103bool Entity::isAncestorTo(Eris::Entity& entity) const
104{
105 if (!entity.getLocation()) {
106 return false;
107 }
108 if (static_cast<const Eris::Entity*>(this) == entity.getLocation()) {
109 return true;
110 }
111 return isAncestorTo(*entity.getLocation());
112
113}
114
115const Element& Entity::valueOfProperty(const std::string& name) const
116{
118 auto A = m_properties.find(name);
119 if (A == m_properties.end())
120 {
121 if (m_type) {
123 const Element* element = m_type->getProperty(name);
124 if (element) {
125 return *element;
126 }
127 }
128 error() << "did valueOfProperty(" << name << ") on entity " << m_id << " which has no such name";
129 throw InvalidOperation("no such property " + name);
130 } else {
131 return A->second;
132 }
133}
134
135bool Entity::hasProperty(const std::string& p) const
136{
138 if (m_properties.find(p) != m_properties.end()) {
139 return true;
140 } else if (m_type) {
142 if (m_type->getProperty(p) != nullptr) {
143 return true;
144 }
145 }
146 return false;
147}
148
149const Element* Entity::ptrOfProperty(const std::string& name) const
150{
152 auto A = m_properties.find(name);
153 if (A == m_properties.end())
154 {
155 if (m_type) {
157 const Element* element = m_type->getProperty(name);
158 if (element) {
159 return element;
160 }
161 }
162 return nullptr;
163 } else {
164 return &A->second;
165 }
166}
167
168
169Entity::PropertyMap Entity::getProperties() const
170{
172 PropertyMap properties;
173 properties.insert(m_properties.begin(), m_properties.end());
174 if (m_type) {
175 fillPropertiesFromType(properties, *m_type);
176 }
177 return properties;
178}
179
180const Entity::PropertyMap& Entity::getInstanceProperties() const
181{
182 return m_properties;
183}
184
185void Entity::fillPropertiesFromType(Entity::PropertyMap& properties, const TypeInfo& typeInfo) const
186{
187 properties.insert(typeInfo.getProperties().begin(), typeInfo.getProperties().end());
189
190 if (typeInfo.getParent()) {
191 fillPropertiesFromType(properties, *typeInfo.getParent());
192 }
193
194}
195
196sigc::connection Entity::observe(const std::string& propertyName, const PropertyChangedSlot& slot, bool evaluateNow)
197{
198 // sometimes, I realize how great SigC++ is
199 auto connection = m_observers[propertyName].connect(slot);
200 if (evaluateNow) {
201 auto prop = ptrOfProperty(propertyName);
202 if (prop) {
203 slot(*prop);
204 }
205 }
206 return connection;
207}
208
209const WFMath::Point<3>& Entity::getPredictedPos() const
210{
211 return (m_moving ? m_predicted.position : m_position);
212}
213
214const WFMath::Vector<3>& Entity::getPredictedVelocity() const
215{
216 return (m_moving ? m_predicted.velocity : m_velocity);
217}
218
219const WFMath::Quaternion& Entity::getPredictedOrientation() const
220{
221 return (m_moving ? m_predicted.orientation : m_orientation);
222}
223
224bool Entity::isMoving() const
225{
226 return m_moving;
227}
228
229void Entity::updatePredictedState(const WFMath::TimeStamp& t, double simulationSpeed)
230{
231 assert(isMoving());
232
233 if (m_acc.isValid() && m_acc != WFMath::Vector<3>::ZERO()) {
234 double posDeltaTime = static_cast<double>((t - m_lastPosTime).milliseconds()) / 1000.0;
235 m_predicted.velocity = m_velocity + (m_acc * posDeltaTime * simulationSpeed);
236 m_predicted.position = m_position + (m_velocity * posDeltaTime * simulationSpeed) + (m_acc * 0.5 * posDeltaTime * posDeltaTime * simulationSpeed);
237 } else {
238 m_predicted.velocity = m_velocity;
239 if (m_predicted.velocity != WFMath::Vector<3>::ZERO()) {
240 double posDeltaTime = static_cast<double>((t - m_lastPosTime).milliseconds()) / 1000.0;
241 m_predicted.position = m_position + (m_velocity * posDeltaTime * simulationSpeed);
242 } else {
243 m_predicted.position = m_position;
244 }
245 }
246 if (m_angularVelocity.isValid() && m_angularMag != .0) {
247 double orientationDeltaTime = static_cast<double>((t - m_lastOrientationTime).milliseconds()) / 1000.0;
248 m_predicted.orientation = m_orientation * WFMath::Quaternion(m_angularVelocity, m_angularMag * orientationDeltaTime * simulationSpeed);
249 } else {
250 m_predicted.orientation = m_orientation;
251 }
252}
253
254void Entity::firstSight(const RootEntity &gent)
255{
256 if (!gent->isDefaultLoc()) {
257 setLocationFromAtlas(gent->getLoc());
258 } else {
259 setLocation(nullptr);
260 }
261
262 setContentsFromAtlas(gent->getContains());
263 //Since this is the first sight of this entity we should include all type props too.
264 setFromRoot(gent, true);
265}
266
267void Entity::setFromRoot(const Root& obj, bool includeTypeInfoProperties)
268{
269 beginUpdate();
270
271 Atlas::Message::MapType properties;
272 obj->addToMessage(properties);
273
274 properties.erase("id"); //Id can't be changed once it's initially set, which it's at Entity creation time.
275 properties.erase("contains"); //Contains are handled by the setContentsFromAtlas method which should be called separately.
276
277 for (auto& entry : properties) {
278 // see if the value in the sight matches the existing value
279 auto I = m_properties.find(entry.first);
280 if ((I != m_properties.end()) && (I->second == entry.second)) {
281 continue;
282 }
283 try {
284 setProperty(entry.first, entry.second);
285 } catch (const std::exception& ex) {
286 warning() << "Error when setting property '" << entry.first << "'. Message: " << ex.what();
287 }
288 }
289
290 //Add any values found in the type, if they aren't defined in the entity already.
291 if (includeTypeInfoProperties && m_type) {
292 Atlas::Message::MapType typeProperties;
293 fillPropertiesFromType(typeProperties, *m_type);
294 for (auto& entry : typeProperties) {
295 propertyChangedFromTypeInfo(entry.first, entry.second);
296 }
297 }
298
299 endUpdate();
300
301}
302
303void Entity::onTalk(const Atlas::Objects::Operation::RootOperation& talk)
304{
305 const std::vector<Root>& talkArgs = talk->getArgs();
306 if (talkArgs.empty())
307 {
308 warning() << "entity " << getId() << " got sound(talk) with no args";
309 return;
310 }
311
312 for (const auto& arg: talkArgs) {
313 Say.emit(arg);
314 }
315 //Noise.emit(talk);
316}
317
318void Entity::onLocationChanged(Entity* oldLoc)
319{
320 LocationChanged.emit(oldLoc);
321}
322
323void Entity::onMoved(const WFMath::TimeStamp& timeStamp)
324{
325 if (m_moving) {
326 //We should update the predicted pos and velocity.
327 updatePredictedState(timeStamp, 1.0);
328 }
329 Moved.emit();
330}
331
332void Entity::onAction(const Atlas::Objects::Operation::RootOperation& arg, const TypeInfo& typeInfo)
333{
334 Acted.emit(arg, typeInfo);
335}
336
337void Entity::onHit(const Atlas::Objects::Operation::Hit& arg, const TypeInfo& typeInfo)
338{
339 Hit.emit(arg, typeInfo);
340}
341
342void Entity::onSoundAction(const Atlas::Objects::Operation::RootOperation& op, const TypeInfo& typeInfo)
343{
344 Noise.emit(op, typeInfo);
345}
346
347void Entity::onImaginary(const Atlas::Objects::Root& arg)
348{
349 Atlas::Message::Element attr;
350 if (arg->copyAttr("description", attr) == 0 && attr.isString()) {
351 Emote.emit(attr.asString());
352 }
353}
354
355void Entity::setMoving(bool inMotion)
356{
357 assert(m_moving != inMotion);
358
359 m_moving = inMotion;
360 Moving.emit(inMotion);
361
362}
363
364void Entity::onChildAdded(Entity* child)
365{
366 ChildAdded.emit(child);
367}
368
369void Entity::onChildRemoved(Entity* child)
370{
371 ChildRemoved(child);
372}
373
374void Entity::onTaskAdded(const std::string& id, Task* task)
375{
376 TaskAdded(id, task);
377}
378
379
380void Entity::setProperty(const std::string &p, const Element &v)
381{
382 beginUpdate();
383
384 m_properties[p] = v;
385
386 nativePropertyChanged(p, v);
387 onPropertyChanged(p, v);
388
389 // fire observers
390
391 auto obs = m_observers.find(p);
392 if (obs != m_observers.end()) {
393 obs->second.emit(v);
394 }
395
396 addToUpdate(p);
397 endUpdate();
398}
399
400bool Entity::nativePropertyChanged(const std::string& p, const Element& v)
401{
402 // in the future, hash these names to a compile-time integer index, and
403 // make this a switch statement. The same index could also be used
404 // in endUpdate
405
406 if (p == "name") {
407 m_name = v.asString();
408 return true;
409 } else if (p == "stamp") {
410 m_stamp = v.asFloat();
411 return true;
412 } else if (p == "pos") {
413 m_position.fromAtlas(v);
414 return true;
415 } else if (p == "velocity") {
416 m_velocity.fromAtlas(v);
417 return true;
418 } else if (p == "angular") {
419 m_angularVelocity.fromAtlas(v);
420 m_angularMag = m_angularVelocity.mag();
421 return true;
422 } else if (p == "accel") {
423 m_acc.fromAtlas(v);
424 return true;
425 } else if (p == "orientation") {
426 m_orientation.fromAtlas(v);
427 return true;
428 } else if (p == "bbox") {
429 m_bboxUnscaled.fromAtlas(v);
430 m_bbox = m_bboxUnscaled;
431 if (m_scale.isValid() && m_bbox.isValid()) {
432 m_bbox.lowCorner().x() *= m_scale.x();
433 m_bbox.lowCorner().y() *= m_scale.y();
434 m_bbox.lowCorner().z() *= m_scale.z();
435 m_bbox.highCorner().x() *= m_scale.x();
436 m_bbox.highCorner().y() *= m_scale.y();
437 m_bbox.highCorner().z() *= m_scale.z();
438 }
439 m_hasBBox = m_bbox.isValid();
440 return true;
441 } else if (p == "loc") {
442 setLocationFromAtlas(v.asString());
443 return true;
444 } else if (p == "contains") {
445 throw InvalidOperation("tried to set contains via setProperty");
446 } else if (p == "tasks") {
447 updateTasks(v);
448 return true;
449 } else if (p == "scale") {
450 if (v.isList()) {
451 if (v.List().size() == 1) {
452 if (v.List().front().isNum()) {
453 auto num = static_cast<WFMath::CoordType>(v.List().front().asNum());
454 m_scale = WFMath::Vector<3>(num, num, num);
455 }
456 } else {
457 m_scale.fromAtlas(v.List());
458 }
459 } else {
460 m_scale = WFMath::Vector<3>();
461 }
462 m_bbox = m_bboxUnscaled;
463 if (m_scale.isValid() && m_bbox.isValid()) {
464 m_bbox.lowCorner().x() *= m_scale.x();
465 m_bbox.lowCorner().y() *= m_scale.y();
466 m_bbox.lowCorner().z() *= m_scale.z();
467 m_bbox.highCorner().x() *= m_scale.x();
468 m_bbox.highCorner().y() *= m_scale.y();
469 m_bbox.highCorner().z() *= m_scale.z();
470 }
471 return true;
472 }
473
474 return false; // not a native property
475}
476
477void Entity::onPropertyChanged(const std::string& propertyName, const Element& v)
478{
479 // no-op by default
480}
481
482
483void Entity::typeInfo_PropertyChanges(const std::string& propertyName, const Atlas::Message::Element& element)
484{
485 propertyChangedFromTypeInfo(propertyName, element);
486}
487
488void Entity::propertyChangedFromTypeInfo(const std::string& propertyName, const Atlas::Message::Element& element)
489{
491 if (m_properties.find(propertyName) == m_properties.end()) {
492 beginUpdate();
493 nativePropertyChanged(propertyName, element);
494 onPropertyChanged(propertyName, element);
495
496 // fire observers
497
498 ObserverMap::const_iterator obs = m_observers.find(propertyName);
499 if (obs != m_observers.end()) {
500 obs->second.emit(element);
501 }
502
503 addToUpdate(propertyName);
504 endUpdate();
505 }
506}
507
508
509void Entity::beginUpdate()
510{
511 ++m_updateLevel;
512}
513
514void Entity::addToUpdate(const std::string& propertyName)
515{
516 assert(m_updateLevel > 0);
517 m_modifiedProperties.insert(propertyName);
518}
519
520void Entity::endUpdate()
521{
522 if (m_updateLevel < 1)
523 {
524 error() << "mismatched begin/end update pair on entity";
525 return;
526 }
527
528 if (--m_updateLevel == 0) // unlocking updates
529 {
530 Changed.emit(m_modifiedProperties);
531
532 if (m_modifiedProperties.find("pos") != m_modifiedProperties.end() ||
533 m_modifiedProperties.find("velocity") != m_modifiedProperties.end() ||
534 m_modifiedProperties.find("orientation") != m_modifiedProperties.end() ||
535 m_modifiedProperties.find("angular") != m_modifiedProperties.end())
536 {
537 auto now = TimeStamp::now();
538 if (m_modifiedProperties.find("pos") != m_modifiedProperties.end()) {
539 m_lastPosTime = now;
540 }
541 if (m_modifiedProperties.find("orientation") != m_modifiedProperties.end()) {
542 m_lastOrientationTime = now;
543 }
544
545 const WFMath::Vector<3> & velocity = getVelocity();
546 bool nowMoving = (velocity.isValid() && (velocity.sqrMag() > 1e-3)) || (m_angularVelocity.isValid() && m_angularVelocity != WFMath::Vector<3>::ZERO());
547 if (nowMoving != m_moving) {
548 setMoving(nowMoving);
549 }
550
551 onMoved(now);
552 }
553
554 m_modifiedProperties.clear();
555 }
556}
557
558
559void Entity::updateTasks(const Element& e)
560{
561 if (e.isNone()) {
562 for (auto& entry : m_tasks) {
563 TaskRemoved(entry.first, entry.second.get());
564 }
565 m_tasks.clear();
566 return;
567 }
568 if (!e.isMap()) {
569 return; // malformed
570 }
571 auto& taskMap = e.Map();
572
573 auto previousTasks = std::move(m_tasks);
574 m_tasks.clear();
575
576 for (auto& entry : taskMap) {
577 auto& taskElement = entry.second;
578 if (!taskElement.isMap()) {
579 continue;
580 }
581 const MapType& tkmap(taskElement.Map());
582 auto it = tkmap.find("name");
583 if (it == tkmap.end())
584 {
585 error() << "task without name";
586 continue;
587 }
588 if (!it->second.isString())
589 {
590 error() << "task with invalid name";
591 continue;
592 }
593
594 auto tasksI = previousTasks.find(entry.first);
595 std::unique_ptr<Task> task;
596
597 bool newTask = false;
598 if (tasksI == previousTasks.end())
599 { // not found, create a new one
600 task = std::make_unique<Task>(*this, it->second.asString());
601 newTask = true;
602 } else {
603 task = std::move(tasksI->second);
604 previousTasks.erase(entry.first);
605 }
606
607 task->updateFromAtlas(tkmap);
608 if (newTask) {
609 onTaskAdded(entry.first, task.get());
610 }
611 m_tasks.emplace(entry.first, std::move(task));
612 } // of Atlas-specified tasks iteration
613
614 for (auto& entry : previousTasks) {
615
616 if (entry.second) {
617 TaskRemoved(entry.first, entry.second.get());
618 }
619 } // of previous-task cleanup iteration
620}
621
622void Entity::setLocationFromAtlas(const std::string& locId) {
623 if (locId.empty()) {
624 return;
625 }
626
627 Entity* newLocation = getEntity(locId);
628 if (!newLocation) {
629
630 m_waitingForParentBind = true;
631 setVisible(false); // fire disappearance, VisChanged if necessary
632
633 if (m_location) {
634 removeFromLocation();
635 }
636 m_location = nullptr;
637 assert(!m_visible);
638 return;
639 }
640
641 setLocation(newLocation);
642}
643
644void Entity::setLocation(Entity* newLocation, bool removeFromOldLocation)
645{
646 if (newLocation == m_location) return;
647
648 if (newLocation) {
649 m_waitingForParentBind = newLocation->m_waitingForParentBind;
650 }
651
652// do the actual member updating
653 bool wasVisible = isVisible();
654 if (m_location && removeFromOldLocation) {
655 removeFromLocation();
656 }
657
658 Entity* oldLocation = m_location;
659 m_location = newLocation;
660
661 onLocationChanged(oldLocation);
662
663// fire VisChanged and Appearance/Disappearance signals
664 updateCalculatedVisibility(wasVisible);
665
666 if (m_location) {
667 addToLocation();
668 }
669}
670
671void Entity::addToLocation()
672{
673 assert(!m_location->hasChild(m_id));
674 m_location->addChild(this);
675}
676
677void Entity::removeFromLocation()
678{
679 assert(m_location->hasChild(m_id));
680 m_location->removeChild(this);
681}
682
683void Entity::buildEntityDictFromContents(IdEntityMap& dict)
684{
685 for (auto& child : m_contents) {
686 dict[child->getId()] = child;
687 }
688}
689
690void Entity::setContentsFromAtlas(const std::vector<std::string>& contents)
691{
692// convert existing contents into a map, for fast membership tests
693 IdEntityMap oldContents;
694 buildEntityDictFromContents(oldContents);
695
696// iterate over new contents
697 for (auto& content : contents) {
698 Entity* child = nullptr;
699
700 auto J = oldContents.find(content);
701 if (J != oldContents.end()) {
702 child = J->second;
703 assert(child->getLocation() == this);
704 oldContents.erase(J);
705 } else {
706 child = getEntity(content);
707 if (!child) {
708 continue;
709 }
710
711 if (child->m_waitingForParentBind) {
712 assert(!child->m_visible);
713 child->m_waitingForParentBind = false;
714 }
715
716 /* we have found the child, update it's location */
717 child->setLocation(this);
718 }
719
720 child->setVisible(true);
721 } // of contents list iteration
722
723// mark previous contents which are not in new contents as invisible
724 for (auto& entry : oldContents) {
725 entry.second->setVisible(false);
726 }
727}
728
729bool Entity::hasChild(const std::string& eid) const
730{
731 for (auto& m_content : m_contents) {
732 if (m_content->getId() == eid) {
733 return true;
734 }
735 }
736
737 return false;
738}
739
740void Entity::addChild(Entity* e)
741{
742 m_contents.push_back(e);
743 onChildAdded(e);
744 assert(e->getLocation() == this);
745}
746
747void Entity::removeChild(Entity* e)
748{
749 assert(e->getLocation() == this);
750
751 auto I = std::find(m_contents.begin(), m_contents.end(), e);
752 if (I != m_contents.end()) {
753 m_contents.erase(I);
754 onChildRemoved(e);
755 return;
756 }
757 error() << "child " << e->getId() << " of entity " << m_id << " not found doing remove";
758}
759
760// visiblity related methods
761
762void Entity::setVisible(bool vis)
763{
764 // force visibility to false if in limbo; necessary for the character entity,
765 // which otherwise gets double appearances on activation
766 if (m_waitingForParentBind) vis = false;
767
768 bool wasVisible = isVisible(); // store before we update m_visible
769 m_visible = vis;
770
771 updateCalculatedVisibility(wasVisible);
772}
773
774bool Entity::isVisible() const
775{
776 if (m_waitingForParentBind) return false;
777
778 if (m_location) {
779 return m_visible && m_location->isVisible();
780 } else {
781 return m_visible; // only for the root entity
782 }
783}
784
785void Entity::updateCalculatedVisibility(bool wasVisible)
786{
787 bool nowVisible = isVisible();
788 if (nowVisible == wasVisible) return;
789
790 /* the following code looks odd, so remember that only one of nowVisible and
791 wasVisible can ever be true. The structure is necessary so that we fire
792 Appearances top-down, but Disappearances bottom-up. */
793
794 if (nowVisible) {
795 onVisibilityChanged(true);
796 }
797
798 for (auto& item : m_contents) {
799 /* in case this isn't clear; if we were visible, then child visibility
800 was simply it's locally set value; if we were invisible, that the
801 child must also have been invisible too. */
802 bool childWasVisible = wasVisible && item->m_visible;
803 item->updateCalculatedVisibility(childWasVisible);
804 }
805
806 if (wasVisible) {
807 onVisibilityChanged(false);
808 }
809}
810
811void Entity::onVisibilityChanged(bool vis)
812{
813 VisibilityChanged.emit(vis);
814}
815
816boost::optional<std::string> Entity::extractEntityId(const Atlas::Message::Element& element)
817{
818 if (element.isString()) {
819 return element.String();
820 } else if (element.isMap()) {
821 auto I = element.asMap().find("$eid");
822 if (I != element.asMap().end() && I->second.isString()) {
823 return I->second.String();
824 }
825 }
826 return boost::none;
827
828}
829
830
831} // of namespace
Entity is a concrete (instantiable) class representing one game entity.
Definition: Entity.h:56
void setVisible(bool vis)
Definition: Entity.cpp:762
void setLocation(Entity *newLocation, bool removeFromOldLocation=true)
Definition: Entity.cpp:644
Entity * getTopEntity()
Gets the top level entity for this entity, i.e. the parent location which has no parent....
Definition: Entity.cpp:92
bool m_waitingForParentBind
waiting for parent bind
Definition: Entity.h:575
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
Definition: Entity.h:656
sigc::slot< void, const Atlas::Message::Element & > PropertyChangedSlot
A slot which can be used for receiving property update signals.
Definition: Entity.h:123
The representation of an Atlas type (i.e a class or operation definition). This class supports effice...
Definition: TypeInfo.h:33
const Atlas::Message::MapType & getProperties() const
Gets the default properties for this entity type. Note that the map returned does not include inherit...
Definition: TypeInfo.h:207
const TypeInfo * getParent() const
Gets the currently resolved parent TypeInfo instances.
Definition: TypeInfo.h:231
Definition: Account.cpp:33