bool plNetObjectDebugger::AddDebugObject(const char* objName, const char* pageName) { if (!objName) return false; int size=strlen(objName)+1; hsTempArray<char> tmpObjName(size); memset(tmpObjName, 0, size); // // set string matching flags // int len = strlen(objName); uint32_t flags=0; if (objName[0]=='*') { if (objName[len-1]=='*') { flags = kSubStringMatch; // *foo* strncpy(tmpObjName, objName+1, strlen(objName)-2); } else { flags = kEndStringMatch; // *foo strncpy(tmpObjName, objName+1, strlen(objName)-1); } } if (!flags && objName[len-1]=='*') { flags = kStartStringMatch; // foo* strncpy(tmpObjName, objName, strlen(objName)-1); } if (!flags) { flags = kExactStringMatch; strcpy(tmpObjName, objName); } // // set plLocation // plLocation loc; if (pageName) { loc = plKeyFinder::Instance().FindLocation(NetCommGetAge()->ageDatasetName, pageName); flags |= kPageMatch; } fDebugObjects.push_back(new DebugObject(tmpObjName, loc, flags)); ICreateStatusLog(); return true; }
// // if both objName and pageName are provided, and this object has page info, // return true if object matches both string and location. // else just return true if object matches string // bool plNetObjectDebugger::DebugObject::ObjectMatches(const char* objName, const char* pageName) { if (!objName) return false; if (!pageName || (fFlags & kPageMatch)==0) { // only have enough info to match by objName return StringMatches(objName); } plLocation loc; loc = plKeyFinder::Instance().FindLocation(NetCommGetAge()->ageDatasetName, pageName); return (StringMatches(objName) && loc==fLoc); }
void plAutoProfileImp::INextProfile() { // Haven't linked to our first age yet, do that before we start profiling if (fNextAge == 0) { if (!INextAge()) IShutdown(); } else { // Log the stats for this spawn point if (!fLastSpawnPointName.IsNull()) { plString ageName = NetCommGetAge()->ageDatasetName; plProfileManagerFull::Instance().LogStats(ageName, fLastSpawnPointName); plMipmap mipmap; if (plClient::GetInstance()->GetPipeline()->CaptureScreen(&mipmap)) { plString fileName = plFormat("{}\\{}_{}.jpg", plProfileManagerFull::Instance().GetProfilePath(), ageName, fLastSpawnPointName); plJPEG::Instance().SetWriteQuality(100); plJPEG::Instance().WriteToFile(fileName.c_str(), &mipmap); } fLastSpawnPointName = plString::Null; } // Try to go to the next spawn point if (!INextSpawnPoint()) { // Link to the next age if (!INextAge()) { // We've done all the ages, shut down IShutdown(); } } } }
void plLinkEffectsMgr::ISendAllReadyCallbacks() { int i; for (i = fLinks.GetCount() - 1; i >= 0; i--) { if (fLinks[i]->fEffects <= 0) { if (fLinks[i]->IsLeavingAge()) { if (fLinks[i]->GetLinkKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) { plLinkOutUnloadMsg* lam = new plLinkOutUnloadMsg; // derived from LoadAgeMsg lam->SetAgeFilename( NetCommGetAge()->ageDatasetName ); lam->AddReceiver(plNetClientMgr::GetInstance()->GetKey()); lam->SetPlayerID(plNetClientMgr::GetInstance()->GetPlayerID()); lam->Send(); } } else { plLinkInDoneMsg* lid = new plLinkInDoneMsg; lid->AddReceiver(fLinks[i]->GetLinkKey()); lid->SetBCastFlag(plMessage::kPropagateToModifiers); lid->Send(); if (fLinks[i]->GetLinkKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) { plLinkInDoneMsg* lid = new plLinkInDoneMsg; lid->AddReceiver(plNetClientMgr::GetInstance()->GetKey()); lid->Send(); } } hsRefCnt_SafeUnRef(fLinks[i]); fLinks.Remove(i); hsStatusMessage("Done - removing link FX msg\n"); } } }
uint8_t 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(kNilUuid); accInfo.SetOnline(false); rvnInfo->DecRef(); } #else // Update our online status if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); wchar_t ageInstName[MAX_PATH]; plUUID 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()) { info->SetAgeFilename(plNetLinkingMgr::GetProperAgeName(info->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 (info->GetAgeFilename().CompareI(kStartUpAgeFilename) == 0) link->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); //------------------------------------------------------------------------ // SPECIAL CASE: Nexus: force original link if (info->GetAgeFilename().CompareI(kNexusAgeFilename) == 0) link->SetLinkingRules(plNetCommon::LinkingRules::kOriginalBook); //------------------------------------------------------------------------ // SPECIAL CASE: ACA: force original link if (info->GetAgeFilename().CompareI(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()) { plUUID newuuid = plUUID::Generate(); info->SetAgeInstanceGuid(&newuuid); } 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 plString title; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') title = plString::Format("%s'", nc->GetPlayerName().c_str()); else title = plString::Format("%s's", nc->GetPlayerName().c_str()); info->SetAgeUserDefinedName(title.c_str()); } if (!info->HasAgeDescription()) { // set description plString desc; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') desc = plString::Format("%s' %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); else desc = plString::Format("%s's %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); info->SetAgeDescription(desc.c_str()); } if (!info->HasAgeInstanceGuid()) { plUUID newuuid = plUUID::Generate(); info->SetAgeInstanceGuid(&newuuid); } // 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.GetVolatile()) { if (VaultUnregisterOwnedAgeAndWait(&ageInfo)) { // Fill in fields for new age create. if (!info->HasAgeUserDefinedName()) { // set user-defined name plString title; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') title = plString::Format("%s'", nc->GetPlayerName().c_str()); else title = plString::Format("%s's", nc->GetPlayerName().c_str()); info->SetAgeUserDefinedName(title.c_str()); } if (!info->HasAgeDescription()) { // set description plString desc; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') desc = plString::Format("%s' %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); else desc = plString::Format("%s's %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); info->SetAgeDescription( desc.c_str() ); } if (!info->HasAgeInstanceGuid()) { plUUID newuuid = plUUID::Generate(); info->SetAgeInstanceGuid(&newuuid); } VaultRegisterOwnedAge(link); // Note: We MUST break or the OwnedBook code will fail! success = kLinkDeferred; break; } } else { if (info->GetAgeFilename().CompareI(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_t parentAgeName[MAX_PATH]; if (link->HasParentAgeFilename()) StrToUnicode(parentAgeName, link->GetParentAgeFilename(), arrsize(parentAgeName)); switch(VaultAgeFindOrCreateChildAgeLink( (link->HasParentAgeFilename() ? parentAgeName : nil), info, &childLink)) { case static_cast<uint8_t>(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::IPostProcessLink( void ) { // Grab some useful things... plAgeLinkStruct* link = GetAgeLink(); plAgeInfoStruct* info = link->GetAgeInfo(); bool city = (info->GetAgeFilename().CompareI(kCityAgeFilename) == 0); bool hood = (info->GetAgeFilename().CompareI(kNeighborhoodAgeFilename) == 0); bool psnl = (info->GetAgeFilename().CompareI(kPersonalAgeFilename) == 0); // Update our online status if (RelVaultNode* rvnInfo = VaultGetPlayerInfoNodeIncRef()) { VaultPlayerInfoNode accInfo(rvnInfo); wchar_t ageInstName[MAX_PATH]; plUUID 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->GetNodeId(), 1)) VaultAddChildNode( fldr->GetNodeId(), info->GetNodeId(), 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->GetNodeId(), 1)) VaultAddChildNode( fldr->GetNodeId(), info->GetNodeId(), 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; } }
//============================================================================ bool plNCAgeJoiner::MsgReceive (plMessage * msg) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); plAvatarMgr * am = plAvatarMgr::GetInstance(); plAgeLoader * al = plAgeLoader::GetInstance(); //======================================================================== // Finished updating the age from FileSrv //======================================================================== if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) { if (resMsg->Success()) { nc->ResetServerTimeOffset(); NetCommLinkToAge( age, this ); LogMsg(kLogPerf, L"AgeJoiner: Next:kNoOp (age updated)"); } else Complete(false, resMsg->GetError()); return true; } //======================================================================== // Connected to age instance //======================================================================== if (plNetCommLinkToAgeMsg * linkToAgeMsg = plNetCommLinkToAgeMsg::ConvertNoRef(msg)) { if (IS_NET_ERROR(linkToAgeMsg->result)) { Complete(false, "LinkToAge failed"); } else if (unsigned ageVaultId = NetCommGetAge()->ageVaultId) { // Download the age vault VaultDownload( L"AgeJoin", ageVaultId, AgeVaultDownloadCallback, this, nil, // FVaultDownloadProgressCallback this ); } else { // not vault to downloaded, just start loading age data LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadAge (no vault)"); nextOp = kLoadAge; } return true; } //======================================================================== // All age data paged in //======================================================================== if (plAgeLoaded2Msg * ageLoaded2Msg = plAgeLoaded2Msg::ConvertNoRef(msg)) { // Exec custom age settings al->ExecPendingAgeFniFiles(); al->ExecPendingAgeCsvFiles(); LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadPlayer"); nextOp = kLoadPlayer; return true; } //======================================================================== // Local avatar loaded //======================================================================== plPlayerPageMsg * playerPageMsg = plPlayerPageMsg::ConvertNoRef(msg); if (playerPageMsg && !playerPageMsg->fUnload && playerPageMsg->fPlayer && playerPageMsg->fLocallyOriginated) { if (NetCommNeedToLoadAvatar()) NetCommSetAvatarLoaded(); LogMsg(kLogPerf, L"AgeJoiner: Next:kPropagatePlayer"); nextOp = kPropagatePlayer; return false; // NetClientMgr must also handle this message } //======================================================================== // Received all SDL states //======================================================================== plNetClientMgrMsg * netClientMgrMsg = plNetClientMgrMsg::ConvertNoRef(msg); if (netClientMgrMsg && netClientMgrMsg->type == plNetClientMgrMsg::kNotifyRcvdAllSDLStates) { LogMsg(kLogPerf, L"AgeJoiner: Next:kEnableClickables"); nextOp = kDestroyProgressBar; return true; } //======================================================================== // Done loading all states. Time to link in! //======================================================================== plInitialAgeStateLoadedMsg * stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef(msg); if(stateMsg) { plNetObjectDebugger::GetInstance()->LogMsg("OnServerInitComplete"); nc->SetFlagsBit(plNetClientApp::kLoadingInitialAgeState, false); const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); plLinkEffectsTriggerMsg* lem = new plLinkEffectsTriggerMsg(); lem->SetLeavingAge(false); // linking in lem->SetLinkKey(nc->GetLocalPlayerKey()); plKey animKey = avMod->GetLinkInAnimKey(); lem->SetLinkInAnimKey(animKey); // indicate if we are invisible if (avMod && avMod->IsInStealthMode() && avMod->GetTarget(0)) lem->SetInvisLevel(avMod->GetStealthLevel()); lem->SetBCastFlag(plMessage::kNetPropagate); lem->MuteLinkSfx(muteLinkSfx); lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kLinkEffectsMgr_KEY))); lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kClient_KEY))); lem->Send(); Complete(true, "Age joined"); return true; } return false; }
//============================================================================ bool plNCAgeJoiner::MsgReceive (plMessage * msg) { plNetClientMgr * nc = plNetClientMgr::GetInstance(); plAvatarMgr * am = plAvatarMgr::GetInstance(); plAgeLoader * al = plAgeLoader::GetInstance(); //======================================================================== // Finished updating the age from FileSrv //======================================================================== if (plResPatcherMsg * resMsg = plResPatcherMsg::ConvertNoRef(msg)) { if (resMsg->Success()) { nc->ResetServerTimeOffset(); NetCommLinkToAge( age, this ); LogMsg(kLogPerf, L"AgeJoiner: Next:kNoOp (age updated)"); } else Complete(false, resMsg->GetError()); return true; } //======================================================================== // Connected to age instance //======================================================================== if (plNetCommLinkToAgeMsg * linkToAgeMsg = plNetCommLinkToAgeMsg::ConvertNoRef(msg)) { if (IS_NET_ERROR(linkToAgeMsg->result)) { Complete(false, "LinkToAge failed"); } else if (unsigned ageVaultId = NetCommGetAge()->ageVaultId) { // Download the age vault VaultDownload( L"AgeJoin", ageVaultId, AgeVaultDownloadCallback, this, nil, // FVaultDownloadProgressCallback this ); } else { // not vault to downloaded, just start loading age data LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadAge (no vault)"); nextOp = kLoadAge; } return true; } //======================================================================== // All age data paged in //======================================================================== if (plAgeLoaded2Msg * ageLoaded2Msg = plAgeLoaded2Msg::ConvertNoRef(msg)) { // Exec custom age settings al->ExecPendingAgeFniFiles(); al->ExecPendingAgeCsvFiles(); LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadPlayer"); nextOp = kLoadPlayer; return true; } //======================================================================== // Local avatar loaded //======================================================================== plPlayerPageMsg * playerPageMsg = plPlayerPageMsg::ConvertNoRef(msg); if (playerPageMsg && !playerPageMsg->fUnload && playerPageMsg->fPlayer && playerPageMsg->fLocallyOriginated) { if (NetCommNeedToLoadAvatar()) NetCommSetAvatarLoaded(); LogMsg(kLogPerf, L"AgeJoiner: Next:kPropagatePlayer"); nextOp = kPropagatePlayer; return false; // NetClientMgr must also handle this message } //======================================================================== // Received all SDL states //======================================================================== plNetClientMgrMsg * netClientMgrMsg = plNetClientMgrMsg::ConvertNoRef(msg); if (netClientMgrMsg && netClientMgrMsg->type == plNetClientMgrMsg::kNotifyRcvdAllSDLStates) { LogMsg(kLogPerf, L"AgeJoiner: Next:kEnableClickables"); nextOp = kDestroyProgressBar; return true; } return false; }
//============================================================================ void plNCAgeLeaver::ExecNextOp () { plNetClientMgr * nc = plNetClientMgr::GetInstance(); plAvatarMgr * am = plAvatarMgr::GetInstance(); plAgeLoader * al = plAgeLoader::GetInstance(); NextOp next = nextOp; nextOp = kNoOp; switch (next) { //==================================================================== case kNoOp: { } break; //==================================================================== case kDisableClickables: { (new plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableClickables))->Send(); nextOp = kLinkOutFX; } break; //==================================================================== case kLinkOutFX: { nc->StartLinkOutFX(); } break; //==================================================================== case kUnloadAge: { NetCliGameDisconnect(); // Cull nodes that were part of this age vault (but not shared by the player's vault) VaultCull(NetCommGetAge()->ageVaultId); // remove the age device inbox mappings VaultClearDeviceInboxMap(); // Tell our local player that he's unspawning (if that is indeed the case) nc->IPlayerChangeAge(true /* exiting */, 0/* respawn */); // disconnect age vault // @@@ TODO: Unload age vault here plAgeLoader::GetInstance()->UnloadAge(); // unload age nc->ISendCameraReset(false/*leaving age*/); // reset camera nc->IUnloadRemotePlayers(); // unload other players nc->IUnloadNPCs(); // unload non-player clones if (NetCommNeedToLoadAvatar()) am->UnLoadLocalPlayer(); } break; //==================================================================== case kNotifyAgeUnloaded: { // Set "Playing Game" bit to false nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false); // Release AgeSDL object, if any if (nc->fAgeSDLObjectKey) nc->GetKey()->Release(nc->fAgeSDLObjectKey); // All done leaving age Complete(true, "Age unloaded"); } break; DEFAULT_FATAL(nextOp); } }