diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 13402fa61b..085a335a8f 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -5656,7 +5656,8 @@ ACMD_FUNC(autotrade) { 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_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ) + { Sql_ShowDebug( mmysql_handle ); } diff --git a/src/map/chrif.c b/src/map/chrif.c index 23cdf9b2ee..c08bd807c5 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -564,7 +564,7 @@ void chrif_on_ready(void) { //Re-save any guild castles that were modified in the disconnection time. guild_castle_reconnect(-1, 0, 0); - // Charserver is ready for this now + // Charserver is ready for laoding autotrader do_init_vending_autotrade(); } diff --git a/src/map/vending.c b/src/map/vending.c index 16dcb4c534..82c34020b4 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -23,30 +23,36 @@ #include // atoi #include -struct vending_entry{ +/// Struct for vending entry of autotrader +struct s_autotrade_entry { int cartinventory_id; - int amount; + uint16 amount; int price; - int index; + uint16 index; }; -struct vending{ +/// Struct of autotrader +struct s_autotrade { int account_id; int char_id; int vendor_id; int m; - int x; - int y; + uint16 x, + y; unsigned char sex; char title[MESSAGE_SIZE]; - uint32 count; - struct vending_entry** entries; + uint16 count; + struct s_autotrade_entry **entries; struct map_session_data *sd; }; static int vending_nextid = 0; ///Vending_id counter static DBMap *vending_db; ///Db holder the vender : charid -> map_session_data -static DBMap *autotrade_db; + +//Autotrader +static struct s_autotrade **autotraders; ///Autotraders Storage +static uint16 autotrader_count; ///Autotrader count +static void do_final_vending_autotrade(void); /** * Lookup to get the vending_db outside module @@ -453,40 +459,42 @@ bool vending_searchall(struct map_session_data* sd, const struct s_search_store_ return true; } +/** Open vending for Autotrader +* @param sd Player as autotrader +*/ void vending_reopen( struct map_session_data* sd ){ - int i, count; - uint8 *data, *p; - uint16 *index, *amount; - uint32 *value; - struct vending *vending; - struct vending_entry *entry; - - vending = (struct vending*)idb_get( autotrade_db, sd->status.char_id ); - - if( !vending ){ - map_quit(sd); + if (!sd || !autotrader_count || !autotraders) return; - } + else { // Ready to open vending for this char + uint16 i; + uint8 *data, *p; + uint16 j, count; - if( vending->count > 0 ){ - data = (uint8*)aMalloc( vending->count * 8 ); + 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); + return; + } + + // Init vending data for autotrader + CREATE(data, uint8, autotraders[i]->count * 8); - for( i = 0, p = data, count = vending->count; i < vending->count; i++ ){ - entry = vending->entries[i]; + for (j = 0, p = data, count = autotraders[i]->count; j < autotraders[i]->count; j++) { + struct s_autotrade_entry *entry = autotraders[i]->entries[j]; + uint16 *index = (uint16*)(p + 0); + uint16 *amount = (uint16*)(p + 2); + uint32 *value = (uint32*)(p + 4); - index = (uint16*)(p + 0); - amount = (uint16*)(p + 2); - value = (uint32*)(p + 4); + // Find item position in cart + ARR_FIND(0, MAX_CART, entry->index, sd->status.cart[entry->index].id == entry->cartinventory_id); - ARR_FIND( 0, MAX_CART, entry->index, sd->status.cart[entry->index].id == entry->cartinventory_id ); - - if( entry->index == MAX_CART ){ + if (entry->index == MAX_CART) { count--; continue; } *index = entry->index + 2; - *amount = entry->amount; + *amount = min(entry->amount, sd->status.cart[entry->index].amount); // Limit the vending amount *value = entry->price; p += 8; @@ -496,154 +504,180 @@ void vending_reopen( struct map_session_data* sd ){ sd->state.prevend = 1; // Open the shop again - vending_openvending( sd, vending->title, data, count ); + 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", + 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;", vendings_db, sd->vender_id ) != SQL_SUCCESS ){ + if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", + vendings_db, sd->vender_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 ){ + if( battle_config.feature_autotrade_sit ) pc_setsit(sd); - } - } - aFree(data); - - idb_remove( autotrade_db, sd->status.char_id ); - - for( i = 0; i < vending->count; i++ ){ - aFree( vending->entries[i] ); - } - - aFree(vending->entries); - aFree(vending); - - if( !count ){ - map_quit(sd); + //If the last autotrade is loaded, clear autotraders [Cydh] + if (i+1 >= autotrader_count) + do_final_vending_autotrade(); } } -void do_init_vending_autotrade( void ){ - if( battle_config.feature_autotrade ){ - struct vending **autotraders; - struct vending *vending; - struct vending_entry *entry; - uint32 count; - int i, j; +/** +* Initializing autotraders from table +*/ +void do_init_vending_autotrade( void ) { + if (battle_config.feature_autotrade) { + uint16 i, items = 0; + autotrader_count = 0; - if( Sql_Query(mmysql_handle, - "SELECT `id`, `account_id`, `char_id`, `sex`, `map`, `x`, `y`, `title`" - "FROM `%s`" - "WHERE `autotrade` = 1;", vendings_db ) != SQL_SUCCESS ) { + // 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` " + "FROM `%s` " + "WHERE `autotrade` = 1 AND (SELECT COUNT(`vending_id`) FROM `%s` WHERE `vending_id` = `id`) > 0;", + vendings_db, vending_items_db ) != SQL_SUCCESS ) + { Sql_ShowDebug(mmysql_handle); return; } - count = (uint32)Sql_NumRows(mmysql_handle); - - if( count <= 0 ){ + if (!(autotrader_count = (uint32)Sql_NumRows(mmysql_handle))) //Nothing to do return; - } + + // Init autotraders + CREATE(autotraders, struct s_autotrade *, autotrader_count); - autotraders = (struct vending**)aMalloc( sizeof( struct vending* ) * count ); + // Init each autotrader data i = 0; - - while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ) { + while (SQL_SUCCESS == Sql_NextRow(mmysql_handle) && i < autotrader_count) { size_t len; char* data; - vending = autotraders[i] = (struct vending *)aMalloc( sizeof( struct vending ) ); + CREATE(autotraders[i], struct s_autotrade, 1); - Sql_GetData( mmysql_handle, 0, &data, NULL ); vending->vendor_id = atoi(data); - Sql_GetData( mmysql_handle, 1, &data, NULL ); vending->account_id = atoi(data); - Sql_GetData( mmysql_handle, 2, &data, NULL ); vending->char_id = atoi(data); - Sql_GetData( mmysql_handle, 3, &data, NULL ); vending->sex = data[0]; - Sql_GetData( mmysql_handle, 4, &data, NULL ); vending->m = map_mapname2mapid( data ); - Sql_GetData( mmysql_handle, 5, &data, NULL ); vending->x = atoi(data); - Sql_GetData( mmysql_handle, 6, &data, NULL ); vending->y = atoi(data); - Sql_GetData( mmysql_handle, 7, &data, &len ); safestrncpy( vending->title, data, min( len + 1, MESSAGE_SIZE ) ); + Sql_GetData(mmysql_handle, 0, &data, NULL); autotraders[i]->vendor_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)); + autotraders[i]->count = 0; - vending->count = 0; - - idb_put( autotrade_db, vending->char_id, vending ); - // initialize player - CREATE(vending->sd, TBL_PC, 1); - - pc_setnewpc( vending->sd, vending->account_id, vending->char_id, 0, gettick(), vending->sex == 'F' ? 0 : 1, 0 ); - - vending->sd->state.autotrade = 1; - - chrif_authreq( vending->sd, true ); - + 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); - Sql_FreeResult( mmysql_handle ); + if (autotraders == NULL) { //This is shouldn't happen [Cydh] + ShowError("Failed to initialize autotraders!\n"); + do_final_vending_autotrade(); + return; + } - for( i = 0; i < count; i++ ){ - vending = autotraders[i]; + //Init items on vending list each autotrader + for (i = 0; i < autotrader_count; i++){ + struct s_autotrade *at = NULL; + uint16 j; - if( SQL_ERROR == Sql_Query(mmysql_handle, - "SELECT `cartinventory_id`, `amount`, `price`" - "FROM `%s`" + if (autotraders[i] == NULL) + continue; + at = autotraders[i]; + + if (SQL_ERROR == Sql_Query(mmysql_handle, + "SELECT `cartinventory_id`, `amount`, `price` " + "FROM `%s` " "WHERE `vending_id` = %d " - "ORDER BY `index` ASC;", vending_items_db, vending->vendor_id ) ) { + "ORDER BY `index` ASC;", vending_items_db, at->vendor_id ) ) + { Sql_ShowDebug(mmysql_handle); continue; } - vending->count = (uint32)Sql_NumRows(mmysql_handle); - - if( vending->count <= 0 ){ - // Player was not correctly deleted, must not be set online - idb_remove( autotrade_db, vending->char_id ); - map_quit(vending->sd); - aFree(vending); + if (!(at->count = (uint32)Sql_NumRows(mmysql_handle))) { + map_quit(at->sd); continue; } - - vending->entries = (struct vending_entry**)aMalloc( sizeof( struct vending_entry* ) * vending->count ); - j = 0; - - while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ) { - char* data; - - entry = vending->entries[j] = (struct vending_entry*)aMalloc( sizeof( struct vending_entry ) ); - Sql_GetData( mmysql_handle, 0, &data, NULL ); entry->cartinventory_id = atoi(data); - Sql_GetData( mmysql_handle, 1, &data, NULL ); entry->amount = atoi(data); - Sql_GetData( mmysql_handle, 2, &data, NULL ); entry->price = atoi(data); + //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]->cartinventory_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++; } - - Sql_FreeResult( mmysql_handle ); + items += j; + Sql_FreeResult(mmysql_handle); } - aFree(autotraders); - - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' autotraders.\n",count); + 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`;", vendings_db ) != SQL_SUCCESS || - Sql_Query( mmysql_handle, "DELETE FROM `%s`;", vending_items_db ) != SQL_SUCCESS ){ + if (Sql_Query( mmysql_handle, "DELETE FROM `%s`;", vendings_db ) != SQL_SUCCESS || + Sql_Query( mmysql_handle, "DELETE FROM `%s`;", vending_items_db ) != SQL_SUCCESS) + { Sql_ShowDebug(mmysql_handle); } } +/** +* Clear all autotraders +* @author [Cydh] +*/ +void do_final_vending_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; + } +} + /** * Initialise the vending module * called in map::do_init */ void do_final_vending(void) { db_destroy(vending_db); - db_destroy(autotrade_db); + do_final_vending_autotrade(); //Make sure everything is cleared [Cydh] } /** @@ -652,6 +686,5 @@ void do_final_vending(void) { */ void do_init_vending(void) { vending_db = idb_alloc(DB_OPT_BASE); - autotrade_db = idb_alloc(DB_OPT_BASE); vending_nextid = 0; }