/*==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==*/ ///////////////////////////////////////////////////////////////////////////////////////// // // INCLUDES // ///////////////////////////////////////////////////////////////////////////////////////// // singular #include "plCoopCoordinator.h" // local #include "plAvBrainCoop.h" #include "plAvatarMgr.h" #include "plAvTaskBrain.h" #include "plAvTaskSeek.h" // global #include "hsUtils.h" // other #include "plMessage/plAvCoopMsg.h" #include "plMessage/plAvatarMsg.h" #include "plMessage/plInputIfaceMgrMsg.h" #include "pnMessage/plNotifyMsg.h" #include "pnNetCommon/plNetApp.h" #include "plNetClient/plNetClientMgr.h" #include "plPhysical.h" #include "pnTimer/plTimerCallbackManager.h" #include "plMessage/plTimerCallbackMsg.h" const unsigned kAbortTimer = 1; const float kAbortTimerDuration = 15; // 15 seconds ///////////////////////////////////////////////////////////////////////////////////////// // // CONSTRUCTORS // ///////////////////////////////////////////////////////////////////////////////////////// plCoopCoordinator::plCoopCoordinator() : fHostBrain(nil), fGuestBrain(nil), fInitiatorID(0), fInitiatorSerial(0), fHostOfferStage(0), fGuestAcceptStage(0), fGuestAcceptMsg(nil), fAutoStartGuest(nil), fGuestAccepted(false) { } // plCoopCoordinator ---------------------------------------- // ------------------ plCoopCoordinator::plCoopCoordinator(plKey host, plKey guest, plAvBrainCoop *hostBrain, plAvBrainCoop *guestBrain, const char *synchBone, UInt32 hostOfferStage, UInt32 guestAcceptStage, plMessage *guestAcceptMsg, bool autoStartGuest) : fHostKey(host), fGuestKey(guest), fHostBrain(hostBrain), fGuestBrain(guestBrain), fInitiatorID(hostBrain->GetInitiatorID()), fInitiatorSerial(hostBrain->GetInitiatorSerial()), fHostOfferStage(hostOfferStage), fGuestAcceptStage(guestAcceptStage), fGuestAcceptMsg(guestAcceptMsg), fAutoStartGuest(autoStartGuest), fGuestAccepted(false), fGuestLinked(false) { const char * hostName = host->GetName(); const char * guestName = guest->GetName(); static int serial = 0; int len = strlen(hostName) + strlen(guestName) + 3 /* serial num */ + 1; char *newName = TRACKED_NEW char[len]; serial = serial % 999; sprintf(newName, "%s%s%3i\x000", hostName, guestName, serial++); plKey newKey = hsgResMgr::ResMgr()->NewKey(newName, this, host->GetUoid().GetLocation()); delete[] newName; fSynchBone = hsStrcpy(synchBone); plKey avMgrKey = plAvatarMgr::GetInstance()->GetKey(); guestBrain->SetRecipient(avMgrKey); hostBrain->SetRecipient(avMgrKey); // disable our clickability here if we are the guest if (plNetClientMgr::GetInstance()->GetLocalPlayerKey() == guest) { plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable); pMsg->SetAvKey(guest); pMsg->SetBCastFlag(plMessage::kNetPropagate); pMsg->SetBCastFlag(plMessage::kNetForce); pMsg->Send(); } } // plCoopCoordinator ------------------ // ------------------ plCoopCoordinator::~plCoopCoordinator() { delete[] fSynchBone; } ///////////////////////////////////////////////////////////////////////////////////////// // // CONSTRUCTORS // ///////////////////////////////////////////////////////////////////////////////////////// // MsgReceive -------------------------------------- // ----------- hsBool plCoopCoordinator::MsgReceive(plMessage *msg) { plNotifyMsg *notify = plNotifyMsg::ConvertNoRef(msg); if(notify) { proMultiStageEventData * mtevt = static_cast(notify->FindEventRecord(proEventData::kMultiStage)); if(mtevt) { int stageNum = mtevt->fStage; UInt32 stageState = mtevt->fEvent; plKey noteSender = notify->GetSender(); bool isFromHost = (noteSender == fHostKey); bool isFromGuest = (noteSender == fGuestKey); DebugMsg("COOP: Received multi-stage callback - stageNum = %d, stageState = %d, isFromHost = %d", stageNum, stageState, isFromHost ? 1 : 0); if(isFromHost) { if(!fGuestAccepted) { // we've just entered the host offer stage (i.e., the offer is ready) if(stageNum == fHostOfferStage && stageState == proEventData::kEnterStage) { if(fAutoStartGuest) { IStartGuest(); IStartTimeout(); } else { fHostBrain->EnableGuestClick(); } fGuestAccepted = true; } } } else if(isFromGuest) { if(stageNum == fGuestAcceptStage && stageState == proEventData::kEnterStage) { plKey localPlayer = plNetClientApp::GetInstance()->GetLocalPlayerKey(); // we only actually fire off the guest accept message if we're on the guest machine. // if it needs to be netpropped, the client can set that up when they set up the coop. if(fGuestAcceptMsg && localPlayer == fGuestKey) { fGuestAcceptMsg->Send(); } // kill the message (along with being active) fGuestAcceptMsg = nil; fGuestLinked = true; IAdvanceParticipant(true); // advance the host // IAdvanceParticipant(false); // advance the guest } } else { // not from host; not from guest // let's assume for the moment it's from a trigger. IStartHost(); } } } plAvCoopMsg *coop = plAvCoopMsg::ConvertNoRef(msg); if(coop) { DebugMsg("COOP: Received coop message: %d", coop->fCommand); switch(coop->fCommand) { case plAvCoopMsg::kGuestAccepted: IStartGuest(); IStartTimeout(); break; case plAvCoopMsg::kGuestSeeked: // if they did make it to their target, then continue IContinueGuest(); break; case plAvCoopMsg::kGuestSeekAbort: // if they aborted then just advance the host // kill the message (along with being active) fGuestAcceptMsg = nil; fGuestLinked = true; IAdvanceParticipant(true); // advance the host break; } } plAvTaskSeekDoneMsg *seekDone = plAvTaskSeekDoneMsg::ConvertNoRef(msg); if (seekDone) { DebugMsg("COOP: Received avatar seek finished msg: aborted = %d", seekDone->fAborted ? 1 : 0); if ( seekDone->fAborted ) { plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestSeekAbort,fInitiatorID,(UInt16)fInitiatorSerial); coopM->SetBCastFlag(plMessage::kNetPropagate); coopM->SetBCastFlag(plMessage::kNetForce); coopM->AddReceiver(GetKey()); coopM->Send(); } else { plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestSeeked,fInitiatorID,(UInt16)fInitiatorSerial); coopM->SetBCastFlag(plMessage::kNetPropagate); coopM->SetBCastFlag(plMessage::kNetForce); coopM->AddReceiver(GetKey()); coopM->Send(); } } plTimerCallbackMsg* timerMsg = plTimerCallbackMsg::ConvertNoRef(msg); if (timerMsg) { if (timerMsg->fID == kAbortTimer && !fGuestLinked) ITimeout(); } return false; } // Run ---------------------- // ---- void plCoopCoordinator::Run() { IStartHost(); } bool plCoopCoordinator::IsActiveForReal() { return fGuestAcceptMsg ? true : false; } // GetInitiatorID ------------------------ // --------------- UInt32 plCoopCoordinator::GetInitiatorID() { return fInitiatorID; } // GetInitiatorSerial ------------------------ UInt16 plCoopCoordinator::GetInitiatorSerial() { return (UInt16)fInitiatorSerial; } // IStartHost ---------------------- // ----------- void plCoopCoordinator::IStartHost() { DebugMsg("COOP: IStartHost()"); plArmatureMod *guestAv = plAvatarMgr::FindAvatar(fGuestKey); plArmatureMod *hostAv = plAvatarMgr::FindAvatar(fHostKey); if (guestAv && hostAv) { plAvSeekMsg *msg = TRACKED_NEW plAvSeekMsg(nil, hostAv->GetKey(), nil, 1.f, true); hsClearBits(msg->fFlags, plAvSeekMsg::kSeekFlagForce3rdPersonOnStart); guestAv->GetPositionAndRotationSim(&msg->fTargetLookAt, nil); hostAv->GetPositionAndRotationSim(&msg->fTargetPos, nil); msg->Send(); } // now tell the host to initiate the thing. plAvTaskBrain *brainT = TRACKED_NEW plAvTaskBrain(fHostBrain); plAvTaskMsg *brainM = TRACKED_NEW plAvTaskMsg(GetKey(), fHostKey, brainT); brainM->SetBCastFlag(plMessage::kPropagateToModifiers); brainM->Send(); } // IStartGuest ---------------------- // ------------ void plCoopCoordinator::IStartGuest() { DebugMsg("COOP: IStartGuest()"); plSceneObject *avSO = plSceneObject::ConvertNoRef(fHostKey->ObjectIsLoaded()); if ( !avSO ) return; const plArmatureMod *hostAv = (plArmatureMod*)avSO->GetModifierByType(plArmatureMod::Index()); if ( hostAv ) { const plSceneObject *targetBone = hostAv->FindBone(fSynchBone); if(targetBone) { plAvSeekMsg *seekMsg = TRACKED_NEW plAvSeekMsg( nil, nil,targetBone->GetKey(), 0, true, kAlignHandle, nil, false, plAvSeekMsg::kSeekFlagNoWarpOnTimeout, GetKey()); plAvTaskSeek *seekT = TRACKED_NEW plAvTaskSeek(seekMsg); plAvTaskMsg *seekM = TRACKED_NEW plAvTaskMsg(GetKey(), fGuestKey, seekT); seekM->SetBCastFlag(plMessage::kPropagateToModifiers); seekM->Send(); } } } // IContinueGuest ---------------------- // ------------ void plCoopCoordinator::IContinueGuest() { DebugMsg("COOP: IContinueGuest()"); plAvTaskBrain *brainT = TRACKED_NEW plAvTaskBrain(fGuestBrain); plAvTaskMsg *brainM = TRACKED_NEW plAvTaskMsg(GetKey(), fGuestKey, brainT); brainM->SetBCastFlag(plMessage::kPropagateToModifiers); brainM->Send(); fGuestBrain = nil; // the armature will destroy the brain when done. } // IContinueHost ---------------------- // -------------- void plCoopCoordinator::IAdvanceParticipant(bool host) { DebugMsg("COOP: IAdvanceParticipant(%d)", host ? 1 : 0); plKey &who = host ? fHostKey : fGuestKey; plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg(nil, who, plAvBrainGenericMsg::kNextStage, 0, false, 0.0, false, false, 0.0); pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); pMsg->Send(); } // IStartTimeout ---------------------- // -------------- void plCoopCoordinator::IStartTimeout() { plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey(), kAbortTimer); plgTimerCallbackMgr::NewTimer(kAbortTimerDuration, timerMsg); } // ITimeout --------------------------- // -------------- void plCoopCoordinator::ITimeout() { fGuestAcceptMsg = nil; IAdvanceParticipant(true); // advance the host } // Read ------------------------------------------------------------- // ----- void plCoopCoordinator::Read(hsStream *stream, hsResMgr *mgr) { fHostKey = mgr->ReadKey(stream); fGuestKey = mgr->ReadKey(stream); fHostBrain = plAvBrainCoop::ConvertNoRef(mgr->ReadCreatable(stream)); fGuestBrain = plAvBrainCoop::ConvertNoRef(mgr->ReadCreatable(stream)); fHostOfferStage = stream->ReadByte(); fGuestAcceptStage = stream->ReadBool(); if(stream->Readbool()) fGuestAcceptMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); else fGuestAcceptMsg = nil; fSynchBone = stream->ReadSafeString(); fAutoStartGuest = stream->Readbool(); fInitiatorID = fHostBrain->GetInitiatorID(); fInitiatorSerial = fHostBrain->GetInitiatorSerial(); } // Write ------------------------------------------------------------- // ------ void plCoopCoordinator::Write(hsStream *stream, hsResMgr *mgr) { mgr->WriteKey(stream, fHostKey); mgr->WriteKey(stream, fGuestKey); mgr->WriteCreatable(stream, fHostBrain); mgr->WriteCreatable(stream, fGuestBrain); stream->WriteByte((UInt8)fHostOfferStage); stream->WriteByte((UInt8)fGuestAcceptStage); stream->Writebool(fGuestAcceptMsg != nil); if(fGuestAcceptMsg) mgr->WriteCreatable(stream, fGuestAcceptMsg); stream->WriteSafeString(fSynchBone); stream->Writebool(fAutoStartGuest); }