/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "AIObject.h" #include "AIObjectManager.h" #include "Environment.h" bool CAbstractUntypedRef::IsNil() const { return (m_nID == INVALID_AIOBJECTID); } bool CAbstractUntypedRef::IsSet() const { return (m_nID != INVALID_AIOBJECTID); } tAIObjectID CAbstractUntypedRef::GetObjectID() const { return m_nID; } IAIObject* CAbstractUntypedRef::GetIAIObject() const { CRY_ASSERT(gAIEnv.pObjectContainer); CAIObject* object = gAIEnv.pObjectContainer->GetAIObject(*this); if (!object) { return NULL; } return reinterpret_cast(object); } bool CAbstractUntypedRef::operator==(const IAIObject* const pThatObject) const { return GetIAIObject() == pThatObject; } bool CAbstractUntypedRef::operator!=(const IAIObject* const pThatObject) const { return !(*this == pThatObject); } bool CAbstractUntypedRef::operator==(const CAbstractUntypedRef& that) const { return (m_nID == that.m_nID); } bool CAbstractUntypedRef::operator!=(const CAbstractUntypedRef& that) const { return !(*this == that); } bool CAbstractUntypedRef::operator<(const CAbstractUntypedRef& that) const { return this->m_nID < that.m_nID; } void CAbstractUntypedRef::Assign(tAIObjectID nID) { m_nID = nID; } template CWeakRef CAbstractRef::GetWeakRef() const { return CWeakRef(m_nID); } template CStrongRef::CStrongRef() { this->m_nID = INVALID_AIOBJECTID; } template CStrongRef::CStrongRef(CStrongRef& ref) { this->m_nID = ref.GiveOwnership(); } template CStrongRef::CStrongRef(type_nil_ref) { this->m_nID = INVALID_AIOBJECTID; } template CStrongRef::~CStrongRef() { Release(); } template CStrongRef& CStrongRef::operator=(CStrongRef& ref) { // Do nothing if assigned to self if (this != &ref) { // Release any object currently owned Release(); // Take ownership of object this->m_nID = ref.GiveOwnership(); } return *this; } template T* CStrongRef::GetAIObject() const { return static_cast(this->GetIAIObject()); } template IAIObject* CStrongRef::GetIAIObject() const { return CAbstractUntypedRef::GetIAIObject(); } template bool CStrongRef::Release() { if (this->m_nID != INVALID_AIOBJECTID) { CRY_ASSERT(gAIEnv.pObjectContainer); gAIEnv.pObjectContainer->DeregisterObject(*this); this->m_nID = INVALID_AIOBJECTID; return true; } return false; } template CStrongRef::operator bool() const { return !this->IsNil(); } template void CStrongRef::Serialize(TSerialize ser, const char* sName) { #if _DEBUG if (ser.IsWriting() && this->m_nID != INVALID_AIOBJECTID) { if (CAIObject* pObject = GetAIObject()) { // serializing a strong ref to a pooled object is a bug assert(!pObject->IsFromPool()); } else { assert(false); CryLogAlways("Saving an AI strong ref to an object that doesn't exist - code bug"); } } #endif // Need to take care serializing strong refs into / out of bookmarks. If the owning // object is destroyed (returned to the pool) then the ref will be released, // destroying the referenced object. Then when loading from the pool again // this code cannot recreate the object properly. In that case the reference shouldn't // be serialized as an ID, instead we actually serialize the entire object into the // bookmark as well. if (gAIEnv.pAIObjectManager->IsSerializingBookmark()) { ser.BeginGroup("SubAIObject"); T* pObject = GetAIObject(); SAIObjectCreationHelper objHeader(pObject); objHeader.Serialize(ser); if (ser.IsWriting() && this->IsSet()) { // reserve the object ID. This means no other object will steal the ID later on // (which would cause problems if we tried to recreate this object again). gAIEnv.pObjectContainer->ReserveID(objHeader.objectId); } if (ser.IsReading() && !pObject && objHeader.objectId != INVALID_AIOBJECTID) { pObject = objHeader.RecreateObject(); // reregister with the same ID as previously (will work even though the ID is reserved) gAIEnv.pObjectContainer->RegisterObject(pObject, *this, objHeader.objectId); } if (pObject) { pObject->Serialize(ser); // Tell the object manager not to serialize this // object as well. pObject->SetShouldSerialize(false); } ser.EndGroup(); return; } // [AlexMcC|19.03.10] Make sure we release this object before overwriting it. // Otherwise we'll leak any objects that were created as things were initialized // that are about to be replaced with things we load from save games. if (ser.IsReading()) { Release(); } // Will the compiler coalesce all these? Or, would #T be useful? ser.Value((sName ? sName : "strongRef"), this->m_nID); } template T* CStrongRef::operator->() const { return this->GetAIObject(); } template tAIObjectID CStrongRef::GiveOwnership() { // Here, obviously, we quietly revert to an empty reference without destroying the object we own. // It is not a problem if we are Nil. Callers should be responsible new owners tAIObjectID nID = this->m_nID; this->m_nID = INVALID_AIOBJECTID; return nID; } template CWeakRef StaticCast(const CAbstractRef& ref) { // Allow any cast that static_cast would allow on a bare pointer S* pDummy = static_cast((U*)0); (void)pDummy; return CWeakRef(ref.GetObjectID()); } template CWeakRef::CWeakRef() { this->m_nID = INVALID_AIOBJECTID; } template template CWeakRef::CWeakRef(const CAbstractRef& ref) { // Allow this for anywhere an implicit pointer cast would succeed. T* pDummy = (S*)0; (void)pDummy; this->m_nID = ref.GetObjectID(); } template CWeakRef::CWeakRef(type_nil_ref) { this->m_nID = INVALID_AIOBJECTID; } template void CWeakRef::Reset() { this->m_nID = INVALID_AIOBJECTID; } template bool CWeakRef::IsReset() const { return(this->m_nID == INVALID_AIOBJECTID); } template bool CWeakRef::ValidateOrReset() { if (IsValid()) { return true; } Reset(); return false; } template T* CWeakRef::GetAIObjectOrReset() { T* pObject = this->GetAIObject(); if (!pObject) { Reset(); } return pObject; } template T* CWeakRef::GetAIObject() const { T* pObject = static_cast(this->GetIAIObject()); return pObject; } template bool CWeakRef::IsValid() const { return gAIEnv.pObjectContainer->IsValid(*this); } template void CWeakRef::Assign(const CAbstractRef& ref) { this->m_nID = ref.GetObjectID(); } template void CWeakRef::Serialize(TSerialize ser, const char* sName) { if (ser.IsWriting()) { this->ValidateOrReset(); } ser.Value((sName ? sName : "weakRef"), this->m_nID); // Will the compiler coalesce all these? Or, would #T be useful? } template CWeakRef::CWeakRef(tAIObjectID nID) { this->m_nID = nID; } template CWeakRef GetWeakRef(T* pObject) { if (!pObject) { return NILREF; } return StaticCast(pObject->GetSelfReference()); } template void SerialisationHack(TSerialize ser, const char* sName, T** pObj) { CWeakRef ref; if (ser.IsReading()) { ref.Serialize(ser, sName); *pObj = ref.GetAIObject(); } if (ser.IsWriting()) { if (*pObj) { ref = GetWeakRef(*pObj); } ref.Serialize(ser, sName); } } #ifdef DEBUG_REFERENCES #define SET_DEBUG_OBJ(x) pObj = x #else #define SET_DEBUG_OBJ(x) #endif template CCountedRef::CCountedRef() { this->m_pCounter = NULL; SET_DEBUG_OBJ(NULL); } template CCountedRef::CCountedRef(CStrongRef& ref) { this->m_pCounter = NULL; // Remember there's no point counting Nil references. We also don't check validity, just like Strong. if (!ref.IsNil()) { ObtainCounter(); // Get a reference counting object (starts at 1) this->m_pCounter->m_strongRef = ref; // Acquire the ownership - note we don't need to be friends SET_DEBUG_OBJ(ref.GetAIObject()); } } template CCountedRef::CCountedRef(const CCountedRef& ref) { this->m_pCounter = NULL; // If the other instance is empty, we don't need to do anything. if (ref.m_pCounter) { this->m_pCounter = ref.m_pCounter; // Share the counter ++(this->m_pCounter->m_nRefs); // Increment the count SET_DEBUG_OBJ(ref.GetAIObject()); } } template CCountedRef::CCountedRef(type_nil_ref) { this->m_pCounter = NULL; } template CCountedRef& CCountedRef::operator=(const CCountedRef& ref) { // Do nothing if assigned to same counter if (m_pCounter == ref.m_pCounter) { return *this; } // Release one count on any object currently owned and possibly release object itself Release(); if (ref.m_pCounter != NULL) { this->m_pCounter = ref.m_pCounter; ++(this->m_pCounter->m_nRefs); SET_DEBUG_OBJ(ref.GetAIObject()); } return *this; } template CCountedRef::~CCountedRef() { Release(); } template bool CCountedRef::IsNil() const { return (this->m_pCounter == NULL); } template bool CCountedRef::Release() { if (!this->m_pCounter) { return false; } --(this->m_pCounter->m_nRefs); if (this->m_pCounter->m_nRefs == 0) { ReleaseCounter(); } else { this->m_pCounter = NULL; SET_DEBUG_OBJ(NULL); } return true; } template CCountedRef::operator bool() const { return this->m_pCounter != NULL; } template void CCountedRef::Serialize(TSerialize ser, const char* sName) { // (MATT) Perhaps this could be tidied up {2009/04/02} // Here, for now, I have to hack it and just handle the case of a count of 1 when serialisation occurs. // Given that the multiple references are really only needed for STL compatibility, that's safe for now. // (MATT) No it's not, when counted refs are being copied _during_ serisation to help implement the process {2009/04/01} //assert(!this->m_pCounter || this->m_pCounter->m_nRefs <= 1); // I think that when reading, an initial count greater than one is a very bad thing if (ser.IsReading()) { assert(!this->m_pCounter || this->m_pCounter->m_nRefs == 1); } // Easiest efficient way to do this: a local copy of our Strong ref // We temporarily transfer any ownership to this strong ref CStrongRef strongRef; if (this->m_pCounter) { strongRef = this->m_pCounter->m_strongRef; // Note this takes ownership } // Serialise that, which of course will write it out or read it in, which will replace ownership if need be strongRef.Serialize(ser, (sName ? sName : "countedRef")); // Now we must translate back into a counter object, if any is needed // This is true whether we wrote out, and need our counter working again to continue play, // or whether we read in and so need to make a counter object to start working with the new value. // If we need a counter now and we don't already have one, create one if (strongRef.IsSet() && !this->m_pCounter) { ObtainCounter(); } if (this->m_pCounter) { // If we had a counter but don't need it anymore, because the object we owned has been replaced by Nil, we must delete the counter if (strongRef.IsNil()) { ForceReleaseCounter(); } else { this->m_pCounter->m_strongRef = strongRef; // Note this passes any ownership back } } // We shouldn't have local ownership when we finish assert(strongRef.IsNil()); } template bool CCountedRef::operator==(const IAIObject* const pThatObject) const { // No reference is equivalent to Nil is equivalent to a NULL pointer if (!m_pCounter) { return (pThatObject == NULL); } return m_pCounter->m_strongRef == pThatObject; } template bool CCountedRef::operator==(const CAbstractUntypedRef& that) const { // No reference is equivalent to Nil if (!m_pCounter) { return (that.IsNil()); } return m_pCounter->m_strongRef == that; } template CWeakRef CCountedRef::GetWeakRef() const { return (m_pCounter ? CWeakRef(m_pCounter->m_strongRef.GetObjectID()) : NILREF); } template T* CCountedRef::GetAIObject() const { return (m_pCounter ? m_pCounter->m_strongRef.GetAIObject() : NULL); } template T* CCountedRef::operator->() const { return this->m_pCounter->m_strongRef.GetAIObject(); } template tAIObjectID CCountedRef::GetObjectID() const { return (m_pCounter ? m_pCounter->m_strongRef.GetObjectID() : INVALID_AIOBJECTID); } template void CCountedRef::ObtainCounter() { assert(!m_pCounter); m_pCounter = new SRefCounter(); } template void CCountedRef::ReleaseCounter() { assert(m_pCounter && m_pCounter->m_nRefs == 0); SAFE_DELETE(m_pCounter); SET_DEBUG_OBJ(NULL); } template void CCountedRef::ForceReleaseCounter() { assert(m_pCounter && m_pCounter->m_nRefs == 1); SAFE_DELETE(m_pCounter); SET_DEBUG_OBJ(NULL); }