void CharCreationManager::HandleLifeEvents(MsgEntry* me,Client* client) { psLifeEventMsg message(me); if(!message.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psLifeEventMsg from client id %u.\n",me->clientnum); return; } psLifeEventMsg response(me->clientnum); for(size_t x = 0; x < lifeEvents.GetSize(); x++) { response.AddEvent(lifeEvents[x]); } response.ConstructMessage(); if(response.valid) response.SendMessage(); else { Bug2("Failed to construct a valid psLifeEventMsg for client id %u.\n",me->clientnum); } }
void CharCreationManager::HandleParents(MsgEntry* me,Client* client) { psCreationChoiceMsg message(me); if(!message.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psCreationChoiceMsg from client id %u.\n",me->clientnum); return; } psCreationChoiceMsg response(me->clientnum, (int) parentData.GetSize(), MSGTYPE_CHAR_CREATE_PARENTS); for(size_t x = 0; x < parentData.GetSize(); x++) { response.AddChoice(parentData[x]->id, parentData[x]->name, parentData[x]->description, parentData[x]->choiceArea, parentData[x]->cpCost); } response.ConstructMessage(); if(response.valid) response.SendMessage(); else { Bug2("Failed to construct a valid psCreationChoiceMsg for client id %u.\n",me->clientnum); } }
void CharCreationManager::HandleChildhood(MsgEntry* me,Client* client) { psCreationChoiceMsg message(me); if(!message.valid) { Debug2(LOG_NET, me->clientnum,"Received unparsable Childhood request from client: %u\n", me->clientnum); return; } psCreationChoiceMsg response(me->clientnum, (int) childhoodData.GetSize(), MSGTYPE_CHAR_CREATE_CHILDHOOD); for(size_t x = 0; x < childhoodData.GetSize(); x++) { response.AddChoice(childhoodData[x]->id, childhoodData[x]->name, childhoodData[x]->description, childhoodData[x]->choiceArea, childhoodData[x]->cpCost); } response.ConstructMessage(); if(response.valid) { response.SendMessage(); } else { Bug2("Failed to construct a valid psCreationChoiceMsg for client id %u.\n",me->clientnum); } }
void ClientConnectionSet::MarkDelete(Client* client) { CS::Threading::RecursiveMutexScopedLock lock(mutex); uint32_t clientid = client->GetClientNum(); if(!addrHash.DeleteAll(client->GetAddress())) Bug2("Couldn't delete client %d, it was never added!", clientid); hash.DeleteAll(clientid); toDelete.Push(client); }
void AuthenticationServer::SendDisconnect(Client* client, const char *reason) { if (client->GetActor()) { psDisconnectMessage msg(client->GetClientNum(), client->GetActor()->GetEID(), reason); if (msg.valid) { msg.msg->priority = PRIORITY_LOW; psserver->GetEventManager()->Broadcast(msg.msg, NetBase::BC_FINALPACKET); } else { Bug2("Could not create a valid psDisconnectMessage for client %u.\n",client->GetClientNum()); } } else { psDisconnectMessage msg(client->GetClientNum(), 0, reason); if (msg.valid) { psserver->GetEventManager()->Broadcast(msg.msg, NetBase::BC_FINALPACKET); } } }
void AuthenticationServer::HandleAuthent(MsgEntry *me, Client *notused) { csTicks start = csGetTicks(); psAuthenticationMessage msg(me); // This cracks message into members. if (!msg.valid) { Debug1(LOG_NET,me->clientnum,"Mangled psAuthenticationMessage received.\n"); return; } if (!CheckAuthenticationPreCondition(me->clientnum, msg.NetVersionOk(),msg.sUser)) return; csString status; status.Format("%s, %u, Received Authentication Message", (const char *) msg.sUser, me->clientnum); psserver->GetLogCSV()->Write(CSV_AUTHENT, status); if ( msg.sUser.Length() == 0 || msg.sPassword.Length() == 0) { psserver->RemovePlayer(me->clientnum,"No username or password entered"); Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: No username or password.\n", (const char *)msg.sUser); return; } // Check if login was correct Notify2(LOG_CONNECTIONS,"Check Login for: '%s'\n", (const char*)msg.sUser); psAccountInfo *acctinfo=CacheManager::GetSingleton().GetAccountInfoByUsername((const char *)msg.sUser); if ( !acctinfo ) { // invalid psserver->RemovePlayer(me->clientnum,"Incorrect password or username."); Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected: No account found with that name.\n", (const char *)msg.sUser); return; } // Add account to cache to optimize repeated login attempts CacheManager::GetSingleton().AddToCache(acctinfo,msg.sUser,120); // Check if password was correct csString passwordhashandclientnum (acctinfo->password); passwordhashandclientnum.Append(":"); passwordhashandclientnum.Append(me->clientnum); csString encoded_hash = csMD5::Encode(passwordhashandclientnum).HexString(); if (strcmp( encoded_hash.GetData() , msg.sPassword.GetData())) // authentication error { psserver->RemovePlayer(me->clientnum, "Incorrect password or username."); Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected (Bad password).",(const char *)msg.sUser); // No delete necessary because AddToCache will auto-delete // delete acctinfo; return; } /** * Check if the client is already logged in */ Client* existingClient = clients->FindAccount(acctinfo->accountid, me->clientnum); if (existingClient) // account already logged in { // invalid authent message from a different client csString reason; if(existingClient->IsZombie()) { reason.Format("Your character(%s) was still in combat or casting a spell when you disconnected. " "This connection is being overridden by a new login.", existingClient->GetName()); } else { reason.Format("You are already logged on to this server as %s. " "This connection is being overridden by a new login..", existingClient->GetName()); } psserver->RemovePlayer(existingClient->GetClientNum(), reason); Notify2(LOG_CONNECTIONS,"User '%s' authentication request overrides an existing logged in user.\n", (const char *)msg.sUser); // No delete necessary because AddToCache will auto-delete // delete acctinfo; } if(csGetTicks() - start > 500) { csString status; status.Format("Warning: Spent %u time authenticating account ID %u, After password check", csGetTicks() - start, acctinfo->accountid); psserver->GetLogCSV()->Write(CSV_STATUS, status); } Client *client = clients->FindAny(me->clientnum); if (!client) { Bug2("Couldn't find client %d?!?",me->clientnum); // No delete necessary because AddToCache will auto-delete // delete acctinfo; return; } client->SetName(msg.sUser); client->SetAccountID( acctinfo->accountid ); // Check to see if the client is banned time_t now = time(0); BanEntry* ban = banmanager.GetBanByAccount(acctinfo->accountid); if (ban == NULL) { // Account not banned; try IP range ban = banmanager.GetBanByIPRange(client->GetIPRange()); // 2 day IP ban limit removed //if (ban && ban->end && now > ban->start + IP_RANGE_BAN_TIME) //{ // // Only ban by IP range for the first 2 days // ban = NULL; //} } if (ban) { if (now > ban->end) // Time served { banmanager.RemoveBan(acctinfo->accountid); } else // Notify and block { tm* timeinfo = gmtime(&(ban->end)); csString banmsg; banmsg.Format("You are banned until %d-%d-%d %d:%d GMT. Reason: %s", timeinfo->tm_year+1900, timeinfo->tm_mon+1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, ban->reason.GetData() ); psserver->RemovePlayer(me->clientnum, banmsg); Notify2(LOG_CONNECTIONS,"User '%s' authentication request rejected (Banned).",(const char *)msg.sUser); // No delete necessary because AddToCache will auto-delete // delete acctinfo; return; } } if(csGetTicks() - start > 500) { csString status; status.Format("Warning: Spent %u time authenticating account ID %u, After ban check", csGetTicks() - start, acctinfo->accountid); psserver->GetLogCSV()->Write(CSV_STATUS, status); } /** Check to see if there are any players on that account. All accounts should have * at least one player in this function. */ psCharacterList *charlist = psserver->CharacterLoader.LoadCharacterList(acctinfo->accountid); if (!charlist) { Error2("Could not load Character List for account! Rejecting client %s!\n",(const char *)msg.sUser); psserver->RemovePlayer( me->clientnum, "Could not load the list of characters for your account. Please contact a PS Admin for help."); delete acctinfo; return; } // cache will auto-delete this ptr if it times out CacheManager::GetSingleton().AddToCache(charlist, CacheManager::GetSingleton().MakeCacheName("list", client->GetAccountID().Unbox()),120); /** * CHECK 6: Connection limit * * We check against number of concurrent connections, but players with * security rank of GameMaster or higher are not subject to this limit. */ if (psserver->IsFull(clients->Count(),client)) { // invalid psserver->RemovePlayer(me->clientnum, "The server is full right now. Please try again in a few minutes."); Notify2(LOG_CONNECTIONS, "User '%s' authentication request rejected: Too many connections.\n", (const char *)msg.sUser ); // No delete necessary because AddToCache will auto-delete // delete acctinfo; status = "User limit hit!"; psserver->GetLogCSV()->Write(CSV_STATUS, status); return; } Notify3(LOG_CONNECTIONS,"User '%s' (%d) added to active client list\n",(const char*) msg.sUser, me->clientnum); // Get the struct to refresh // Update last login ip and time char addr[20]; client->GetIPAddress(addr); acctinfo->lastloginip = addr; tm* gmtm = gmtime(&now); csString timeStr; timeStr.Format("%d-%02d-%02d %02d:%02d:%02d", gmtm->tm_year+1900, gmtm->tm_mon+1, gmtm->tm_mday, gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec); acctinfo->lastlogintime = timeStr; acctinfo->os = msg.os_; acctinfo->gfxcard = msg.gfxcard_; acctinfo->gfxversion = msg.gfxversion_; CacheManager::GetSingleton().UpdateAccountInfo(acctinfo); iCachedObject *obj = CacheManager::GetSingleton().RemoveFromCache(CacheManager::GetSingleton().MakeCacheName("auth",acctinfo->accountid)); CachedAuthMessage *cam; if (!obj) { // Send approval message psAuthApprovedMessage *message = new psAuthApprovedMessage(me->clientnum,client->GetPID(), charlist->GetValidCount() ); if(csGetTicks() - start > 500) { csString status; status.Format("Warning: Spent %u time authenticating account ID %u, After approval", csGetTicks() - start, acctinfo->accountid); psserver->GetLogCSV()->Write(CSV_STATUS, status); } // Send out the character list to the auth'd player for (int i=0; i<MAX_CHARACTERS_IN_LIST; i++) { if (charlist->GetEntryValid(i)) { // Quick load the characters to get enough info to send to the client psCharacter* character = psserver->CharacterLoader.QuickLoadCharacterData( charlist->GetCharacterID(i), false ); if (character == NULL) { Error2("QuickLoadCharacterData failed for character '%s'", charlist->GetCharacterName(i)); continue; } Notify3(LOG_CHARACTER, "Sending %s to client %d\n", character->name.GetData(), me->clientnum ); character->AppendCharacterSelectData(*message); delete character; } } message->ConstructMsg(); cam = new CachedAuthMessage(message); } else { // recover underlying object cam = (CachedAuthMessage *)obj->RecoverObject(); // update client id since new connection here cam->msg->msg->clientnum = me->clientnum; } // Send auth approved and char list in one message now cam->msg->SendMessage(); CacheManager::GetSingleton().AddToCache(cam, CacheManager::GetSingleton().MakeCacheName("auth",acctinfo->accountid), 10); SendMsgStrings(me->clientnum, true); client->SetSpamPoints(acctinfo->spamPoints); client->SetAdvisorPoints(acctinfo->advisorPoints); client->SetSecurityLevel(acctinfo->securitylevel); if (acctinfo->securitylevel >= GM_TESTER) { psserver->GetAdminManager()->Admin(me->clientnum, client); } if (CacheManager::GetSingletonPtr()->GetCommandManager()->Validate(client->GetSecurityLevel(), "default advisor")) psserver->GetAdviceManager()->AddAdvisor(client); if (CacheManager::GetSingletonPtr()->GetCommandManager()->Validate(client->GetSecurityLevel(), "default buddylisthide")) client->SetBuddyListHide(true); psserver->GetWeatherManager()->SendClientGameTime(me->clientnum); if(csGetTicks() - start > 500) { csString status; status.Format("Warning: Spent %u time authenticating account ID %u, After load", csGetTicks() - start, acctinfo->accountid); psserver->GetLogCSV()->Write(CSV_STATUS, status); } status.Format("%s - %s, %u, Logged in", addr, (const char*) msg.sUser, me->clientnum); psserver->GetLogCSV()->Write(CSV_AUTHENT, status); }
void CharCreationManager::HandleUploadMessage(MsgEntry* me, Client* client) { Debug1(LOG_NEWCHAR, me->clientnum,"New Character is being created"); psCharUploadMessage upload(me); if(!upload.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psUploadMessage from client %u.",me->clientnum); return; } AccountID acctID = client->GetAccountID(); if(!acctID.IsValid()) { Error2("Player tried to upload a character to unknown account %s.", ShowID(acctID)); psCharRejectedMessage reject(me->clientnum); psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET); psserver->RemovePlayer(me->clientnum,"Could not find your account."); return; } // Check to see if the player already has 4 accounts; csString query; query.Format("SELECT id FROM characters WHERE account_id=%d", acctID.Unbox()); Result result(db->Select(query)); if(result.IsValid() && result.Count() >= CHARACTERS_ALLOWED) { psserver->RemovePlayer(me->clientnum,"At your character limit."); return; } csString playerName = upload.name; csString lastName = upload.lastname; playerName = NormalizeCharacterName(playerName); lastName = NormalizeCharacterName(lastName); // Check banned names if(psserver->GetCharManager()->IsBanned(playerName)) { csString error; error.Format("The name %s is banned", playerName.GetData()); psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::RESERVED_NAME, (char*)error.GetData()); reject.SendMessage(); return; } if(psserver->GetCharManager()->IsBanned(lastName)) { csString error; error.Format("The lastname %s is banned", lastName.GetData()); psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::RESERVED_NAME, (char*)error.GetData()); reject.SendMessage(); return; } Debug3(LOG_NEWCHAR, me->clientnum,"Got player firstname (%s) and lastname (%s)\n",playerName.GetData(), lastName.GetData()); /////////////////////////////////////////////////////////////// // Check to see if the player name is valid /////////////////////////////////////////////////////////////// if(playerName.Length() == 0 || !FilterName(playerName)) { psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::NON_LEGAL_NAME, "The name you specifed is not a legal player name."); psserver->GetEventManager()->SendMessage(reject.msg); return; } if(lastName.Length() != 0 && !FilterName(lastName)) { psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::NON_LEGAL_NAME, "The name you specifed is not a legal lastname."); psserver->GetEventManager()->SendMessage(reject.msg); return; } Debug2(LOG_NEWCHAR, me->clientnum,"Checking player firstname '%s'..\n",playerName.GetData()); /////////////////////////////////////////////////////////////// // Check to see if the character name is unique in 'characters'. /////////////////////////////////////////////////////////////// if(!IsUnique(playerName)) { psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::NON_UNIQUE_NAME, "The firstname you specifed is not unique."); psserver->GetEventManager()->SendMessage(reject.msg); return; } if(lastName.Length()) { Debug2(LOG_NEWCHAR, me->clientnum,"Checking player lastname '%s'..\n",lastName.GetData()); if(!IsLastNameAvailable(lastName, acctID)) { psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::NON_UNIQUE_NAME, "The lastname you specifed is not unique."); psserver->GetEventManager()->SendMessage(reject.msg); return; } } /////////////////////////////////////////////////////////////// // Check to see if the character name is on the reserve list. /////////////////////////////////////////////////////////////// int reservedName = IsReserved(playerName, acctID); if(reservedName == NAME_RESERVED) { csString error; error.Format("The name %s is reserved", playerName.GetData()); psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::RESERVED_NAME, (char*)error.GetData()); psserver->GetEventManager()->SendMessage(reject.msg); return; } csString error; if(!psserver->charCreationManager->Validate(upload, error)) { error.Append(", your creation choices are invalid."); psCharRejectedMessage reject(me->clientnum, psCharRejectedMessage::INVALID_CREATION, (char*)error.GetData()); reject.SendMessage(); return; } /////////////////////////////////////////////////////////////// // Create the psCharacter structure for the player. /////////////////////////////////////////////////////////////// psCharacter* chardata=new psCharacter(); chardata->SetCharType(PSCHARACTER_TYPE_PLAYER); chardata->SetFullName(playerName,lastName); chardata->SetCreationInfo(upload.bio); psRaceInfo* raceinfo=psserver->GetCacheManager()->GetRaceInfoByNameGender(upload.race, (PSCHARACTER_GENDER)upload.gender); if(raceinfo==NULL) { Error3("Invalid race/gender combination on character creation: Race='%d' Gender='%d'", upload.race, upload.gender); psCharRejectedMessage reject(me->clientnum); psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET); psserver->RemovePlayer(me->clientnum,"Player tried to create an invalid race/gender."); delete chardata; return; } chardata->SetRaceInfo(raceinfo); chardata->SetHitPoints(50.0); chardata->GetMaxHP().SetBase(0.0); chardata->GetMaxMana().SetBase(0.0); //range is unused here float x,y,z,yrot,range; const char* sectorname; InstanceID newinstance = DEFAULT_INSTANCE; //get the option entries for tutorial from the server options. Note it's tutorial:variousdata optionEntry* tutorialEntry = psserver->GetCacheManager()->getOptionSafe("tutorial",""); sectorname = tutorialEntry->getOptionSafe("sectorname", "tutorial")->getValue(); psSectorInfo* sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname); if(!sectorinfo || PlayerHasFinishedTutorial(acctID, sectorinfo->uid)) { raceinfo->GetStartingLocation(x,y,z,yrot,range,sectorname); sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName(sectorname); //As we aren't going in the tutorial disable the tutorial help messages disable them for(int i = 0; i < TutorialManager::TUTOREVENTTYPE_COUNT; i++) chardata->CompleteHelpEvent(i); } else { // Try tutorial level first. x = tutorialEntry->getOptionSafe("sectorx", "-225.37")->getValueAsDouble(); y = tutorialEntry->getOptionSafe("sectory", "-21.32")->getValueAsDouble(); z = tutorialEntry->getOptionSafe("sectorz", "26.79")->getValueAsDouble(); yrot = tutorialEntry->getOptionSafe("sectoryrot", "-2.04")->getValueAsDouble(); } bool sectorFound = true; if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL) { Error2("Sector='%s' found but no map file was detected for it. Using NPCroom1", sectorname); sectorinfo = psserver->GetCacheManager()->GetSectorInfoByName("NPCroom1"); if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name) == NULL) { Error1("NPCroom1 failed - Critical"); sectorFound = false; } else if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name)) { sectorFound = true; } else { sectorFound = false; } } else if(sectorinfo && EntityManager::GetSingleton().FindSector(sectorinfo->name)) { sectorFound = true; } else { sectorFound = false; } if(!sectorFound) { Error2("Unresolvable starting sector='%s'", sectorname); psCharRejectedMessage reject(me->clientnum); psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET); psserver->RemovePlayer(me->clientnum,"No starting Sector."); delete chardata; return; } chardata->SetLocationInWorld(newinstance, sectorinfo, x, y, z, yrot); psTrait* trait; // CPrintf(CON_DEBUG, "Trait: %d\n", upload.selectedFace ); trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedFace); if(trait) chardata->SetTraitForLocation(trait->location, trait); trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairStyle); if(trait) chardata->SetTraitForLocation(trait->location, trait); trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedBeardStyle); if(trait) chardata->SetTraitForLocation(trait->location, trait); trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedHairColour); if(trait) chardata->SetTraitForLocation(trait->location, trait); trait = psserver->GetCacheManager()->GetTraitByID(upload.selectedSkinColour); if(trait) chardata->SetTraitForLocation(trait->location, trait); gemActor* actor = new gemActor(gemSupervisor, cacheManager, entityManager, chardata, raceinfo->mesh_name, newinstance, EntityManager::GetSingleton().FindSector(sectorinfo->name), csVector3(x,y,z),yrot, client->GetClientNum()); actor->SetupCharData(); if(!upload.verify) { if(!psServer::CharacterLoader.NewCharacterData(acctID,chardata)) { Error1("Character could not be created."); psCharRejectedMessage reject(me->clientnum); psserver->GetEventManager()->Broadcast(reject.msg, NetBase::BC_FINALPACKET); psserver->RemovePlayer(me->clientnum,"Your character could not be created in the database."); delete chardata; return; } } // Check to see if a path name was set. If so we will use that to generate // the character starting stats and skills. if(upload.path != "None") { // Progression Event name is PATH_PathName csString name("PATH_"); name.Append(upload.path); ProgressionScript* script = psserver->GetProgressionManager()->FindScript(name.GetData()); if(script) { // The script uses the race base character points to calculate starting stats. MathEnvironment env; env.Define("CharPoints", raceinfo->initialCP); env.Define("Actor", actor); script->Run(&env); } } else { //int cpUsage = psserver->charCreationManager->CalculateCPChoice( upload.choices ) + // psserver->charCreationManager->CalculateCPLife(upload.lifeEvents ); for(size_t ci = 0; ci < upload.choices.GetSize(); ci++) { CharCreationManager::CreationChoice* choice = psserver->charCreationManager->FindChoice(upload.choices[ci]); if(choice) { csString name(psserver->charCreationManager->FindChoice(upload.choices[ci])->name.GetData()); Debug3(LOG_NEWCHAR, me->clientnum,"Choice: %s Creation Script: %s", name.GetData(), choice->eventScript.GetData()); MathEnvironment env; env.Define("Actor", actor); if(choice->choiceArea == FATHER_JOB || choice->choiceArea == MOTHER_JOB) { int modifier = (choice->choiceArea == FATHER_JOB) ? upload.fatherMod : upload.motherMod; if(modifier > 3 || modifier < 1) modifier = 1; env.Define("ParentStatus", modifier); } ProgressionScript* script = psserver->GetProgressionManager()->FindScript(choice->eventScript); if(script) script->Run(&env); } else { Debug2(LOG_NEWCHAR, me->clientnum,"Character Choice %d not found\n", upload.choices[ci]); } } for(size_t li = 0; li < upload.lifeEvents.GetSize(); li++) { MathEnvironment env; env.Define("Actor", actor); LifeEventChoiceServer* lifeEvent = psserver->charCreationManager->FindLifeEvent(upload.lifeEvents[li]); if(!lifeEvent) { Error2("No LifeEvent Script found: %d", upload.lifeEvents[li]); continue; } csString scriptName(lifeEvent->eventScript.GetData()); Debug2(LOG_NEWCHAR, me->clientnum, "LifeEvent Script: %s", scriptName.GetDataSafe()); ProgressionScript* script = psserver->GetProgressionManager()->FindScript(scriptName); if(script) script->Run(&env); } } if(!upload.verify) { if(reservedName == NAME_RESERVED_FOR_YOU) { AssignScript(chardata); } // This function recalculates the Max HP, Mana and Stamina of the new character chardata->RecalculateStats(); // Make sure the new player have HP, Mana and Samina that was calculated chardata->SetHitPoints(chardata->GetMaxHP().Base()); chardata->SetMana(chardata->GetMaxMana().Base()); chardata->SetStamina(chardata->GetMaxPStamina().Base(),true); chardata->SetStamina(chardata->GetMaxMStamina().Base(),false); psServer::CharacterLoader.SaveCharacterData(chardata, actor); Debug1(LOG_NEWCHAR,me->clientnum,"Player Creation Complete"); // Remove cached objects to make sure that the client gets a fresh character // list from the database if it logs out and in within 2 minutes. iCachedObject* obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("list",client->GetAccountID().Unbox())); if(obj) { obj->ProcessCacheTimeout(); obj->DeleteSelf(); } obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("auth",client->GetAccountID().Unbox())); if(obj) { obj->ProcessCacheTimeout(); obj->DeleteSelf(); } // Here everything is ok client->SetPID(chardata->GetPID()); client->SetName(playerName); psCharApprovedMessage app(me->clientnum); if(app.valid) psserver->GetEventManager()->SendMessage(app.msg); else Bug2("Could not create valid psCharApprovedMessage for client %u.\n",me->clientnum); } else { psCharVerificationMesg mesg(me->clientnum); size_t z; //unfortunately count goes out of valid area so we need to check on charisma for(z = 0; z < psserver->GetCacheManager()->GetSkillAmount(); z++) { unsigned int rank = chardata->Skills().GetSkillRank((PSSKILL) z).Base(); psSkillInfo* info = psserver->GetCacheManager()->GetSkillByID(z); csString name("Not found"); if(info) name.Replace(info->name); if(rank > 0) { if(z >= PSSKILL_AGI && z <= PSSKILL_WILL) { mesg.AddStat(rank, name); } else { mesg.AddSkill(rank, name); } } } mesg.Construct(); mesg.SendMessage(); } delete actor; if(!upload.verify) { // Remove char data from the cache iCachedObject* obj = psserver->GetCacheManager()->RemoveFromCache(psserver->GetCacheManager()->MakeCacheName("char", chardata->GetPID().Unbox())); if(obj) { obj->ProcessCacheTimeout(); obj->DeleteSelf(); } } }
// This function is called from the network thread, no acces to server // internal data should be made void NetManager::CheckLinkDead() { csTicks currenttime = csGetTicks(); csArray<uint32_t> checkedClients; Client *pClient = NULL; // Delete all clients marked for deletion already clients.SweepDelete(); while(true) { pClient = NULL; // Put the iterator in a limited scope so we don't hold on to the lock which may cause deadlock { ClientIterator i(clients); while(i.HasNext()) { Client *candidate = i.Next(); // Skip if already seen if(checkedClients.FindSortedKey(csArrayCmp<uint32_t, uint32_t> (candidate->GetClientNum())) != csArrayItemNotFound) continue; checkedClients.InsertSorted(candidate->GetClientNum()); pClient = candidate; break; } } // No more clients to check so break if(!pClient) break; // Shortcut here so zombies may immediately disconnect if(pClient->IsZombie() && pClient->ZombieAllowDisconnect()) { /* This simulates receipt of this message from the client ** without any network access, so that disconnection logic ** is all in one place. */ psDisconnectMessage discon(pClient->GetClientNum(), 0, "You should not see this."); if (discon.valid) { Connection* connection = pClient->GetConnection(); HandleCompletedMessage(discon.msg, connection, NULL,NULL); } else { Bug2("Failed to create valid psDisconnectMessage for client id %u.\n", pClient->GetClientNum()); } } else if (pClient->GetConnection()->lastRecvPacketTime+timeout < currenttime) { if (pClient->GetConnection()->heartbeat < 10 && pClient->GetConnection()->lastRecvPacketTime+timeout * 10 > currenttime) { psHeartBeatMsg ping(pClient->GetClientNum()); Broadcast(ping.msg, NetBase::BC_FINALPACKET); pClient->GetConnection()->heartbeat++; } else { if(!pClient->AllowDisconnect()) continue; char ipaddr[20]; pClient->GetIPAddress(ipaddr); csString status; status.Format("%s, %u, Client (%s) went linkdead.", ipaddr, pClient->GetClientNum(), pClient->GetName()); psserver->GetLogCSV()->Write(CSV_AUTHENT, status); /* This simulates receipt of this message from the client ** without any network access, so that disconnection logic ** is all in one place. */ psDisconnectMessage discon(pClient->GetClientNum(), 0, "You are linkdead."); if (discon.valid) { Connection* connection = pClient->GetConnection(); HandleCompletedMessage(discon.msg, connection, NULL,NULL); } else { Bug2("Failed to create valid psDisconnectMessage for client id %u.\n", pClient->GetClientNum()); } } } } }