/* * Client is requesting to move a charslot */ int chclif_parse_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( (charserv_config.charmove_config.char_move_enabled)==0 || ( (charserv_config.charmove_config.char_moves_unlimited)==0 && sd->char_moves[from] <= 0 ) ){ chclif_moveCharSlotReply( fd, sd, from, 1 ); return 1; } // We don't even have a character on the chosen slot? if( sd->found_char[from] <= 0 || to >= sd->char_slots ){ chclif_moveCharSlotReply( fd, sd, from, 1 ); return 1; } if( sd->found_char[to] > 0 ){ // We want to move to a used position if( charserv_config.charmove_config.char_movetoused ){ // TODO: check if the target is in deletion process // Admin is friendly and uses triangle exchange if( SQL_ERROR == Sql_QueryStr(sql_handle, "START TRANSACTION") || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'",schema_config.char_db, to, sd->found_char[from] ) || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'", schema_config.char_db, from, sd->found_char[to] ) || SQL_ERROR == Sql_QueryStr(sql_handle, "COMMIT") ){ chclif_moveCharSlotReply( fd, sd, from, 1 ); Sql_ShowDebug(sql_handle); Sql_QueryStr(sql_handle,"ROLLBACK"); return 1; } }else{ // Admin doesn't allow us to chclif_moveCharSlotReply( fd, sd, from, 1 ); return 1; } }else if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d'", schema_config.char_db, to, sd->found_char[from] ) ){ Sql_ShowDebug(sql_handle); chclif_moveCharSlotReply( fd, sd, from, 1 ); return 1; } if( (charserv_config.charmove_config.char_moves_unlimited)==0 ){ sd->char_moves[from]--; Sql_Query(sql_handle, "UPDATE `%s` SET `moves`='%d' WHERE `char_id`='%d'", schema_config.char_db, sd->char_moves[from], sd->found_char[from] ); } // We successfully moved the char - time to notify the client chclif_moveCharSlotReply( fd, sd, from, 0 ); chclif_mmo_char_send(fd, sd); return 1; }
void itemdb_save_serials(void) { struct item_data *id; StringBuf buf; int count = 0, i; StringBuf_Init(&buf); StringBuf_Printf(&buf, "REPLACE INTO `item_serials` (`nameid`, `serial`) VALUES "); for( i = 0; i < ARRAYLENGTH(itemdb_array); i++ ) { id = itemdb_array[i]; if( id == NULL || itemdb_isstackable2(id) || id->last_serial == 0 || !id->changed ) continue; if( count ) StringBuf_AppendStr(&buf, ","); StringBuf_Printf(&buf, "('%d','%u')", id->nameid, id->last_serial); id->changed = false; count++; } if( count && SQL_ERROR == Sql_QueryStr(mmysql_handle, StringBuf_Value(&buf)) ) Sql_ShowDebug(mmysql_handle); StringBuf_Destroy(&buf); return; }
/** * Delete an existing account entry and its regs. * @param self: pointer to db * @param account_id: id of user account * @return true if successful, false if something has failed */ static bool account_db_sql_remove(AccountDB* self, const uint32 account_id) { AccountDB_SQL* db = (AccountDB_SQL*)self; Sql* sql_handle = db->accounts; bool result = false; if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION") || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id) || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->accreg_db, account_id) ) Sql_ShowDebug(sql_handle); else result = true; result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); return result; }
//-------------------------------------------------------- // 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; }
//Request to save skill cooldown data int chmapif_parse_req_saveskillcooldown(int fd){ if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) ) return 0; else { int count, aid, cid; aid = RFIFOL(fd,4); cid = RFIFOL(fd,8); count = RFIFOW(fd,12); if( count > 0 ) { struct skill_cooldown_data data; StringBuf buf; int i; StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `skill`, `tick`) VALUES ", schema_config.skillcooldown_db); for( i = 0; i < count; ++i ) { memcpy(&data,RFIFOP(fd,14+i*sizeof(struct skill_cooldown_data)),sizeof(struct skill_cooldown_data)); if( i > 0 ) StringBuf_AppendStr(&buf, ", "); StringBuf_Printf(&buf, "('%d','%d','%d','%d')", aid, cid, data.skill_id, data.tick); } if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) Sql_ShowDebug(sql_handle); StringBuf_Destroy(&buf); } RFIFOSKIP(fd, RFIFOW(fd, 2)); } return 1; }
/** * Updates an achievement in a character's achievementlog. * @param char_id: Character ID * @param ad: Achievement data * @return false in case of errors, true otherwise */ bool mapif_achievement_update(uint32 char_id, struct achievement* ad) { StringBuf buf; int i; StringBuf_Init(&buf); StringBuf_Printf(&buf, "UPDATE `%s` SET ", schema_config.achievement_table); if( ad->completed ){ StringBuf_Printf(&buf, "`completed` = FROM_UNIXTIME('%u'),", (uint32)ad->completed); }else{ StringBuf_AppendStr(&buf, "`completed` = NULL,"); } if( ad->rewarded ){ StringBuf_Printf(&buf, "`rewarded` = FROM_UNIXTIME('%u')", (uint32)ad->rewarded); }else{ StringBuf_AppendStr(&buf, "`rewarded` = NULL"); } for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; ++i) StringBuf_Printf(&buf, ", `count%d` = '%d'", i + 1, ad->count[i]); StringBuf_Printf(&buf, " WHERE `id` = %d AND `char_id` = %u", ad->achievement_id, char_id); if (SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf))) { Sql_ShowDebug(sql_handle); StringBuf_Destroy(&buf); return false; } StringBuf_Destroy(&buf); return true; }
/** * Adds an achievement to a character's achievementlog. * @param char_id: Character ID * @param ad: Achievement data * @return false in case of errors, true otherwise */ bool mapif_achievement_add(uint32 char_id, struct achievement* ad) { StringBuf buf; int i; StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `id`, `completed`, `rewarded`", schema_config.achievement_table); for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; ++i) StringBuf_Printf(&buf, ", `count%d`", i + 1); StringBuf_AppendStr(&buf, ")"); StringBuf_Printf(&buf, " VALUES ('%u', '%d',", char_id, ad->achievement_id, (uint32)ad->completed, (uint32)ad->rewarded); if( ad->completed ){ StringBuf_Printf(&buf, "FROM_UNIXTIME('%u'),", (uint32)ad->completed); }else{ StringBuf_AppendStr(&buf, "NULL,"); } if( ad->rewarded ){ StringBuf_Printf(&buf, "FROM_UNIXTIME('%u')", (uint32)ad->rewarded); }else{ StringBuf_AppendStr(&buf, "NULL"); } for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; ++i) StringBuf_Printf(&buf, ", '%d'", ad->count[i]); StringBuf_AppendStr(&buf, ")"); if (SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf))) { Sql_ShowDebug(sql_handle); StringBuf_Destroy(&buf); return false; } StringBuf_Destroy(&buf); return true; }
/** * Save a struct mmo_account in sql. * @param db: pointer to db * @param acc: pointer of mmo_account to save * @param is_new: if it's a new entry or should we update * @return true if successful, false if something has failed */ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) { Sql* sql_handle = db->accounts; SqlStmt* stmt = SqlStmt_Malloc(sql_handle); bool result = false; int i; // try do { if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION") ) { Sql_ShowDebug(sql_handle); break; } if( is_new ) {// insert into account table if( SQL_SUCCESS != SqlStmt_Prepare(stmt, #ifdef VIP_ENABLE "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`, `vip_time`, `old_group` ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", #else "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", #endif db->account_db) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_STRING, (void*)&acc->email, strlen(acc->email)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_INT, (void*)&acc->expiration_time, sizeof(acc->expiration_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->lastlogin, strlen(acc->lastlogin)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change)) #ifdef VIP_ENABLE || SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 17, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group)) #endif || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); break; } } else {// update account table if( SQL_SUCCESS != SqlStmt_Prepare(stmt, #ifdef VIP_ENABLE "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=?, `vip_time`=?, `old_group`=? WHERE `account_id` = '%d'", #else "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=? WHERE `account_id` = '%d'", #endif db->account_db, acc->account_id) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void*)acc->email, strlen(acc->email)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->expiration_time, sizeof(acc->expiration_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_STRING, (void*)&acc->lastlogin, strlen(acc->lastlogin)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change)) #ifdef VIP_ENABLE || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group)) #endif || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); break; } } // remove old account regs if( SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) ) { Sql_ShowDebug(sql_handle); break; } // insert new account regs if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , ? , ? );", db->accreg_db, acc->account_id) ) { SqlStmt_ShowDebug(stmt); break; } for( i = 0; i < acc->account_reg2_num; ++i ) { if( SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->account_reg2[i].str, strlen(acc->account_reg2[i].str)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->account_reg2[i].value, strlen(acc->account_reg2[i].value)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); break; } } if( i < acc->account_reg2_num ) { result = false; break; } // if we got this far, everything was successful result = true; } while(0); // finally result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); SqlStmt_Free(stmt); return result; }
/** * 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 * @param at Autotrader info, or NULL if requetsed not from autotrade persistance * @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 */ int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count, struct s_autotrader *at) { unsigned int i, weight, listidx; char message_sql[MESSAGE_SIZE*2]; StringBuf buf; 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->inventory.u.items_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_table, 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, 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`(`buyingstore_id`,`index`,`item_id`,`amount`,`price`) VALUES", buyingstore_items_table); for (i = 0; i < sd->buyingstore.slots; i++){ StringBuf_Printf(&buf, "(%d,%d,%hu,%d,%d)", sd->buyer_id, i, sd->buyingstore.items[i].nameid, sd->buyingstore.items[i].amount, sd->buyingstore.items[i].price); if (i < sd->buyingstore.slots-1) StringBuf_AppendStr(&buf, ","); } if (SQL_ERROR == Sql_QueryStr(mmysql_handle, StringBuf_Value(&buf))) Sql_ShowDebug(mmysql_handle); StringBuf_Destroy(&buf); clif_buyingstore_myitemlist(sd); clif_buyingstore_entry(sd); idb_put(buyingstore_db, sd->status.char_id, sd); return 0; }
/** * 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; }
/// Stores a single message in the database. /// Returns the message's ID if successful (or 0 if it fails). int mail_savemessage(struct mail_message* msg) { StringBuf buf; SqlStmt* stmt; int i, j; bool found = false; // build message save query StringBuf_Init(&buf); StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`,`type`", schema_config.mail_db); StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d'", msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->type); StringBuf_AppendStr(&buf, ")"); // prepare and execute query stmt = SqlStmt_Malloc(sql_handle); if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, msg->send_name, strnlen(msg->send_name, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, msg->dest_name, strnlen(msg->dest_name, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, msg->title, strnlen(msg->title, MAIL_TITLE_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, msg->body, strnlen(msg->body, MAIL_BODY_LENGTH)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); StringBuf_Destroy(&buf); return msg->id = 0; } else msg->id = (int)SqlStmt_LastInsertId(stmt); SqlStmt_Free(stmt); StringBuf_Clear(&buf); StringBuf_Printf(&buf,"INSERT INTO `%s` (`id`, `index`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`, `bound`", schema_config.mail_attachment_db); for (j = 0; j < MAX_SLOTS; j++) StringBuf_Printf(&buf, ", `card%d`", j); for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) { StringBuf_Printf(&buf, ", `option_id%d`", j); StringBuf_Printf(&buf, ", `option_val%d`", j); StringBuf_Printf(&buf, ", `option_parm%d`", j); } StringBuf_AppendStr(&buf, ") VALUES "); for( i = 0; i < MAIL_MAX_ITEM; i++ ){ // skip empty and already matched entries if( msg->item[i].nameid == 0 ) continue; if( found ){ StringBuf_AppendStr(&buf, ","); }else{ found = true; } StringBuf_Printf(&buf, "('%" PRIu64 "', '%hu', '%d', '%hu', '%d', '%d', '%d', '%" PRIu64 "', '%d'", (uint64)msg->id, i, msg->item[i].amount, msg->item[i].nameid, msg->item[i].refine, msg->item[i].attribute, msg->item[i].identify, msg->item[i].unique_id, msg->item[i].bound); for (j = 0; j < MAX_SLOTS; j++) StringBuf_Printf(&buf, ", '%hu'", msg->item[i].card[j]); for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) { StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].id); StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].value); StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].param); } StringBuf_AppendStr(&buf, ")"); } if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ){ Sql_ShowDebug(sql_handle); } StringBuf_Destroy(&buf); return msg->id; }
/** * 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&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( battle_config.feature_autotrade && 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(725), idb->jname); // Item '%s' has not yet saved well to the cart. Please re-log your character. clif_displaymessage(sd->fd, msg); clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0, 0); return 4; } 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( 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; }
/** * 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; int vending_skill_lvl; char message_sql[MESSAGE_SIZE*2]; StringBuf buf; 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, 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 == 1 // 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 = min(value, (unsigned int)battle_config.vending_max_value); 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->state.workinprogress = WIP_DISABLE_NONE; 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_table, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == SEX_FEMALE ? '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_table); 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; }