From 54aed550d3d0a6208da6651b460fbbff65ca10c4 Mon Sep 17 00:00:00 2001 From: ultramage Date: Tue, 22 Jul 2008 16:09:20 +0000 Subject: Fixed a bug in the npc shop code, where requesting an item that wasn't on the list caused the server to keep scanning past the list's array bounds. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@12980 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/clif.c | 34 +++++++++--------- src/map/npc.c | 112 +++++++++++++++++++++++++++++++++------------------------ 2 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/map/clif.c b/src/map/clif.c index bf9655aff..103994a76 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -8869,27 +8869,29 @@ void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd) npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6)); } -/*========================================== - * - *------------------------------------------*/ -void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd) -{ - int fail=0,n; - unsigned short *item_list; - - n = (RFIFOW(fd,2)-4) /4; - item_list = (unsigned short*)RFIFOP(fd,4); - - if (sd->state.trading|| !sd->npc_shopid) - fail = 1; +/// Request to buy chosen items from npc shop +/// S 00c8 .w {.w .w).4b* +/// R 00ca .b +/// result = 00 -> "The deal has successfully completed." +/// result = 01 -> "You do not have enough zeny." +/// result = 02 -> "You are over your Weight Limit." +/// result = 03 -> "Out of the maximum capacity, you have too many items." +void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd) +{ + int n = (RFIFOW(fd,2)-4) /4; + unsigned short* item_list = (unsigned short*)RFIFOP(fd,4); + int result; + + if( sd->state.trading || !sd->npc_shopid ) + result = 1; else - fail = npc_buylist(sd,n,item_list); + result = npc_buylist(sd,n,item_list); sd->npc_shopid = 0; //Clear shop data. WFIFOHEAD(fd,packet_len(0xca)); - WFIFOW(fd,0)=0xca; - WFIFOB(fd,2)=fail; + WFIFOW(fd,0) = 0xca; + WFIFOB(fd,2) = result; WFIFOSET(fd,packet_len(0xca)); } diff --git a/src/map/npc.c b/src/map/npc.c index 827ee76f8..7247aec33 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1114,49 +1114,60 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po return 0; } -/*========================================== - * - *------------------------------------------*/ +/// Player item purchase from npc shop. +/// +/// @param item_list 'n' pairs +/// @return result code for clif_parse_NpcBuyListSend int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) { - struct npc_data *nd; + struct npc_data* nd; double z; - int i,j,w,skill,itemamount=0,new_=0; + int i,j,w,skill,new_; nullpo_retr(3, sd); nullpo_retr(3, item_list); - if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) + nd = npc_checknear(sd,map_id2bl(sd->npc_shopid)); + if( nd == NULL ) return 3; - - if (nd->master_nd) //Script-based shops. + if( nd->master_nd != NULL ) //Script-based shops. return npc_buylist_sub(sd,n,item_list,nd->master_nd); - - if (nd->subtype != SHOP) + if( nd->subtype != SHOP ) return 3; - for(i=0,w=0,z=0; i < n; i++) { - for(j=0; nd->u.shop.shop_item[j].nameid; j++) { - if (nd->u.shop.shop_item[j].nameid==item_list[i*2+1] || //Normal items - itemdb_viewid(nd->u.shop.shop_item[j].nameid)==item_list[i*2+1]) //item_avail replacement - break; - } - if (nd->u.shop.shop_item[j].nameid == 0 || !itemdb_exists(nd->u.shop.shop_item[j].nameid)) - return 3; + z = 0; + w = 0; + new_ = 0; + // process entries in buy list, one by one + for( i = 0; i < n; ++i ) + { + int nameid, amount, value; + + // find this entry in the shop's sell list + ARR_FIND( 0, nd->u.shop.count, j, + item_list[i*2+1] == nd->u.shop.shop_item[j].nameid || //Normal items + item_list[i*2+1] == itemdb_viewid(nd->u.shop.shop_item[j].nameid) //item_avail replacement + ); + + if( j == nd->u.shop.count ) + return 3; // no such item in shop - if (!itemdb_isstackable(nd->u.shop.shop_item[j].nameid) && item_list[i*2] > 1) + amount = item_list[i*2+0]; + nameid = item_list[i*2+1] = nd->u.shop.shop_item[j].nameid; //item_avail replacement + value = nd->u.shop.shop_item[j].value; + + if( !itemdb_exists(nameid) ) + return 3; // item no longer in itemdb + + if( !itemdb_isstackable(nameid) && amount > 1 ) { //Exploit? You can't buy more than 1 of equipment types o.O ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n", - sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop.shop_item[j].nameid); - item_list[i*2] = 1; + sd->status.name, sd->status.account_id, sd->status.char_id, amount, nameid); + amount = item_list[i*2+0] = 1; } - if (itemdb_value_notdc(nd->u.shop.shop_item[j].nameid)) - z+=(double)nd->u.shop.shop_item[j].value * item_list[i*2]; - else - z+=(double)pc_modifybuyvalue(sd,nd->u.shop.shop_item[j].value) * item_list[i*2]; - itemamount+=item_list[i*2]; - switch(pc_checkadditem(sd,nd->u.shop.shop_item[j].nameid,item_list[i*2])) { + switch( pc_checkadditem(sd,nameid,amount) ) + { case ADDITEM_EXIST: break; @@ -1168,46 +1179,54 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 2; } - w += itemdb_weight(nd->u.shop.shop_item[j].nameid) * item_list[i*2]; + if( !itemdb_value_notdc(nameid) ) + value = pc_modifybuyvalue(sd,value); - if (nd->u.shop.shop_item[j].nameid != item_list[i*2+1]) - item_list[i*2+1] = nd->u.shop.shop_item[j].nameid; // item_avail replacement + z += (double)value * amount; + w += itemdb_weight(nameid) * amount; } - if (z > (double)sd->status.zeny) + + if( z > (double)sd->status.zeny ) return 1; // Not enough Zeny - if (w+sd->weight > sd->max_weight) + if( w + sd->weight > sd->max_weight ) return 2; // Too heavy - if (pc_inventoryblank(sd) < new_) + if( pc_inventoryblank(sd) < new_ ) return 3; // Not enough space to store items //Logs (S)hopping Zeny [Lupus] - if(log_config.zeny > 0 ) + if( log_config.zeny > 0 ) log_zeny(sd, "S", sd, -(int)z); //Logs pc_payzeny(sd,(int)z); - for(i=0; i 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) { - if (sd->status.skill[MC_DISCOUNT].flag != 0) + // custom merchant shop exp bonus + if( battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0 ) + { + if( sd->status.skill[MC_DISCOUNT].flag != 0 ) skill = sd->status.skill[MC_DISCOUNT].flag - 2; - if (skill > 0) { + if( skill > 0 ) + { z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) + if( z < 1 ) z = 1; pc_gainexp(sd,NULL,0,(int)z); } @@ -1222,7 +1241,7 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) { double z; - int i,skill,itemamount=0; + int i,skill; struct npc_data *nd; nullpo_retr(1, sd); @@ -1265,7 +1284,6 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid); pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty); } - itemamount+=qty; pc_delitem(sd,idx,qty,0); } -- cgit v1.2.3-60-g2f50