Forgive me if I do something taboo, I have only been following this scene for a little over a week. Any way I dove head first into the code and decided to fix a few things that were bugging me (err.. yeah).
Any way here is what I did:
-Zone Bind Points (added some fields to start_zones and fixed an issue that would crash the client if you died to your bind point)
-Fixed an issue that was causing you character creation skin selections to be lost, now everyone doesn't have to look the same (Yeeaa!!)
Additions
-the start_zone table now has support for wildcarding on selection this means that DB developers don't have to put in (number of selections on map screen) *(number of races) *(number of classes)*(number of dieties) = number records in the database. putting a -1 in of these fields in the DB basically selects all of the combinations for that field. So if I only wanted to have 1 starting point for each class I would put -1 in the diety, choice and race coloumn for each record.
When specifying start_zones just remeber this SQL statement (its the one we use now to get start_zones):
Code:
SELECT x,y,z,zone_id,bind_id,bind_x,bind_y,bind_z FROM start_zones WHERE (player_choice=%i OR player_choice=-1) and (player_class=%i OR player_class=-1) and (player_deity=%i OR player_deity=-1) and (player_race=%i OR player_race=-1) ORDER BY select_rank DESC
select_rank allows you to assign importance to the start_zone record basically the more specific the start_zone record (records with more -1 wildcards are less specific) the greater the select_rank should be. A good rule is to start at 50 and for every wildcard you use subtract 10 from this value.
This allows you to specify things like I want all characters to start in the nexus as a default. However I want all warriors to start in Halas. This would require 2 records in the DB, one for the nexus with -1 for race,choice,class and diety and a select rank of say 10. And one for the warriors that go to halas and it would have -1 for race, choice, and diety and a select_rank of say 20. If later you wanted to further specify where agnostic warriors go, you would just add one more record with a higher select_rank.
Zoneing Fixes
well after I added that code I realised that bound characters that die crash thier clients (never knew that since I was never bound before). So I decided to hunt that bug down. It turns out that this peice of code was the culperit
Code:
if (zc->zoneID != 0 && dead)
{
#ifdef GUILDWARS
if(animation > 65 && admin<80 && CheckCheat()){
if(cheater || cheatcount>0){
Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
char descript[50]={0};
sprintf(descript,"%s: %i","Death zone cheat");
database.logevents(this->AccountName(),this->AccountID(),admin,this->GetName(),"none","Death zone cheat",descript,15);
if(cheater==false){
worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater. %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",this->GetName(),this->AccountName());
cheater=true;
}
cheatcount=0;
}
else
cheatcount++;
}
#else
break;
#endif
}
This section executes in the OP_ZoneChange case under handlepacket in client_process.cpp. Any way basically if you died and the zone change packet specified a zone (other than 0) then you would break??? You got a zone change requrest why wouldn't you want to finish it? Anyway taking out the break fixed the issue it seems.
I also cleaned up the code a little in OP_ZoneChange so you wouldn't do wierd things like go to -3,-3,-3 when there was a perfectly acceptable safe_zone to go too.
Character skins
The last thing I decided to tackle was character skins and this was acutually the hardest thing of all to debug. First I corrected struct that recieved info from the client on character creation as there were a few guessed at spots inside that struct. I did this through numerous tedious trial and error chracter creations (Ie create a gnome a certian way, then create a second gnome the exact same way except this time change just his beard and analysis the data we get).
After I got the data for CharCreate_Struct ok, I moved onto trying to figure out why these character specific skin changes never got back to client. It turns out that CharacterSelect_Struct had about 60 bytes of unknown data. So I then went through a fairly painful process of trying to figure out what order the data went in on its way back. Fortunatly I was able to figure it out finally. It was challenging though. Hope everyone can benefit from some of my work at least a little

