/*==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 #include "hsMatrix44.h" #include "hsGeometry3.h" #include "plNetClientMgr.h" #include "../plNetMessage/plNetMessage.h" #include "../pnNetCommon/plNetServers.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnKeyedObject/plKey.h" #include "../plNetTransport/plNetTransportMember.h" #include "../plMessage/plMemberUpdateMsg.h" #include "../plMessage/plNetVoiceListMsg.h" #include "../plStatusLog/plStatusLog.h" #include "../plVault/plVault.h" #define SAME_TALK_AND_LISTEN struct DistSqInfo { plNetTransportMember* fMbr; float fDistSq; DistSqInfo(plNetTransportMember* m, float d) : fMbr(m),fDistSq(d) {} }; bool lessComp(DistSqInfo a, DistSqInfo b) { return (a.fDistSq& newListenList, hsBool forceSynch) { hsBool changed=false; int i; // see if new listen list differs from current one if (forceSynch || newListenList.size() != GetListenList()->GetNumMembers()) changed=true; else { for(i=0;iFindMember(newListenList[i].fMbr)==-1) { changed=true; break; } } } // set as new listen list if (changed) { DebugMsg("ListenList changed, forceSynch=%d\n", forceSynch); plNetMsgListenListUpdate llu; llu.SetPlayerID(GetPlayerID()); llu.SetNetProtocol(kNetProtocolCli2Game); // // for each client in the old list, if not in the new list, send a ListenList remove msg // llu.Receivers()->Clear(); llu.SetAdding(false); for(i=0;iGetNumMembers(); i++) { hsBool found=false; if (!forceSynch) { int j; for(j=0;jGetMember(i)) { found=true; break; } } } if (found==false) { llu.Receivers()->AddReceiverPlayerID(GetListenList()->GetMember(i)->GetPlayerID()); } } #ifndef SAME_TALK_AND_LISTEN if (llu.Receivers()->GetNumReceivers()) { // DEBUGGING int i; for(i=0;iGetNumReceivers(); i++) { int idx=fTransport.FindMember(llu.Receivers()->GetReceiverClientNum(i)); plNetTransportMember* mbr=fTransport.GetMember(idx); DebugMsg(" ListenListUpdate msg, adding=%d\n", mbr->AsStdString().c_str(), llu.GetAdding()); } SendMsg(&llu); } #endif // // for each client in the new list, [if not in the old list,] send a ListenList add msg // llu.Receivers()->Clear(); llu.SetAdding(true); for(i=0;iFindMember(newListenList[i].fMbr)==-1) { // if not in the old list, send a ListenList add msg llu.Receivers()->AddReceiverPlayerID(newListenList[i].fMbr->GetPlayerID()); } } #ifndef SAME_TALK_AND_LISTEN if (llu.Receivers()->GetNumReceivers()) { // DEBUGGING int i; for(i=0;iGetNumReceivers(); i++) { int cNum=llu.Receivers()->GetReceiverClientNum(i); int idx=fTransport.FindMember(cNum); plNetTransportMember* mbr=fTransport.GetMember(idx); DebugMsg(" ListenListUpdate msg, adding=%d, cNum=%d\n", mbr->AsStdString().c_str(), llu.GetAdding(), cNum); } SendMsg(&llu); } #endif // // set as new listen list // GetListenList()->Clear(); #ifdef HS_DEBUGGING DebugMsg("New ListenList, size=%d\n", newListenList.size()); #endif for(i=0;iAddMember(newListenList[i].fMbr); #ifdef HS_DEBUGGING DebugMsg("\tLL Member %d, name=%s, cNum=%d, dist=%f\n", i, newListenList[i].fMbr->AsStdString().c_str(), newListenList[i].fMbr->GetPlayerID(), newListenList[i].fDistSq); #endif } } return changed; } // // Periodically updates the list of what remote players I'm listening to. // Used to filter voice streams. // Returns true if the listenList was changed. // Note: Updates distSq to each member. Other things rely on this so we must do it even if p2p is disabled. // hsBool plNetClientMgr::IUpdateListenList(double secs) { if (GetFlagsBit(kDisabled)) return false; if (!fLocalPlayerKey || !fLocalPlayerKey->ObjectIsLoaded()) return false; hsBool changed = false; if (secs - GetListenList()->GetLastUpdateTime()>plNetListenList::kUpdateInterval) { GetListenList()->SetLastUpdateTime(secs); std::vector newListenList; switch (fListenListMode) { case kListenList_Forced: { #ifdef SAME_TALK_AND_LISTEN SynchTalkList(); #endif } return true; case kListenList_Distance: { // Finds the 3 closest players to our local player // Search is unoptimized for now... // compute our players pos plSceneObject* locPlayer = plSceneObject::ConvertNoRef(fLocalPlayerKey->ObjectIsLoaded()); hsAssert(locPlayer, "local player is not a sceneObject?"); hsAssert(locPlayer->GetCoordinateInterface(), "locPlayer has no coordInterface"); hsMatrix44 l2w=locPlayer->GetCoordinateInterface()->GetLocalToWorld(); hsPoint3 locPlayerPos=l2w.GetTranslate(); int i; for(i=0;iSetDistSq(hsScalarMax); if (fTransport.GetMember(i)->IsServer()) continue; if(VaultAmIgnoringPlayer(fTransport.GetMember(i)->GetPlayerID())) { continue; } plKey k=fTransport.GetMember(i)->GetAvatarKey(); #if 0 if (!k) { DebugMsg("UpdateListenList: Nil avatar key on member %s\n", fTransport.GetMember(i)->AsStdString().c_str()); } #endif plSceneObject* obj=plSceneObject::ConvertNoRef(k ? k->ObjectIsLoaded() : nil); if (obj && obj->GetCoordinateInterface()) { #if 1 // compute distSq to me l2w=obj->GetCoordinateInterface()->GetLocalToWorld(); hsPoint3 pos=l2w.GetTranslate(); float distSq = hsVector3(&pos, &locPlayerPos).MagnitudeSquared(); fTransport.GetMember(i)->SetDistSq(distSq); // I can't listen to players that are more than 50 ft away if (distSq>plNetListenList::kMaxListenDistSq) continue; // if we are p2p and member isn't, skip them. if ( IsPeerToPeer() && !fTransport.GetMember(i)->IsPeerToPeer() ) continue; // otherwise, we aren't p2p so just update the listen list // normally so it will update in the gui as distance changes. #else float distSq=1; #endif // if we have < 3 elements in the list, grow the list, or, // if obj is closer than item 3, add it to the list. // keep the list (3) elements sorted. if (plNetListenList::kMaxListenListSize==-1 || newListenList.size()CheckForceSynch() #endif ); } // update talkList based on listenList if (changed) { #ifdef SAME_TALK_AND_LISTEN SynchTalkList(); #endif // notify KI, member distances have been updated plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; mu->Send(); } return changed; } void plNetClientMgr::SynchTalkList() { GetTalkList()->Clear(); int i; for(i=0;iGetNumMembers(); i++) GetTalkList()->AddMember(GetListenList()->GetMember(i)); } void plNetClientMgr::SetListenListMode(int i) { // set new mode, clear list and force update fListenListMode = i; GetListenList()->Clear(); GetListenList()->SetLastUpdateTime(0.f); } void plNetClientMgr::IHandleNetVoiceListMsg(plNetVoiceListMsg* msg) { if (msg->GetCmd() == plNetVoiceListMsg::kForcedListenerMode) { // first make sure this message applies to us: int i; bool included = false; for (i = 0; i < msg->GetClientList()->Count(); i++) { if (msg->GetClientList()->AcquireArray()[i] == NetCommGetPlayer()->playerInt) { included = true; break; } } if (!included) return; SetListenListMode(kListenList_Forced); // add in the members we receive from python for (i = 0; i < msg->GetClientList()->Count(); i++) { plNetTransportMember **members = nil; plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); if( members != nil) { for(int j= 0; j < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); j++ ) { plNetTransportMember *mbr = members[ j ]; if( mbr != nil && mbr->GetAvatarKey() != nil && mbr->GetPlayerID() == msg->GetClientList()->AcquireArray()[i]) { plNetClientMgr::GetInstance()->GetListenList()->AddMember(mbr); } } } delete [] members; } } else if (msg->GetCmd() == plNetVoiceListMsg::kDistanceMode) { // again, see that it is us that we care about: if (msg->GetRemovedKey() == GetLocalPlayerKey()) SetListenListMode(kListenList_Distance); } }