int CharCreationManager::IsReserved(const char* name, AccountID acctID) { // Check to see if this name is reserved. Does this check by comparing // the email address of the account with that stored in the migration table. csString query; csString escape; db->Escape( escape, name ); query.Format( "SELECT m.email FROM migration m WHERE m.username='******'", escape.GetData() ); Result result (db->Select( query ) ); if ( result.IsValid() && result.Count() == 1 ) { csString savedEmail( result[0][0] ); query.Format("SELECT username FROM accounts WHERE id=%d\n", acctID.Unbox()); Result result2(db->Select( query ) ); if ( result2.IsValid() && result2.Count() == 1 ) { csString email( result2[0][0] ); if ( savedEmail.CompareNoCase(email) ) return NAME_RESERVED_FOR_YOU; else return NAME_RESERVED; } } return NAME_AVAILABLE; }
bool psNPCLoader::WriteSpecificKnowledge(int questid) { csRef<iDocumentNode> specificsNode = npcRoot->CreateNodeBefore(CS_NODE_ELEMENT); specificsNode->SetValue("specificknowledge"); Result result; if(questid == -1) result = db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=0 and area=\"%s\" and quest_id<1 " ,area.GetData()); else result = db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=0 and quest_id=%i " ,questid); if(result.IsValid()) { for(int i=0; i < (int)result.Count(); i++) { csString res(result[i][0]); if(!WriteTrigger(specificsNode, res, 0, questid)) return false; } } return true; }
bool CharCreationManager::IsUnique( const char* name , bool dbUniqueness) { csString escape; db->Escape( escape, name ); // Check to see if name already exists in character database. // Querying the DB is slow, but I don't see another way to do it csString query; query.Format( "Select id from characters where name='%s'", escape.GetData() ); Result result (db->Select( query ) ); //if dbUniqueness is true we will check result.Count() for > 1 else > 0 (like >= 1) return ! ( result.IsValid() && (result.Count() > (unsigned long)(dbUniqueness ? 1:0)) ); }
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); csString scriptName(psserver->charCreationManager->FindLifeEvent(upload.lifeEvents[li])->eventScript.GetData()); Debug2(LOG_NEWCHAR, me->clientnum, "LifeEvent Script: %s", scriptName.GetData()); 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 <= PSITEMSTATS_STAT_CHARISMA; z++ ) { int value = chardata->GetSkillRank(statToSkill((PSITEMSTATS_STAT) z)).Current(); if ( value > 0 ) { mesg.AddStat( value, psserver->GetCacheManager()->Attribute2String((PSITEMSTATS_STAT)z)); } } 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 ) { 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(); } } }
void CharCreationManager::HandleName( MsgEntry* me, Client *client ) { psNameCheckMessage name; name.FromClient( me ); /// Check in migration/reserve table csString query; csString escape; if ( name.firstName.Length() == 0 ) { psNameCheckMessage response( me->clientnum, false, "Cannot have an empty first name!"); response.SendMessage(); return; } if (name.firstName.Length() > MAX_PLAYER_NAME_LENGTH) { psNameCheckMessage response(me->clientnum, false, "First name is too long!"); response.SendMessage(); return; } if (name.lastName.Length() > MAX_PLAYER_NAME_LENGTH) { psNameCheckMessage response(me->clientnum, false, "Last name is too long!"); response.SendMessage(); return; } db->Escape( escape, name.firstName ); query.Format( "SELECT * FROM migration m WHERE m.username='******'", escape.GetData() ); Result result (db->Select( query ) ); if ( result.IsValid() && result.Count() == 1 ) { int reservedName = IsReserved(name.firstName, client->GetAccountID()); if ( reservedName == NAME_RESERVED ) { psNameCheckMessage response( me->clientnum, false, "Name is in reserved database"); response.SendMessage(); return; } } // Check uniqueness of the names if(!IsUnique(name.firstName)) { psNameCheckMessage response( me->clientnum, false, "First name is already in use"); response.SendMessage(); return; } if(!IsLastNameAvailable(name.lastName, client->GetAccountID())) { psNameCheckMessage response( me->clientnum, false, "Last name is already in use"); response.SendMessage(); return; } csString playerName = name.firstName; csString lastName = name.lastName; // Check if the name is banned if(psserver->GetCharManager()->IsBanned(playerName)) { csString error; error.Format("The name %s is banned", playerName.GetData() ); psNameCheckMessage reject(me->clientnum, false, error ); reject.SendMessage(); return; } if(psserver->GetCharManager()->IsBanned(lastName)) { csString error; error.Format("The lastname %s is banned", lastName.GetData() ); psNameCheckMessage reject(me->clientnum, false, error ); reject.SendMessage(); return; } psNameCheckMessage response( me->clientnum, true, "Ok"); response.SendMessage(); }
bool psNPCLoader::WriteTrigger(csRef<iDocumentNode> specificsNode, csString &trigger,int priorID, int questID) { // <trigger> // <phrase value=".."> // <phrase value=".."> // <attitude min="0" max="100"> // <response say=".. "/> // <trigger> // .. // </trigger> // </attitude> // </trigger> Result result; Result phrases; csString escArea; csString escTrigger; db->Escape(escArea, area); db->Escape(escTrigger, trigger); if(questID == -1) { result = db->Select("SELECT response_id, " " min_attitude_required, " " max_attitude_required " "FROM npc_triggers " "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and area=\"%s\" and quest_id<1;", priorID, escTrigger.GetData(), escArea.GetData()); // all triggers that have the same response id will be grouped together // and written to the xml file as <phrase value=".."> phrases = db->Select("SELECT id, trigger " "FROM npc_triggers " "WHERE response_id=\"%i\" and area=\"%s\" and quest_id<1;", result[0].GetInt(0), escArea.GetData()); } else { result = db->Select("SELECT response_id, " " min_attitude_required, " " max_attitude_required " "FROM npc_triggers " "WHERE prior_response_required=\"%i\" and trigger=\"%s\" and quest_id=\"%i\";", priorID, escTrigger.GetData(), questID); // all triggers that have the same response id will be grouped together // and written to the xml file as <phrase value=".."> phrases = db->Select("SELECT id, trigger, area " "FROM npc_triggers " "WHERE response_id=\"%i\" and quest_id=\"%i\";", result[0].GetInt(0), questID); } csString temp; csRef<iDocumentNode> triggerNode; for(int i=0; i< (int)phrases.Count(); i++) { int triggerID = phrases[i].GetInt(0); bool duplicate=false; for(size_t j=0; j<triggers.GetSize(); j++) { if(triggerID==triggers[j]) duplicate=true; } // if the trigger hasn't already been added if(!duplicate) { triggers.Push(triggerID); if(!triggerNode) { triggerNode = specificsNode->CreateNodeBefore(CS_NODE_ELEMENT); triggerNode->SetValue("trigger"); csString area(phrases[0][2]); if(this->area.IsEmpty() && !area.IsEmpty()) triggerNode->SetAttribute("area", area); } csRef<iDocumentNode> phraseNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT); phraseNode->SetValue("phrase"); phraseNode->SetAttribute("value", phrases[i][1]); } } if(triggerNode) { for(int i=0; i<(int)result.Count(); i++) { int responseID = result[i].GetInt(0); int minAttitude = result[i].GetInt(1); int maxAttitude = result[i].GetInt(2); csRef<iDocumentNode> attitudeNode = triggerNode->CreateNodeBefore(CS_NODE_ELEMENT); attitudeNode->SetValue("attitude"); attitudeNode->SetAttributeAsInt("min", minAttitude); attitudeNode->SetAttributeAsInt("max", maxAttitude); if(!WriteResponse(attitudeNode, responseID, questID)) return false; // check if this trigger contains other triggers Result childResult(db->Select("SELECT DISTINCT trigger " "FROM npc_triggers " "WHERE prior_response_required=%i;" ,responseID)); if(childResult.IsValid()) { for(int j=0; j<(int)childResult.Count(); j++) { csString res(childResult[j][0]); if(!WriteTrigger(attitudeNode, res, responseID, questID)) return false; } } } } return true; }