diff --git a/world/client.cpp b/world/client.cpp index 055126d10..5f47d36eb 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -368,15 +368,560 @@ void Client::SendPostEnterWorld() { safe_delete(outapp); } +bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { + if (app->size != sizeof(LoginInfo_Struct)) { + return false; + } + + LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; + + // Quagmire - max len for name is 18, pass 15 + char name[19] = {0}; + char password[16] = {0}; + strn0cpy(name, (char*)li->login_info,18); + strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); + + if (strlen(password) <= 1) { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Login without a password"); + return false; + } + + pZoning=(li->zoning==1); + +#ifdef IPBASED_AUTH_HACK + struct in_addr tmpip; + tmpip.s_addr = ip; +#endif + uint32 id=0; + bool minilogin = loginserverlist.MiniLogin(); + if(minilogin){ + struct in_addr miniip; + miniip.s_addr = ip; + id = database.GetMiniLoginAccount(inet_ntoa(miniip)); + } + else if(strncasecmp(name, "LS#", 3) == 0) + id=atoi(&name[3]); + else + id=atoi(name); +#ifdef IPBASED_AUTH_HACK + if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password))) +#else + if (loginserverlist.Connected() == false && !pZoning) { + clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server."); + return false; + } + if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password))) +#endif + { + if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { + clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?"); + if(!minilogin) + clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable..."); + else + clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); + return false; + } + + cle->SetOnline(); + + clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)"); + + if(minilogin){ + WorldConfig::DisableStats(); + clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID()); + } + else { + clog(WORLD__CLIENT,"LS Account #%d",cle->LSID()); + } + + const WorldConfig *Config=WorldConfig::get(); + + if(Config->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSPlayerJoinWorld; + pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer; + strcpy(join->key,GetLSKey()); + join->lsaccount_id = GetLSID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } + + if (!pZoning) + SendGuildList(); + SendLogServer(); + SendApproveWorld(); + SendEnterWorld(cle->name()); + SendPostEnterWorld(); + if (!pZoning) { + SendExpansionInfo(); + SendCharInfo(); + database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); + } + + } + else { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name); + return false; + } + + if (!cle) + return true; + + cle->SetIP(GetIP()); + return true; +} + +bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { + + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); + return false; + } + + snprintf(char_name, 64, "%s", (char*)app->pBuffer); + uchar race = app->pBuffer[64]; + uchar clas = app->pBuffer[68]; + + clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket; + outapp->SetOpcode(OP_ApproveName); + outapp->pBuffer = new uchar[1]; + outapp->size = 1; + + bool valid; + if(!database.CheckNameFilter(char_name)) { + valid = false; + } + else if(char_name[0] < 'A' && char_name[0] > 'Z') { + //name must begin with an upper-case letter. + valid = false; + } + else if (database.ReserveName(GetAccountID(), char_name)) { + valid = true; + } + else { + valid = false; + } + outapp->pBuffer[0] = valid? 1 : 0; + QueuePacket(outapp); + safe_delete(outapp); + + if(!valid) { + memset(char_name, 0, sizeof(char_name)); + } + + return true; +} + +bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { + // creates up to a 10 char name + char vowels[18]="aeiouyaeiouaeioe"; + char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; + char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; + int rndnum=MakeRandomInt(0, 75),n=1; + bool dlc=false; + bool vwl=false; + bool dbl=false; + if (rndnum>63) + { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel + rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" + rndname[0]=paircons[rndnum]; + rndname[1]=paircons[rndnum+1]; + n=2; + } + else if (rndnum>16) + { + rndnum-=17; + rndname[0]=cons[rndnum]; + } + else + { + rndname[0]=vowels[rndnum]; + vwl=true; + } + int namlen=MakeRandomInt(5, 10); + for (int i=n;i46) + { // pick a cons pair + if (i>namlen-3) // last 2 chars in name? + { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" + rndnum=MakeRandomInt(0, 7)*2; + } + else + { // pick any from the set + rndnum=(rndnum-47)*2; + } + rndname[i]=paircons[rndnum]; + rndname[i+1]=paircons[rndnum+1]; + dlc=true; // flag keeps second letter from being doubled below + i+=1; + } + else + { // select a single cons + rndname[i]=cons[rndnum]; + } + } + else + { // select a vowel + rndname[i]=vowels[MakeRandomInt(0, 16)]; + } + vwl=!vwl; + if (!dbl && !dlc) + { // one chance at double letters in name + if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name + { + rndname[i+1]=rndname[i]; + dbl=true; + i+=1; + } + } + } + + rndname[0]=toupper(rndname[0]); + NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; + memset(ngs->name,0,64); + strcpy(ngs->name,rndname); + + QueuePacket(app); + return true; +} + +bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) { + // New OpCode in SoF + uint32 allocs = character_create_allocations.size(); + uint32 combos = character_create_race_class_combos.size(); + uint32 len = sizeof(RaceClassAllocation) * allocs; + len += sizeof(RaceClassCombos) * combos; + len += sizeof(uint8); + len += sizeof(uint32); + len += sizeof(uint32); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len); + unsigned char *ptr = outapp->pBuffer; + *((uint8*)ptr) = 0; + ptr += sizeof(uint8); + + *((uint32*)ptr) = allocs; + ptr += sizeof(uint32); + + for(int i = 0; i < allocs; ++i) { + RaceClassAllocation *alc = (RaceClassAllocation*)ptr; + + alc->Index = character_create_allocations[i].Index; + for(int j = 0; j < 7; ++j) { + alc->BaseStats[j] = character_create_allocations[i].BaseStats[j]; + alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j]; + } + ptr += sizeof(RaceClassAllocation); + } + + *((uint32*)ptr) = combos; + ptr += sizeof(uint32); + for(int i = 0; i < combos; ++i) { + RaceClassCombos *cmb = (RaceClassCombos*)ptr; + cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired; + cmb->Race = character_create_race_class_combos[i].Race; + cmb->Class = character_create_race_class_combos[i].Class; + cmb->Deity = character_create_race_class_combos[i].Deity; + cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex; + cmb->Zone = character_create_race_class_combos[i].Zone; + ptr += sizeof(RaceClassCombos); + } + + QueuePacket(outapp); + safe_delete(outapp); + return true; +} + +bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { + if (GetAccountID() == 0) + { + clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); + return false; + } + else if (app->size != sizeof(CharCreate_Struct)) + { + clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); + DumpPacket(app); + // the previous behavior was essentially returning true here + // but that seems a bit odd to me. + return true; + } + + CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; + if(OPCharCreate(char_name, cc) == false) + { + database.DeleteCharacter(char_name); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); + outapp->pBuffer[0] = 0; + QueuePacket(outapp); + safe_delete(outapp); + } + else + SendCharInfo(); + + return true; +} + +bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { + + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); + eqs->Close(); + return true; + } + + if(GetAdmin() < 0) + { + clog(WORLD__CLIENT,"Account banned or suspended."); + eqs->Close(); + return true; + } + + if (RuleI(World, MaxClientsPerIP) >= 0) { + client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection + } + + EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; + strn0cpy(char_name, ew->name, 64); + + EQApplicationPacket *outapp; + uint32 tmpaccid = 0; + charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID); + if (charid == 0 || tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name); + eqs->Close(); + return true; + } + + // Make sure this account owns this character + if (tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name); + eqs->Close(); + return true; + } + + if(!pZoning && ew->return_home) + { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool home_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->gohome[x] == 1) + { + home_enabled = true; + return true; + } + } + } + safe_delete(cs); + + if(home_enabled) + { + zoneID = database.MoveCharacterToBind(charid,4); + } + else + { + clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name); + database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); + eqs->Close(); + return true; + } + } + + if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool tutorial_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->tutorial[x] == 1) + { + tutorial_enabled = true; + return true; + } + } + } + safe_delete(cs); + + if(tutorial_enabled) + { + zoneID = RuleI(World, TutorialZoneID); + database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); + } + else + { + clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name); + database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); + eqs->Close(); + return true; + } + } + + if (zoneID == 0 || !database.GetZoneName(zoneID)) { + // This is to save people in an invalid zone, once it's removed from the DB + database.MoveCharacterToZone(charid, "arena"); + clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name); + } + + if(instanceID > 0) + { + if(!database.VerifyInstanceAlive(instanceID, GetCharID())) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + else + { + if(!database.VerifyZoneInstance(zoneID, instanceID)) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + } + } + + if(!pZoning) { + database.SetGroupID(char_name, 0, charid); + database.SetLoginFlags(charid, false, false, 1); + } + else{ + uint32 groupid=database.GetGroupID(char_name); + if(groupid>0){ + char* leader=0; + char leaderbuf[64]={0}; + if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){ + EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; + gj->action=8; + strcpy(gj->yourname,char_name); + strcpy(gj->membername,leader); + QueuePacket(outapp3); + safe_delete(outapp3); + } + } + } + + outapp = new EQApplicationPacket(OP_MOTD); + char tmp[500] = {0}; + if (database.GetVariable("MOTD", tmp, 500)) { + outapp->size = strlen(tmp)+1; + outapp->pBuffer = new uchar[outapp->size]; + memset(outapp->pBuffer,0,outapp->size); + strcpy((char*)outapp->pBuffer, tmp); + + } else { + // Null Message of the Day. :) + outapp->size = 1; + outapp->pBuffer = new uchar[outapp->size]; + outapp->pBuffer[0] = 0; + } + QueuePacket(outapp); + safe_delete(outapp); + + int MailKey = MakeRandomInt(1, INT_MAX); + + database.SetMailKey(charid, GetIP(), MailKey); + + char ConnectionType; + + if(ClientVersionBit & BIT_UnderfootAndLater) + ConnectionType = 'U'; + else if(ClientVersionBit & BIT_SoFAndLater) + ConnectionType = 'S'; + else + ConnectionType = 'C'; + + EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer); + char buffer[112]; + + const WorldConfig *Config = WorldConfig::get(); + + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + outapp2 = new EQApplicationPacket(OP_SetChatServer2); + + if(ClientVersionBit & BIT_TitaniumAndEarlier) + ConnectionType = 'M'; + + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + EnterWorld(); + + return true; +} + +bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { + + uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); + if(char_acct_id == GetAccountID()) + { + clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); + database.DeleteCharacter((char *)app->pBuffer); + SendCharInfo(); + } + + return true; +} + +bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) { + // HoT sends this to world while zoning and wants it echoed back. + if(ClientVersionBit & BIT_RoFAndLater) + { + QueuePacket(app); + } + return true; +} + bool Client::HandlePacket(const EQApplicationPacket *app) { - const WorldConfig *Config=WorldConfig::get(); + EmuOpcode opcode = app->GetOpcode(); clog(WORLD__CLIENT_TRACE,"Recevied EQApplicationPacket"); _pkt(WORLD__CLIENT_TRACE,app); - bool ret = true; - if (!eqs->CheckState(ESTABLISHED)) { clog(WORLD__CLIENT,"Client disconnected (net inactive on send)"); return false; @@ -401,586 +946,75 @@ bool Client::HandlePacket(const EQApplicationPacket *app) { switch(opcode) { - case OP_CrashDump: - break; - case OP_SendLoginInfo: - { - if (app->size != sizeof(LoginInfo_Struct)) { - ret = false; - break; - } - - LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; - - // Quagmire - max len for name is 18, pass 15 - char name[19] = {0}; - char password[16] = {0}; - strn0cpy(name, (char*)li->login_info,18); - strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); - - if (strlen(password) <= 1) { - // TODO: Find out how to tell the client wrong username/password - clog(WORLD__CLIENT_ERR,"Login without a password"); - ret = false; - break; - } - - pZoning=(li->zoning==1); - -#ifdef IPBASED_AUTH_HACK - struct in_addr tmpip; - tmpip.s_addr = ip; -#endif - uint32 id=0; - bool minilogin = loginserverlist.MiniLogin(); - if(minilogin){ - struct in_addr miniip; - miniip.s_addr = ip; - id = database.GetMiniLoginAccount(inet_ntoa(miniip)); - } - else if(strncasecmp(name, "LS#", 3) == 0) - id=atoi(&name[3]); - else - id=atoi(name); -#ifdef IPBASED_AUTH_HACK - if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password))) -#else - if (loginserverlist.Connected() == false && !pZoning) { - clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server."); - ret = false; - break; - } - if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password))) -#endif - { - if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { - clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?"); - if(!minilogin) - clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable..."); - else - clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); - ret = false; - break; - } - - cle->SetOnline(); - - clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)"); - - if(minilogin){ - WorldConfig::DisableStats(); - clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID()); - } - else { - clog(WORLD__CLIENT,"LS Account #%d",cle->LSID()); - } - if(Config->UpdateStats){ - ServerPacket* pack = new ServerPacket; - pack->opcode = ServerOP_LSPlayerJoinWorld; - pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); - pack->pBuffer = new uchar[pack->size]; - memset(pack->pBuffer,0,pack->size); - ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer; - strcpy(join->key,GetLSKey()); - join->lsaccount_id = GetLSID(); - loginserverlist.SendPacket(pack); - safe_delete(pack); - } - - if (!pZoning) - SendGuildList(); - SendLogServer(); - SendApproveWorld(); - SendEnterWorld(cle->name()); - SendPostEnterWorld(); - if (!pZoning) { - SendExpansionInfo(); - SendCharInfo(); - database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); - } - - } - else { - // TODO: Find out how to tell the client wrong username/password - clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name); - ret = false; - break; - } - - if (!cle) - break; - cle->SetIP(GetIP()); - break; - } - case OP_ApproveName: //Name approval - { - if (GetAccountID() == 0) { - clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); - ret = false; - break; - } - snprintf(char_name, 64, "%s", (char*)app->pBuffer); - uchar race = app->pBuffer[64]; - uchar clas = app->pBuffer[68]; - - clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket; - outapp->SetOpcode(OP_ApproveName); - outapp->pBuffer = new uchar[1]; - outapp->size = 1; - bool valid; - if(!database.CheckNameFilter(char_name)) { - valid = false; - } - else if(char_name[0] < 'A' && char_name[0] > 'Z') { - //name must begin with an upper-case letter. - valid = false; - } - else if (database.ReserveName(GetAccountID(), char_name)) { - valid = true; - } - else { - valid = false; - } - outapp->pBuffer[0] = valid? 1 : 0; - QueuePacket(outapp); - safe_delete(outapp); - - if(!valid) { - memset(char_name, 0, sizeof(char_name)); - } - - break; - } - case OP_RandomNameGenerator: - { - // creates up to a 10 char name - char vowels[18]="aeiouyaeiouaeioe"; - char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; - char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; - int rndnum=MakeRandomInt(0, 75),n=1; - bool dlc=false; - bool vwl=false; - bool dbl=false; - if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" - rndname[0]=paircons[rndnum]; - rndname[1]=paircons[rndnum+1]; - n=2; - } - else if (rndnum>16) - { - rndnum-=17; - rndname[0]=cons[rndnum]; - } - else - { - rndname[0]=vowels[rndnum]; - vwl=true; - } - int namlen=MakeRandomInt(5, 10); - for (int i=n;i46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" - rndnum=MakeRandomInt(0, 7)*2; - } - else - { // pick any from the set - rndnum=(rndnum-47)*2; - } - rndname[i]=paircons[rndnum]; - rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below - i+=1; - } - else - { // select a single cons - rndname[i]=cons[rndnum]; - } - } - else - { // select a vowel - rndname[i]=vowels[MakeRandomInt(0, 16)]; - } - vwl=!vwl; - if (!dbl && !dlc) - { // one chance at double letters in name - if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name - { - rndname[i+1]=rndname[i]; - dbl=true; - i+=1; - } - } - } - - rndname[0]=toupper(rndname[0]); - NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; - memset(ngs->name,0,64); - strcpy(ngs->name,rndname); - -// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" }; -// //Could have parts of the random name in this struct and they compile together -// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; -// strncpy(ngs->name,"Notcreated",64); - - QueuePacket(app); - break; - - } - case OP_CharacterCreateRequest: { - // New OpCode in SoF - uint32 allocs = character_create_allocations.size(); - uint32 combos = character_create_race_class_combos.size(); - uint32 len = sizeof(RaceClassAllocation) * allocs; - len += sizeof(RaceClassCombos) * combos; - len += sizeof(uint8); - len += sizeof(uint32); - len += sizeof(uint32); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len); - unsigned char *ptr = outapp->pBuffer; - *((uint8*)ptr) = 0; - ptr += sizeof(uint8); - - *((uint32*)ptr) = allocs; - ptr += sizeof(uint32); - - for(int i = 0; i < allocs; ++i) { - RaceClassAllocation *alc = (RaceClassAllocation*)ptr; - - alc->Index = character_create_allocations[i].Index; - for(int j = 0; j < 7; ++j) { - alc->BaseStats[j] = character_create_allocations[i].BaseStats[j]; - alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j]; - } - ptr += sizeof(RaceClassAllocation); - } - - *((uint32*)ptr) = combos; - ptr += sizeof(uint32); - for(int i = 0; i < combos; ++i) { - RaceClassCombos *cmb = (RaceClassCombos*)ptr; - cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired; - cmb->Race = character_create_race_class_combos[i].Race; - cmb->Class = character_create_race_class_combos[i].Class; - cmb->Deity = character_create_race_class_combos[i].Deity; - cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex; - cmb->Zone = character_create_race_class_combos[i].Zone; - ptr += sizeof(RaceClassCombos); - } - - QueuePacket(outapp); - safe_delete(outapp); - break; - } - - case OP_CharacterCreate: //Char create - { - if (GetAccountID() == 0) - { - clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); - ret = false; - break; - } - else if (app->size != sizeof(CharCreate_Struct)) - { - clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); - DumpPacket(app); - break; - } - - CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; - if(OPCharCreate(char_name, cc) == false) - { - database.DeleteCharacter(char_name); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); - outapp->pBuffer[0] = 0; - QueuePacket(outapp); - safe_delete(outapp); - } - else - SendCharInfo(); - break; - } - case OP_EnterWorld: // Enter world - { - if (GetAccountID() == 0) { - clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); - eqs->Close(); - break; - } - if(GetAdmin() < 0) - { - clog(WORLD__CLIENT,"Account banned or suspended."); - eqs->Close(); - break; - } - - if (RuleI(World, MaxClientsPerIP) >= 0) { - client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection - } - - EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; - strn0cpy(char_name, ew->name, 64); - - EQApplicationPacket *outapp; - uint32 tmpaccid = 0; - charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID); - if (charid == 0 || tmpaccid != GetAccountID()) { - clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name); - eqs->Close(); - break; - } - - // Make sure this account owns this character - if (tmpaccid != GetAccountID()) { - clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name); - eqs->Close(); - break; - } - - if(!pZoning && ew->return_home) - { - CharacterSelect_Struct* cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); - bool home_enabled = false; - - for(int x = 0; x < 10; ++x) - { - if(strcasecmp(cs->name[x], char_name) == 0) - { - if(cs->gohome[x] == 1) - { - home_enabled = true; - break; - } - } - } - safe_delete(cs); - - if(home_enabled) - { - zoneID = database.MoveCharacterToBind(charid,4); - } - else - { - clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name); - database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); - eqs->Close(); - break; - } - } - - if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { - CharacterSelect_Struct* cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); - bool tutorial_enabled = false; - - for(int x = 0; x < 10; ++x) - { - if(strcasecmp(cs->name[x], char_name) == 0) - { - if(cs->tutorial[x] == 1) - { - tutorial_enabled = true; - break; - } - } - } - safe_delete(cs); - - if(tutorial_enabled) - { - zoneID = RuleI(World, TutorialZoneID); - database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); - } - else - { - clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name); - database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); - eqs->Close(); - break; - } - } - - if (zoneID == 0 || !database.GetZoneName(zoneID)) { - // This is to save people in an invalid zone, once it's removed from the DB - database.MoveCharacterToZone(charid, "arena"); - clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name); - } - - if(instanceID > 0) - { - if(!database.VerifyInstanceAlive(instanceID, GetCharID())) - { - zoneID = database.MoveCharacterToBind(charid); - instanceID = 0; - } - else - { - if(!database.VerifyZoneInstance(zoneID, instanceID)) - { - zoneID = database.MoveCharacterToBind(charid); - instanceID = 0; - } - } - } - - if(!pZoning) { - database.SetGroupID(char_name, 0, charid); - database.SetLoginFlags(charid, false, false, 1); - } - else{ - uint32 groupid=database.GetGroupID(char_name); - if(groupid>0){ - char* leader=0; - char leaderbuf[64]={0}; - if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){ - EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; - gj->action=8; - strcpy(gj->yourname,char_name); - strcpy(gj->membername,leader); - QueuePacket(outapp3); - safe_delete(outapp3); - } - } - } - - outapp = new EQApplicationPacket(OP_MOTD); - char tmp[500] = {0}; - if (database.GetVariable("MOTD", tmp, 500)) { - outapp->size = strlen(tmp)+1; - outapp->pBuffer = new uchar[outapp->size]; - memset(outapp->pBuffer,0,outapp->size); - strcpy((char*)outapp->pBuffer, tmp); - - } else { - // Null Message of the Day. :) - outapp->size = 1; - outapp->pBuffer = new uchar[outapp->size]; - outapp->pBuffer[0] = 0; - } - QueuePacket(outapp); - safe_delete(outapp); - - int MailKey = MakeRandomInt(1, INT_MAX); - - database.SetMailKey(charid, GetIP(), MailKey); - - char ConnectionType; - - if(ClientVersionBit & BIT_UnderfootAndLater) - ConnectionType = 'U'; - else if(ClientVersionBit & BIT_SoFAndLater) - ConnectionType = 'S'; - else - ConnectionType = 'C'; - - EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer); - char buffer[112]; - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); - - outapp2 = new EQApplicationPacket(OP_SetChatServer2); - - if(ClientVersionBit & BIT_TitaniumAndEarlier) - ConnectionType = 'M'; - - sprintf(buffer,"%s,%i,%s.%s,%c%08X", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - this->GetCharName(), ConnectionType, MailKey - ); - outapp2->size=strlen(buffer)+1; - outapp2->pBuffer = new uchar[outapp2->size]; - memcpy(outapp2->pBuffer,buffer,outapp2->size); - QueuePacket(outapp2); - safe_delete(outapp2); - - EnterWorld(); - break; - } - case OP_LoginComplete:{ - break; - } - case OP_DeleteCharacter: { - uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); - if(char_acct_id == GetAccountID()) - { - clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); - database.DeleteCharacter((char *)app->pBuffer); - SendCharInfo(); - } - break; - } - case OP_ApproveWorld: - { - break; - } - case OP_WorldClientReady:{ - break; - } case OP_World_Client_CRC1: - case OP_World_Client_CRC2: { + case OP_World_Client_CRC2: + { // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, // then 'Start Tutorial' was not chosen. StartInTutorial = false; - break; + return true; } - case OP_WearChange: { // User has selected a different character - break; + case OP_SendLoginInfo: + { + return HandleSendLoginInfoPacket(app); } - case OP_WorldComplete: { + case OP_ApproveName: //Name approval + { + return HandleNameApprovalPacket(app); + } + case OP_RandomNameGenerator: + { + return HandleGenerateRandomNamePacket(app); + } + case OP_CharacterCreateRequest: + { + // New OpCode in SoF + return HandleCharacterCreateRequestPacket(app); + } + case OP_CharacterCreate: //Char create + { + return HandleCharacterCreatePacket(app); + } + case OP_EnterWorld: // Enter world + { + return HandleEnterWorldPacket(app); + } + case OP_DeleteCharacter: + { + return HandleDeleteCharacterPacket(app); + } + case OP_WorldComplete: + { eqs->Close(); - break; + return true; + } + case OP_ZoneChange: + { + // HoT sends this to world while zoning and wants it echoed back. + return HandleZoneChangePacket(app); } case OP_LoginUnknown1: case OP_LoginUnknown2: - break; - - case OP_ZoneChange: - // HoT sends this to world while zoning and wants it echoed back. - if(ClientVersionBit & BIT_RoFAndLater) - { - QueuePacket(app); - } - break; - - - default: { + case OP_CrashDump: + case OP_WearChange: + case OP_LoginComplete: + case OP_ApproveWorld: + case OP_WorldClientReady: + { + // Essentially we are just 'eating' these packets, indicating + // they are handled. + return true; + } + default: + { clog(WORLD__CLIENT_ERR,"Received unknown EQApplicationPacket"); _pkt(WORLD__CLIENT_ERR,app); - break; + return true; } } - return ret; + return true; } bool Client::Process() { diff --git a/world/client.h b/world/client.h index ea8f1a27c..877ec17a0 100644 --- a/world/client.h +++ b/world/client.h @@ -97,7 +97,17 @@ private: bool firstlogin; bool seencharsel; bool realfirstlogin; + bool HandlePacket(const EQApplicationPacket *app); + bool HandleNameApprovalPacket(const EQApplicationPacket *app); + bool HandleSendLoginInfoPacket(const EQApplicationPacket *app); + bool HandleGenerateRandomNamePacket(const EQApplicationPacket *app); + bool HandleCharacterCreateRequestPacket(const EQApplicationPacket *app); + bool HandleCharacterCreatePacket(const EQApplicationPacket *app); + bool HandleEnterWorldPacket(const EQApplicationPacket *app); + bool HandleDeleteCharacterPacket(const EQApplicationPacket *app); + bool HandleZoneChangePacket(const EQApplicationPacket *app); + EQStreamInterface* const eqs; };