.
Here are the diffs:
First SQL changes
Code:
ALTER TABLE start_zones ADD bind_x FLOAT DEFAULT "0" NOT NULL
ALTER TABLE start_zones ADD bind_y FLOAT DEFAULT "0" NOT NULL
ALTER TABLE start_zones ADD bind_z FLOAT DEFAULT "0" NOT NULL
ALTER TABLE start_zones ADD select_rank TINYINT UNSIGNED DEFAULT "50" NOT NULL
Now the database.cpp diff:
Code:
Index: database.cpp
===================================================================
RCS file: /cvsroot/eqemu/eqemu/eqemu/Source/common/database.cpp,v
retrieving revision 1.1.1.11
diff -r1.1.1.11 database.cpp
1164,1165c1164
< cs->gender[char_num] = pp->gender;
< cs->face[char_num] = pp->face;
---
> cs->gender[char_num] = pp->gender;
1167a1167,1174
> 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->hair[char_num] = pp->hairstyle;
> cs->beard[char_num] = pp->beard;
>
6551c6558
< cout<<"Choice:"<<in_cc->start_zone<<endl;
---
> //cout<<"Choice:"<<in_cc->start_zone<<" Class:"<<in_cc->class_<<" Race:"<<in_cc->race<<" Diety:"<<in_cc->deity<<endl;
6556c6563,6564
< if (RunQuery(query, MakeAnyLenString(&query, "SELECT x,y,z,zone_id,bind_id FROM start_zones WHERE player_choice=%i and player_class=%i and player_deity=%i and player_race=%i", in_cc->start_zone, in_cc->class_, in_cc->deity, in_cc->race), errbuf, &result)) {
---
> int qLen = MakeAnyLenString(&query, "SELECT x,y,z,zone_id,bind_id,bind_x,bind_y,bind_z FROM start_zones WHERE (player_choice=%i OR player_choice=-1) and (player_class=%i OR player_class=-1) and (player_deity=%i OR player_deity=-1) and (player_race=%i OR player_race=-1) ORDER BY select_rank DESC", in_cc->start_zone, in_cc->class_, in_cc->deity, in_cc->race);
> if (RunQuery(query,qLen,errbuf,&result)) {
6564c6572,6597
< in_pp->bind_zone_id = atoi(row[4]);
---
> in_pp->bind_zone_id = atoi(row[4]);
> //if we haven't set a bind_zone_id lets set it to the starting zone
> if (in_pp->bind_zone_id ==0) in_pp->bind_zone_id = in_pp->zone_id;
> in_pp->bind_x = atof(row[5]);
> in_pp->bind_y = atof(row[6]);
> in_pp->bind_z = atof(row[7]);
> //if bind x,y,z are all 0 then we need as a default to use the safe point
> //for the bound zone
> if (in_pp->bind_x ==0 && in_pp->bind_y ==0 && in_pp->bind_z ==0){
> char *query2 = 0;
> MYSQL_RES *result2;
> MYSQL_ROW row2;
> if (RunQuery(query2, MakeAnyLenString(&query2, "SELECT safe_x,safe_y,safe_z FROM zone WHERE zoneidnumber=%i",in_pp->bind_zone_id), errbuf, &result2)) {
> if (mysql_num_rows(result2) != 0) {
> row2 = mysql_fetch_row(result2);
> in_pp->bind_x = atof(row[0]);
> in_pp->bind_y = atof(row[1]);
> in_pp->bind_z = atof(row[2]);
> }
> mysql_free_result(result2);
> } else {
> safe_delete_array(query2);
> LogFile->write(EQEMuLog::Error, "Database: could not find a zone entry in database for the starting bound zone.");
> }
>
> }
6569c6602,6603
< LogFile->write(EQEMuLog::Error, "Database: could not find start_zones entry in database. Using Defaults..");
---
> LogFile->write(EQEMuLog::Error, "Database: could not find a start_zones entry in database for this choice,class,race,diety. Using Defaults..");
>
6574c6608,6609
< LogFile->write(EQEMuLog::Error, "Database: could not find start_zones table in database. Using Defaults..");
---
> LogFile->write(EQEMuLog::Error, errbuf);
> LogFile->write(EQEMuLog::Error, "Database: SQL Error. Using Defaults..");
eq_packet_structs.h Diff
Code:
Index: eq_packet_structs.h
===================================================================
RCS file: /cvsroot/eqemu/eqemu/eqemu/Source/common/eq_packet_structs.h,v
retrieving revision 1.1.1.10
diff -r1.1.1.10 eq_packet_structs.h
151c151,156
< /*1600*/ int8 unknown1600[60]; // ***Placeholder
---
> /*1600*/ int8 haircolor[10];
> /*1610*/ int8 beardcolor[10];
> /*1620*/ int8 eyecolor2[10];
> /*1630*/ int8 eyecolor1[10];
> /*1640*/ int8 hair[10];
> /*1650*/ int8 beard[10];
604,606c609,611
< /*0068*/ int32 haircolor; //guess
< /*0072*/ int32 eyecolor1; //guess
< /*0076*/ int32 eyecolor2; //guess
---
> /*0068*/ int32 haircolor;
> /*0072*/ int32 beard;
> /*0076*/ int32 beardcolor;
638,640c643,645
< /*0128*/ int32 beard;//guess
< /*0132*/ int32 beardcolor;//guess
< /*0136*/ int32 face;
---
> /*0128*/ int32 face;
> /*0132*/ int32 eyecolor1;//its possiable we could have these switched
> /*0136*/ int32 eyecolor2;//since setting one sets the other we really can't check
and finally the client_process.cpp diff
Code:
Index: client_process.cpp
===================================================================
RCS file: /cvsroot/eqemu/eqemu/eqemu/Source/zone/client_process.cpp,v
retrieving revision 1.1.1.12
diff -r1.1.1.12 client_process.cpp
98,99c98
< #endif
<
---
> #endif
1098c1097
< case OP_Death: {
---
> case OP_Death: {
1100,1101c1099
< break;
<
---
> break;
1585c1583
< case OP_ZoneChange: {
---
> case OP_ZoneChange: {
1606a1605,1606
>
> #ifdef GUILDWARS
1609,1618c1609,1619
< #ifdef GUILDWARS
< if(animation > 65 && admin<80 && CheckCheat()){
< if(cheater || cheatcount>0){
< Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
< char descript[50]={0};
< sprintf(descript,"%s: %i","Death zone cheat");
< database.logevents(this->AccountName(),this->AccountID(),admin,this->GetName(),"none","Death zone cheat",descript,15);
< if(cheater==false){
< worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater. %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",this->GetName(),this->AccountName());
< cheater=true;
---
> if(animation > 65 && admin<80 && CheckCheat()){
> if(cheater || cheatcount>0){
> Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
> char descript[50]={0};
> sprintf(descript,"%s: %i","Death zone cheat");
> database.logevents(this->AccountName(),this->AccountID(),admin,this->GetName(),"none","Death zone cheat",descript,15);
> if(cheater==false){
> worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater. %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",this->GetName(),this->AccountName());
> cheater=true;
> }
> cheatcount=0;
1620c1621,1622
< cheatcount=0;
---
> else
> cheatcount++;
1622,1623d1623
< else
< cheatcount++;
1625,1626d1624
< #else
< break;
1628c1626
< }
---
>
1652,1655c1650,1651
<
< tarx=zonesummon_x;
< tary=zonesummon_y;
< tarz=zonesummon_z;
---
> //zone debugging
> ZonePoint* zp = zone_point;
1669,1673c1665,1673
< else if (zonesummon_x == -3 && zonesummon_y == -3 && (zonesummon_z == -3 || zonesummon_z == -30) && database.GetZoneName(m_pp.bind_zone_id)) {
< strcpy(target_zone, database.GetZoneName(m_pp.bind_zone_id));
< tarx = m_pp.bind_x;
< tary = m_pp.bind_y;
< tarz = m_pp.bind_z;
---
> else if (zonesummon_x == -3 && zonesummon_y == -3 && (zonesummon_z == -3 || zonesummon_z == -30)) {
> if (database.GetZoneName(m_pp.bind_zone_id)){
> //zoneing to bind point
> strcpy(target_zone, database.GetZoneName(m_pp.bind_zone_id));
> tarx = m_pp.bind_x;
> tary = m_pp.bind_y;
> tarz = m_pp.bind_z;
>
> } //else bind point isn't set and we will zone to the zone safe point
1693c1693
< tarheading = zone_point->target_heading;
---
> tarheading = zone_point->target_heading;
1696a1697
>
1699a1701
>
1704c1706
< else {
---
> else {
1707,1709c1709,1711
< tarx=-1;
< tary=-1;
< tarz=-1;
---
> //tarx=-1;
> //tary=-1;
> //tarz=-1;
1719c1721
< if (target_zone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) {
---
> if (target_zone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) {
1737c1739
< if (m_pp.zone_id == zone->GetZoneID()) {
---
> if (m_pp.zone_id == zone->GetZoneID()) {
1749a1752,1753
> //zoneing to another zone so we need to the let the world server
> //handle things with the client for a while
1763,1764c1767,1768
< else {
< LogFile->write(EQEMuLog::Error, "Zone %i is not available", zc->zoneID);
---
> else {
> LogFile->write(EQEMuLog::Error, "Zone %i is not available because target wasn't found or character insufficent level", zc->zoneID);
Feel free to critique...
Ves