瀏覽代碼

Follow up Autotrade Persistency, also as fix of bugreport:8572 http://rathena.org/board/tracker/issue-8572-vendingc/

Signed-off-by: Cydh Ramdh <house.bad@gmail.com>
Cydh Ramdh 11 年之前
父節點
當前提交
46ca940692
共有 3 個文件被更改,包括 161 次插入127 次删除
  1. 2 1
      src/map/atcommand.c
  2. 1 1
      src/map/chrif.c
  3. 158 125
      src/map/vending.c

+ 2 - 1
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 );
 	}
 

+ 1 - 1
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();
 }
 

+ 158 - 125
src/map/vending.c

@@ -23,30 +23,36 @@
 #include <stdlib.h> // atoi
 #include <string.h>
 
-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;
-	}
-
-	if( vending->count > 0 ){
-		data = (uint8*)aMalloc( vending->count * 8 );
-
-		for( i = 0, p = data, count = vending->count; i < vending->count; i++ ){
-			entry = vending->entries[i];
+	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) { //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);
 
-			index = (uint16*)(p + 0);
-			amount = (uint16*)(p + 2);
-			value = (uint32*)(p + 4);
+		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);
 
-			ARR_FIND( 0, MAX_CART, entry->index, sd->status.cart[entry->index].id == entry->cartinventory_id );
+			// Find item position in cart
+			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;
-
-		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 ) {
+/**
+* Initializing autotraders from table
+*/
+void do_init_vending_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` "
+			"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 ) );
-
-			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 ) );
+			CREATE(autotraders[i], struct s_autotrade, 1);
 
-			vending->count = 0;
+			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;
 
-			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);
+
+		if (autotraders == NULL) { //This is shouldn't happen [Cydh]
+			ShowError("Failed to initialize autotraders!\n");
+			do_final_vending_autotrade();
+			return;
+		}
 
-		Sql_FreeResult( mmysql_handle );
+		//Init items on vending list each autotrader
+		for (i = 0; i < autotrader_count; i++){
+			struct s_autotrade *at = NULL;
+			uint16 j;
 
-		for( i = 0; i < count; i++ ){
-			vending = autotraders[i];
+			if (autotraders[i] == NULL)
+				continue;
+			at = autotraders[i];
 
-			if( SQL_ERROR == Sql_Query(mmysql_handle, 
-				"SELECT `cartinventory_id`, `amount`, `price`"
-				"FROM `%s`"
+			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;
+			
+			//Init the list
+			CREATE(at->entries, struct s_autotrade_entry *,at->count);
 
-			while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ) {
+			//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);
 
-				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);
-
+				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;
 }