bool CharCreationManager::IsLastNameAvailable(const char* lastname, AccountID requestingAcct) { if(!lastname || strlen(lastname) < 1) return true; // blank last names are allowed. // Check to see if name already exists in character database. csString query; csString escape; db->Escape(escape, lastname); query.Format("SELECT account_id FROM characters WHERE lastname='%s'", escape.GetData()); Result result(db->Select(query)); if(result.IsValid()) { if(result.Count() == 0) return true; // nobody owns it yet, it's available if(requestingAcct.IsValid()) { for(unsigned int i = 0; i < result.Count(); i++) { if(AccountID(result[i].GetInt("account_id")) == requestingAcct) return true; // another character on the same account; available } } } return false; // already in use by someone else }
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(); } } }