/*==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 . 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 "plSynchedObject.h" #include "plSynchedValue.h" #include "plNetApp.h" #include "plNetGroup.h" #include "hsResMgr.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnKeyedObject/plKey.h" #include "../pnMessage/plSDLModifierMsg.h" #include "../pnMessage/plSetNetGroupIDMsg.h" #include // statics plSynchedObject* plSynchedObject::fStaticSynchedObj=nil; std::vector plSynchedObject::fDirtyStates; std::vector plSynchedObject::fSynchStateStack; plSynchedObject::plSynchedObject() : fSynchFlags(0), #ifdef USE_SYNCHED_VALUES fSynchedValueAddrOffsets(nil), fNumSynchedValues(0), fSynchedValueFriends(nil), fNumSynchedValueFriends(0), #endif fNetGroup(plNetGroup::kNetGroupUnknown) { fStaticSynchedObj=this; } plSynchedObject::~plSynchedObject() { #ifdef USE_SYNCHED_VALUES delete [] fSynchedValueAddrOffsets; delete [] fSynchedValueFriends; #endif } hsBool plSynchedObject::MsgReceive(plMessage* msg) { plSetNetGroupIDMsg* setNetGroupID = plSetNetGroupIDMsg::ConvertNoRef(msg); if (setNetGroupID) { SetNetGroupConstant(setNetGroupID->fId); return true; } return hsKeyedObject::MsgReceive(msg); } #ifdef USE_SYNCHED_VALUES plSynchedValueBase* plSynchedObject::GetSynchedValue(int i) const { if (i>2; hsAssert(hsABS(addrOff) < (UInt32)(1<<(sizeof(AddrOffsetType)<<3)), "address offset overflow"); IAppendSynchedValueAddrOffset((AddrOffsetType)addrOff); Int32 idx = fNumSynchedValues-1; hsAssert(idx<256, "index too big"); return (UInt8)idx; } hsBool plSynchedObject::RemoveSynchedValue(plSynchedValueBase* v) { int i; for(i=0;iSetFlags(synchFlags); hsAssert(GetKey(), "nil key on synchedObject?"); sdlMsg->Send(GetKey()); } // // Tell an object to send an sdl state update. // The request will get queued (returns true) // hsBool plSynchedObject::DirtySynchState(const char* SDLStateName, UInt32 synchFlags /*SendSDLStateFlags*/) { if (!IOKToDirty(SDLStateName)) { #if 0 if (plNetClientApp::GetInstance()) plNetClientApp::GetInstance()->DebugMsg("NotOKToDirty - Not queueing SDL state, obj %s, sdl %s", GetKeyName(), SDLStateName); #endif return false; } if (!IOKToNetwork(SDLStateName, &synchFlags)) { #if 0 if (plNetClientApp::GetInstance()) plNetClientApp::GetInstance()->DebugMsg("LocalOnly Object - Not queueing SDL msg, obj %s, sdl %s", GetKeyName(), SDLStateName); #endif return false; } if (!(synchFlags & kSkipLocalOwnershipCheck)) { int localOwned=IsLocallyOwned(); if (localOwned==plSynchedObject::kNo) return false; if (localOwned==plSynchedObject::kYes) synchFlags |= kSkipLocalOwnershipCheck; // don't have to check again else { if (plNetClientApp::GetInstance()) plNetClientApp::GetInstance()->DebugMsg("Queueing SDL state with 'maybe' ownership, obj %s, sdl %s", GetKeyName(), SDLStateName); } } if (synchFlags & kSendImmediately) { SendSDLStateMsg(SDLStateName, synchFlags); } else { IAddDirtyState(GetKey(), SDLStateName, synchFlags); } return true; } // // STATIC // add state defn if not already there. // if there adjust flags if necessary // void plSynchedObject::IAddDirtyState(plKey objKey, const char* sdlName, UInt32 sendFlags) { bool found=false; std::vector::iterator it=fDirtyStates.begin(); for( ; it != fDirtyStates.end(); it++) { if ((*it).fObjKey==objKey && !stricmp((*it).fSDLName.c_str(), sdlName)) { if (sendFlags & kForceFullSend) (*it).fSendFlags |= kForceFullSend; if (sendFlags & kBCastToClients) (*it).fSendFlags |= kBCastToClients; found=true; break; } } if (!found) { StateDefn state(objKey, sendFlags, sdlName); fDirtyStates.push_back(state); } else { #if 0 plNetClientApp::GetInstance()->DebugMsg("Not queueing diplicate request for SDL state, obj %s, sdl %s", objKey->GetName(), sdlName); #endif } } // // STATIC // void plSynchedObject::IRemoveDirtyState(plKey objKey, const char* sdlName) { std::vector::iterator it=fDirtyStates.begin(); for( ; it != fDirtyStates.end(); it++) { if ((*it).fObjKey==objKey && !stricmp((*it).fSDLName.c_str(), sdlName)) { fDirtyStates.erase(it); break; } } } void plSynchedObject::SetNetGroupConstant(plNetGroupId netGroup) { ClearSynchFlagsBit(kHasConstantNetGroup); SetNetGroup(netGroup); // may recurse SetSynchFlagsBit(kHasConstantNetGroup); } plNetGroupId plSynchedObject::SelectNetGroup(plKey rmKey) { return plNetClientApp::GetInstance() ? plNetClientApp::GetInstance()->SelectNetGroup(this, rmKey) : plNetGroup::kNetGroupUnknown; } plNetGroupId plSynchedObject::GetEffectiveNetGroup() const { return plNetClientApp::GetInstance() ? plNetClientApp::GetInstance()->GetEffectiveNetGroup(this) : plNetGroup::kNetGroupLocalPlayer; } int plSynchedObject::IsLocallyOwned() const { return plNetClientApp::GetInstance() ? plNetClientApp::GetInstance()->IsLocallyOwned(this) : kYes; } void plSynchedObject::Read(hsStream* stream, hsResMgr* mgr) { hsKeyedObject::Read(stream, mgr); fNetGroup = GetKey()->GetUoid().GetLocation(); stream->ReadSwap(&fSynchFlags); if (fSynchFlags & kExcludePersistentState) { Int16 num; stream->ReadSwap(&num); fSDLExcludeList.clear(); int i; for(i=0;iReadSwap(&num); fSDLVolatileList.clear(); int i; for(i=0;iWriteSwap(fSynchFlags); if (fSynchFlags & kExcludePersistentState) { Int16 num=fSDLExcludeList.size(); stream->WriteSwap(num); SDLStateList::iterator it=fSDLExcludeList.begin(); for(; it != fSDLExcludeList.end(); it++) { plMsgStdStringHelper::Poke(*it, stream); } } if (fSynchFlags & kHasVolatileState) { Int16 num=fSDLVolatileList.size(); stream->WriteSwap(num); SDLStateList::iterator it=fSDLVolatileList.begin(); for(; it != fSDLVolatileList.end(); it++) { plMsgStdStringHelper::Poke(*it, stream); } } } // // static // hsBool plSynchedObject::PopSynchDisabled() { if (fSynchStateStack.size()) { hsBool ret=fSynchStateStack.back(); fSynchStateStack.pop_back(); return ret; } else { hsAssert(false, "invalid stack size?"); } return true; // disabled } #ifdef USE_DIRTY_NOTIFIERS void plSynchedObject::AddDirtyNotifier(plDirtyNotifier* dn) { if (dn) { std::vector::iterator it=std::find(fDirtyNotifiers.begin(), fDirtyNotifiers.end(), dn); if (it == fDirtyNotifiers.end()) // not there { dn->SetSynchedObjKey(GetKey()); fDirtyNotifiers.push_back(dn); } } } void plSynchedObject::RemoveDirtyNotifier(plDirtyNotifier* dn) { if (dn) { std::vector::iterator it=std::find(fDirtyNotifiers.begin(), fDirtyNotifiers.end(), dn); if (it != fDirtyNotifiers.end()) // its there fDirtyNotifiers.erase(it); } } void plSynchedObject::CallDirtyNotifiers() { int i; for(i=0;iCallback(); } #else void plSynchedObject::CallDirtyNotifiers() {} #endif // // return true if it's ok to dirty this object // bool plSynchedObject::IOKToDirty(const char* SDLStateName) const { // is synching disabled? bool synchDisabled = (GetSynchDisabled()!=0); if (synchDisabled) return false; // is object dirtyAble? bool dontDirty = (fSynchFlags & kDontDirty) != 0; if (dontDirty) return false; // is object in a LocalOnly age? if (plNetClientApp::GetConstInstance() && plNetClientApp::GetConstInstance()->ObjectInLocalAge(this)) return false; return true; // OK to dirty } // // return true if this object should send his SDL msg (for persistence or synch) over the net // bool plSynchedObject::IOKToNetwork(const char* sdlName, UInt32* synchFlags) const { // determine destination bool dstServerOnly=false, dstClientsOnly=false, dstClientsAndServer=false; if ((*synchFlags) & kBCastToClients) { // bcasting to clients and server if ((*synchFlags) & kDontPersistOnServer) dstClientsOnly=true; else dstClientsAndServer=true; } else { // not bcasting, must be sending to server only hsAssert( ((*synchFlags) & kDontPersistOnServer)==0, "invalid synchedObject msg flag"); dstServerOnly=true; } bool netSynched = IsNetSynched(); bool inExcludeList = IsInSDLExcludeList(sdlName); // // check if ok to network based on destination // if (dstClientsOnly) { return netSynched; } if (dstClientsAndServer) { if ( !netSynched ) { *synchFlags &= ~kBCastToClients; // don't send to clients } if ( inExcludeList ) { *synchFlags |= kDontPersistOnServer; // don't store on server } return !inExcludeList || netSynched; } if (dstServerOnly) { return !inExcludeList; } hsAssert(false, "how did I get here"); return false; } plSynchedObject::SDLStateList::const_iterator plSynchedObject::IFindInSDLStateList(const SDLStateList& list, const char* sdlName) const { if (!sdlName) return list.end(); // false SDLStateList::const_iterator it = list.begin(); for(; it != list.end(); it++) if (!_stricmp((*it).c_str(), sdlName)) return it; return it; // .end(), false } /////////////////////////// // EXCLUDE LIST /////////////////////////// void plSynchedObject::AddToSDLExcludeList(const char* sdlName) { if (sdlName) { if (IFindInSDLStateList(fSDLExcludeList, sdlName)==fSDLExcludeList.end()) { fSDLExcludeList.push_back(sdlName); // Don't dupe sdlName, std::string will copy fSynchFlags |= kExcludePersistentState; } } } void plSynchedObject::RemoveFromSDLExcludeList(const char* sdlName) { SDLStateList::const_iterator it=IFindInSDLStateList(fSDLExcludeList, sdlName); if (it != fSDLExcludeList.end()) { fSDLExcludeList.erase(fSDLExcludeList.begin()+(it-fSDLExcludeList.begin())); if (fSDLExcludeList.size()==0) fSynchFlags &= ~kExcludePersistentState; } } bool plSynchedObject::IsInSDLExcludeList(const char* sdlName) const { if ((fSynchFlags & kExcludeAllPersistentState) != 0) return true; if ((fSynchFlags & kExcludePersistentState) == 0) return false; SDLStateList::const_iterator it=IFindInSDLStateList(fSDLExcludeList, sdlName); return (it != fSDLExcludeList.end()); } /////////////////////////// // VOLATILE LIST /////////////////////////// void plSynchedObject::AddToSDLVolatileList(const char* sdlName) { if (sdlName) { if (IFindInSDLStateList(fSDLVolatileList,sdlName)==fSDLVolatileList.end()) { fSDLVolatileList.push_back(sdlName); // Don't dupe sdlName, std::string will copy fSynchFlags |= kHasVolatileState; } } } void plSynchedObject::RemoveFromSDLVolatileList(const char* sdlName) { SDLStateList::const_iterator it=IFindInSDLStateList(fSDLVolatileList,sdlName); if (it != fSDLVolatileList.end()) { fSDLVolatileList.erase(fSDLVolatileList.begin()+(it-fSDLVolatileList.begin())); if (fSDLVolatileList.size()==0) fSynchFlags &= ~kHasVolatileState; } } bool plSynchedObject::IsInSDLVolatileList(const char* sdlName) const { if ((fSynchFlags & kAllStateIsVolatile) != 0) return true; if ((fSynchFlags & kHasVolatileState) == 0) return false; SDLStateList::const_iterator it=IFindInSDLStateList(fSDLVolatileList,sdlName); return (it != fSDLVolatileList.end()); }