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.

667 lines
18 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/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
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->ReadLE(&fStartPos);
s->ReadLE(&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->ReadLE32();
s->ReadLE32();
}
void plKeyImp::Write(hsStream* s)
{
fUoid.Write(s);
s->WriteLE(fStartPos);
s->WriteLE(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);
}
}