| 
		
			| Zothen | 05-04-2011 05:29 AM |  
 Too bad about one account multi-boxing ;) Would be very handy if it worked... 
Your idea about the char limit sounds good and would only take the following changes:
Global 
	eq_packet_structs.hCode: 
 #define UPPER_CHAR_LIMIT        32
 
	worlddb.cppCode: 
 struct CharacterSelect_Struct {/*0000*/        int32        race[UPPER_CHAR_LIMIT];                                // Characters Race
 /*0040*/        Color_Struct        cs_colors[UPPER_CHAR_LIMIT][9];        // Characters Equipment Colors
 /*0400*/        int8        beardcolor[UPPER_CHAR_LIMIT];                        // Characters beard Color
 /*0410*/        int8        hairstyle[UPPER_CHAR_LIMIT];                        // Characters hair style
 /*0420*/        int32        equip[UPPER_CHAR_LIMIT][9];                        // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2  (Might not be)
 /*0780*/        int32        secondary[UPPER_CHAR_LIMIT];                        // Characters secondary IDFile number
 /*0820*/        int32        drakkin_heritage[UPPER_CHAR_LIMIT];                // added for SoF
 /*0860*/        int32        drakkin_tattoo[UPPER_CHAR_LIMIT];                        // added for SoF
 /*0900*/        int32        drakkin_details[UPPER_CHAR_LIMIT];                // added for SoF
 /*0940*/        int32        deity[UPPER_CHAR_LIMIT];                                // Characters Deity
 /*0980*/        int8        gohome[UPPER_CHAR_LIMIT];                                // 1=Go Home available, 0=not
 /*0990*/        int8        tutorial[UPPER_CHAR_LIMIT];                        // 1=Tutorial available, 0=not
 /*1000*/        int8        beard[UPPER_CHAR_LIMIT];                                // Characters Beard Type
 /*1010*/        int8        unknown902[UPPER_CHAR_LIMIT];                        // 10x ff
 /*1020*/        int32        primary[UPPER_CHAR_LIMIT];                        // Characters primary IDFile number
 /*1060*/        int8        haircolor[UPPER_CHAR_LIMIT];                        // Characters Hair Color
 /*1070*/        int8        unknown0962[2];                        // 2x 00
 /*1072*/        int32        zone[UPPER_CHAR_LIMIT];                                // Characters Current Zone
 /*1112*/        int8        class_[UPPER_CHAR_LIMIT];                                // Characters Classes
 /*1022*/        int8        face[UPPER_CHAR_LIMIT];                                // Characters Face Type
 /*1032*/        char        name[UPPER_CHAR_LIMIT][64];                        // Characters Names
 /*1672*/        int8        gender[UPPER_CHAR_LIMIT];                                // Characters Gender
 /*1682*/        int8        eyecolor1[UPPER_CHAR_LIMIT];                        // Characters Eye Color
 /*1692*/        int8        eyecolor2[UPPER_CHAR_LIMIT];                        // Characters Eye 2 Color
 /*1702*/        int8        level[UPPER_CHAR_LIMIT];                                // Characters Levels
 /*1712*/
 };
 
	client.cpp
