Browse Source

Autotrade persistency

Lemongrass3110 11 years ago
parent
commit
07b9fe761f
14 changed files with 316 additions and 9 deletions
  1. 11 0
      conf/battle/feature.conf
  2. 2 0
      conf/inter_athena.conf
  3. 23 3
      src/char/char.c
  4. 7 0
      src/map/atcommand.c
  5. 3 0
      src/map/battle.c
  6. 5 0
      src/map/battle.h
  7. 12 3
      src/map/chrif.c
  8. 1 1
      src/map/chrif.h
  9. 1 1
      src/map/clif.c
  10. 6 0
      src/map/map.c
  11. 2 0
      src/map/map.h
  12. 4 0
      src/map/pc.c
  13. 236 0
      src/map/vending.c
  14. 3 1
      src/map/vending.h

+ 11 - 0
conf/battle/feature.conf

@@ -22,3 +22,14 @@ feature.atcommand_suggestions: off
 // Banking (Note 1)
 // Requires: 2013-07-24aRagexe or later
 feature.banking: on
+
+// Autotrade persistency (Note 1)
+feature.autotrade: on
+
+// In which direction should respawned autotraders look?
+// Possible values are from 0-7
+// Default: 4(South)
+feature.autotrade_direction: 4
+
+// Do you want your autotraders to sit? (Note 1)
+feature.autotrade_sit: yes

+ 2 - 0
conf/inter_athena.conf

@@ -122,6 +122,8 @@ mob_skill_db_re_db: mob_skill_db_re
 mob_skill_db2_db: mob_skill_db2
 //mob_skill_db2_db: mob_skill_db2_re
 mapreg_db: mapreg
+vending_db: vendings
+vending_items_db: vending_items
 
 // Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
 use_sql_db: no

+ 23 - 3
src/char/char.c

@@ -3779,7 +3779,7 @@ int parse_frommap(int fd)
 		break;
 
 		case 0x2b26: // auth request from map-server
-			if (RFIFOREST(fd) < 19)
+			if (RFIFOREST(fd) < 20)
 				return 0;
 
 		{
@@ -3791,13 +3791,15 @@ int parse_frommap(int fd)
 			struct auth_node* node;
 			struct mmo_charstatus* cd;
 			struct mmo_charstatus char_dat;
+			bool autotrade = false;
 
 			account_id = RFIFOL(fd,2);
 			char_id    = RFIFOL(fd,6);
 			login_id1  = RFIFOL(fd,10);
 			sex        = RFIFOB(fd,14);
 			ip         = ntohl(RFIFOL(fd,15));
-			RFIFOSKIP(fd,19);
+			autotrade  = RFIFOB(fd,19);
+			RFIFOSKIP(fd,20);
 
 			node = (struct auth_node*)idb_get(auth_db, account_id);
 			cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
@@ -3806,7 +3808,25 @@ int parse_frommap(int fd)
 				mmo_char_fromsql(char_id, &char_dat, true);
 				cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
 			}
