瀏覽代碼

Buyingstore persistency

Basic features of @autotrade persistency now also available for buyingstores.
Lemongrass3110 11 年之前
父節點
當前提交
b3506fb8f5
共有 9 個文件被更改,包括 313 次插入10 次删除
  1. 2 0
      conf/inter_athena.conf
  2. 22 0
      sql-files/main.sql
  3. 8 5
      src/map/atcommand.c
  4. 264 0
      src/map/buyingstore.c
  5. 3 0
      src/map/buyingstore.h
  6. 2 0
      src/map/chrif.c
  7. 7 1
      src/map/map.c
  8. 2 0
      src/map/map.h
  9. 3 4
      src/map/vending.c

+ 2 - 0
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

+ 22 - 0
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;

+ 8 - 5
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 ) {

+ 264 - 0
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;
+	}
+}

+ 3 - 0
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_

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

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

+ 2 - 0
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];

+ 3 - 4
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