/*==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 "hsTimer.h" #include "plNetClientMgr.h" #include "plNetClientMsgHandler.h" #include "hsResMgr.h" #include "plCreatableIndex.h" #include "plgDispatch.h" #include "plNetLinkingMgr.h" #include "plCCRMgrBase.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/plObjRefMsg.h" #include "../pnMessage/plNodeRefMsg.h" #include "../pnMessage/plClientMsg.h" //#include "../pnMessage/plWarpMsg.h" #include "../pnMessage/plTimeMsg.h" #include "../pnMessage/plCameraMsg.h" #include "../pnMessage/plPlayerPageMsg.h" #include "../pnFactory/plCreator.h" #include "../pnSceneObject/plAudioInterface.h" #include "../pnNetCommon/plSDLTypes.h" #include "../plAudible/plWinAudible.h" #include "../plAvatar/plAvatarMgr.h" #include "../plNetTransport/plNetTransportMember.h" #include "../plMessage/plMemberUpdateMsg.h" #include "../plMessage/plNetOwnershipMsg.h" #include "../plMessage/plCCRMsg.h" #include "../plVault/plVault.h" #include "../plSDL/plSDL.h" #include "../plNetCommon/plNetCommonConstants.h" #include "../plNetMessage/plNetMessage.h" #include "../plNetMessage/plNetCommonMessage.h" #include "../../FeatureLib/pfMessage/pfKIMsg.h" // Should be moved to PubUtil level //////////////////////////////////////////////////////////////////////// plNetClientMsgHandler::plNetClientMsgHandler(plNetClientMgr * mgr) { SetNetApp(mgr); } plNetClientMsgHandler::~plNetClientMsgHandler() { } plNetClientMgr * plNetClientMsgHandler::IGetNetClientMgr() { return plNetClientMgr::ConvertNoRef(GetNetApp()); } int plNetClientMsgHandler::PeekMsg(plNetMessage * netMsg) { plNetClientMgr * nc = IGetNetClientMgr(); int cnt = -1; if (netMsg->GetNetCoreMsg()) // && !netMsg->Peeked()) // not needed { cnt = netMsg->PeekBuffer(netMsg->GetNetCoreMsg()->GetData(), netMsg->GetNetCoreMsg()->GetLen()); hsAssert(cnt,"0 length message"); } return cnt; } void plNetClientMsgHandler::IFillInTransportMember(const plNetMsgMemberInfoHelper* mbi, plNetTransportMember* mbr) { const plNetClientMgr* nc=IGetNetClientMgr(); UInt16 port = mbi->GetClientGuid()->GetSrcPort(); UInt32 addr = mbi->GetClientGuid()->GetSrcAddr(); UInt32 flags = mbi->GetFlags(); UInt32 plrID = mbi->GetClientGuid()->GetPlayerID(); plUoid avUoid = mbi->GetAvatarUoid(); plKey avKey=hsgResMgr::ResMgr()->FindKey(avUoid); mbr->SetPlayerName(mbi->GetClientGuid()->GetPlayerName()); mbr->SetFlags(flags); mbr->SetPlayerID(plrID); mbr->SetCCRLevel(mbi->GetClientGuid()->GetCCRLevel()); if (avKey) mbr->SetAvatarKey(avKey); } int plNetClientMsgHandler::ReceiveMsg(plNetMessage *& netMsg) { #ifdef HS_DEBUGGING //plNetClientMgr::GetInstance()->DebugMsg(" %s", netMsg->ClassName()); #endif plNetClientMgr::GetInstance()->UpdateServerTimeOffset(netMsg); switch(netMsg->ClassIndex()) { default: plNetClientMgr::GetInstance()->ErrorMsg( "Unknown msg: %s", netMsg->ClassName() ); return hsFail; MSG_HANDLER_CASE(plNetMsgTerminated) MSG_HANDLER_CASE(plNetMsgGroupOwner) case CLASS_INDEX_SCOPED(plNetMsgSDLStateBCast): MSG_HANDLER_CASE(plNetMsgSDLState) case CLASS_INDEX_SCOPED(plNetMsgGameMessageDirected): case CLASS_INDEX_SCOPED(plNetMsgLoadClone): MSG_HANDLER_CASE(plNetMsgGameMessage) MSG_HANDLER_CASE(plNetMsgVoice) MSG_HANDLER_CASE(plNetMsgMembersList) MSG_HANDLER_CASE(plNetMsgMemberUpdate) MSG_HANDLER_CASE(plNetMsgListenListUpdate) MSG_HANDLER_CASE(plNetMsgInitialAgeStateSent) } } //////////////////////////////////////////////////////////////////// MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgTerminated) { return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgGroupOwner) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgGroupOwner* m = plNetMsgGroupOwner::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ /* plNetOwnershipMsg* netOwnMsg = TRACKED_NEW plNetOwnershipMsg; int i; for(i=0;iGetNumGroups();i++) { plNetMsgGroupOwner::GroupInfo gr=m->GetGroupInfo(i); netOwnMsg->AddGroupInfo(gr); nc->GetNetGroups()->SetGroup(gr.fGroupID, gr.fOwnIt!=0 ? true : false); hsLogEntry( nc->DebugMsg("\tGroup 0x%x, ownIt=%d\n", (const char*)gr.fGroupID.Room().GetSequenceNumber(), gr.fOwnIt) ); } if (netOwnMsg->GetNumGroups()) netOwnMsg->Send(); else delete netOwnMsg; */ nc->SetObjectOwner(m->IsOwner()); return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgSDLState) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgSDLState* m = plNetMsgSDLState::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ UInt32 rwFlags = 0; if ( m->IsInitialState() ) { nc->IncNumInitialSDLStates(); rwFlags |= plSDL::kMakeDirty; // if initial state, we want all vars. } else if ( nc->GetFlagsBit( plNetClientApp::kLoadingInitialAgeState ) ) { if ( nc->GetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount ) ) { hsLogEntry( nc->DebugMsg( "Ignoring SDL state because we are still joining age and don't have initial age state count yet." ) ); return hsOK; } if ( nc->GetNumInitialSDLStates()GetRequiredNumInitialSDLStates() ) { hsLogEntry( nc->DebugMsg( "Ignoring SDL state because we are still joining age and have not received all initial state yet." ) ); return hsOK; } hsLogEntry( nc->DebugMsg( "We are still joining age, but have all initial states. Accepting this state (risky?)." ) ); } // extract stateDataRecord from msg hsReadOnlyStream stream(m->StreamInfo()->GetStreamLen(), m->StreamInfo()->GetStreamBuf()); char* descName = nil; int ver; plStateDataRecord::ReadStreamHeader(&stream, &descName, &ver); plStateDescriptor* des = plSDLMgr::GetInstance()->FindDescriptor(descName, ver); if (strcmpi(descName, kSDLAvatarPhysical) == 0) rwFlags |= plSDL::kKeepDirty; // // ERROR CHECK SDL FILE // plStateDataRecord* sdRec = des ? TRACKED_NEW plStateDataRecord(des) : nil; if (!sdRec || sdRec->GetDescriptor()->GetVersion()!=ver) { std::string err; if (!sdRec) err = xtl::format( "SDL descriptor %s missing, v=%d", descName, ver); else err = xtl::format( "SDL descriptor %s, version mismatch, server v=%d, client v=%d", descName, ver, sdRec->GetDescriptor()->GetVersion()); hsAssert(false, err.c_str()); nc->ErrorMsg(const_cast(err.c_str())); // Post Quit message nc->QueueDisableNet(true, "SDL Desc Problem"); delete sdRec; } else if( sdRec->Read( &stream, 0, rwFlags ) ) { plStateDataRecord* stateRec = nil; if (m->IsInitialState()) { stateRec = TRACKED_NEW plStateDataRecord(des); stateRec->SetFromDefaults(false); stateRec->UpdateFrom(*sdRec, rwFlags); delete sdRec; } else stateRec = sdRec; plNetClientMgr::PendingLoad* pl = TRACKED_NEW plNetClientMgr::PendingLoad(); pl->fSDRec = stateRec; // will be deleted when PendingLoad is processed if (m->GetHasPlayerID()) pl->fPlayerID = m->GetPlayerID(); // copy originating playerID if we have it pl->fUoid = m->ObjectInfo()->GetUoid(); // queue up state nc->fPendingLoads.push_back(pl); hsLogEntry( nc->DebugMsg( "Added pending SDL delivery for %s:%s", m->ObjectInfo()->GetObjectName(), des->GetName() ) ); } else delete sdRec; delete [] descName; // We've only used descName for a lookup (via SDR, and some error strings. Must delete now. return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgGameMessage) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgGameMessage* m = plNetMsgGameMessage::ConvertNoRef(netMsg); if (m) { PeekMsg(m); plNetMsgLoadClone * lcMsg = plNetMsgLoadClone::ConvertNoRef( m ); if ( lcMsg ) { if ( lcMsg->GetIsInitialState() ) { nc->IncNumInitialSDLStates(); } } hsReadOnlyStream stream(m->StreamInfo()->GetStreamLen(), m->StreamInfo()->GetStreamBuf()); plMessage* gameMsg = plMessage::ConvertNoRef(hsgResMgr::ResMgr()->ReadCreatable(&stream)); hsAssert(gameMsg, "nil game msg?"); if (gameMsg) { /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES!!! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sndr %s rcvr %s sz=%d", m->ClassName(), m->AsStdString().c_str(), gameMsg->GetSender() ? gameMsg->GetSender()->GetName() : "?", gameMsg->GetNumReceivers() ? gameMsg->GetReceiver(0)->GetName() : "?", m->GetNetCoreMsgLen()) ); */ if (lcMsg) { if (!lcMsg->GetIsLoading()) { plLoadAvatarMsg* unloadClone = plLoadAvatarMsg::ConvertNoRef(gameMsg); if (unloadClone) { plLoadAvatarMsg* unloadMsg = TRACKED_NEW plLoadAvatarMsg(unloadClone->GetCloneKey(), unloadClone->GetRequestorKey(), unloadClone->GetUserData(), unloadClone->GetIsPlayer(), false); unloadMsg->SetOriginatingPlayerID(unloadClone->GetOriginatingPlayerID()); gameMsg = unloadMsg; } } else { plLoadCloneMsg* loadClone = plLoadCloneMsg::ConvertNoRef(gameMsg); if (loadClone) { int idx = nc->fTransport.FindMember(loadClone->GetOriginatingPlayerID()); if (idx == -1) { hsLogEntry( nc->DebugMsg( "Ignoring load clone because player isn't in our players list: %d", loadClone->GetOriginatingPlayerID()) ); return hsOK; } } } } plNetClientApp::UnInheritNetMsgFlags(gameMsg); gameMsg->SetBCastFlag(plMessage::kNetCreatedRemotely); if (!m->GetDeliveryTime().AtEpoch()) { double timeStamp; double secs=hsTimer::GetSysSeconds(); m->GetDeliveryTime().ConvertToGameTime(&timeStamp, secs); hsAssert(timeStamp>=secs, "invalid future timeStamp"); gameMsg->SetTimeStamp(timeStamp); nc->DebugMsg("Converting game msg future timeStamp, curT=%f, futT=%f", secs, timeStamp); } plgDispatch::Dispatch()->MsgSend(gameMsg); // Debug if (m->GetHasPlayerID()) { int idx=nc->fTransport.FindMember(m->GetPlayerID()); plNetTransportMember* mbr = idx != -1 ? nc->fTransport.GetMember(idx) : nil; if (mbr) mbr->SetTransportFlags(mbr->GetTransportFlags() | plNetTransportMember::kSendingActions); } return hsOK; } } return hsFail; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgVoice) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgVoice* m = plNetMsgVoice::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ int bufLen = m->GetVoiceDataLen(); const char* buf = m->GetVoiceData(); BYTE flags = m->GetFlags(); BYTE numFrames = m->GetNumFrames(); plKey key = NULL; // plKey key=hsgResMgr::ResMgr()->FindKey(m->ObjectInfo()->GetUoid()); // Filter ignored sender if ( VaultAmIgnoringPlayer( m->GetPlayerID() ) ) { hsLogEntry( nc->DebugMsg( "Ignoring voice chat from ignored player %lu", m->GetPlayerID() ) ); return hsOK; } int idx=nc->fTransport.FindMember(m->GetPlayerID()); plNetTransportMember* mbr = idx != -1 ? nc->fTransport.GetMember(idx) : nil; if (mbr) { key = mbr->GetAvatarKey(); // filter based on listen/talk list (for forced mode) if (nc->GetListenListMode() == plNetClientMgr::kListenList_Forced) { if (nc->GetListenList()->FindMember( mbr )) { hsLogEntry( nc->DebugMsg( "Ignoring voice chat from ignored player %lu", m->GetPlayerID() ) ); return hsOK; } } mbr->SetTransportFlags(mbr->GetTransportFlags() | plNetTransportMember::kSendingVoice); } // hsKeyedObject* obj = key ? key->ObjectIsLoaded() : nil; plSceneObject* avObj = key ? plSceneObject::ConvertNoRef( key->ObjectIsLoaded() ) : nil; // if (obj) if (avObj) { plAudible * aud = avObj->GetAudioInterface()->GetAudible(); pl2WayWinAudible* pAud = pl2WayWinAudible::ConvertNoRef(aud); if (pAud) pAud->PlayNetworkedSpeech(buf, bufLen, numFrames, flags); else { nc->ErrorMsg("\tObject doesn't have audible"); } } else { nc->DebugMsg("\tCan't find loaded object\n"); } return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgMembersList) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgMembersList* m = plNetMsgMembersList::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ int i; // remove existing members, except server for( i=nc->fTransport.GetNumMembers()-1 ; i>=0; i-- ) { if (!nc->fTransport.GetMember(i)->IsServer()) { nc->fTransport.RemoveMember(i); } } // for // update the members list from the msg. // this app is not one of the members in the msg for( i=0 ;iMemberListInfo()->GetNumMembers() ;i++ ) { plNetTransportMember* mbr = TRACKED_NEW plNetTransportMember(nc); IFillInTransportMember(m->MemberListInfo()->GetMember(i), mbr); hsLogEntry(nc->DebugMsg("\tAdding transport member, name=%s, p2p=%d, plrID=%d\n", mbr->AsStdString().c_str(), mbr->IsPeerToPeer(), mbr->GetPlayerID())); int idx=nc->fTransport.AddMember(mbr); hsAssert(idx>=0, "Failed adding member?"); } // for // new player has been aded send local MembersUpdate msg plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; mu->Send(); return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgMemberUpdate) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgMemberUpdate* m = plNetMsgMemberUpdate::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ if (m->AddingMember()) { plNetTransportMember* mbr=nil; int idx = nc->fTransport.FindMember(m->MemberInfo()->GetClientGuid()->GetPlayerID()); if ( idx>=0 ) mbr = nc->fTransport.GetMember(idx); else mbr = TRACKED_NEW plNetTransportMember(nc); hsAssert(mbr, "nil xport member"); IFillInTransportMember(m->MemberInfo(), mbr); if ( idx<0 ) { // didn't find him if ( nc->fTransport.AddMember(mbr)<0 ) delete mbr; // delete newly created member } } else { int idx=nc->fTransport.FindMember(m->MemberInfo()->GetClientGuid()->GetPlayerID()); if (idx<0) { hsLogEntry( nc->DebugMsg("\tCan't find member to remove.") ); } else { nc->fTransport.RemoveMember(idx); } } // new player has been aded send local MembersUpdate msg plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; mu->Send(); return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgListenListUpdate) { plNetClientMgr* nc = IGetNetClientMgr(); plNetMsgListenListUpdate* m = plNetMsgListenListUpdate::ConvertNoRef(netMsg); PeekMsg(m); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); */ int idx=nc->fTransport.FindMember(m->GetPlayerID()); plNetTransportMember* tm = (idx==-1 ? nil : nc->fTransport.GetMember(idx)); if(!tm) { #if 0 tm = TRACKED_NEW plNetTransportMember(nc); tm->SetClientNum(m->GetSenderClientNum()); int idx=nc->fTransport.AddMember(tm); hsAssert(idx>=0, "Failed adding member?"); nc->DebugMsg("ListenListUpdate msg: Adding member on the fly\n"); #endif return hsOK; } if (m->GetAdding()) { // add the sender to my talk list nc->GetTalkList()->AddMember(tm); } else { // remove the sender from my talk list nc->GetTalkList()->RemoveMember(tm); } return hsOK; } MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgInitialAgeStateSent) { plNetClientMgr * nc = IGetNetClientMgr(); plNetMsgInitialAgeStateSent* msg = plNetMsgInitialAgeStateSent::ConvertNoRef(netMsg); PeekMsg(msg); /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", netMsg->ClassName(), netMsg->AsStdString().c_str(), netMsg->GetNetCoreMsgLen()) ); */ nc->DebugMsg( "Initial age SDL count: %d", msg->GetNumInitialSDLStates( ) ); nc->SetRequiredNumInitialSDLStates( msg->GetNumInitialSDLStates() ); nc->SetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount, false ); if (nc->GetNumInitialSDLStates() >= nc->GetRequiredNumInitialSDLStates()) { nc->ICheckPendingStateLoad(hsTimer::GetSysSeconds()); nc->NotifyRcvdAllSDLStates(); } return hsOK; } //////////////////////////////////////////////////////////////////// // End.