You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

230 lines
7.3 KiB

/*==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 <http://www.gnu.org/licenses/>.
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 "plSDLModifier.h"
#include "pnNetCommon/plSynchedObject.h"
#include "pnDispatch/plDispatch.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnMessage/plSDLModifierMsg.h"
#include "plNetMessage/plNetMessage.h"
#include "plSDL/plSDL.h"
#include "plNetClient/plNetClientMgr.h"
#include "plNetClient/plNetObjectDebugger.h"
plSDLModifier::plSDLModifier() : fStateCache(nil), fSentOrRecvdState(false)
{
}
plSDLModifier::~plSDLModifier()
{
delete fStateCache;
}
plKey plSDLModifier::GetStateOwnerKey() const
{
return GetTarget() ? GetTarget()->GetKey() : nil;
}
void plSDLModifier::AddTarget(plSceneObject* so)
{
if (so)
plSingleModifier::AddTarget(so);
if (!fStateCache)
fStateCache = TRACKED_NEW plStateDataRecord(GetSDLName());
}
UInt32 plSDLModifier::IApplyModFlags(UInt32 sendFlags)
{
return sendFlags;
}
//
// write to net msg and send to server
//
void plSDLModifier::ISendNetMsg(plStateDataRecord*& state, plKey senderKey, UInt32 sendFlags)
{
hsAssert(senderKey, "nil senderKey?");
plSynchedObject* sobj = plSynchedObject::ConvertNoRef(senderKey->ObjectIsLoaded());
if (sobj && (sobj->IsInSDLVolatileList(GetSDLName())))
state->SetFlags(state->GetFlags() | plStateDataRecord::kVolatile);
bool dirtyOnly = (sendFlags & plSynchedObject::kForceFullSend) == 0;
bool broadcast = (sendFlags & plSynchedObject::kBCastToClients) != 0;
int writeOptions=0;
// if (dirtyOnly)
writeOptions |= plSDL::kDirtyOnly;
if (broadcast)
writeOptions |= plSDL::kBroadcast;
writeOptions |= plSDL::kTimeStampOnRead;
plNetClientMgr::GetInstance()->StoreSDLState(state, senderKey->GetUoid(), sendFlags, writeOptions);
fSentOrRecvdState = true;
}
//
// Process SDL msgs to send and recv state
//
hsBool plSDLModifier::MsgReceive(plMessage* msg)
{
plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg);
if (sdlMsg && !stricmp(sdlMsg->GetSDLName(),GetSDLName()))
{
UInt32 sendFlags = IApplyModFlags(sdlMsg->GetFlags());
if (!fSentOrRecvdState)
sendFlags |= plSynchedObject::kNewState;
if (sdlMsg->GetAction()==plSDLModifierMsg::kSendToServer)
{
// local player is changing the state and sending it out
plStateChangeNotifier::SetCurrentPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
SendState(sendFlags);
}
else
if (sdlMsg->GetAction()==plSDLModifierMsg::kSendToServerAndClients)
{
// local player is changing the state and sending it out
plStateChangeNotifier::SetCurrentPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
SendState(sendFlags | plSynchedObject::kBCastToClients);
}
else
if (sdlMsg->GetAction()==plSDLModifierMsg::kRecv)
{
plStateDataRecord* sdRec=sdlMsg->GetState();
plStateChangeNotifier::SetCurrentPlayerID(sdlMsg->GetPlayerID()); // remote player changed the state
ReceiveState(sdRec);
}
return true; // consumed
}
return plSingleModifier::MsgReceive(msg);
}
//
// send a state update
//
bool gMooseDump=false;
void plSDLModifier::SendState(UInt32 sendFlags)
{
hsAssert(fStateCache, "nil stateCache");
bool debugObject = (plNetObjectDebugger::GetInstance() &&
plNetObjectDebugger::GetInstance()->IsDebugObject(GetStateOwnerKey()->ObjectIsLoaded()));
bool force = (sendFlags & plSynchedObject::kForceFullSend) != 0;
bool broadcast = (sendFlags & plSynchedObject::kBCastToClients) != 0;
// record current state
plStateDataRecord* curState = TRACKED_NEW plStateDataRecord(GetSDLName());
IPutCurrentStateIn(curState); // return sdl record which reflects current state of sceneObj, dirties curState
if (!force)
{
curState->FlagDifferentState(*fStateCache); // flag items which are different from localCopy as dirty
}
if (curState->IsDirty())
{
// send current state
bool dirtyOnly = force ? false : true;
ISendNetMsg(curState, GetStateOwnerKey(), sendFlags); // send the state
if (debugObject)
{
gMooseDump=true;
plNetObjectDebugger::GetInstance()->SetDebugging(true);
curState->DumpToObjectDebugger(xtl::format("Object %s SENDS SDL state",
GetStateOwnerKey()->GetName(), dirtyOnly).c_str());
gMooseDump=false;
}
// cache current state, send notifications if necessary
fStateCache->UpdateFrom(*curState, dirtyOnly); // update local copy of state
ISentState(curState);
}
delete curState;
if (plNetObjectDebugger::GetInstance())
plNetObjectDebugger::GetInstance()->SetDebugging(false);
}
void plSDLModifier::ReceiveState(const plStateDataRecord* srcState)
{
hsAssert(fStateCache, "nil stateCache");
if (plNetObjectDebugger::GetInstance() &&
plNetObjectDebugger::GetInstance()->IsDebugObject(GetStateOwnerKey()->ObjectIsLoaded()))
{
gMooseDump=true;
plNetObjectDebugger::GetInstance()->SetDebugging(true);
srcState->DumpToObjectDebugger(xtl::format("Object %s RECVS SDL state",
GetStateOwnerKey()->GetName()).c_str());
gMooseDump=false;
}
if (srcState->IsUsed())
{
plSynchEnabler ps(false); // disable dirty tracking while we are receiving/applying state
// apply incoming state
ISetCurrentStateFrom(srcState); // apply incoming state to sceneObj
// cache state, send notifications if necessary
fStateCache->UpdateFrom(*srcState, false); // update local copy of state
fSentOrRecvdState = true;
}
else
{
plNetClientApp::GetInstance()->DebugMsg("\tReceiving and ignoring unused SDL state msg: type %s, object %s",
GetSDLName(), GetStateOwnerKey()->GetName());
}
if (plNetObjectDebugger::GetInstance())
plNetObjectDebugger::GetInstance()->SetDebugging(false);
}
void plSDLModifier::AddNotifyForVar(plKey key, const char* varName, float tolerance) const
{
// create a SDL notifier object
plStateChangeNotifier notifier(tolerance, key);
// set the notification
plStateDataRecord* rec = GetStateCache();
if (rec)
{
plSimpleStateVariable* var = rec->FindVar(varName);
// was the variable found?
if (var)
var->AddStateChangeNotification(notifier);
}
}