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.

651 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);
}
}