diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp index c8760d2a..a86bfac9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp @@ -2700,7 +2700,7 @@ PyObject* cyMisc::LoadAvatarModel(const char* modelName, pyKey& spawnPoint, cons void cyMisc::UnLoadAvatarModel(pyKey& avatar) { - plAvatarMgr::GetInstance()->UnLoadAvatar(avatar.getKey(), false); + plAvatarMgr::GetInstance()->UnLoadAvatar(avatar.getKey(), false, true); } void cyMisc::ForceCursorHidden() diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp index 9f7ff0c8..35fd9c05 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -374,7 +374,8 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadAvatarModel, args, "Params: modelName, spa return cyMisc::LoadAvatarModel(modelName, *key, userStr.c_str()); } -PYTHON_GLOBAL_METHOD_DEFINITION(PtUnLoadAvatarModel, args, "Params: avatarKey\nUnloads the specified avatar model") +PYTHON_GLOBAL_METHOD_DEFINITION(PtUnLoadAvatarModel, args, "Params: avatarKey\nForcibly unloads the specified avatar model.\n" + "Do not use this method unless you require fine-grained control of avatar unloading.") { PyObject* keyObj = NULL; if (!PyArg_ParseTuple(args, "O", &keyObj)) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp index 51fb0198..a633d871 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp @@ -217,11 +217,17 @@ plKey plAvatarMgr::LoadAvatar(const char *name, const char *accountName, bool is return result; } -void plAvatarMgr::UnLoadAvatar(plKey avatarKey, bool isPlayer) +void plAvatarMgr::UnLoadAvatar(const plKey& avatarKey, bool isPlayer, bool netPropagate) const { - hsBool isLoading = false; - plLoadAvatarMsg *msg = new plLoadAvatarMsg(avatarKey, GetKey(), 0, isPlayer, isLoading); - msg->Send(); + if (avatarKey) + { + plKey requestor = GetKey(); + plLoadAvatarMsg* msg = new plLoadAvatarMsg(avatarKey, requestor, 0, isPlayer, false); + + // only netprop if the user has a death wish + msg->SetBCastFlag(plMessage::kNetPropagate, netPropagate); + msg->Send(); + } } // our player's already loaded locally, but we've just linked into an age and others there need to be @@ -271,22 +277,6 @@ bool plAvatarMgr::UnPropagateLocalPlayer() return false; } -// UNLOADREMOTEPLAYER -void plAvatarMgr::UnLoadRemotePlayer(plKey remotePlayer) -{ - if(remotePlayer) - { - plKey requestor = GetKey(); - bool isPlayer = true; - hsBool isLoading = false; - plLoadAvatarMsg * msg = new plLoadAvatarMsg(remotePlayer, requestor, 0, isPlayer, isLoading); - - // don't propagate over the network. this is just for removing our local version - msg->SetBCastFlag(plMessage::kNetPropagate, false); - msg->Send(); - } -} - // UNLOADLOCALPLAYER void plAvatarMgr::UnLoadLocalPlayer() { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h index 30539fe4..2283ad40 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h @@ -115,15 +115,21 @@ public: plKey LoadPlayer(const char* name, const char *account); plKey LoadPlayer(const char* name, const char *account, const char *linkName); plKey LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr = nil); - /** Unload an avatar - player or npc - both locally and remotely. */ - void UnLoadAvatar(plKey avKey, bool isPlayer); + + /** + * Unload an avatar clone + * + * This unloads the clone of an avatar (remote player or NPC) from our local game. + * The avatar clone can be unloaded globally by setting netPropagate; however, this + * is highly discouraged. + */ + void UnLoadAvatar(const plKey& avKey, bool isPlayer, bool netPropagate=false) const; /** send our (already loaded) local player to newly-associated clients - used when linking */ void PropagateLocalPlayer(int spawnPoint = -1); /** Unload our local player on other machines because we're leaving this age. The player will stay around on our local machine, though. */ bool UnPropagateLocalPlayer(); - void UnLoadRemotePlayer(plKey playerKey); void UnLoadLocalPlayer(); void AddAvatar(plArmatureMod *avatar); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp index 6c7da6a6..6b08c567 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp @@ -99,16 +99,6 @@ void plNPCSpawnMod::AddTarget(plSceneObject* so) Trigger(); } -void plNPCSpawnMod::RemoveTarget(plSceneObject *so) -{ - plSingleModifier::RemoveTarget(so); - - if(fSpawnedKey) - { - plAvatarMgr::GetInstance()->UnLoadAvatar(fSpawnedKey, false); - } -} - // TRIGGER bool plNPCSpawnMod::Trigger() { @@ -123,6 +113,7 @@ bool plNPCSpawnMod::Trigger() // spawn the NPC plKey spawnPoint = GetTarget(0)->GetKey(); + // Note: we will be unloaded by the NetApp's NPC magick fSpawnedKey = plAvatarMgr::GetInstance()->LoadAvatar(fModelName, fAccountName, false, spawnPoint, nil); ISendNotify(fSpawnedKey); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h index 0e9003bf..b827d998 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h @@ -58,7 +58,6 @@ public: GETINTERFACE_ANY( plNPCSpawnMod, plSingleModifier ); virtual void AddTarget(plSceneObject* so); - virtual void RemoveTarget(plSceneObject *so); // hsBool MsgReceive(plMessage* msg); virtual void Read(hsStream *stream, hsResMgr *mgr); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp index 46869a21..6a2eb6a8 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp @@ -223,6 +223,7 @@ void plNCAgeLeaver::ExecNextOp () { plAgeLoader::GetInstance()->UnloadAge(); // unload age nc->ISendCameraReset(false/*leaving age*/); // reset camera nc->IUnloadRemotePlayers(); // unload other players + nc->IUnloadNPCs(); // unload non-player clones if (NetCommNeedToLoadAvatar()) am->UnLoadLocalPlayer(); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp index 50704007..0be08041 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp @@ -181,13 +181,9 @@ void plNetClientMgr::Shutdown() plNetLinkingMgr::GetInstance()->LeaveAge(true); - // release existing remote players - int i; - for (i=0;iUnLoadRemotePlayer(k); - } + // release all avatar clones + IUnloadRemotePlayers(); + IUnloadNPCs(); // Finally, pump the dispatch system so all the new refs get delivered. plgDispatch::Dispatch()->MsgQueueProcess(); @@ -197,7 +193,7 @@ void plNetClientMgr::Shutdown() delete fMsgRecorder; fMsgRecorder = nil; } - for (i = 0; i < fMsgPlayers.size(); i++) + for (int i = 0; i < fMsgPlayers.size(); i++) delete fMsgPlayers[i]; fMsgPlayers.clear(); @@ -424,9 +420,19 @@ int plNetClientMgr::IPrepMsg(plNetMessage* msg) // void plNetClientMgr::IUnloadRemotePlayers() { - for(int i=RemotePlayerKeys().size()-1;i>=0;i--) - plAvatarMgr::GetInstance()->UnLoadRemotePlayer(RemotePlayerKeys()[i]); - hsAssert(!RemotePlayerKeys().size(),"Still remote players left when linking out"); + for (size_t i = fRemotePlayerKeys.size(); i > 0; --i) + plAvatarMgr::GetInstance()->UnLoadAvatar(fRemotePlayerKeys[i-1], true); + hsAssert(fRemotePlayerKeys.empty(), "Still remote players left when linking out"); +} + +// +// unload NPCs since we're leaving the age +// +void plNetClientMgr::IUnloadNPCs() +{ + for (size_t i = fNPCKeys.size(); i > 0; --i) + plAvatarMgr::GetInstance()->UnLoadAvatar(fNPCKeys[i-1], false); + hsAssert(fNPCKeys.empty(), "Still npcs left when linking out"); } // @@ -853,6 +859,31 @@ plSynchedObject* plNetClientMgr::GetLocalPlayer(hsBool forceLoad) const plSynchedObject::ConvertNoRef(fLocalPlayerKey->ObjectIsLoaded()) : nil; } +plSynchedObject* plNetClientMgr::GetNPC(uint32_t i) const +{ + return fNPCKeys[i] ? plSynchedObject::ConvertNoRef(fNPCKeys[i]->ObjectIsLoaded()) : nil; +} + +void plNetClientMgr::AddNPCKey(const plKey& npc) +{ + // note: npc keys have little sanity checking... + hsAssert(npc, "adding nil npc key? naughty, naughty..."); + fNPCKeys.push_back(npc); +} + +bool plNetClientMgr::IsNPCKey(const plKey& npc, int* idx) const +{ + if (npc) + { + plKeyVec::const_iterator it = std::find(fNPCKeys.begin(), fNPCKeys.end(), npc); + bool found = it != fNPCKeys.end(); + if (idx) + *idx = found ? (it - fNPCKeys.begin()) : -1; + return found; + } + return false; +} + // // return a ptr to a remote player // diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h index 2f6bef94..a236a90b 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h @@ -153,7 +153,7 @@ private: // cached char info plKey fLocalPlayerKey; plKeyVec fRemotePlayerKeys; - // plKeyVec fNPCKeys; + plKeyVec fNPCKeys; class plNetClientMgrMsg * fDisableMsg; @@ -222,6 +222,7 @@ private: void IRemoveCloneRoom(); void IUnloadRemotePlayers(); + void IUnloadNPCs(); plKey ILoadClone(plLoadCloneMsg *cloneMsg); @@ -309,9 +310,15 @@ public: // avatar vault actions int UploadPlayerVault(uint32_t vaultFlags); + + // npc clones + const plKeyVec& NPCKeys() const { return fNPCKeys; } + plSynchedObject* GetNPC(uint32_t i) const; + void AddNPCKey(const plKey& npc); + bool IsNPCKey(const plKey& npc, int* idx=nil) const; // remote players - const std::vector& RemotePlayerKeys() const { return fRemotePlayerKeys; } + const plKeyVec& RemotePlayerKeys() const { return fRemotePlayerKeys; } plSynchedObject* GetRemotePlayer(int i) const; void AddRemotePlayerKey(plKey p); hsBool IsRemotePlayerKey(const plKey p, int* idx=nil); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp index 80fb9738..afb894db 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp @@ -113,13 +113,16 @@ plKey plNetClientMgr::ILoadClone(plLoadCloneMsg *pCloneMsg) // check if local or remote player before loading plLoadAvatarMsg* loadAvMsg=plLoadAvatarMsg::ConvertNoRef(pCloneMsg); - if (loadAvMsg && loadAvMsg->GetIsPlayer()) + if (loadAvMsg) { bool originating = ( pCloneMsg->GetOriginatingPlayerID() == this->GetPlayerID() ); - if (originating) - fLocalPlayerKey = cloneKey; - else - AddRemotePlayerKey(cloneKey); + if (loadAvMsg->GetIsPlayer()) + if (originating) + fLocalPlayerKey = cloneKey; + else + AddRemotePlayerKey(cloneKey); + else // hey, we got a quab or yeesha... or some other such devilry... + AddNPCKey(cloneKey); } plKey cloneNodeKey = hsgResMgr::ResMgr()->FindKey(kNetClientCloneRoom_KEY); @@ -139,6 +142,12 @@ plKey plNetClientMgr::ILoadClone(plLoadCloneMsg *pCloneMsg) return cloneKey; } + // need to drop our ref if it's an NPC + // remote players handled by plPlayerPageMsg--don't sweat that + plKeyVec::iterator it = std::find(fNPCKeys.begin(), fNPCKeys.end(), cloneKey); + if (it != fNPCKeys.end()) + fNPCKeys.erase(it); + ICheckPendingStateLoad(hsTimer::GetSysSeconds()); plSynchEnabler p(false); // turn off dirty tracking while in this function