diff --git a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj index 2e8abad4..df4d769d 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj +++ b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj @@ -169,6 +169,7 @@ + @@ -178,6 +179,7 @@ + diff --git a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters index 6c960a03..72dd754e 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters +++ b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters @@ -26,6 +26,9 @@ Source Files + + Source Files + @@ -49,5 +52,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Sources/Plasma/CoreLib/HeadSpin.h b/Sources/Plasma/CoreLib/HeadSpin.h index bfa616f0..e8ca8edd 100644 --- a/Sources/Plasma/CoreLib/HeadSpin.h +++ b/Sources/Plasma/CoreLib/HeadSpin.h @@ -48,4 +48,20 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsTypes.h" #include "hsMalloc.h" +#ifdef HAVE_OVERRIDE +# define HS_OVERRIDE override +# define HS_FINAL final +#else +# define HS_OVERRIDE +# define HS_FINAL +#endif + +#ifdef HAVE_NOEXCEPT +# define HS_NOEXCEPT noexcept +# define HS_NOEXCEPT_IF(cond) noexcept(cond) +#else +# define HS_NOEXCEPT throw() +# define HS_NOEXCEPT_IF(cond) +#endif + #endif diff --git a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp index ad359e5c..7cec3bff 100644 --- a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp +++ b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp @@ -49,8 +49,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../pnNetCommon/plNetApp.h" #include "../plAvatar/plArmatureMod.h" #include "../pnSceneObject/plSceneObject.h" - -bool plVolumeSensorConditionalObject::makeBriceHappyVar = true; +#include "../pnMessage/plPlayerPageMsg.h" +#include "../../NucleusLib/inc/plgDispatch.h" plObjectInBoxConditionalObject::plObjectInBoxConditionalObject() : fCurrentTrigger(nil) @@ -129,12 +129,52 @@ fTrigNum(-1), fType(0), fFirst(false), fTriggered(false), -fIgnoreExtraEnters(true) +fFlags(kIgnoreExtraEnters) { SetSatisfied(true); } +void plVolumeSensorConditionalObject::IgnoreExtraEnters(bool ignore) +{ + if (ignore) + fFlags |= kIgnoreExtraEnters; + else + fFlags &= ~kIgnoreExtraEnters; +} + +void plVolumeSensorConditionalObject::NoServerArbitration(bool noArbitration) +{ + if (noArbitration) { + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + fFlags |= kNoServerArbitration; + } else { + plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + fFlags &= ~kNoServerArbitration; + } +} + +bool plVolumeSensorConditionalObject::IIsLocal(const plKey& key) const +{ + if (key == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + return true; + + const plSceneObject* hitter = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + if (hitter) { + for (size_t i = 0; i < hitter->GetNumModifiers(); ++i) { + const plArmatureMod* am = plArmatureMod::ConvertNoRef(hitter->GetModifier(i)); + if (am && !am->IsLocalAI()) + return false; + } + if (hitter->IsLocallyOwned() != plSynchedObject::kYes) + return false; + } + + // Yes, I know that we're saying YES for not loaded objects. This matches the previous behavior. + return true; +} + + hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg) { plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); @@ -144,55 +184,40 @@ hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg) if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger)) fLogicMod->GetNotify()->ClearEvents(); + // Track the hittee for the NoArbitration case so we can trigger the exit volume on link out + fHittee = pActivateMsg->fHiteeObj; + + // Track the enters/exits on all clients if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter) { - int i; - for (i = 0; i < fInside.Count(); i++) - { - if (fInside[i] == pActivateMsg->fHitterObj) - { - if (fIgnoreExtraEnters) - return false; // this is the "correct" way to handle this situation - break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting - } - } - if (i == fInside.Count()) - fInside.Append(pActivateMsg->fHitterObj); - if (makeBriceHappyVar) - { - plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); - if( pObj ) - { - //need to check for human vs quabish type things in here - int i; - for( i = 0; i < pObj->GetNumModifiers(); i++ ) - { - if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) - { - if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) - { - plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); - if((am->IsLocalAI())==nil) - { - return false; - } - } - } - } - plSynchedObject* syncObj = (plSynchedObject*)pObj; - if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) - { - return false; - } - } + auto it = fInside.find(pActivateMsg->fHitterObj); + if (it != fInside.end() && fFlags & kIgnoreExtraEnters) { + // This is normally what we should do. You're already inside the region, + // so we don't care about a dupe enter. However, PhysX is weird, so sometimes + // we might want to allow dupe enters. + return false; } + fInside.insert(pActivateMsg->fHitterObj); + + // From here on out, we only care about local avatars + if (!IIsLocal(pActivateMsg->fHitterObj)) + return false; if (fType == kTypeEnter) { fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); - fLogicMod->RequestTrigger(false); + if ((fFlags & kNoServerArbitration)) + { + if (Satisfied()) + fLogicMod->Trigger(false); + } + else + { + fLogicMod->RequestTrigger(false); + } } else + if (fType == kTypeExit && !(fFlags & kNoServerArbitration)) { fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); fLogicMod->RequestUnTrigger(); @@ -202,56 +227,54 @@ hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg) else if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit) { - for (int i = 0; i < fInside.Count(); i++) - { - if (fInside[i] == pActivateMsg->fHitterObj) + auto it = fInside.find(pActivateMsg->fHitterObj); + if (it == fInside.end()) + return false; + fInside.erase(it); + + // From here on out, we only care about local avatars + if (!IIsLocal(pActivateMsg->fHitterObj)) + return false; + + if (fType == kTypeExit) { + fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + if (fFlags & kNoServerArbitration) { - fInside.Remove(i); - if (makeBriceHappyVar) - { - //need to check for human vs quabish type things in here - plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); - if( pObj ) - { - int i; - for( i = 0; i < pObj->GetNumModifiers(); i++ ) - { - if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) - { - if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) - { - plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); - if((am->IsLocalAI())==nil) - { - return false; - } - } - } - } - plSynchedObject* syncObj = (plSynchedObject*)pObj; - if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) - { - return false; - } - } - } - if (fType == kTypeExit) - { - fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); - fLogicMod->RequestTrigger(false); - - } - else - { - fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); - fLogicMod->RequestUnTrigger(); - } - return false; + if (Satisfied()) + fLogicMod->Trigger(false); + } + else + { + fLogicMod->RequestTrigger(false); } } + else if (fType == kTypeEnter && !(fFlags & kNoServerArbitration)) + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + fLogicMod->RequestUnTrigger(); + } + return true; } + return true; + } - return false; + plPlayerPageMsg* page = plPlayerPageMsg::ConvertNoRef(msg); + if (page && page->fUnload) { + hsAssert(fFlags & kNoServerArbitration, "WTF -- should only get here if the VSCO skips arbitration!"); + + auto it = fInside.find(page->fPlayer); + if (it != fInside.end()) { + fInside.erase(it); + if (fHittee && fType == kTypeExit) { + const plSceneObject* hitteeSO = plSceneObject::ConvertNoRef(fHittee->ObjectIsLoaded()); + if (hitteeSO && hitteeSO->IsLocallyOwned() == plSynchedObject::kYes) { + fLogicMod->GetNotify()->AddCollisionEvent(false, page->fPlayer, fHittee, false); + if (Satisfied()) + fLogicMod->Trigger(false); + } + } + } + return true; } return plConditionalObject::MsgReceive(msg); } @@ -260,20 +283,20 @@ hsBool plVolumeSensorConditionalObject::Satisfied() { if (fType == kTypeExit && fFirst && !fTriggered) { - if (fInside.Count()) + if (!fInside.empty()) fTriggered = true; return true; } if (fTriggered) { - if (fInside.Count() == 0) + if (fInside.empty()) fTriggered = false; return false; } if (fTrigNum == -1) return true; - if (fInside.Count() == fTrigNum) + if (fInside.size() == fTrigNum) return true; else return false; @@ -293,156 +316,11 @@ void plVolumeSensorConditionalObject::Write(hsStream* stream, hsResMgr* mgr) stream->WriteSwap32(fType); stream->WriteBool(fFirst); } -#include "../pnMessage/plPlayerPageMsg.h" -#include "../../NucleusLib/inc/plgDispatch.h" -hsBool plVolumeSensorConditionalObjectNoArbitration::MsgReceive(plMessage* msg) -{ - plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); - if (pActivateMsg) - { - // single player hack - if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger)) - fLogicMod->GetNotify()->ClearEvents(); - fHittee= pActivateMsg->fHiteeObj; - if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter) - { - int i; - for (i = 0; i < fInside.Count(); i++) - { - if (fInside[i] == pActivateMsg->fHitterObj) - { - if (fIgnoreExtraEnters) - return false; // this is the "correct" way to handle this situation - break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting - } - } - if (i == fInside.Count()) - fInside.Append(pActivateMsg->fHitterObj); - if (makeBriceHappyVar) - { - plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); - if( pObj ) - { - //need to check for human vs quabish type things in here - int i; - for( i = 0; i < pObj->GetNumModifiers(); i++ ) - { - if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) - { - if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) - { - plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); - if((am->IsLocalAI())==nil) - { - return false; - } - } - } - } - plSynchedObject* syncObj = (plSynchedObject*)pObj; - if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) - { - return false; - } - } - } - if (fType == kTypeEnter) - { - fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); - //fLogicMod->RequestTrigger(false); - - if (!Satisfied()) - return false; - - fLogicMod->Trigger(false); - } - - return false; - } - else - if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit) - { - for (int i = 0; i < fInside.Count(); i++) - { - if (fInside[i] == pActivateMsg->fHitterObj) - { - fInside.Remove(i); - if (makeBriceHappyVar) - { - //need to check for human vs quabish type things in here - plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); - if( pObj ) - { - int i; - for( i = 0; i < pObj->GetNumModifiers(); i++ ) - { - if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) - { - if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) - { - plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); - if((am->IsLocalAI())==nil) - { - return false; - } - } - } - } - plSynchedObject* syncObj = (plSynchedObject*)pObj; - if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) - { - return false; - } - } - } - if (fType == kTypeExit) - { - fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); - //fLogicMod->RequestTrigger(false); - if (!Satisfied()) - return false; - - fLogicMod->Trigger(false); - } - return false; - } - } - } - - return false; - } - - plPlayerPageMsg* page = plPlayerPageMsg::ConvertNoRef(msg); - if(page && page->fUnload) - { - for(int j= 0; j< fInside.Count(); j++) - { - if(fInside[j] == page->fPlayer) - {//this is the one inside - if(fHittee) - { - plSceneObject *so = plSceneObject::ConvertNoRef(fHittee->ObjectIsLoaded()); - if(so && so->IsLocallyOwned()) - { - if (fType == kTypeExit) - { - fLogicMod->GetNotify()->AddCollisionEvent(false, page->fPlayer, fHittee, false); - //fLogicMod->RequestTrigger(false); - if (!Satisfied()) - return false; - fLogicMod->Trigger(false); - } - } - } - fInside.Remove(j); - } - } - } - return plConditionalObject::MsgReceive(msg); -} void plVolumeSensorConditionalObjectNoArbitration::Read(hsStream* stream, hsResMgr* mgr) { plVolumeSensorConditionalObject::Read(stream, mgr); - plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); -} \ No newline at end of file + + // We must have a valid fpKey before we do this, hence why this is not in the constructor + NoServerArbitration(true); +} diff --git a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h index 9eab8eb4..c215ad60 100644 --- a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h +++ b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h @@ -43,8 +43,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef plObjectInBoxConditionalObject_inc #define plObjectInBoxConditionalObject_inc +#include #include "../../NucleusLib/pnModifier/plConditionalObject.h" #include "hsTemplates.h" +#include class plKey; @@ -77,16 +79,25 @@ class plVolumeSensorConditionalObject : public plConditionalObject protected: - hsTArray fInside; + std::set fInside; int fTrigNum; int fType; hsBool fFirst; hsBool fTriggered; - hsBool fIgnoreExtraEnters; -public: - static bool makeBriceHappyVar; + plKey fHittee; + uint32_t fFlags; + + enum + { + /** */ + kIgnoreExtraEnters = (1<<0), + kNoServerArbitration = (1<<1), + }; + bool IIsLocal(const plKey& key) const; + +public: enum { @@ -99,33 +110,30 @@ public: CLASSNAME_REGISTER( plVolumeSensorConditionalObject ); GETINTERFACE_ANY( plVolumeSensorConditionalObject, plConditionalObject ); - virtual hsBool MsgReceive(plMessage* msg); + hsBool MsgReceive(plMessage* msg); - void Evaluate(){;} + void Evaluate() {} void Reset() { SetSatisfied(false); } - virtual hsBool Satisfied(); + hsBool Satisfied() HS_OVERRIDE; void SetType(int i) { fType = i; } void SetTrigNum(int i) { fTrigNum = i; } void SetFirst(hsBool b) { fFirst = b; } - void IgnoreExtraEnters(hsBool ignore = true) {fIgnoreExtraEnters = ignore;} + void IgnoreExtraEnters(bool ignore = true); + void NoServerArbitration(bool noArbitration = true); - virtual void Read(hsStream* stream, hsResMgr* mgr); - virtual void Write(hsStream* stream, hsResMgr* mgr); + void Read(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE; + void Write(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE; }; class plVolumeSensorConditionalObjectNoArbitration : public plVolumeSensorConditionalObject { public: - plVolumeSensorConditionalObjectNoArbitration ():plVolumeSensorConditionalObject(){;} - ~plVolumeSensorConditionalObjectNoArbitration (){;} - CLASSNAME_REGISTER( plVolumeSensorConditionalObjectNoArbitration ); - GETINTERFACE_ANY( plVolumeSensorConditionalObjectNoArbitration, plConditionalObject ); - virtual hsBool MsgReceive(plMessage* msg); - virtual void Read(hsStream* stream, hsResMgr* mgr); -protected: - plKey fHittee; + CLASSNAME_REGISTER(plVolumeSensorConditionalObjectNoArbitration); + GETINTERFACE_ANY( plVolumeSensorConditionalObjectNoArbitration, plVolumeSensorConditionalObject); + + void Read(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE; }; diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp index b6c7fb28..38daf6b9 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp +++ b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp @@ -3336,6 +3336,15 @@ PF_CONSOLE_CMD(Logic, WriteDetectorLog, "", "Write detector log to logfile") DetectorDoLogfile(); } +PF_CONSOLE_CMD(Logic, + DelayArbitration, + "int millis", + "Simulates network delay for LogicMod arbitration") +{ + int ms = params[0]; + plLogicModBase::SetArbitrationDelay(ms); +} + #endif // LIMIT_CONSOLE_COMMANDS //////////////////////////////////////////////////////////////////////// diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp index 4147e9cb..719af55c 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp +++ b/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp @@ -457,11 +457,3 @@ PF_CONSOLE_CMD( Game, SetLocalClientAsAdmin, "bool enable", "Makes chat messages plgDispatch::MsgSend( msg ); } #endif - -#include "../pfConditional/plObjectInBoxConditionalObject.h" - -PF_CONSOLE_CMD( Game, BreakVolumeSensors, "bool break", "reverts to old broken volume sensor logic" ) -{ - bool b = params[ 0 ]; - plVolumeSensorConditionalObject::makeBriceHappyVar = !b; -} diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp index 1aa2d8f3..4d94af91 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp @@ -993,3 +993,17 @@ void pySceneObject::VolumeSensorIgnoreExtraEnters(bool ignore) } } } + +void pySceneObject::VolumeSensorNoArbitration(bool noArbitration) +{ + if (fSceneObjects.Count() > 0) { + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if (obj) { + for (size_t i = 0; i < obj->GetNumModifiers(); ++i) { + plLogicModifier* logic = const_cast(plLogicModifier::ConvertNoRef(obj->GetModifier(i))); + if (logic) + logic->VolumeNoArbitration(noArbitration); + } + } + } +} \ No newline at end of file diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h index 9f22a1b3..65988273 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h +++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h @@ -200,6 +200,9 @@ public: // hack for garrison void VolumeSensorIgnoreExtraEnters(bool ignore); + + /** More SubWorld hacks */ + void VolumeSensorNoArbitration(bool noArbitration); }; #endif // _pySceneObject_h_ diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp index c379a1fe..8966dbc9 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp @@ -452,6 +452,17 @@ PYTHON_METHOD_DEFINITION(ptSceneobject, volumeSensorIgnoreExtraEnters, args) PYTHON_RETURN_NONE; } +PYTHON_METHOD_DEFINITION(ptSceneobject, volumeSensorNoArbitration, args) +{ + bool noArbitration = true; + if (!PyArg_ParseTuple(args, "|b", &noArbitration)) { + PyErr_SetString(PyExc_TypeError, "volumeSensorNoArbitration expects an optional boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->VolumeSensorNoArbitration(noArbitration); + PYTHON_RETURN_NONE; +} + PYTHON_START_METHODS_TABLE(ptSceneobject) PYTHON_METHOD(ptSceneobject, addKey, "Params: key\nMostly used internally.\n" "Add another sceneobject ptKey"), @@ -510,6 +521,7 @@ PYTHON_START_METHODS_TABLE(ptSceneobject) PYTHON_METHOD(ptSceneobject, getSoundIndex, "Params: sndComponentName\nGet the index of the requested sound component"), PYTHON_METHOD(ptSceneobject, volumeSensorIgnoreExtraEnters, "Params: ignore\nTells the volume sensor attached to this object to ignore extra enters (default), or not (hack for garrison)."), + PYTHON_METHOD(ptSceneobject, volumeSensorNoArbitration, "Params: noArbitration\nTells the volume sensor attached to this object whether or not to negotiate exclusive locks with the server."), PYTHON_END_METHODS_TABLE; PYTHON_GET_DEFINITION(ptSceneobject, draw) diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index 6c2d9a51..75f0e728 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -367,6 +367,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plDynamicCamMap), CLASS_INDEX(plRidingAnimatedPhysicalDetector), CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration), + CLASS_INDEX(plPXSubWorld), //--------------------------------------------------------- // Keyed objects above this line, unkeyed (such as messages) below.. //--------------------------------------------------------- diff --git a/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h b/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h index 05c81c70..ffb6b6b5 100644 --- a/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h +++ b/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h @@ -57,6 +57,8 @@ class hsResMgr; class plServerReplyMsg : public plMessage { int fType; + bool fWasDelayed; + public: enum @@ -67,10 +69,13 @@ public: }; void SetType(int t) { fType = t; } - int GetType() { return fType; } + int GetType() const { return fType; } + + void SetWasDelayed(bool v) { fWasDelayed = v; } + bool GetWasDelayed() const { return fWasDelayed; } - plServerReplyMsg() : fType(kUnInit) { } - plServerReplyMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t), fType(kUnInit) { } + plServerReplyMsg() : fType(kUnInit), fWasDelayed(false) { } + plServerReplyMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t), fType(kUnInit), fWasDelayed(false) { } CLASSNAME_REGISTER( plServerReplyMsg ); GETINTERFACE_ANY( plServerReplyMsg, plMessage ); diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp index ab48b16c..80c8dc2b 100644 --- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp +++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp @@ -44,6 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plgDispatch.h" #include "hsResMgr.h" #include "hsTimer.h" +#include "../pnTimer/plTimerCallbackManager.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnNetCommon/plGenericVar.h" #include "../pnNetCommon/plNetApp.h" @@ -53,6 +54,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../pnMessage/plEnableMsg.h" #include "../pnMessage/plServerReplyMsg.h" +uint32_t plLogicModBase::sArbitrationDelayMs = 0; + void plLogicModBase::ConsoleTrigger(plKey playerKey) { // Setup the event data in case this is a OneShot responder that needs it @@ -138,31 +141,12 @@ hsBool plLogicModBase::MsgReceive(plMessage* msg) plServerReplyMsg* pSMsg = plServerReplyMsg::ConvertNoRef(msg); if (pSMsg) { - hsAssert(pSMsg->GetType() != plServerReplyMsg::kUnInit, "uninit server reply msg"); - -#if 1 - char str[256]; - sprintf(str, "LM: LogicModifier %s recvd trigger request reply:%s, wasRequesting=%d, t=%f\n", GetKeyName(), - pSMsg->GetType() == plServerReplyMsg::kDeny ? "denied" : "confirmed", - HasFlag(kRequestingTrigger), hsTimer::GetSysSeconds()); - plNetClientApp::GetInstance()->DebugMsg(str); -#endif - - if (pSMsg->GetType() == plServerReplyMsg::kDeny) - { - if (HasFlag(kRequestingTrigger)) - { - plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, clearing requestingTrigger"); - ClearFlag(kRequestingTrigger); - } - else - plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, but not requesting?"); - } - else - { - hsBool netRequest=false; // we're triggering as a result of a local activation - PreTrigger(netRequest); - IUpdateSharedState(false /* untriggering */); + if (sArbitrationDelayMs == 0 || pSMsg->GetWasDelayed()) { + IHandleArbitration(pSMsg); + } else { + pSMsg->SetWasDelayed(true); + pSMsg->Ref(); // timer callback manager steals this reference + plgTimerCallbackMgr::NewTimer(static_cast(sArbitrationDelayMs) / 1000, pSMsg); } return true; } @@ -186,6 +170,29 @@ hsBool plLogicModBase::MsgReceive(plMessage* msg) return plSingleModifier::MsgReceive(msg); } +void plLogicModBase::IHandleArbitration(plServerReplyMsg* pSMsg) +{ + hsAssert(pSMsg->GetType() != plServerReplyMsg::kUnInit, "uninit server reply msg"); + + plNetClientApp::GetInstance()->DebugMsg("LM: LogicModifier %s recvd trigger request reply:%s, wasRequesting=%d, t=%f\n", + GetKeyName(), + pSMsg->GetType() == plServerReplyMsg::kDeny ? "denied" : "confirmed", + HasFlag(kRequestingTrigger), hsTimer::GetSysSeconds()); + + if (pSMsg->GetType() == plServerReplyMsg::kDeny) { + if (HasFlag(kRequestingTrigger)) { + plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, clearing requestingTrigger"); + ClearFlag(kRequestingTrigger); + } else { + plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, but not requesting?"); + } + } else { + bool netRequest=false; // we're triggering as a result of a local activation + PreTrigger(netRequest); + IUpdateSharedState(false /* untriggering */); + } +} + void plLogicModBase::RequestTrigger(hsBool netRequest) { if (HasFlag(kTriggered)) diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h index 6a9050ff..908f5924 100644 --- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h +++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h @@ -43,6 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef plLogicModBase_inc #define plLogicModBase_inc +#include #include "plSingleModifier.h" #include "../pnNetCommon/plSynchedValue.h" #include "hsTemplates.h" @@ -66,6 +67,8 @@ public: }; protected: + static uint32_t sArbitrationDelayMs; + hsTArray fCommandList; hsTArray fReceiverList; UInt32 fCounterLimit; @@ -77,6 +80,7 @@ protected: virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return false;} void IUpdateSharedState(bool triggered) const; + void IHandleArbitration(class plServerReplyMsg* msg); hsBool IEvalCounter(); virtual void PreTrigger(hsBool netRequest); virtual void Trigger(hsBool netRequest); @@ -85,7 +89,7 @@ protected: void CreateNotifyMsg(); public: - friend plVolumeSensorConditionalObjectNoArbitration; + friend class plVolumeSensorConditionalObject; plLogicModBase(); ~plLogicModBase(); CLASSNAME_REGISTER( plLogicModBase ); @@ -121,6 +125,9 @@ public: // for debug purposes only! void ConsoleTrigger(plKey playerKey); void ConsoleRequestTrigger(); + + /** Specifies an amount of time (in milliseconds) to delay processing server arbitration responses */ + static void SetArbitrationDelay(uint32_t ms) { sArbitrationDelayMs = ms; } }; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index f69e6cba..90fc261c 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -1978,7 +1978,7 @@ hsBool plArmatureMod::IsLocalAvatar() return plAvatarMgr::GetInstance()->GetLocalAvatar() == this; } -hsBool plArmatureMod::IsLocalAI() +hsBool plArmatureMod::IsLocalAI() const { plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index())); if (ai) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h index 26d1839c..b79f2105 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -220,7 +220,7 @@ public: void GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation); hsBool IsLocalAvatar(); - hsBool IsLocalAI(); + hsBool IsLocalAI() const; virtual const plSceneObject *FindBone(const char * name) const; virtual const plSceneObject *FindBone(UInt32 id) const; // use an id from an appropriate taxonomy, such as plAvBrainHuman::BoneID virtual void AddBoneMapping(UInt32 id, const plSceneObject *bone); diff --git a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp index ab6f15c0..8ec40186 100644 --- a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp @@ -279,3 +279,12 @@ void plLogicModifier::VolumeIgnoreExtraEnters(bool ignore /* = true */) condition->IgnoreExtraEnters(ignore); } } + +void plLogicModifier::VolumeNoArbitration(bool noArbitration) +{ + for (size_t i = 0; i < fConditionList.Count(); ++i) { + plVolumeSensorConditionalObject* condition = plVolumeSensorConditionalObject::ConvertNoRef(fConditionList[i]); + if (condition) + condition->NoServerArbitration(noArbitration); + } +} diff --git a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h index 77f2b922..10952e18 100644 --- a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h +++ b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h @@ -70,6 +70,7 @@ public: virtual void Reset(bool bCounterReset); void VolumeIgnoreExtraEnters(bool ignore = true); // hack for garrison + void VolumeNoArbitration(bool noArbitration = true); int fMyCursor; }; diff --git a/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp b/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp index 53b97ff5..22714384 100644 --- a/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp +++ b/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp @@ -127,6 +127,7 @@ plNetMsgScreener::Answer plNetMsgScreener::IAllowMessageType(Int16 classIndex, c case CLASS_INDEX_SCOPED(plClothingMsg): case CLASS_INDEX_SCOPED(plEnableMsg): case CLASS_INDEX_SCOPED(plLinkToAgeMsg): + case CLASS_INDEX_SCOPED(plSubWorldMsg): return kYes; // definitely yes or no (based on whether sender is a CCR) diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp index 3745f067..03996b27 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp @@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../pnKeyedObject/plKey.h" #include "../pnMessage/plCorrectionMsg.h" #include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" #include "../pnMessage/plSDLModifierMsg.h" #include "../plMessage/plSimStateMsg.h" #include "../plMessage/plSimInfluenceMsg.h" @@ -72,6 +73,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../plStatusLog/plStatusLog.h" #include "plPXConvert.h" #include "plPXPhysicalControllerCore.h" +#include "plPXSubWorld.h" #include "../plModifier/plDetectorLog.h" @@ -84,25 +86,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define LogActivate(func) if (fActor->isSleeping()) SimLog("%s activated by %s", GetKeyName(), func); -PhysRecipe::PhysRecipe() - : mass(0.f) - , friction(0.f) - , restitution(0.f) - , bounds(plSimDefs::kBoundsMax) - , group(plSimDefs::kGroupMax) - , reportsOn(0) - , objectKey(nil) - , sceneNode(nil) - , worldKey(nil) - , convexMesh(nil) - , triMesh(nil) - , radius(0.f) - , offset(0.f, 0.f, 0.f) - , meshStream(nil) -{ - l2s.Reset(); -} - plProfile_Extern(MaySendLocation); plProfile_Extern(LocationsSent); plProfile_Extern(PhysicsUpdates); @@ -128,19 +111,15 @@ int plPXPhysical::fNumberAnimatedActivators = 0; plPXPhysical::plPXPhysical() : fSDLMod(nil) , fActor(nil) - , fBoundsType(plSimDefs::kBoundsMax) , fLOSDBs(plSimDefs::kLOSDBNone) , fGroup(plSimDefs::kGroupMax) , fReportsOn(0) , fLastSyncTime(0.0f) , fProxyGen(nil) - , fSceneNode(nil) - , fWorldKey(nil) , fSndGroup(nil) , fWorldHull(nil) , fSaveTriangles(nil) , fHullNumberPlanes(0) - , fMass(0.f) , fWeWereHit(false) , fHitForce(0,0,0) , fHitPos(0,0,0) @@ -354,15 +333,14 @@ hsBool plPXPhysical::Should_I_Trigger(hsBool enter, hsPoint3& pos) } -hsBool plPXPhysical::Init(PhysRecipe& recipe) +hsBool plPXPhysical::Init() { hsBool startAsleep = false; - fBoundsType = recipe.bounds; - fGroup = recipe.group; - fReportsOn = recipe.reportsOn; - fObjectKey = recipe.objectKey; - fSceneNode = recipe.sceneNode; - fWorldKey = recipe.worldKey; + + fGroup = fRecipe.group; + fReportsOn = fRecipe.reportsOn; + fObjectKey = fRecipe.objectKey; + fSceneNode = fRecipe.sceneNode; NxActorDesc actorDesc; NxSphereShapeDesc sphereDesc; @@ -370,19 +348,19 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) NxTriangleMeshShapeDesc trimeshShapeDesc; NxBoxShapeDesc boxDesc; - plPXConvert::Matrix(recipe.l2s, actorDesc.globalPose); + plPXConvert::Matrix(fRecipe.l2s, actorDesc.globalPose); - switch (fBoundsType) + switch (fRecipe.bounds) { case plSimDefs::kSphereBounds: { hsMatrix44 sphereL2W; sphereL2W.Reset(); - sphereL2W.SetTranslate(&recipe.offset); + sphereL2W.SetTranslate(&fRecipe.offset); - sphereDesc.radius = recipe.radius; + sphereDesc.radius = fRecipe.radius; plPXConvert::Matrix(sphereL2W, sphereDesc.localPose); - sphereDesc.group = fGroup; + sphereDesc.group = fRecipe.group; actorDesc.shapes.pushBack(&sphereDesc); } break; @@ -391,54 +369,54 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) // If this is read time (ie, meshStream is nil), turn the convex hull // into a box. That way the data won't have to change when convex hulls // actually work right. - if (fGroup == plSimDefs::kGroupDetector && recipe.meshStream == nil) + if (fRecipe.group == plSimDefs::kGroupDetector && fRecipe.meshStream == nil) { #ifdef USE_BOXES_FOR_DETECTOR_HULLS - MakeBoxFromHull(recipe.convexMesh, boxDesc); - plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh); - boxDesc.group = fGroup; + MakeBoxFromHull(fRecipe.convexMesh, boxDesc); + plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*fRecipe.convexMesh); + boxDesc.group = fRecipe.group; actorDesc.shapes.push_back(&boxDesc); #else #ifdef USE_PHYSX_CONVEXHULL_WORKAROUND // make a hull of planes for testing IsInside - IMakeHull(recipe.convexMesh,recipe.l2s); + IMakeHull(fRecipe.convexMesh,fRecipe.l2s); #endif // USE_PHYSX_CONVEXHULL_WORKAROUND - convexShapeDesc.meshData = recipe.convexMesh; - convexShapeDesc.userData = recipe.meshStream; - convexShapeDesc.group = fGroup; + convexShapeDesc.meshData = fRecipe.convexMesh; + convexShapeDesc.userData = fRecipe.meshStream; + convexShapeDesc.group = fRecipe.group; actorDesc.shapes.pushBack(&convexShapeDesc); #endif // USE_BOXES_FOR_DETECTOR_HULLS } else { - convexShapeDesc.meshData = recipe.convexMesh; - convexShapeDesc.userData = recipe.meshStream; - convexShapeDesc.group = fGroup; + convexShapeDesc.meshData = fRecipe.convexMesh; + convexShapeDesc.userData = fRecipe.meshStream; + convexShapeDesc.group = fRecipe.group; actorDesc.shapes.pushBack(&convexShapeDesc); } break; case plSimDefs::kBoxBounds: { - boxDesc.dimensions = plPXConvert::Point(recipe.bDimensions); + boxDesc.dimensions = plPXConvert::Point(fRecipe.bDimensions); hsMatrix44 boxL2W; boxL2W.Reset(); - boxL2W.SetTranslate(&recipe.bOffset); + boxL2W.SetTranslate(&fRecipe.bOffset); plPXConvert::Matrix(boxL2W, boxDesc.localPose); - boxDesc.group = fGroup; + boxDesc.group = fRecipe.group; actorDesc.shapes.push_back(&boxDesc); } break; case plSimDefs::kExplicitBounds: case plSimDefs::kProxyBounds: - if (fGroup == plSimDefs::kGroupDetector) + if (fRecipe.group == plSimDefs::kGroupDetector) { SimLog("Someone using an Exact on a detector region: %s", GetKeyName()); } - trimeshShapeDesc.meshData = recipe.triMesh; - trimeshShapeDesc.userData = recipe.meshStream; - trimeshShapeDesc.group = fGroup; + trimeshShapeDesc.meshData = fRecipe.triMesh; + trimeshShapeDesc.userData = fRecipe.meshStream; + trimeshShapeDesc.group = fRecipe.group; actorDesc.shapes.pushBack(&trimeshShapeDesc); break; default: @@ -449,10 +427,9 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) // Now fill out the body, or dynamic part of the physical NxBodyDesc bodyDesc; - fMass = recipe.mass; - if (recipe.mass != 0) + if (fRecipe.mass != 0) { - bodyDesc.mass = recipe.mass; + bodyDesc.mass = fRecipe.mass; actorDesc.body = &bodyDesc; if (GetProperty(plSimulationInterface::kPinned)) @@ -461,13 +438,13 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) startAsleep = true; // put it to sleep if they are going to be frozen } - if (fGroup != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim)) + if (fRecipe.group != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim)) { SetProperty(plSimulationInterface::kPassive, true); // Even though the code for animated physicals and animated activators are the same // keep these code snippets separated for fine tuning. Thanks. - if (fGroup == plSimDefs::kGroupDynamic) + if (fRecipe.group == plSimDefs::kGroupDynamic) { // handle the animated physicals.... make kinematic for now. fNumberAnimatedPhysicals++; @@ -495,7 +472,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) // Put the dynamics into actor group 1. The actor groups are only used for // deciding who we get contact reports for. - if (fGroup == plSimDefs::kGroupDynamic) + if (fRecipe.group == plSimDefs::kGroupDynamic) actorDesc.group = 1; NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); @@ -512,7 +489,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) return false; NxShape* shape = fActor->getShapes()[0]; - shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, recipe.friction, recipe.restitution)); + shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, fRecipe.friction, fRecipe.restitution)); // Turn on the trigger flags for any detectors. // @@ -523,7 +500,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) // problems trying to calculate an intertial tensor. By letting it be // created as a normal dynamic first, then setting the flags, we work around // that problem. - if (fGroup == plSimDefs::kGroupDetector) + if (fRecipe.group == plSimDefs::kGroupDetector) { shape->setFlag(NX_TRIGGER_ON_ENTER, true); shape->setFlag(NX_TRIGGER_ON_LEAVE, true); @@ -547,14 +524,8 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe) plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(fSceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kPhysical); hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef); - if (fWorldKey) - { - plGenRefMsg* ref = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld); - hsgResMgr::ResMgr()->AddViaNotify(fWorldKey, ref, plRefFlags::kActiveRef); - } - // only dynamic physicals without noSync need SDLs - if ( fGroup == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) ) + if ( fRecipe.group == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) ) { // add SDL modifier plSceneObject* sceneObj = plSceneObject::ConvertNoRef(fObjectKey->ObjectIsLoaded()); @@ -611,29 +582,65 @@ hsBool plPXPhysical::MsgReceive( plMessage* msg ) hsBool plPXPhysical::HandleRefMsg(plGenRefMsg* refMsg) { UInt8 refCtxt = refMsg->GetContext(); - plKey refKey = refMsg->GetRef()->GetKey(); - plKey ourKey = GetKey(); - PhysRefType refType = PhysRefType(refMsg->fType); - const char* refKeyName = refKey ? refKey->GetName() : "MISSING"; + switch (refMsg->fType) { + case kPhysRefWorld: { + switch (refCtxt) { + case plRefMsg::kOnCreate: + case plRefMsg::kOnRequest: + // PotS files specify a plHKSubWorld as the subworld key. For everything else, + // the subworlds are a plSceneObject key. For sanity purposes, we will allow + // references to the plPXSubWorld here. HOWEVER, we will need to grab the target + // and replace our reference... + if (plPXSubWorld* subWorldIface = plPXSubWorld::ConvertNoRef(refMsg->GetRef())) { + hsAssert(subWorldIface->GetOwnerKey(), "subworld owner is NULL?! Uh oh..."); + plGenRefMsg* replaceRefMsg = new plGenRefMsg(GetKey(), plRefMsg::kOnReplace, 0, kPhysRefWorld); + hsgResMgr::ResMgr()->AddViaNotify(subWorldIface->GetOwnerKey(), replaceRefMsg, plRefFlags::kActiveRef); + } + // fall-thru is intentional :) + case plRefMsg::kOnReplace: + // loading into a subworld + if (plSceneObject* subSO = plSceneObject::ConvertNoRef(refMsg->GetRef())) { + fWorldKey = subSO->GetKey(); + + // Cyan produced files will never have plPXSubWorld as this is a H'uru-ism. + // Let us make a default one such that we can play with default subworlds at runtime. + // HAAAAAAAAAX!!! + plPXSubWorld* subWorldIface = plPXSubWorld::ConvertNoRef(subSO->GetGenericInterface(plPXSubWorld::Index())); + if (!subWorldIface) { + subWorldIface = new plPXSubWorld(); + + // This can be simplified if/when incorporating zrax' String Theory library + std::string strKeyName = std::string(subSO->GetKeyName()) + "_DefSubWorld"; + const char *cKeyName = strKeyName.c_str(); + + hsgResMgr::ResMgr()->NewKey(cKeyName, + subWorldIface, GetKey()->GetUoid().GetLocation()); + + plObjRefMsg* subIfaceRef = new plObjRefMsg(subSO->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface); + // this will send the reference immediately + hsgResMgr::ResMgr()->SendRef(subWorldIface, subIfaceRef, plRefFlags::kActiveRef); + } - if (refType == kPhysRefWorld) - { - if (refCtxt == plRefMsg::kOnCreate || refCtxt == plRefMsg::kOnRequest) - { - // Cache the initial transform, since we assume the sceneobject already knows - // that and doesn't need to be told again - IGetTransformGlobal(fCachedLocal2World); + // Now, we can initialize the physical... + Init(); + IGetTransformGlobal(fCachedLocal2World); + return true; + } } - if (refCtxt == plRefMsg::kOnDestroy) - { + break; + case plRefMsg::kOnDestroy: { // our world was deleted out from under us: move to the main world + // NOTE: this was not implemented even before the PXSubWorld rewrite. + // not going to bother! // hsAssert(0, "Lost world"); } + break; } - else if (refType == kPhysRefSndGroup) - { - switch (refCtxt) + break; + + case kPhysRefSndGroup: { + switch (refCtxt) { case plRefMsg::kOnCreate: case plRefMsg::kOnRequest: @@ -645,9 +652,11 @@ hsBool plPXPhysical::HandleRefMsg(plGenRefMsg* refMsg) break; } } - else - { + break; + + default: hsAssert(0, "Unknown ref type, who sent us this?"); + break; } return true; @@ -878,7 +887,6 @@ void plPXPhysical::IGetTransformGlobal(hsMatrix44& l2w) const { plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); hsAssert(so, "Scene object not loaded while accessing subworld."); - // We'll hit this at export time, when the ci isn't ready yet, so do a check if (so->GetCoordinateInterface()) { const hsMatrix44& s2w = so->GetCoordinateInterface()->GetLocalToWorld(); @@ -1026,23 +1034,22 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) plPhysical::Read(stream, mgr); ClearMatrix(fCachedLocal2World); - PhysRecipe recipe; - recipe.mass = stream->ReadSwapScalar(); - recipe.friction = stream->ReadSwapScalar(); - recipe.restitution = stream->ReadSwapScalar(); - recipe.bounds = (plSimDefs::Bounds)stream->ReadByte(); - recipe.group = (plSimDefs::Group)stream->ReadByte(); - recipe.reportsOn = stream->ReadSwap32(); + fRecipe.mass = stream->ReadSwapScalar(); + fRecipe.friction = stream->ReadSwapScalar(); + fRecipe.restitution = stream->ReadSwapScalar(); + fRecipe.bounds = (plSimDefs::Bounds)stream->ReadByte(); + fRecipe.group = (plSimDefs::Group)stream->ReadByte(); + fRecipe.reportsOn = stream->ReadSwap32(); fLOSDBs = stream->ReadSwap16(); //hack for swim regions currently they are labeled as static av blockers if(fLOSDBs==plSimDefs::kLOSDBSwimRegion) { - recipe.group=plSimDefs::kGroupMax; + fRecipe.group=plSimDefs::kGroupMax; } // - recipe.objectKey = mgr->ReadKey(stream); - recipe.sceneNode = mgr->ReadKey(stream); - recipe.worldKey = mgr->ReadKey(stream); + fRecipe.objectKey = mgr->ReadKey(stream); + fRecipe.sceneNode = mgr->ReadKey(stream); + fRecipe.worldKey = mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld), plRefFlags::kActiveRef); mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefSndGroup), plRefFlags::kActiveRef); @@ -1050,30 +1057,33 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) hsQuat rot; pos.Read(stream); rot.Read(stream); - rot.MakeMatrix(&recipe.l2s); - recipe.l2s.SetTranslate(&pos); + rot.MakeMatrix(&fRecipe.l2s); + fRecipe.l2s.SetTranslate(&pos); fProps.Read(stream); - if (recipe.bounds == plSimDefs::kSphereBounds) + if (fRecipe.bounds == plSimDefs::kSphereBounds) { - recipe.radius = stream->ReadSwapScalar(); - recipe.offset.Read(stream); + fRecipe.radius = stream->ReadSwapScalar(); + fRecipe.offset.Read(stream); } - else if (recipe.bounds == plSimDefs::kBoxBounds) + else if (fRecipe.bounds == plSimDefs::kBoxBounds) { - recipe.bDimensions.Read(stream); - recipe.bOffset.Read(stream); + fRecipe.bDimensions.Read(stream); + fRecipe.bOffset.Read(stream); } else { - if (recipe.bounds == plSimDefs::kHullBounds) - recipe.convexMesh = IReadHull(stream); + if (fRecipe.bounds == plSimDefs::kHullBounds) + fRecipe.convexMesh = IReadHull(stream); else - recipe.triMesh = IReadTriMesh(stream); + fRecipe.triMesh = IReadTriMesh(stream); } - Init(recipe); + // If we do not have a world, specified, we go ahead and init into the main world... + // This will been done in MsgReceive otherwise + if (!fRecipe.worldKey) + Init(); hsAssert(!fProxyGen, "Already have proxy gen, double read?"); @@ -1115,7 +1125,7 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) // Statics are yellow physColor.Set(1.f,0.8f,0.2f,1.f); // if in a subworld... slightly transparent - if(fWorldKey) + if(fRecipe.worldKey) opac = 0.6f; } else @@ -1241,7 +1251,7 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr) stream->WriteSwapScalar(fActor->getMass()); stream->WriteSwapScalar(friction); stream->WriteSwapScalar(restitution); - stream->WriteByte(fBoundsType); + stream->WriteByte(fRecipe.bounds); stream->WriteByte(fGroup); stream->WriteSwap32(fReportsOn); stream->WriteSwap16(fLOSDBs); @@ -1259,14 +1269,14 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr) fProps.Write(stream); - if (fBoundsType == plSimDefs::kSphereBounds) + if (fRecipe.bounds == plSimDefs::kSphereBounds) { const NxSphereShape* sphereShape = shape->isSphere(); stream->WriteSwapScalar(sphereShape->getRadius()); hsPoint3 localPos = plPXConvert::Point(sphereShape->getLocalPosition()); localPos.Write(stream); } - else if (fBoundsType == plSimDefs::kBoxBounds) + else if (fRecipe.bounds == plSimDefs::kBoxBounds) { const NxBoxShape* boxShape = shape->isBox(); hsPoint3 dim = plPXConvert::Point(boxShape->getDimensions()); @@ -1276,7 +1286,7 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr) } else { - if (fBoundsType == plSimDefs::kHullBounds) + if (fRecipe.bounds == plSimDefs::kHullBounds) hsAssert(shape->isConvexMesh(), "Hull shape isn't a convex mesh"); else hsAssert(shape->isTriangleMesh(), "Exact shape isn't a trimesh"); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h index 5c4ca8ae..54b99644 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h @@ -47,6 +47,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../plPhysical/plSimDefs.h" #include "hsBitVector.h" #include "hsUtils.h" +#include +#include +#include class NxActor; class NxConvexMesh; @@ -72,8 +75,6 @@ class NxCapsule; class PhysRecipe { public: - PhysRecipe(); - hsScalar mass; hsScalar friction; hsScalar restitution; @@ -100,6 +101,12 @@ public: // For export time only. The original data used to create the mesh hsVectorStream* meshStream; + + PhysRecipe() + : mass(0.f), friction(0.f), restitution(0.f), bounds(plSimDefs::kBoundsMax), + group(plSimDefs::kGroupMax), reportsOn(0), convexMesh(nullptr), triMesh(nullptr), + radius(0.f), offset(0.f, 0.f, 0.f), meshStream(nullptr) + { } }; class plPXPhysical : public plPhysical @@ -120,7 +127,7 @@ public: GETINTERFACE_ANY(plPXPhysical, plPhysical); // Export time and internal use only - hsBool Init(PhysRecipe& recipe); + hsBool Init(); virtual void Read(hsStream* s, hsResMgr* mgr); virtual void Write(hsStream* s, hsResMgr* mgr); @@ -191,7 +198,11 @@ public: //this partially for exclude regions vs avatar capsule virtual hsBool OverlapWithCapsule(NxCapsule& cap); - virtual hsScalar GetMass() {return fMass;} + virtual hsScalar GetMass() {return fRecipe.mass;} + + PhysRecipe& GetRecipe() { return fRecipe; } + const PhysRecipe& GetRecipe() const { return fRecipe; } + protected: class NxConvexMesh* IReadHull(hsStream* s); class NxTriangleMesh* IReadTriMesh(hsStream* s); @@ -244,12 +255,11 @@ protected: NxActor* fActor; plKey fWorldKey; // either a subworld or nil - plSimDefs::Bounds fBoundsType; + PhysRecipe fRecipe; plSimDefs::Group fGroup; UInt32 fReportsOn; // bit vector for groups we report interactions with UInt16 fLOSDBs; // Which LOS databases we get put into hsBitVector fProps; // plSimulationInterface::plSimulationProperties kept here - float fMass; plKey fObjectKey; // the key to our scene object plKey fSceneNode; // the room we're in diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp new file mode 100644 index 00000000..b295089a --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp @@ -0,0 +1,52 @@ +/*==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 . +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 "plPXSubWorld.h" + +void plPXSubWorld::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + fGravity.Read(s); +} + +void plPXSubWorld::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + fGravity.Write(s); +} + +void plPXSubWorld::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + // All this magick is handled elsewhere... Not asserting due to call spam. +} \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h new file mode 100644 index 00000000..77bf5fe0 --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h @@ -0,0 +1,67 @@ +/*==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 . +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==*/ + +#ifndef plPXSubWorld_h_inc +#define plPXSubWorld_h_inc + +#include "../../NucleusLib/pnSceneObject/plObjInterface.h" +#include "hsGeometry3.h" + +#define X_GRAVITY 0.f +#define Y_GRAVITY 0.f +#define Z_GRAVITY -32.174049f + +class plPXSubWorld : public plObjInterface +{ + hsVector3 fGravity; + +public: + plPXSubWorld() : fGravity(X_GRAVITY, Y_GRAVITY, Z_GRAVITY) { } + plPXSubWorld(const hsVector3& gravity) : fGravity(gravity) { } + + CLASSNAME_REGISTER(plPXSubWorld); + GETINTERFACE_ANY(plPXSubWorld, plObjInterface); + + void Read(hsStream* s, hsResMgr* mgr) HS_OVERRIDE; + void Write(hsStream* s, hsResMgr* mgr) HS_OVERRIDE; + + Int32 GetNumProperties() const HS_OVERRIDE { return 0; } + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) HS_OVERRIDE; + + const hsVector3& GetGravity() const { return fGravity; } + hsVector3& GetGravity() { return fGravity; } + void SetGravity(const hsVector3& gravity) { fGravity = gravity; } +}; + +#endif // plPXSubWorld_h_inc \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h b/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h index cf986171..9c8cbfab 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h @@ -48,6 +48,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com REGISTER_CREATABLE(plPXPhysical); +#include "plPXSubWorld.h" +REGISTER_CREATABLE(plPXSubWorld); + //#include "plHKSimulationSynchMsg.h" //REGISTER_CREATABLE(plHKSimulationSynchMsg); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 5603c566..fc922676 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -49,6 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plPXPhysical.h" #include "plPXPhysicalControllerCore.h" #include "plPXConvert.h" +#include "plPXSubWorld.h" #include "plLOSDispatch.h" #include "../plPhysical/plPhysicsSoundMgr.h" #include "../plStatusLog/plStatusLog.h" @@ -424,8 +425,18 @@ NxScene* plSimulationMgr::GetScene(plKey world) { UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize); + // The world key is assumed to be loaded (or null for main world) if we are here. + // As such, let us grab the plSceneObject's PXSubWorld definition to figure out + // what gravity should look like. Who knows, we might be in MC Escher land... + NxVec3 gravity(X_GRAVITY, Y_GRAVITY, Z_GRAVITY); + if (plSceneObject* so = plSceneObject::ConvertNoRef(world->VerifyLoaded())) { + if (plPXSubWorld* subworld = plPXSubWorld::ConvertNoRef(so->GetGenericInterface(plPXSubWorld::Index()))) { + gravity = plPXConvert::Vector(subworld->GetGravity()); + } + } + NxSceneDesc sceneDesc; - sceneDesc.gravity.set(0, 0, -32.174049f); + sceneDesc.gravity = gravity; sceneDesc.userTriggerReport = &gSensorReport; sceneDesc.userContactReport = &gContactReport; sceneDesc.maxTimestep = fStepSize;