You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
650 lines
16 KiB
650 lines
16 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "plKeyImp.h" |
|
#include "hsStream.h" |
|
#include "hsKeyedObject.h" |
|
#include "hsResMgr.h" |
|
#include "hsTypes.h" |
|
#include "pnMessage/plRefMsg.h" |
|
#include "pnMessage/plSelfDestructMsg.h" |
|
#include "hsTimer.h" |
|
#include "plProfile.h" |
|
#include "plgDispatch.h" |
|
|
|
plProfile_CreateMemCounter("Keys", "Memory", KeyMem); |
|
|
|
static UInt32 CalcKeySize(plKeyImp* key) |
|
{ |
|
UInt32 nameLen = 0; |
|
if (key->GetUoid().GetObjectName()) |
|
nameLen = strlen(key->GetUoid().GetObjectName()) + 1; |
|
return sizeof(plKeyImp) + nameLen; |
|
} |
|
|
|
//#define LOG_ACTIVE_REFS |
|
#ifdef LOG_ACTIVE_REFS |
|
#include "plCreatableIndex.h" |
|
static const char* kObjName = "GUI_District_OptionsMenuGUI"; |
|
static UInt16 kClassType = CLASS_INDEX_SCOPED(plSceneNode); |
|
static UInt32 kCloneID = 0; |
|
hsBool IsTrackedKey(const plKeyImp* key) |
|
{ |
|
return hsStrEQ(key->GetName(), kObjName) && key->GetUoid().GetClassType() == kClassType && key->GetUoid().GetCloneID() == kCloneID; |
|
} |
|
#endif |
|
|
|
plKeyImp::plKeyImp() : |
|
fObjectPtr(nil), |
|
fStartPos(-1), |
|
fDataLen(-1), |
|
fNumActiveRefs(0), |
|
fPendingRefs(1), |
|
fCloneOwner(nil) |
|
{ |
|
#ifdef HS_DEBUGGING |
|
fIDName = nil; |
|
fClassType = nil; |
|
#endif |
|
} |
|
|
|
plKeyImp::plKeyImp(plUoid u, UInt32 pos,UInt32 len): |
|
fUoid(u), |
|
fObjectPtr(nil), |
|
fStartPos(pos), |
|
fDataLen(len), |
|
fNumActiveRefs(0), |
|
fPendingRefs(1), |
|
fCloneOwner(nil) |
|
{ |
|
plProfile_NewMem(KeyMem, CalcKeySize(this)); |
|
|
|
#ifdef HS_DEBUGGING |
|
fIDName = fUoid.GetObjectName(); |
|
fClassType = plFactory::GetNameOfClass( fUoid.GetClassType() ); |
|
#endif |
|
} |
|
|
|
plKeyImp::~plKeyImp() |
|
{ |
|
plProfile_DelMem(KeyMem, CalcKeySize(this)); |
|
|
|
#if defined(HS_DEBUGGING) && 0 |
|
// Colin debugging |
|
char buf[512]; |
|
sprintf(buf, "0x%x %s %s\n", this, fIDName, fClassType); |
|
hsStatusMessage(buf); |
|
#endif |
|
|
|
hsAssert(fObjectPtr == nil, "Deleting non-nil key! Bad idea!"); |
|
|
|
if (fCloneOwner != nil) |
|
{ |
|
// Must be a clone, remove us from our parent list |
|
((plKeyImp*)fCloneOwner)->RemoveClone(this); |
|
} |
|
|
|
for (int i = 0; i < fClones.GetCount(); i++) |
|
{ |
|
if (fClones[i]) |
|
fClones[i]->UnRegister(); |
|
} |
|
fClones.Reset(); |
|
|
|
// This is normally empty by now, but if we never got loaded, |
|
// there will be unsent ref messages in the NotifyCreated list |
|
ClearNotifyCreated(); |
|
} |
|
|
|
void plKeyImp::SetUoid(const plUoid& uoid) |
|
{ |
|
fUoid = uoid; |
|
#ifdef HS_DEBUGGING |
|
fIDName = fUoid.GetObjectName(); |
|
fClassType = plFactory::GetNameOfClass(fUoid.GetClassType()); |
|
#endif |
|
} |
|
|
|
const char* plKeyImp::GetName() const |
|
{ |
|
return fUoid.GetObjectName(); |
|
} |
|
|
|
hsKeyedObject* plKeyImp::GetObjectPtr() |
|
{ |
|
return ObjectIsLoaded(); |
|
} |
|
|
|
hsKeyedObject* plKeyImp::ObjectIsLoaded() const |
|
{ |
|
return this ? fObjectPtr : nil; |
|
} |
|
|
|
// Copy the contents of p for cloning process |
|
void plKeyImp::CopyForClone(const plKeyImp *p, UInt32 playerID, UInt32 cloneID) |
|
{ |
|
fObjectPtr = nil; // the clone object start as nil |
|
fUoid = p->GetUoid(); // we will set the UOID the same to start |
|
|
|
#ifdef HS_DEBUGGING |
|
fIDName = fUoid.GetObjectName(); |
|
fClassType = plFactory::GetNameOfClass( fUoid.GetClassType() ); |
|
#endif |
|
|
|
fStartPos = p->GetStartPos(); |
|
fDataLen = p->GetDataLen(); |
|
fUoid.SetClone(playerID, cloneID); |
|
} |
|
|
|
hsKeyedObject* plKeyImp::VerifyLoaded() |
|
{ |
|
if (!fObjectPtr) |
|
hsgResMgr::ResMgr()->ReadObject(this); |
|
|
|
return fObjectPtr; |
|
} |
|
|
|
//// Read/Write ////////////////////////////////////////////////////////////// |
|
// The actual key read/writes for the index file, the only time the whole |
|
// key is ever actually stored. |
|
|
|
void plKeyImp::Read(hsStream* s) |
|
{ |
|
fUoid.Read(s); |
|
s->ReadSwap(&fStartPos); |
|
s->ReadSwap(&fDataLen); |
|
|
|
plProfile_NewMem(KeyMem, CalcKeySize(this)); |
|
|
|
#ifdef HS_DEBUGGING |
|
fIDName = fUoid.GetObjectName(); |
|
fClassType = plFactory::GetNameOfClass(fUoid.GetClassType()); |
|
#endif |
|
} |
|
|
|
void plKeyImp::SkipRead(hsStream* s) |
|
{ |
|
plUoid tempUoid; |
|
tempUoid.Read(s); |
|
s->ReadSwap32(); |
|
s->ReadSwap32(); |
|
} |
|
|
|
void plKeyImp::Write(hsStream* s) |
|
{ |
|
fUoid.Write(s); |
|
s->WriteSwap(fStartPos); |
|
s->WriteSwap(fDataLen); |
|
if (fStartPos == (UInt32)-1) |
|
int foo = 0; |
|
} |
|
|
|
//// WriteObject ///////////////////////////////////////////////////////////// |
|
// Writes the key's object to the already opened stream |
|
|
|
void plKeyImp::WriteObject(hsStream* stream) |
|
{ |
|
hsKeyedObject* ko = ObjectIsLoaded(); |
|
if (ko == nil) |
|
{ |
|
// Mark the key as not written |
|
fStartPos = (UInt32)-1; |
|
fDataLen = (UInt32)-1; |
|
return; |
|
} |
|
|
|
fStartPos = stream->GetPosition(); |
|
hsgResMgr::ResMgr()->WriteCreatable(stream, ko); |
|
fDataLen = stream->GetPosition() - fStartPos; |
|
} |
|
|
|
void plKeyImp::UnRegister() // called from plRegistry |
|
{ |
|
plKey safeRefUntilWereDone = plKey::Make(this); |
|
|
|
hsKeyedObject* ko = ObjectIsLoaded(); |
|
if (ko) |
|
{ |
|
INotifyDestroyed(); |
|
fObjectPtr = nil; |
|
fNumActiveRefs = 0; |
|
|
|
hsRefCnt_SafeUnRef(ko); |
|
} |
|
IClearRefs(); |
|
ClearNotifyCreated(); |
|
}; |
|
|
|
hsKeyedObject* plKeyImp::RefObject(plRefFlags::Type flags) |
|
{ |
|
if ((flags == plRefFlags::kPassiveRef) && !ObjectIsLoaded()) |
|
return nil; |
|
|
|
#ifdef LOG_ACTIVE_REFS |
|
if (IsTrackedKey(this)) |
|
hsStatusMessageF("@@@ RefObject adding active ref to %s (%d total)", kObjName, fNumActiveRefs+1); |
|
#endif // LOG_ACTIVE_REFS |
|
|
|
IncActiveRefs(); |
|
|
|
return VerifyLoaded(); // load object on demand |
|
} |
|
|
|
void plKeyImp::UnRefObject(plRefFlags::Type flags) |
|
{ |
|
// Rather than using hsRefCnt's, make Ref and |
|
// UnRef work with ActiveRef system |
|
if ( (flags == plRefFlags::kPassiveRef) && !ObjectIsLoaded()) |
|
return; |
|
|
|
#ifdef LOG_ACTIVE_REFS |
|
if (IsTrackedKey(this)) |
|
hsStatusMessageF("@@@ UnRefObject releasing active ref to %s (%d total)", kObjName, fNumActiveRefs-1); |
|
#endif // LOG_ACTIVE_REFS |
|
DecActiveRefs(); |
|
|
|
if( !GetActiveRefs() ) |
|
{ |
|
INotifyDestroyed(); |
|
|
|
IClearRefs(); |
|
ClearNotifyCreated(); |
|
|
|
plKey key=plKey::Make( this ); // for linux build |
|
plSelfDestructMsg* nuke = TRACKED_NEW plSelfDestructMsg( key ); |
|
plgDispatch::Dispatch()->MsgSend(nuke); |
|
} |
|
} |
|
|
|
hsKeyedObject* plKeyImp::SetObjectPtr(hsKeyedObject* p) |
|
{ |
|
hsKeyedObject* retVal = nil; |
|
|
|
// If our object is the only one with a ref to us, this function will crash, so we |
|
// make sure we have an extra ref, just like in UnRegister(). |
|
plKey safeRefUntilWereDone = plKey::Make(this); |
|
|
|
if (p) |
|
{ |
|
#ifdef HS_DEBUGGING |
|
if (fClassType) |
|
{ |
|
char str[2048]; |
|
sprintf(str, "Mismatch of class (we are a %s, given a %s)", fClassType, p->ClassName()); |
|
hsAssert(fClassType == p->ClassName() || strcmp(fClassType, p->ClassName()) == 0, str); // points to static |
|
} |
|
else |
|
fClassType = p->ClassName(); |
|
#endif |
|
|
|
hsAssert(!fObjectPtr, "Setting an ObjectPtr thats already Set!"); |
|
|
|
retVal = fObjectPtr = p; |
|
} |
|
else |
|
{ |
|
if (fObjectPtr) |
|
UnRegister(); |
|
|
|
fObjectPtr = nil; |
|
retVal = nil; |
|
} |
|
|
|
return retVal; |
|
} |
|
|
|
void plKeyImp::ClearNotifyCreated() |
|
{ |
|
for (int i = 0; i < fNotifyCreated.GetCount(); i++) |
|
hsRefCnt_SafeUnRef(fNotifyCreated[i]); |
|
fNotifyCreated.Reset(); |
|
fNotified.Reset(); |
|
fActiveRefs.Reset(); |
|
} |
|
|
|
void plKeyImp::AddNotifyCreated(plRefMsg* msg, plRefFlags::Type flags) |
|
{ |
|
if (!(flags == plRefFlags::kPassiveRef)) |
|
{ |
|
#ifdef LOG_ACTIVE_REFS |
|
if (IsTrackedKey(this)) |
|
{ |
|
hsStatusMessageF("@@@ %s(%s) adding active ref to %s (%d total)", msg->GetReceiver(0)->GetName(), |
|
plFactory::GetNameOfClass(msg->GetReceiver(0)->GetUoid().GetClassType()), kObjName, fNumActiveRefs+1); |
|
} |
|
#endif // LOG_ACTIVE_REFS |
|
|
|
IncActiveRefs(); |
|
SetActiveRef(GetNumNotifyCreated()); |
|
} |
|
|
|
hsRefCnt_SafeRef(msg); |
|
fNotifyCreated.Append(msg); |
|
} |
|
|
|
void plKeyImp::RemoveNotifyCreated(int i) |
|
{ |
|
hsRefCnt_SafeUnRef(fNotifyCreated[i]); |
|
fNotifyCreated.Remove(i); |
|
|
|
fNotified.RemoveBit(i); |
|
fActiveRefs.RemoveBit(i); |
|
} |
|
|
|
void plKeyImp::AddRef(plKeyImp* key) const |
|
{ |
|
fPendingRefs++; |
|
fRefs.Append(key); |
|
} |
|
|
|
|
|
void plKeyImp::RemoveRef(plKeyImp* key) const |
|
{ |
|
int idx = fRefs.Find(key); |
|
if (fRefs.kMissingIndex != idx) |
|
fRefs.Remove(idx); |
|
} |
|
|
|
void plKeyImp::AddClone(plKeyImp* key) |
|
{ |
|
hsAssert(!GetClone(key->GetUoid().GetClonePlayerID(), key->GetUoid().GetCloneID()), |
|
"Adding a clone which is already there?"); |
|
|
|
key->fCloneOwner = plKey::Make(this); |
|
fClones.Append(key); |
|
} |
|
|
|
void plKeyImp::RemoveClone(plKeyImp* key) const |
|
{ |
|
if (key->GetUoid().IsClone()) |
|
{ |
|
int idx = fClones.Find(key); |
|
if (idx != -1) |
|
{ |
|
fClones.Remove(idx); |
|
key->fCloneOwner = nil; |
|
} |
|
} |
|
} |
|
|
|
plKey plKeyImp::GetClone(UInt32 playerID, UInt32 cloneID) const |
|
{ |
|
for (int i = 0; i < fClones.GetCount(); i++) |
|
{ |
|
plKeyImp* cloneKey = fClones[i]; |
|
if (cloneKey |
|
&& cloneKey->GetUoid().GetCloneID() == cloneID |
|
&& cloneKey->GetUoid().GetClonePlayerID() == playerID) |
|
return plKey::Make(cloneKey); |
|
} |
|
|
|
return plKey(); |
|
} |
|
|
|
UInt32 plKeyImp::GetNumClones() |
|
{ |
|
return fClones.GetCount(); |
|
} |
|
|
|
plKey plKeyImp::GetCloneByIdx(UInt32 idx) |
|
{ |
|
if (idx < fClones.GetCount()) |
|
return plKey::Make(fClones[idx]); |
|
|
|
return nil; |
|
} |
|
|
|
void plKeyImp::SatisfyPending(plRefMsg* msg) const |
|
{ |
|
for (int i = 0; i < msg->GetNumReceivers(); i++) |
|
((plKeyImp*)msg->GetReceiver(i))->SatisfyPending(); |
|
} |
|
|
|
void plKeyImp::SatisfyPending() const |
|
{ |
|
hsAssert(fPendingRefs > 0, "Have more requests satisfied than we made"); |
|
if (!--fPendingRefs) |
|
{ |
|
#ifdef PL_SEND_SATISFIED |
|
plSatisfiedMsg* msg = TRACKED_NEW plSatisfiedMsg(this); |
|
plgDispatch::MsgSend(msg); |
|
#endif // PL_SEND_SATISFIED |
|
} |
|
} |
|
|
|
void plKeyImp::ISetupNotify(plRefMsg* msg, plRefFlags::Type flags) |
|
{ |
|
msg->SetSender(nil); |
|
|
|
AddNotifyCreated(msg, flags); |
|
|
|
hsAssert(msg->GetNumReceivers(), "nil object getting a reference"); |
|
for (int i = 0; i < msg->GetNumReceivers(); i++) |
|
((plKeyImp*)msg->GetReceiver(i))->AddRef(plKey::Make(this)); |
|
} |
|
|
|
void plKeyImp::SetupNotify(plRefMsg* msg, plRefFlags::Type flags) |
|
{ |
|
hsKeyedObject* ko = ObjectIsLoaded(); |
|
|
|
ISetupNotify(msg, flags); |
|
|
|
// the KeyedObject is already Loaded, so we better go ahead and send the notify message just added. |
|
if (ko) |
|
{ |
|
hsRefCnt_SafeRef(ko); |
|
msg->SetRef(ko); |
|
msg->SetTimeStamp(hsTimer::GetSysSeconds()); |
|
|
|
// Add ref, since Dispatch will unref this msg but we want to keep using it. |
|
hsRefCnt_SafeRef(msg); |
|
|
|
SetNotified(GetNumNotifyCreated()-1); |
|
SatisfyPending(msg); |
|
|
|
#ifdef LOAD_IN_THREAD // test for resLoader |
|
plgDispatch::Dispatch()->MsgQueue(msg); |
|
#else |
|
plgDispatch::Dispatch()->MsgSend(msg); |
|
#endif |
|
|
|
hsRefCnt_SafeUnRef(ko); |
|
} |
|
hsRefCnt_SafeUnRef(msg); |
|
} |
|
|
|
// We could just call NotifyCreated() on all our fRefs, and then fix |
|
// up fNotified to only get set when the message actually was delivered (i.e. |
|
// refMsg->GetReceiver(0)->GetObjectPtr() != nil. But that only really works |
|
// if we guarantee the refMsg->GetNumReceivers() == 1. |
|
// This looks like it'll take forever to run, but this is only called right |
|
// when our object has just been loaded, at which time normally fRefs.GetCount() == 0. |
|
void plKeyImp::INotifySelf(hsKeyedObject* ko) |
|
{ |
|
for (int i = 0; i < fRefs.GetCount(); i++) |
|
{ |
|
hsKeyedObject* rcv = fRefs[i]->GetObjectPtr(); |
|
if (rcv) |
|
{ |
|
for (int j = 0; j < fRefs[i]->fNotifyCreated.GetCount(); j++) |
|
{ |
|
plRefMsg* refMsg = fRefs[i]->fNotifyCreated[j]; |
|
if (refMsg && refMsg->GetRef() && !fRefs[i]->IsNotified(j)) |
|
{ |
|
hsAssert(refMsg->GetRef() == rcv, "Ref message out of sync with its ref"); |
|
|
|
// GetNumReceivers() should always be 1 for a refMsg. |
|
for (int k = 0; k < refMsg->GetNumReceivers(); k++) |
|
{ |
|
if (&(*refMsg->GetReceiver(k)) == (plKeyData*)this) |
|
{ |
|
fRefs[i]->SetNotified(j); |
|
fRefs[i]->SatisfyPending(refMsg); |
|
|
|
hsRefCnt_SafeRef(refMsg); |
|
plgDispatch::MsgSend(refMsg); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void plKeyImp::NotifyCreated() |
|
{ |
|
hsKeyedObject* ko = GetObjectPtr(); |
|
hsRefCnt_SafeRef(ko); |
|
hsAssert(ko, "Notifying of created before on nil object"); |
|
|
|
INotifySelf(ko); |
|
|
|
for (int i = 0; i < GetNumNotifyCreated(); i++) |
|
{ |
|
if (!IsNotified(i) && GetNotifyCreated(i)->GetReceiver(0)->GetObjectPtr()) |
|
{ |
|
plRefMsg* msg = GetNotifyCreated(i); |
|
msg->SetRef(ko); |
|
msg->SetTimeStamp(hsTimer::GetSysSeconds()); |
|
msg->SetContext(plRefMsg::kOnCreate); |
|
hsRefCnt_SafeRef(msg); |
|
|
|
SetNotified(i); |
|
SatisfyPending(msg); |
|
|
|
plgDispatch::MsgSend(msg); |
|
} |
|
} |
|
hsRefCnt_SafeUnRef(ko); |
|
} |
|
|
|
void plKeyImp::INotifyDestroyed() |
|
{ |
|
hsKeyedObject* ko = GetObjectPtr(); |
|
hsAssert(ko, "Notifying of destroy on already destroyed"); |
|
int i; |
|
for( i = 0; i < GetNumNotifyCreated(); i++ ) |
|
{ |
|
hsAssert(ko, "Notifying of destroy on already destroyed"); |
|
plRefMsg* msg = GetNotifyCreated(i); |
|
msg->SetRef(ko); |
|
msg->SetTimeStamp(hsTimer::GetSysSeconds()); |
|
msg->SetContext(plRefMsg::kOnDestroy); |
|
hsRefCnt_SafeRef(msg); |
|
msg->Send(); |
|
} |
|
fNotified.Clear(); |
|
} |
|
|
|
void plKeyImp::IClearRefs() |
|
{ |
|
while (GetNumRefs()) |
|
IRelease(GetRef(0)); |
|
fRefs.Reset(); |
|
|
|
for (int i = 0; i < GetNumNotifyCreated(); i++) |
|
{ |
|
plRefMsg* msg = GetNotifyCreated(i); |
|
for (int j = 0; j < msg->GetNumReceivers(); j++) |
|
((plKeyImp*)msg->GetReceiver(j))->RemoveRef(this); |
|
} |
|
} |
|
|
|
void plKeyImp::Release(plKey targetKey) |
|
{ |
|
IRelease((plKeyImp*)targetKey); |
|
} |
|
|
|
void plKeyImp::IRelease(plKeyImp* iTargetKey) |
|
{ |
|
// Remove the target key from my ref list |
|
RemoveRef(iTargetKey); |
|
|
|
// Inspect the target key to find whether it is supposed to send a message |
|
// to me on destruction, and to find out if I have an active of passive |
|
// ref on this key. Not sure why I don't track my own active/passive ref states |
|
hsBool isActive = false; |
|
int iTarg = -1; |
|
for (int i = 0; (iTarg < 0) && (i < iTargetKey->GetNumNotifyCreated()); i++) |
|
{ |
|
plMessage* rcvMsg = iTargetKey->GetNotifyCreated(i); |
|
for (int j = 0; j < rcvMsg->GetNumReceivers(); j++) |
|
{ |
|
if (&(*rcvMsg->GetReceiver(j)) == (plKeyData*)this) |
|
{ |
|
isActive = iTargetKey->IsActiveRef(iTarg = i); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (iTarg < 0) |
|
{ |
|
// If it doesn't send me a message on destruction, I am assuming I don't have an |
|
// active ref on the key (seems to be always true, but should we depend on it?) |
|
// Since it doesn't send me a message, and won't be destroyed by the release, no |
|
// need to do anything more here |
|
return; |
|
} |
|
|
|
// If releasing this target causes its destruction, we'll notify |
|
// everyone that target is dead. Otherwise, we'll remove |
|
// releaser from target's notify list and notify releaser |
|
// it's been removed. Object doesn't actually get deleted until |
|
// it receives the SelfDestruct msg, by which time everyone referencing |
|
// it has been notified it is going away. |
|
#ifdef LOG_ACTIVE_REFS |
|
if (isActive && IsTrackedKey(iTargetKey)) |
|
hsStatusMessageF("@@@ %s(%s) releasing active ref on %s (%d total)", GetName(), plFactory::GetNameOfClass(GetUoid().GetClassType()), kObjName, iTargetKey->fNumActiveRefs-1); |
|
#endif // LOG_ACTIVE_REFS |
|
|
|
if (isActive && iTargetKey->GetActiveRefs() && !iTargetKey->DecActiveRefs()) |
|
{ |
|
iTargetKey->INotifyDestroyed(); |
|
|
|
iTargetKey->IClearRefs(); |
|
iTargetKey->ClearNotifyCreated(); |
|
|
|
plKey key = plKey::Make(iTargetKey); |
|
plSelfDestructMsg* nuke = TRACKED_NEW plSelfDestructMsg(key); |
|
plgDispatch::Dispatch()->MsgSend(nuke); |
|
} |
|
else |
|
{ |
|
plRefMsg* refMsg = iTargetKey->GetNotifyCreated(iTarg); |
|
hsRefCnt_SafeRef(refMsg); |
|
iTargetKey->RemoveNotifyCreated(iTarg); |
|
if (refMsg) |
|
{ |
|
refMsg->SetRef(iTargetKey->ObjectIsLoaded()); |
|
refMsg->SetTimeStamp(hsTimer::GetSysSeconds()); |
|
refMsg->SetContext(plRefMsg::kOnRemove); |
|
hsRefCnt_SafeRef(refMsg); |
|
plgDispatch::MsgSend(refMsg); |
|
} |
|
hsRefCnt_SafeUnRef(refMsg); |
|
} |
|
}
|
|
|