//-------------------------------------------------------- // Save registry to sql int inter_accreg_tosql(uint32 account_id, uint32 char_id, struct accreg* reg, int type) { StringBuf buf; int i; if( account_id <= 0 ) return 0; reg->account_id = account_id; reg->char_id = char_id; //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) switch( type ) { case 3: //Char Reg if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", schema_config.reg_db, char_id) ) Sql_ShowDebug(sql_handle); account_id = 0; break; case 2: //Account Reg if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", schema_config.reg_db, account_id) ) Sql_ShowDebug(sql_handle); char_id = 0; break; case 1: //Account2 Reg ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n"); return 0; default: ShowError("inter_accreg_tosql: Invalid type %d\n", type); return 0; } if( reg->reg_num <= 0 ) return 0; StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", schema_config.reg_db); for( i = 0; i < reg->reg_num; ++i ) { struct global_reg* r = ®->reg[i]; if( r->str[0] != '\0' && r->value[0] != '\0' ) { char str[32]; char val[256]; if( i > 0 ) StringBuf_AppendStr(&buf, ","); Sql_EscapeString(sql_handle, str, r->str); Sql_EscapeString(sql_handle, val, r->value); StringBuf_Printf(&buf, "('%d','%d','%d','%s','%s')", type, account_id, char_id, str, val); } } if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) { Sql_ShowDebug(sql_handle); } StringBuf_Destroy(&buf); return 1; }
/** * Handles save reg data from map server and distributes accordingly. * * @param val either str or int, depending on type * @param type false when int, true otherwise **/ void inter_savereg(uint32 account_id, uint32 char_id, const char *key, unsigned int index, intptr_t val, bool is_string) { char esc_val[254*2+1]; char esc_key[32*2+1]; Sql_EscapeString(sql_handle, esc_key, key); if( is_string && val ) { Sql_EscapeString(sql_handle, esc_val, (char*)val); } if( key[0] == '#' && key[1] == '#' ) { // global account reg if( session_isValid(login_fd) ) chlogif_send_global_accreg(key,index,val,is_string); else { ShowError("Login server unavailable, can't perform update on '%s' variable for AID:%d CID:%d\n",key,account_id,char_id); } } else if ( key[0] == '#' ) { // local account reg if( is_string ) { if( val ) { if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", schema_config.acc_reg_str_table, account_id, esc_key, index, esc_val) ) Sql_ShowDebug(sql_handle); } else { if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", schema_config.acc_reg_str_table, account_id, esc_key, index) ) Sql_ShowDebug(sql_handle); } } else { if( val ) { if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", schema_config.acc_reg_num_table, account_id, esc_key, index, (int)val) ) Sql_ShowDebug(sql_handle); } else { if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", schema_config.acc_reg_num_table, account_id, esc_key, index) ) Sql_ShowDebug(sql_handle); } } } else { /* char reg */ if( is_string ) { if( val ) { if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", schema_config.char_reg_str_table, char_id, esc_key, index, esc_val) ) Sql_ShowDebug(sql_handle); } else { if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", schema_config.char_reg_str_table, char_id, esc_key, index) ) Sql_ShowDebug(sql_handle); } } else { if( val ) { if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", schema_config.char_reg_num_table, char_id, esc_key, index, (int)val) ) Sql_ShowDebug(sql_handle); } else { if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", schema_config.char_reg_num_table, char_id, esc_key, index) ) Sql_ShowDebug(sql_handle); } } } }
/** * Retrieve data from db and store it in the provided data structure. * Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching. * Filled data structure is done by delegation to account_db_sql_load_num. * @param self: pointer to db * @param acc: pointer of mmo_account to fill * @param userid: name of user account * @return true if successful, false if something has failed */ static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) { AccountDB_SQL* db = (AccountDB_SQL*)self; Sql* sql_handle = db->accounts; char esc_userid[2*NAME_LENGTH+1]; uint32 account_id; char* data; Sql_EscapeString(sql_handle, esc_userid, userid); // get the list of account IDs for this user ID if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'", db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) ) { Sql_ShowDebug(sql_handle); return false; } if( Sql_NumRows(sql_handle) > 1 ) {// serious problem - duplicit account ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid); Sql_FreeResult(sql_handle); return false; } if( SQL_SUCCESS != Sql_NextRow(sql_handle) ) {// no such entry Sql_FreeResult(sql_handle); return false; } Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); return account_db_sql_load_num(self, acc, account_id); }
/* [Dekamaster/Nightroad] */ void mapif_parse_accinfo(int fd) { int u_fd = RFIFOL(fd,2), aid = RFIFOL(fd,6), castergroup = RFIFOL(fd,10); char query[NAME_LENGTH], query_esq[NAME_LENGTH*2+1]; int account_id; char *data; safestrncpy(query, (char *) RFIFOP(fd,14), NAME_LENGTH); Sql_EscapeString(sql_handle, query_esq, query); account_id = atoi(query); if(account_id < START_ACCOUNT_NUM) { // is string if(SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", char_db, query_esq) || Sql_NumRows(sql_handle) == 0) { if(Sql_NumRows(sql_handle) == 0) { inter_msg_to_fd(fd, u_fd, aid, read_message("Source.char.inter_parse_accinfo_s1"), query); } else { Sql_ShowDebug(sql_handle); inter_msg_to_fd(fd, u_fd, aid, read_message("Source.char.inter_parse_accinfo_s2")); } Sql_FreeResult(sql_handle); return; } else { if(Sql_NumRows(sql_handle) == 1) { //we found a perfect match Sql_NextRow(sql_handle); Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_FreeResult(sql_handle); } else {// more than one, listing... [Dekamaster/Nightroad] inter_msg_to_fd(fd, u_fd, aid, read_message("Source.char.inter_parse_accinfo_s3"), (int)Sql_NumRows(sql_handle)); while (SQL_SUCCESS == Sql_NextRow(sql_handle)) { int class_; short base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name)); Sql_GetData(sql_handle, 2, &data, NULL); class_ = atoi(data); Sql_GetData(sql_handle, 3, &data, NULL); base_level = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); job_level = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); online = atoi(data); inter_msg_to_fd(fd, u_fd, aid, read_message("Source.char.inter_parse_accinfo_s4"), account_id, name, job_name(class_), base_level, job_level, online?"Online":"Offline"); } Sql_FreeResult(sql_handle); return; } } } /* it will only get here if we have a single match */ /* and we will send packet with account id to login server asking for account info */ if(account_id) { mapif_on_parse_accinfo(account_id, u_fd, aid, castergroup, fd); } return; }
/** * Receive a account_info request from map-serv * @author : [Dekamaster/Nightroad] * @param fd : map-serv link */ void mapif_parse_accinfo(int fd) { int u_fd = RFIFOL(fd,2), u_aid = RFIFOL(fd,6), u_group = RFIFOL(fd,10); char type= RFIFOB(fd,14); char query[NAME_LENGTH], query_esq[NAME_LENGTH*2+1]; uint32 account_id = 0; char *data; safestrncpy(query, RFIFOCP(fd,15), NAME_LENGTH); Sql_EscapeString(sql_handle, query_esq, query); account_id = atoi(query); if (account_id < START_ACCOUNT_NUM) { // is string if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", schema_config.char_db, query_esq) || Sql_NumRows(sql_handle) == 0 ) { if( Sql_NumRows(sql_handle) == 0 ) { inter_to_fd(fd, u_fd, u_aid, (char *)msg_txt(212) ,query); } else { Sql_ShowDebug(sql_handle); inter_to_fd(fd, u_fd, u_aid, (char *)msg_txt(213)); } Sql_FreeResult(sql_handle); return; } else { if( Sql_NumRows(sql_handle) == 1 ) {//we found a perfect match Sql_NextRow(sql_handle); Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_FreeResult(sql_handle); } else {// more than one, listing... [Dekamaster/Nightroad] inter_to_fd(fd, u_fd, u_aid, (char *)msg_txt(214),(int)Sql_NumRows(sql_handle)); while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { int class_; short base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name)); Sql_GetData(sql_handle, 2, &data, NULL); class_ = atoi(data); Sql_GetData(sql_handle, 3, &data, NULL); base_level = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); job_level = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); online = atoi(data); inter_to_fd(fd, u_fd, u_aid, (char *)msg_txt(215), account_id, name, job_name(class_), base_level, job_level, online?"Online":"Offline"); } Sql_FreeResult(sql_handle); return; } } } /* it will only get here if we have a single match then ask login-server to fetch the `login` record */ if (!account_id || chlogif_req_accinfo(fd, u_fd, u_aid, u_group, account_id, type) != 1) { inter_to_fd(fd, u_fd, u_aid, (char *)msg_txt(213)); } return; }
/** * Player setup a new shop * @param sd : player opening the shop * @param message : shop title * @param data : itemlist data \n * data := {<index>.w <amount>.w <value>.l}[count] * @param count : number of different items * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - Cart data isn't saved yet, 5 - No valid item found */ char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) { int i, j; int vending_skill_lvl; char message_sql[MESSAGE_SIZE*2]; nullpo_retr(false,sd); if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) { return 1; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once } vending_skill_lvl = pc_checkskill(sd, MC_VENDING); // skill level and cart check if( !vending_skill_lvl || !pc_iscarton(sd) ) { clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 2; } // check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 3; } if (save_settings&2) // Avoid invalid data from saving chrif_save(sd, 0); // filter out invalid items i = 0; for( j = 0; j < count; j++ ) { short index = *(uint16*)(data + 8*j + 0); short amount = *(uint16*)(data + 8*j + 2); unsigned int value = *(uint32*)(data + 8*j + 4); index -= 2; // offset adjustment (client says that the first cart position is 2) if( index < 0 || index >= MAX_CART // invalid position || pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity //NOTE: official server does not do any of the following checks! || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case || (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item continue; sd->vending[i].index = index; sd->vending[i].amount = amount; sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value); // Player just moved item to cart and we don't have the correct cart ID yet. if (sd->status.cart[sd->vending[i].index].id == 0) { struct item_data *idb = itemdb_search(sd->status.cart[index].nameid); char msg[256]; sprintf(msg, msg_txt(sd, 733), idb->jname); clif_displaymessage(sd->fd, msg); clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 4; } i++; // item successfully added } if( i != j ) clif_displaymessage (sd->fd, msg_txt(sd,266)); //"Some of your items cannot be vended and were removed from the shop." if( i == 0 ) { // no valid item found clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet return 5; } sd->state.prevend = 0; sd->state.vending = true; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); Sql_EscapeString( mmysql_handle, message_sql, sd->message ); if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`autotrade`, `body_direction`, `head_direction`, `sit`) " "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d' );", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } for( i = 0; i < count; i++ ) { if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES( %d, %d, %d, %d, %d );", vending_items_db, sd->vender_id, i, sd->status.cart[sd->vending[i].index].id, sd->vending[i].amount, sd->vending[i].value ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } } clif_openvending(sd,sd->bl.id,sd->vending); clif_showvendingboard(&sd->bl,message,0); idb_put(vending_db, sd->status.char_id, sd); return 0; }
/** * Attempt to create new buying store * @param sd * @param zenylimit * @param result * @param storename * @param *itemlist { <nameid>.W, <amount>.W, <price>.L }* * @param count Number of item on the itemlist * @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight */ char buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count) { unsigned int i, weight, listidx; char message_sql[MESSAGE_SIZE*2]; nullpo_retr(1, sd); if( !result || count == 0 ) {// canceled, or no items return 5; } if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] ) {// disabled or invalid input sd->buyingstore.slots = 0; clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return 1; } if( !pc_can_give_items(sd) ) {// custom: GM is not allowed to buy (give zeny) sd->buyingstore.slots = 0; clif_displaymessage(sd->fd, msg_txt(sd,246)); clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return 6; } if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) ) {// custom: mute limitation return 2; } if( map[sd->bl.m].flag.novending ) {// custom: no vending maps clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map" return 3; } if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) {// custom: no vending cells clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell." return 4; } weight = sd->weight; // check item list for( i = 0; i < count; i++ ) {// itemlist: <name id>.W <amount>.W <price>.L unsigned short nameid, amount; int price, idx; struct item_data* id; nameid = RBUFW(itemlist,i*8+0); amount = RBUFW(itemlist,i*8+2); price = RBUFL(itemlist,i*8+4); if( ( id = itemdb_exists(nameid) ) == NULL || amount == 0 ) {// invalid input break; } if( price <= 0 || price > BUYINGSTORE_MAX_PRICE ) {// invalid price: unlike vending, items cannot be bought at 0 Zeny break; } if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_get_group_level(sd), pc_get_group_level(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) {// restrictions: allowed, no character-bound items and at least one must be owned break; } if( sd->status.inventory[idx].amount+amount > BUYINGSTORE_MAX_AMOUNT ) {// too many items of same kind break; } if( i ) {// duplicate check. as the client does this too, only malicious intent should be caught here ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == nameid ); if( listidx != i ) {// duplicate ShowWarning("buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", nameid, amount, sd->status.account_id, sd->status.char_id); break; } } weight+= id->weight*amount; sd->buyingstore.items[i].nameid = nameid; sd->buyingstore.items[i].amount = amount; sd->buyingstore.items[i].price = price; } if( i != count ) {// invalid item/amount/price sd->buyingstore.slots = 0; clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); return 5; } if( (sd->max_weight*90)/100 < weight ) {// not able to carry all wanted items without getting overweight (90%) sd->buyingstore.slots = 0; clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE_OVERWEIGHT, weight); return 7; } // success sd->state.buyingstore = true; sd->buyer_id = buyingstore_getuid(); sd->buyingstore.zenylimit = zenylimit; sd->buyingstore.slots = i; // store actual amount of items safestrncpy(sd->message, storename, sizeof(sd->message)); Sql_EscapeString( mmysql_handle, message_sql, sd->message ); if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`limit`,`autotrade`, `body_direction`, `head_direction`, `sit`) " "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, %d, '%d', '%d', '%d' );", buyingstores_db, sd->buyer_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->buyingstore.zenylimit, sd->state.autotrade, sd->ud.dir, sd->head_dir, pc_issit(sd) ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } for( i = 0; i < sd->buyingstore.slots; i++ ){ if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`buyingstore_id`,`index`,`item_id`,`amount`,`price`) VALUES( %d, %d, %hu, %d, %d );", buyingstore_items_db, sd->buyer_id, i, sd->buyingstore.items[i].nameid, sd->buyingstore.items[i].amount, sd->buyingstore.items[i].price ) != SQL_SUCCESS ){ Sql_ShowDebug(mmysql_handle); } } clif_buyingstore_myitemlist(sd); clif_buyingstore_entry(sd); idb_put(buyingstore_db, sd->status.char_id, sd); return 0; }
/* [Dekamaster/Nightroad] */ void mapif_parse_accinfo(int fd) { int u_fd = RFIFOL(fd,2), aid = RFIFOL(fd,6), castergroup = RFIFOL(fd,10); char query[NAME_LENGTH], query_esq[NAME_LENGTH*2+1]; int account_id; char *data; safestrncpy(query, (char*) RFIFOP(fd,14), NAME_LENGTH); Sql_EscapeString(sql_handle, query_esq, query); account_id = atoi(query); if (account_id < START_ACCOUNT_NUM) { // is string if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `char` WHERE `name` LIKE '%s' LIMIT 10", query_esq) || Sql_NumRows(sql_handle) == 0 ) { if( Sql_NumRows(sql_handle) == 0 ) { inter_to_fd(fd, u_fd, aid, "No matches were found for your criteria, '%s'",query); } else { Sql_ShowDebug(sql_handle); inter_to_fd(fd, u_fd, aid, "An error occured, bother your admin about it."); } Sql_FreeResult(sql_handle); return; } else { if( Sql_NumRows(sql_handle) == 1 ) {//we found a perfect match Sql_NextRow(sql_handle); Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_FreeResult(sql_handle); } else {// more than one, listing... [Dekamaster/Nightroad] inter_to_fd(fd, u_fd, aid, "Your query returned the following %d results, please be more specific...",(int)Sql_NumRows(sql_handle)); while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { int class_; short base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name)); Sql_GetData(sql_handle, 2, &data, NULL); class_ = atoi(data); Sql_GetData(sql_handle, 3, &data, NULL); base_level = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); job_level = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); online = atoi(data); inter_to_fd(fd, u_fd, aid, "[AID: %d] %s | %s | Level: %d/%d | %s", account_id, name, job_name(class_), base_level, job_level, online?"Online":"Offline"); } Sql_FreeResult(sql_handle); return; } } } /* it will only get here if we have a single match */ if( account_id ) { char userid[NAME_LENGTH], user_pass[NAME_LENGTH], email[40], last_ip[20], lastlogin[30]; short level = -1; int logincount = 0,state = 0; if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `userid`, `user_pass`, `email`, `last_ip`, `group_id`, `lastlogin`, `logincount`, `state` FROM `login` WHERE `account_id` = '%d' LIMIT 1", account_id) || Sql_NumRows(sql_handle) == 0 ) { if( Sql_NumRows(sql_handle) == 0 ) { inter_to_fd(fd, u_fd, aid, "No account with ID '%d' was found.", account_id ); } else { inter_to_fd(fd, u_fd, aid, "An error occured, bother your admin about it."); Sql_ShowDebug(sql_handle); } } else { Sql_NextRow(sql_handle); Sql_GetData(sql_handle, 0, &data, NULL); safestrncpy(userid, data, sizeof(userid)); Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(user_pass, data, sizeof(user_pass)); Sql_GetData(sql_handle, 2, &data, NULL); safestrncpy(email, data, sizeof(email)); Sql_GetData(sql_handle, 3, &data, NULL); safestrncpy(last_ip, data, sizeof(last_ip)); Sql_GetData(sql_handle, 4, &data, NULL); level = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); safestrncpy(lastlogin, data, sizeof(lastlogin)); Sql_GetData(sql_handle, 6, &data, NULL); logincount = atoi(data); Sql_GetData(sql_handle, 7, &data, NULL); state = atoi(data); } Sql_FreeResult(sql_handle); if (level == -1) return; inter_to_fd(fd, u_fd, aid, "-- Account %d --", account_id ); inter_to_fd(fd, u_fd, aid, "User: %s | GM Group: %d | State: %d", userid, level, state ); if (level < castergroup) /* only show pass if your gm level is greater than the one you're searching for */ inter_to_fd(fd, u_fd, aid, "Password: %s", user_pass ); inter_to_fd(fd, u_fd, aid, "Account e-mail: %s", email); inter_to_fd(fd, u_fd, aid, "Last IP: %s (%s)", last_ip, geoip_getcountry(str2ip(last_ip)) ); inter_to_fd(fd, u_fd, aid, "This user has logged %d times, the last time were at %s", logincount, lastlogin ); inter_to_fd(fd, u_fd, aid, "-- Character Details --" ); if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`, `name`, `char_num`, `class`, `base_level`, `job_level`, `online` FROM `char` WHERE `account_id` = '%d' ORDER BY `char_num` LIMIT %d", account_id, MAX_CHARS) || Sql_NumRows(sql_handle) == 0 ) { if( Sql_NumRows(sql_handle) == 0 ) inter_to_fd(fd, u_fd, aid,"This account doesn't have characters."); else { inter_to_fd(fd, u_fd, aid,"An error occured, bother your admin about it."); Sql_ShowDebug(sql_handle); } } else { while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { int char_id, class_; short char_num, base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData(sql_handle, 0, &data, NULL); char_id = atoi(data); Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name)); Sql_GetData(sql_handle, 2, &data, NULL); char_num = atoi(data); Sql_GetData(sql_handle, 3, &data, NULL); class_ = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); base_level = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); job_level = atoi(data); Sql_GetData(sql_handle, 6, &data, NULL); online = atoi(data); inter_to_fd(fd, u_fd, aid, "[Slot/CID: %d/%d] %s | %s | Level: %d/%d | %s", char_num, char_id, name, job_name(class_), base_level, job_level, online?"On":"Off"); } } Sql_FreeResult(sql_handle); } return; }
/** * Player setup a new shop * @param sd : player opening the shop * @param message : shop title * @param data : itemlist data * data := {<index>.w <amount>.w <value>.l}[count] * @param count : number of different items * @param at Autotrader info, or NULL if requetsed not from autotrade persistance * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - Cart data isn't saved yet, 5 - No valid item found */ int8 vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count, struct s_autotrader *at) { int i, j, k, n; int vending_skill_lvl; char message_sql[MESSAGE_SIZE*2]; int item_bad_price[MAX_VENDING]; StringBuf buf; struct item_data *item; nullpo_retr(false,sd); if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) { return 1; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once } vending_skill_lvl = pc_checkskill(sd, MC_VENDING); // skill level and cart check if( !vending_skill_lvl || !pc_iscarton(sd) ) { clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 2; } // check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 3; } if (save_settings&CHARSAVE_VENDING) // Avoid invalid data from saving chrif_save(sd, 0); // filter out invalid items i = k = 0; for( j = 0; j < count; j++ ) { short index = *(uint16*)(data + 8*j + 0); short amount = *(uint16*)(data + 8*j + 2); unsigned int value = *(uint32*)(data + 8*j + 4); index -= 2; // offset adjustment (client says that the first cart position is 2) if( index < 0 || index >= MAX_CART // invalid position || pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity //NOTE: official server does not do any of the following checks! || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case || (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item continue; item = itemdb_search(sd->status.cart[index].nameid); if (item->value_buy_min > 0 && value > item->value_buy_min) { if (battle_config.vending_price_min_overflow > 0 ) { if (value > item->value_buy_min + (item->value_buy_min * (battle_config.vending_price_min_overflow / 10000.))) { item_bad_price[k++] = sd->status.cart[index].nameid; continue; } } else { item_bad_price[k++] = sd->status.cart[index].nameid; continue; } } sd->vending[i].index = index; sd->vending[i].amount = amount; sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value); // Player just moved item to cart and we don't have the correct cart ID yet. if (sd->status.cart[sd->vending[i].index].id == 0) { char msg[256]; snprintf(msg, 256, "äÍà·çÁ %s ÂѧäÁèä´éºÑ¹·Ö¡. ¡ÃسÒÍÍ¡à¢éÒãËÁè à¾×èÍãËéäÍà·çÁ·Ó¡ÒÃ૿ŧÃéÒ¹¤éÒ", item->jname); clif_displaymessage(sd->fd, msg); clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); return 4; } i++; // item successfully added } //if( i != j ) //clif_displaymessage (sd->fd, msg_txt(sd,266)); //"Some of your items cannot be vended and were removed from the shop." for (n = 0; n < k; n++) { char msg[512]; item = itemdb_search(item_bad_price[n]); if (battle_config.vending_price_min_overflow > 0) sprintf(msg, "%s µÑé§ÃÒ¤Ò¢ÒÂÊÙ§¡ÇèÒ·Õè NPC ÁÕ¢ÒÂÍÂÙèà¡Ô¹ %d%% ¨Ð¶Ù¡µÑ´ÍÍ¡¨Ò¡ÃÒ¡ÒÃà¾×èÍ»éͧ¡Ñ¹¡ÒÃâ¡è§ÃÒ¤Ò", item->jname, battle_config.vending_price_min_overflow/100); else sprintf(msg, "%s µÑé§ÃÒ¤Ò¢ÒÂÊÙ§¡ÇèÒ·Õè NPC ÁÕ¢ÒÂÍÂÙè ¨Ð¶Ù¡µÑ´ÍÍ¡¨Ò¡ÃÒ¡ÒÃà¾×èÍ»éͧ¡Ñ¹¡ÒÃâ¡è§ÃÒ¤Ò", item->jname); clif_displaymessage(sd->fd, msg); } if( i == 0 ) { // no valid item found clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet return 5; } sd->state.prevend = 0; sd->state.vending = true; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); Sql_EscapeString( mmysql_handle, message_sql, sd->message ); if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`, `account_id`, `char_id`, `sex`, `map`, `x`, `y`, `title`, `autotrade`, `body_direction`, `head_direction`, `sit`) " "VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d' );", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 0 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, at ? at->dir : sd->ud.dir, at ? at->head_dir : sd->head_dir, at ? at->sit : pc_issit(sd) ) != SQL_SUCCESS ) { Sql_ShowDebug(mmysql_handle); } StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES", vending_items_db); for (i = 0; i < count; i++) { StringBuf_Printf(&buf, "(%d,%d,%d,%d,%d)", sd->vender_id, i, sd->status.cart[sd->vending[i].index].id, sd->vending[i].amount, sd->vending[i].value); if (i < count-1) StringBuf_AppendStr(&buf, ","); } if (SQL_ERROR == Sql_QueryStr(mmysql_handle, StringBuf_Value(&buf))) Sql_ShowDebug(mmysql_handle); StringBuf_Destroy(&buf); clif_openvending(sd,sd->bl.id,sd->vending); clif_showvendingboard(&sd->bl,message,0); idb_put(vending_db, sd->status.char_id, sd); return 0; }
//-------------------------------------------------------- // Save registry to sql int inter_accreg_tosql (int account_id, int char_id, struct accreg *reg, int type) { struct global_reg *r; StringBuf buf; int i; if (account_id <= 0) return 0; reg->account_id = account_id; reg->char_id = char_id; //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) switch (type) { case 3: //Char Reg if (SQL_ERROR == Sql_Query (sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id)) Sql_ShowDebug (sql_handle); account_id = 0; break; case 2: //Account Reg if (SQL_ERROR == Sql_Query (sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id)) Sql_ShowDebug (sql_handle); char_id = 0; break; case 1: //Account2 Reg ShowError ("inter_accreg_tosql: char-server não deveria manusear registros de valor tipo 1 (##). Isto é trabalho do login-server!\n"); return 0; default: ShowError ("inter_accreg_tosql: Tipo inválido: %d\n", type); return 0; } if (reg->reg_num <= 0) return 0; StringBuf_Init (&buf); StringBuf_Printf (&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", reg_db); for (i = 0; i < reg->reg_num; ++i) { r = ®->reg[i]; if (r->str[0] != '\0' && r->value[0] != '\0') { char str[32]; char val[256]; if (i > 0) StringBuf_AppendStr (&buf, ","); Sql_EscapeString (sql_handle, str, r->str); Sql_EscapeString (sql_handle, val, r->value); StringBuf_Printf (&buf, "('%d','%d','%d','%s','%s')", type, account_id, char_id, str, val); } } if (SQL_ERROR == Sql_QueryStr (sql_handle, StringBuf_Value (&buf))) { Sql_ShowDebug (sql_handle); } StringBuf_Destroy (&buf); return 1; }
/* [Dekamaster/Nightroad] */ void mapif_parse_accinfo (int fd) { int u_fd = RFIFOL (fd, 2), aid = RFIFOL (fd, 6), castergroup = RFIFOL (fd, 10); char query[NAME_LENGTH], query_esq[NAME_LENGTH * 2 + 1]; int account_id; char *data; safestrncpy (query, (char *) RFIFOP (fd, 14), NAME_LENGTH); Sql_EscapeString (sql_handle, query_esq, query); account_id = atoi (query); if (account_id < START_ACCOUNT_NUM) { // is string if (SQL_ERROR == Sql_Query (sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `char` WHERE `name` LIKE '%s' LIMIT 10", query_esq) || Sql_NumRows (sql_handle) == 0) { if (Sql_NumRows (sql_handle) == 0) { inter_to_fd (fd, u_fd, aid, "Não foram encontrados resultados para seu critério, '%s'", query); } else { Sql_ShowDebug (sql_handle); inter_to_fd (fd, u_fd, aid, "Um erro ocorreu, incomode seu administrador sobre isso."); } Sql_FreeResult (sql_handle); return; } else { if (Sql_NumRows (sql_handle) == 1) {//we found a perfect match Sql_NextRow (sql_handle); Sql_GetData (sql_handle, 0, &data, NULL); account_id = atoi (data); Sql_FreeResult (sql_handle); } else {// more than one, listing... [Dekamaster/Nightroad] inter_to_fd (fd, u_fd, aid, "Sua requisição retornou %d resultados, seja mais específico...", (int) Sql_NumRows (sql_handle)); while (SQL_SUCCESS == Sql_NextRow (sql_handle)) { int class_; short base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData (sql_handle, 0, &data, NULL); account_id = atoi (data); Sql_GetData (sql_handle, 1, &data, NULL); safestrncpy (name, data, sizeof (name)); Sql_GetData (sql_handle, 2, &data, NULL); class_ = atoi (data); Sql_GetData (sql_handle, 3, &data, NULL); base_level = atoi (data); Sql_GetData (sql_handle, 4, &data, NULL); job_level = atoi (data); Sql_GetData (sql_handle, 5, &data, NULL); online = atoi (data); inter_to_fd (fd, u_fd, aid, "[AID: %d] %s | %s | Nível: %d/%d | %s", account_id, name, job_name (class_), base_level, job_level, online ? "Online" : "Offline"); } Sql_FreeResult (sql_handle); return; } } } /* it will only get here if we have a single match */ if (account_id) { char userid[NAME_LENGTH], user_pass[NAME_LENGTH], email[40], last_ip[20], lastlogin[30]; short level = -1; int logincount = 0, state = 0; if (SQL_ERROR == Sql_Query (sql_handle, "SELECT `userid`, `user_pass`, `email`, `last_ip`, `group_id`, `lastlogin`, `logincount`, `state` FROM `login` WHERE `account_id` = '%d' LIMIT 1", account_id) || Sql_NumRows (sql_handle) == 0) { if (Sql_NumRows (sql_handle) == 0) { inter_to_fd (fd, u_fd, aid, "Nenhuma conta de ID '%d' foi encontrada.", account_id); } else { inter_to_fd (fd, u_fd, aid, "Um erro ocorreu, incomode seu administrador sobre isso."); Sql_ShowDebug (sql_handle); } } else { Sql_NextRow (sql_handle); Sql_GetData (sql_handle, 0, &data, NULL); safestrncpy (userid, data, sizeof (userid)); Sql_GetData (sql_handle, 1, &data, NULL); safestrncpy (user_pass, data, sizeof (user_pass)); Sql_GetData (sql_handle, 2, &data, NULL); safestrncpy (email, data, sizeof (email)); Sql_GetData (sql_handle, 3, &data, NULL); safestrncpy (last_ip, data, sizeof (last_ip)); Sql_GetData (sql_handle, 4, &data, NULL); level = atoi (data); Sql_GetData (sql_handle, 5, &data, NULL); safestrncpy (lastlogin, data, sizeof (lastlogin)); Sql_GetData (sql_handle, 6, &data, NULL); logincount = atoi (data); Sql_GetData (sql_handle, 7, &data, NULL); state = atoi (data); } Sql_FreeResult (sql_handle); if (level == -1) return; inter_to_fd (fd, u_fd, aid, "-- Conta %d --", account_id); inter_to_fd (fd, u_fd, aid, "Usuário: %s | Grupo de GM: %d | Estado: %d", userid, level, state); if (level < castergroup) /* only show pass if your gm level is greater than the one you're searching for */ inter_to_fd (fd, u_fd, aid, "Senha: %s", user_pass); inter_to_fd (fd, u_fd, aid, "Conta de email: %s", email); inter_to_fd (fd, u_fd, aid, "Último IP: %s (%s)", last_ip, geoip_getcountry (str2ip (last_ip))); inter_to_fd (fd, u_fd, aid, "Este usuário logou %d vezes, a última vez foi em %s", logincount, lastlogin); inter_to_fd (fd, u_fd, aid, "-- Detalhes do personagem --"); if (SQL_ERROR == Sql_Query (sql_handle, "SELECT `char_id`, `name`, `char_num`, `class`, `base_level`, `job_level`, `online` FROM `char` WHERE `account_id` = '%d' ORDER BY `char_num` LIMIT %d", account_id, MAX_CHARS) || Sql_NumRows (sql_handle) == 0) { if (Sql_NumRows (sql_handle) == 0) inter_to_fd (fd, u_fd, aid, "Esta conta não tem personagens."); else { inter_to_fd (fd, u_fd, aid, "Um erro ocorreu, fale com seu administrador sobre isso."); Sql_ShowDebug (sql_handle); } } else { while (SQL_SUCCESS == Sql_NextRow (sql_handle)) { int char_id, class_; short char_num, base_level, job_level, online; char name[NAME_LENGTH]; Sql_GetData (sql_handle, 0, &data, NULL); char_id = atoi (data); Sql_GetData (sql_handle, 1, &data, NULL); safestrncpy (name, data, sizeof (name)); Sql_GetData (sql_handle, 2, &data, NULL); char_num = atoi (data); Sql_GetData (sql_handle, 3, &data, NULL); class_ = atoi (data); Sql_GetData (sql_handle, 4, &data, NULL); base_level = atoi (data); Sql_GetData (sql_handle, 5, &data, NULL); job_level = atoi (data); Sql_GetData (sql_handle, 6, &data, NULL); online = atoi (data); inter_to_fd (fd, u_fd, aid, "[Slot/CID: %d/%d] %s | %s | Nível: %d/%d | %s", char_num, char_id, name, job_name (class_), base_level, job_level, online ? "On" : "Off"); } } Sql_FreeResult (sql_handle); } return; }
/** * Player setup a new shop * @param sd : player opening the shop * @param message : shop title * @param data : itemlist data * data := {<index>.w <amount>.w <value>.l}[count] * @param count : number of different items * @param at Autotrader info, or NULL if requetsed not from autotrade persistance * @return 0 If success, 1 - Cannot open (die, not state.prevend, trading), 2 - No cart, 3 - Count issue, 4 - No valid item found */ int8 vending_openvending(struct map_session_data *sd, const char *message, const uint8 *data, int count, struct s_autotrader *at) { int i, j; int vending_skill_lvl; char message_sql[MESSAGE_SIZE * 2]; StringBuf buf; nullpo_retr(1, sd); if( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd) ) return 1; //Can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once vending_skill_lvl = pc_checkskill(sd, MC_VENDING); //Skill level and cart check if( !vending_skill_lvl || !pc_iscarton(sd) ) { clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0, 0); return 2; } //Check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { //Invalid item count clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0, 0); return 3; } if( save_settings&CHARSAVE_VENDING ) // Avoid invalid data from saving chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART); //Filter out invalid items i = 0; for( j = 0; j < count; j++ ) { short index = *(uint16 *)(data + 8 * j + 0); short amount = *(uint16 *)(data + 8 * j + 2); unsigned int value = *(uint32 *)(data + 8 * j + 4); index -= 2; //Offset adjustment (client says that the first cart position is 2) if( index < 0 || index >= MAX_CART || //Invalid position pc_cartitem_amount(sd, index, amount) < 0 || //Invalid item or insufficient quantity //NOTE: Official server does not do any of the following checks! !sd->cart.u.items_cart[index].identify || //Unidentified item sd->cart.u.items_cart[index].attribute || //Broken item sd->cart.u.items_cart[index].expire_time || //It should not be in the cart but just in case (sd->cart.u.items_cart[index].bound && !pc_can_give_bounded_items(sd)) || //Can't trade account bound items and has no permission !itemdb_cantrade(&sd->cart.u.items_cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) //Untradeable item continue; sd->vending[i].index = index; sd->vending[i].amount = amount; sd->vending[i].value = umin(value, (unsigned int)battle_config.vending_max_value); i++; //Item successfully added } if( i != j ) clif_displaymessage(sd->fd, msg_txt(266)); // "Some of your items cannot be vended and were removed from the shop." if( i == 0 ) { //No valid item found clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0, 0); //Custom reply packet return 4; } sd->state.prevend = 0; sd->state.vending = 1; sd->vender_id = vending_getuid(); sd->vend_num = i; safestrncpy(sd->message, message, MESSAGE_SIZE); Sql_EscapeString(mmysql_handle, message_sql, sd->message); if( Sql_Query(mmysql_handle, "INSERT INTO `%s`(`id`,`account_id`,`char_id`,`sex`,`map`,`x`,`y`,`title`,`autotrade`,`body_direction`,`head_direction`,`sit`) VALUES(%d, %d, %d, '%c', '%s', %d, %d, '%s', %d, '%d', '%d', '%d');", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, (!sd->status.sex ? 'F' : 'M'), map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade, (at ? at->dir : sd->ud.dir), (at ? at->head_dir : sd->head_dir), (at ? at->sit : pc_issit(sd))) != SQL_SUCCESS ) Sql_ShowDebug(mmysql_handle); StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES", vending_items_db); for( j = 0; j < i; j++ ) { StringBuf_Printf(&buf, "(%d,%d,%d,%d,%d)", sd->vender_id, j, sd->cart.u.items_cart[sd->vending[j].index].id, sd->vending[j].amount, sd->vending[j].value); if( j < i - 1 ) StringBuf_AppendStr(&buf, ","); } if( SQL_ERROR == Sql_QueryStr(mmysql_handle, StringBuf_Value(&buf)) ) Sql_ShowDebug(mmysql_handle); StringBuf_Destroy(&buf); clif_openvending(sd, sd->bl.id, sd->vending); clif_showvendingboard(&sd->bl, message, 0); idb_put(vending_db, sd->status.char_id, sd); return 0; }