Merge pull request #40 from Lemongrass3110/master

Added autotrade persistence support.
- Players who are autotrading during a crash/shut down will be restored on start up.
- Live vendor data is stored in SQL.
- Credits to Ind and Lemongrass.
This commit is contained in:
aleos89 2014-01-23 07:49:18 -08:00
commit 27cbc7ff24
25 changed files with 400 additions and 37 deletions

View File

@ -22,3 +22,15 @@ feature.atcommand_suggestions: off
// Banking (Note 1)
// Requires: 2013-07-24aRagexe or later
feature.banking: on
// Autotrade persistency (Note 1)
// Should vendors that used @autotrade be restored after a restart?
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

View File

@ -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

View File

@ -12014,7 +12014,7 @@ OnTouch:
que_job01,6,94,0 warp morocc#01 2,2,morocc,45,103
que_job01,17,48,0 warp que_job01#02 2,2,que_job01,68,92
que_job01,68,96,0 warp que_job01#03 2,2,que_job01,17,53;
que_job01,68,96,0 warp que_job01#03 2,2,que_job01,17,53
que_job01,82,95,3 script Bar Master#moc2_01 46,{
if (checkweight(1201,1) == 0) {

View File

@ -2548,7 +2548,7 @@ OnTouch:
}
}
spl_in02,236,86,0 warp terrashome_out 1,1,splendide,285,139;
spl_in02,236,86,0 warp terrashome_out 1,1,splendide,285,139
spl_fild01,357,44,0 script ???#ep13mdf01 844,{
if (checkweight(1201,2) == 0) {

View File

@ -2409,9 +2409,9 @@ lou_in02,192,170,0 script Supply Stack#2 111,{
close;
}
louyang,129,121,0 warp Storage Warp#1 1,1,lou_in02,203,161;
louyang,125,121,0 warp Storage Warp#2 1,1,lou_in02,198,161;
lou_in02,198,159,0 warp Storage Warp#3 1,1,louyang,124,118;
louyang,129,121,0 warp Storage Warp#1 1,1,lou_in02,203,161
louyang,125,121,0 warp Storage Warp#2 1,1,lou_in02,198,161
lou_in02,198,159,0 warp Storage Warp#3 1,1,louyang,124,118
lou_in02,203,159,0 warp Storage Warp#4 1,1,louyang,129,118
// Poison King Quest :: poison_king

View File

@ -3646,8 +3646,8 @@ que_ba,181,14,7 script Researcher#bpast_2_2 865,{
close;
}
que_ba,183,25,0 warp #bpast_2to3_1 1,1,que_ba,72,25;
que_ba,183,52,0 warp #bpast_2to3_2 1,1,que_ba,72,51;
que_ba,183,25,0 warp #bpast_2to3_1 1,1,que_ba,72,25
que_ba,183,52,0 warp #bpast_2to3_2 1,1,que_ba,72,51
que_ba,102,56,0 script #3room_barmunt -1,3,3,{
//OnTouch2:

View File

