Index: src/login/login.c =================================================================== --- src/login/login.c (revision 17365) +++ src/login/login.c (working copy) @@ -567,6 +567,7 @@ char birthdate[10+1] = ""; char pincode[PINCODE_LENGTH+1]; int account_id = RFIFOL(fd,2); + int cl_version = 0; memset(pincode,0,PINCODE_LENGTH+1); @@ -575,15 +576,18 @@ if( !accounts->load_num(accounts, &acc, account_id) ) ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", server[id].name, account_id, ip); else{ + struct auth_node *node = (struct auth_node*)idb_get(auth_db, account_id); safestrncpy(email, acc.email, sizeof(email)); expiration_time = acc.expiration_time; group_id = acc.group_id; char_slots = acc.char_slots; safestrncpy(birthdate, acc.birthdate, sizeof(birthdate)); safestrncpy(pincode, acc.pincode, sizeof(pincode)); + if(node) cl_version = node->version; + ShowInfo("login R_0x2716, S_0x2717 version = %d, node=%d\n",cl_version,node); } - WFIFOHEAD(fd,72); + WFIFOHEAD(fd,73); WFIFOW(fd,0) = 0x2717; WFIFOL(fd,2) = account_id; safestrncpy((char*)WFIFOP(fd,6), email, 40); @@ -593,13 +597,13 @@ safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1); safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 ); WFIFOL(fd,68) = (uint32)acc.pincode_change; - WFIFOSET(fd,72); + WFIFOB(fd,72) = cl_version; + WFIFOSET(fd,73); } break; case 0x2719: // ping request from charserver RFIFOSKIP(fd,2); - WFIFOHEAD(fd,2); WFIFOW(fd,0) = 0x2718; WFIFOSET(fd,2); @@ -1270,10 +1274,8 @@ { struct online_login_data* data; - // mark client as 'online' data = add_online_user(-1, sd->account_id); - // schedule deletion of this node data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, sd->account_id, 0); } Index: src/map/status.c =================================================================== --- src/map/status.c (revision 17365) +++ src/map/status.c (working copy) @@ -49,10 +49,12 @@ }; static int max_weight_base[CLASS_COUNT]; -static int hp_coefficient[CLASS_COUNT]; -static int hp_coefficient2[CLASS_COUNT]; -static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1]; -static int sp_coefficient[CLASS_COUNT]; +static struct { + int hp_table[MAX_LEVEL]; + int sp_table[MAX_LEVEL]; + int max_level; +} hpsp_info[CLASS_COUNT]; + #ifdef RENEWAL_ASPD static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE+1]; #else @@ -2239,39 +2241,11 @@ return 1; } -/// Helper function for status_base_pc_maxhp(), used to pre-calculate the hp_sigma_val[] array -static void status_calc_sigma(void) -{ - int i,j; - - for(i = 0; i < CLASS_COUNT; i++) - { - unsigned int k = 0; - hp_sigma_val[i][0] = hp_sigma_val[i][1] = 0; - for(j = 2; j <= MAX_LEVEL; j++) - { - k += (hp_coefficient[i]*j + 50) / 100; - hp_sigma_val[i][j] = k; - if (k >= INT_MAX) - break; //Overflow protection. [Skotlex] - } - for(; j <= MAX_LEVEL; j++) - hp_sigma_val[i][j] = INT_MAX; - } -} - -/// Calculates base MaxHP value according to class and base level -/// The recursive equation used to calculate level bonus is (using integer operations) -/// f(0) = 35 | f(x+1) = f(x) + A + (x + B)*C/D -/// which reduces to something close to -/// f(x) = 35 + x*(A + B*C/D) + sum(i=2..x){ i*C/D } +//Calculate maxHP from tables static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data* status) { - uint64 val = pc_class2idx(sd->status.class_); - val = 35 + sd->status.base_level*(int64)hp_coefficient2[val]/100 + hp_sigma_val[val][sd->status.base_level]; + uint32 val = hpsp_info[pc_class2idx(sd->status.class_)].hp_table[sd->status.base_level-1]; - if((sd->class_&MAPID_UPPERMASK) == MAPID_NINJA || (sd->class_&MAPID_UPPERMASK) == MAPID_GUNSLINGER) - val += 100; //Since their HP can't be approximated well enough without this. if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON)) val *= 3; //Triple max HP for top ranking Taekwons over level 90. if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99) @@ -2286,11 +2260,11 @@ return (unsigned int)val; } +//Calculate maxSP from tables static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status) { - uint64 val; + uint32 val = hpsp_info[pc_class2idx(sd->status.class_)].sp_table[sd->status.base_level-1]; - val = 10 + sd->status.base_level*(int64)sp_coefficient[pc_class2idx(sd->status.class_)]/100; val += val * status->int_/100; if (sd->class_&JOBL_UPPER) @@ -3950,8 +3924,8 @@ status->matk_max += sd->bonus.ematk; status->matk_min += sd->bonus.ematk; } - status->matk_min = status_calc_ematk(bl, sc, status->matk_min); - status->matk_max = status_calc_ematk(bl, sc, status->matk_max); + status->matk_min = status_calc_ematk(bl, sc, status->matk_min); + status->matk_max = status_calc_ematk(bl, sc, status->matk_max); //This is the only portion in MATK that varies depending on the weapon level and refinement rate. if( status->rhw.matk > 0 ){ int wMatk = status->rhw.matk; @@ -3961,17 +3935,19 @@ } } #endif - if (bl->type&BL_PC && sd->matk_rate != 100) { + if (bl->type&BL_PC && sd->matk_rate != 100) { status->matk_max = status->matk_max * sd->matk_rate/100; status->matk_min = status->matk_min * sd->matk_rate/100; } - status->matk_min = status_calc_matk(bl, sc, status->matk_min); + status->matk_max = status_calc_matk(bl, sc, status->matk_max); - if ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk - || sc->data[SC_RECOGNIZEDSPELL]) - status->matk_min = status->matk_max; + if ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk + || (sc && sc->data[SC_RECOGNIZEDSPELL])) + status->matk_min = status->matk_max; + else + status->matk_min = status_calc_matk(bl, sc, status->matk_min); #ifdef RENEWAL if( sd && sd->right_weapon.overrefine > 0){ @@ -4741,37 +4717,37 @@ return cap_value(matk,0,USHRT_MAX); #ifndef RENEWAL // take note fixed value first before % modifiers - if (sc->data[SC_MATKPOTION]) - matk += sc->data[SC_MATKPOTION]->val1; - if (sc->data[SC_MATKFOOD]) - matk += sc->data[SC_MATKFOOD]->val1; - if (sc->data[SC_MANA_PLUS]) - matk += sc->data[SC_MANA_PLUS]->val1; - if (sc->data[SC_AQUAPLAY_OPTION]) - matk += sc->data[SC_AQUAPLAY_OPTION]->val2; - if (sc->data[SC_CHILLY_AIR_OPTION]) - matk += sc->data[SC_CHILLY_AIR_OPTION]->val2; - if (sc->data[SC_WATER_BARRIER]) - matk -= sc->data[SC_WATER_BARRIER]->val3; - if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) - matk += 50; - if (sc->data[SC_ODINS_POWER]) - matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2 - if (sc->data[SC_IZAYOI]) - matk += 50 * sc->data[SC_IZAYOI]->val1; + if (sc->data[SC_MATKPOTION]) + matk += sc->data[SC_MATKPOTION]->val1; + if (sc->data[SC_MATKFOOD]) + matk += sc->data[SC_MATKFOOD]->val1; + if (sc->data[SC_MANA_PLUS]) + matk += sc->data[SC_MANA_PLUS]->val1; + if (sc->data[SC_AQUAPLAY_OPTION]) + matk += sc->data[SC_AQUAPLAY_OPTION]->val2; + if (sc->data[SC_CHILLY_AIR_OPTION]) + matk += sc->data[SC_CHILLY_AIR_OPTION]->val2; + if (sc->data[SC_WATER_BARRIER]) + matk -= sc->data[SC_WATER_BARRIER]->val3; + if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) + matk += 50; + if (sc->data[SC_ODINS_POWER]) + matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2 + if (sc->data[SC_IZAYOI]) + matk += 50 * sc->data[SC_IZAYOI]->val1; #endif - if (sc->data[SC_MAGICPOWER] && sc->data[SC_MAGICPOWER]->val4) - matk += matk * sc->data[SC_MAGICPOWER]->val3/100; - if (sc->data[SC_MINDBREAKER]) - matk += matk * sc->data[SC_MINDBREAKER]->val2/100; - if (sc->data[SC_INCMATKRATE]) - matk += matk * sc->data[SC_INCMATKRATE]->val1/100; - if (sc->data[SC_MOONLITSERENADE]) - matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100; - if (sc->data[SC_MELODYOFSINK]) - matk += matk * sc->data[SC_MELODYOFSINK]->val3/100; - if (sc->data[SC_BEYONDOFWARCRY]) - matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100; + if (sc->data[SC_MAGICPOWER] && sc->data[SC_MAGICPOWER]->val4) + matk += matk * sc->data[SC_MAGICPOWER]->val3/100; + if (sc->data[SC_MINDBREAKER]) + matk += matk * sc->data[SC_MINDBREAKER]->val2/100; + if (sc->data[SC_INCMATKRATE]) + matk += matk * sc->data[SC_INCMATKRATE]->val1/100; + if (sc->data[SC_MOONLITSERENADE]) + matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100; + if (sc->data[SC_MELODYOFSINK]) + matk += matk * sc->data[SC_MELODYOFSINK]->val3/100; + if (sc->data[SC_BEYONDOFWARCRY]) + matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100; if( sc->data[SC_ZANGETSU] ) matk += matk * sc->data[SC_ZANGETSU]->val2 / 100; @@ -11341,15 +11317,8 @@ } -/*------------------------------------------ - * DB reading. - * job_db1.txt - weight, hp, sp, aspd - * job_db2.txt - job level stat bonuses - * size_fix.txt - size adjustment table for weapons - * refine_db.txt - refining data table - *------------------------------------------*/ -static bool status_readdb_job1(char* fields[], int columns, int current) -{// Job-specific values (weight, HP, SP, ASPD) +//Reading job_db1.txt line, (class,weight,aspd) +static bool status_readdb_job1(char* fields[], int columns, int current){ int idx, class_; unsigned int i; @@ -11363,20 +11332,18 @@ idx = pc_class2idx(class_); max_weight_base[idx] = atoi(fields[1]); - hp_coefficient[idx] = atoi(fields[2]); - hp_coefficient2[idx] = atoi(fields[3]); - sp_coefficient[idx] = atoi(fields[4]); #ifdef RENEWAL_ASPD for(i = 0; i <= MAX_WEAPON_TYPE; i++) #else for(i = 0; i < MAX_WEAPON_TYPE; i++) #endif { - aspd_base[idx][i] = atoi(fields[i+5]); + aspd_base[idx][i] = atoi(fields[i+2]); } return true; } +//Reading job_db2.txt line (class,JobLv1,JobLv2,JobLv3,...) static bool status_readdb_job2(char* fields[], int columns, int current) { int idx, class_, i; @@ -11397,6 +11364,78 @@ return true; } +//Reading job_maxhp.txt line +static bool status_readdb_maxhp(char* fields[], int columns, int current) +{ + int idx, i, maxlvl; + int job_id,job_count,jobs[CLASS_COUNT]; + + job_count = pc_split_atoi(fields[1],jobs,':',CLASS_COUNT); + if (job_count < 1) + return false; + for (i = 1; i < job_count; i++) { + job_id = jobs[i]; + if(!pcdb_checkid(job_id)) + { + ShowWarning("status_readdb_maxhp: Invalid job class %d specified.\n", job_id); + return false; + } + idx = pc_class2idx(job_id); + + maxlvl = atoi(fields[0]); + if(maxlvl > MAX_LEVEL){ + ShowWarning("status_readdb_maxhp: Invalid maxlevel %d specified.\n", maxlvl); + return false; + } + if(hpsp_info[idx].max_level && hpsp_info[idx].max_level != maxlvl){ + ShowWarning("status_readdb_maxhp: maxlevel %d was already specified with different value %d.\n",maxlvl,hpsp_info[idx].max_level); + return false; + } + hpsp_info[idx].max_level = maxlvl; + for(i = 2; i < maxlvl; i++) + { + hpsp_info[idx].hp_table[i-1] = atoi(fields[i]); + } + } + return true; +} + +//Reading job_maxsp.txt line +static bool status_readdb_maxsp(char* fields[], int columns, int current) +{ + int idx, i, maxlvl; + int job_id,job_count,jobs[CLASS_COUNT]; + + job_count = pc_split_atoi(fields[1],jobs,':',CLASS_COUNT); + if (job_count < 1) + return false; + for (i = 1; i < job_count; i++) { + job_id = jobs[i]; + if(!pcdb_checkid(job_id)) + { + ShowWarning("status_readdb_maxhp: Invalid job class %d specified.\n", job_id); + return false; + } + idx = pc_class2idx(job_id); + + maxlvl = atoi(fields[0]); + if(maxlvl > MAX_LEVEL){ + ShowWarning("status_readdb_maxhp: Invalid maxlevel %d specified.\n", maxlvl); + return false; + } + if(hpsp_info[idx].max_level && hpsp_info[idx].max_level != maxlvl){ + ShowWarning("status_readdb_maxhp: maxlevel %d was already specified with different value %d.\n",maxlvl,hpsp_info[idx].max_level); + return false; + } + hpsp_info[idx].max_level = maxlvl; + for(i = 2; i < maxlvl; i++) + { + hpsp_info[idx].sp_table[i-1] = atoi(fields[i]); + } + } + return true; +} + static bool status_readdb_sizefix(char* fields[], int columns, int current) { unsigned int i; @@ -11442,34 +11481,33 @@ return true; } -/* -* Read status db -* job1.txt -* job2.txt -* size_fixe.txt -* refine_db.txt -*/ + +/*------------------------------------------ + * DB reading. + * job_db1.txt - job weight, aspd + * job_db2.txt - job level stat bonuses + * job_maxhp_db.txt - job max hp + * job_maxsp_db.txt - job max sp + * size_fix.txt - size adjustment table for weapons + * refine_db.txt - refining data table + *------------------------------------------*/ int status_readdb(void) { int i, j; - // initialize databases to default // - // reset job_db1.txt data memset(max_weight_base, 0, sizeof(max_weight_base)); - memset(hp_coefficient, 0, sizeof(hp_coefficient)); - memset(hp_coefficient2, 0, sizeof(hp_coefficient2)); - memset(sp_coefficient, 0, sizeof(sp_coefficient)); memset(aspd_base, 0, sizeof(aspd_base)); // reset job_db2.txt data memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus + // hp_sptable + memset(hpsp_info,0,sizeof(hpsp_info)); // size_fix.txt for(i=0;i struct auth_node* @@ -2001,13 +2002,12 @@ void mmo_char_send(int fd, struct char_session_data* sd){ ShowInfo("sd->version = %d\n",sd->version); -#if PACKETVER >= 20130000 + if(sd->version > 34){ mmo_char_send082d(fd,sd); char_charlist_notify(fd,sd); char_block_character(fd,sd); -#endif - //@FIXME dump from kro doesn't show 6b transmission - mmo_char_send006b(fd,sd); + } + mmo_char_send006b(fd,sd); //@FIXME dump from kro doesn't show 6b transmission } int char_married(int pl1, int pl2) @@ -2280,7 +2280,7 @@ break; case 0x2717: // account data - if (RFIFOREST(fd) < 72) + if (RFIFOREST(fd) < 73) return 0; // find the authenticated session with this account id @@ -2300,6 +2300,7 @@ safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate)); safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode)); sd->pincode_change = (time_t)RFIFOL(fd,68); + sd->version = RFIFOB(fd,72); ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] ); // continued from char_auth_ok... if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359 @@ -2346,7 +2347,7 @@ #endif } } - RFIFOSKIP(fd,72); + RFIFOSKIP(fd,73); break; // login-server alive packet @@ -3620,34 +3621,490 @@ WFIFOSET(fd,10); } +//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 || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } } -static void char_delete2_req(int fd, struct char_session_data* sd) -{// CH: <0827>.W .L + +// 0065 .L .L .L .W .B +int char_parse_reqtoconnect(int fd, struct char_session_data* sd,uint32 ipl){ + if( RFIFOREST(fd) < 17 ) // request to connect + return 0; + else { + struct auth_node* node; + + int account_id = RFIFOL(fd,2); + uint32 login_id1 = RFIFOL(fd,6); + uint32 login_id2 = RFIFOL(fd,10); + int sex = RFIFOB(fd,16); + RFIFOSKIP(fd,17); + + ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2); + + 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? + ShowInfo("Already registered break\n"); + return 0; + } + + CREATE(session[fd]->session_data, struct char_session_data, 1); + sd = (struct char_session_data*)session[fd]->session_data; + sd->account_id = account_id; + sd->login_id1 = login_id1; + sd->login_id2 = login_id2; + sd->sex = sex; + sd->auth = false; // not authed yet + + // send back account_id + WFIFOHEAD(fd,4); + WFIFOL(fd,0) = account_id; + WFIFOSET(fd,4); + + if( runflag != CHARSERVER_ST_RUNNING ) + { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0;// rejected from server + WFIFOSET(fd,3); + return 0; + } + + // search authentification + node = (struct auth_node*)idb_get(auth_db, account_id); + if( node != NULL && + node->account_id == account_id && + node->login_id1 == login_id1 && + node->login_id2 == login_id2 /*&& + node->ip == ipl*/ ) + {// authentication found (coming from map server) + idb_remove(auth_db, account_id); + char_auth_ok(fd, sd); + } + else + {// authentication not found (coming from login server) + if (login_fd > 0) { // don't send request if no login-server + WFIFOHEAD(login_fd,23); + WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account + WFIFOL(login_fd,2) = sd->account_id; + WFIFOL(login_fd,6) = sd->login_id1; + WFIFOL(login_fd,10) = sd->login_id2; + WFIFOB(login_fd,14) = sd->sex; + WFIFOL(login_fd,15) = htonl(ipl); + WFIFOL(login_fd,19) = fd; + WFIFOSET(login_fd,23); + } else { // if no login-server, we must refuse connection + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + } + } + } + return 1; +} + +int char_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){ + FIFOSD_CHECK(3); + { + struct mmo_charstatus char_dat; + struct mmo_charstatus *cd; + char* data; + int char_id; + uint32 subnet_map_ip; + struct auth_node* node; + int i, map_fd; + + int slot = RFIFOB(fd,2); + RFIFOSKIP(fd,3); + + if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot) + || SQL_SUCCESS != Sql_NextRow(sql_handle) + || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, NULL) ) + { //Not found?? May be forged packet. + Sql_ShowDebug(sql_handle); + Sql_FreeResult(sql_handle); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; // rejected from server + WFIFOSET(fd,3); + return 0; + } + + char_id = atoi(data); + Sql_FreeResult(sql_handle); + + /* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */ + set_char_online(-2,char_id,sd->account_id); + if( !mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */ + set_char_offline(char_id, sd->account_id); + /* failed to load something. REJECT! */ + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + return 0;/* jump off this boat */ + } + + //Have to switch over to the DB instance otherwise data won't propagate [Kevin] + cd = (struct mmo_charstatus *)idb_get(char_db_, char_id); + cd->sex = sd->sex; + + if (log_char) { + char esc_name[NAME_LENGTH*2+1]; + + Sql_EscapeStringLen(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH)); + if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", + charlog_db, sd->account_id, slot, esc_name) ) + Sql_ShowDebug(sql_handle); + } + ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); + + // searching map server + i = search_mapserver(cd->last_point.map, -1, -1); + + // if map is not found, we check major cities + if (i < 0 || !cd->last_point.map) { + unsigned short j; + //First check that there's actually a map server online. + ARR_FIND( 0, ARRAYLENGTH(server), j, server[j].fd >= 0 && server[j].map[0] ); + if (j == ARRAYLENGTH(server)) { + ShowInfo("Connection Closed. No map servers available.\n"); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return 0; + } + if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { + cd->last_point.x = 273; + cd->last_point.y = 354; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { + cd->last_point.x = 120; + cd->last_point.y = 100; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { + cd->last_point.x = 160; + cd->last_point.y = 94; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { + cd->last_point.x = 116; + cd->last_point.y = 57; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { + cd->last_point.x = 87; + cd->last_point.y = 117; + } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { + cd->last_point.x = 94; + cd->last_point.y = 103; + } else { + ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return 0; + } + ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j)); + cd->last_point.map = j; + } + + //Send NEW auth packet [Kevin] + //FIXME: is this case even possible? [ultramage] + if ((map_fd = server[i].fd) < 1 || session[map_fd] == NULL) + { + ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); + server[i].fd = -1; + memset(&server[i], 0, sizeof(struct mmo_map_server)); + //Send server closed. + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x81; + WFIFOB(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + return 0; + } + + //Send player to map + WFIFOHEAD(fd,28); + WFIFOW(fd,0) = 0x71; + WFIFOL(fd,2) = cd->char_id; + mapindex_getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6)); + subnet_map_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] + WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : server[i].ip); + WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!] + WFIFOSET(fd,28); + + // create temporary auth entry + CREATE(node, struct auth_node, 1); + node->account_id = sd->account_id; + node->char_id = cd->char_id; + node->login_id1 = sd->login_id1; + node->login_id2 = sd->login_id2; + node->sex = sd->sex; + node->expiration_time = sd->expiration_time; + node->group_id = sd->group_id; + node->ip = ipl; + node->version = sd->version; + idb_put(auth_db, sd->account_id, node); + + } + return 1; +} + +// S 0970 .24B .B .W .W +// S 0067 .24B .B .B .B .B .B .B .B .W .W +int char_parse_createnewchar(int fd, struct char_session_data* sd,int cmd){ + int i=0, ch; + + if (cmd == 0x970) FIFOSD_CHECK(31) //>=20120307 + else if (cmd == 0x67) FIFOSD_CHECK(37) + else return 0; + + if( !char_new ) //turn character creation on/off [Kevin] + i = -2; + else { + if (cmd == 0x970){ + i = make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29)); + RFIFOSKIP(fd,31); + } + else if(cmd == 0x67){ +#if PACKETVER < 20120307 + 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)); + RFIFOSKIP(fd,37); +#endif + } + } + + //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) + if (i < 0) { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6e; + /* Others I found [Ind] */ + /* 0x02 = Symbols in Character Names are forbidden */ + /* 0x03 = You are not elegible to open the Character Slot. */ + switch (i) { + case -1: WFIFOB(fd,2) = 0x00; break; + case -2: WFIFOB(fd,2) = 0xFF; break; + case -3: WFIFOB(fd,2) = 0x01; break; + case -4: WFIFOB(fd,2) = 0x03; break; + } + WFIFOSET(fd,3); + } else { + int len; + // retrieve data + struct mmo_charstatus char_dat; + mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed. + + // send to player + WFIFOHEAD(fd,2+MAX_CHAR_BUF); + 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; // the char_id of the new char + } + return 1; +} + +int char_parse_delchar(int fd,struct char_session_data* sd, int cmd){ + char email[40]; + int i, ch; + + if (cmd == 0x68) FIFOSD_CHECK(46) + else if (cmd == 0x1fb) FIFOSD_CHECK(56) + else return 0; + + { + int cid = RFIFOL(fd,2); + + 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,( cmd == 0x68) ? 46 : 56); + + // Check if e-mail is correct + if(strcmpi(email, sd->email) && //email does not matches and + ( + strcmp("a@a.com", sd->email) || //it is not default email, or + (strcmp("a@a.com", email) && strcmp("", email)) //email sent does not matches default + )) { //Fail + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x70; + WFIFOB(fd,2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd,3); + return 0; + } + + // 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); + return 0; + } + + // 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; + + /* Delete character */ + if(delete_char_sql(cid)<0){ + //can't delete the char + //either SQL error or can't delete by some CONFIG conditions + //del fail + WFIFOHEAD(fd,3); + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + return 0; + } + /* Char successfully deleted.*/ + WFIFOHEAD(fd,2); + WFIFOW(fd,0) = 0x6f; + WFIFOSET(fd,2); + } + return 1; +} + +// R 0187 .l +int char_parse_keepalive(int fd){ + if (RFIFOREST(fd) < 6) + return 0; + //int aid = RFIFOL(fd,2); + RFIFOSKIP(fd,6); + return 1; +} + +// R 08fc .l .24B +// R 028d .l .l .24B +int char_parse_reqrename(int fd, struct char_session_data* sd, int cmd){ + if(cmd == 0x8fc) FIFOSD_CHECK(30) + if(cmd == 0x28d) FIFOSD_CHECK(34) + else return 0; + + { + int i, cid=0; + char name[NAME_LENGTH]; + char esc_name[NAME_LENGTH*2+1]; + safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH); + + if(cmd == 0x8fc){ + cid =RFIFOL(fd,2); + RFIFOSKIP(fd,30); + } + else if(cmd == 0x28d) { + int aid = RFIFOL(fd,2); + cid =RFIFOL(fd,6); + if( aid != sd->account_id ) + return 0; + RFIFOSKIP(fd,34); + } + + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); + if( i == MAX_CHARS ) + return 0; + + normalize_name(name,TRIM_CHARS); + Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); + if( !check_char_name(name,esc_name) ) + { + i = 1; + safestrncpy(sd->new_name, name, NAME_LENGTH); + } + else + i = 0; + + WFIFOHEAD(fd, 4); + WFIFOW(fd,0) = 0x28e; + WFIFOW(fd,2) = i; + WFIFOSET(fd,4); + } + return 1; +} + +// 0x28f .L +int char_parse_ackrename(int fd, struct char_session_data* sd){ + // 0: Sucessfull + // 1: This character's name has already been changed. You cannot change a character's name more than once. + // 2: User information is not correct. + // 3: You have failed to change this character's name. + // 4: Another user is using this character name, so please select another one. + FIFOSD_CHECK(6) + + { + int i; + int cid = RFIFOL(fd,2); + RFIFOSKIP(fd,6); + + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); + if( i == MAX_CHARS ) + return 0; + i = rename_char_sql(sd, cid); + + WFIFOHEAD(fd, 4); + WFIFOW(fd,0) = 0x290; + WFIFOW(fd,2) = i; + WFIFOSET(fd,4); + } + return 1; +} + +// R 07e5 .w .l +int char_parse_reqcaptcha(int fd){ + WFIFOHEAD(fd,5); + WFIFOW(fd,0) = 0x7e9; + WFIFOW(fd,2) = 5; + WFIFOB(fd,4) = 1; + WFIFOSET(fd,5); + RFIFOSKIP(fd,8); + return 1; +} + +// R 07e7 .w .l .b10 .b14 +int char_parse_chkcaptcha(int fd){ + WFIFOHEAD(fd,5); + WFIFOW(fd,0) = 0x7e9; + WFIFOW(fd,2) = 5; + WFIFOB(fd,4) = 1; + WFIFOSET(fd,5); + RFIFOSKIP(fd,32); + return 1; +} + +// CH: <0827>.W .L +int char_delete2_req(int fd, struct char_session_data* sd) +{ int char_id, i; char* data; time_t delete_date; + FIFOSD_CHECK(6) + char_id = RFIFOL(fd,2); + RFIFOSKIP(fd,6); ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found char_delete2_ack(fd, char_id, 3, 0); - return; + return 0; } if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != Sql_NextRow(sql_handle) ) { Sql_ShowDebug(sql_handle); char_delete2_ack(fd, char_id, 3, 0); - return; + return 0; } Sql_GetData(sql_handle, 0, &data, NULL); delete_date = strtoul(data, NULL, 10); if( delete_date ) {// character already queued for deletion char_delete2_ack(fd, char_id, 0, 0); - return; + return 0; } /* @@ -3674,14 +4131,14 @@ { Sql_ShowDebug(sql_handle); char_delete2_ack(fd, char_id, 3, 0); - return; + return 0; } char_delete2_ack(fd, char_id, 1, delete_date); + return 1; } - -static void char_delete2_accept(int fd, struct char_session_data* sd) +int char_delete2_accept(int fd, struct char_session_data* sd) {// CH: <0829>.W .L .6B char birthdate[8+1]; int char_id, i, k; @@ -3689,6 +4146,8 @@ char* data; time_t delete_date; + FIFOSD_CHECK(12) + char_id = RFIFOL(fd,2); ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, char_id); @@ -3703,19 +4162,20 @@ birthdate[6] = RFIFOB(fd,10); birthdate[7] = RFIFOB(fd,11); birthdate[8] = 0; + RFIFOSKIP(fd,12); ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found char_delete2_accept_ack(fd, char_id, 3); - return; + return 0; } if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `base_level`,`delete_date` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) || SQL_SUCCESS != Sql_NextRow(sql_handle) ) {// data error Sql_ShowDebug(sql_handle); char_delete2_accept_ack(fd, char_id, 3); - return; + return 0; } Sql_GetData(sql_handle, 0, &data, NULL); base_level = (unsigned int)strtoul(data, NULL, 10); @@ -3724,26 +4184,26 @@ if( !delete_date || delete_date>time(NULL) ) {// not queued or delay not yet passed char_delete2_accept_ack(fd, char_id, 4); - return; + return 0; } if( strcmp(sd->birthdate+2, birthdate) ) // +2 to cut off the century {// birth date is wrong char_delete2_accept_ack(fd, char_id, 5); - return; + return 0; } if( ( char_del_level > 0 && base_level >= (unsigned int)char_del_level ) || ( char_del_level < 0 && base_level <= (unsigned int)(-char_del_level) ) ) {// character level config restriction char_delete2_accept_ack(fd, char_id, 2); - return; + return 0; } // success if( delete_char_sql(char_id) < 0 ) { char_delete2_accept_ack(fd, char_id, 3); - return; + return 0; } // refresh character list cache @@ -3754,20 +4214,24 @@ sd->found_char[MAX_CHARS-1] = -1; char_delete2_accept_ack(fd, char_id, 1); + return 1; } - -static void char_delete2_cancel(int fd, struct char_session_data* sd) -{// CH: <082b>.W .L +// CH: <082b>.W .L +int char_delete2_cancel(int fd, struct char_session_data* sd) +{ int char_id, i; + FIFOSD_CHECK(6) + char_id = RFIFOL(fd,2); + RFIFOSKIP(fd,6); ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == char_id ); if( i == MAX_CHARS ) {// character not found char_delete2_cancel_ack(fd, char_id, 2); - return; + return 0; } // there is no need to check, whether or not the character was @@ -3777,24 +4241,61 @@ { Sql_ShowDebug(sql_handle); char_delete2_cancel_ack(fd, char_id, 2); - return; + return 0; } char_delete2_cancel_ack(fd, char_id, 1); + return 1; } +int char_parse_maplogin(int fd){ + int i; + if (RFIFOREST(fd) < 60) + return 0; + else { + char* l_user = (char*)RFIFOP(fd,2); + char* l_pass = (char*)RFIFOP(fd,26); + l_user[23] = '\0'; + l_pass[23] = '\0'; + ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd <= 0 ); + if( runflag != CHARSERVER_ST_RUNNING || + i == ARRAYLENGTH(server) || + strcmp(l_user, userid) != 0 || + strcmp(l_pass, passwd) != 0 ) + { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x2af9; + WFIFOB(fd,2) = 3; + WFIFOSET(fd,3); + } else { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x2af9; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + + server[i].fd = fd; + server[i].ip = ntohl(RFIFOL(fd,54)); + server[i].port = ntohs(RFIFOW(fd,58)); + server[i].users = 0; + memset(server[i].map, 0, sizeof(server[i].map)); + session[fd]->func_parse = parse_frommap; + session[fd]->flag.server = 1; + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + char_mapif_init(fd); + } + RFIFOSKIP(fd,60); + } + return 1; +} + int parse_char(int fd) { - int i, ch; - char email[40]; + int i; unsigned short cmd; - int map_fd; - struct char_session_data* sd; + struct char_session_data* sd = (struct char_session_data*)session[fd]->session_data; uint32 ipl = session[fd]->client_addr; - sd = (struct char_session_data*)session[fd]->session_data; - // disconnect any player if no login-server. if(login_fd < 0) set_eof(fd); @@ -3815,582 +4316,44 @@ 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 || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } } - cmd = RFIFOW(fd,0); switch( cmd ) { - // request to connect - // 0065 .L .L .L .W .B - case 0x65: - if( RFIFOREST(fd) < 17 ) - return 0; - { - struct auth_node* node; - - int account_id = RFIFOL(fd,2); - uint32 login_id1 = RFIFOL(fd,6); - uint32 login_id2 = RFIFOL(fd,10); - int sex = RFIFOB(fd,16); - RFIFOSKIP(fd,17); - - ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2); - - 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? - break; - } - - CREATE(session[fd]->session_data, struct char_session_data, 1); - sd = (struct char_session_data*)session[fd]->session_data; - sd->account_id = account_id; - sd->login_id1 = login_id1; - sd->login_id2 = login_id2; - sd->sex = sex; - sd->auth = false; // not authed yet - - // send back account_id - WFIFOHEAD(fd,4); - WFIFOL(fd,0) = account_id; - WFIFOSET(fd,4); - - if( runflag != CHARSERVER_ST_RUNNING ) - { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOB(fd,2) = 0;// rejected from server - WFIFOSET(fd,3); - break; - } - - // search authentification - node = (struct auth_node*)idb_get(auth_db, account_id); - if( node != NULL && - node->account_id == account_id && - node->login_id1 == login_id1 && - node->login_id2 == login_id2 /*&& - node->ip == ipl*/ ) - {// authentication found (coming from map server) - idb_remove(auth_db, account_id); - char_auth_ok(fd, sd); - } - else - {// authentication not found (coming from login server) - if (login_fd > 0) { // don't send request if no login-server - WFIFOHEAD(login_fd,23); - WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account - WFIFOL(login_fd,2) = sd->account_id; - WFIFOL(login_fd,6) = sd->login_id1; - WFIFOL(login_fd,10) = sd->login_id2; - WFIFOB(login_fd,14) = sd->sex; - WFIFOL(login_fd,15) = htonl(ipl); - WFIFOL(login_fd,19) = fd; - WFIFOSET(login_fd,23); - } else { // if no login-server, we must refuse connection - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - } - } - } - break; - + case 0x65: char_parse_reqtoconnect(fd,sd,ipl); break; // char select - case 0x66: - FIFOSD_CHECK(3); - { - struct mmo_charstatus char_dat; - struct mmo_charstatus *cd; - char* data; - int char_id; - uint32 subnet_map_ip; - struct auth_node* node; - - int slot = RFIFOB(fd,2); - RFIFOSKIP(fd,3); - - if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot) - || SQL_SUCCESS != Sql_NextRow(sql_handle) - || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, NULL) ) - { //Not found?? May be forged packet. - Sql_ShowDebug(sql_handle); - Sql_FreeResult(sql_handle); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOB(fd,2) = 0; // rejected from server - WFIFOSET(fd,3); - break; - } - - char_id = atoi(data); - Sql_FreeResult(sql_handle); - - /* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */ - set_char_online(-2,char_id,sd->account_id); - if( !mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */ - set_char_offline(char_id, sd->account_id); - /* failed to load something. REJECT! */ - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6c; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - break;/* jump off this boat */ - } - - //Have to switch over to the DB instance otherwise data won't propagate [Kevin] - cd = (struct mmo_charstatus *)idb_get(char_db_, char_id); - cd->sex = sd->sex; - - if (log_char) { - char esc_name[NAME_LENGTH*2+1]; - - Sql_EscapeStringLen(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH)); - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", - charlog_db, sd->account_id, slot, esc_name) ) - Sql_ShowDebug(sql_handle); - } - ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); - - // searching map server - i = search_mapserver(cd->last_point.map, -1, -1); - - // if map is not found, we check major cities - if (i < 0 || !cd->last_point.map) { - unsigned short j; - //First check that there's actually a map server online. - ARR_FIND( 0, ARRAYLENGTH(server), j, server[j].fd >= 0 && server[j].map[0] ); - if (j == ARRAYLENGTH(server)) { - ShowInfo("Connection Closed. No map servers available.\n"); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { - cd->last_point.x = 273; - cd->last_point.y = 354; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { - cd->last_point.x = 120; - cd->last_point.y = 100; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { - cd->last_point.x = 160; - cd->last_point.y = 94; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { - cd->last_point.x = 116; - cd->last_point.y = 57; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { - cd->last_point.x = 87; - cd->last_point.y = 117; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { - cd->last_point.x = 94; - cd->last_point.y = 103; - } else { - ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j)); - cd->last_point.map = j; - } - - //Send NEW auth packet [Kevin] - //FIXME: is this case even possible? [ultramage] - if ((map_fd = server[i].fd) < 1 || session[map_fd] == NULL) - { - ShowError("parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, i); - server[i].fd = -1; - memset(&server[i], 0, sizeof(struct mmo_map_server)); - //Send server closed. - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = 1; // 01 = Server closed - WFIFOSET(fd,3); - break; - } - - //Send player to map - WFIFOHEAD(fd,28); - WFIFOW(fd,0) = 0x71; - WFIFOL(fd,2) = cd->char_id; - mapindex_getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6)); - subnet_map_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] - WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : server[i].ip); - WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!] - WFIFOSET(fd,28); - - // create temporary auth entry - CREATE(node, struct auth_node, 1); - node->account_id = sd->account_id; - node->char_id = cd->char_id; - node->login_id1 = sd->login_id1; - node->login_id2 = sd->login_id2; - node->sex = sd->sex; - node->expiration_time = sd->expiration_time; - node->group_id = sd->group_id; - node->ip = ipl; - idb_put(auth_db, sd->account_id, node); - - } - break; - - // create new char -#if PACKETVER >= 20120307 - // S 0970 .24B .B .W .W - case 0x970: - FIFOSD_CHECK(31); -#else - // S 0067 .24B .B .B .B .B .B .B .B .W .W - case 0x67: - FIFOSD_CHECK(37); -#endif - - if( !char_new ) //turn character creation on/off [Kevin] - i = -2; - else -#if PACKETVER >= 20120307 - i = make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29)); -#else - 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)); -#endif - - //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) - if (i < 0) { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x6e; - /* Others I found [Ind] */ - /* 0x02 = Symbols in Character Names are forbidden */ - /* 0x03 = You are not elegible to open the Character Slot. */ - switch (i) { - case -1: WFIFOB(fd,2) = 0x00; break; - case -2: WFIFOB(fd,2) = 0xFF; break; - case -3: WFIFOB(fd,2) = 0x01; break; - case -4: WFIFOB(fd,2) = 0x03; break; - } - WFIFOSET(fd,3); - } else { - int len; - // retrieve data - struct mmo_charstatus char_dat; - mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed. - - // send to player - WFIFOHEAD(fd,2+MAX_CHAR_BUF); - 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; // the char_id of the new char - } -#if PACKETVER >= 20120307 - RFIFOSKIP(fd,31); -#else - RFIFOSKIP(fd,37); -#endif - break; - + case 0x66: char_parse_charselect(fd,sd,ipl); break; + // createnewchar + case 0x970: char_parse_createnewchar(fd,sd,cmd); break; + case 0x67: char_parse_createnewchar(fd,sd,cmd); break; // 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); - { - int cid = RFIFOL(fd,2); - - 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,( cmd == 0x68) ? 46 : 56); - - // Check if e-mail is correct - if(strcmpi(email, sd->email) && //email does not matches and - ( - strcmp("a@a.com", sd->email) || //it is not default email, or - (strcmp("a@a.com", email) && strcmp("", email)) //email sent does not matches default - )) { //Fail - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x70; - WFIFOB(fd,2) = 0; // 00 = Incorrect Email address - WFIFOSET(fd,3); - break; - } - - // 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; - - /* Delete character */ - if(delete_char_sql(cid)<0){ - //can't delete the char - //either SQL error or can't delete by some CONFIG conditions - //del fail - WFIFOHEAD(fd,3); - WFIFOW(fd, 0) = 0x70; - WFIFOB(fd, 2) = 0; - WFIFOSET(fd, 3); - break; - } - /* Char successfully deleted.*/ - WFIFOHEAD(fd,2); - WFIFOW(fd,0) = 0x6f; - WFIFOSET(fd,2); - } - break; - + case 0x68: char_parse_delchar(fd,sd,cmd); break; // + case 0x1fb: char_parse_delchar(fd,sd,cmd); break; // 2004-04-19aSakexe+ langtype 12 char deletion packet // client keep-alive packet (every 12 seconds) - // R 0187 .l - case 0x187: - if (RFIFOREST(fd) < 6) - return 0; - RFIFOSKIP(fd,6); - break; - // char rename request - // R 08fc .l .24B - case 0x8fc: - FIFOSD_CHECK(30); - { - int i, cid =RFIFOL(fd,2); - char name[NAME_LENGTH]; - char esc_name[NAME_LENGTH*2+1]; - safestrncpy(name, (char *)RFIFOP(fd,6), NAME_LENGTH); - RFIFOSKIP(fd,30); - - ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); - if( i == MAX_CHARS ) - break; - - normalize_name(name,TRIM_CHARS); - Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( !check_char_name(name,esc_name) ) { - i = 1; - safestrncpy(sd->new_name, name, NAME_LENGTH); - } else - i = 0; - - WFIFOHEAD(fd, 4); - WFIFOW(fd,0) = 0x28e; - WFIFOW(fd,2) = i; - WFIFOSET(fd,4); - } - break; - - // char rename request - // R 028d .l .l .24B - case 0x28d: - FIFOSD_CHECK(34); - { - int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6); - char name[NAME_LENGTH]; - char esc_name[NAME_LENGTH*2+1]; - safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH); - RFIFOSKIP(fd,34); - - if( aid != sd->account_id ) - break; - ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); - if( i == MAX_CHARS ) - break; - - normalize_name(name,TRIM_CHARS); - Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( !check_char_name(name,esc_name) ) - { - i = 1; - safestrncpy(sd->new_name, name, NAME_LENGTH); - } - else - i = 0; - - WFIFOHEAD(fd, 4); - WFIFOW(fd,0) = 0x28e; - WFIFOW(fd,2) = i; - WFIFOSET(fd,4); - } - break; - //Confirm change name. - // 0x28f .L - case 0x28f: - // 0: Sucessfull - // 1: This character's name has already been changed. You cannot change a character's name more than once. - // 2: User information is not correct. - // 3: You have failed to change this character's name. - // 4: Another user is using this character name, so please select another one. - FIFOSD_CHECK(6); - { - int i; - int cid = RFIFOL(fd,2); - RFIFOSKIP(fd,6); - - ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); - if( i == MAX_CHARS ) - break; - i = rename_char_sql(sd, cid); - - WFIFOHEAD(fd, 4); - WFIFOW(fd,0) = 0x290; - WFIFOW(fd,2) = i; - WFIFOSET(fd,4); - } - break; - - // captcha code request (not implemented) - // R 07e5 .w .l - case 0x7e5: - WFIFOHEAD(fd,5); - WFIFOW(fd,0) = 0x7e9; - WFIFOW(fd,2) = 5; - WFIFOB(fd,4) = 1; - WFIFOSET(fd,5); - RFIFOSKIP(fd,8); - break; - - // captcha code check (not implemented) - // R 07e7 .w .l .b10 .b14 - case 0x7e7: - WFIFOHEAD(fd,5); - WFIFOW(fd,0) = 0x7e9; - WFIFOW(fd,2) = 5; - WFIFOB(fd,4) = 1; - WFIFOSET(fd,5); - RFIFOSKIP(fd,32); - break; - + case 0x187: char_parse_keepalive(fd); break; + // char rename + case 0x8fc: char_parse_reqrename(fd,sd,cmd); break; //request + case 0x28d: char_parse_reqrename(fd,sd,cmd); break; //request + case 0x28f: char_parse_ackrename(fd,sd); break; //Confirm change name. + // captcha + case 0x7e5: char_parse_reqcaptcha(fd); break; // captcha code request (not implemented) + case 0x7e7: char_parse_chkcaptcha(fd); break; // captcha code check (not implemented) // deletion timer request - case 0x827: - FIFOSD_CHECK(6); - char_delete2_req(fd, sd); - RFIFOSKIP(fd,6); - break; - + case 0x827: char_delete2_req(fd, sd); break; // deletion accept request - case 0x829: - FIFOSD_CHECK(12); - char_delete2_accept(fd, sd); - RFIFOSKIP(fd,12); - break; - + case 0x829: char_delete2_accept(fd, sd); break; // deletion cancel request - case 0x82b: - FIFOSD_CHECK(6); - char_delete2_cancel(fd, sd); - RFIFOSKIP(fd,6); - break; - + case 0x82b: char_delete2_cancel(fd, sd); break; // login as map-server - case 0x2af8: - if (RFIFOREST(fd) < 60) - return 0; - else { - char* l_user = (char*)RFIFOP(fd,2); - char* l_pass = (char*)RFIFOP(fd,26); - l_user[23] = '\0'; - l_pass[23] = '\0'; - ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd <= 0 ); - if( runflag != CHARSERVER_ST_RUNNING || - i == ARRAYLENGTH(server) || - strcmp(l_user, userid) != 0 || - strcmp(l_pass, passwd) != 0 ) - { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x2af9; - WFIFOB(fd,2) = 3; - WFIFOSET(fd,3); - } else { - WFIFOHEAD(fd,3); - WFIFOW(fd,0) = 0x2af9; - WFIFOB(fd,2) = 0; - WFIFOSET(fd,3); - - server[i].fd = fd; - server[i].ip = ntohl(RFIFOL(fd,54)); - server[i].port = ntohs(RFIFOW(fd,58)); - server[i].users = 0; - memset(server[i].map, 0, sizeof(server[i].map)); - session[fd]->func_parse = parse_frommap; - session[fd]->flag.server = 1; - realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); - char_mapif_init(fd); - } - RFIFOSKIP(fd,60); - } - return 0; // avoid processing of followup packets here - - // checks the entered pin - case 0x8b8: - if( RFIFOREST(fd) < 10 ) - return 0; - if( pincode_enabled && RFIFOL(fd,2) == sd->account_id ) - pincode_check( fd, sd ); - RFIFOSKIP(fd,10); - break; - - // request for PIN window - case 0x8c5: - if( RFIFOREST(fd) < 6 ) - return 0; - if( pincode_enabled && RFIFOL(fd,2) == sd->account_id ){ - if( strlen( sd->pincode ) <= 0 ){ - pincode_sendstate( fd, sd, PINCODE_NEW ); - }else{ - pincode_sendstate( fd, sd, PINCODE_ASK ); - } - } - RFIFOSKIP(fd,6); - break; - - // pincode change request - case 0x8be: - if( RFIFOREST(fd) < 14 ) - return 0; - - if( pincode_enabled && RFIFOL(fd,2) == sd->account_id ) - pincode_change( fd, sd ); - - RFIFOSKIP(fd,14); - break; - - // activate PIN system and set first PIN - case 0x8ba: - if( RFIFOREST(fd) < 10 ) - return 0; - if( pincode_enabled && RFIFOL(fd,2) == sd->account_id ) - pincode_setnew( fd, sd ); - RFIFOSKIP(fd,10); - break; - + case 0x2af8: char_parse_maplogin(fd); return 0; // avoid processing of followup packets here + //pincode + case 0x8b8: pincode_check( fd, sd ); break; // checks the entered pin + case 0x8c5: char_parse_reqpincode_window(fd,sd); break; // request for PIN window + case 0x8be: pincode_change( fd, sd ); break; // pincode change request + case 0x8ba: pincode_setnew( fd, sd ); break; // activate PIN system and set first PIN // character movement request - case 0x8d4: - if( RFIFOREST(fd) < 8 ) - return 0; + case 0x8d4: moveCharSlot(fd,sd); break; - moveCharSlot( fd, sd, RFIFOW(fd, 2), RFIFOW(fd, 4) ); - mmo_char_send(fd, sd); - RFIFOSKIP(fd,8); - break; - case 0x9a1: if( RFIFOREST(fd) < 2 ) return 0; @@ -4598,18 +4561,37 @@ //------------------------------------------------ //Pincode system //------------------------------------------------ -void pincode_check( int fd, struct char_session_data* sd ){ +int char_parse_reqpincode_window(int fd, struct char_session_data* sd){ + if( RFIFOREST(fd) < 6 ) + return 0; + if( pincode_enabled && RFIFOL(fd,2) == sd->account_id ){ + if( strlen( sd->pincode ) <= 0 ){ + pincode_sendstate( fd, sd, PINCODE_NEW ); + }else{ + pincode_sendstate( fd, sd, PINCODE_ASK ); + } + } + RFIFOSKIP(fd,6); + return 1; +} + +int pincode_check( int fd, struct char_session_data* sd ){ char pin[PINCODE_LENGTH+1]; + if( RFIFOREST(fd) < 10 ) + return 0; + if( pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) + return 0; + memset(pin,0,PINCODE_LENGTH+1); - strncpy((char*)pin, (char*)RFIFOP(fd, 6), PINCODE_LENGTH); + RFIFOSKIP(fd,10); pincode_decrypt(sd->pincode_seed, pin ); - if( pincode_compare( fd, sd, pin ) ){ pincode_sendstate( fd, sd, PINCODE_PASSED ); } + return 1; } int pincode_compare( int fd, struct char_session_data* sd, char* pin ){ @@ -4627,34 +4609,46 @@ } } -void pincode_change( int fd, struct char_session_data* sd ){ +int pincode_change( int fd, struct char_session_data* sd ){ char oldpin[PINCODE_LENGTH+1]; char newpin[PINCODE_LENGTH+1]; + if( RFIFOREST(fd) < 14 ) + return 0; + if( pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) + return 0; + memset(oldpin,0,PINCODE_LENGTH+1); memset(newpin,0,PINCODE_LENGTH+1); + strncpy(oldpin, (char*)RFIFOP(fd,6), PINCODE_LENGTH); + strncpy(newpin, (char*)RFIFOP(fd,10), PINCODE_LENGTH); + RFIFOSKIP(fd,14); - strncpy(oldpin, (char*)RFIFOP(fd,6), PINCODE_LENGTH); pincode_decrypt(sd->pincode_seed,oldpin); - if( !pincode_compare( fd, sd, oldpin ) ) - return; - - strncpy(newpin, (char*)RFIFOP(fd,10), PINCODE_LENGTH); + return 0; pincode_decrypt(sd->pincode_seed,newpin); pincode_notifyLoginPinUpdate( sd->account_id, newpin ); strncpy(sd->pincode, newpin, sizeof(newpin)); pincode_sendstate( fd, sd, PINCODE_PASSED ); + return 1; } -void pincode_setnew( int fd, struct char_session_data* sd ){ +int pincode_setnew( int fd, struct char_session_data* sd ){ char newpin[PINCODE_LENGTH+1]; memset(newpin,0,PINCODE_LENGTH+1); + if( RFIFOREST(fd) < 10 ) + return 0; + + if( pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) + return 0; strncpy( newpin, (char*)RFIFOP(fd,6), PINCODE_LENGTH ); + RFIFOSKIP(fd,10); + pincode_decrypt( sd->pincode_seed, newpin ); pincode_notifyLoginPinUpdate( sd->account_id, newpin ); @@ -4724,17 +4718,26 @@ //------------------------------------------------ //Add On system //------------------------------------------------ -void moveCharSlot( int fd, struct char_session_data* sd, unsigned short from, unsigned short to ){ +int moveCharSlot( int fd, struct char_session_data* sd){ + uint16 from, to; + + if( RFIFOREST(fd) < 8 ) + return 0; + from = RFIFOW(fd,2); + to = RFIFOW(fd,4); + //Cnt = RFIFOW(fd,6); //how many time we have left to change (client.. lol we don't trust him) + RFIFOSKIP(fd,8); + // Have we changed to often or is it disabled? if( !char_move_enabled || ( !char_moves_unlimited && sd->char_moves[from] <= 0 ) ){ moveCharSlotReply( fd, sd, from, 1 ); - return; + return 0; } // We dont even have a character on the chosen slot? if( sd->found_char[from] <= 0 ){ moveCharSlotReply( fd, sd, from, 1 ); - return; + return 0; } if( sd->found_char[to] > 0 ){ @@ -4749,7 +4752,7 @@ moveCharSlotReply( fd, sd, from, 1 ); Sql_ShowDebug(sql_handle); Sql_QueryStr(sql_handle,"ROLLBACK"); - return; + return 0; } }else{ // Admin doesnt allow us to @@ -4759,7 +4762,7 @@ }else if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d'", char_db, to, sd->found_char[from] ) ){ Sql_ShowDebug(sql_handle); moveCharSlotReply( fd, sd, from, 1 ); - return; + return 0; } if( !char_moves_unlimited ){ @@ -4770,6 +4773,7 @@ // We successfully moved the char - time to notify the client moveCharSlotReply( fd, sd, from, 0 ); mmo_char_send(fd, sd); + return 1; } // reason Index: src/common/socket.c =================================================================== --- src/common/socket.c (revision 17365) +++ src/common/socket.c (working copy) @@ -637,7 +637,7 @@ /// advance the RFIFO cursor (marking 'len' bytes as processed) int RFIFOSKIP(int fd, size_t len) { - struct socket_data *s; + struct socket_data *s; if ( !session_isActive(fd) ) return 0; Index: db/packet_db.txt =================================================================== --- db/packet_db.txt (revision 17365) +++ db/packet_db.txt (working copy) @@ -1416,7 +1416,7 @@ //2009-08-18aRagexeRE 0x07e3,6 -0x07e4,-1,itemlistwindowselected,2:4:8 +0x07e4,-1,itemlistwindowselected,2:4:8:12 0x07e6,8 //2009-08-25aRagexeRE @@ -1612,7 +1612,7 @@ //2010-11-24aRagexeRE packet_ver: 26 -0x0288,-1,cashshopbuy,2:4:8:10 +0x0288,-1,cashshopbuy,2:4:6:10 0x0436,19,wanttoconnection,2:6:10:14:18 0x035f,5,walktoxy,2 0x0360,6,ticksend,2 @@ -1658,7 +1658,7 @@ 0x083c,19,wanttoconnection,2:6:10:14:18 0x08aa,7,actionrequest,2:6 0x02c4,10,useskilltoid,2:4:6 -0x0811,-1,itemlistwindowselected,2:4:8 +0x0811,-1,itemlistwindowselected,2:4:8:12 0x890,8 0x08a5,18,bookingregreq,2:4:6 0x0835,-1,reqopenbuyingstore,2:4:8:9:89 @@ -1689,7 +1689,7 @@ 0x0929,26,partyinvite2,2 0x0885,7,actionrequest,2:6 0x0889,10,useskilltoid,2:4:6 -0x0870,-1,itemlistwindowselected,2:4:8 +0x0870,-1,itemlistwindowselected,2:4:8:12 //0x0926,18,bookingregreq,2:4:6 0x0815,-1,reqopenbuyingstore,2:4:8:9:89 0x0817,2,reqclosebuyingstore,0 @@ -1707,13 +1707,13 @@ 0x089c,26,friendslistadd,2 0x0885,5,hommenu,2:4 0x0961,36,storagepassword,2:4:20 -0x0288,-1,cashshopbuy,2:4:8:10 +0x0288,-1,cashshopbuy,2:4:6:10 0x091c,26,partyinvite2,2 0x094b,19,wanttoconnection,2:6:10:14:18 0x0369,7,actionrequest,2:6 0x083c,10,useskilltoid,2:4:6 0x0439,8,useitem,2:4 -0x0945,-1,itemlistwindowselected,2:4:8 +0x0945,-1,itemlistwindowselected,2:4:8:12 0x0815,-1,reqopenbuyingstore,2:4:8:9:89 0x0817,2,reqclosebuyingstore,0 0x0360,6,reqclickbuyingstore,2 @@ -1769,7 +1769,7 @@ 0x08A8,36,storagepassword,2:4:20 0x0802,26,partyinvite2,2 0x022D,19,wanttoconnection,2:6:10:14:18 -0x0281,-1,itemlistwindowselected,2:4:8 +0x0281,-1,itemlistwindowselected,2:4:8:12 0x035F,6,ticksend,2 0x0202,5,changedir,2:4 0x07E4,6,takeitem,2 @@ -1808,7 +1808,7 @@ packet_ver: 34 0x014f,6,guildrequestinfo,2 0x01fd,15,repairitem,2:4:6:7:9:11:13 -//0x0281,-1,itemlistwindowselected,2:4:8 +//0x0281,-1,itemlistwindowselected,2:4:8:12 0x035f,6,reqclickbuyingstore,2 0x0363,6,ticksend,2 0x0365,12,searchstoreinfolistitemclick,2:6:10 @@ -1821,7 +1821,7 @@ 0x084b,19 //fallitem4 0x085a,90,useskilltoposinfo,2:4:6:8:10 0x085d,18,bookingregreq,2:4:6 -0x0868,-1,itemlistwindowselected,2:4:8 +0x0868,-1,itemlistwindowselected,2:4:8:12 0x086d,26,partyinvite2,2 0x086f,26,friendslistadd,2 0x0874,8,movefromkafra,2:4 @@ -1891,7 +1891,7 @@ 0x0815,-1,reqopenbuyingstore,2:4:8:9:89 0x092D,18,bookingregreq,2:4:6 //0x08AA,8 CZ_JOIN_BATTLE_FIELD -0x0963,-1,itemlistwindowselected,2:4:8 +0x0963,-1,itemlistwindowselected,2:4:8:12 0x0943,19,wanttoconnection,2:6:10:14:18 0x0947,26,partyinvite2,2 //0x0862,4 CZ_GANGSI_RANK @@ -1923,7 +1923,7 @@ 0x0874,-1,reqopenbuyingstore,2:4:8:9:89 0x089B,18,bookingregreq,2:4:6 //0x0965,8 CZ_JOIN_BATTLE_FIELD -0x086A,-1,itemlistwindowselected,2:4:8 +0x086A,-1,itemlistwindowselected,2:4:8:12 0x08A9,19,wanttoconnection,2:6:10:14:18 0x0950,26,partyinvite2,2 //0x08AC,4 CZ_GANGSI_RANK @@ -1955,7 +1955,7 @@ 0x0869,-1,reqopenbuyingstore,2:4:8:9:89 0x0874,41,bookingregreq,2,4:6 // 0x088E,8); // CZ_JOIN_BATTLE_FIELD -0x0958,-1,itemlistwindowselected,2:4:8 +0x0958,-1,itemlistwindowselected,2:4:8:12 0x0919,19,wanttoconnection,2:6:10:14:18 0x08A8,26,partyinvite2,2 // 0x0888,4); // CZ_GANGSI_RANK @@ -1987,7 +1987,7 @@ 0x0815,-1,reqopenbuyingstore,2:4:8:9:89 0x0365,41,bookingregreq,2:4:6 // 0x0363,8 // CZ_JOIN_BATTLE_FIELD -0x0281,-1,itemlistwindowselected,2:4:8 +0x0281,-1,itemlistwindowselected,2:4:8:12 0x022D,19,wanttoconnection,2:6:10:14:18 0x0802,26,partyinvite2,2 // 0x0436,4 // CZ_GANGSI_RANK @@ -2019,7 +2019,7 @@ 0x0815,-1,reqopenbuyingstore,2:4:8:9:89 0x0365,18,bookingregreq,2:4:6 // 0x0363,8 CZ_JOIN_BATTLE_FIELD -0x0281,-1,itemlistwindowselected,2:4:8 +0x0281,-1,itemlistwindowselected,2:4:8:12 0x0919,19,wanttoconnection,2:6:10:14:18 0x0802,26,partyinvite2,2 // 0x0436,4 CZ_GANGSI_RANK