/*==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 . 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 "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" /***************************************************************************** * * 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; } // If a link was deferred in order to register an owned age, we will // get a VaultNotify about the registration if (plVaultNotifyMsg* vaultMsg = plVaultNotifyMsg::ConvertNoRef(msg)) { IProcessVaultNotifyMsg(vaultMsg); 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() ); // Actually do stuff... UInt8 pre = IPreProcessLink(); if (pre == kLinkImmediately) { msg->Ref(); IDoLink(msg); } else if (pre == kLinkDeferred) { msg->Ref(); fDeferredLink = msg; } else if (pre == kLinkFailed) { hsLogEntry( nc->ErrorMsg( "IPreProcessLink failed. Not linking." ) ); // Restore previous age info state. GetAgeLink()->CopyFrom( GetPrevAgeLink() ); GetPrevAgeLink()->CopyFrom( &save ); result = false; } return result; } //////////////////////////////////////////////////////////////////// void plNetLinkingMgr::IDoLink(plLinkToAgeMsg* msg) { plNetClientMgr* nc = plNetClientMgr::GetInstance(); 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); // UnRef msg->UnRef(); } //////////////////////////////////////////////////////////////////// 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::IProcessVaultNotifyMsg(plVaultNotifyMsg* msg) { // No deferred link? Bye bye. if (fDeferredLink == nil) return false; plAgeLinkStruct* cur = GetAgeLink(); RelVaultNode* cVaultLink = nil; switch (msg->GetType()) { case plVaultNotifyMsg::kRegisteredChildAgeLink: case plVaultNotifyMsg::kRegisteredOwnedAge: case plVaultNotifyMsg::kRegisteredSubAgeLink: cVaultLink = VaultGetNodeIncRef(msg->GetArgs()->GetInt(plNetCommon::VaultTaskArgs::kAgeLinkNode)); break; default: return false; } if (cVaultLink != nil) { // Verify that if the Age vault already exists that it matches the requested // deferred link. If it doesn't exist, well, I hope you're happy with the result. VaultAgeLinkNode accLink(cVaultLink); if (RelVaultNode* rvnInfo = cVaultLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { plAgeInfoStruct dest; VaultAgeInfoNode accInfo(rvnInfo); accInfo.CopyTo(&dest); if (!dest.IsEqualTo(fDeferredLink->GetAgeLink()->GetAgeInfo())) { hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Waiting for a deferred link to '%s' but got AgeInfo for '%s' instead.", fDeferredLink->GetAgeLink()->AsStdString().c_str(), dest.AsStdString().c_str() ); ); rvnInfo->DecRef(); return false; } rvnInfo->DecRef(); } // If we're still here, that means the links match. Set the vault copy as our current // AgeLink and AgeInfo. Note this mainly copies the AgeInfo. accLink.CopyTo(cur); hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Performing deferred link to '%s'", cur->AsStdString().c_str() ); ); // Steals fDeferredLink IDoLink(fDeferredLink); fDeferredLink = nil; cVaultLink->DecRef(); return true; } else { hsLogEntry( plNetClientMgr::GetInstance()->ErrorMsg( "Waiting for a deferred link to '%s' but got a garbage link?", fDeferredLink->GetAgeLink()->AsStdString().c_str() ) ); } return false; } //////////////////////////////////////////////////////////////////// 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 ) { // Grab some useful things... plAgeLinkStruct* link = GetAgeLink(); plAgeInfoStruct* info = link->GetAgeInfo(); bool city = (stricmp(info->GetAgeFilename(), kCityAgeFilename) == 0); bool hood = (stricmp(info->GetAgeFilename(), kNeighborhoodAgeFilename) == 0); bool psnl = (stricmp(info->GetAgeFilename(), kPersonalAgeFilename) == 0); // Update our online status if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); wchar ageInstName[MAX_PATH]; Uuid ageInstGuid = *info->GetAgeInstanceGuid(); StrToUnicode(ageInstName, info->GetAgeInstanceName(), arrsize(ageInstName)); accInfo.SetAgeInstName(ageInstName); accInfo.SetAgeInstUuid(ageInstGuid); accInfo.SetOnline(true); rvnInfo->DecRef(); } switch (link->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)) VaultAddChildNode( fldr->nodeId, info->nodeId, NetCommGetPlayer()->playerInt, nil, nil ); 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)) VaultAddChildNode( fldr->nodeId, info->nodeId, NetCommGetPlayer()->playerInt, nil, nil ); 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; VaultAgeFindOrCreateSubAgeLink(GetPrevAgeLink()->GetAgeInfo(), &subAgeLink, NetCommGetAge()->ageInstId); } break; } } //////////////////////////////////////////////////////////////////// UInt8 plNetLinkingMgr::IPreProcessLink( void ) { // Grab some stuff we're gonna use extensively plNetClientMgr* nc = plNetClientMgr::GetInstance(); plAgeLinkStruct* link = GetAgeLink(); plAgeInfoStruct* info = link->GetAgeInfo(); PreProcessResult success = kLinkImmediately; if ( nc->GetFlagsBit( plNetClientMgr::kNullSend ) ) { hsLogEntry( nc->DebugMsg( "NetClientMgr nullsend. Not linking." ) ); return kLinkFailed; } #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, info->GetAgeInstanceName(), arrsize(ageInstName)); accInfo.SetAgeInstName(ageInstName); accInfo.SetAgeInstUuid(ageInstGuid); accInfo.SetOnline(true); rvnInfo->DecRef(); } #endif //------------------------------------------------------------------------ // Fixup empty fields if (info->HasAgeFilename()) { GetAgeLink()->GetAgeInfo()->SetAgeFilename( plNetLinkingMgr::GetProperAgeName( GetAgeLink()->GetAgeInfo()->GetAgeFilename() ).c_str() ); if (!info->HasAgeInstanceName()) { info->SetAgeInstanceName(info->GetAgeFilename()); } } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Pre-Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( link->GetLinkingRules() ) ) ); //------------------------------------------------------------------------ // SPECIAL CASE: StartUp: force basic link if (stricmp(info->GetAgeFilename(), kStartUpAgeFilename) == 0) { link->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); } //------------------------------------------------------------------------ // SPECIAL CASE: Nexus: force original link if (stricmp(info->GetAgeFilename(), kNexusAgeFilename) == 0) { link->SetLinkingRules(plNetCommon::LinkingRules::kOriginalBook); } //------------------------------------------------------------------------ // SPECIAL CASE: ACA: force original link if (stricmp(info->GetAgeFilename(), kAvCustomizationFilename ) == 0) { link->SetLinkingRules(plNetCommon::LinkingRules::kOriginalBook); } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( link->GetLinkingRules() ) ) ); switch ( link->GetLinkingRules() ) { //-------------------------------------------------------------------- // BASIC LINK. Link to a unique instance of the age, if no instance specified. case plNetCommon::LinkingRules::kBasicLink: if (!info->HasAgeInstanceGuid()) info->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(info->GetAgeFilename()); plAgeLinkStruct ownedLink; if (!VaultGetOwnedAgeLink(&ageInfo, &ownedLink)) { // Fill in fields for new age create. if (!info->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() ); info->SetAgeUserDefinedName(title.c_str()); } if (!info->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(), info->GetAgeInstanceName() ); else xtl::format( desc, "%s's %s", nc->GetPlayerName(), info->GetAgeInstanceName() ); info->SetAgeDescription( desc.c_str() ); } if (!info->HasAgeInstanceGuid()) { info->SetAgeInstanceGuid(&plUUID(GuidGenerate())); } // register this as an owned age now before we link to it. // Note: We MUST break or the OwnedBook code will fail! VaultRegisterOwnedAge(link); success = kLinkDeferred; break; } 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 ( !info->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() ); info->SetAgeUserDefinedName( title.c_str() ); } if ( !info->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(), info->GetAgeInstanceName() ); else xtl::format( desc, "%s's %s", nc->GetPlayerName(), info->GetAgeInstanceName() ); info->SetAgeDescription( desc.c_str() ); } if (!info->HasAgeInstanceGuid()) { info->SetAgeInstanceGuid(&plUUID(GuidGenerate())); } VaultRegisterOwnedAge(link); // Note: We MUST break or the OwnedBook code will fail! success = kLinkDeferred; break; } } else { if (stricmp(info->GetAgeFilename(), kNeighborhoodAgeFilename) == 0) { // if we get here then it's 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 link->SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); success = kLinkImmediately; break; } } linkNode->DecRef(); } } link->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(info, &ownedLink)) { info->CopyFrom(ownedLink.GetAgeInfo()); // Remember spawn point (treasure book support) plSpawnPointInfo theSpawnPt = link->SpawnPoint(); VaultAddOwnedAgeSpawnPoint(*info->GetAgeInstanceGuid(), theSpawnPt); } else { success = kLinkFailed; } } break; //-------------------------------------------------------------------- // VISIT BOOK. Look for the book in our AgesICanVisit folder case plNetCommon::LinkingRules::kVisitBook: { plAgeLinkStruct visitLink; if (VaultGetVisitAgeLink(info, &visitLink)) info->CopyFrom(visitLink.GetAgeInfo()); else success = kLinkFailed; } 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 (VaultAgeFindOrCreateSubAgeLink(info, &subAgeLink, NetCommGetAge()->ageInstId)) info->CopyFrom(subAgeLink.GetAgeInfo()); else success = kLinkDeferred; } 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 (link->HasParentAgeFilename()) StrToUnicode(parentAgeName, link->GetParentAgeFilename(), arrsize(parentAgeName)); switch(VaultAgeFindOrCreateChildAgeLink( (link->HasParentAgeFilename() ? parentAgeName : nil), info, &childLink)) { case hsFail: success = kLinkFailed; break; case FALSE: success = kLinkDeferred; break; case TRUE: success = kLinkImmediately; } if (success == kLinkImmediately) info->CopyFrom(childLink.GetAgeInfo()); } break; //-------------------------------------------------------------------- // ??? DEFAULT_FATAL(link->GetLinkingRules()); } hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Post-Process: Linking with %s rules...", plNetCommon::LinkingRules::LinkingRuleStr( link->GetLinkingRules() ) ) ); hsAssert( info->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.