diff options
-rw-r--r-- | src/char/char.c | 294 | ||||
-rw-r--r-- | src/char_sql/char.c | 567 |
2 files changed, 362 insertions, 499 deletions
diff --git a/src/char/char.c b/src/char/char.c index d3bd322c7..37b779f79 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -78,7 +78,7 @@ char bind_ip_str[128]; uint32 bind_ip = INADDR_ANY; uint16 char_port = 6121; int char_maintenance = 0; -int char_new = 1; +bool char_new = true; int char_new_display = 0; int email_creation = 0; // disabled by default @@ -103,7 +103,8 @@ struct s_subnet { } subnet[16]; int subnet_count = 0; -struct char_session_data{ +struct char_session_data { + int fd; int account_id, login_id1, login_id2, sex; int found_char[MAX_CHARS]; char email[40]; // e-mail (default: a@a.com) by [Yor] @@ -1148,106 +1149,80 @@ int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) //----------------------------------- // Function to create a new character //----------------------------------- -int make_new_char(int fd, unsigned char *dat) +int make_new_char(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int char_num, int hair_color, int hair_style) { - int i; - struct char_session_data *sd; char name[NAME_LENGTH]; + int i; - sd = (struct char_session_data*)session[fd]->session_data; + safestrncpy(name, name_, NAME_LENGTH); + normalize_name(name,TRIM_CHARS); + if( remove_control_chars(name) ) { + char_log("Make new char error (control char received in the name): (connection #%d, account: %d).\n", sd->fd, sd->account_id); + return -2; + } - // remove control characters from the name - strncpy(name, dat, NAME_LENGTH); - name[NAME_LENGTH-1] = '\0'; //Trunc name to max possible value (23) - - normalize_name(name,TRIM_CHARS);//Normalize character name. [Skotlex] - - //check name != main chat nick [LuzZza] - if(strcmpi(name, main_chat_nick) == 0) { - char_log("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", - sd->account_id, name); - return -1; - } - - if (remove_control_chars(name)) { - char_log("Make new char error (control char received in the name): (connection #%d, account: %d).\n", - fd, sd->account_id); - return -1; + // check length of character name + if( name[0] == '\0' ) { + char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", sd->fd, sd->account_id, name); + return -2; } - // check lenght of character name - if (strlen(name) < 4) { - char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s').\n", - fd, sd->account_id, dat); + // check name != main chat nick [LuzZza] + if( strcmpi(name, main_chat_nick) == 0 ) { + char_log("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name); return -1; } - // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) { - char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, name, name[i]); - return -1; - } - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) { - char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c.\n", - fd, sd->account_id, dat, dat[i]); - return -1; - } - } // else, all letters/symbols are authorised (except control char removed before) - - if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5*6 || // stats - dat[30] >= MAX_CHARS || // slots (dat[30] can not be negativ) - dat[33] <= 0 || dat[33] >= 24 || // hair style - dat[31] >= 9) { // hair color (dat[31] can not be negativ) - char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + if (strcmpi(wisp_server_name, name) == 0) { + char_log("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s, stats: %d/%d/%d/%d/%d/%d, hair: %d, hair color: %d.\n", + sd->fd, sd->account_id, char_num, name, str, agi, vit, int_, dex, luk, hair_style, hair_color); return -1; } - // check individual stat value - for(i = 24; i <= 29; i++) { - if (dat[i] < 1 || dat[i] > 9) { - char_log("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct - - if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { - if (log_char) { - char_log("Make new char error (invalid stat value): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d\n", - fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; - } - } // now when we have passed all stat checks - - for(i = 0; i < char_num; i++) { + for( i = 0; i < char_num; i++ ) { + // check if name doesn't already exist if ((name_ignoring_case != 0 && strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0) || (name_ignoring_case == 0 && strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0)) { - char_log("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + char_log("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d/%d/%d/%d/%d/%d, hair: %d, hair color: %d.\n", + sd->fd, sd->account_id, char_num, name, char_dat[i].status.name, str, agi, vit, int_, dex, luk, hair_style, hair_color); return -1; } - if (char_dat[i].status.account_id == sd->account_id && char_dat[i].status.char_num == dat[30]) { - char_log("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], dat, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + // check if this account's slot is not already occupied + if (char_dat[i].status.account_id == sd->account_id && char_dat[i].status.char_num == char_num) { + char_log("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d/%d/%d/%d/%d/%d, hair: %d, hair color: %d.\n", + sd->fd, sd->account_id, char_num, name, char_dat[i].status.name, str, agi, vit, int_, dex, luk, hair_style, hair_color); return -1; } } - if (strcmp(wisp_server_name, name) == 0) { - char_log("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], name, char_dat[i].status.name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); - return -1; + // Check Authorised letters/symbols in the name of the character + if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised + for( i = 0; i < NAME_LENGTH && name[i]; i++ ) + if (strchr(char_name_letters, name[i]) == NULL) + return -2; + } else + if( char_name_option == 2 ) { // letters/symbols in char_name_letters are forbidden + for( i = 0; i < NAME_LENGTH && name[i]; i++ ) + if( strchr(char_name_letters, name[i]) != NULL ) + return -2; + } // else, all letters/symbols are authorised (except control char removed before) + + //check other inputs + if((char_num >= MAX_CHARS) // slots + || (hair_style >= 24) // hair style + || (hair_color >= 9) // hair color + || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats + || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values + || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs + { + char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d/%d/%d/%d/%d/%d, hair: %d, hair color: %d\n", + sd->fd, sd->account_id, char_num, name, str, agi, vit, int_, dex, luk, hair_style, hair_color); + return -2; } if (char_num >= char_max) { char_max += 256; - char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max); + RECREATE(char_dat, struct character_data, char_max); if (!char_dat) { ShowFatalError("Out of memory: make_new_char (realloc of char_dat).\n"); char_log("Out of memory: make_new_char (realloc of char_dat).\n"); @@ -1255,14 +1230,14 @@ int make_new_char(int fd, unsigned char *dat) } } - char_log("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d.\n", - fd, sd->account_id, dat[30], name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + char_log("Creation of New Character: (connection #%d, account: %d) slot %d, character Name: %s, stats: %d/%d/%d/%d/%d/%d, hair: %d, hair color: %d.\n", + sd->fd, sd->account_id, char_num, name, str, agi, vit, int_, dex, luk, hair_style, hair_color); memset(&char_dat[i], 0, sizeof(struct character_data)); char_dat[i].status.char_id = char_id_count++; char_dat[i].status.account_id = sd->account_id; - char_dat[i].status.char_num = dat[30]; + char_dat[i].status.char_num = char_num; strcpy(char_dat[i].status.name,name); char_dat[i].status.class_ = 0; char_dat[i].status.base_level = 1; @@ -1270,12 +1245,12 @@ int make_new_char(int fd, unsigned char *dat) char_dat[i].status.base_exp = 0; char_dat[i].status.job_exp = 0; char_dat[i].status.zeny = start_zeny; - char_dat[i].status.str = dat[24]; - char_dat[i].status.agi = dat[25]; - char_dat[i].status.vit = dat[26]; - char_dat[i].status.int_ = dat[27]; - char_dat[i].status.dex = dat[28]; - char_dat[i].status.luk = dat[29]; + char_dat[i].status.str = str; + char_dat[i].status.agi = agi; + char_dat[i].status.vit = vit; + char_dat[i].status.int_ = int_; + char_dat[i].status.dex = dex; + char_dat[i].status.luk = luk; char_dat[i].status.max_hp = 40 * (100 + char_dat[i].status.vit) / 100; char_dat[i].status.max_sp = 11 * (100 + char_dat[i].status.int_) / 100; char_dat[i].status.hp = char_dat[i].status.max_hp; @@ -1287,8 +1262,8 @@ int make_new_char(int fd, unsigned char *dat) char_dat[i].status.manner = 0; char_dat[i].status.party_id = 0; char_dat[i].status.guild_id = 0; - char_dat[i].status.hair = dat[33]; - char_dat[i].status.hair_color = dat[31]; + char_dat[i].status.hair = hair_style; + char_dat[i].status.hair_color = hair_color; char_dat[i].status.clothes_color = 0; char_dat[i].status.inventory[0].nameid = start_weapon; // Knife char_dat[i].status.inventory[0].amount = 1; @@ -3369,16 +3344,17 @@ int parse_char(int fd) return 0; } - while(RFIFOREST(fd) >= 2) + while( RFIFOREST(fd) >= 2 ) { //For use in packets that depend on an sd being present [Skotlex] #define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } cmd = RFIFOW(fd,0); - switch(cmd) + switch( cmd ) { - case 0x65: // request to connect + // request to connect + case 0x65: ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); if (RFIFOREST(fd) < 17) return 0; @@ -3388,6 +3364,7 @@ int parse_char(int fd) if (sd) { //Received again auth packet for already authentified account?? Discard it. //TODO: Perhaps log this as a hack attempt? + //TODO: and perhaps send back a reply? RFIFOSKIP(fd,17); break; } @@ -3398,26 +3375,28 @@ int parse_char(int fd) CREATE(session[fd]->session_data, struct char_session_data, 1); sd = (struct char_session_data*)session[fd]->session_data; + sd->fd = fd; strncpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail - sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + sd->connect_until_time = 0; // unknown or unlimited (not displaying on map-server) sd->account_id = RFIFOL(fd,2); sd->login_id1 = RFIFOL(fd,6); sd->login_id2 = RFIFOL(fd,10); sd->sex = RFIFOB(fd,16); + // send back account_id WFIFOHEAD(fd,4); WFIFOL(fd,0) = RFIFOL(fd,2); WFIFOSET(fd,4); + // search authentification - for(i = 0; i < AUTH_FIFO_SIZE && !( + ARR_FIND( 0, AUTH_FIFO_SIZE, i, auth_fifo[i].account_id == sd->account_id && auth_fifo[i].login_id1 == sd->login_id1 && auth_fifo[i].login_id2 == sd->login_id2 && auth_fifo[i].ip == session[fd]->client_addr && - auth_fifo[i].delflag == 2) - ; i++); + auth_fifo[i].delflag == 2 ); - if (i < AUTH_FIFO_SIZE) { + if( i < AUTH_FIFO_SIZE ) { auth_fifo[i].delflag = 1; char_auth_ok(fd, sd); } else { // authentication not found @@ -3437,12 +3416,13 @@ int parse_char(int fd) WFIFOSET(fd,3); } } + } RFIFOSKIP(fd,17); - } break; - case 0x66: // char select + // char select + case 0x66: FIFOSD_CHECK(3); { int char_num = RFIFOB(fd,2); @@ -3459,9 +3439,7 @@ int parse_char(int fd) break; } // otherwise, load the character - for (ch = 0; ch < MAX_CHARS; ch++) - if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num) - break; + ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num ); if (ch == MAX_CHARS) { //Not found?? May be forged packet. break; @@ -3478,9 +3456,7 @@ int parse_char(int fd) if (i < 0) { unsigned short j; //First check that there's actually a map server online. - for(j = 0; j < MAX_MAP_SERVERS; j++) - if (server_fd[j] >= 0 && server[j].map[0]) - break; + ARR_FIND( 0, MAX_MAP_SERVERS, j, server_fd[j] >= 0 && server[j].map[0] ); if (j == MAX_MAP_SERVERS) { ShowInfo("Connection Closed. No map servers available.\n"); WFIFOHEAD(fd,3); @@ -3557,7 +3533,8 @@ int parse_char(int fd) WFIFOB(fd,2) = 1; // 01 = Server closed WFIFOSET(fd,3); break; - } + } + //Send auth to server. WFIFOHEAD(map_fd, 20 + sizeof(struct mmo_charstatus)); WFIFOW(map_fd,0) = 0x2afd; @@ -3574,13 +3551,15 @@ int parse_char(int fd) } break; - case 0x67: // make new + // create new char + // S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <char num>.B <hair color>.W <hair style>.W + case 0x67: FIFOSD_CHECK(37); - if(char_new == 0) //turn character creation on/off [Kevin] + if( !char_new ) //turn character creation on/off [Kevin] i = -2; else - i = make_new_char(fd, RFIFOP(fd,2)); + i = make_new_char(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27),RFIFOB(fd,28),RFIFOB(fd,29),RFIFOB(fd,30),RFIFOB(fd,31),RFIFOB(fd,32),RFIFOW(fd,33),RFIFOW(fd,35)); //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) if (i < 0) @@ -3596,27 +3575,28 @@ int parse_char(int fd) RFIFOSKIP(fd,37); break; } - - { //Send to player. + else + { int len; + // send to player WFIFOHEAD(fd,110); WFIFOW(fd,0) = 0x6d; len = 2 + mmo_char_tobuf(WFIFOP(fd,2), &char_dat[i].status); WFIFOSET(fd,len); - } - for(ch = 0; ch < MAX_CHARS; ch++) { - if (sd->found_char[ch] == -1) { - sd->found_char[ch] = i; - break; - } + // add new entry to the chars list + ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 ); + if( ch < MAX_CHARS ) + sd->found_char[ch] = i; } RFIFOSKIP(fd,37); break; - case 0x68: // delete char - case 0x1fb: // 2004-04-19aSakexe+ langtype 12 char deletion packet + // delete char + case 0x68: + // 2004-04-19aSakexe+ langtype 12 char deletion packet + case 0x1fb: if (cmd == 0x68) FIFOSD_CHECK(46); if (cmd == 0x1fb) FIFOSD_CHECK(56); { @@ -3641,24 +3621,24 @@ int parse_char(int fd) break; } // we change the packet to set it like selection. - for (i = 0; i < MAX_CHARS; i++) - if (sd->found_char[i] != -1 && char_dat[sd->found_char[i]].status.char_id == cid) { - // we save new e-mail - memcpy(sd->email, email, 40); - // we send new e-mail to login-server ('online' login-server is checked before) - WFIFOHEAD(login_fd,46); - WFIFOW(login_fd,0) = 0x2715; - WFIFOL(login_fd,2) = sd->account_id; - memcpy(WFIFOP(login_fd, 6), email, 40); - WFIFOSET(login_fd,46); - // change value to put new packet (char selection) - RFIFOSKIP(fd,-3); //FIXME: Will this work? Messing with the received buffer is ugly anyway... - RFIFOW(fd,0) = 0x66; - RFIFOB(fd,2) = char_dat[sd->found_char[i]].status.char_num; - // not send packet, it's modify of actual packet - break; - } - if (i == MAX_CHARS) { + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] != -1 && char_dat[sd->found_char[i]].status.char_id == cid ); + if( i < MAX_CHARS ) + { + // we save new e-mail + memcpy(sd->email, email, 40); + // we send new e-mail to login-server ('online' login-server is checked before) + WFIFOHEAD(login_fd,46); + WFIFOW(login_fd,0) = 0x2715; + WFIFOL(login_fd,2) = sd->account_id; + memcpy(WFIFOP(login_fd, 6), email, 40); + WFIFOSET(login_fd,46); + // change value to put new packet (char selection) + RFIFOSKIP(fd,-3); //FIXME: Will this work? Messing with the received buffer is ugly anyway... + RFIFOW(fd,0) = 0x66; + RFIFOB(fd,2) = char_dat[sd->found_char[i]].status.char_num; + // not send packet, it's modify of actual packet + break; + } else { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x70; WFIFOB(fd,2) = 0; // 00 = Incorrect Email address @@ -3677,11 +3657,10 @@ int parse_char(int fd) break; } - for (i = 0; i < MAX_CHARS; i++) { - if (sd->found_char[i] == -1) continue; - if (char_dat[sd->found_char[i]].status.char_id == cid) break; - } - if (i == MAX_CHARS) { // Such a character does not exist in the account + // check if this char exists + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] != -1 && char_dat[sd->found_char[i]].status.char_id == cid ); + if( i == MAX_CHARS ) + { // Such a character does not exist in the account WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x70; WFIFOB(fd,2) = 0; @@ -3711,29 +3690,38 @@ int parse_char(int fd) } } char_num--; + + // remove char from list and compact it for(ch = i; ch < MAX_CHARS-1; ch++) sd->found_char[ch] = sd->found_char[ch+1]; sd->found_char[MAX_CHARS-1] = -1; + + /* Char successfully deleted.*/ WFIFOHEAD(fd,2); WFIFOW(fd,0) = 0x6f; WFIFOSET(fd,2); } break; - case 0x187: // R 0187 <account ID>.l - client keep-alive packet (every 12 seconds) + // client keep-alive packet (every 12 seconds) + // R 0187 <account ID>.l + case 0x187: if (RFIFOREST(fd) < 6) return 0; RFIFOSKIP(fd,6); break; - case 0x28d: // R 028d <account ID>.l <char ID>.l <new name>.24B - char rename request + // char rename request + // R 028d <account ID>.l <char ID>.l <new name>.24B + case 0x28d: if (RFIFOREST(fd) < 34) return 0; //not implemented RFIFOSKIP(fd,34); break; - case 0x2af8: // login as map-server + // login as map-server + case 0x2af8: if (RFIFOREST(fd) < 60) return 0; { @@ -3779,8 +3767,8 @@ int parse_char(int fd) } break; - case 0x7530: // Athena info get - { + // Athena info get + case 0x7530: WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x7531; WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; @@ -3791,15 +3779,17 @@ int parse_char(int fd) WFIFOB(fd,7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; WFIFOW(fd,8) = ATHENA_MOD_VERSION; WFIFOSET(fd,10); + RFIFOSKIP(fd,2); - return 0; - } + break; - case 0x7532: // disconnect request from login server + // disconnect request from login server + case 0x7532: set_eof(fd); return 0; - default: // unknown packet received + // unknown packet received + default: ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL)); set_eof(fd); return 0; @@ -4120,7 +4110,7 @@ int char_config_read(const char *cfgName) } else if (strcmpi(w1, "char_maintenance") == 0) { char_maintenance = atoi(w2); } else if (strcmpi(w1, "char_new") == 0) { - char_new = atoi(w2); + char_new = (bool)atoi(w2); } else if (strcmpi(w1, "char_new_display") == 0) { char_new_display = atoi(w2); } else if (strcmpi(w1, "email_creation") == 0) { diff --git a/src/char_sql/char.c b/src/char_sql/char.c index ffe02f4a3..a02bc32d9 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -97,7 +97,7 @@ char bind_ip_str[128]; uint32 bind_ip = INADDR_ANY; uint16 char_port = 6121; int char_maintenance = 0; -int char_new = 1; +bool char_new = true; int char_new_display = 0; int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] @@ -131,7 +131,8 @@ struct s_subnet { } subnet[16]; int subnet_count = 0; -struct char_session_data{ +struct char_session_data { + int fd; int account_id, login_id1, login_id2, sex; int found_char[MAX_CHARS]; char email[40]; // e-mail (default: a@a.com) by [Yor] @@ -267,6 +268,7 @@ void set_char_offline(int char_id, int account_id) struct mmo_charstatus *cp; struct online_char_data* character; + //FIXME: usage of 'magic constant'; this needs to go! [ultramage] if ( char_id == 99 ) { if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `online`='0' WHERE `account_id`='%d'", char_db, account_id) ) @@ -1087,141 +1089,104 @@ int mmo_char_sql_init(void) //========================================================================================================== -int make_new_char_sql(int fd, unsigned char *dat) +int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int char_num, int hair_color, int hair_style) { - struct char_session_data *sd; char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1]; - unsigned int i; // Used in for loop and comparing with strlen, safe to be unsigned. [Lance] + unsigned int i; int char_id; + bool valid; - safestrncpy(name, dat, NAME_LENGTH); - normalize_name(name,TRIM_CHARS); //Normalize character name. [Skotlex] + safestrncpy(name, name_, NAME_LENGTH); + normalize_name(name,TRIM_CHARS); Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if (!session_isValid(fd) || !(sd = (struct char_session_data*)session[fd]->session_data)) - return -2; - ShowInfo("New character request (%d)\n", sd->account_id); - // check lenght of character name - if (strlen(name) < 4) { - ShowInfo("Create char failed (character name too small): (connection #%d, account: %d, name: '%s').\n", - fd, sd->account_id, dat); + // check the number of already existing chars in this account + if( char_per_account != 0 ) { + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id) ) + Sql_ShowDebug(sql_handle); + if( Sql_NumRows(sql_handle) >= char_per_account ) { + ShowInfo("Create char failed (%d): charlimit exceeded.\n", sd->account_id); + Sql_FreeResult(sql_handle); + return -2; + } + } + + // check char slot. + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'", char_db, sd->account_id, char_num) ) + Sql_ShowDebug(sql_handle); + if( Sql_NumRows(sql_handle) > 0 ) + { + ShowWarning("Create char failed (%d, slot: %d), slot already in use\n", sd->account_id, char_num); return -2; } - //check name != main chat nick [LuzZza] - if(strcmpi(name, main_chat_nick) == 0) { - ShowInfo("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name); + // check length of character name + if( name[0] == '\0' ) { + ShowInfo("Create char failed (empty character name): (connection #%d, account: %d).\n", sd->fd, sd->account_id); return -2; } - //check for charcount (maxchars) :) - if(char_per_account != 0){ - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id) ) - Sql_ShowDebug(sql_handle); - if( Sql_NumRows(sql_handle) >= char_per_account ) - { - //hehe .. limit exceeded :P - ShowInfo("Create char failed (%d): charlimit exceeded.\n", sd->account_id); - Sql_FreeResult(sql_handle); - return -2; - } + // check name != main chat nick [LuzZza] + if( strcmpi(name, main_chat_nick) == 0 ) { + ShowInfo("Create char failed (%d): this nick (%s) reserved for mainchat messages.\n", sd->account_id, name); + return -2; } // Check Authorised letters/symbols in the name of the character - if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) == NULL) + if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised + for( i = 0; i < NAME_LENGTH && name[i]; i++ ) + if( strchr(char_name_letters, name[i]) == NULL ) return -2; - } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden - for (i = 0; i < NAME_LENGTH && name[i]; i++) - if (strchr(char_name_letters, name[i]) != NULL) + } else + if( char_name_option == 2 ) { // letters/symbols in char_name_letters are forbidden + for( i = 0; i < NAME_LENGTH && name[i]; i++ ) + if( strchr(char_name_letters, name[i]) != NULL ) return -2; } // else, all letters/symbols are authorised (except control char removed before) - //check stat error - if ((dat[24]+dat[25]+dat[26]+dat[27]+dat[28]+dat[29]!=6*5 ) || // stats - (dat[30] >= MAX_CHARS) || // slots (dat[30] can not be negativ) - (dat[33] <= 0) || (dat[33] >= 24) || // hair style - (dat[31] >= 9)) { // hair color (dat[31] can not be negativ) - if (log_char) { - // char.log to charlog - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db,"make new char error", sd->account_id, dat[30], esc_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]) ) - Sql_ShowDebug(sql_handle); - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); + // check name (already in use?) + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) { + Sql_ShowDebug(sql_handle); return -2; - } // for now we have checked: stat points used <31, char slot is less then 9, hair style/color values are acceptable + } + if( Sql_NumRows(sql_handle) > 0 ) { + ShowInfo("Create char failed: charname already in use\n"); + return -1; + } - // check individual stat value - for(i = 24; i <= 29; i++) { - if (dat[i] < 1 || dat[i] > 9) { - if (log_char) { - // char.log to charlog - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db, "make new char error", sd->account_id, dat[30], esc_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]) ) - Sql_ShowDebug(sql_handle); - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); - return -2; - } - } // now we know that every stat has proper value but we have to check if str/int agi/luk vit/dex pairs are correct - - if( ((dat[24]+dat[27]) > 10) || ((dat[25]+dat[29]) > 10) || ((dat[26]+dat[28]) > 10) ) { - if (log_char) { - // char.log to charlog - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db, "make new char error", sd->account_id, dat[30], esc_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]) ) - Sql_ShowDebug(sql_handle); - } - ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); - return -2; - } // now when we have passed all stat checks + //check other inputs + if((char_num >= MAX_CHARS) // slots + || (hair_style >= 24) // hair style + || (hair_color >= 9) // hair color + || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats + || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values + || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs + valid = false; + else + valid = true; + // log result if (log_char) { - // char.log to charlog - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" + if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db, "make new char", sd->account_id, dat[30], esc_name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]) ) + charlog_db,(valid?"make new char":"make new char error"), sd->account_id, char_num, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) ) Sql_ShowDebug(sql_handle); } - //printf("make new char %d-%d %s %d, %d, %d, %d, %d, %d - %d, %d\n", - // fd, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); - - //Check Name (already in use?) - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) - { - Sql_ShowDebug(sql_handle); - return -2; - } - if( Sql_NumRows(sql_handle) > 0 ) - { - ShowInfo("Create char failed: charname already in use\n"); - return -1; - } - // check char slot. - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_num` FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'", char_db, sd->account_id, dat[30]) ) - Sql_ShowDebug(sql_handle); - if( Sql_NumRows(sql_handle) > 0 ) - { - ShowWarning("Create char failed (%d, slot: %d), slot already in use\n", sd->account_id, dat[30]); + if( !valid ) { + ShowWarning("Create char failed (%d): stats error (bot cheat?!)\n", sd->account_id); return -2; } - //New Querys [Sirius] //Insert the char to the 'chardb' ^^ if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`," "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES (" "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')", - char_db, sd->account_id , dat[30] , esc_name, start_zeny, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], - (40 * (100 + dat[26])/100) , (40 * (100 + dat[26])/100 ), (11 * (100 + dat[27])/100), (11 * (100 + dat[27])/100), dat[33], dat[31], + char_db, sd->account_id , char_num, esc_name, start_zeny, str, agi, vit, int_, dex, luk, + (40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color, mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) ) { Sql_ShowDebug(sql_handle); @@ -1240,7 +1205,7 @@ int make_new_char_sql(int fd, unsigned char *dat) Sql_ShowDebug(sql_handle); } - ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, dat[30], name); + ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, char_num, name); return char_id; } @@ -1307,39 +1272,13 @@ int delete_char_sql(int char_id, int partner_id) if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'", pet_db, char_id) ) Sql_ShowDebug(sql_handle); - // Komurka's suggested way to clear pets, modified by [Skotlex] (because I always personalize what I do :X) - //Removing pets that are in the char's inventory.... - { //NOTE: The syntax for multi-table deletes is a bit changed between 4.0 and 4.1 regarding aliases, so we have to consider the version... [Skotlex] - //Since we only care about the major and minor version, a double conversion is good enough. (4.1.20 -> 4.10000) - //double mysql_version = atof(mysql_get_server_info(&mysql_handle)); - // - //sprintf(tmp_sql, - //"delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", - // (mysql_version<4.1?pet_db:"p"), char_db, inventory_db, pet_db, char_id); - // - //if(mysql_query(&mysql_handle, tmp_sql)) { - // ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - // ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - //} - - ////Removing pets that are in the char's cart.... - //sprintf(tmp_sql, - //"delete FROM `%s` USING `%s` as c LEFT JOIN `%s` as i ON c.char_id = i.char_id, `%s` as p WHERE c.char_id = '%d' AND i.card0 = -256 AND p.pet_id = (i.card1|(i.card2<<2))", - // (mysql_version<4.1?pet_db:"p"), char_db, cart_db, pet_db, char_id); - // - //if(mysql_query(&mysql_handle, tmp_sql)) { - // ShowSQL("DB error - %s\n",mysql_error(&mysql_handle)); - // ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - //} - - //## TODO double-check the functionality of this query - if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE pet_id IN " - "(SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256" - " UNION" - " SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256)", - pet_db, inventory_db, char_id, cart_db, char_id) ) - Sql_ShowDebug(sql_handle); - } + //Delete all pets that are stored in eggs (inventory + cart) + if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE pet_id IN " + "(SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256" + " UNION" + " SELECT card1|card2<<2 FROM `%s` WHERE char_id = '%d' AND card0 = -256)", + pet_db, inventory_db, char_id, cart_db, char_id) ) + Sql_ShowDebug(sql_handle); /* remove homunculus */ if (hom_id) @@ -1487,14 +1426,12 @@ int mmo_char_send006b(int fd, struct char_session_data* sd) set_char_online(-1, 99, sd->account_id); - //search char. + // load the char_ids of all chars on this account if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id` = '%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) ) Sql_ShowDebug(sql_handle); for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) { - // char_id - Sql_GetData(sql_handle, 0, &data, NULL); - sd->found_char[i] = atoi(data); + Sql_GetData(sql_handle, 0, &data, NULL); sd->found_char[i] = atoi(data); // char_id } found_num = i; for( ; i < MAX_CHARS; ++i ) @@ -1586,23 +1523,24 @@ int parse_fromlogin(int fd) sd = (struct char_session_data*)session[fd]->session_data; - // hehe. no need to set user limit on SQL version. :P - // but char limitation is good way to maintain server. :D while(RFIFOREST(fd) >= 2) { -// printf("parse_fromlogin : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); + uint16 command = RFIFOW(fd,0); + + switch( command ) + { - switch(RFIFOW(fd,0)) { + // acknowledgement of connect-to-loginserver request case 0x2711: if (RFIFOREST(fd) < 3) return 0; + if (RFIFOB(fd,2)) { //printf("connect login server error : %d\n", RFIFOB(fd, 2)); ShowError("Can not connect to login-server.\n"); ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n"); ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n"); - //exit(1); //fixed for server shutdown. }else { ShowStatus("Connected to login-server (connection #%d).\n", fd); @@ -1610,32 +1548,28 @@ int parse_fromlogin(int fd) send_accounts_tologin(-1, gettick(), 0, 0); // if no map-server already connected, display a message... - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server_fd[i] > 0 && server[i].map[0]) // if map-server online and at least 1 map - break; - if (i == MAX_MAP_SERVERS) + ARR_FIND( 0, MAX_MAP_SERVERS, i, server_fd[i] > 0 && server[i].map[0] ); + if( i == MAX_MAP_SERVERS ) ShowStatus("Awaiting maps from map-server.\n"); } RFIFOSKIP(fd,3); break; + // acknowledgement of account authentication request case 0x2713: if (RFIFOREST(fd) < 51) return 0; - for(i = 0; i < fd_max && !( - session[i] && - (sd = (struct char_session_data*)session[i]->session_data) && - sd->account_id == RFIFOL(fd,2)) - ; i++); - - if (i < fd_max) { - if (RFIFOB(fd,6) != 0) { + // find the session with this account id + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); + if( i < fd_max ) + { + if( RFIFOB(fd,6) != 0 ) { // failure WFIFOHEAD(i,3); WFIFOW(i,0) = 0x6c; WFIFOB(i,2) = 0x42; WFIFOSET(i,3); - } else { + } else { // success memcpy(sd->email, RFIFOP(fd, 7), 40); sd->connect_until_time = (time_t)RFIFOL(fd,47); char_auth_ok(i, sd); @@ -1644,17 +1578,17 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd,51); break; + // acknowledgement of e-mail/limited time request case 0x2717: if (RFIFOREST(fd) < 50) return 0; - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == RFIFOL(fd,2)) { - memcpy(sd->email, RFIFOP(fd,6), 40); - sd->connect_until_time = (time_t)RFIFOL(fd,46); - break; - } - } + + // find the session with this account id + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); + if( i < fd_max ) + { + memcpy(sd->email, RFIFOP(fd,6), 40); + sd->connect_until_time = (time_t)RFIFOL(fd,46); } RFIFOSKIP(fd,50); break; @@ -1666,36 +1600,11 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd,2); break; - // Receiving authentification from Freya-type login server (to avoid char->login->char) - case 0x2719: - if (RFIFOREST(fd) < 18) - return 0; - // to conserv a maximum of authentification, search if account is already authentified and replace it - // that will reduce multiple connection too - for(i = 0; i < AUTH_FIFO_SIZE; i++) - if (auth_fifo[i].account_id == RFIFOL(fd,2)) - break; - // if not found, use next value - if (i == AUTH_FIFO_SIZE) { - if (auth_fifo_pos >= AUTH_FIFO_SIZE) - auth_fifo_pos = 0; - i = auth_fifo_pos; - auth_fifo_pos++; - } - //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", i, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); - auth_fifo[i].account_id = RFIFOL(fd,2); - auth_fifo[i].char_id = 0; - auth_fifo[i].login_id1 = RFIFOL(fd,6); - auth_fifo[i].login_id2 = RFIFOL(fd,10); - auth_fifo[i].delflag = 2; // 0: auth_fifo canceled/void, 2: auth_fifo received from login/map server in memory, 1: connection authentified - auth_fifo[i].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) - auth_fifo[i].ip = ntohl(RFIFOL(fd,14)); - RFIFOSKIP(fd,18); - break; - - case 0x2721: // gm reply. I don't want to support this function. + // gm reply. I don't want to support this function. + case 0x2721: if (RFIFOREST(fd) < 10) return 0; + /* Note that this is the code from char-txt! Even uncommenting it will not work. printf("0x2721:GM reply\n"); { @@ -1722,14 +1631,14 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd, 10); break; - case 0x2723: // changesex reply (modified by [Yor]) + // changesex reply + case 0x2723: if (RFIFOREST(fd) < 7) return 0; { - int acc, sex; + int acc = RFIFOL(fd,2); + int sex = RFIFOB(fd,6); unsigned char buf[16]; - acc = RFIFOL(fd,2); - sex = RFIFOB(fd,6); if( acc > 0 ) { int char_id[MAX_CHARS]; @@ -1744,15 +1653,9 @@ int parse_fromlogin(int fd) Sql_ShowDebug(sql_handle); for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) { - // char_id - Sql_GetData(sql_handle, 0, &data, NULL); - char_id[i] = atoi(data); - // class - Sql_GetData(sql_handle, 1, &data, NULL); - class_[i] = atoi(data); - // guild_id - Sql_GetData(sql_handle, 2, &data, NULL); - guild_id[i] = atoi(data); + Sql_GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data); + Sql_GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data); + Sql_GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data); } num = i; for( i = 0; i < num; ++i ) @@ -1788,45 +1691,42 @@ int parse_fromlogin(int fd) } Sql_FreeResult(sql_handle); } + // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == acc) { - set_eof(i); - break; - } - } - } + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == acc ); + if( i < fd_max ) + set_eof(i); + // notify all mapservers about this change WBUFW(buf,0) = 0x2b0d; WBUFL(buf,2) = acc; WBUFB(buf,6) = sex; mapif_sendall(buf, 7); - - RFIFOSKIP(fd,7); } + RFIFOSKIP(fd,7); break; - // account_reg2•ÏX’Ê’m + // reply to an account_reg2 registry request case 0x2729: if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; + { //Receive account_reg2 registry, forward to map servers. unsigned char buf[ACCOUNT_REG2_NUM*(256+32+2)+16]; memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); // WBUFW(buf,0) = 0x2b11; WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] mapif_sendall(buf, WBUFW(buf,2)); - RFIFOSKIP(fd, RFIFOW(fd,2)); } + RFIFOSKIP(fd, RFIFOW(fd,2)); break; - // State change of account/ban notification (from login-server) by [Yor] + // State change of account/ban notification (from login-server) case 0x2731: if (RFIFOREST(fd) < 11) return 0; - // send to all map-servers to disconnect the player - { + + { // send to all map-servers to disconnect the player unsigned char buf[11]; WBUFW(buf,0) = 0x2b14; WBUFL(buf,2) = RFIFOL(fd,2); @@ -1835,20 +1735,18 @@ int parse_fromlogin(int fd) mapif_sendall(buf, 11); } // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == RFIFOL(fd,2)) { - set_eof(i); - break; - } - } - } + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); + if( i < fd_max ) + set_eof(i); + RFIFOSKIP(fd,11); break; + // gm account information from login server case 0x2732: if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; + if(!char_gm_read) { unsigned char buf[32000]; if (gm_account != NULL) @@ -1866,55 +1764,12 @@ int parse_fromlogin(int fd) memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); WBUFW(buf,0) = 0x2b15; mapif_sendall(buf, RFIFOW(fd,2)); - - RFIFOSKIP(fd,RFIFOW(fd,2)); } - break; - // Receive GM accounts [Freya login server packet by Yor] - case 0x2733: - if (RFIFOREST(fd) < 7) - return 0; - { - int new_level = 0; - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == RFIFOL(fd,2)) { - if (gm_account[i].level != (int)RFIFOB(fd,6)) { - gm_account[i].level = (int)RFIFOB(fd,6); - new_level = 1; - } - break; - } - // if not found, add it - if (i == GM_num) { - // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) - // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - if (((int)RFIFOB(fd,6)) > 0 && GM_num < 4000) { - if (GM_num == 0) { - gm_account = (struct gm_account*)aMalloc(sizeof(struct gm_account)); - } else { - gm_account = (struct gm_account*)aRealloc(gm_account, sizeof(struct gm_account) * (GM_num + 1)); - } - gm_account[GM_num].account_id = RFIFOL(fd,2); - gm_account[GM_num].level = (int)RFIFOB(fd,6); - new_level = 1; - GM_num++; - if (GM_num >= 4000) - ShowWarning("4000 GM accounts found. Next GM accounts are not readed.\n"); - } - } - if (new_level == 1) { - ShowStatus("From login-server: receiving GM account information (%d: level %d).\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); - //create_online_files(); // not change online file for only 1 player (in next timer, that will be done - // send gm acccounts level to map-servers - mapif_send_gmaccounts(); - } - - RFIFOSKIP(fd,7); - } + RFIFOSKIP(fd,RFIFOW(fd,2)); break; - //Login server request to kick a character out. [Skotlex] + // Login server request to kick a character out. [Skotlex] case 0x2734: if (RFIFOREST(fd) < 6) return 0; @@ -1923,32 +1778,35 @@ int parse_fromlogin(int fd) int aid = RFIFOL(fd,2); if ((character = idb_get(online_char_db, aid)) != NULL) { //Kick out this player. - if (character->server > -1) + if( character->server != -1 ) { //Kick it from the map server it is on. mapif_disconnectplayer(server_fd[character->server], character->account_id, character->char_id, 2); if (character->waiting_disconnect == -1) character->waiting_disconnect = add_timer(gettick()+15000, chardb_waiting_disconnect, character->account_id, 0); - } else { //Manual kick from char server. + } + else + { //Manual kick from char server. struct char_session_data *tsd; int i; - for(i = 0; i < fd_max; i++) { - if (session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid) - { - WFIFOHEAD(i,3); - WFIFOW(i,0) = 0x81; - WFIFOB(i,2) = 2; - WFIFOSET(i,3); - break; - } + ARR_FIND( 0, fd_max, i, session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid ); + if( i < fd_max ) + { + WFIFOHEAD(i,3); + WFIFOW(i,0) = 0x81; + WFIFOB(i,2) = 2; + WFIFOSET(i,3); + break; } - if (i == fd_max) //Shouldn't happen, but just in case. + else //Shouldn't happen, but just in case. set_char_offline(99, aid); } } - RFIFOSKIP(fd,6); } + + RFIFOSKIP(fd,6); break; + // ip address update signal from login server case 0x2735: { unsigned char buf[2]; @@ -1966,18 +1824,19 @@ int parse_fromlogin(int fd) { //Update ip. char_ip = new_ip; ShowInfo("Updating IP for [%s].\n", char_ip_str); + // notify login server about the change WFIFOHEAD(fd,6); WFIFOW(fd,0) = 0x2736; WFIFOL(fd,2) = htonl(char_ip); WFIFOSET(fd,6); } + } RFIFOSKIP(fd,2); - } break; default: - ShowError("Unknown packet 0x%04x received from login server, disconnecting.\n", RFIFOW(fd,0)); + ShowError("Unknown packet 0x%04x received from login server, disconnecting.\n", command); set_eof(fd); return 0; } @@ -2845,16 +2704,17 @@ int parse_char(int fd) return 0; } - while(RFIFOREST(fd) >= 2) + while( RFIFOREST(fd) >= 2 ) { //For use in packets that depend on an sd being present [Skotlex] #define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL) { RFIFOSKIP(fd,rest); return 0; } } cmd = RFIFOW(fd,0); - switch(cmd) + switch( cmd ) { - case 0x65: // request to connect + // request to connect + case 0x65: ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); if (RFIFOREST(fd) < 17) return 0; @@ -2862,31 +2722,34 @@ int parse_char(int fd) if (sd) { //Received again auth packet for already authentified account?? Discard it. //TODO: Perhaps log this as a hack attempt? + //TODO: and perhaps send back a reply? RFIFOSKIP(fd,17); break; } CREATE(session[fd]->session_data, struct char_session_data, 1); sd = (struct char_session_data*)session[fd]->session_data; - sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + sd->fd = fd; + sd->connect_until_time = 0; // unknown or unlimited (not displaying on map-server) sd->account_id = RFIFOL(fd,2); sd->login_id1 = RFIFOL(fd,6); sd->login_id2 = RFIFOL(fd,10); sd->sex = RFIFOB(fd,16); + // send back account_id WFIFOHEAD(fd,4); WFIFOL(fd,0) = RFIFOL(fd,2); WFIFOSET(fd,4); + // search authentification - for(i = 0; i < AUTH_FIFO_SIZE && !( + ARR_FIND( 0, AUTH_FIFO_SIZE, i, auth_fifo[i].account_id == sd->account_id && auth_fifo[i].login_id1 == sd->login_id1 && auth_fifo[i].login_id2 == sd->login_id2 && auth_fifo[i].ip == session[fd]->client_addr && - auth_fifo[i].delflag == 2) - ; i++); - - if (i < AUTH_FIFO_SIZE) { + auth_fifo[i].delflag == 2 ); + + if( i < AUTH_FIFO_SIZE ) { auth_fifo[i].delflag = 1; char_auth_ok(fd, sd); } else { // authentication not found @@ -2906,15 +2769,16 @@ int parse_char(int fd) WFIFOSET(fd,3); } } + } RFIFOSKIP(fd,17); - } break; - case 0x66: // char select + // char select + case 0x66: FIFOSD_CHECK(3); - do // TODO: poor code structure + do //FIXME: poor code structure { struct mmo_charstatus char_dat; char* data; @@ -2953,9 +2817,7 @@ int parse_char(int fd) if (i < 0) { unsigned short j; //First check that there's actually a map server online. - for(j = 0; j < MAX_MAP_SERVERS; j++) - if (server_fd[j] >= 0 && server[j].map[0]) - break; + ARR_FIND( 0, MAX_MAP_SERVERS, j, server_fd[j] >= 0 && server[j].map[0] ); if (j == MAX_MAP_SERVERS) { ShowInfo("Connection Closed. No map servers available.\n"); WFIFOHEAD(fd,3); @@ -3043,19 +2905,22 @@ int parse_char(int fd) memcpy(WFIFOP(map_fd,20), &char_dat, sizeof(struct mmo_charstatus)); WFIFOSET(map_fd, WFIFOW(map_fd,2)); - set_char_online(i, auth_fifo[auth_fifo_pos].char_id, auth_fifo[auth_fifo_pos].account_id); + set_char_online(i, char_dat.char_id, char_dat.account_id); auth_fifo_pos++; } while(0); + RFIFOSKIP(fd,3); break; - case 0x67: // make new + // create new char + // S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <char num>.B <hair color>.W <hair style>.W + case 0x67: FIFOSD_CHECK(37); - if(char_new == 0) //turn character creation on/off [Kevin] + if( !char_new ) //turn character creation on/off [Kevin] i = -2; else - i = make_new_char_sql(fd, RFIFOP(fd,2)); + i = make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27),RFIFOB(fd,28),RFIFOB(fd,29),RFIFOB(fd,30),RFIFOB(fd,31),RFIFOB(fd,32),RFIFOW(fd,33),RFIFOW(fd,35)); //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) if (i < 0) @@ -3071,29 +2936,32 @@ int parse_char(int fd) RFIFOSKIP(fd,37); break; } - { //Send to player. - int len; - struct mmo_charstatus char_dat; - WFIFOHEAD(fd,110); - WFIFOW(fd,0) = 0x6d; - mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed. - len = 2 + mmo_char_tobuf(WFIFOP(fd,2), &char_dat); - WFIFOSET(fd,len); + else + { + int len; + // retrieve data + struct mmo_charstatus char_dat; + mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed. - //to do - for(ch = 0; ch < MAX_CHARS; ch++) { - if (sd->found_char[ch] == -1) { - sd->found_char[ch] = char_dat.char_id; - break; - } + // send to player + WFIFOHEAD(fd,110); + WFIFOW(fd,0) = 0x6d; + len = 2 + mmo_char_tobuf(WFIFOP(fd,2), &char_dat); + WFIFOSET(fd,len); + + // add new entry to the chars list + ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 ); + if( ch < MAX_CHARS ) + sd->found_char[ch] = i; } RFIFOSKIP(fd,37); - } break; - case 0x68: // delete char - case 0x1fb: // 2004-04-19aSakexe+ langtype 12 char deletion packet + // delete char + case 0x68: + // 2004-04-19aSakexe+ langtype 12 char deletion packet + case 0x1fb: if (cmd == 0x68) FIFOSD_CHECK(46); if (cmd == 0x1fb) FIFOSD_CHECK(56); { @@ -3101,7 +2969,7 @@ int parse_char(int fd) int char_pid=0; WFIFOHEAD(fd,46); - ShowInfo(CL_RED" Request Char Deletion:"CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); + ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); memcpy(email, RFIFOP(fd,6), 40); RFIFOSKIP(fd,RFIFOREST(fd)); // hack to make the other deletion packet work @@ -3118,21 +2986,21 @@ int parse_char(int fd) break; } - for(i = 0; i < MAX_CHARS; i++) { - if (sd->found_char[i] == cid) { - for(ch = i; ch < MAX_CHARS-1; ch++) - sd->found_char[ch] = sd->found_char[ch+1]; - sd->found_char[MAX_CHARS-1] = -1; - break; - } - } - if (i == MAX_CHARS) { // Such a character does not exist in the account + // check if this char exists + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); + if( i == MAX_CHARS ) + { // Such a character does not exist in the account WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x70; WFIFOB(fd,2) = 0; WFIFOSET(fd,3); break; } + + // remove char from list and compact it + for(ch = i; ch < MAX_CHARS-1; ch++) + sd->found_char[ch] = sd->found_char[ch+1]; + sd->found_char[MAX_CHARS-1] = -1; /* Grab the partner id */ if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `partner_id` FROM `%s` WHERE `char_id`='%d'", char_db, cid) ) @@ -3140,9 +3008,7 @@ int parse_char(int fd) else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { char* data; - - Sql_GetData(sql_handle, 0, &data, NULL); - char_pid = atoi(data); + Sql_GetData(sql_handle, 0, &data, NULL); char_pid = atoi(data); } /* Delete character and partner (if any) */ @@ -3169,20 +3035,25 @@ int parse_char(int fd) } break; - case 0x187: // R 0187 <account ID>.l - client keep-alive packet (every 12 seconds) + // client keep-alive packet (every 12 seconds) + // R 0187 <account ID>.l + case 0x187: if (RFIFOREST(fd) < 6) return 0; RFIFOSKIP(fd,6); break; - case 0x28d: // R 028d <account ID>.l <char ID>.l <new name>.24B - char rename request + // char rename request + // R 028d <account ID>.l <char ID>.l <new name>.24B + case 0x28d: if (RFIFOREST(fd) < 34) return 0; //not implemented RFIFOSKIP(fd,34); break; - case 0x2af8: // login as map-server + // log in as map-server + case 0x2af8: if (RFIFOREST(fd) < 60) return 0; { @@ -3228,8 +3099,8 @@ int parse_char(int fd) } break; - case 0x7530: // Athena info get - { + // Athena info get + case 0x7530: WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x7531; WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; @@ -3240,15 +3111,17 @@ int parse_char(int fd) WFIFOB(fd,7) = ATHENA_SERVER_INTER | ATHENA_SERVER_CHAR; WFIFOW(fd,8) = ATHENA_MOD_VERSION; WFIFOSET(fd,10); + RFIFOSKIP(fd,2); - return 0; - } + break; - case 0x7532: // disconnect request from login server + // disconnect request from login server + case 0x7532: set_eof(fd); return 0; - default: // unknown packet received + // unknown packet received + default: ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL)); set_eof(fd); return 0; @@ -3659,7 +3532,7 @@ int char_config_read(const char* cfgName) } else if (strcmpi(w1, "char_maintenance") == 0) { char_maintenance = atoi(w2); } else if (strcmpi(w1, "char_new") == 0) { - char_new = atoi(w2); + char_new = (bool)atoi(w2); } else if (strcmpi(w1, "char_new_display") == 0) { char_new_display = atoi(w2); } else if (strcmpi(w1, "max_connect_user") == 0) { |