in HandlePacket()Code: 
 void WorldDatabase::GetCharSelectInfo(int32 account_id, CharacterSelect_Struct* cs) {char errbuf[MYSQL_ERRMSG_SIZE];
 char* query = 0;
 MYSQL_RES *result;
 MYSQL_ROW row;
 Inventory *inv;
 
 int iMaxChars = RuleI(World, MaxCharSelectChars);
 if ( iMaxChars > UPPER_CHAR_LIMIT )
 iMaxChars = UPPER_CHAR_LIMIT;
 
 
 
 for (int i=0; i<iMaxChars; i++) {
 strcpy(cs->name[i], "<none>");
 cs->zone[i] = 0;
 cs->level[i] = 0;
 cs->tutorial[i] = 0;
 cs->gohome[i] = 0;
 }
 
 int char_num = 0;
 unsigned long* lengths;
 
 // Populate character info
 if (RunQuery(query, MakeAnyLenString(&query, "SELECT name,profile,zonename,class,level FROM character_ WHERE account_id=%i order by name limit %i", account_id, iMaxChars), errbuf, &result)) {
 safe_delete_array(query);
 while ((row = mysql_fetch_row(result))) {
 lengths = mysql_fetch_lengths(result);
 ////////////
 ////////////        This is the current one, the other are for converting
 ////////////
 if ((lengths[1] == sizeof(PlayerProfile_Struct))) {
 strcpy(cs->name[char_num], row[0]);
 PlayerProfile_Struct* pp = (PlayerProfile_Struct*)row[1];
 uint8 clas = atoi(row[3]);
 uint8 lvl = atoi(row[4]);
 
 // Character information
 if(lvl == 0)
 cs->level[char_num]                = pp->level;        //no level in DB, trust PP
 else
 cs->level[char_num]                = lvl;
 if(clas == 0)
 cs->class_[char_num]        = pp->class_;        //no class in DB, trust PP
 else
 cs->class_[char_num]        = clas;
 cs->race[char_num]                        = pp->race;
 cs->gender[char_num]                = pp->gender;
 cs->deity[char_num]                        = pp->deity;
 cs->zone[char_num]                        = GetZoneID(row[2]);
 cs->face[char_num]                        = pp->face;
 cs->haircolor[char_num]                = pp->haircolor;
 cs->beardcolor[char_num]        = pp->beardcolor;
 cs->eyecolor2[char_num]         = pp->eyecolor2;
 cs->eyecolor1[char_num]         = pp->eyecolor1;
 cs->hairstyle[char_num]                = pp->hairstyle;
 cs->beard[char_num]                        = pp->beard;
 cs->drakkin_heritage[char_num]        = pp->drakkin_heritage;
 cs->drakkin_tattoo[char_num]        = pp->drakkin_tattoo;
 cs->drakkin_details[char_num]        = pp->drakkin_details;
 
 if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial)))
 cs->tutorial[char_num] = 1;
 
 if(RuleB(World, EnableReturnHomeButton)) {
 int now = time(NULL);
 if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome))
 cs->gohome[char_num] = 1;
 }
 
 
 // This part creates home city entries for characters created before the home bind point was tracked.
 // Do it here because the player profile is already loaded and it's as good a spot as any.  This whole block should
 // probably be removed at some point, when most accounts are safely converted.
 if(pp->binds[4].zoneId == 0) {
 bool altered = false;
 MYSQL_RES *result2;
 MYSQL_ROW row2;
 char startzone[50] = {0};
 
 // check for start zone variable (I didn't even know any variables were still being used...)
 if(database.GetVariable("startzone", startzone, 50)) {
 uint32 zoneid = database.GetZoneID(startzone);
 if(zoneid) {
 pp->binds[4].zoneId = zoneid;
 GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z);
 altered = true;
 }
 }
 else {
 RunQuery(query,
 MakeAnyLenString(&query,
 "SELECT zone_id,bind_id,x,y,z FROM start_zones "
 "WHERE player_class=%i AND player_deity=%i AND player_race=%i",
 pp->class_,
 pp->deity,
 pp->race
 ),
 errbuf,
 &result2
 );
 safe_delete_array(query);
 
 // if there is only one possible start city, set it
 if(mysql_num_rows(result2) == 1) {
 row2 = mysql_fetch_row(result2);
 if(atoi(row2[1]) != 0) {                // if a bind_id is specified, make them start there
 pp->binds[4].zoneId = (uint32)atoi(row2[1]);
 GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z);
 }
 else {        // otherwise, use the zone and coordinates given
 pp->binds[4].zoneId = (uint32)atoi(row2[0]);
 float x = atof(row2[2]);
 float y = atof(row2[3]);
 float z = atof(row2[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;
 }
 altered = true;
 }
 
 mysql_free_result(result2);
 }
 
 // update the player profile
 if(altered) {
 uint32 char_id = GetCharacterID(cs->name[char_num]);
 RunQuery(query,MakeAnyLenString(&query,"SELECT extprofile FROM character_ WHERE id=%i",char_id), errbuf, &result2);
 safe_delete_array(query);
 if(result2) {
 row2 = mysql_fetch_row(result2);
 ExtendedProfile_Struct* ext = (ExtendedProfile_Struct*)row2[0];
 SetPlayerProfile(account_id,char_id,pp,inv,ext);
 }
 mysql_free_result(result2);
 }
 }        // end of "set start zone" block
 
 
 // Character's equipped items
 // @merth: Haven't done bracer01/bracer02 yet.
 // Also: this needs a second look after items are a little more solid
 // NOTE: items don't have a color, players MAY have a tint, if the
 // use_tint part is set.  otherwise use the regular color
 inv = new Inventory;
 if(GetInventory(account_id, cs->name[char_num], inv))
 {
 for (uint8 material = 0; material <= 8; material++)
 {
 uint32 color;
 ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material));
 if(item == 0)
 continue;
 
 cs->equip[char_num][material] = item->GetItem()->Material;
 
 if(pp->item_tint[material].rgb.use_tint)        // they have a tint (LoY dye)
 color = pp->item_tint[material].color;
 else        // no tint, use regular item color
 color = item->GetItem()->Color;
 
 cs->cs_colors[char_num][material].color = color;
 
 // the weapons are kept elsewhere
 if ((material==MATERIAL_PRIMARY) || (material==MATERIAL_SECONDARY))
 {
 if(strlen(item->GetItem()->IDFile) > 2) {
 int32 idfile=atoi(&item->GetItem()->IDFile[2]);
 if (material==MATERIAL_PRIMARY)
 cs->primary[char_num]=idfile;
 else
 cs->secondary[char_num]=idfile;
 }
 }
 }
 }
 else
 {
 printf("Error loading inventory for %s\n", cs->name[char_num]);
 }
 safe_delete(inv);
 if (++char_num > iMaxChars)
 break;
 }
 else
 {
 cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << endl;
 cout << "PP length ="<<lengths[1]<<" but PP should be "<<sizeof(PlayerProfile_Struct)<<endl;
 //DeleteCharacter(row[0]);
 }
 }
 mysql_free_result(result);
 }
 else
 {
 cerr << "Error in GetCharSelectInfo query '" << query << "' " << errbuf << endl;
 safe_delete_array(query);
 return;
 }
 
 return;
 }
 
	Code: 
                 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());  //Lieka Edit Begin:  Check current CLE Entry IPs against incoming connection
 }
 
 EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer;
 strn0cpy(char_name, ew->name, 64);
 
 EQApplicationPacket *outapp;
 int32 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;
 }
 int iMaxChars = RuleI(World, MaxCharSelectChars);
 if ( iMaxChars > UPPER_CHAR_LIMIT )
 iMaxChars = UPPER_CHAR_LIMIT;
 
 
 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 < iMaxChars; ++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 < iMaxChars; ++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.SetLFP(charid, false);
 database.SetLFG(charid, false);
 }
 else{
 int32 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;
 }
 