-			if( runflag == CHARSERVER_ST_RUNNING &&
+
+			if( runflag == CHARSERVER_ST_RUNNING && autotrade && cd ){
+				uint32 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25;
+				cd->sex = sex;
+
+				WFIFOHEAD(fd,mmo_charstatus_len);
+				WFIFOW(fd,0) = 0x2afd;
+				WFIFOW(fd,2) = mmo_charstatus_len;
+				WFIFOL(fd,4) = account_id;
+				WFIFOL(fd,8) = 0;
+				WFIFOL(fd,12) = 0;
+				WFIFOL(fd,16) = 0;
+				WFIFOL(fd,20) = 0;
+				WFIFOB(fd,24) = 0;
+				memcpy(WFIFOP(fd,25), cd, sizeof(struct mmo_charstatus));
+				WFIFOSET(fd, WFIFOW(fd,2));
+
+				set_char_online(id, char_id, account_id);
+			}else if( runflag == CHARSERVER_ST_RUNNING &&
 				cd != NULL &&
 				node != NULL &&
 				node->account_id == account_id &&

+ 7 - 0
src/map/atcommand.c

@@ -5653,6 +5653,11 @@ ACMD_FUNC(autotrade) {
 	}
 
 	sd->state.autotrade = 1;
+
+	if( battle_config.feature_autotrade && Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", "vendings", sd->vender_id ) != SQL_SUCCESS ){
+		Sql_ShowDebug( mmysql_handle );
+	}
+
 	if( battle_config.at_timeout ) {
 		int timeout = atoi(message);
 		status_change_start(NULL,&sd->bl, SC_AUTOTRADE, 10000, 0, 0, 0, 0, ((timeout > 0) ? min(timeout,battle_config.at_timeout) : battle_config.at_timeout) * 60000, 0);
@@ -5660,6 +5665,8 @@ ACMD_FUNC(autotrade) {
 
 	channel_pcquit(sd,0xF); //leave all chan
 	clif_authfail_fd(sd->fd, 15);
+	
+	chrif_save(sd,1);
 
 	return 0;
 }

+ 3 - 0
src/map/battle.c

@@ -7339,6 +7339,9 @@ static const struct _battle_data {
 	{ "discount_item_point_shop",			&battle_config.discount_item_point_shop,		0,		0,		3,				},
 	{ "update_enemy_position",				&battle_config.update_enemy_position,			0,		0,		1,				},
 	{ "devotion_rdamage",					&battle_config.devotion_rdamage,				0,		0,		100,			},
+	{ "feature.autotrade",					&battle_config.feature_autotrade,				1,		0,		1,				},
+	{ "feature.autotrade_direction",		&battle_config.feature_autotrade_direction,		4,		0,		7,				},
+	{ "feature.autotrade_sit",				&battle_config.feature_autotrade_sit,			1,		0,		1,				},
 };
 #ifndef STATS_OPT_OUT
 /**

+ 5 - 0
src/map/battle.h

@@ -519,6 +519,11 @@ extern struct Battle_Config
 	int discount_item_point_shop;
 	int update_enemy_position;
 	int devotion_rdamage;
+
+	// autotrade persistency
+	int feature_autotrade;
+	int feature_autotrade_direction;
+	int feature_autotrade_sit;
 } battle_config;
 
 void do_init_battle(void);

+ 12 - 3
src/map/chrif.c

@@ -562,6 +562,9 @@ 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
+	do_init_vending_autotrade();
 }
 
 
@@ -615,7 +618,7 @@ int chrif_skillcooldown_request(int account_id, int char_id) {
 /*==========================================
  * Request auth confirmation
  *------------------------------------------*/
-void chrif_authreq(struct map_session_data *sd) {
+void chrif_authreq(struct map_session_data *sd, bool autotrade) {
 	struct auth_node *node= chrif_search(sd->bl.id);
 
 	if( node != NULL || !chrif_isconnected() ) {
@@ -623,14 +626,15 @@ void chrif_authreq(struct map_session_data *sd) {
 		return;
 	}
 
-	WFIFOHEAD(char_fd,19);
+	WFIFOHEAD(char_fd,20);
 	WFIFOW(char_fd,0) = 0x2b26;
 	WFIFOL(char_fd,2) = sd->status.account_id;
 	WFIFOL(char_fd,6) = sd->status.char_id;
 	WFIFOL(char_fd,10) = sd->login_id1;
 	WFIFOB(char_fd,14) = sd->status.sex;
 	WFIFOL(char_fd,15) = htonl(session[sd->fd]->client_addr);
-	WFIFOSET(char_fd,19);
+	WFIFOB(char_fd,19) = autotrade;
+	WFIFOSET(char_fd,20);
 	chrif_sd_to_auth(sd, ST_LOGIN);
 }
 
@@ -1364,6 +1368,11 @@ int chrif_load_scdata(int fd) {
 		status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 1|2|4|8);
 	}
 #endif
+
+	if( sd->state.autotrade ){
+		vending_reopen( sd );
+	}
+
 	return 0;
 }
 

+ 1 - 1
src/map/chrif.h

@@ -35,7 +35,7 @@ struct auth_node* chrif_auth_check(int account_id, int char_id, enum sd_state st
 bool chrif_auth_delete(int account_id, int char_id, enum sd_state state);
 bool chrif_auth_finished(struct map_session_data* sd);
 
-void chrif_authreq(struct map_session_data* sd);
+void chrif_authreq(struct map_session_data* sd, bool autotrade);
 void chrif_authok(int fd);
 int chrif_scdata_request(int account_id, int char_id);
 int chrif_skillcooldown_request(int account_id, int char_id);

+ 1 - 1
src/map/clif.c

@@ -9436,7 +9436,7 @@ void clif_parse_WantToConnection(int fd, struct map_session_data* sd)
 	WFIFOSET(fd,packet_len(0x283));
 #endif
 
-	chrif_authreq(sd);
+	chrif_authreq(sd,false);
 }
 
 

+ 6 - 0
src/map/map.c

@@ -81,6 +81,8 @@ char mob_db2_db[32] = "mob_db2";
 char mob_skill_db_db[32] = "mob_skill_db";
 char mob_skill_db_re_db[32] = "mob_skill_db_re";
 char mob_skill_db2_db[32] = "mob_skill_db2";
+char vendings_db[32] = "vendings";
+char vending_items_db[32] = "vending_items";
 
 // log database
 char log_db_ip[32] = "127.0.0.1";
@@ -3555,6 +3557,10 @@ int inter_config_read(char *cfgName)
 			strcpy( item_cash_db_db, w2 );
 		else if( strcmpi( w1, "item_cash_db2_db" ) == 0 )
 			strcpy( item_cash_db2_db, w2 );
+		else if( strcmpi( w1, "vending_db" ) == 0 )
+			strcpy( vendings_db, w2 );
+		else if( strcmpi( w1, "vending_items_db" ) == 0 )
+			strcpy( vending_items_db, w2 );
 		else
 		//Map Server SQL DB
 		if(strcmpi(w1,"map_server_ip")==0)

+ 2 - 0
src/map/map.h

@@ -909,6 +909,8 @@ extern char mob_db2_db[32];
 extern char mob_skill_db_db[32];
 extern char mob_skill_db_re_db[32];
 extern char mob_skill_db2_db[32];
+extern char vendings_db[32];
+extern char vending_items_db[32];
 
 void do_shutdown(void);
 

+ 4 - 0
src/map/pc.c

@@ -1328,6 +1328,10 @@ int pc_reg_received(struct map_session_data *sd)
 
 		clif_changeoption( &sd->bl );
 	}
+	
+	if( sd->state.autotrade ){
+		clif_parse_LoadEndAck(sd->fd, sd);
+	}
 
 	return 1;
 }

+ 236 - 0
src/map/vending.c

@@ -2,6 +2,8 @@
 // For more information, see LICENCE in the main folder
 
 #include "../common/nullpo.h"
+#include "../common/malloc.h" // aMalloc, aFree
+#include "../common/showmsg.h" // ShowInfo
 #include "../common/strlib.h"
 #include "../common/utils.h"
 #include "clif.h"
@@ -18,10 +20,33 @@
 #include "log.h"
 
 #include <stdio.h>
+#include <stdlib.h> // atoi
 #include <string.h>
 
+struct vending_entry{
+	int cartinventory_id;
+	int amount;
+	int price;
+	int index;
+};
+
+struct vending{
+	int account_id;
+	int char_id;
+	int vendor_id;
+	int m;
+	int x;
+	int y;
+	unsigned char sex;
+	char title[MESSAGE_SIZE];
+	uint32 count;
+	struct vending_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;
 
 /**
  * Lookup to get the vending_db outside module
@@ -48,6 +73,11 @@ void vending_closevending(struct map_session_data* sd)
 	nullpo_retv(sd);
 
 	if( sd->state.vending ) {
+		if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE vending_id = %d;", vending_items_db, sd->vender_id ) != SQL_SUCCESS ||
+			Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ){
+				Sql_ShowDebug(mmysql_handle);
+		}
+		
 		sd->state.vending = false;
 		clif_closevendingboard(&sd->bl, 0);
 		idb_remove(vending_db, sd->status.char_id);
@@ -198,6 +228,17 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
 		// vending item
 		pc_additem(sd, &vsd->status.cart[idx], amount, LOG_TYPE_VENDING);
 		vsd->vending[vend_list[i]].amount -= amount;
+
+		if( vsd->vending[vend_list[i]].amount ){
+			if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){
+				Sql_ShowDebug( mmysql_handle );
+			}
+		}else{
+			if( Sql_Query( mmysql_handle, "DELETE FROM `%s` WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_db, vsd->vender_id, vsd->status.cart[idx].id ) != SQL_SUCCESS ){
+				Sql_ShowDebug( mmysql_handle );
+			}
+		}
+
 		pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING);
 		clif_vendingreport(vsd, idx, amount);
 
@@ -253,12 +294,15 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
 void vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
 	int i, j;
 	int vending_skill_lvl;
+	char message_sql[MESSAGE_SIZE*2];
+	
 	nullpo_retv(sd);
 
 	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd))
 		return; // 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);
@@ -310,6 +354,18 @@ void vending_openvending(struct map_session_data* sd, const char* message, const
 	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`) VALUES( %d, %d, %d, '%c', '%s', %d, %d, '%s', %d );", vendings_db, sd->vender_id, sd->status.account_id, sd->status.char_id, sd->status.sex == 2 ? 'F' : 'M', map[sd->bl.m].name, sd->bl.x, sd->bl.y, message_sql, sd->state.autotrade ) != SQL_SUCCESS ){
+		Sql_ShowDebug(mmysql_handle);
+	}
+
+	for( i = 0; i < count; i++ ){
+		if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`vending_id`,`index`,`cartinventory_id`,`amount`,`price`) VALUES( %d, %d, %d, %d, %d );", vending_items_db, sd->vender_id, i, sd->status.cart[sd->vending[i].index].id, sd->vending[i].amount, sd->vending[i].value ) != SQL_SUCCESS ){
+			Sql_ShowDebug(mmysql_handle);
+		}
+	}
 
 	clif_openvending(sd,sd->bl.id,sd->vending);
 	clif_showvendingboard(&sd->bl,message,0);
@@ -397,12 +453,191 @@ bool vending_searchall(struct map_session_data* sd, const struct s_search_store_
 	return true;
 }
 
+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);
+		return;
+	}
+
+	if( vending->count <= 0 ){
+		idb_remove( autotrade_db, sd->status.char_id );
+		map_quit(sd);
+		return;
+	}
+
+	data = (uint8*)aMalloc( vending->count * 8 );
+
+	for( i = 0, p = data, count = vending->count; i < vending->count; i++ ){
+		entry = &vending->entries[i];
+
+		index = (uint16*)(p + 0);
+		amount = (uint16*)(p + 2);
+		value = (uint32*)(p + 4);
+
+		ARR_FIND( 0, MAX_CART, entry->index, sd->status.cart[entry->index].id == entry->cartinventory_id );
+
+		if( entry->index == MAX_CART ){
+			count--;
+			continue;
+		}
+
+		*index = entry->index + 2;
+		*amount = entry->amount;
+		*value = entry->price;
+
+		p += 8;
+	}
+
+	if( !count ){
+		map_quit(sd);
+		return;
+	}
+
+	vending->count = count;
+
+	// Set him into a hacked prevend state
+	sd->state.prevend = 1;
+
+	// Open the shop again
+	vending_openvending( sd, vending->title, data, vending->count );
+
+	// Make him look perfect
+	unit_setdir(&sd->bl,battle_config.feature_autotrade_direction);
+
+	if( battle_config.feature_autotrade_sit ){
+		pc_setsit(sd);
+	}
+
+	idb_remove( autotrade_db, sd->status.char_id );
+
+	aFree(data);
+	aFree(vending->entries);
+	aFree(vending);
+}
+
+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 ) {
+			Sql_ShowDebug(mmysql_handle);
+			return;
+		}
+
+		count = (uint32)Sql_NumRows(mmysql_handle);
+
+		if( count <= 0 ){
+			return;
+		}
+
+		autotraders = (struct vending*)aMalloc( sizeof( struct vending ) * count );
+		i = 0;
+
+		while( SQL_SUCCESS == Sql_NextRow(mmysql_handle) ) {
+			size_t len;
+			char* data;
+
+			vending = &autotraders[i];
+
+			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 ) );
+
+			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, 0 );
+
+			vending->sd->state.autotrade = 1;
+
+			chrif_authreq( vending->sd, true );
+
+			i++;
+		}
+
+		Sql_FreeResult( mmysql_handle );
+
+		for( i = 0; i < count; i++ ){
+			vending = &autotraders[i];
+
+			if( SQL_ERROR == Sql_Query(mmysql_handle, 
+				"SELECT `cartinventory_id`, `amount`, `price`"
+				"FROM `%s`"
+				"WHERE `vending_id` = %d;", vending_items_db, vending->vendor_id ) ) {
+				Sql_ShowDebug(mmysql_handle);
+				return;
+			}
+
+			vending->count = (uint32)Sql_NumRows(mmysql_handle);
+
+			if( vending->count <= 0 ){
+				// Player was not correctly deleted, must not be set online
+				vending->count = 0;
+				map_quit(vending->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];
+			
+				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);
+
+				j++;
+			}
+
+			Sql_FreeResult( mmysql_handle );
+		}
+
+		ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' autotraders.\n",count);
+	}
+	
+	// Everything is loaded fine, their entries will be reinserted once they are loaded
+	if( Sql_Query( mmysql_handle, "TRUNCATE TABLE `%s`;", vendings_db ) != SQL_SUCCESS ||
+		Sql_Query( mmysql_handle, "TRUNCATE TABLE `%s`;", vending_items_db ) != SQL_SUCCESS ){
+		Sql_ShowDebug(mmysql_handle);
+		return;
+	}
+}
+
 /**
  * Initialise the vending module
  * called in map::do_init
  */
 void do_final_vending(void) {
 	db_destroy(vending_db);
+	db_destroy(autotrade_db);
 }
 
 /**
@@ -411,5 +646,6 @@ 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;
 }

+ 3 - 1
src/map/vending.h

@@ -18,7 +18,9 @@ struct s_vending {
 DBMap * vending_getdb();
 void do_final_vending(void);
 void do_init_vending(void);
-
+void do_init_vending_autotrade( void );
+ 
+void vending_reopen( struct map_session_data* sd );
 void vending_closevending(struct map_session_data* sd);
 void vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count);
 void vending_vendinglistreq(struct map_session_data* sd, int id);