#ifndef OBJREFERENCES #define OBJREFERENCES #include #include #include #include #include #include /* * The basic idea here is that over the course of editing and generally making changes * in the editor, stuff tends to get created and deleted. When things are deleted they * are actually deleted from memory, and recreated from scratch on undo. This poses a * problem for undo actions that need to operate on a specific object because if their * target object has been deleted and remade then the undo actions are stuck with a * garbage pointer to deleted data. This generally results in crashes. * * These pointer classes solve the problem by encapsulating a reference to the object * using IDs and indices instead of storing a direct pointer to the object itself. This * way the correct object is always looked up from the area/scene/etc that it's from and * the pointer is always valid. They are designed to be as easy and pain-free to use as * possible. * * Most of this stuff could be moved to Core if it turns out to be useful in other places. * QList dependencies would need to be removed but they could easily be replaced with * std::list instead. */ #define DEFINE_PTR_LIST_CLASS(ClassName, PtrClassName, DereferencedClassName) \ class ClassName : public QList \ { \ public: \ ClassName() : QList() {} \ \ ClassName(const QList& rkIn) \ { \ foreach (DereferencedClassName InObj, rkIn) \ { \ *this << InObj; \ } \ } \ \ QList DereferenceList() const \ { \ QList Out; \ \ foreach (const PtrClassName& rkPtr, *this) \ Out << *rkPtr; \ \ return Out; \ } \ }; class CNodePtr { u32 mNodeID; CScene *mpScene; bool mValid; public: CNodePtr() { SetNode(nullptr); } CNodePtr(CSceneNode *pNode) { SetNode(pNode); } inline void SetNode(CSceneNode *pNode) { mNodeID = pNode ? pNode->ID() : 0; mpScene = pNode ? pNode->Scene() : nullptr; mValid = pNode ? true : false; } inline bool Valid() const { return mValid; } inline u32 NodeID() const { return mNodeID; } inline CScene* Scene() const { return mpScene; } inline CSceneNode* operator* () const { return mValid ? mpScene->NodeByID(mNodeID) : nullptr; } inline CSceneNode* operator->() const { return mValid ? mpScene->NodeByID(mNodeID) : nullptr; } inline CNodePtr& operator=(CSceneNode *pNode) { SetNode(pNode); return *this; } inline bool operator==(const CNodePtr& rkOther) const { return (mNodeID == rkOther.mNodeID && mpScene == rkOther.mpScene); } }; class CInstancePtr { u32 mInstanceID; CGameArea *mpArea; bool mValid; public: CInstancePtr() { SetInstance(nullptr); } CInstancePtr(CScriptObject *pInst) { SetInstance(pInst); } inline void SetInstance(CScriptObject *pInst) { mInstanceID = pInst ? pInst->InstanceID() : 0; mpArea = pInst ? pInst->Area() : nullptr; mValid = pInst ? true : false; } inline u32 InstanceID() const { return mInstanceID; } inline CGameArea* Area() const { return mpArea; } inline CScriptObject* operator* () const { return mValid ? mpArea->InstanceByID(mInstanceID) : nullptr; } inline CScriptObject* operator->() const { return mValid ? mpArea->InstanceByID(mInstanceID) : nullptr; } inline CInstancePtr& operator=(CScriptObject *pInst) { SetInstance(pInst); return *this; } inline bool operator==(const CInstancePtr& rkOther) const { return (mInstanceID == rkOther.mInstanceID && mpArea == rkOther.mpArea); } }; class CPropertyPtr { CInstancePtr mpInstance; TIDString mPropertyID; bool mValid; public: CPropertyPtr() { SetProperty(nullptr); } CPropertyPtr(IProperty *pProp) { SetProperty(pProp); } inline void SetProperty(IProperty *pProp) { mpInstance = pProp ? pProp->Instance() : nullptr; mPropertyID = pProp ? pProp->IDString(true) : ""; mValid = pProp ? true : false; } inline CInstancePtr InstancePtr() const { return mpInstance; } inline TIDString PropertyID() const { return mPropertyID; } inline IProperty* operator* () const { return mValid ? mpInstance->PropertyByIDString(mPropertyID) : nullptr; } inline IProperty* operator->() const { return mValid ? mpInstance->PropertyByIDString(mPropertyID) : nullptr; } inline CPropertyPtr& operator=(IProperty *pProp) { SetProperty(pProp); return *this; } inline bool operator==(const CPropertyPtr& rkOther) const { return (mpInstance == rkOther.mpInstance && mPropertyID == rkOther.mPropertyID); } }; class CLinkPtr { CInstancePtr mpInstance; u32 mLinkIndex; bool mValid; public: CLinkPtr() { SetLink(nullptr); } CLinkPtr(CLink *pLink) { SetLink(pLink); } inline void SetLink(CLink *pLink) { mpInstance = pLink ? pLink->Sender() : 0; mLinkIndex = pLink ? pLink->SenderIndex() : 0; mValid = pLink ? true : false; } inline u32 LinkIndex() const { return mLinkIndex; } inline CLink* operator* () const { return mValid ? mpInstance->Link(eOutgoing, mLinkIndex) : nullptr; } inline CLink* operator->() const { return mValid ? mpInstance->Link(eOutgoing, mLinkIndex) : nullptr; } inline CLinkPtr& operator=(CLink *pLink) { SetLink(pLink); return *this; } inline bool operator==(const CLinkPtr& rkOther) const { return (mpInstance == rkOther.mpInstance && mLinkIndex == rkOther.mLinkIndex); } }; DEFINE_PTR_LIST_CLASS(CNodePtrList, CNodePtr, CSceneNode*) DEFINE_PTR_LIST_CLASS(CInstancePtrList, CInstancePtr, CScriptObject*) DEFINE_PTR_LIST_CLASS(CPropertyPtrList, CPropertyPtr, IProperty*) DEFINE_PTR_LIST_CLASS(CLinkPtrList, CLinkPtr, CLink*) #endif // OBJREFERENCES