diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 84da53f63..7828dd5cc 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -49,7 +49,6 @@ public: // database static const ClientVersion CHARACTER_CREATION_CLIENT = ClientVersion::RoF2; // adjust according to starting item placement and target client - // This value should be at least 8 or Titanium will have issues (tested at 6) static const size_t CHARACTER_CREATION_LIMIT = RoF2::consts::CHARACTER_CREATION_LIMIT; // inventory diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 0f1b63136..188214dad 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -158,32 +158,46 @@ struct CharSelectEquip Color_Struct Color; }; +struct CharacterSelectEntry_Struct +{ + char Name[64]; + uint8 Class; + uint32 Race; + uint8 Level; + uint8 ShroudClass; + uint32 ShroudRace; + uint16 Zone; + uint16 Instance; + uint8 Gender; + uint8 Face; + CharSelectEquip Equip[9]; + uint8 Unknown15; // Seen FF + uint8 Unknown19; // Seen FF + uint32 DrakkinTattoo; + uint32 DrakkinDetails; + uint32 Deity; + uint32 PrimaryIDFile; + uint32 SecondaryIDFile; + uint8 HairColor; + uint8 BeardColor; + uint8 EyeColor1; + uint8 EyeColor2; + uint8 HairStyle; + uint8 Beard; + uint8 Enabled; + uint8 Tutorial; // Seen 1 for new char or 0 for existing + uint32 DrakkinHeritage; + uint8 Unknown1; // Seen 0 + uint8 GoHome; // Seen 0 for new char and 1 for existing + uint32 LastLogin; + uint8 Unknown2; // Seen 0 +}; + struct CharacterSelect_Struct { - uint32 Race[10]; // Characters Race - uint8 BeardColor[10]; // Characters beard Color - uint8 HairStyle[10]; // Characters hair style - CharSelectEquip Equip[10][9]; - uint32 Secondary[10]; // Characters secondary IDFile number - uint32 DrakkinHeritage[10]; // added for SoF - uint32 DrakkinTattoo[10]; // added for SoF - uint32 DrakkinDetails[10]; // added for SoF - uint32 Deity[10]; // Characters Deity - uint8 GoHome[10]; // 1=Go Home available, 0=not - uint8 Tutorial[10]; // 1=Tutorial available, 0=not - uint8 Beard[10]; // Characters Beard Type - uint8 Unknown902[10]; // 10x ff - uint32 Primary[10]; // Characters primary IDFile number - uint8 HairColor[10]; // Characters Hair Color - uint8 Unknown0962[2]; // 2x 00 - uint32 Zone[10]; // Characters Current Zone - uint8 Class_[10]; // Characters Classes - uint8 Face[10]; // Characters Face Type - char Name[10][64]; // Characters Names - uint8 Gender[10]; // Characters Gender - uint8 EyeColor1[10]; // Characters Eye Color - uint8 EyeColor2[10]; // Characters Eye 2 Color - uint8 Level[10]; // Characters Levels + uint32 CharCount; //number of chars in this packet + uint32 TotalChars; //total number of chars allowed? + CharacterSelectEntry_Struct Entries[0]; }; /* diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 6e89f7d39..485dc4118 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2897,85 +2897,99 @@ namespace RoF ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; - int char_count; - int namelen = 0; - for (char_count = 0; char_count < 10; char_count++) { - if (emu->Name[char_count][0] == '\0') - break; - if (strcmp(emu->Name[char_count], "") == 0) - break; - namelen += strlen(emu->Name[char_count]); + FINISH_ENCODE(); + return; } - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr; - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + eq->CharCount = character_count; + //eq->TotalChars = emu->TotalChars; - eq->CharCount = char_count; - //eq->total_chars = 10; + //if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + // eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; - unsigned char *bufptr = (unsigned char *)eq->Entries; - int r; - for (r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - memcpy(eq2->Name, emu->Name[r], strlen(emu->Name[r]) + 1); + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char *eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(eq_cse->Name); + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Class = emu_cse->Class; + eq_cse->Race = emu_cse->Race; + eq_cse->Level = emu_cse->Level; + eq_cse->ShroudClass = emu_cse->ShroudClass; + eq_cse->ShroudRace = emu_cse->ShroudRace; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Instance = emu_cse->Instance; + eq_cse->Gender = emu_cse->Gender; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial; + eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HeroForgeModel; + eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Material2; + eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color; } - //adjust for name. - bufptr += strlen(emu->Name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Class_ = emu->Class_[r]; - eq2->Race = emu->Race[r]; - eq2->Level = emu->Level[r]; - eq2->Class_2 = emu->Class_[r]; - eq2->Race2 = emu->Race[r]; - eq2->Zone = emu->Zone[r]; - eq2->Instance = 0; - eq2->Gender = emu->Gender[r]; - eq2->Face = emu->Face[r]; - int k; - for (k = 0; k < _MaterialCount; k++) { - eq2->Equip[k].Material = emu->Equip[r][k].Material; - eq2->Equip[k].Unknown1 = emu->Equip[r][k].Unknown1; - eq2->Equip[k].EliteMaterial = emu->Equip[r][k].EliteMaterial; - eq2->Equip[k].HeroForgeModel = emu->Equip[r][k].HeroForgeModel; - eq2->Equip[k].Material2 = emu->Equip[r][k].Material2; - eq2->Equip[k].Color.Color = emu->Equip[r][k].Color.Color; - } - eq2->Unknown15 = 0xFF; - eq2->Uknown19 = 0xFF; - eq2->DrakkinTattoo = emu->DrakkinTattoo[r]; - eq2->DrakkinDetails = emu->DrakkinDetails[r]; - eq2->Deity = emu->Deity[r]; - eq2->Primary = emu->Primary[r]; - eq2->Secondary = emu->Secondary[r]; - eq2->HairColor = emu->HairColor[r]; - eq2->BeardColor = emu->BeardColor[r]; - eq2->EyeColor1 = emu->EyeColor1[r]; - eq2->EyeColor2 = emu->EyeColor2[r]; - eq2->HairStyle = emu->HairStyle[r]; - eq2->Beard = emu->Beard[r]; - eq2->CharEnabled = 1; - eq2->Tutorial = emu->Tutorial[r]; - eq2->DrakkinHeritage = emu->DrakkinHeritage[r]; - eq2->Unknown1 = 0; - eq2->GoHome = emu->GoHome[r]; - eq2->LastLogin = 1212696584; - eq2->Unknown2 = 0; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + + eq_cse->Unknown15 = emu_cse->Unknown15; + eq_cse->Unknown19 = emu_cse->Unknown19; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + eq_cse->Deity = emu_cse->Deity; + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Beard = emu_cse->Beard; + eq_cse->Enabled = emu_cse->Enabled; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->Unknown1 = emu_cse->Unknown1; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->LastLogin = emu_cse->LastLogin; + eq_cse->Unknown2 = emu_cse->Unknown2; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); } FINISH_ENCODE(); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 0215911e6..0ad31517e 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2981,85 +2981,99 @@ namespace RoF2 ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; - int char_count; - int namelen = 0; - for (char_count = 0; char_count < 10; char_count++) { - if (emu->Name[char_count][0] == '\0') - break; - if (strcmp(emu->Name[char_count], "") == 0) - break; - namelen += strlen(emu->Name[char_count]); + FINISH_ENCODE(); + return; } - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr; - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + eq->CharCount = character_count; + //eq->TotalChars = emu->TotalChars; - eq->CharCount = char_count; - //eq->total_chars = 10; + //if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + // eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; - unsigned char *bufptr = (unsigned char *)eq->Entries; - int r; - for (r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - memcpy(eq2->Name, emu->Name[r], strlen(emu->Name[r]) + 1); + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char *eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(eq_cse->Name); + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Class = emu_cse->Class; + eq_cse->Race = emu_cse->Race; + eq_cse->Level = emu_cse->Level; + eq_cse->ShroudClass = emu_cse->ShroudClass; + eq_cse->ShroudRace = emu_cse->ShroudRace; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Instance = emu_cse->Instance; + eq_cse->Gender = emu_cse->Gender; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial; + eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HeroForgeModel; + eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Material2; + eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color; } - //adjust for name. - bufptr += strlen(emu->Name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Class_ = emu->Class_[r]; - eq2->Race = emu->Race[r]; - eq2->Level = emu->Level[r]; - eq2->Class_2 = emu->Class_[r]; - eq2->Race2 = emu->Race[r]; - eq2->Zone = emu->Zone[r]; - eq2->Instance = 0; - eq2->Gender = emu->Gender[r]; - eq2->Face = emu->Face[r]; - int k; - for (k = 0; k < _MaterialCount; k++) { - eq2->Equip[k].Material = emu->Equip[r][k].Material; - eq2->Equip[k].Unknown1 = emu->Equip[r][k].Unknown1; - eq2->Equip[k].EliteMaterial = emu->Equip[r][k].EliteMaterial; - eq2->Equip[k].HeroForgeModel = emu->Equip[r][k].HeroForgeModel; - eq2->Equip[k].Material2 = emu->Equip[r][k].Material2; - eq2->Equip[k].Color.Color = emu->Equip[r][k].Color.Color; - } - eq2->Unknown15 = 0xFF; - eq2->Unknown19 = 0xFF; - eq2->DrakkinTattoo = emu->DrakkinTattoo[r]; - eq2->DrakkinDetails = emu->DrakkinDetails[r]; - eq2->Deity = emu->Deity[r]; - eq2->Primary = emu->Primary[r]; - eq2->Secondary = emu->Secondary[r]; - eq2->HairColor = emu->HairColor[r]; - eq2->BeardColor = emu->BeardColor[r]; - eq2->EyeColor1 = emu->EyeColor1[r]; - eq2->EyeColor2 = emu->EyeColor2[r]; - eq2->HairStyle = emu->HairStyle[r]; - eq2->Beard = emu->Beard[r]; - eq2->CharEnabled = 1; - eq2->Tutorial = emu->Tutorial[r]; - eq2->DrakkinHeritage = emu->DrakkinHeritage[r]; - eq2->Unknown1 = 0; - eq2->GoHome = emu->GoHome[r]; - eq2->LastLogin = 1212696584; - eq2->Unknown2 = 0; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + + eq_cse->Unknown15 = emu_cse->Unknown15; + eq_cse->Unknown19 = emu_cse->Unknown19; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + eq_cse->Deity = emu_cse->Deity; + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Beard = emu_cse->Beard; + eq_cse->Enabled = emu_cse->Enabled; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->Unknown1 = emu_cse->Unknown1; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->LastLogin = emu_cse->LastLogin; + eq_cse->Unknown2 = emu_cse->Unknown2; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); } FINISH_ENCODE(); diff --git a/common/patches/rof2_constants.h b/common/patches/rof2_constants.h index 750a6fb3e..4c8245737 100644 --- a/common/patches/rof2_constants.h +++ b/common/patches/rof2_constants.h @@ -103,7 +103,7 @@ namespace RoF2 { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 10; + static const size_t CHARACTER_CREATION_LIMIT = 12; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 24; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 7f8fc6978..fd6f0b2ea 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -166,11 +166,11 @@ struct CharSelectEquip struct CharacterSelectEntry_Struct { /*0000*/ char Name[1]; // Name null terminated -/*0000*/ uint8 Class_; +/*0000*/ uint8 Class; /*0000*/ uint32 Race; /*0000*/ uint8 Level; -/*0000*/ uint8 Class_2; -/*0000*/ uint32 Race2; +/*0000*/ uint8 ShroudClass; +/*0000*/ uint32 ShroudRace; /*0000*/ uint16 Zone; /*0000*/ uint16 Instance; /*0000*/ uint8 Gender; @@ -181,15 +181,15 @@ struct CharacterSelectEntry_Struct /*0000*/ uint32 DrakkinTattoo; /*0000*/ uint32 DrakkinDetails; /*0000*/ uint32 Deity; -/*0000*/ uint32 Primary; -/*0000*/ uint32 Secondary; +/*0000*/ uint32 PrimaryIDFile; +/*0000*/ uint32 SecondaryIDFile; /*0000*/ uint8 HairColor; /*0000*/ uint8 BeardColor; /*0000*/ uint8 EyeColor1; /*0000*/ uint8 EyeColor2; /*0000*/ uint8 HairStyle; /*0000*/ uint8 Beard; -/*0000*/ uint8 CharEnabled; +/*0000*/ uint8 Enabled; /*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing /*0000*/ uint32 DrakkinHeritage; /*0000*/ uint8 Unknown1; // Seen 0 diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 86367b3d9..8b5fadbf2 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -102,7 +102,7 @@ namespace RoF { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 10; + static const size_t CHARACTER_CREATION_LIMIT = 12; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 24; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index a1260fe72..4b8fe3b01 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -165,37 +165,37 @@ struct CharSelectEquip struct CharacterSelectEntry_Struct { -/*0000*/ char Name[1]; // Name null terminated -/*0000*/ uint8 Class_; +/*0000*/ char Name[1]; // Name null terminated +/*0000*/ uint8 Class; /*0000*/ uint32 Race; /*0000*/ uint8 Level; -/*0000*/ uint8 Class_2; -/*0000*/ uint32 Race2; +/*0000*/ uint8 ShroudClass; +/*0000*/ uint32 ShroudRace; /*0000*/ uint16 Zone; /*0000*/ uint16 Instance; /*0000*/ uint8 Gender; /*0000*/ uint8 Face; /*0000*/ CharSelectEquip Equip[9]; -/*0000*/ uint8 Unknown15; // Seen FF -/*0000*/ uint8 Uknown19; // Seen FF +/*0000*/ uint8 Unknown15; // Seen FF +/*0000*/ uint8 Unknown19; // Seen FF /*0000*/ uint32 DrakkinTattoo; /*0000*/ uint32 DrakkinDetails; /*0000*/ uint32 Deity; -/*0000*/ uint32 Primary; -/*0000*/ uint32 Secondary; +/*0000*/ uint32 PrimaryIDFile; +/*0000*/ uint32 SecondaryIDFile; /*0000*/ uint8 HairColor; /*0000*/ uint8 BeardColor; /*0000*/ uint8 EyeColor1; /*0000*/ uint8 EyeColor2; /*0000*/ uint8 HairStyle; /*0000*/ uint8 Beard; -/*0000*/ uint8 CharEnabled; -/*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing +/*0000*/ uint8 Enabled; +/*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing /*0000*/ uint32 DrakkinHeritage; -/*0000*/ uint8 Unknown1; // Seen 0 -/*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing +/*0000*/ uint8 Unknown1; // Seen 0 +/*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing /*0000*/ uint32 LastLogin; -/*0000*/ uint8 Unknown2; // Seen 0 +/*0000*/ uint8 Unknown2; // Seen 0 }; /* diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index c2ac79d7d..a8b69556a 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1911,76 +1911,95 @@ namespace SoD ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; + eq->TotalChars = eq->TotalChars; - int char_count; - int namelen = 0; - for (char_count = 0; char_count < 10; char_count++) { - if (emu->Name[char_count][0] == '\0') - break; - if (strcmp(emu->Name[char_count], "") == 0) - break; - namelen += strlen(emu->Name[char_count]); + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; + + FINISH_ENCODE(); + return; } - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr; - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + eq->CharCount = character_count; + eq->TotalChars = emu->TotalChars; - eq->CharCount = char_count; - eq->TotalChars = 10; + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; - unsigned char *bufptr = (unsigned char *)eq->Entries; - int r; - for (r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Level = emu->Level[r]; - eq2->HairStyle = emu->HairStyle[r]; - eq2->Gender = emu->Gender[r]; - memcpy(eq2->Name, emu->Name[r], strlen(emu->Name[r]) + 1); + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char *eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Level = emu_cse->Level; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Gender = emu_cse->Gender; + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(eq_cse->Name); + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Beard = emu_cse->Beard; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial; + eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color; } - //adjust for name. - bufptr += strlen(emu->Name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Beard = emu->Beard[r]; - eq2->HairColor = emu->HairColor[r]; - eq2->Face = emu->Face[r]; - int k; - for (k = 0; k < _MaterialCount; k++) { - eq2->Equip[k].Material = emu->Equip[r][k].Material; - eq2->Equip[k].Unknown1 = emu->Equip[r][k].Unknown1; - eq2->Equip[k].EliteMaterial = emu->Equip[r][k].EliteMaterial; - eq2->Equip[k].Color.Color = emu->Equip[r][k].Color.Color; - } - eq2->Primary = emu->Primary[r]; - eq2->Secondary = emu->Secondary[r]; - eq2->Tutorial = emu->Tutorial[r]; // was u15 - eq2->Unknown15 = 0xFF; - eq2->Deity = emu->Deity[r]; - eq2->Zone = emu->Zone[r]; - eq2->Unknown19 = 0xFF; - eq2->Race = emu->Race[r]; - eq2->GoHome = emu->GoHome[r]; - eq2->Class_ = emu->Class_[r]; - eq2->EyeColor1 = emu->EyeColor1[r]; - eq2->BeardColor = emu->BeardColor[r]; - eq2->EyeColor2 = emu->EyeColor2[r]; - eq2->DrakkinHeritage = emu->DrakkinHeritage[r]; - eq2->DrakkinTattoo = emu->DrakkinTattoo[r]; - eq2->DrakkinDetails = emu->DrakkinDetails[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->Unknown15 = emu_cse->Unknown15; + eq_cse->Deity = emu_cse->Deity; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Unknown19 = emu_cse->Unknown19; + eq_cse->Race = emu_cse->Race; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->Class = emu_cse->Class; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); } FINISH_ENCODE(); diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 5fdb89578..5dad9bb09 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -101,7 +101,7 @@ namespace SoD { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 10; + static const size_t CHARACTER_CREATION_LIMIT = 12; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 24; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index f18e3d2f6..274cfe876 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -132,8 +132,8 @@ struct CharacterSelectEntry_Struct /*0001*/ uint8 HairColor; // /*0000*/ uint8 Face; // /*0000*/ CharSelectEquip Equip[9]; -/*0000*/ uint32 Primary; // -/*0000*/ uint32 Secondary; // +/*0000*/ uint32 PrimaryIDFile; // +/*0000*/ uint32 SecondaryIDFile; // /*0000*/ uint8 Unknown15; // 0xff /*0000*/ uint32 Deity; // /*0000*/ uint16 Zone; // @@ -142,7 +142,7 @@ struct CharacterSelectEntry_Struct /*0000*/ uint8 Unknown19; // 0xff /*0000*/ uint32 Race; // /*0000*/ uint8 Tutorial; // -/*0000*/ uint8 Class_; // +/*0000*/ uint8 Class; // /*0000*/ uint8 EyeColor1; // /*0000*/ uint8 BeardColor; // /*0000*/ uint8 EyeColor2; // diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 5220a0a6e..e0b24b393 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1570,76 +1570,95 @@ namespace SoF ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; + eq->TotalChars = eq->TotalChars; - int char_count; - int namelen = 0; - for (char_count = 0; char_count < 10; char_count++) { - if (emu->Name[char_count][0] == '\0') - break; - if (strcmp(emu->Name[char_count], "") == 0) - break; - namelen += strlen(emu->Name[char_count]); + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; + + FINISH_ENCODE(); + return; } - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr; - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + eq->CharCount = character_count; + eq->TotalChars = emu->TotalChars; - eq->CharCount = char_count; - eq->TotalChars = 10; + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; - unsigned char *bufptr = (unsigned char *)eq->Entries; - int r; - for (r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Level = emu->Level[r]; - eq2->HairStyle = emu->HairStyle[r]; - eq2->Gender = emu->Gender[r]; - memcpy(eq2->Name, emu->Name[r], strlen(emu->Name[r]) + 1); + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char *eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Level = emu_cse->Level; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Gender = emu_cse->Gender; + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(eq_cse->Name); + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Beard = emu_cse->Beard; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial; + eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color; } - //adjust for name. - bufptr += strlen(emu->Name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Beard = emu->Beard[r]; - eq2->HairColor = emu->HairColor[r]; - eq2->Face = emu->Face[r]; - int k; - for (k = 0; k < _MaterialCount; k++) { - eq2->Equip[k].Material = emu->Equip[r][k].Material; - eq2->Equip[k].Unknown1 = emu->Equip[r][k].Unknown1; - eq2->Equip[k].EliteMaterial = emu->Equip[r][k].EliteMaterial; - eq2->Equip[k].Color.Color = emu->Equip[r][k].Color.Color; - } - eq2->Primary = emu->Primary[r]; - eq2->Secondary = emu->Secondary[r]; - eq2->Tutorial = emu->Tutorial[r]; // was u15 - eq2->Unknown15 = 0xff; - eq2->Deity = emu->Deity[r]; - eq2->Zone = emu->Zone[r]; - eq2->Unknown19 = 0xFF; - eq2->Race = emu->Race[r]; - eq2->GoHome = emu->GoHome[r]; - eq2->Class_ = emu->Class_[r]; - eq2->EyeColor1 = emu->EyeColor1[r]; - eq2->BeardColor = emu->BeardColor[r]; - eq2->EyeColor2 = emu->EyeColor2[r]; - eq2->DrakkinHeritage = emu->DrakkinHeritage[r]; - eq2->DrakkinTattoo = emu->DrakkinTattoo[r]; - eq2->DrakkinDetails = emu->DrakkinDetails[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->Unknown15 = emu_cse->Unknown15; + eq_cse->Deity = emu_cse->Deity; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Unknown19 = emu_cse->Unknown19; + eq_cse->Race = emu_cse->Race; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->Class = emu_cse->Class; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); } FINISH_ENCODE(); diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index 4729eb436..260722f00 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -101,7 +101,7 @@ namespace SoF { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 10; + static const size_t CHARACTER_CREATION_LIMIT = 12; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 24; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index e66ace468..23b080f0c 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -132,17 +132,17 @@ struct CharacterSelectEntry_Struct /*0001*/ uint8 HairColor; // /*0000*/ uint8 Face; // /*0000*/ CharSelectEquip Equip[9]; -/*0000*/ uint32 Primary; // -/*0000*/ uint32 Secondary; // -/*0000*/ uint8 Unknown15; // 0xff +/*0000*/ uint32 PrimaryIDFile; // +/*0000*/ uint32 SecondaryIDFile; // +/*0000*/ uint8 Unknown15; // 0xff /*0000*/ uint32 Deity; // /*0000*/ uint16 Zone; // /*0000*/ uint16 Instance; /*0000*/ uint8 GoHome; // -/*0000*/ uint8 Unknown19; // 0xff +/*0000*/ uint8 Unknown19; // 0xff /*0000*/ uint32 Race; // /*0000*/ uint8 Tutorial; // -/*0000*/ uint8 Class_; // +/*0000*/ uint8 Class; // /*0000*/ uint8 EyeColor1; // /*0000*/ uint8 BeardColor; // /*0000*/ uint8 EyeColor2; // diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index e636b6a9c..efacfe980 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1155,39 +1155,96 @@ namespace Titanium ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); - int r; - for (r = 0; r < 10; r++) { - OUT(Zone[r]); - OUT(EyeColor1[r]); - OUT(EyeColor2[r]); - OUT(HairStyle[r]); - OUT(Primary[r]); - if (emu->Race[r] > 473) - eq->Race[r] = 1; - else - eq->Race[r] = emu->Race[r]; - OUT(Class_[r]); - OUT_str(Name[r]); - OUT(Gender[r]); - OUT(Level[r]); - OUT(Secondary[r]); - OUT(Face[r]); - OUT(Beard[r]); - int k; - for (k = 0; k < 9; k++) { - eq->Equip[r][k] = emu->Equip[r][k].Material; - eq->CS_Colors[r][k].Color = emu->Equip[r][k].Color.Color; + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + for (size_t index = 0; index < 10; ++index) { + memset(eq->Name[index], 0, 64); + } + + // Non character-indexed packet fields + eq->Unknown830[0] = 0; + eq->Unknown830[1] = 0; + eq->Unknown0962[0] = 0; + eq->Unknown0962[1] = 0; + + size_t char_index = 0; + for (; char_index < emu->CharCount && char_index < 8; ++char_index) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + + eq->Race[char_index] = emu_cse->Race; + if (eq->Race[char_index] > 473) + eq->Race[char_index] = 1; + + for (int index = 0; index < _MaterialCount; ++index) { + eq->CS_Colors[char_index][index].Color = emu_cse->Equip[index].Color.Color; } - OUT(HairColor[r]); - OUT(GoHome[r]); - OUT(Tutorial[r]); - OUT(Deity[r]); - OUT(BeardColor[r]); - eq->Unknown820[r] = 0xFF; - eq->Unknown902[r] = 0xFF; + + eq->BeardColor[char_index] = emu_cse->BeardColor; + eq->HairStyle[char_index] = emu_cse->HairStyle; + + for (int index = 0; index < _MaterialCount; ++index) { + eq->Equip[char_index][index] = emu_cse->Equip[index].Material; + } + + eq->SecondaryIDFile[char_index] = emu_cse->SecondaryIDFile; + eq->Unknown820[char_index] = 0xFF; + eq->Deity[char_index] = emu_cse->Deity; + eq->GoHome[char_index] = emu_cse->GoHome; + eq->Tutorial[char_index] = emu_cse->Tutorial; + eq->Beard[char_index] = emu_cse->Beard; + eq->Unknown902[char_index] = 0xFF; + eq->PrimaryIDFile[char_index] = emu_cse->PrimaryIDFile; + eq->HairColor[char_index] = emu_cse->HairColor; + eq->Zone[char_index] = emu_cse->Zone; + eq->Class[char_index] = emu_cse->Class; + eq->Face[char_index] = emu_cse->Face; + + memcpy(eq->Name[char_index], emu_cse->Name, 64); + + eq->Gender[char_index] = emu_cse->Gender; + eq->EyeColor1[char_index] = emu_cse->EyeColor1; + eq->EyeColor2[char_index] = emu_cse->EyeColor2; + eq->Level[char_index] = emu_cse->Level; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + for (; char_index < 10; ++char_index) { + eq->Race[char_index] = 0; + + for (int index = 0; index < _MaterialCount; ++index) { + eq->CS_Colors[char_index][index].Color = 0; + } + + eq->BeardColor[char_index] = 0; + eq->HairStyle[char_index] = 0; + + for (int index = 0; index < _MaterialCount; ++index) { + eq->Equip[char_index][index] = 0; + } + + eq->SecondaryIDFile[char_index] = 0; + eq->Unknown820[char_index] = 0xFF; + eq->Deity[char_index] = 0; + eq->GoHome[char_index] = 0; + eq->Tutorial[char_index] = 0; + eq->Beard[char_index] = 0; + eq->Unknown902[char_index] = 0xFF; + eq->PrimaryIDFile[char_index] = 0; + eq->HairColor[char_index] = 0; + eq->Zone[char_index] = 0; + eq->Class[char_index] = 0; + eq->Face[char_index] = 0; + //eq->Name[char_index][0] = '\0'; // Cleared above + eq->Gender[char_index] = 0; + eq->EyeColor1[char_index] = 0; + eq->EyeColor2[char_index] = 0; + eq->Level[char_index] = 0; } FINISH_ENCODE(); diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index c93de7666..d7e2a4964 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -100,7 +100,7 @@ namespace Titanium { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 8; + static const size_t CHARACTER_CREATION_LIMIT = 8; // Hard-coded in client - DO NOT ALTER static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 16; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index a61b96a94..60b764dfe 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -122,7 +122,7 @@ struct CharacterSelect_Struct /*0400*/ uint8 BeardColor[10]; // Characters beard Color /*0410*/ uint8 HairStyle[10]; // Characters hair style /*0420*/ uint32 Equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) -/*0780*/ uint32 Secondary[10]; // Characters secondary IDFile number +/*0780*/ uint32 SecondaryIDFile[10]; // Characters secondary IDFile number /*0820*/ uint8 Unknown820[10]; // 10x ff /*0830*/ uint8 Unknown830[2]; // 2x 00 /*0832*/ uint32 Deity[10]; // Characters Deity @@ -130,11 +130,11 @@ struct CharacterSelect_Struct /*0882*/ uint8 Tutorial[10]; // 1=Tutorial available, 0=not /*0892*/ uint8 Beard[10]; // Characters Beard Type /*0902*/ uint8 Unknown902[10]; // 10x ff -/*0912*/ uint32 Primary[10]; // Characters primary IDFile number +/*0912*/ uint32 PrimaryIDFile[10]; // Characters primary IDFile number /*0952*/ uint8 HairColor[10]; // Characters Hair Color /*0962*/ uint8 Unknown0962[2]; // 2x 00 /*0964*/ uint32 Zone[10]; // Characters Current Zone -/*1004*/ uint8 Class_[10]; // Characters Classes +/*1004*/ uint8 Class[10]; // Characters Classes /*1014*/ uint8 Face[10]; // Characters Face Type /*1024*/ char Name[10][64]; // Characters Names /*1664*/ uint8 Gender[10]; // Characters Gender diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 18ea67af0..d8635485c 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2194,77 +2194,103 @@ namespace UF ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; + eq->TotalChars = eq->TotalChars; - int char_count; - int namelen = 0; - for (char_count = 0; char_count < 10; char_count++) { - if (emu->Name[char_count][0] == '\0') - break; - if (strcmp(emu->Name[char_count], "") == 0) - break; - namelen += strlen(emu->Name[char_count]); + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; + + // Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars' + uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field + eq->TotalChars = adjusted_total; + + FINISH_ENCODE(); + return; + } + + unsigned char *emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); } - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr; - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + eq->CharCount = character_count; + eq->TotalChars = emu->TotalChars; - eq->CharCount = char_count; - eq->TotalChars = 10; + if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT) + eq->TotalChars = consts::CHARACTER_CREATION_LIMIT; - unsigned char *bufptr = (unsigned char *)eq->Entries; - int r; - for (r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Level = emu->Level[r]; - eq2->HairStyle = emu->HairStyle[r]; - eq2->Gender = emu->Gender[r]; - memcpy(eq2->Name, emu->Name[r], strlen(emu->Name[r]) + 1); - } - //adjust for name. - bufptr += strlen(emu->Name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->Beard = emu->Beard[r]; - eq2->HairColor = emu->HairColor[r]; - eq2->Face = emu->Face[r]; - int k; - for (k = 0; k < _MaterialCount; k++) { - eq2->Equip[k].Material = emu->Equip[r][k].Material; - eq2->Equip[k].Unknown1 = emu->Equip[r][k].Unknown1; - eq2->Equip[k].EliteMaterial = emu->Equip[r][k].EliteMaterial; - eq2->Equip[k].Color.Color = emu->Equip[r][k].Color.Color; - } - eq2->Primary = emu->Primary[r]; - eq2->Secondary = emu->Secondary[r]; - eq2->Tutorial = emu->Tutorial[r]; // was u15 - eq2->Unknown15 = 0xFF; - eq2->Deity = emu->Deity[r]; - eq2->Zone = emu->Zone[r]; - eq2->Unknown19 = 0xFF; - eq2->Race = emu->Race[r]; - eq2->GoHome = emu->GoHome[r]; - eq2->Class_ = emu->Class_[r]; - eq2->EyeColor1 = emu->EyeColor1[r]; - eq2->BeardColor = emu->BeardColor[r]; - eq2->EyeColor2 = emu->EyeColor2[r]; - eq2->DrakkinHeritage = emu->DrakkinHeritage[r]; - eq2->DrakkinTattoo = emu->DrakkinTattoo[r]; - eq2->DrakkinDetails = emu->DrakkinDetails[r]; + // Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars' in this client + uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field + eq->TotalChars = adjusted_total; + + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char *eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Level = emu_cse->Level; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Gender = emu_cse->Gender; + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(eq_cse->Name); + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + + eq_cse->Beard = emu_cse->Beard; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial; + eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color; } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->Unknown15 = emu_cse->Unknown15; + eq_cse->Deity = emu_cse->Deity; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Unknown19 = emu_cse->Unknown19; + eq_cse->Race = emu_cse->Race; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->Class = emu_cse->Class; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); } FINISH_ENCODE(); diff --git a/common/patches/uf_constants.h b/common/patches/uf_constants.h index 3320fee76..1a5a4cc44 100644 --- a/common/patches/uf_constants.h +++ b/common/patches/uf_constants.h @@ -101,7 +101,7 @@ namespace UF { } namespace consts { - static const size_t CHARACTER_CREATION_LIMIT = 10; + static const size_t CHARACTER_CREATION_LIMIT = 12; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_BANK_SIZE = 24; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index ec912b681..2f5aa95b3 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -132,8 +132,8 @@ struct CharacterSelectEntry_Struct /*0001*/ uint8 HairColor; // /*0000*/ uint8 Face; // /*0000*/ CharSelectEquip Equip[9]; -/*0000*/ uint32 Primary; // -/*0000*/ uint32 Secondary; // +/*0000*/ uint32 PrimaryIDFile; // +/*0000*/ uint32 SecondaryIDFile; // /*0000*/ uint8 Unknown15; // 0xff /*0000*/ uint32 Deity; // /*0000*/ uint16 Zone; // @@ -142,7 +142,7 @@ struct CharacterSelectEntry_Struct /*0000*/ uint8 Unknown19; // 0xff /*0000*/ uint32 Race; // /*0000*/ uint8 Tutorial; // -/*0000*/ uint8 Class_; // +/*0000*/ uint8 Class; // /*0000*/ uint8 EyeColor1; // /*0000*/ uint8 BeardColor; // /*0000*/ uint8 EyeColor2; // diff --git a/world/client.cpp b/world/client.cpp index 55ca440cc..0393f0f65 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -16,6 +16,7 @@ #include "../common/string_util.h" #include "../common/clientversions.h" #include "../common/random.h" +#include "../common/shareddb.h" #include "client.h" #include "worlddb.h" @@ -161,32 +162,34 @@ void Client::SendCharInfo() { cle->SetOnline(CLE_Status_CharSelect); } - if (m_ClientVersionBit & BIT_RoFAndLater) - { - // Can make max char per account into a rule - New to VoA - SendMaxCharCreate(10); + if (m_ClientVersionBit & BIT_RoFAndLater) { + SendMaxCharCreate(); SendMembership(); SendMembershipSettings(); } seencharsel = true; - // Send OP_SendCharInfo - auto outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); - CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer; + EQApplicationPacket *outapp = nullptr; + database.GetCharSelectInfo(GetAccountID(), &outapp, m_ClientVersionBit); - database.GetCharSelectInfo(GetAccountID(), cs, m_ClientVersionBit); - - QueuePacket(outapp); + if (outapp) { + QueuePacket(outapp); + } + else { + Log.Out(Logs::General, Logs::World_Server, "[Error] Database did not return an OP_SendCharInfo packet for account %u", GetAccountID()); + } safe_delete(outapp); } -void Client::SendMaxCharCreate(int max_chars) { +void Client::SendMaxCharCreate() { auto outapp = new EQApplicationPacket(OP_SendMaxCharacters, sizeof(MaxCharacters_Struct)); MaxCharacters_Struct* mc = (MaxCharacters_Struct*)outapp->pBuffer; - mc->max_chars = max_chars; + mc->max_chars = EQLimits::CharacterCreationLimit(m_ClientVersion); + if (mc->max_chars > EmuConstants::CHARACTER_CREATION_LIMIT) + mc->max_chars = EmuConstants::CHARACTER_CREATION_LIMIT; QueuePacket(outapp); safe_delete(outapp); @@ -716,66 +719,71 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { return true; } - if(!pZoning && ew->return_home && !ew->tutorial) { - auto cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs, m_ClientVersionBit); - bool home_enabled = false; + // This can probably be moved outside and have another method return requested info (don't forget to remove the #include "../common/shareddb.h" above) + if (!pZoning) { + size_t character_limit = EQLimits::CharacterCreationLimit(eqs->GetClientVersion()); + if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT) { character_limit = EmuConstants::CHARACTER_CREATION_LIMIT; } + if (eqs->GetClientVersion() == ClientVersion::Titanium) { character_limit = 8; } - for(int x = 0; x < 10; ++x) - { - if(strcasecmp(cs->Name[x], char_name) == 0) - { - if(cs->GoHome[x] == 1) - { - home_enabled = true; - break; + std::string tgh_query = StringFormat( + "SELECT " + "`id`, " + "name, " + "`level`, " + "last_login, " + "FROM " + "character_data " + "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", GetAccountID(), character_limit); + auto tgh_results = database.QueryDatabase(tgh_query); + + /* Check GoHome */ + if (ew->return_home && !ew->tutorial) { + bool home_enabled = false; + for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) { + if (strcasecmp(row[1], char_name) == 0) { + if (RuleB(World, EnableTutorialButton) && ((uint8)atoi(row[2]) <= RuleI(World, MaxLevelForTutorial))) { + home_enabled = true; + break; + } } } - } - safe_delete(cs); - if(home_enabled) { - zoneID = database.MoveCharacterToBind(charid,4); - } - else { - Log.Out(Logs::Detail, Logs::World_Server,"'%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))) { - auto cs = new CharacterSelect_Struct; - memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs, m_ClientVersionBit); - 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; - } + if (home_enabled) { + zoneID = database.MoveCharacterToBind(charid, 4); + } + else { + Log.Out(Logs::Detail, Logs::World_Server, "'%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; } } - safe_delete(cs); - if(tutorial_enabled) - { - zoneID = RuleI(World, TutorialZoneID); - database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); - } - else - { - Log.Out(Logs::Detail, Logs::World_Server,"'%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; + /* Check Tutorial*/ + if (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial)) { + bool tutorial_enabled = false; + for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) { + if (strcasecmp(row[1], char_name) == 0) { + if (RuleB(World, EnableReturnHomeButton)) { + int now = time(nullptr); + if ((now - atoi(row[3])) >= RuleI(World, MinOfflineTimeToReturnHome)) { + tutorial_enabled = true; + break; + } + } + } + } + + if (tutorial_enabled) { + zoneID = RuleI(World, TutorialZoneID); + database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); + } + else { + Log.Out(Logs::Detail, Logs::World_Server, "'%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; + } } } diff --git a/world/client.h b/world/client.h index ea1df42ba..d7a42ab28 100644 --- a/world/client.h +++ b/world/client.h @@ -41,7 +41,7 @@ public: bool Process(); void ReceiveData(uchar* buf, int len); void SendCharInfo(); - void SendMaxCharCreate(int max_chars); + void SendMaxCharCreate(); void SendMembership(); void SendMembershipSettings(); void EnterWorld(bool TryBootup = true); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index e47d9d54a..4ba21101c 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -33,21 +33,20 @@ extern std::vector character_create_race_class_combos; // the current stuff is at the bottom of this function -void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) +void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit) { - Inventory *inv = nullptr; - uint8 has_home = 0; - uint8 has_bind = 0; - - /* Initialize Variables */ - for (int i=0; i<10; i++) { - strcpy(cs->Name[i], ""); - cs->Zone[i] = 0; - cs->Level[i] = 0; - cs->Tutorial[i] = 0; - cs->GoHome[i] = 0; - } + /* Set Character Creation Limit */ + ClientVersion client_version = ClientVersionFromBit(clientVersionBit); + size_t character_limit = EQLimits::CharacterCreationLimit(client_version); + + // Validate against absolute server max + if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT) + character_limit = EmuConstants::CHARACTER_CREATION_LIMIT; + // Force Titanium clients to use '8' + if (client_version == ClientVersion::Titanium) + character_limit = 8; + /* Get Character Info */ std::string cquery = StringFormat( "SELECT " @@ -73,52 +72,93 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* "zone_id " // 19 "FROM " "character_data " - "WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id); - auto results = database.QueryDatabase(cquery); int char_num = 0; - for (auto row = results.begin(); row != results.end() && char_num < 10; ++row, ++char_num) { + "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit); + auto results = database.QueryDatabase(cquery); + + size_t character_count = results.RowCount(); + if (character_count == 0) { + *outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); + CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer; + cs->CharCount = 0; + cs->TotalChars = character_limit; + return; + } + + size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); + *outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size); + + unsigned char *buff_ptr = (*outApp)->pBuffer; + CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr; + + cs->CharCount = character_count; + cs->TotalChars = character_limit; + + buff_ptr += sizeof(CharacterSelect_Struct); + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; PlayerProfile_Struct pp; + Inventory inv; + uint32 character_id = (uint32)atoi(row[0]); + uint8 has_home = 0; + uint8 has_bind = 0; + memset(&pp, 0, sizeof(PlayerProfile_Struct)); + + /* Fill CharacterSelectEntry_Struct */ + strcpy(cse->Name, row[1]); + cse->Class = (uint8)atoi(row[4]); + cse->Race = (uint32)atoi(row[3]); + cse->Level = (uint8)atoi(row[5]); + cse->ShroudClass = cse->Class; + cse->ShroudRace = cse->Race; + cse->Zone = (uint16)atoi(row[19]); + cse->Instance = 0; + cse->Gender = (uint8)atoi(row[2]); + cse->Face = (uint8)atoi(row[15]); + cse->Equip[0] = { 0 }; // Processed below + cse->Unknown15 = 0xFF; + cse->Unknown19 = 0xFF; + cse->DrakkinTattoo = (uint32)atoi(row[17]); + cse->DrakkinDetails = (uint32)atoi(row[18]); + cse->Deity = (uint32)atoi(row[6]); + cse->PrimaryIDFile = 0; // Processed Below + cse->SecondaryIDFile = 0; // Processed Below + cse->HairColor = (uint8)atoi(row[9]); + cse->BeardColor = (uint8)atoi(row[10]); + cse->EyeColor1 = (uint8)atoi(row[11]); + cse->EyeColor2 = (uint8)atoi(row[12]); + cse->HairStyle = (uint8)atoi(row[13]); + cse->Beard = (uint8)atoi(row[14]); + cse->Enabled = 1; + cse->Tutorial = 0; // Processed Below + cse->DrakkinHeritage = (uint32)atoi(row[16]); + cse->Unknown1 = 0; + cse->GoHome = 0; // Processed Below + cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584 + cse->Unknown2 = 0; + /* Fill End */ - uint32 character_id = atoi(row[0]); - strcpy(cs->Name[char_num], row[1]); - uint8 lvl = atoi(row[5]); - cs->Level[char_num] = lvl; - cs->Class_[char_num] = atoi(row[4]); - cs->Race[char_num] = atoi(row[3]); - cs->Gender[char_num] = atoi(row[2]); - cs->Deity[char_num] = atoi(row[6]); - cs->Zone[char_num] = atoi(row[19]); - cs->Face[char_num] = atoi(row[15]); - cs->HairColor[char_num] = atoi(row[9]); - cs->BeardColor[char_num] = atoi(row[10]); - cs->EyeColor2[char_num] = atoi(row[12]); - cs->EyeColor1[char_num] = atoi(row[11]); - cs->HairStyle[char_num] = atoi(row[13]); - cs->Beard[char_num] = atoi(row[14]); - cs->DrakkinHeritage[char_num] = atoi(row[16]); - cs->DrakkinTattoo[char_num] = atoi(row[17]); - cs->DrakkinDetails[char_num] = atoi(row[18]); - - if (RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) - cs->Tutorial[char_num] = 1; + if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) { + cse->Tutorial = 1; + } if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) - cs->GoHome[char_num] = 1; + cse->GoHome = 1; } /* Set Bind Point Data for any character that may possibly be missing it for any reason */ cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); - auto results_bind = database.QueryDatabase(cquery); has_home = 0; has_bind = 0; + auto results_bind = database.QueryDatabase(cquery); for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; } if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } } - if (has_home == 0 || has_bind == 0){ + if (has_home == 0 || has_bind == 0) { cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", - cs->Class_[char_num], cs->Deity[char_num], cs->Race[char_num]); + cse->Class, cse->Deity, cse->Race); auto results_bind = database.QueryDatabase(cquery); for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ @@ -134,32 +174,87 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* float z = atof(row_d[4]); if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; - } } pp.binds[0] = pp.binds[4]; /* If no home bind set, set it */ - if (has_home == 0){ + if (has_home == 0) { std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1); - auto results_bset = QueryDatabase(query); + auto results_bset = QueryDatabase(query); } /* If no regular bind set, set it */ - if (has_bind == 0){ + if (has_bind == 0) { std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); - auto results_bset = QueryDatabase(query); + auto results_bset = QueryDatabase(query); } } /* Bind End */ + /* Load Inventory */ + // If we ensure that the material data is updated appropriately, we can do away with inventory loads + if (GetInventory(accountID, cse->Name, &inv)) { + const Item_Struct* item = nullptr; + const ItemInst* inst = nullptr; + int16 invslot = 0; + + for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) { + invslot = Inventory::CalcSlotFromMaterial(matslot); + if (invslot == INVALID_INDEX) { continue; } + inst = inv.GetItem(invslot); + if (inst == nullptr) { continue; } + item = inst->GetItem(); + if (item == nullptr) { continue; } + + if (matslot > 6) { + uint32 idfile = 0; + // Weapon Models + if (inst->GetOrnamentationIDFile() != 0) { + idfile = inst->GetOrnamentationIDFile(); + cse->Equip[matslot].Material = idfile; + } + else { + if (strlen(item->IDFile) > 2) { + idfile = atoi(&item->IDFile[2]); + cse->Equip[matslot].Material = idfile; + } + } + if (matslot == MaterialPrimary) { + cse->PrimaryIDFile = idfile; + } + else { + cse->SecondaryIDFile = idfile; + } + } + else { + uint32 color = 0; + if (pp.item_tint[matslot].RGB.UseTint) { + color = pp.item_tint[matslot].Color; + } + else { + color = inst->GetColor(); + } + + // Armor Materials/Models + cse->Equip[matslot].Material = item->Material; + cse->Equip[matslot].EliteMaterial = item->EliteMaterial; + cse->Equip[matslot].HeroForgeModel = inst->GetOrnamentHeroModel(matslot); + cse->Equip[matslot].Color.Color = color; + } + } + } + else { + printf("Error loading inventory for %s\n", cse->Name); + } + /* Load Inventory End */ + /* Load Character Material Data for Char Select */ cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; - for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) - { + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); pp.item_tint[slot].RGB.Red = atoi(row_b[1]); pp.item_tint[slot].RGB.Green = atoi(row_b[2]); @@ -167,81 +262,8 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* pp.item_tint[slot].RGB.UseTint = atoi(row_b[4]); } - /* Load Inventory */ - // If we ensure that the material data is updated appropriately, we can do away with inventory loads - inv = new Inventory; - if (GetInventory(account_id, cs->Name[char_num], inv)) - { - const Item_Struct* item = nullptr; - const ItemInst* inst = nullptr; - int16 invslot = 0; - - for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) - { - invslot = Inventory::CalcSlotFromMaterial(matslot); - if (invslot == INVALID_INDEX) { continue; } - - inst = inv->GetItem(invslot); - if (inst == nullptr) { continue; } - - item = inst->GetItem(); - if (item == nullptr) { continue; } - - if (matslot > 6) - { - uint32 idfile = 0; - // Weapon Models - if (inst->GetOrnamentationIDFile() != 0) - { - idfile = inst->GetOrnamentationIDFile(); - cs->Equip[char_num][matslot].Material = idfile; - } - else - { - if (strlen(item->IDFile) > 2) - { - idfile = atoi(&item->IDFile[2]); - cs->Equip[char_num][matslot].Material = idfile; - } - } - if (matslot == MaterialPrimary) - { - cs->Primary[char_num] = idfile; - } - else - { - cs->Secondary[char_num] = idfile; - } - } - else - { - uint32 color = 0; - if (pp.item_tint[matslot].RGB.UseTint) - { - color = pp.item_tint[matslot].Color; - } - else - { - color = inst->GetColor(); - } - - // Armor Materials/Models - cs->Equip[char_num][matslot].Material = item->Material; - cs->Equip[char_num][matslot].EliteMaterial = item->EliteMaterial; - cs->Equip[char_num][matslot].HeroForgeModel = inst->GetOrnamentHeroModel(matslot); - cs->Equip[char_num][matslot].Color.Color = color; - } - } - } - else - { - printf("Error loading inventory for %s\n", cs->Name[char_num]); - } - - safe_delete(inv); + buff_ptr += sizeof(CharacterSelectEntry_Struct); } - - return; } int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) { diff --git a/world/worlddb.h b/world/worlddb.h index 166248e72..b0c2ff221 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -20,6 +20,7 @@ #include "../common/shareddb.h" #include "../common/zone_numbers.h" +#include "../common/eq_packet.h" struct PlayerProfile_Struct; struct CharCreate_Struct; @@ -29,7 +30,7 @@ struct CharacterSelect_Struct; class WorldDatabase : public SharedDatabase { public: bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium); - void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*, uint32 ClientVersion); + void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit); int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result);