@ -712,3 +712,24 @@ CREATE TABLE IF NOT EXISTS `bonus_script` (
`type` char(1) NOT NULL DEFAULT '0',
`icon` varchar(3) NOT NULL DEFAULT '-1'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `vending_items` (
`vending_id` int(10) unsigned NOT NULL,
`index` smallint(5) unsigned NOT NULL,
`cartinventory_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 `vendings` (
`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,
`autotrade` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

View File

@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS `vending_items` (
`vending_id` int(10) unsigned NOT NULL,
`index` smallint(5) unsigned NOT NULL,
`cartinventory_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 `vendings` (
`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,
`autotrade` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

View File

@ -3308,10 +3308,6 @@ int parse_frommap(int fd)
WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data);
WFIFOW(fd,12) = count;
WFIFOSET(fd,WFIFOW(fd,2));
//Clear the data once loaded.
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) )
Sql_ShowDebug(sql_handle);
}
}
Sql_FreeResult(sql_handle);
@ -3715,8 +3711,10 @@ int parse_frommap(int fd)
cid = RFIFOL(fd, 8);
count = RFIFOW(fd, 12);
if( count > 0 )
{
// Whatever comes from the mapserver, now is the time to drop previous entries
if( Sql_Query( sql_handle, "DELETE FROM `%s` where `account_id` = %d and `char_id` = %d;", scdata_db, aid, cid ) != SQL_SUCCESS ){
Sql_ShowDebug( sql_handle );
}else if( count > 0 ){
struct status_change_data data;
StringBuf buf;
int i;
@ -3779,7 +3777,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 +3789,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 +3806,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 &&

View File

@ -5653,6 +5653,13 @@ 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", 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 +5667,8 @@ ACMD_FUNC(autotrade) {
channel_pcquit(sd,0xF); //leave all chan
clif_authfail_fd(sd->fd, 15);
chrif_save(sd,3);
return 0;
}

View File

@ -7340,6 +7340,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
/**

View File

@ -520,6 +520,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);

View File

@ -119,6 +119,8 @@ int channel_delete(struct Channel *channel) {
int channel_join(struct Channel *channel, struct map_session_data *sd) {
if(!channel || !sd)
return -1;
if(sd->state.autotrade)
return 0; // fake success
if(channel_haspc(channel,sd)==1)
return -2;

View File

@ -277,6 +277,7 @@ int chrif_isconnected(void) {
* Saves character data.
* Flag = 1: Character is quitting
* Flag = 2: Character is changing map-servers
* Flag = 3: Character used @autotrade
*------------------------------------------*/
int chrif_save(struct map_session_data *sd, int flag) {
uint32 mmo_charstatus_len = 0;
@ -292,7 +293,7 @@ int chrif_save(struct map_session_data *sd, int flag) {
chrif_save_bsdata(sd);
chrif_req_login_operation(sd->status.account_id, sd->status.name, 7, 0, 2, sd->status.bank_vault); //save Bank data
}
if ( !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
}
@ -562,6 +563,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 +619,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 +627,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);
}
@ -1117,8 +1122,13 @@ int chrif_disconnectplayer(int fd) {
}
if (!sd->fd) { //No connection
if (sd->state.autotrade)
if (sd->state.autotrade){
if( sd->state.vending ){
vending_closevending(sd);
}
map_quit(sd); //Remove it.
}
//Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
//and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
return 0;
@ -1286,9 +1296,6 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the
count++;
}
if (count == 0)
return 0; //Nothing to save.
WFIFOW(char_fd,12) = count;
WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
WFIFOSET(char_fd,WFIFOW(char_fd,2));
@ -1364,6 +1371,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;
}

View File

@ -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);

View File

@ -9441,7 +9441,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);
}
@ -17857,6 +17857,7 @@ void packetdb_readdb(void)
packet_len(i) = packet_len_table[i];
for(f = 0; f<ARRAYLENGTH(filename); f++){
entries = 0;
sprintf(line, "%s/%s", db_path,filename[f]);
if( (fp=fopen(line,"r"))==NULL ){
if(f==0) {
@ -18004,7 +18005,8 @@ void packetdb_readdb(void)
clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
}
ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, "packet_db.txt");
sprintf(line, "%s/%s", db_path,filename[f]);
ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, line);
}
ShowStatus("Using default packet version: "CL_WHITE"%d"CL_RESET".\n", clif_config.packet_db_ver);
}

View File

