|
|
|
/*==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/>.
|
|
|
|
|
|
|
|
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 "plCreatableIndex.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
#include "hsStream.h"
|
|
|
|
#include "plNetClientMgr.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "plPhysical.h"
|
|
|
|
#include "plNetClientMsgHandler.h"
|
|
|
|
#include "plNetLinkingMgr.h"
|
|
|
|
#include "plNetObjectDebugger.h"
|
|
|
|
|
|
|
|
#include "pnUtils/pnUtils.h"
|
|
|
|
#include "pnProduct/pnProduct.h"
|
|
|
|
#include "pnNetCommon/plSynchedObject.h"
|
|
|
|
#include "pnNetCommon/plNetServers.h"
|
|
|
|
#include "pnNetCommon/plSDLTypes.h"
|
|
|
|
#include "pnKeyedObject/plKey.h"
|
|
|
|
#include "pnKeyedObject/plFixedKey.h"
|
|
|
|
#include "pnKeyedObject/hsKeyedObject.h"
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "pnSceneObject/plObjInterface.h"
|
|
|
|
#include "pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "pnMessage/plClientMsg.h"
|
|
|
|
#include "pnMessage/plSDLModifierMsg.h"
|
|
|
|
#include "pnMessage/plPlayerPageMsg.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
#include "pnModifier/plModifier.h"
|
|
|
|
#include "pnAsyncCore/pnAsyncCore.h"
|
|
|
|
|
|
|
|
#include "plAgeLoader/plAgeLoader.h"
|
|
|
|
#include "plAgeLoader/plResPatcher.h"
|
|
|
|
#include "plNetClientRecorder/plNetClientRecorder.h"
|
|
|
|
#include "plScene/plSceneNode.h"
|
|
|
|
#include "plNetCommon/plNetCommonConstants.h"
|
|
|
|
#include "plNetMessage/plNetMessage.h"
|
|
|
|
#include "plMessage/plLoadAvatarMsg.h"
|
|
|
|
#include "plMessage/plLoadCloneMsg.h"
|
|
|
|
#include "plMessage/plSynchEnableMsg.h"
|
|
|
|
#include "plMessage/plLinkToAgeMsg.h"
|
|
|
|
#include "plMessage/plLoadAgeMsg.h"
|
|
|
|
#include "plMessage/plAgeLoadedMsg.h"
|
|
|
|
#include "plMessage/plCCRMsg.h"
|
|
|
|
#include "plMessage/plAvatarMsg.h"
|
|
|
|
#include "plMessage/plNetVoiceListMsg.h"
|
|
|
|
#include "plMessage/plNetCommMsgs.h"
|
|
|
|
#include "plMessage/plNetClientMgrMsg.h"
|
|
|
|
#include "plMessage/plResPatcherMsg.h"
|
|
|
|
#include "plMessage/plVaultNotifyMsg.h"
|
|
|
|
#include "plResMgr/plKeyFinder.h"
|
|
|
|
#include "plResMgr/plPageInfo.h"
|
|
|
|
#include "plNetTransport/plNetTransportMember.h"
|
|
|
|
#include "plAgeDescription/plAgeDescription.h"
|
|
|
|
#include "plAvatar/plAvatarClothing.h"
|
|
|
|
#include "plAvatar/plArmatureMod.h"
|
|
|
|
#include "plAvatar/plAvatarMgr.h"
|
|
|
|
#include "plSurface/plLayerInterface.h"
|
|
|
|
#include "plStatusLog/plStatusLog.h"
|
|
|
|
#include "plSDL/plSDL.h"
|
|
|
|
#include "plUnifiedTime/plUnifiedTime.h"
|
|
|
|
#include "plFile/plEncryptedStream.h"
|
|
|
|
#include "plProgressMgr/plProgressMgr.h"
|
|
|
|
#include "plVault/plVault.h"
|
|
|
|
|
|
|
|
#include "pfMessage/pfKIMsg.h" // Move this to PubUtil level
|
|
|
|
|
|
|
|
#if 1 // for debugging
|
|
|
|
#include "plCreatableIndex.h"
|
|
|
|
#include "plModifier/plResponderModifier.h"
|
|
|
|
#include "plSurface/plLayerAnimation.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plNetClientMgr::PendingLoad::~PendingLoad()
|
|
|
|
{
|
|
|
|
delete fSDRec;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//
|
|
|
|
// CONSTRUCT
|
|
|
|
//
|
|
|
|
#pragma warning(disable:4355) // this used in initializer list
|
|
|
|
plNetClientMgr::plNetClientMgr() :
|
|
|
|
fLocalPlayerKey(nil),
|
|
|
|
fMsgHandler(this),
|
|
|
|
fJoinOrder(0),
|
|
|
|
// fProgressBar( nil ),
|
|
|
|
fTaskProgBar( nil ),
|
|
|
|
fMsgRecorder(nil),
|
|
|
|
fServerTimeOffset(0),
|
|
|
|
fTimeSamples(0),
|
|
|
|
fLastTimeUpdate(0),
|
|
|
|
fListenListMode(kListenList_Distance),
|
|
|
|
fAgeSDLObjectKey(nil),
|
|
|
|
fExperimentalLevel(0),
|
|
|
|
fOverrideAgeTimeOfDayPercent(-1.f),
|
|
|
|
fNumInitialSDLStates(0),
|
|
|
|
fRequiredNumInitialSDLStates(0),
|
|
|
|
fDisableMsg(nil),
|
|
|
|
fIsOwner(true)
|
|
|
|
{
|
|
|
|
#ifndef HS_DEBUGGING
|
|
|
|
// release code will timeout inactive players on servers by default
|
|
|
|
SetFlagsBit(kAllowTimeOut);
|
|
|
|
#endif
|
|
|
|
SetFlagsBit(kAllowAuthTimeOut);
|
|
|
|
|
|
|
|
// fPlayerVault.SetPlayerName("SinglePlayer"); // in a MP game, this will be replaced with a player name like 'Atrus'
|
|
|
|
fTransport.SetNumChannels(kNetNumChannels);
|
|
|
|
}
|
|
|
|
#pragma warning(default:4355)
|
|
|
|
|
|
|
|
//
|
|
|
|
// DESTRUCT
|
|
|
|
//
|
|
|
|
plNetClientMgr::~plNetClientMgr()
|
|
|
|
{
|
|
|
|
plSynchedObject::PushSynchDisabled(true); // disable dirty tracking
|
|
|
|
|
|
|
|
IDeInitNetClientComm();
|
|
|
|
|
|
|
|
if (this==GetInstance())
|
|
|
|
SetInstance(nil); // we're going down boys
|
|
|
|
|
|
|
|
IClearPendingLoads();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// App is exiting
|
|
|
|
//
|
|
|
|
void plNetClientMgr::Shutdown()
|
|
|
|
{
|
|
|
|
ISendDirtyState(hsTimer::GetSysSeconds());
|
|
|
|
|
|
|
|
plNetLinkingMgr::GetInstance()->LeaveAge(true);
|
|
|
|
|
|
|
|
// release existing remote players
|
|
|
|
int i;
|
|
|
|
for (i=0;i<RemotePlayerKeys().size();i++)
|
|
|
|
{
|
|
|
|
plKey k=RemotePlayerKeys()[i];
|
|
|
|
plAvatarMgr::GetInstance()->UnLoadRemotePlayer(k);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, pump the dispatch system so all the new refs get delivered.
|
|
|
|
plgDispatch::Dispatch()->MsgQueueProcess();
|
|
|
|
|
|
|
|
if (fMsgRecorder)
|
|
|
|
{
|
|
|
|
delete fMsgRecorder;
|
|
|
|
fMsgRecorder = nil;
|
|
|
|
}
|
|
|
|
for (i = 0; i < fMsgPlayers.size(); i++)
|
|
|
|
delete fMsgPlayers[i];
|
|
|
|
fMsgPlayers.clear();
|
|
|
|
|
|
|
|
IRemoveCloneRoom();
|
|
|
|
|
|
|
|
// RATHER BAD DEBUG HACK: Clear the spawn override in armatureMod so there's no memory leak
|
|
|
|
plArmatureMod::SetSpawnPointOverride( nil );
|
|
|
|
|
|
|
|
VaultDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Clear any pending state, probably joining a new age
|
|
|
|
//
|
|
|
|
void plNetClientMgr::IClearPendingLoads()
|
|
|
|
{
|
|
|
|
std::for_each( fPendingLoads.begin(), fPendingLoads.end(), xtl::delete_ptr() );
|
|
|
|
fPendingLoads.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// manually add/remove the cloned object room in plClient
|
|
|
|
//
|
|
|
|
void plNetClientMgr::IAddCloneRoom()
|
|
|
|
{
|
|
|
|
plSceneNode *cloneRoom = TRACKED_NEW plSceneNode();
|
|
|
|
cloneRoom->RegisterAs(kNetClientCloneRoom_KEY);
|
|
|
|
plKey clientKey=hsgResMgr::ResMgr()->FindKey(kClient_KEY);
|
|
|
|
plClientRefMsg *pMsg1 = TRACKED_NEW plClientRefMsg(clientKey, plRefMsg::kOnCreate, -1, plClientRefMsg::kManualRoom);
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(cloneRoom->GetKey(), pMsg1, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// manually add/remove the cloned object room in plClient
|
|
|
|
//
|
|
|
|
void plNetClientMgr::IRemoveCloneRoom()
|
|
|
|
{
|
|
|
|
plKey cloneRoomKey = hsgResMgr::ResMgr()->FindKey(kNetClientCloneRoom_KEY);
|
|
|
|
plSceneNode *cloneRoom = cloneRoomKey ? plSceneNode::ConvertNoRef(cloneRoomKey->ObjectIsLoaded()) : nil;
|
|
|
|
if (cloneRoom)
|
|
|
|
{
|
|
|
|
cloneRoom->UnRegisterAs(kNetClientCloneRoom_KEY);
|
|
|
|
cloneRoom = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// turn null send on/off. Null send does everything except actually send the msg out on the socket
|
|
|
|
//
|
|
|
|
void plNetClientMgr::SetNullSend(hsBool on)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// returns server time in the form "[m/d/y h:m:s]"
|
|
|
|
//
|
|
|
|
const char* plNetClientMgr::GetServerLogTimeAsString(std::string& timestamp) const
|
|
|
|
{
|
|
|
|
const plUnifiedTime st=GetServerTime();
|
|
|
|
xtl::format(timestamp, "{%02d/%02d %02d:%02d:%02d}",
|
|
|
|
st.GetMonth(), st.GetDay(), st.GetHour(), st.GetMinute(), st.GetSecond());
|
|
|
|
return timestamp.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// support for a single leading tab in statusLog strings
|
|
|
|
//
|
|
|
|
const char* ProcessTab(const char* fmt)
|
|
|
|
{
|
|
|
|
static std::string s;
|
|
|
|
if (fmt && *fmt=='\t')
|
|
|
|
{
|
|
|
|
s = xtl::format(" %s", fmt);
|
|
|
|
return s.c_str();
|
|
|
|
}
|
|
|
|
return fmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// override for plLoggable
|
|
|
|
//
|
|
|
|
bool plNetClientMgr::Log(const char* str) const
|
|
|
|
{
|
|
|
|
if (strlen(str)==nil)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// prepend raw time
|
|
|
|
std::string buf2 = xtl::format("%.2f %s", hsTimer::GetSeconds(), ProcessTab(str));
|
|
|
|
|
|
|
|
if ( GetConsoleOutput() )
|
|
|
|
hsStatusMessage(buf2.c_str());
|
|
|
|
|
|
|
|
// create status log if necessary
|
|
|
|
if(fStatusLog==nil)
|
|
|
|
{
|
|
|
|
fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "network.log",
|
|
|
|
plStatusLog::kTimestamp | plStatusLog::kFilledBackground | plStatusLog::kAlignToTop |
|
|
|
|
plStatusLog::kServerTimestamp);
|
|
|
|
fWeCreatedLog = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plNetObjectDebugger::GetInstance()->LogMsgIfMatch(buf2.c_str());
|
|
|
|
return fStatusLog->AddLine(buf2.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Display OS version info for log
|
|
|
|
//
|
|
|
|
void plNetClientMgr::IDumpOSVersionInfo() const
|
|
|
|
{
|
|
|
|
DebugMsg("*** OS Info");
|
|
|
|
char** versionStrs = DisplaySystemVersion();
|
|
|
|
int i=0;
|
|
|
|
while(versionStrs && versionStrs[i])
|
|
|
|
{
|
|
|
|
DebugMsg(versionStrs[i]);
|
|
|
|
delete [] versionStrs[i];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
delete [] versionStrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// initialize net client. returns hsFail on err.
|
|
|
|
//
|
|
|
|
int plNetClientMgr::Init()
|
|
|
|
{
|
|
|
|
int ret=hsOK;
|
|
|
|
hsLogEntry( DebugMsg("*** plNetClientMgr::Init GMT:%s", plUnifiedTime::GetCurrentTime().Print()) );
|
|
|
|
|
|
|
|
IDumpOSVersionInfo();
|
|
|
|
|
|
|
|
if (GetFlagsBit(kNullSend))
|
|
|
|
SetNullSend(true);
|
|
|
|
|
|
|
|
VaultInitialize();
|
|
|
|
|
|
|
|
RegisterAs( kNetClientMgr_KEY );
|
|
|
|
IAddCloneRoom();
|
|
|
|
|
|
|
|
fNetGroups.Reset();
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNetClientMgrMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoaded2Msg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plCCRPetitionMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNetVoiceListMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNetCommActivePlayerMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNetCommLinkToAgeMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
// We need plVaultNotifyMsgs for the NetLinkingMgr
|
|
|
|
plgDispatch::Dispatch()->RegisterForType(plVaultNotifyMsg::Index(), GetKey());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plResPatcherMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
IInitNetClientComm();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Prepare to send.
|
|
|
|
// Update p2p transport groups and rcvrs list in some msgs
|
|
|
|
// Returns channel.
|
|
|
|
//
|
|
|
|
int plNetClientMgr::IPrepMsg(plNetMessage* msg)
|
|
|
|
{
|
|
|
|
// pick channel, prepare msg
|
|
|
|
int channel=kNetChanDefault;
|
|
|
|
plNetMsgVoice* v=plNetMsgVoice::ConvertNoRef(msg);
|
|
|
|
if (v)
|
|
|
|
{ // VOICE MSG
|
|
|
|
channel=kNetChanVoice;
|
|
|
|
|
|
|
|
// compute new transport group (from talkList) if necessary
|
|
|
|
GetTalkList()->UpdateTransportGroup(this);
|
|
|
|
|
|
|
|
// update receivers list in voice msg based on talk list
|
|
|
|
v->Receivers()->Clear();
|
|
|
|
int i;
|
|
|
|
for(i=0;i<GetTalkList()->GetNumMembers(); i++)
|
|
|
|
{
|
|
|
|
v->Receivers()->AddReceiverPlayerID(GetTalkList()->GetMember(i)->GetPlayerID());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->IsBitSet(plNetMessage::kEchoBackToSender))
|
|
|
|
v->Receivers()->AddReceiverPlayerID(GetPlayerID());
|
|
|
|
}
|
|
|
|
else if (plNetMsgListenListUpdate::ConvertNoRef(msg))
|
|
|
|
{ // LISTEN LIST UPDATE MSG
|
|
|
|
channel=kNetChanListenListUpdate;
|
|
|
|
|
|
|
|
// update transport group from rcvrs list, add p2p mbrs to trasnport group
|
|
|
|
fTransport.ClearChannelGrp(kNetChanListenListUpdate);
|
|
|
|
if (IsPeerToPeer())
|
|
|
|
{
|
|
|
|
plNetMsgReceiversListHelper* rl = plNetMsgReceiversListHelper::ConvertNoRef(msg);
|
|
|
|
hsAssert(rl, "error converting msg to rcvrs list?");
|
|
|
|
int i;
|
|
|
|
for(i=0;i<rl->GetNumReceivers();i++)
|
|
|
|
{
|
|
|
|
int idx=fTransport.FindMember(rl->GetReceiverPlayerID(i));
|
|
|
|
hsAssert(idx!=-1, "error finding transport mbr");
|
|
|
|
plNetTransportMember* tm = fTransport.GetMember(idx);
|
|
|
|
if (tm->IsPeerToPeer())
|
|
|
|
fTransport.SubscribeToChannelGrp(tm, kNetChanListenListUpdate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( plNetMsgGameMessageDirected::ConvertNoRef( msg ) )
|
|
|
|
{
|
|
|
|
channel = kNetChanDirectedMsg;
|
|
|
|
}
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// unload other player since we're leaving the age
|
|
|
|
//
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// begin linking out sounds and gfx
|
|
|
|
//
|
|
|
|
void plNetClientMgr::StartLinkOutFX()
|
|
|
|
{
|
|
|
|
// send msg to trigger link out effect
|
|
|
|
if (fLocalPlayerKey)
|
|
|
|
{
|
|
|
|
plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance();
|
|
|
|
|
|
|
|
plLinkEffectsTriggerMsg* lem = TRACKED_NEW plLinkEffectsTriggerMsg();
|
|
|
|
lem->SetLeavingAge(true);
|
|
|
|
lem->SetLinkKey(fLocalPlayerKey);
|
|
|
|
lem->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
lem->SetBCastFlag(plMessage::kNetForce); // Necessary?
|
|
|
|
lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kLinkEffectsMgr_KEY)));
|
|
|
|
lem->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// beging linking in sounds snd gfx
|
|
|
|
//
|
|
|
|
void plNetClientMgr::StartLinkInFX()
|
|
|
|
{
|
|
|
|
if (fLocalPlayerKey)
|
|
|
|
{
|
|
|
|
const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
|
|
|
|
|
|
|
plLinkEffectsTriggerMsg* lem = TRACKED_NEW plLinkEffectsTriggerMsg();
|
|
|
|
lem->SetLeavingAge(false); // linking in
|
|
|
|
lem->SetLinkKey(fLocalPlayerKey);
|
|
|
|
lem->SetLinkInAnimKey(avMod->GetLinkInAnimKey());
|
|
|
|
|
|
|
|
// indicate if we are invisible
|
|
|
|
if (avMod && avMod->IsInStealthMode() && avMod->GetTarget(0))
|
|
|
|
{
|
|
|
|
lem->SetInvisLevel(avMod->GetStealthLevel());
|
|
|
|
}
|
|
|
|
|
|
|
|
lem->SetBCastFlag(plMessage::kNetPropagate);
|
|
|
|
|
|
|
|
lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kLinkEffectsMgr_KEY)));
|
|
|
|
lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kClient_KEY)));
|
|
|
|
plgDispatch::MsgSend(lem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// compute the difference between our clock and the server's in unified time
|
|
|
|
//
|
|
|
|
void plNetClientMgr::UpdateServerTimeOffset(plNetMessage* msg)
|
|
|
|
{
|
|
|
|
if ((hsTimer::GetSysSeconds() - fLastTimeUpdate) > 5)
|
|
|
|
{
|
|
|
|
fLastTimeUpdate = hsTimer::GetSysSeconds();
|
|
|
|
|
|
|
|
const plUnifiedTime& msgSentUT = msg->GetTimeSent();
|
|
|
|
if (!msgSentUT.AtEpoch())
|
|
|
|
{
|
|
|
|
double diff = plUnifiedTime::GetTimeDifference(msgSentUT, plClientUnifiedTime::GetCurrentTime());
|
|
|
|
|
|
|
|
if (fServerTimeOffset == 0)
|
|
|
|
{
|
|
|
|
fServerTimeOffset = diff;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fServerTimeOffset = fServerTimeOffset + ((diff - fServerTimeOffset) / ++fTimeSamples);
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugMsg("Setting server time offset to %f", fServerTimeOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::ResetServerTimeOffset()
|
|
|
|
{
|
|
|
|
fServerTimeOffset = 0;
|
|
|
|
fTimeSamples = 0;
|
|
|
|
fLastTimeUpdate = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return the gameservers time
|
|
|
|
//
|
|
|
|
plUnifiedTime plNetClientMgr::GetServerTime() const
|
|
|
|
{
|
|
|
|
if ( fServerTimeOffset==0 ) // offline mode or before connecting/calibrating to a server
|
|
|
|
return plUnifiedTime::GetCurrentTime();
|
|
|
|
|
|
|
|
plUnifiedTime serverUT;
|
|
|
|
if (fServerTimeOffset<0)
|
|
|
|
return plUnifiedTime::GetCurrentTime() - plUnifiedTime(fabs(fServerTimeOffset));
|
|
|
|
else
|
|
|
|
return plUnifiedTime::GetCurrentTime() + plUnifiedTime(fServerTimeOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// How old is the age
|
|
|
|
//
|
|
|
|
double plNetClientMgr::GetCurrentAgeElapsedSeconds() const
|
|
|
|
{
|
|
|
|
return plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeElapsedSeconds(GetServerTime());
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get the time in the current age (0-1), taking into account the age's day length
|
|
|
|
//
|
|
|
|
float plNetClientMgr::GetCurrentAgeTimeOfDayPercent() const
|
|
|
|
{
|
|
|
|
if (fOverrideAgeTimeOfDayPercent>=0)
|
|
|
|
return fOverrideAgeTimeOfDayPercent;
|
|
|
|
|
|
|
|
return plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeTimeOfDayPercent(GetServerTime());
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// main update fxn for net client code, return hsFail on err
|
|
|
|
//
|
|
|
|
int plNetClientMgr::Update(double secs)
|
|
|
|
{
|
|
|
|
int ret=hsOK; // ret code is unchecked, but what the hay
|
|
|
|
|
|
|
|
if (GetFlagsBit(kDisableOnNextUpdate)) {
|
|
|
|
SetFlagsBit(kDisableOnNextUpdate, false);
|
|
|
|
IDisableNet();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pump net messages
|
|
|
|
NetCommUpdate();
|
|
|
|
|
|
|
|
static double lastUpdateTime=0;
|
|
|
|
double curTime=hsTimer::GetSeconds();
|
|
|
|
if (curTime-lastUpdateTime > 1.f)
|
|
|
|
{
|
|
|
|
DebugMsg("NetClient hasn't updated for %f secs", curTime-lastUpdateTime);
|
|
|
|
}
|
|
|
|
lastUpdateTime=curTime;
|
|
|
|
|
|
|
|
if (!GetFlagsBit(kDisabled))
|
|
|
|
{
|
|
|
|
fNetClientStats.UpdateAgeStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetFlagsBit(kPlayingGame) )
|
|
|
|
{
|
|
|
|
MaybeSendPendingPagingRoomMsgs();
|
|
|
|
ICheckPendingStateLoad(secs);
|
|
|
|
ISendDirtyState(secs);
|
|
|
|
IUpdateListenList(secs);
|
|
|
|
if (GetFlagsBit(plNetClientApp::kShowLists))
|
|
|
|
IShowLists();
|
|
|
|
if (GetFlagsBit(plNetClientApp::kShowRooms))
|
|
|
|
IShowRooms();
|
|
|
|
if (GetFlagsBit(plNetClientApp::kShowAvatars))
|
|
|
|
IShowAvatars();
|
|
|
|
if (GetFlagsBit(plNetClientApp::kShowRelevanceRegions))
|
|
|
|
IShowRelevanceRegions();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send dirty nodes, deliver vault callbacks, etc.
|
|
|
|
VaultUpdate();
|
|
|
|
|
|
|
|
plNetLinkingMgr::GetInstance()->Update();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// See if there is state recvd from the network that needsto be delivered
|
|
|
|
//
|
|
|
|
void plNetClientMgr::ICheckPendingStateLoad(double secs)
|
|
|
|
{
|
|
|
|
if (!fPendingLoads.empty() && GetFlagsBit( kPlayingGame ) || (GetFlagsBit(kLoadingInitialAgeState) && !GetFlagsBit(kNeedInitialAgeStateCount)))
|
|
|
|
{
|
|
|
|
PendingLoadsList::iterator it = fPendingLoads.begin();
|
|
|
|
while ( it!=fPendingLoads.end() )
|
|
|
|
{
|
|
|
|
PendingLoad * pl = (*it);
|
|
|
|
|
|
|
|
// cache rcvr key
|
|
|
|
if (!pl->fKey)
|
|
|
|
{
|
|
|
|
// check for existence of key in dataset, excluding clone info.
|
|
|
|
plUoid tmpUoid = pl->fUoid;
|
|
|
|
tmpUoid.SetClone(0,0);
|
|
|
|
if ( !hsgResMgr::ResMgr()->FindKey( tmpUoid ) )
|
|
|
|
{
|
|
|
|
// discard the state if object not found in dataset.
|
|
|
|
char tmp[256];
|
|
|
|
hsLogEntry( DebugMsg( "Failed to find object %s in dataset. Discarding pending state '%s'",
|
|
|
|
tmpUoid.StringIze(tmp),
|
|
|
|
pl->fSDRec->GetDescriptor()->GetName() ) );
|
|
|
|
delete pl;
|
|
|
|
it = fPendingLoads.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// find and cache the real key.
|
|
|
|
pl->fKey = hsgResMgr::ResMgr()->FindKey(pl->fUoid);
|
|
|
|
}
|
|
|
|
|
|
|
|
// deliver state if possible
|
|
|
|
plSynchedObject*so = pl->fKey ? plSynchedObject::ConvertNoRef(pl->fKey->ObjectIsLoaded()) : nil;
|
|
|
|
if (so && so->IsFinal())
|
|
|
|
{
|
|
|
|
plSDLModifierMsg* sdlMsg = TRACKED_NEW plSDLModifierMsg(pl->fSDRec->GetDescriptor()->GetName(),
|
|
|
|
plSDLModifierMsg::kRecv);
|
|
|
|
sdlMsg->SetState( pl->fSDRec, true/*delete pl->fSDRec for us*/ );
|
|
|
|
sdlMsg->SetPlayerID( pl->fPlayerID );
|
|
|
|
|
|
|
|
#ifdef HS_DEBUGGING
|
|
|
|
if (plNetObjectDebugger::GetInstance()->IsDebugObject(so))
|
|
|
|
{
|
|
|
|
hsLogEntry( DebugMsg( "Delivering SDL state %s:%s", pl->fKey->GetName(), pl->fSDRec->GetDescriptor()->GetName() ) );
|
|
|
|
// hsLogEntry(plNetObjectDebugger::GetInstance()->LogMsg(xtl::format("Dispatching SDL state, type %s to object:%s, locallyOwned=%d, st=%.3f rt=%.3f",
|
|
|
|
// pl->fSDRec->GetDescriptor()->GetName(), pl->fKey->GetName(),
|
|
|
|
// so->IsLocallyOwned()==plSynchedObject::kYes, secs, hsTimer::GetSeconds()).c_str()));
|
|
|
|
// hsLogEntry( pl->fSDRec->DumpToObjectDebugger( "Delivering SDL state", false, 0 ) );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
sdlMsg->Send(pl->fKey);
|
|
|
|
|
|
|
|
pl->fSDRec = nil; // so it won't be deleted in the PendingLoad dtor.
|
|
|
|
delete pl;
|
|
|
|
it = fPendingLoads.erase(it);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// report old pending state
|
|
|
|
double rawSecs = hsTimer::GetSeconds();
|
|
|
|
if ((rawSecs - pl->fQueuedTime) > 60.f /*secs*/)
|
|
|
|
{
|
|
|
|
char tmp[256], tmp2[256];
|
|
|
|
if (pl->fQueueTimeResets >= 5)
|
|
|
|
{
|
|
|
|
// if this is our fifth time in here then we've been queued
|
|
|
|
// for around 5 minutes and its time to go
|
|
|
|
|
|
|
|
WarningMsg( "Pending state '%s' for object [uoid:%s,key:%s] has been queued for about %f secs. Removing...",
|
|
|
|
pl->fSDRec && pl->fSDRec->GetDescriptor() ? pl->fSDRec->GetDescriptor()->GetName() : "?",
|
|
|
|
pl->fUoid.StringIze(tmp2), pl->fKey ? pl->fKey->GetUoid().StringIze(tmp) : "?",
|
|
|
|
( rawSecs - pl->fQueuedTime ) * pl->fQueueTimeResets);
|
|
|
|
|
|
|
|
delete pl;
|
|
|
|
it = fPendingLoads.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
WarningMsg( "Pending state '%s' for object [uoid:%s,key:%s] has been queued for about %f secs. %s",
|
|
|
|
pl->fSDRec && pl->fSDRec->GetDescriptor() ? pl->fSDRec->GetDescriptor()->GetName() : "?",
|
|
|
|
pl->fUoid.StringIze(tmp2), pl->fKey ? pl->fKey->GetUoid().StringIze(tmp) : "?",
|
|
|
|
( rawSecs - pl->fQueuedTime ) * pl->fQueueTimeResets,
|
|
|
|
so ? "(not loaded)" : "(not final)" );
|
|
|
|
// reset queue time so we don't spew too many log msgs.
|
|
|
|
pl->fQueuedTime = rawSecs;
|
|
|
|
pl->fQueueTimeResets += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Determine the net group for a given object (based on paging unit)
|
|
|
|
//
|
|
|
|
plNetGroupId plNetClientMgr::SelectNetGroup(plSynchedObject* objIn, plKey roomKey)
|
|
|
|
{
|
|
|
|
if( objIn->GetSynchFlags() & plSynchedObject::kHasConstantNetGroup )
|
|
|
|
return objIn->GetNetGroup();
|
|
|
|
|
|
|
|
return plNetGroupId(roomKey->GetUoid().GetLocation(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get the net group that an object belongs to, taking into acct
|
|
|
|
// that objects may derive their net group from another object they belong to.
|
|
|
|
//
|
|
|
|
plNetGroupId plNetClientMgr::GetEffectiveNetGroup(const plSynchedObject*& obj) const
|
|
|
|
{
|
|
|
|
plNetGroupId netGroup = plNetGroup::kNetGroupUnknown;
|
|
|
|
// the object is an interface, so consider the owner instead
|
|
|
|
const plObjInterface* oInt=plObjInterface::ConvertNoRef(obj);
|
|
|
|
if (oInt)
|
|
|
|
{
|
|
|
|
// the object is an interface, so consider the owner instead
|
|
|
|
if (oInt->GetOwner())
|
|
|
|
obj = oInt->GetOwner();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const plModifier* mod=plModifier::ConvertNoRef(obj);
|
|
|
|
if (mod)
|
|
|
|
{
|
|
|
|
// the object is a modifier, so consider the first target instead
|
|
|
|
if (mod->GetNumTargets() && mod->GetTarget(0))
|
|
|
|
obj = mod->GetTarget(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const plLayerInterface* li = plLayerInterface::ConvertNoRef(obj);
|
|
|
|
const plClothingOutfit* cl = plClothingOutfit::ConvertNoRef(obj);
|
|
|
|
if (li || cl)
|
|
|
|
{
|
|
|
|
// the object is a layer interface or clothing object
|
|
|
|
plUoid u = obj->GetKey()->GetUoid();
|
|
|
|
if (u.GetLocation().IsReserved())
|
|
|
|
{
|
|
|
|
// layer interface is associated with an avatar, find which one
|
|
|
|
int cloneID = u.GetCloneID();
|
|
|
|
int clonePlayerID = u.GetClonePlayerID();
|
|
|
|
for(int i = -1; i < RemotePlayerKeys().size(); i++)
|
|
|
|
{
|
|
|
|
plKey pKey = (i==-1) ? GetLocalPlayerKey() : RemotePlayerKeys()[i];
|
|
|
|
if (pKey && pKey->GetUoid().GetCloneID()==cloneID && pKey->GetUoid().GetClonePlayerID()==clonePlayerID)
|
|
|
|
{
|
|
|
|
obj = plSynchedObject::ConvertNoRef(pKey->ObjectIsLoaded());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// layer interface is on a non-avatar object
|
|
|
|
netGroup = u.GetLocation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const plPhysical* ph = plPhysical::ConvertNoRef(obj);
|
|
|
|
if (ph)
|
|
|
|
{
|
|
|
|
// the object is a physical
|
|
|
|
obj = plSynchedObject::ConvertNoRef(ph->GetObjectKey()->ObjectIsLoaded());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj)
|
|
|
|
netGroup = obj->GetNetGroup();
|
|
|
|
|
|
|
|
return netGroup;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// If we don't know for sure if something is locallyOwned, check if it' related to the remote or
|
|
|
|
// local avatar. if not, fallback to join order.
|
|
|
|
//
|
|
|
|
int plNetClientMgr::IDeduceLocallyOwned(const plUoid& uoid) const
|
|
|
|
{
|
|
|
|
// check against local/remote avatars
|
|
|
|
if (fLocalPlayerKey)
|
|
|
|
if (uoid.GetLocation() == fLocalPlayerKey->GetUoid().GetLocation() &&
|
|
|
|
uoid.GetClonePlayerID() == fLocalPlayerKey->GetUoid().GetClonePlayerID())
|
|
|
|
return plSynchedObject::kYes; // this object's location is the same as the local player's
|
|
|
|
|
|
|
|
if (!fRemotePlayerKeys.empty())
|
|
|
|
if (uoid.GetLocation() == fRemotePlayerKeys.back()->GetUoid().GetLocation() &&
|
|
|
|
uoid.GetClonePlayerID() == fRemotePlayerKeys.back()->GetUoid().GetClonePlayerID())
|
|
|
|
return plSynchedObject::kNo; // this object's location is the same as the currently loading remote player's
|
|
|
|
|
|
|
|
return fIsOwner;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// returns yes/no
|
|
|
|
// for special cases, like sceneNodes.
|
|
|
|
//
|
|
|
|
int plNetClientMgr::IsLocallyOwned(const plUoid& uoid) const
|
|
|
|
{
|
|
|
|
// we're non-networked
|
|
|
|
if ( GetFlagsBit(kDisabled))
|
|
|
|
return plSynchedObject::kYes;
|
|
|
|
|
|
|
|
plNetGroupId netGroup = uoid.GetLocation();
|
|
|
|
int answer=GetNetGroups()->IsGroupLocal(netGroup);
|
|
|
|
|
|
|
|
if (answer==-1)
|
|
|
|
answer = IDeduceLocallyOwned(uoid);
|
|
|
|
|
|
|
|
hsAssert(answer==plSynchedObject::kYes || answer==plSynchedObject::kNo, "invalid answer to IsLocallyOwned()");
|
|
|
|
return answer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// check if this object is in a group which is owned (mastered) by this client.
|
|
|
|
// returns yes/no
|
|
|
|
//
|
|
|
|
int plNetClientMgr::IsLocallyOwned(const plSynchedObject* obj) const
|
|
|
|
{
|
|
|
|
// we're non-networked
|
|
|
|
if ( GetFlagsBit(kDisabled) )
|
|
|
|
return plSynchedObject::kYes;
|
|
|
|
|
|
|
|
if( !obj )
|
|
|
|
return plSynchedObject::kNo;
|
|
|
|
|
|
|
|
// object is flagged as local only
|
|
|
|
if (!obj->IsNetSynched())
|
|
|
|
return plSynchedObject::kYes;
|
|
|
|
|
|
|
|
plNetGroupId netGroup = GetEffectiveNetGroup(obj);
|
|
|
|
int answer=GetNetGroups()->IsGroupLocal(netGroup);
|
|
|
|
|
|
|
|
if (answer==-1) // not sure
|
|
|
|
answer = IDeduceLocallyOwned(obj->GetKey()->GetUoid());
|
|
|
|
|
|
|
|
hsAssert(answer==plSynchedObject::kYes || answer==plSynchedObject::kNo, "invalid answer to IsLocallyOwned()");
|
|
|
|
return answer;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return localPlayer ptr
|
|
|
|
//
|
|
|
|
plSynchedObject* plNetClientMgr::GetLocalPlayer(hsBool forceLoad) const
|
|
|
|
{
|
|
|
|
if (forceLoad)
|
|
|
|
return fLocalPlayerKey ? plSynchedObject::ConvertNoRef(fLocalPlayerKey->GetObjectPtr()) : nil;
|
|
|
|
else
|
|
|
|
return fLocalPlayerKey ?
|
|
|
|
plSynchedObject::ConvertNoRef(fLocalPlayerKey->ObjectIsLoaded()) : nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return a ptr to a remote player
|
|
|
|
//
|
|
|
|
plSynchedObject* plNetClientMgr::GetRemotePlayer(int i) const
|
|
|
|
{
|
|
|
|
return fRemotePlayerKeys[i] ? plSynchedObject::ConvertNoRef(fRemotePlayerKeys[i]->ObjectIsLoaded()) : nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// check if a key si a remote player
|
|
|
|
//
|
|
|
|
hsBool plNetClientMgr::IsRemotePlayerKey(const plKey pKey, int *idx)
|
|
|
|
{
|
|
|
|
if (pKey)
|
|
|
|
{
|
|
|
|
plKeyVec::iterator result=std::find(fRemotePlayerKeys.begin(), fRemotePlayerKeys.end(), pKey);
|
|
|
|
hsBool found = result!=fRemotePlayerKeys.end();
|
|
|
|
if (idx)
|
|
|
|
*idx = found ? result-fRemotePlayerKeys.begin() : -1;
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// add remote char if not already there
|
|
|
|
//
|
|
|
|
void plNetClientMgr::AddRemotePlayerKey(plKey pKey)
|
|
|
|
{
|
|
|
|
hsAssert(pKey, "adding nil remote player key");
|
|
|
|
if (!IsRemotePlayerKey(pKey))
|
|
|
|
{
|
|
|
|
fRemotePlayerKeys.push_back(pKey);
|
|
|
|
hsAssert(pKey!=fLocalPlayerKey, "setting remote player to the local player?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// MsgReceive handler for plasma messages
|
|
|
|
//
|
|
|
|
hsBool plNetClientMgr::MsgReceive( plMessage* msg )
|
|
|
|
{
|
|
|
|
if (plNetLinkingMgr::GetInstance()->MsgReceive( msg ))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg);
|
|
|
|
if (evalMsg)
|
|
|
|
{
|
|
|
|
IPlaybackMsgs();
|
|
|
|
|
|
|
|
if ( GetFlagsBit( kNeedToSendAgeLoadedMsg ) )
|
|
|
|
{
|
|
|
|
SetFlagsBit( kNeedToSendAgeLoadedMsg, false );
|
|
|
|
plAgeLoader::GetInstance()->NotifyAgeLoaded( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( GetFlagsBit( kNeedToSendInitialAgeStateLoadedMsg ) )
|
|
|
|
{
|
|
|
|
SetFlagsBit(kNeedToSendInitialAgeStateLoadedMsg, false);
|
|
|
|
plInitialAgeStateLoadedMsg* m = TRACKED_NEW plInitialAgeStateLoadedMsg;
|
|
|
|
m->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg);
|
|
|
|
if (ref)
|
|
|
|
{
|
|
|
|
if( ref->fType == kVaultImage )
|
|
|
|
{
|
|
|
|
// Ignore, we just use it for reffing, don't care about the actual pointer
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsAssert(ref->fType==kAgeSDLHook, "unknown ref msg context");
|
|
|
|
if (ref->GetContext()==plRefMsg::kOnCreate)
|
|
|
|
{
|
|
|
|
hsAssert(fAgeSDLObjectKey==nil, "already have a ref to age sdl hook");
|
|
|
|
fAgeSDLObjectKey = ref->GetRef()->GetKey();
|
|
|
|
char tmp[256];
|
|
|
|
DebugMsg("Age SDL hook object created, uoid=%s", fAgeSDLObjectKey->GetUoid().StringIze(tmp));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fAgeSDLObjectKey=nil;
|
|
|
|
DebugMsg("Age SDL hook object destroyed");
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plNetClientMgrMsg * ncmMsg = plNetClientMgrMsg::ConvertNoRef(msg)) {
|
|
|
|
if (ncmMsg->type == plNetClientMgrMsg::kCmdDisableNet) {
|
|
|
|
SetFlagsBit(kDisableOnNextUpdate);
|
|
|
|
hsRefCnt_SafeUnRef(fDisableMsg);
|
|
|
|
fDisableMsg = ncmMsg;
|
|
|
|
fDisableMsg->Ref();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plNetCommAuthMsg * authMsg = plNetCommAuthMsg::ConvertNoRef(msg)) {
|
|
|
|
if (IS_NET_ERROR(authMsg->result)) {
|
|
|
|
char str[256];
|
|
|
|
StrPrintf(str, arrsize(str), "Authentication failed: %S", NetErrorToString(authMsg->result));
|
|
|
|
QueueDisableNet(true, str);
|
|
|
|
return false; // @@@ TODO: Handle this failure better
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plNetCommActivePlayerMsg * activePlrMsg = plNetCommActivePlayerMsg::ConvertNoRef(msg)) {
|
|
|
|
if (IS_NET_ERROR(activePlrMsg->result)) {
|
|
|
|
char str[256];
|
|
|
|
StrPrintf(str, arrsize(str), "SetActivePlayer failed: %S", NetErrorToString(activePlrMsg->result));
|
|
|
|
QueueDisableNet(true, str);
|
|
|
|
return false; // @@@ TODO: Handle this failure better.
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plPlayerPageMsg *playerPageMsg = plPlayerPageMsg::ConvertNoRef(msg);
|
|
|
|
if(playerPageMsg)
|
|
|
|
{
|
|
|
|
IHandlePlayerPageMsg(playerPageMsg);
|
|
|
|
return true; // handled
|
|
|
|
}
|
|
|
|
|
|
|
|
plLoadCloneMsg* pCloneMsg = plLoadCloneMsg::ConvertNoRef(msg);
|
|
|
|
if(pCloneMsg)
|
|
|
|
{
|
|
|
|
ILoadClone(pCloneMsg);
|
|
|
|
return true; // handled
|
|
|
|
}
|
|
|
|
|
|
|
|
// player is petitioning a CCR
|
|
|
|
plCCRPetitionMsg* petMsg=plCCRPetitionMsg::ConvertNoRef(msg);
|
|
|
|
if (petMsg)
|
|
|
|
{
|
|
|
|
ISendCCRPetition(petMsg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// a remote CCR is turning invisible
|
|
|
|
plCCRInvisibleMsg* invisMsg=plCCRInvisibleMsg::ConvertNoRef(msg);
|
|
|
|
if (invisMsg)
|
|
|
|
{
|
|
|
|
LogMsg(kLogDebug, "plNetClientMgr::MsgReceive - Got plCCRInvisibleMsg");
|
|
|
|
MakeCCRInvisible(invisMsg->fAvKey, invisMsg->fInvisLevel);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plCCRBanLinkingMsg* banLinking = plCCRBanLinkingMsg::ConvertNoRef(msg);
|
|
|
|
if (banLinking)
|
|
|
|
{
|
|
|
|
DebugMsg("Setting BanLinking to %d", banLinking->fBan);
|
|
|
|
SetFlagsBit(kBanLinking, banLinking->fBan);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plCCRSilencePlayerMsg* silence = plCCRSilencePlayerMsg::ConvertNoRef(msg);
|
|
|
|
if (silence)
|
|
|
|
{
|
|
|
|
DebugMsg("Setting Silence to %d", silence->fSilence);
|
|
|
|
SetFlagsBit(kSilencePlayer, silence->fSilence);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
plInitialAgeStateLoadedMsg *stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef( msg );
|
|
|
|
if( stateMsg != nil )
|
|
|
|
{
|
|
|
|
// done receiving the initial state of the age from the server
|
|
|
|
plNetObjectDebugger::GetInstance()->LogMsg("OnServerInitComplete");
|
|
|
|
|
|
|
|
// delete fProgressBar;
|
|
|
|
// fProgressBar=nil;
|
|
|
|
|
|
|
|
SetFlagsBit(kLoadingInitialAgeState, false);
|
|
|
|
StartLinkInFX();
|
|
|
|
}
|
|
|
|
|
|
|
|
plNetVoiceListMsg* voxList = plNetVoiceListMsg::ConvertNoRef(msg);
|
|
|
|
if (voxList)
|
|
|
|
{
|
|
|
|
IHandleNetVoiceListMsg(voxList);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSynchEnableMsg* synchEnable = plSynchEnableMsg::ConvertNoRef(msg);
|
|
|
|
if (synchEnable)
|
|
|
|
{
|
|
|
|
if (synchEnable->fPush)
|
|
|
|
{
|
|
|
|
plSynchedObject::PushSynchDisabled(!synchEnable->fEnable);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plSynchedObject::PopSynchDisabled();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg);
|
|
|
|
if (clientMsg && clientMsg->GetClientMsgFlag()==plClientMsg::kInitComplete)
|
|
|
|
{
|
|
|
|
// add 1 debug object for age sdl
|
|
|
|
if (plNetObjectDebugger::GetInstance())
|
|
|
|
{
|
|
|
|
plNetObjectDebugger::GetInstance()->RemoveDebugObject("AgeSDLHook");
|
|
|
|
plNetObjectDebugger::GetInstance()->AddDebugObject("AgeSDLHook");
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we're linking to startup we don't need (or want) a player set
|
|
|
|
char ageName[kMaxAgeNameLength];
|
|
|
|
StrCopy(ageName, NetCommGetStartupAge()->ageDatasetName, arrsize(ageName));
|
|
|
|
if (!StrLen(ageName))
|
|
|
|
StrCopy(ageName, "StartUp", arrsize(ageName));
|
|
|
|
if (0 == StrCmpI(ageName, "StartUp"))
|
|
|
|
NetCommSetActivePlayer(0, nil);
|
|
|
|
|
|
|
|
plAgeLinkStruct link;
|
|
|
|
link.GetAgeInfo()->SetAgeFilename(NetCommGetStartupAge()->ageDatasetName);
|
|
|
|
link.SetLinkingRules(plNetCommon::LinkingRules::kOriginalBook);
|
|
|
|
plNetLinkingMgr::GetInstance()->LinkToAge(&link);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plNetClientApp::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::IncNumInitialSDLStates()
|
|
|
|
{
|
|
|
|
fNumInitialSDLStates++;
|
|
|
|
DebugMsg( "Received %d initial SDL states", fNumInitialSDLStates );
|
|
|
|
if ( GetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount ) )
|
|
|
|
{
|
|
|
|
DebugMsg( "Need initial SDL state count" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( GetNumInitialSDLStates()>=GetRequiredNumInitialSDLStates() )
|
|
|
|
{
|
|
|
|
ICheckPendingStateLoad(hsTimer::GetSysSeconds());
|
|
|
|
NotifyRcvdAllSDLStates();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::NotifyRcvdAllSDLStates() {
|
|
|
|
DebugMsg( "Got all initial SDL states" );
|
|
|
|
plNetClientMgrMsg * msg = TRACKED_NEW plNetClientMgrMsg();
|
|
|
|
msg->type = plNetClientMgrMsg::kNotifyRcvdAllSDLStates;
|
|
|
|
msg->SetBCastFlag(plMessage::kBCastByType);
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return true if we should send this msg
|
|
|
|
//
|
|
|
|
bool plNetClientMgr::CanSendMsg(plNetMessage * msg)
|
|
|
|
{
|
|
|
|
// TEMP
|
|
|
|
if (GetFlagsBit(kSilencePlayer))
|
|
|
|
{
|
|
|
|
if (plNetMsgVoice::ConvertNoRef(msg))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
plNetMsgGameMessage* gm=plNetMsgGameMessage::ConvertNoRef(msg);
|
|
|
|
if (gm && gm->StreamInfo()->GetStreamType()==pfKIMsg::Index())
|
|
|
|
{
|
|
|
|
hsReadOnlyStream stream(gm->StreamInfo()->GetStreamLen(), gm->StreamInfo()->GetStreamBuf());
|
|
|
|
pfKIMsg* kiMsg = pfKIMsg::ConvertNoRef(hsgResMgr::ResMgr()->ReadCreatable(&stream));
|
|
|
|
if (kiMsg && kiMsg->GetCommand()==pfKIMsg::kHACKChatMsg)
|
|
|
|
{
|
|
|
|
delete kiMsg;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
delete kiMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Return the net client (account) name of the player whose avatar
|
|
|
|
// key is provided. If avKey is nil, returns local client name.
|
|
|
|
//
|
|
|
|
const char* plNetClientMgr::GetPlayerName(const plKey avKey) const
|
|
|
|
{
|
|
|
|
// local case
|
|
|
|
if (!avKey || avKey==GetLocalPlayerKey())
|
|
|
|
return NetCommGetPlayer()->playerNameAnsi;
|
|
|
|
|
|
|
|
plNetTransportMember* mbr=TransportMgr().GetMember(TransportMgr().FindMember(avKey));
|
|
|
|
return mbr ? mbr->GetPlayerName() : nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char * plNetClientMgr::GetPlayerNameById (unsigned playerId) const {
|
|
|
|
// local case
|
|
|
|
if (NetCommGetPlayer()->playerInt == playerId)
|
|
|
|
return NetCommGetPlayer()->playerNameAnsi;
|
|
|
|
|
|
|
|
plNetTransportMember * mbr = TransportMgr().GetMember(TransportMgr().FindMember(playerId));
|
|
|
|
return mbr ? mbr->GetPlayerName() : nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned plNetClientMgr::GetPlayerIdByName (const char name[]) const {
|
|
|
|
// local case
|
|
|
|
if (0 == StrCmp(name, NetCommGetPlayer()->playerNameAnsi, (unsigned)-1))
|
|
|
|
NetCommGetPlayer()->playerInt;
|
|
|
|
|
|
|
|
unsigned n = TransportMgr().GetNumMembers();
|
|
|
|
for (unsigned i = 0; i < n; ++i)
|
|
|
|
if (plNetTransportMember * member = TransportMgr().GetMember(i))
|
|
|
|
if (0 == StrCmp(name, member->GetPlayerName(), (unsigned)-1))
|
|
|
|
return member->GetPlayerID();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 plNetClientMgr::GetPlayerID() const
|
|
|
|
{
|
|
|
|
return NetCommGetPlayer()->playerInt;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// return true if the age is the set of local ages
|
|
|
|
//
|
|
|
|
static const char* gLocalAges[]={"GUI", "AvatarCustomization", ""};
|
|
|
|
static bool IsLocalAge(const char* ageName)
|
|
|
|
{
|
|
|
|
int i=0;
|
|
|
|
while(strlen(gLocalAges[i]))
|
|
|
|
{
|
|
|
|
if (!stricmp(gLocalAges[i], ageName))
|
|
|
|
return true;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// is the object in a local age?
|
|
|
|
//
|
|
|
|
bool plNetClientMgr::ObjectInLocalAge(const plSynchedObject* obj) const
|
|
|
|
{
|
|
|
|
plLocation loc = obj->GetKey()->GetUoid().GetLocation();
|
|
|
|
const plPageInfo* pageInfo = plKeyFinder::Instance().GetLocationInfo(loc);
|
|
|
|
bool ret= IsLocalAge(pageInfo->GetAge());
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// the next age we are going to
|
|
|
|
//
|
|
|
|
const char* plNetClientMgr::GetNextAgeFilename()
|
|
|
|
{
|
|
|
|
// set when we start linking to an age.
|
|
|
|
plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance();
|
|
|
|
return lm->GetAgeLink()->GetAgeInfo()->GetAgeFilename();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// a remote ccr is turning [in]visible
|
|
|
|
//
|
|
|
|
void plNetClientMgr::MakeCCRInvisible(plKey avKey, int level)
|
|
|
|
{
|
|
|
|
if (!avKey)
|
|
|
|
{
|
|
|
|
ErrorMsg("Failed to make remote CCR Invisible, nil avatar key");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level<0)
|
|
|
|
{
|
|
|
|
ErrorMsg("Failed to make remote CCR Invisible, negative level");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!avKey->ObjectIsLoaded() || !avKey->ObjectIsLoaded()->IsFinal())
|
|
|
|
{
|
|
|
|
hsAssert(false, "avatar not loaded or final");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAvatarStealthModeMsg *msg = TRACKED_NEW plAvatarStealthModeMsg();
|
|
|
|
msg->SetSender(avKey);
|
|
|
|
msg->fLevel = level;
|
|
|
|
|
|
|
|
if (GetCCRLevel()<level) // I'm a lower level than him, so he's invisible to me
|
|
|
|
msg->fMode = plAvatarStealthModeMsg::kStealthCloaked;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// he's visible to me
|
|
|
|
if (AmCCR() && level > 0)
|
|
|
|
msg->fMode = plAvatarStealthModeMsg::kStealthCloakedButSeen; // draw as semi-invis
|
|
|
|
else
|
|
|
|
msg->fMode = plAvatarStealthModeMsg::kStealthVisible;
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugMsg("Handled MakeCCRInvisible - sending stealth msg");;
|
|
|
|
|
|
|
|
// This fxn is called when avatar SDL state is received from the server,
|
|
|
|
// so this msg will inherit the 'non-local' status of the parent sdl msg.
|
|
|
|
// That means that the avatar linkSound won't receive it, since the linkSound
|
|
|
|
// is set as localOnly.
|
|
|
|
// So, terminate the remote cascade and start a new (local) cascade.
|
|
|
|
msg->SetBCastFlag(plMessage::kNetStartCascade);
|
|
|
|
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::QueueDisableNet (bool showDlg, const char str[]) {
|
|
|
|
|
|
|
|
plNetClientMgrMsg * msg = NEWZERO(plNetClientMgrMsg);
|
|
|
|
msg->type = plNetClientMgrMsg::kCmdDisableNet;
|
|
|
|
msg->yes = showDlg;
|
|
|
|
if (str)
|
|
|
|
StrCopy(msg->str, str, arrsize(msg->str));
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
msg->Send(GetKey());
|
|
|
|
#else
|
|
|
|
msg->Ref();
|
|
|
|
fDisableMsg = msg;
|
|
|
|
IDisableNet();
|
|
|
|
#endif;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::IDisableNet () {
|
|
|
|
ASSERT(fDisableMsg);
|
|
|
|
|
|
|
|
if (!GetFlagsBit(kDisabled)) {
|
|
|
|
SetFlagsBit(kDisabled);
|
|
|
|
// cause subsequent net operations to fail immediately, but don't block
|
|
|
|
// waiting for net subsystem to shutdown (we'll do that later)
|
|
|
|
NetCommEnableNet(false, false);
|
|
|
|
|
|
|
|
// display a msg to the player
|
|
|
|
if ( fDisableMsg->yes )
|
|
|
|
{
|
|
|
|
if (!GetFlagsBit(plNetClientApp::kPlayingGame))
|
|
|
|
{
|
|
|
|
// KI may not be loaded
|
|
|
|
char title[256];
|
|
|
|
StrPrintf(title, arrsize(title), "%S Error", ProductCoreName());
|
|
|
|
hsMessageBox(fDisableMsg->str, title, hsMessageBoxNormal, hsMessageBoxIconError );
|
|
|
|
plClientMsg *quitMsg = NEW(plClientMsg)(plClientMsg::kQuit);
|
|
|
|
quitMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pfKIMsg *msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIOKDialog);
|
|
|
|
msg->SetString(fDisableMsg->str);
|
|
|
|
msg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsRefCnt_SafeUnRef(fDisableMsg);
|
|
|
|
fDisableMsg = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plNetClientMgr::IHandlePlayerPageMsg(plPlayerPageMsg *playerMsg)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
plKey playerKey = playerMsg->fPlayer;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
if(playerMsg->fUnload)
|
|
|
|
{
|
|
|
|
if (GetLocalPlayerKey() == playerKey)
|
|
|
|
{
|
|
|
|
fLocalPlayerKey = nil;
|
|
|
|
DebugMsg("Net: Unloading local player %s", playerKey->GetName());
|
|
|
|
|
|
|
|
// notify server - NOTE: he might not still be around to get this...
|
|
|
|
plNetMsgPlayerPage npp (playerKey->GetUoid(), playerMsg->fUnload);
|
|
|
|
npp.SetNetProtocol(kNetProtocolCli2Game);
|
|
|
|
SendMsg(&npp);
|
|
|
|
}
|
|
|
|
else if (IsRemotePlayerKey(playerKey, &idx))
|
|
|
|
{
|
|
|
|
fRemotePlayerKeys.erase(fRemotePlayerKeys.begin()+idx); // remove key from list
|
|
|
|
DebugMsg("Net: Unloading remote player %s", playerKey->GetName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plSceneObject *playerSO = plSceneObject::ConvertNoRef(playerKey->ObjectIsLoaded());
|
|
|
|
if (!playerSO)
|
|
|
|
{
|
|
|
|
hsStatusMessageF("Ignoring player page message for non-existant player.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if(playerMsg->fPlayer)
|
|
|
|
{
|
|
|
|
if (playerMsg->fLocallyOriginated)
|
|
|
|
{
|
|
|
|
hsAssert(!GetLocalPlayerKey() ||
|
|
|
|
GetLocalPlayerKey() == playerKey,
|
|
|
|
"Different local player already loaded");
|
|
|
|
|
|
|
|
hsLogEntry(DebugMsg("Adding LOCAL player %s\n", playerKey->GetName()));
|
|
|
|
playerSO->SetNetGroupConstant(plNetGroup::kNetGroupLocalPlayer);
|
|
|
|
|
|
|
|
// don't save avatar state permanently on server
|
|
|
|
playerSO->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile);
|
|
|
|
const plCoordinateInterface* co = playerSO->GetCoordinateInterface();
|
|
|
|
if (co)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<co->GetNumChildren();i++)
|
|
|
|
{
|
|
|
|
if (co->GetChild(i) && co->GetChild(i)->GetOwner())
|
|
|
|
const_cast<plSceneObject*>(co->GetChild(i)->GetOwner())->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// notify server
|
|
|
|
plNetMsgPlayerPage npp (playerKey->GetUoid(), playerMsg->fUnload);
|
|
|
|
npp.SetNetProtocol(kNetProtocolCli2Game);
|
|
|
|
SendMsg(&npp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsLogEntry(DebugMsg("Adding REMOTE player %s\n", playerKey->GetName()));
|
|
|
|
playerSO->SetNetGroupConstant(plNetGroup::kNetGroupRemotePlayer);
|
|
|
|
idx=fTransport.FindMember(playerMsg->fClientID);
|
|
|
|
if( idx != -1 )
|
|
|
|
{
|
|
|
|
hsAssert(playerKey, "NIL KEY?");
|
|
|
|
hsAssert(playerKey->GetName(), "UNNAMED KEY");
|
|
|
|
fTransport.GetMember(idx)->SetAvatarKey(playerKey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsLogEntry(DebugMsg("Ignoring player page msg (player not found in member list) : %s\n", playerKey->GetName()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plAvatarSDLModifier)), "avatar missing avatar SDL modifier");
|
|
|
|
hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plClothingSDLModifier)), "avatar missing clothing SDL modifier");
|
|
|
|
hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plAGMasterSDLModifier)), "avatar missing AGMaster SDL modifier");
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// for debugging purposes
|
|
|
|
bool plNetClientMgr::IFindModifier(plSynchedObject* obj, Int16 classIdx)
|
|
|
|
{
|
|
|
|
plLayerAnimation* layer = plLayerAnimation::ConvertNoRef(obj);
|
|
|
|
if (layer)
|
|
|
|
return (layer->GetSDLModifier() != nil);
|
|
|
|
|
|
|
|
plResponderModifier* resp = plResponderModifier::ConvertNoRef(obj);
|
|
|
|
if (resp)
|
|
|
|
return (resp->GetSDLModifier() != nil);
|
|
|
|
|
|
|
|
int cnt=0;
|
|
|
|
plSceneObject* sceneObj=plSceneObject::ConvertNoRef(obj);
|
|
|
|
if (sceneObj)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0; i<sceneObj->GetNumModifiers(); i++)
|
|
|
|
if (sceneObj->GetModifier(i) && sceneObj->GetModifier(i)->ClassIndex()==classIdx)
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsAssert(cnt<2, xtl::format("Object %s has multiple SDL modifiers of the same kind (%s)?",
|
|
|
|
obj->GetKeyName(), plFactory::GetNameOfClass(classIdx)).c_str());
|
|
|
|
return cnt==0 ? false : true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plUoid plNetClientMgr::GetAgeSDLObjectUoid(const char* ageName) const
|
|
|
|
{
|
|
|
|
hsAssert(ageName, "nil ageName");
|
|
|
|
|
|
|
|
// if age sdl hook is loaded
|
|
|
|
if (fAgeSDLObjectKey)
|
|
|
|
return fAgeSDLObjectKey->GetUoid();
|
|
|
|
|
|
|
|
// if age is loaded
|
|
|
|
plLocation loc = plKeyFinder::Instance().FindLocation(ageName,plAgeDescription::GetCommonPage(plAgeDescription::kGlobal));
|
|
|
|
if (!loc.IsValid())
|
|
|
|
{
|
|
|
|
// check current age des
|
|
|
|
if (plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName() &&
|
|
|
|
!strcmp(plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName(), ageName))
|
|
|
|
loc=plAgeLoader::GetInstance()->GetCurrAgeDesc().CalcPageLocation("BuiltIn");
|
|
|
|
|
|
|
|
if (!loc.IsValid())
|
|
|
|
{
|
|
|
|
// try to load age desc
|
|
|
|
hsStream* stream=plAgeLoader::GetAgeDescFileStream(ageName);
|
|
|
|
if (stream)
|
|
|
|
{
|
|
|
|
plAgeDescription ad;
|
|
|
|
ad.Read(stream);
|
|
|
|
loc=ad.CalcPageLocation("BuiltIn");
|
|
|
|
stream->Close();
|
|
|
|
}
|
|
|
|
delete stream;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return plUoid(loc, plSceneObject::Index(), plSDL::kAgeSDLObjectName);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Add a state update to the pending queue
|
|
|
|
//
|
|
|
|
void plNetClientMgr::AddPendingLoad(PendingLoad *pl)
|
|
|
|
{
|
|
|
|
pl->fQueuedTime = hsTimer::GetSeconds(); // timestamp
|
|
|
|
|
|
|
|
// find corresponding key
|
|
|
|
pl->fKey = hsgResMgr::ResMgr()->FindKey(pl->fUoid);
|
|
|
|
|
|
|
|
// check for age SDL state
|
|
|
|
char tmp[256];
|
|
|
|
if (pl->fUoid.GetObjectName() && !strcmp(pl->fUoid.GetObjectName(), plSDL::kAgeSDLObjectName))
|
|
|
|
{
|
|
|
|
DebugMsg("Recv SDL state for age hook object, uoid=%s", pl->fUoid.StringIze(tmp));
|
|
|
|
if (!pl->fKey)
|
|
|
|
WarningMsg("Can't find age hook object, nil key!");
|
|
|
|
else
|
|
|
|
DebugMsg("Found age hook object");
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if object is ready
|
|
|
|
if (pl->fKey)
|
|
|
|
{
|
|
|
|
if (!pl->fKey->ObjectIsLoaded())
|
|
|
|
{
|
|
|
|
WarningMsg("Object %s not loaded, withholding SDL state",
|
|
|
|
pl->fKey->GetUoid().StringIze(tmp));
|
|
|
|
}
|
|
|
|
else if (!pl->fKey->ObjectIsLoaded()->IsFinal())
|
|
|
|
{
|
|
|
|
WarningMsg("Object %s is not FINAL, withholding SDL state",
|
|
|
|
pl->fKey->GetUoid().StringIze(tmp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add entry
|
|
|
|
fPendingLoads.push_back(pl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::AddPendingPagingRoomMsg( plNetMsgPagingRoom * msg )
|
|
|
|
{
|
|
|
|
fPendingPagingRoomMsgs.push_back( msg );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::MaybeSendPendingPagingRoomMsgs()
|
|
|
|
{
|
|
|
|
if ( GetFlagsBit( kPlayingGame ) )
|
|
|
|
SendPendingPagingRoomMsgs();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::SendPendingPagingRoomMsgs()
|
|
|
|
{
|
|
|
|
for ( int i=0; i<fPendingPagingRoomMsgs.size(); i++ )
|
|
|
|
SendMsg( fPendingPagingRoomMsgs[i] );
|
|
|
|
ClearPendingPagingRoomMsgs();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::ClearPendingPagingRoomMsgs()
|
|
|
|
{
|
|
|
|
std::for_each( fPendingPagingRoomMsgs.begin(), fPendingPagingRoomMsgs.end(), xtl::delete_ptr() );
|
|
|
|
fPendingPagingRoomMsgs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool plNetClientMgr::DebugMsgV(const char* fmt, va_list args) const
|
|
|
|
{
|
|
|
|
LogMsgV(kLogDebug, fmt, args);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plNetClientMgr::ErrorMsgV(const char* fmt, va_list args) const
|
|
|
|
{
|
|
|
|
LogMsgV(kLogError, fmt, args);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plNetClientMgr::WarningMsgV(const char* fmt, va_list args) const
|
|
|
|
{
|
|
|
|
LogMsgV(kLogError, fmt, args);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plNetClientMgr::AppMsgV(const char* fmt, va_list args) const
|
|
|
|
{
|
|
|
|
LogMsgV(kLogPerf, fmt, args);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plNetClientMgr::IsObjectOwner()
|
|
|
|
{
|
|
|
|
return fIsOwner;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plNetClientMgr::SetObjectOwner(bool own)
|
|
|
|
{
|
|
|
|
fIsOwner = own;
|
|
|
|
}
|