and finally in SoD.cpp  (or any other patch file, depending on client)
 
	Code: 
 ENCODE(OP_SendCharInfo) {ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
 SETUP_VAR_ENCODE(CharacterSelect_Struct);
 
 
 //EQApplicationPacket *packet = *p;
 //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
 
 int char_count;
 int namelen = 0;
 
 int iMaxChars = RuleI(World, MaxCharSelectChars);
 if ( iMaxChars > UPPER_CHAR_LIMIT )
 iMaxChars = UPPER_CHAR_LIMIT;
 
 for(char_count = 0; char_count < iMaxChars; char_count++) {
 if(emu->name[char_count][0] == '\0')
 break;
 if(strcmp(emu->name[char_count], "<none>") == 0)
 break;
 namelen += strlen(emu->name[char_count]);
 }
 
 int total_length = sizeof(structs::CharacterSelect_Struct)
 + char_count * sizeof(structs::CharacterSelectEntry_Struct)
 + namelen;
 
 ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
 
 //unsigned char *eq_buffer = new unsigned char[total_length];
 //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
 
 eq->char_count = char_count;
 eq->total_chars = UPPER_CHAR_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 < MAX_MATERIALS; k++) {
 eq2->equip[k].equip0 = emu->equip[r][k];
 eq2->equip[k].equip1 = 0;
 eq2->equip[k].itemid = 0;
 eq2->equip[k].color.color = emu->cs_colors[r][k].color;
 }
 eq2->primary = emu->primary[r];
 eq2->secondary = emu->secondary[r];
 eq2->tutorial = emu->tutorial[r]; // was u15
 eq2->u15 = 0xff;
 eq2->deity = emu->deity[r];
 eq2->zone = emu->zone[r];
 eq2->u19 = 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->drakkin_heritage = emu->drakkin_heritage[r];
 eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
 eq2->drakkin_details = emu->drakkin_details[r];
 }
 bufptr += sizeof(structs::CharacterSelectEntry_Struct);
 }
 
 FINISH_ENCODE();
 
 }
 |