/*==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 "plNetLinkingMgr.h" #include "plNetClientMgr.h" #include "plNetCliAgeJoiner.h" #include "plNetCliAgeLeaver.h" #include "../plNetTransport/plNetTransportMember.h" // OfferLinkToPlayer() #include "plgDispatch.h" #include "../pnMessage/plTimeMsg.h" #include "../plMessage/plLinkToAgeMsg.h" #include "../pnKeyedObject/plKey.h" #include "../pnKeyedObject/plUoid.h" #include "../pnKeyedObject/hsKeyedObject.h" #include "../pnSceneObject/plSceneObject.h" #include "../plNetCommon/plNetCommon.h" #include "../plVault/plVault.h" #include "../pnNetCommon/pnNetCommon.h" #include "../plMessage/plVaultNotifyMsg.h" #include "../plNetMessage/plNetMessage.h" #include "../plAvatar/plAvatarMgr.h" #include "../plAvatar/plArmatureMod.h" #include "../plFile/hsFiles.h" #include "../plMessage/plNCAgeJoinerMsg.h" #include "../plVault/plVault.h" /***************************************************************************** * * Private * ***/ struct NlmOpNode; enum ENlmOp { kNlmOpNoOp, kNlmOpWaitOp, kNlmOpJoinAgeOp, kNlmOpLeaveAgeOp, kNumNlmOps }; struct NlmOp { ENlmOp opcode; NlmOpNode * node; NlmOp (const ENlmOp & op) : opcode(op) { } }; struct NlmNoOpOp : NlmOp { NlmNoOpOp () : NlmOp(kNlmOpNoOp) { } }; struct NlmOpWaitOp : NlmOp { NlmOpWaitOp () : NlmOp(kNlmOpWaitOp) { } }; struct NlmJoinAgeOp : NlmOp { NetCommAge age; NlmJoinAgeOp () : NlmOp(kNlmOpJoinAgeOp) { } }; struct NlmLeaveAgeOp : NlmOp { bool quitting; NlmLeaveAgeOp () : NlmOp(kNlmOpLeaveAgeOp) { } }; struct NlmOpNode { LINK(NlmOpNode) link; NlmOp * op; ~NlmOpNode () { DEL(op); } }; /***************************************************************************** * * Private data * ***/ static plNCAgeJoiner * s_ageJoiner; static plNCAgeLeaver * s_ageLeaver; static LISTDECL(NlmOpNode, link) s_oplist; /***************************************************************************** * * Local functions * ***/ //============================================================================ static void QueueOp (NlmOp * op, bool front = false) { NlmOpNode * node = NEWZERO(NlmOpNode); node->op = op; op->node = node; s_oplist.Link(node, front ? kListHead : kListTail); } /***************************************************************************** * * plNetLinkingMgr * ***/ //============================================================================ void plNetLinkingMgr::NCAgeJoinerCallback ( plNCAgeJoiner * joiner, unsigned type, void * notify, void * userState ) { plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); switch (type) { case kAgeJoinerComplete: { ASSERT(joiner == s_ageJoiner); s_ageJoiner = nil; lm->IPostProcessLink(); lm->fLinkedIn = true; lm->SetEnabled(true); // Pull our wait op off exec queue if (NlmOpWaitOp * waitOp = (NlmOpWaitOp *) userState) DEL(waitOp->node); } break; DEFAULT_FATAL(type); } } //============================================================================ void plNetLinkingMgr::NCAgeLeaverCallback ( plNCAgeLeaver * leaver, unsigned type, void * notify, void * userState ) { switch (type) { case kAgeLeaveComplete: { ASSERT(leaver == s_ageLeaver); s_ageLeaver = nil; // Pull our wait op off exec queue if (NlmOpWaitOp * waitOp = (NlmOpWaitOp *) userState) DEL(waitOp->node); } break; DEFAULT_FATAL(type); } } //============================================================================ void plNetLinkingMgr::ExecNextOp () { plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); NlmOpNode * opNode = s_oplist.Head(); if (!opNode) return; switch (opNode->op->opcode) { case kNlmOpNoOp: { } break; case kNlmOpWaitOp: { } return; // don't allow wait op to be unlinked/deleted from list case kNlmOpJoinAgeOp: { ASSERT(!s_ageJoiner); ASSERT(!s_ageLeaver); // Insert a wait operation into the exec queue NlmOpWaitOp * waitOp = NEWZERO(NlmOpWaitOp); QueueOp(waitOp, true); NlmJoinAgeOp * joinAgeOp = (NlmJoinAgeOp *) opNode->op; NCAgeJoinerCreate( &s_ageJoiner, joinAgeOp->age, NCAgeJoinerCallback, waitOp ); } break; case kNlmOpLeaveAgeOp: { ASSERT(!s_ageJoiner); ASSERT(!s_ageLeaver); // Insert a wait operation into the exec queue NlmOpWaitOp * waitOp = NEWZERO(NlmOpWaitOp); QueueOp(waitOp, true); lm->SetEnabled(false); lm->fLinkedIn = false; NlmLeaveAgeOp * leaveAgeOp = (NlmLeaveAgeOp *) opNode->op; NCAgeLeaverCreate( &s_ageLeaver, leaveAgeOp->quitting, NCAgeLeaverCallback, waitOp ); } break; } DEL(opNode); } //////////////////////////////////////////////////////////////////// plNetLinkingMgr::plNetLinkingMgr() : fLinkingEnabled(true) , fLinkedIn (false) { } plNetLinkingMgr * plNetLinkingMgr::GetInstance() { static plNetLinkingMgr Instance; return &Instance; } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::SetEnabled( bool b ) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: %s -> %s", fLinkingEnabled?"Enabled":"Disabled",b?"Enabled":"Disabled" ) ); fLinkingEnabled = b; } //////////////////////////////////////////////////////////////////// // static std::string plNetLinkingMgr::GetProperAgeName( const char * ageName ) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); hsFolderIterator it("dat"PATH_SEPARATOR_STR"*.age", true); while ( it.NextFile() ) { std::string work = it.GetFileName(); work.erase( work.find( ".age" ) ); if ( stricmp( ageName, work.c_str() )==0 ) return work; } return ageName; } //////////////////////////////////////////////////////////////////// hsBool plNetLinkingMgr::MsgReceive( plMessage *msg ) { if (s_ageLeaver && NCAgeLeaverMsgReceive(s_ageLeaver, msg)) return true; if (s_ageJoiner && NCAgeJoinerMsgReceive(s_ageJoiner, msg)) return true; if (plLinkToAgeMsg * pLinkMsg = plLinkToAgeMsg::ConvertNoRef(msg)) { if (!fLinkingEnabled) hsLogEntry(plNetClientMgr::GetInstance()->DebugMsg("Not linking. Linking is disabled.")); else IProcessLinkToAgeMsg(pLinkMsg); return true; } if (plLinkingMgrMsg * pLinkingMgrMsg = plLinkingMgrMsg::ConvertNoRef(msg)) { IProcessLinkingMgrMsg( pLinkingMgrMsg ); return true; } return false; } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::Update() { if (s_ageLeaver) NCAgeLeaverUpdate(s_ageLeaver); if (s_ageJoiner) NCAgeJoinerUpdate(s_ageJoiner); ExecNextOp(); } //////////////////////////////////////////////////////////////////// bool plNetLinkingMgr::IProcessLinkToAgeMsg( plLinkToAgeMsg * msg ) { if (!fLinkingEnabled) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return false; } plNetClientMgr * nc = plNetClientMgr::GetInstance(); bool result = true; plAgeLinkStruct save; save.CopyFrom( GetPrevAgeLink() ); GetPrevAgeLink()->CopyFrom( GetAgeLink() ); GetAgeLink()->CopyFrom( msg->GetAgeLink() ); if ( IPreProcessLink() ) { GetAgeLink()->SetSpawnPoint(msg->GetAgeLink()->SpawnPoint()); if (fLinkedIn) { // Set the link out animation we should use if (plSceneObject *localSO = plSceneObject::ConvertNoRef(nc->GetLocalPlayer())) { plArmatureMod *avMod = const_cast(plArmatureMod::ConvertNoRef(localSO->GetModifierByType(plArmatureMod::Index()))); avMod->SetLinkInAnim(msg->GetLinkInAnimName()); } // Queue leave op NlmLeaveAgeOp * leaveAgeOp = NEWZERO(NlmLeaveAgeOp); QueueOp(leaveAgeOp); } // Queue join op NlmJoinAgeOp * joinAgeOp = NEWZERO(NlmJoinAgeOp); joinAgeOp->age.ageInstId = (Uuid) *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); StrCopy( joinAgeOp->age.ageDatasetName, GetAgeLink()->GetAgeInfo()->GetAgeFilename(), arrsize(joinAgeOp->age.ageDatasetName) ); StrCopy( joinAgeOp->age.spawnPtName, GetAgeLink()->SpawnPoint().GetName(), arrsize(joinAgeOp->age.spawnPtName) ); QueueOp(joinAgeOp); } else { hsLogEntry( nc->ErrorMsg( "IPreProcessLink failed. Not linking." ) ); // Restore previous age info state. GetAgeLink()->CopyFrom( GetPrevAgeLink() ); GetPrevAgeLink()->CopyFrom( &save ); result = false; } return result; } //////////////////////////////////////////////////////////////////// bool plNetLinkingMgr::IProcessLinkingMgrMsg( plLinkingMgrMsg * msg ) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); bool result = true; switch ( msg->GetCmd() ) { ///////////////////// case kLinkPlayerHere: { // player wants to link to our age UInt32 playerID = msg->GetArgs()->GetInt( 0 ); hsLogEntry( nc->DebugMsg( "Linking player %lu to this age.", playerID ) ); LinkPlayerHere( playerID ); } break; ///////////////////// case kLinkPlayerToPrevAge: { // link myself back to my last age hsLogEntry( nc->DebugMsg( "Linking back to my last age.") ); LinkToPrevAge(); } break; ///////////////////// case kOfferLinkToPlayer: { // // Notify the KI that we received an offer. // plVaultNotifyMsg * notify = TRACKED_NEW plVaultNotifyMsg(); // notify->SetType( plVaultNotifyMsg::kPlayerOfferedLink ); // notify->GetArgs()->AddItem( 0, msg->GetArgs()->GetItem( 0 ), true ); // add to notify and have notify take over memory management of the item. // msg->GetArgs()->RemoveItem( 0, true ); // msg to stop memory managing item, notify msg will delete it. // notify->Send(); plAgeLinkStruct *myLink = plAgeLinkStruct::ConvertNoRef(msg->GetArgs()->GetItem( 0 )); LinkToAge(myLink); } break; ///////////////////// default: hsAssert( false, "IProcessLinkingMgrMsg: Unknown linking mgr cmd." ); result = false; break; } return result; } //////////////////////////////////////////////////////////////////// bool plNetLinkingMgr::IDispatchMsg( plMessage * msg, UInt32 playerID ) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); msg->AddReceiver( plNetClientMgr::GetInstance()->GetKey() ); if ( playerID!=kInvalidPlayerID && playerID!=nc->GetPlayerID() ) { msg->SetBCastFlag( plMessage::kNetAllowInterAge ); msg->SetBCastFlag( plMessage::kNetPropagate ); msg->SetBCastFlag( plMessage::kNetForce ); msg->SetBCastFlag( plMessage::kLocalPropagate, 0 ); // send msg to other player (maybe in different age than us) msg->AddNetReceiver( playerID ); } return ( msg->Send()!=0 ); } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::LinkToAge( plAgeLinkStruct * link, UInt32 playerID ) { LinkToAge(link, nil, playerID); } void plNetLinkingMgr::LinkToAge( plAgeLinkStruct * link, const char* linkAnim, UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( link ); if (linkAnim) pMsg->SetLinkInAnimName(linkAnim); IDispatchMsg( pMsg, playerID ); } // link myself back to my last age void plNetLinkingMgr::LinkToPrevAge( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } if (GetPrevAgeLink()->GetAgeInfo()->HasAgeFilename()) { plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( GetPrevAgeLink() ); IDispatchMsg( pMsg, playerID ); } else { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. No prev age fileName." ) ); } } void plNetLinkingMgr::LinkToMyPersonalAge( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } plNetClientMgr * nc = plNetClientMgr::GetInstance(); plAgeLinkStruct link; link.GetAgeInfo()->SetAgeFilename( kPersonalAgeFilename ); link.SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); plSpawnPointInfo hutSpawnPoint; hutSpawnPoint.SetName(kPersonalAgeLinkInPointCloset); link.SetSpawnPoint(hutSpawnPoint); plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); IDispatchMsg( pMsg, playerID ); } void plNetLinkingMgr::LinkToMyNeighborhoodAge( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } plNetClientMgr * nc = plNetClientMgr::GetInstance(); plAgeLinkStruct link; if (!VaultGetLinkToMyNeighborhood(&link)) return; link.SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); IDispatchMsg( pMsg, playerID ); } void plNetLinkingMgr::LinkPlayerHere( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } plNetClientMgr * nc = plNetClientMgr::GetInstance(); // send the player our current age info so they can link here. plAgeLinkStruct link; link.GetAgeInfo()->CopyFrom( GetAgeLink()->GetAgeInfo() ); LinkPlayerToAge( &link, playerID ); } void plNetLinkingMgr::LinkPlayerToAge( plAgeLinkStruct * link, UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } plNetClientMgr * nc = plNetClientMgr::GetInstance(); // send the player the age link so they can link there. link->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( link ); IDispatchMsg( pMsg, playerID ); } // // link the player back to his previous age // void plNetLinkingMgr::LinkPlayerToPrevAge( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } // Send the player a msg telling them to link to their last age plNetClientMgr * nc = plNetClientMgr::GetInstance(); plLinkingMgrMsg* pMsg = TRACKED_NEW plLinkingMgrMsg(); pMsg->SetCmd( kLinkPlayerToPrevAge); IDispatchMsg( pMsg, playerID ); } void plNetLinkingMgr::LinkToPlayersAge( UInt32 playerID ) { if ( !fLinkingEnabled ) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); return; } // Send the player a msg telling them to send us a msg to link to them. isn't that fun? :) plNetClientMgr * nc = plNetClientMgr::GetInstance(); plLinkingMgrMsg* pMsg = TRACKED_NEW plLinkingMgrMsg(); pMsg->SetCmd( kLinkPlayerHere ); pMsg->GetArgs()->AddInt( 0, NetCommGetPlayer()->playerInt ); // send them our id. IDispatchMsg( pMsg, playerID ); } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::OfferLinkToPlayer( const plAgeLinkStruct * inInfo, UInt32 playerID ) { plNetClientMgr *mgr = plNetClientMgr::GetInstance(); plLinkToAgeMsg * linkM = TRACKED_NEW plLinkToAgeMsg(inInfo); linkM->AddReceiver(mgr->GetKey()); plKey host = mgr->GetLocalPlayerKey(); plNetTransport &transport = mgr->TransportMgr(); int guestIdx = transport.FindMember(playerID); plNetTransportMember *guestMem = transport.GetMember(guestIdx); // -1 ? if(guestMem) { plKey guest = guestMem->GetAvatarKey(); plAvatarMgr::OfferLinkingBook(host, guest, linkM, host); } } // my special version - cjp void plNetLinkingMgr::OfferLinkToPlayer( const plAgeLinkStruct * inInfo, UInt32 playerID, plKey replyKey ) { plNetClientMgr *mgr = plNetClientMgr::GetInstance(); plLinkToAgeMsg * linkM = TRACKED_NEW plLinkToAgeMsg(inInfo); linkM->AddReceiver(mgr->GetKey()); plKey host = mgr->GetLocalPlayerKey(); plNetTransport &transport = mgr->TransportMgr(); int guestIdx = transport.FindMember(playerID); plNetTransportMember *guestMem = transport.GetMember(guestIdx); // -1 ? if(guestMem) { plKey guest = guestMem->GetAvatarKey(); plAvatarMgr::OfferLinkingBook(host, guest, linkM, replyKey); } } // for backwards compatibility void plNetLinkingMgr::OfferLinkToPlayer( const plAgeInfoStruct * inInfo, UInt32 playerID ) { plAgeLinkStruct *ageLink = TRACKED_NEW plAgeLinkStruct; ageLink->GetAgeInfo()->CopyFrom(inInfo); ageLink->SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); OfferLinkToPlayer(ageLink, playerID); } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::IPostProcessLink( void ) { bool city = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kCityAgeFilename )); bool hood = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNeighborhoodAgeFilename )); bool psnl = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kPersonalAgeFilename )); // Update our online status if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); wchar ageInstName[MAX_PATH]; Uuid ageInstGuid = *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); StrToUnicode(ageInstName, GetAgeLink()->GetAgeInfo()->GetAgeInstanceName(), arrsize(ageInstName)); accInfo.SetAgeInstName(ageInstName); accInfo.SetAgeInstUuid(ageInstGuid); accInfo.SetOnline(true); rvnInfo->DecRef(); } switch (GetAgeLink()->GetLinkingRules()) { case plNetCommon::LinkingRules::kOwnedBook: { // SPECIAL CASE: City: Every player ever created would be in the list; avoid that. if (city) break; { // Ensure we're in the AgeOwners folder RelVaultNode * fldr = VaultGetAgeAgeOwnersFolderIncRef(); RelVaultNode * info = VaultGetPlayerInfoNodeIncRef(); if (fldr && info) if (!fldr->IsParentOf(info->nodeId, 1)) VaultAddChildNodeAndWait( fldr->nodeId, info->nodeId, NetCommGetPlayer()->playerInt ); if (fldr) fldr->DecRef(); if (info) info->DecRef(); } } break; case plNetCommon::LinkingRules::kVisitBook: { // SPECIAL CASE: City: Every player ever created would be in the list; avoid that. if (city) break; { // Ensure we're in the CanVisit folder RelVaultNode * fldr = VaultGetAgeCanVisitFolderIncRef(); RelVaultNode * info = VaultGetPlayerInfoNodeIncRef(); if (fldr && info) if (!fldr->IsParentOf(info->nodeId, 1)) VaultAddChildNodeAndWait( fldr->nodeId, info->nodeId, NetCommGetPlayer()->playerInt ); if (fldr) fldr->DecRef(); if (info) info->DecRef(); } } break; case plNetCommon::LinkingRules::kSubAgeBook: { // Register the previous age as a sub age of the current one so that we can link back to that instance plAgeLinkStruct subAgeLink; VaultAgeFindOrCreateSubAgeLinkAndWait(GetPrevAgeLink()->GetAgeInfo(), &subAgeLink, NetCommGetAge()->ageInstId); } break; } } //////////////////////////////////////////////////////////////////// bool plNetLinkingMgr::IPreProcessLink( void ) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); bool success = true; if ( nc->GetFlagsBit( plNetClientMgr::kNullSend ) ) { hsLogEntry( nc->DebugMsg( "NetClientMgr nullsend. Not linking." ) ); return false; } #if 0 // Appear offline until we're done linking if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); accInfo.SetAgeInstName(nil); accInfo.SetAgeInstUuid(kNilGuid); accInfo.SetOnline(false); rvnInfo->DecRef(); } #else // Update our online status if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); wchar ageInstName[MAX_PATH]; Uuid ageInstGuid = *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); StrToUnicode(ageInstName, GetAgeLink()->GetAgeInfo()->GetAgeInstanceName(), arrsize(ageInstName)); accInfo.SetAgeInstName(ageInstName); accInfo.SetAgeInstUuid(ageInstGuid); accInfo.SetOnline(true); rvnInfo->DecRef(); } #endif //------------------------------------------------------------------------ // Fixup empty fields if ( GetAgeLink()->GetAgeInfo()->HasAgeFilename() ) { GetAgeLink()->GetAgeInfo()->SetAgeFilename( plNetLinkingMgr::GetProperAgeName( GetAgeLink()->GetAgeInfo()->GetAgeFilename() ).c_str() ); if ( !GetAgeLink()->GetAgeInfo()->HasAgeInstanceName() ) { GetAgeLink()->GetAgeInfo()->SetAgeInstanceName( GetAgeLink()->GetAgeInfo()->GetAgeFilename() ); } } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Pre-Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); //------------------------------------------------------------------------ // SPECIAL CASE: StartUp: force basic link if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kStartUpAgeFilename )==0 ) { GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); } //------------------------------------------------------------------------ // SPECIAL CASE: Nexus: force original link if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNexusAgeFilename )==0 ) { GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); } //------------------------------------------------------------------------ // SPECIAL CASE: ACA: force original link if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kAvCustomizationFilename )==0 ) { GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); switch ( GetAgeLink()->GetLinkingRules() ) { //-------------------------------------------------------------------- // BASIC LINK. Link to a unique instance of the age, if no instance specified. case plNetCommon::LinkingRules::kBasicLink: if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); break; //-------------------------------------------------------------------- // ORIGINAL BOOK. Become an owner of the age, if not already one. case plNetCommon::LinkingRules::kOriginalBook: { // create a new ageinfo struct with the filename of the age because // we just want to find out if we own *any* link to the age, not the specific // link that we're linking through plAgeInfoStruct ageInfo; ageInfo.SetAgeFilename(GetAgeLink()->GetAgeInfo()->GetAgeFilename()); plAgeLinkStruct ownedLink; if (!VaultGetOwnedAgeLink(&ageInfo, &ownedLink)) { // Fill in fields for new age create. if ( !GetAgeLink()->GetAgeInfo()->HasAgeUserDefinedName() ) { // set user-defined name std::string title; unsigned nameLen = StrLen(nc->GetPlayerName()); if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') xtl::format( title, "%s'", nc->GetPlayerName() ); else xtl::format( title, "%s's", nc->GetPlayerName() ); GetAgeLink()->GetAgeInfo()->SetAgeUserDefinedName( title.c_str() ); } if ( !GetAgeLink()->GetAgeInfo()->HasAgeDescription() ) { // set description std::string desc; unsigned nameLen = StrLen(nc->GetPlayerName()); if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') xtl::format( desc, "%s' %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); else xtl::format( desc, "%s's %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); GetAgeLink()->GetAgeInfo()->SetAgeDescription( desc.c_str() ); } if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) { GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); } // register this as an owned age now before we link to it. VaultRegisterOwnedAgeAndWait(GetAgeLink()); } else if (RelVaultNode * linkNode = VaultGetOwnedAgeLinkIncRef(&ageInfo)) { // We have the age in our AgesIOwnFolder. If its volatile, dump it for the new one. VaultAgeLinkNode linkAcc(linkNode); if (linkAcc.volat) { if (VaultUnregisterOwnedAgeAndWait(&ageInfo)) { // Fill in fields for new age create. if ( !GetAgeLink()->GetAgeInfo()->HasAgeUserDefinedName() ) { // set user-defined name std::string title; unsigned nameLen = StrLen(nc->GetPlayerName()); if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') xtl::format( title, "%s'", nc->GetPlayerName() ); else xtl::format( title, "%s's", nc->GetPlayerName() ); GetAgeLink()->GetAgeInfo()->SetAgeUserDefinedName( title.c_str() ); } if ( !GetAgeLink()->GetAgeInfo()->HasAgeDescription() ) { // set description std::string desc; unsigned nameLen = StrLen(nc->GetPlayerName()); if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') xtl::format( desc, "%s' %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); else xtl::format( desc, "%s's %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); GetAgeLink()->GetAgeInfo()->SetAgeDescription( desc.c_str() ); } if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) { GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); } VaultRegisterOwnedAgeAndWait(GetAgeLink()); } } else { if (stricmp(GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNeighborhoodAgeFilename) == 0) { // if we get here then its because we're linking to a neighborhood that we don't belong to // and our own neighborhood book is not volatile, so really we want to basic link GetAgeLink()->SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); success = true; break; } } linkNode->DecRef(); } } GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); // falls thru to OWNED BOOK case... //-------------------------------------------------------------------- // OWNED BOOK. Look for the book in our AgesIOwn folder case plNetCommon::LinkingRules::kOwnedBook: { plAgeLinkStruct ownedLink; if (VaultGetOwnedAgeLink(GetAgeLink()->GetAgeInfo(), &ownedLink)) { GetAgeLink()->GetAgeInfo()->CopyFrom(ownedLink.GetAgeInfo()); // Remember spawn point (treasure book support) plSpawnPointInfo theSpawnPt = GetAgeLink()->SpawnPoint(); VaultAddOwnedAgeSpawnPoint(*GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(), theSpawnPt); } else { success = false; } } break; //-------------------------------------------------------------------- // VISIT BOOK. Look for the book in our AgesICanVisit folder case plNetCommon::LinkingRules::kVisitBook: { plAgeLinkStruct visitLink; if (VaultGetVisitAgeLink(GetAgeLink()->GetAgeInfo(), &visitLink)) GetAgeLink()->GetAgeInfo()->CopyFrom(visitLink.GetAgeInfo()); else success = false; } break; //-------------------------------------------------------------------- // SUB-AGE BOOK: Look for an existing sub-age in this age's SubAges folder and use it if found. // plNetClientTaskHandler will add a SubAge back link to our current age once we arrive there. case plNetCommon::LinkingRules::kSubAgeBook: { plAgeLinkStruct subAgeLink; if (VaultAgeFindOrCreateSubAgeLinkAndWait(GetAgeLink()->GetAgeInfo(), &subAgeLink, NetCommGetAge()->ageInstId)) GetAgeLink()->GetAgeInfo()->CopyFrom(subAgeLink.GetAgeInfo()); else success = false; } break; //-------------------------------------------------------------------- // CHILD-AGE BOOK: Look for an existing child-age in this parent ageinfo ChildAges folder and use it if found. // plNetClientTaskHandler will add a ChildAge back link to our current age once we arrive there. case plNetCommon::LinkingRules::kChildAgeBook: { plAgeLinkStruct childLink; wchar parentAgeName[MAX_PATH]; if (GetAgeLink()->HasParentAgeFilename()) { StrToUnicode(parentAgeName, GetAgeLink()->GetParentAgeFilename(), arrsize(parentAgeName)); success = VaultAgeFindOrCreateChildAgeLinkAndWait(parentAgeName, GetAgeLink()->GetAgeInfo(), &childLink); } else { success = VaultAgeFindOrCreateChildAgeLinkAndWait(nil, GetAgeLink()->GetAgeInfo(), &childLink); } if (success) GetAgeLink()->GetAgeInfo()->CopyFrom(childLink.GetAgeInfo()); } break; //-------------------------------------------------------------------- // ??? DEFAULT_FATAL(GetAgeLink()->GetLinkingRules()); } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Post-Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); hsAssert( GetAgeLink()->GetAgeInfo()->HasAgeFilename(), "AgeLink has no AgeFilename. Link will fail." ); return success; } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::LeaveAge (bool quitting) { // Queue leave op NlmLeaveAgeOp * leaveAgeOp = NEWZERO(NlmLeaveAgeOp); leaveAgeOp->quitting = quitting; QueueOp(leaveAgeOp); } //////////////////////////////////////////////////////////////////// // End.