From 3b69a4aee3ca88f82663202ff12b50becd3b2be5 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 6 May 2012 19:22:03 -0400 Subject: [PATCH 1/4] Track NPC clones Manual state management in python was kind of fiddly, so let's track all avatar clones in the NetApp and unload them as needed. This also seems to fix a potential bug in plNPCSpawnMod (is that even used?). --- Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp | 2 +- .../Plasma/FeatureLib/pfPython/cyMiscGlue.cpp | 3 +- .../PubUtilLib/plAvatar/plAvatarMgr.cpp | 30 ++++------- .../Plasma/PubUtilLib/plAvatar/plAvatarMgr.h | 12 +++-- .../PubUtilLib/plAvatar/plNPCSpawnMod.cpp | 11 +--- .../PubUtilLib/plAvatar/plNPCSpawnMod.h | 1 - .../plNetClient/plNetCliAgeLeaver.cpp | 1 + .../PubUtilLib/plNetClient/plNetClientMgr.cpp | 53 +++++++++++++++---- .../PubUtilLib/plNetClient/plNetClientMgr.h | 11 +++- .../plNetClient/plNetClientMgrLoad.cpp | 19 +++++-- 10 files changed, 89 insertions(+), 54 deletions(-) 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 From 5cf540b42453e117950ef17b19b22fc980e54ddc Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 7 May 2012 23:17:46 -0400 Subject: [PATCH 2/4] Updates to the Python API Throw away some stupid stuff and make things slightly easier to use... --- Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp | 9 +------- .../FeatureLib/pfPython/pyCritterBrain.cpp | 18 +++++++--------- .../FeatureLib/pfPython/pyCritterBrain.h | 3 +-- .../pfPython/pyCritterBrainGlue.cpp | 21 +++---------------- .../PubUtilLib/plAvatar/plArmatureMod.cpp | 8 +++---- .../PubUtilLib/plAvatar/plAvBrainCritter.cpp | 9 +++++++- .../PubUtilLib/plAvatar/plAvBrainCritter.h | 15 ++++++++----- 7 files changed, 34 insertions(+), 49 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp index a86bfac9..613662e6 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp @@ -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/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..5ebd4df6 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 */) { 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) From 2813aaa4673932b22024ebf05c571a8339dbefee Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Tue, 8 May 2012 17:49:39 -0400 Subject: [PATCH 3/4] Fix stupid in plAvBrainCritter::GoToGoal Changing the next behavior to running on every goal update causes the quabs to skitter about one inch every five seconds. Obviously, we do not need to start running if we are already running, so let's check that. --- .../Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp index 5ebd4df6..05f7dd99 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp @@ -317,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 From 1252df83e1d9c1e19aa61efbfbabfb2ad16b09ff Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Tue, 8 May 2012 18:25:26 -0400 Subject: [PATCH 4/4] Ensure that OnServerInitComplete is always executed --- .../FeatureLib/pfPython/plPythonFileMod.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index 0c54f2f1..b97f0031 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(); }