diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp index c8760d2a..613662e6 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() @@ -2868,14 +2868,7 @@ PyObject* cyMisc::GetAIAvatarsByModelName(const char* name) Py_DECREF(tuple); } } - - if (PyList_Size(avList) > 0) - return avList; - else - { - Py_DECREF(avList); - PYTHON_RETURN_NONE; - } + return avList; } void cyMisc::ForceVaultNodeUpdate(unsigned nodeId) 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/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index 585577a1..6e4438eb 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -852,6 +852,34 @@ void plPythonFileMod::AddTarget(plSceneObject* sobj) DisplayPythonOutput(); } + // Oversight fix... Sometimes PythonFileMods are loaded after the AgeInitialState is received. + // We should really let the script know about that via OnServerInitComplete anyway because it's + // not good to make assumptions about game state in workarounds for that method not being called + plNetClientApp* na = plNetClientApp::GetInstance(); + if (!na->GetFlagsBit(plNetClientApp::kLoadingInitialAgeState) && + na->GetFlagsBit(plNetClientApp::kPlayingGame)) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plProfile_BeginTiming(PythonUpdate); + // call it + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnServerInitComplete], + (char*)fFunctionNames[kfunc_OnServerInitComplete], nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnServerInitComplete] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + } + // display python output DisplayPythonOutput(); } diff --git a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp index 29cbf16f..3ac6a550 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp @@ -41,8 +41,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "pyCritterBrain.h" #include "pyGeometry3.h" +#include "pySceneObject.h" #include "plAvatar/plAvBrainCritter.h" +#include "pnSceneObject/plSceneObject.h" pyCritterBrain::pyCritterBrain(): fBrain(nil) {} @@ -66,18 +68,12 @@ void pyCritterBrain::RemoveReceiver(pyKey& oldReceiver) fBrain->RemoveReceiver(oldReceiver.getKey()); } -void pyCritterBrain::LocallyControlled(bool local) +PyObject* pyCritterBrain::GetSceneObject() { - if (!fBrain) - return; - fBrain->LocallyControlled(local); -} - -bool pyCritterBrain::LocallyControlled() const -{ - if (!fBrain) - return false; - return fBrain->LocallyControlled(); + if (fBrain) + if (plSceneObject* obj = fBrain->GetTarget()) + return pySceneObject::New(obj->GetKey()); + PYTHON_RETURN_NONE; } void pyCritterBrain::AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop /* = true */, diff --git a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h index 28bb3eb4..d6566b26 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h +++ b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h @@ -83,8 +83,7 @@ public: void AddReceiver(pyKey& newReceiver); void RemoveReceiver(pyKey& oldReceiver); - void LocallyControlled(bool local); - bool LocallyControlled() const; + PyObject* GetSceneObject(); void AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop = true, bool randomStartPos = true, float fadeInLen = 2.f, float fadeOutLen = 2.f); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp index da1435a6..8bf16494 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp @@ -139,21 +139,9 @@ PYTHON_METHOD_DEFINITION(ptCritterBrain, removeReceiver, args) PYTHON_RETURN_NONE; } -PYTHON_METHOD_DEFINITION(ptCritterBrain, setLocallyControlled, args) +PYTHON_METHOD_DEFINITION(ptCritterBrain, getSceneObject, GetSceneObject) { - char local; - if (!PyArg_ParseTuple(args, "b", &local)) - { - PyErr_SetString(PyExc_TypeError, "setLocallyControlled expects a boolean"); - PYTHON_RETURN_ERROR; - } - self->fThis->LocallyControlled(local != 0); - PYTHON_RETURN_NONE; -} - -PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getLocallyControlled) -{ - PYTHON_RETURN_BOOL(self->fThis->LocallyControlled()); + return self->fThis->GetSceneObject(); } PYTHON_METHOD_DEFINITION_WKEY(ptCritterBrain, addBehavior, args, keywords) @@ -472,10 +460,7 @@ PYTHON_METHOD_DEFINITION(ptCritterBrain, vectorToPlayer, args) PYTHON_START_METHODS_TABLE(ptCritterBrain) PYTHON_METHOD(ptCritterBrain, addReceiver, "Params: key\nTells the brain that the specified key wants AI messages"), PYTHON_METHOD(ptCritterBrain, removeReceiver, "Params: key\nTells the brain that the specified key no longer wants AI messages"), - PYTHON_METHOD(ptCritterBrain, setLocallyControlled, "Params: local\nTells the brain that we are the ones making all the AI decisions, and to prop location " - "and other information to the server."), - PYTHON_METHOD_NOARGS(ptCritterBrain, getLocallyControlled, "Are we the one making AI decisions? NOTE: Not set automatically, some python script needs to " - "tell the brain this using setLocallyControlled()."), + PYTHON_METHOD(ptCritterBrain, getSceneObject, "Returns the ptSceneObject this brain controls."), PYTHON_METHOD_WKEY(ptCritterBrain, addBehavior, "Params: animName, behaviorName, loop = 1, randomStartPos = 1, fadeInLen = 2.0, fadeOutLen = 2.0\n" "Adds a new animation to the brain as a behavior with the specified name and parameters. If multiple animations are assigned to the same behavior, " "they will be randomly picked from when started."), diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 3e8c27cb..3f7110ef 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -1952,10 +1952,10 @@ hsBool plArmatureMod::IsLocalAvatar() hsBool plArmatureMod::IsLocalAI() { - plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index())); - if (ai) - return ai->LocallyControlled(); - return false; // not an AI, obviously not local + // Formerly a lot of silly cached rigamaroll... Now, we'll just rely + // on the fact that one player is the game master. HACK TURD if net groups + // are ever brought back. + return plNetClientApp::GetInstance()->IsLocallyOwned(this); } void plArmatureMod::SynchIfLocal(double timeNow, int force) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp index 9fd821ed..05f7dd99 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp @@ -126,7 +126,7 @@ protected: /////////////////////////////////////////////////////////////////////////////// plAvBrainCritter::plAvBrainCritter(): fCallbackAction(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), - fLocallyControlled(false), fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0), + fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0), fAngRight(0) { SightCone(M_PI/2); // 90deg @@ -229,6 +229,13 @@ void plAvBrainCritter::Resume() plArmatureBrain::Resume(); } +plSceneObject* plAvBrainCritter::GetTarget() const +{ + if (fArmature) + return fArmature->GetTarget(0); + return nil; +} + void plAvBrainCritter::AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop /* = true */, bool randomStartPos /* = true */, float fadeInLen /* = 2.f */, float fadeOutLen /* = 2.f */) { @@ -310,9 +317,13 @@ std::string plAvBrainCritter::RunBehaviorName() const void plAvBrainCritter::GoToGoal(hsPoint3 newGoal, bool avoidingAvatars /* = false */) { fFinalGoalPos = newGoal; - fAvoidingAvatars = avoidingAvatars; - fNextMode = IPickBehavior(kRun); - // TODO: Pathfinding here! + fAvoidingAvatars = avoidingAvatars; // TODO: make this do something? + + // Only play the run behavior if it's not already activated + // Why? This might just be an update to a preexisting goal. + if(!RunningBehavior(RunBehaviorName())) + fNextMode = IPickBehavior(kRun); + // Missing TODO Turd: Pathfinding. } bool plAvBrainCritter::AtGoal() const diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h index dd89f40c..27d2b8c9 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h @@ -83,13 +83,20 @@ public: virtual void Suspend(); virtual void Resume(); + /** + * Gets the SceneObject root for this avatar + * + * This is most useful in scripts that need to act upon the SceneObject directly. + * There are other ways of obtaining the SceneObject, but network synchronization often + * makes those ways more difficult than they need to be, so we have included this method + * to make the scripter's life easier. + */ + plSceneObject* GetTarget() const; + void AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop = true, bool randomStartPos = true, float fadeInLen = 2.f, float fadeOutLen = 2.f); void StartBehavior(const std::string& behaviorName, bool fade = true); bool RunningBehavior(const std::string& behaviorName) const; - - void LocallyControlled(bool local) {fLocallyControlled = local;} - bool LocallyControlled() const {return fLocallyControlled;} std::string BehaviorName(int behavior) const; plString AnimationName(int behavior) const; @@ -157,8 +164,6 @@ protected: int fNextMode; // the next behavior to run (-1 if we aren't switching on next eval) bool fFadingNextBehavior; // is the next behavior supposed to blend? - bool fLocallyControlled; // is our local AI script the one making all the choices? - bool fAvoidingAvatars; // are we avoiding avatars to the best of our ability when pathfinding? hsPoint3 fFinalGoalPos; // the location we are pathfinding to hsPoint3 fImmediateGoalPos; // the location of the point we are immediately going towards (not necessarily our final goal) 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