@ -2105,7 +2105,7 @@ void do_init_guild(void) {
for(i=0; i<ARRAYLENGTH(dbsubpath); i++){
int n1 = strlen(db_path)+strlen(dbsubpath[i])+1;
char* dbsubpath1 = aMalloc(n1+1);
safesnprintf(dbsubpath1,n1+1,"%s/%s",db_path,dbsubpath[i]);
safesnprintf(dbsubpath1,n1+1,"%s%s",db_path,dbsubpath[i]);
sv_readdb(dbsubpath1, "castle_db.txt", ',', 4, 4, -1, &guild_read_castledb, i);
sv_readdb(dbsubpath1, "guild_skill_tree.txt", ',', 2+MAX_GUILD_SKILL_REQUIRE*2, 2+MAX_GUILD_SKILL_REQUIRE*2, -1, &guild_read_guildskill_tree_db, i); //guild skill tree [Komurka]

View File

@ -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)

View File

@ -910,6 +910,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);

View File

@ -4558,9 +4558,9 @@ static void mob_load(void)
int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
char* dbsubpath1 = aMalloc(n1+1);
char* dbsubpath2 = aMalloc(n2+1);
safesnprintf(dbsubpath1,n1+1,"%s/%s",db_path,dbsubpath[i]);
safesnprintf(dbsubpath1,n1+1,"%s%s",db_path,dbsubpath[i]);
if(i==0) safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s/%s",db_path,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s%s",db_path,dbsubpath[i]);
sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, i); // must be read before mobdb
sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, MAX_MOB_CHAT, &mob_parse_row_chatdb, i);

View File

@ -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;
}
@ -10224,9 +10228,9 @@ int pc_readdb(void)
int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
char* dbsubpath1 = aMalloc(n1+1);
char* dbsubpath2 = aMalloc(n2+1);
safesnprintf(dbsubpath1,n1+1,"%s/%s",db_path,dbsubpath[i]);
safesnprintf(dbsubpath1,n1+1,"%s%s",db_path,dbsubpath[i]);
if(i==0) safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s/%s",db_path,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s%s",db_path,dbsubpath[i]);
s = pc_read_statsdb(dbsubpath2,s,i);
#ifdef RENEWAL_ASPD

View File

@ -19229,9 +19229,9 @@ static void skill_readdb(void) {
int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
char* dbsubpath1 = aMalloc(n1+1);
char* dbsubpath2 = aMalloc(n2+1);
safesnprintf(dbsubpath1,n1+1,"%s/%s",db_path,dbsubpath[i]);
safesnprintf(dbsubpath1,n1+1,"%s%s",db_path,dbsubpath[i]);
if(i==0) safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s/%s",db_path,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s%s",db_path,dbsubpath[i]);
sv_readdb(dbsubpath2, "skill_db.txt" , ',', 18, 18, MAX_SKILL_DB, skill_parse_row_skilldb, i);
sv_readdb(dbsubpath2, "skill_require_db.txt" , ',', 34, 34, MAX_SKILL_DB, skill_parse_row_requiredb, i);

View File

@ -12346,9 +12346,9 @@ int status_readdb(void)
int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
char* dbsubpath1 = aMalloc(n1+1);
char* dbsubpath2 = aMalloc(n2+1);
safesnprintf(dbsubpath1,n1+1,"%s/%s",db_path,dbsubpath[i]);
safesnprintf(dbsubpath1,n1+1,"%s%s",db_path,dbsubpath[i]);
if(i==0) safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s/%s",db_path,dbsubpath[i]);
else safesnprintf(dbsubpath2,n2,"%s%s",db_path,dbsubpath[i]);
status_readdb_attrfix(dbsubpath2,i); // !TODO use sv_readdb ?
sv_readdb(dbsubpath1, "size_fix.txt",',',MAX_WEAPON_TYPE,MAX_WEAPON_TYPE,ARRAYLENGTH(atkmods),&status_readdb_sizefix, i);

View File

@ -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,198 @@ 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 );
aFree(vending->entries);
aFree(vending);
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 ){
idb_remove( autotrade_db, sd->status.char_id );
aFree(vending->entries);
aFree(vending);
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 "
"ORDER BY `index` ASC;", 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
idb_remove( autotrade_db, vending->char_id );
map_quit(vending->sd);
aFree(vending);
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 +653,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;
}

View File

@ -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);