From b3506fb8f542ff46850aa2c73eedd8c1e657c7a4 Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Wed, 5 Feb 2014 09:16:21 +0100 Subject: [PATCH] Buyingstore persistency Basic features of @autotrade persistency now also available for buyingstores. --- conf/inter_athena.conf | 2 + sql-files/main.sql | 22 ++++ src/map/atcommand.c | 13 +- src/map/buyingstore.c | 264 +++++++++++++++++++++++++++++++++++++++++ src/map/buyingstore.h | 3 + src/map/chrif.c | 2 + src/map/map.c | 8 +- src/map/map.h | 2 + src/map/vending.c | 7 +- 9 files changed, 313 insertions(+), 10 deletions(-) diff --git a/conf/inter_athena.conf b/conf/inter_athena.conf index d5d5eee092..5c80f7c61e 100644 --- a/conf/inter_athena.conf +++ b/conf/inter_athena.conf @@ -107,6 +107,8 @@ skillcooldown_db: skillcooldown bonus_script_db: bonus_script // Map Database Tables +buyingstore_db: buyingstores +buyingstore_items_db: buyingstore_items item_db_db: item_db item_db_re_db: item_db_re item_db2_db: item_db2 diff --git a/sql-files/main.sql b/sql-files/main.sql index 6e14ae872e..3a5e79e513 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -733,3 +733,25 @@ CREATE TABLE IF NOT EXISTS `vendings` ( `autotrade` tinyint(4) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `buyingstore_items` ( + `buyingstore_id` int(10) unsigned NOT NULL, + `index` smallint(5) unsigned NOT NULL, + `item_id` int(10) unsigned NOT NULL, + `amount` smallint(5) unsigned NOT NULL, + `price` int(10) unsigned NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `buyingstores` ( + `id` int(10) unsigned NOT NULL, + `account_id` int(11) unsigned NOT NULL, + `char_id` int(10) unsigned NOT NULL, + `sex` enum('F','M') NOT NULL DEFAULT 'M', + `map` varchar(20) NOT NULL, + `x` smallint(5) unsigned NOT NULL, + `y` smallint(5) unsigned NOT NULL, + `title` varchar(80) NOT NULL, + `limit` int(10) unsigned NOT NULL, + `autotrade` tinyint(4) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 085a335a8f..aac20cb15a 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -5654,11 +5654,14 @@ ACMD_FUNC(autotrade) { sd->state.autotrade = 1; - if( battle_config.feature_autotrade && - sd->state.vending && - Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ) - { - Sql_ShowDebug( mmysql_handle ); + if( sd->state.vending ){ + if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ){ + Sql_ShowDebug( mmysql_handle ); + } + }else if( sd->state.buyingstore ){ + if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", buyingstore_db, sd->buyer_id ) != SQL_SUCCESS ){ + Sql_ShowDebug( mmysql_handle ); + } } if( battle_config.at_timeout ) { diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index a73159ceb3..a9d70c7b05 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -3,6 +3,7 @@ #include "../common/cbasetypes.h" #include "../common/db.h" // ARR_FIND +#include "../common/malloc.h" // aMalloc, aFree #include "../common/showmsg.h" // ShowWarning #include "../common/socket.h" // RBUF* #include "../common/strlib.h" // safestrncpy @@ -14,6 +15,32 @@ #include "pc.h" // struct map_session_data #include "chrif.h" +/// Struct for buyingstore entry of autotrader +struct s_autotrade_entry { + uint16 amount; + int price; + uint16 item_id; +}; + +/// Struct of autotrader +struct s_autotrade { + int account_id; + int char_id; + int buyer_id; + int m; + uint16 x, y; + unsigned char sex; + char title[MESSAGE_SIZE]; + int limit; + uint16 count; + struct s_autotrade_entry **entries; + struct map_session_data *sd; +}; + +//Autotrader +static struct s_autotrade **autotraders; ///Autotraders Storage +static uint16 autotrader_count; ///Autotrader count +static void do_final_buyingstore_autotrade(void); /// constants (client-side restrictions) #define BUYINGSTORE_MAX_PRICE 99990000 @@ -83,6 +110,7 @@ bool buyingstore_setup(struct map_session_data* sd, unsigned char slots){ void 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]; if( !result || count == 0 ) {// canceled, or no items @@ -190,6 +218,19 @@ void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha 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`) VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d, %d );", buyingstore_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 ) != 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, %d, %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); } @@ -199,6 +240,13 @@ void buyingstore_close(struct map_session_data* sd) { if( sd->state.buyingstore ) { + if( + !sd->state.autotrade && + Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE buyingstore_id = %d;", buyingstore_items_db, sd->buyer_id ) != SQL_SUCCESS || + Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `id` = %d;", buyingstore_db, sd->buyer_id ) != SQL_SUCCESS ){ + Sql_ShowDebug(mmysql_handle); + } + // invalidate data sd->state.buyingstore = false; memset(&sd->buyingstore, 0, sizeof(sd->buyingstore)); @@ -370,6 +418,16 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int pc_delitem(sd, index, amount, 1, 0, LOG_TYPE_BUYING_STORE); pl_sd->buyingstore.items[listidx].amount-= amount; + if( pl_sd->buyingstore.items[listidx].amount > 0 ){ + if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_db, pl_sd->buyingstore.items[listidx].amount, pl_sd->buyer_id, listidx ) != SQL_SUCCESS ){ + Sql_ShowDebug( mmysql_handle ); + } + }else{ + if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_db, pl_sd->buyer_id, listidx ) != SQL_SUCCESS ){ + Sql_ShowDebug( mmysql_handle ); + } + } + // pay up pc_payzeny(pl_sd, zeny, LOG_TYPE_BUYING_STORE, sd); pc_getzeny(sd, zeny, LOG_TYPE_BUYING_STORE, pl_sd); @@ -397,6 +455,10 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int } else {// continue buying + if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `limit` = %d WHERE `id` = %d;", buyingstore_db, pl_sd->buyingstore.zenylimit, pl_sd->buyer_id ) != SQL_SUCCESS ){ + Sql_ShowDebug( mmysql_handle ); + } + return; } @@ -475,3 +537,205 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st return true; } + +/** Open vending for Autotrader +* @param sd Player as autotrader +*/ +void buyingstore_reopen( struct map_session_data* sd ){ + if (!sd || !autotrader_count || !autotraders) + return; + else { // Ready to open vending for this char + uint16 i; + uint8 *data, *p; + uint16 j, count; + + ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id); + if (i >= autotrader_count) { + return; + } + + // Init vending data for autotrader + CREATE(data, uint8, autotraders[i]->count * 8); + + for (j = 0, p = data, count = autotraders[i]->count; j < autotraders[i]->count; j++) { + struct s_autotrade_entry *entry = autotraders[i]->entries[j]; + uint16 *item_id = (uint16*)(p + 0); + uint16 *amount = (uint16*)(p + 2); + uint32 *price = (uint32*)(p + 4); + + *item_id = entry->item_id; + *amount = entry->amount; + *price = entry->price; + + p += 8; + } + + // Open the shop again + buyingstore_setup( sd, (unsigned char)autotraders[i]->count ); + buyingstore_create( sd, autotraders[i]->limit, 1, autotraders[i]->title, data, autotraders[i]->count ); + aFree(data); + + ShowInfo("Loaded autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n", + sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y); + + // Set him to autotrade + if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", + buyingstore_db, sd->buyer_id ) != SQL_SUCCESS ) + { + Sql_ShowDebug( mmysql_handle ); + } + + // Make him look perfect + unit_setdir(&sd->bl,battle_config.feature_autotrade_direction); + + if( battle_config.feature_autotrade_sit ) + pc_setsit(sd); + + //If the last autotrade is loaded, clear autotraders [Cydh] + if (i+1 >= autotrader_count) + do_final_buyingstore_autotrade(); + } +} + +/** +* Initializing autotraders from table +*/ +void do_init_buyingstore_autotrade( void ) { + if(battle_config.feature_autotrade) { + uint16 i, items = 0; + autotrader_count = 0; + + // Get autotrader from table. `map`, `x`, and `y`, aren't used here + // Just read player that has data at vending_items [Cydh] + if (Sql_Query(mmysql_handle, + "SELECT `id`, `account_id`, `char_id`, `sex`, `title`, `limit` " + "FROM `%s` " + "WHERE `autotrade` = 1 AND (SELECT COUNT(`buyingstore_id`) FROM `%s` WHERE `buyingstore_id` = `id`) > 0;", + buyingstore_db, buyingstore_items_db ) != SQL_SUCCESS ) + { + Sql_ShowDebug(mmysql_handle); + return; + } + + if (!(autotrader_count = (uint32)Sql_NumRows(mmysql_handle))) //Nothing to do + return; + + // Init autotraders + CREATE(autotraders, struct s_autotrade *, autotrader_count); + + // Init each autotrader data + i = 0; + while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && i < autotrader_count) { + size_t len; + char* data; + + CREATE(autotraders[i], struct s_autotrade, 1); + + Sql_GetData(mmysql_handle, 0, &data, NULL); autotraders[i]->buyer_id = atoi(data); + Sql_GetData(mmysql_handle, 1, &data, NULL); autotraders[i]->account_id = atoi(data); + Sql_GetData(mmysql_handle, 2, &data, NULL); autotraders[i]->char_id = atoi(data); + Sql_GetData(mmysql_handle, 3, &data, NULL); autotraders[i]->sex = (data[0] == 'F') ? 0 : 1; + Sql_GetData(mmysql_handle, 4, &data, &len); safestrncpy(autotraders[i]->title, data, min(len + 1, MESSAGE_SIZE)); + Sql_GetData(mmysql_handle, 5, &data, NULL); autotraders[i]->limit = atoi(data); + autotraders[i]->count = 0; + + // initialize player + CREATE(autotraders[i]->sd, struct map_session_data, 1); + + pc_setnewpc(autotraders[i]->sd, autotraders[i]->account_id, autotraders[i]->char_id, 0, gettick(), autotraders[i]->sex, 0); + + autotraders[i]->sd->state.autotrade = 1; + chrif_authreq(autotraders[i]->sd, true); + i++; + } + Sql_FreeResult(mmysql_handle); + + if (autotraders == NULL) { //This is shouldn't happen [Cydh] + ShowError("Failed to initialize autotraders!\n"); + do_final_buyingstore_autotrade(); + return; + } + + //Init items on vending list each autotrader + for (i = 0; i < autotrader_count; i++){ + struct s_autotrade *at = NULL; + uint16 j; + + if (autotraders[i] == NULL) + continue; + at = autotraders[i]; + + if (SQL_ERROR == Sql_Query(mmysql_handle, + "SELECT `item_id`, `amount`, `price` " + "FROM `%s` " + "WHERE `buyingstore_id` = %d " + "ORDER BY `index` ASC;", buyingstore_items_db, at->buyer_id ) ) + { + Sql_ShowDebug(mmysql_handle); + continue; + } + + if (!(at->count = (uint32)Sql_NumRows(mmysql_handle))) { + map_quit(at->sd); + continue; + } + + //Init the list + CREATE(at->entries, struct s_autotrade_entry *,at->count); + + //Add the item into list + j = 0; + while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && j < at->count) { + char* data; + CREATE(at->entries[j], struct s_autotrade_entry, 1); + + Sql_GetData(mmysql_handle, 0, &data, NULL); at->entries[j]->item_id = atoi(data); + Sql_GetData(mmysql_handle, 1, &data, NULL); at->entries[j]->amount = atoi(data); + Sql_GetData(mmysql_handle, 2, &data, NULL); at->entries[j]->price = atoi(data); + j++; + } + items += j; + Sql_FreeResult(mmysql_handle); + } + + ShowStatus("Done loading '"CL_WHITE"%d"CL_RESET"' autotraders with '"CL_WHITE"%d"CL_RESET"' items.\n", autotrader_count, items); + } + + // Everything is loaded fine, their entries will be reinserted once they are loaded + if (Sql_Query( mmysql_handle, "DELETE FROM `%s`;", buyingstore_db ) != SQL_SUCCESS || + Sql_Query( mmysql_handle, "DELETE FROM `%s`;", buyingstore_items_db ) != SQL_SUCCESS) + { + Sql_ShowDebug(mmysql_handle); + } +} + +/** +* Clear all autotraders +* @author [Cydh] +*/ +void do_final_buyingstore_autotrade(void) { + if (!autotrader_count || !autotraders) + return; + else { + uint16 i = 0; + while (i < autotrader_count) { //Free the autotrader + if (autotraders[i] == NULL) + continue; + if (autotraders[i]->count) { + uint16 j = 0; + while (j < autotraders[i]->count) { //Free the autotrade entries + if (autotraders[i]->entries == NULL) + continue; + if (autotraders[i]->entries[j]) + aFree(autotraders[i]->entries[j]); + j++; + } + aFree(autotraders[i]->entries); + } + aFree(autotraders[i]); + i++; + } + aFree(autotraders); + autotrader_count = 0; + } +} diff --git a/src/map/buyingstore.h b/src/map/buyingstore.h index 0ed6e54578..d995548e27 100644 --- a/src/map/buyingstore.h +++ b/src/map/buyingstore.h @@ -30,4 +30,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int bool buyingstore_search(struct map_session_data* sd, unsigned short nameid); bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s); +void do_init_buyingstore_autotrade( void ); +void buyingstore_reopen( struct map_session_data* sd ); + #endif // _BUYINGSTORE_H_ diff --git a/src/map/chrif.c b/src/map/chrif.c index f0942b6904..aa55dddea1 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -565,6 +565,7 @@ void chrif_on_ready(void) { guild_castle_reconnect(-1, 0, 0); // Charserver is ready for loading autotrader + do_init_buyingstore_autotrade(); do_init_vending_autotrade(); } @@ -1377,6 +1378,7 @@ int chrif_load_scdata(int fd) { #endif if( sd->state.autotrade ){ + buyingstore_reopen( sd ); vending_reopen( sd ); } diff --git a/src/map/map.c b/src/map/map.c index b108850a94..4ecb256407 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -70,6 +70,8 @@ char map_server_db[32] = "ragnarok"; Sql* mmysql_handle; int db_use_sqldbs = 0; +char buyingstore_db[32] = "buyingstores"; +char buyingstore_items_db[32] = "buyingstore_items"; char item_db_db[32] = "item_db"; char item_db2_db[32] = "item_db2"; char item_db_re_db[32] = "item_db_re"; @@ -3535,7 +3537,11 @@ int inter_config_read(char *cfgName) if( sscanf(line,"%1023[^:]: %1023[^\r\n]",w1,w2) < 2 ) continue; - if(strcmpi(w1,"item_db_db")==0) + if( strcmpi( w1, "buyingstore_db" ) == 0 ) + strcpy( buyingstore_db, w2 ); + else if( strcmpi( w1, "buyingstore_items_db" ) == 0 ) + strcpy( buyingstore_items_db, w2 ); + else if(strcmpi(w1,"item_db_db")==0) strcpy(item_db_db,w2); else if(strcmpi(w1,"item_db2_db")==0) strcpy(item_db2_db,w2); diff --git a/src/map/map.h b/src/map/map.h index 0adff11567..ce94e1c791 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -901,6 +901,8 @@ extern int db_use_sqldbs; extern Sql* mmysql_handle; extern Sql* logmysql_handle; +extern char buyingstore_db[32]; +extern char buyingstore_items_db[32]; extern char item_db_db[32]; extern char item_db2_db[32]; extern char item_db_re_db[32]; diff --git a/src/map/vending.c b/src/map/vending.c index 82c34020b4..a906fddf4f 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -471,8 +471,7 @@ void vending_reopen( struct map_session_data* sd ){ uint16 j, count; ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id); - if (i >= autotrader_count) { //This is shouldn't happen [Cydh] - ShowError("vending_reopen: Player not found as autotrader (aid:%d cid:%d)\n",sd->status.account_id,sd->status.char_id); + if (i >= autotrader_count) { return; } @@ -494,7 +493,7 @@ void vending_reopen( struct map_session_data* sd ){ } *index = entry->index + 2; - *amount = min(entry->amount, sd->status.cart[entry->index].amount); // Limit the vending amount + *amount = entry->amount; *value = entry->price; p += 8; @@ -507,7 +506,7 @@ void vending_reopen( struct map_session_data* sd ){ vending_openvending(sd, autotraders[i]->title, data, count); aFree(data); - ShowInfo("Autotrader loaded: '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n", + ShowInfo("Loaded autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n", sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y); // Set him to autotrade