EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Feature Requests (https://www.eqemulator.org/forums/forumdisplay.php?f=612)
-   -   MaxChars variable in database (https://www.eqemulator.org/forums/showthread.php?t=33447)

Zothen 05-03-2011 06:03 AM

MaxChars variable in database
 
Since it is possible to change the number of max chars per server in the sourcecode-at least working with SOF+ clients- it would be great if there was a database rule variable for that, so we don't need to change the code for every EQEmu release ourselves.

Thanks in advance and keep up the great work! :)

ChaosSlayerZ 05-03-2011 11:54 AM

somehow i believe that there are no char limit - you only limited by how many char you see on your login screen by your client - so Titanium will not show you more than 8, even if you have 20 in DB

trevius 05-03-2011 01:43 PM

There is a limit and it is currently 10 (we increased it from 8 when SoF was added). There is a post with the required changes to make more work, which was posted in the support section I think.

ChaosSlayerZ 05-03-2011 06:45 PM

hmm... Trev, what would happen if I manually go into DB and change which account id a character is assigned too, and go above 10?

lerxst2112 05-03-2011 07:08 PM

Quote:

Originally Posted by ChaosSlayerZ (Post 199290)
hmm... Trev, what would happen if I manually go into DB and change which account id a character is assigned too, and go above 10?

One way to find out is to try it.

The problem with the code snippet to increase the limit is that it isn't easily modifiable for a different number of slots, and it is a compile-time option so it would not be something that could be changed by a rule.

Both of those problems could be solved, but it would take a fair amount of work and presumably a lot of testing to make sure something didn't regress somewhere.

Maybe it's just me, but I don't see a really good reason to put more toons on an account when additional accounts are easy to get and having them split up gives you more flexibility with regard to boxing and such.

Zothen 05-04-2011 02:07 AM

You could bind as many chars to your accountid as you wish, but as long as the server only sends the first 8 (10) to the client, you wont see the others in char select.

As for the benefit of having more chars on one account instead of simply more accounts, imho its nice to have one shared bank for all of my chars and I think you can enable multiboxing on the same account in the database rules. Correct me if I am wrong on that.

Well, I would volunteer to find a solution for the compile-time problem of the involved array in the charselect_struct, if theres any chance for this feature to find its way into the official release.

trevius 05-04-2011 04:54 AM

Yeah, if you have more chars than the client and/or emu supports, you will only see the first X number of characters in alphabetical order.

We could probably create a rule with minimal work by setting the hard coded stuff to use 32 as the max, then add in patches to each of the common/PatchName.cpp files similar to this example:

common/SoD.cpp around line 286
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;
-        for(char_count = 0; char_count < 10; char_count++) {
+        for(char_count = 0; char_count < RuleI(World, MaxCharSelectChars); 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]);
    }


There may be other stuff involved like limiting each client to it's absolute max, but that wouldn't be hard.

As for enabling the option to multi-box from the same account, I would highly recommend against it. The server causes some funky stuff to happen when you do that, which is why we added the option to disable it and made it disabled by default. I log in from the same account on my GM account with more than 1 sometimes, but only when needed. When you log off or zone one of the characters, it will boot the other to char select and swap your chars between windows most of the time. It is very annoying. It is always best to use multiple accounts when multi-boxing.

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
Code:

#define UPPER_CHAR_LIMIT        32
eq_packet_structs.h
Code:

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*/
};

worlddb.cpp
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;
}

client.cpp
in HandlePacket()
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();
       
}


lerxst2112 05-04-2011 05:34 AM

Quote:

Originally Posted by trevius (Post 199321)
We could probably create a rule with minimal work by setting the hard coded stuff to use 32 as the max, then add in patches to each of the common/PatchName.cpp files similar to this example:

I agree that's probably the easiest way to do it, as long as there's bounds checking on the rule value so we don't end up with a bunch of "hey, why does my server crash when I set this to 50?!?" posts. :)

trevius 05-04-2011 07:03 AM

That looks pretty good, Zothen. Though we would probably have to do testing on each client to see if they break if more are sent than the client allows. I would suggest maybe changing your SoD.cpp (and other patch files) from this:

Code:

        if ( iMaxChars > UPPER_CHAR_LIMIT )
                iMaxChars = UPPER_CHAR_LIMIT;

to something like this:


Code:

        if ( iMaxChars > 10 )
                iMaxChars = 10;

Or whatever the (assumed) hard cap is for each client to prevent potential client crashes. Though, if no clients care how many you send them, then I guess that wouldn't matter.

Zothen 05-04-2011 07:27 AM

Since the Titanium client doesnt own a listbox for character selection, a iMaxChars > 8 wouldnt make sense.

As for SoF+ clients, it should work. But youre right, tests need to be done on that.
Too bad I dont have SoF, else I would test it myself. I can test SoD for a limit of 32 chars though.

trevius 05-04-2011 08:36 AM

Hmm, looks like the Titanium encode is already set to be able to send up to 10, so maybe the clients can handle higher numbers after-all:

Code:

ENCODE(OP_SendCharInfo) {
        ENCODE_LENGTH_EXACT(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++) {
                        OUT(equip[r][k]);
                        OUT(cs_colors[r][k].color);
                }
                OUT(haircolor[r]);
                OUT(gohome[r]);
            OUT(tutorial[r]);
                OUT(deity[r]);
                OUT(beardcolor[r]);
                eq->unknown820[r] = 0xFF;
                eq->unknown902[r] = 0xFF;
        }
        FINISH_ENCODE();
}


Zothen 05-04-2011 09:09 AM

Handle yes, display no. ;)


All times are GMT -4. The time now is 03:06 PM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.