summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
authorshennetsind <ind@henn.et>2014-01-06 15:26:00 -0200
committershennetsind <ind@henn.et>2014-01-08 11:22:00 -0200
commitb79a9d7efa9213e3c791ec356bf21b712878d1aa (patch)
tree043173233a97e805a49089a5ced25f213a86217f /src/map
parent85327cde8e451b8b1bacd1f5a98c034a6f42e5ea (diff)
downloadhercules-b79a9d7efa9213e3c791ec356bf21b712878d1aa.tar.gz
hercules-b79a9d7efa9213e3c791ec356bf21b712878d1aa.tar.bz2
hercules-b79a9d7efa9213e3c791ec356bf21b712878d1aa.tar.xz
hercules-b79a9d7efa9213e3c791ec356bf21b712878d1aa.zip
Introducing Hercules Autotrade Persistency
Aka autotrading merchants survive server restarts. Originally sekai's (aka me). Special Thanks to Haruna, Michieru. Signed-off-by: shennetsind <ind@henn.et>
Diffstat (limited to 'src/map')
-rw-r--r--src/map/atcommand.c9
-rw-r--r--src/map/chrif.c21
-rw-r--r--src/map/chrif.h2
-rw-r--r--src/map/clif.c2
-rw-r--r--src/map/map.c7
-rw-r--r--src/map/map.h2
-rw-r--r--src/map/pc.c243
-rw-r--r--src/map/pc.h27
-rw-r--r--src/map/vending.c3
9 files changed, 291 insertions, 25 deletions
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index ce73319e2..ffd6da9d5 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -5479,12 +5479,19 @@ ACMD(autotrade) {
int timeout = atoi(message);
status->change_start(&sd->bl, SC_AUTOTRADE, 10000, 0, 0, 0, 0, ((timeout > 0) ? min(timeout,battle_config.at_timeout) : battle_config.at_timeout) * 60000, 0);
}
-
+
clif->chsys_quit(sd);
clif->authfail_fd(sd->fd, 15);
+
+#ifdef AUTOTRADE_PERSISTENCY
+ pc->autotrade_prepare(sd);
+
+ return false;/* we fail to not cause it to proceed on is_atcommand */
+#else
return true;
+#endif
}
/*==========================================
diff --git a/src/map/chrif.c b/src/map/chrif.c
index e9c3bbabf..7b5cbbe59 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -119,7 +119,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) {
struct auth_node *node;
-
+
if ( (node = chrif->auth_check(account_id, char_id, state) ) ) {
int fd = node->sd ? node->sd->fd : node->fd;
@@ -128,7 +128,7 @@ bool chrif_auth_delete(int account_id, int char_id, enum sd_state state) {
if ( node->char_dat )
aFree(node->char_dat);
-
+
if ( node->sd )
aFree(node->sd);
@@ -486,6 +486,7 @@ int chrif_reconnect(DBKey key, DBData *data, va_list ap) {
/// Called when all the connection steps are completed.
void chrif_on_ready(void) {
+ static bool once = false;
ShowStatus("Map Server is now online.\n");
chrif->state = 2;
@@ -503,6 +504,13 @@ void chrif_on_ready(void) {
//Re-save any guild castles that were modified in the disconnection time.
guild->castle_reconnect(-1, 0, 0);
+
+ if( !once ) {
+#ifdef AUTOTRADE_PERSISTENCY
+ pc->autotrade_load();
+#endif
+ once = true;
+ }
}
@@ -544,22 +552,23 @@ int chrif_scdata_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 hstandalone) {
struct auth_node *node= chrif->search(sd->bl.id);
-
+
if( node != NULL || !chrif->isconnected() ) {
set_eof(sd->fd);
return;
}
- WFIFOHEAD(chrif->fd,19);
+ WFIFOHEAD(chrif->fd,20);
WFIFOW(chrif->fd,0) = 0x2b26;
WFIFOL(chrif->fd,2) = sd->status.account_id;
WFIFOL(chrif->fd,6) = sd->status.char_id;
WFIFOL(chrif->fd,10) = sd->login_id1;
WFIFOB(chrif->fd,14) = sd->status.sex;
WFIFOL(chrif->fd,15) = htonl(session[sd->fd]->client_addr);
- WFIFOSET(chrif->fd,19);
+ WFIFOB(chrif->fd,19) = hstandalone ? 1 : 0;
+ WFIFOSET(chrif->fd,20);
chrif->sd_to_auth(sd, ST_LOGIN);
}
diff --git a/src/map/chrif.h b/src/map/chrif.h
index b69d34210..24b92908d 100644
--- a/src/map/chrif.h
+++ b/src/map/chrif.h
@@ -79,7 +79,7 @@ struct chrif_interface {
bool (*auth_delete) (int account_id, int char_id, enum sd_state state);
bool (*auth_finished) (struct map_session_data* sd);
- void (*authreq) (struct map_session_data* sd);
+ void (*authreq) (struct map_session_data* sd, bool hstandalone);
void (*authok) (int fd);
int (*scdata_request) (int account_id, int char_id);
int (*save) (struct map_session_data* sd, int flag);
diff --git a/src/map/clif.c b/src/map/clif.c
index b442d6279..e69748809 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -9188,7 +9188,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);
}
void clif_hercules_chsys_mjoin(struct map_session_data *sd) {
if( !map->list[sd->bl.m].channel ) {
diff --git a/src/map/map.c b/src/map/map.c
index aef34ef00..922807158 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -1676,6 +1676,9 @@ int map_quit(struct map_session_data *sd) {
if( sd->bg_id && !sd->bg_queue.arena ) /* TODO: dump this chunk after bg_queue is fully enabled */
bg->team_leave(sd,1);
+ if( sd->state.autotrade && runflag != MAPSERVER_ST_SHUTDOWN && !hChSys.closing )
+ pc->autotrade_update(sd,PAUC_REMOVE);
+
skill->cooldown_save(sd);
pc->itemcd_do(sd,false);
@@ -3606,6 +3609,10 @@ int inter_config_read(char *cfgName) {
map->db_use_sql_mob_skill_db = config_switch(w2);
ShowStatus ("Using monster skill database as SQL: '%s'\n", w2);
}
+ else if(strcmpi(w1,"autotrade_merchants_db")==0)
+ strcpy(map->autotrade_merchants_db, w2);
+ else if(strcmpi(w1,"autotrade_data_db")==0)
+ strcpy(map->autotrade_data_db, w2);
/* sql log db */
else if(strcmpi(w1,"log_db_ip")==0)
strcpy(logs->db_ip, w2);
diff --git a/src/map/map.h b/src/map/map.h
index 906202f83..a39cc7f86 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -839,6 +839,8 @@ struct map_interface {
char mob_skill_db_db[32];
char mob_skill_db2_db[32];
char interreg_db[32];
+ char autotrade_merchants_db[32];
+ char autotrade_data_db[32];
char default_codepage[32];
diff --git a/src/map/pc.c b/src/map/pc.c
index fc1d56b6d..9e819bcac 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -1330,7 +1330,7 @@ int pc_reg_received(struct map_session_data *sd)
if( npc->motd ) /* [Ind/Hercules] */
script->run(npc->motd->u.scr.script, 0, sd->bl.id, npc->fake_nd->bl.id);
-
+
return 1;
}
@@ -10291,6 +10291,12 @@ void pc_scdata_received(struct map_session_data *sd) {
pc->expire_check(sd);
}
+
+ if( sd->state.standalone ) {
+ clif->pLoadEndAck(0,sd);
+ pc->autotrade_populate(sd);
+ pc->autotrade_start(sd);
+ }
}
int pc_expiration_timer(int tid, int64 tick, int id, intptr_t data) {
struct map_session_data *sd = map->id2sd(id);
@@ -10342,14 +10348,211 @@ void pc_expire_check(struct map_session_data *sd) {
sd->expiration_tid = timer->add(timer->gettick() + (int64)(sd->expiration_time - time(NULL))*1000, pc->expiration_timer, sd->bl.id, 0);
}
+/**
+ * Loads autotraders
+ ***/
+void pc_autotrade_load(void) {
+ struct map_session_data *sd;
+ char *data;
+
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT `account_id`,`char_id`,`sex`,`title` FROM `%s`",map->autotrade_merchants_db))
+ Sql_ShowDebug(map->mysql_handle);
+
+ while( SQL_SUCCESS == SQL->NextRow(map->mysql_handle) ) {
+ int account_id, char_id;
+ char title[MESSAGE_SIZE];
+ unsigned char sex;
+
+ SQL->GetData(map->mysql_handle, 0, &data, NULL); account_id = atoi(data);
+ SQL->GetData(map->mysql_handle, 1, &data, NULL); char_id = atoi(data);
+ SQL->GetData(map->mysql_handle, 2, &data, NULL); sex = atoi(data);
+ SQL->GetData(map->mysql_handle, 3, &data, NULL); safestrncpy(title, data, sizeof(title));
-/*==========================================
- * pc Init/Terminate
- *------------------------------------------*/
-void do_final_pc(void) {
+ CREATE(sd, TBL_PC, 1);
+
+ pc->setnewpc(sd, account_id, char_id, 0, 0, sex, 0);
+
+ safestrncpy(sd->message, title, MESSAGE_SIZE);
+
+ sd->state.standalone = 1;
+ sd->group = pcg->get_dummy_group();
+
+ chrif->authreq(sd,true);
+ }
+
+ SQL->FreeResult(map->mysql_handle);
+}
+/**
+ * Loads vending data and sets it up, is triggered when char server data that pc_autotrade_load requested arrives
+ **/
+void pc_autotrade_start(struct map_session_data *sd) {
+ unsigned int count = 0;
+ int i;
+ char *data;
- db_destroy(pc->itemcd_db);
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT `itemkey`,`amount`,`price` FROM `%s` WHERE `char_id` = '%d'",map->autotrade_data_db,sd->status.char_id))
+ Sql_ShowDebug(map->mysql_handle);
+
+ while( SQL_SUCCESS == SQL->NextRow(map->mysql_handle) ) {
+ int itemkey, amount, price;
+
+ SQL->GetData(map->mysql_handle, 0, &data, NULL); itemkey = atoi(data);
+ SQL->GetData(map->mysql_handle, 1, &data, NULL); amount = atoi(data);
+ SQL->GetData(map->mysql_handle, 2, &data, NULL); price = atoi(data);
+ ARR_FIND(0, MAX_CART, i, sd->status.cart[i].id == itemkey);
+
+ if( i != MAX_CART && itemdb_cantrade(&sd->status.cart[i], 0, 0) ) {
+ if( amount > sd->status.cart[i].amount )
+ amount = sd->status.cart[i].amount;
+
+ if( amount ) {
+ sd->vending[count].index = i;
+ sd->vending[count].amount = amount;
+ sd->vending[count].value = cap_value(price, 0, (unsigned int)battle_config.vending_max_value);
+
+ count++;
+ }
+ }
+ }
+
+ if( !count ) {
+ pc->autotrade_update(sd,PAUC_REMOVE);
+ map->quit(sd);
+ } else {
+ sd->state.autotrade = 1;
+ sd->vender_id = ++vending->next_id;
+ sd->vend_num = count;
+ sd->state.vending = true;
+ idb_put(vending->db, sd->status.char_id, sd);
+ if( map->list[sd->bl.m].users )
+ clif->showvendingboard(&sd->bl,sd->message,0);
+ }
+}
+/**
+ * Perform a autotrade action
+ **/
+void pc_autotrade_update(struct map_session_data *sd, enum e_pc_autotrade_update_action action) {
+ int i;
+
+ /* either way, this goes down */
+ if( action != PAUC_START ) {
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d'",map->autotrade_data_db,sd->status.char_id))
+ Sql_ShowDebug(map->mysql_handle);
+ }
+
+ switch( action ) {
+ case PAUC_REMOVE:
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' LIMIT 1",map->autotrade_merchants_db,sd->status.char_id))
+ Sql_ShowDebug(map->mysql_handle);
+ break;
+ case PAUC_START:
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s` (`account_id`,`char_id`,`sex`,`title`) VALUES ('%d','%d','%d','%s')",
+ map->autotrade_merchants_db,
+ sd->status.account_id,
+ sd->status.char_id,
+ sd->status.sex,
+ sd->message
+ ))
+ Sql_ShowDebug(map->mysql_handle);
+ /* yes we want it to fall */
+ case PAUC_REFRESH:
+ for( i = 0; i < sd->vend_num; i++ ) {
+ if( sd->vending[i].amount == 0 )
+ continue;
+
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "INSERT INTO `%s` (`char_id`,`itemkey`,`amount`,`price`) VALUES ('%d','%d','%d','%d')",
+ map->autotrade_data_db,
+ sd->status.char_id,
+ sd->status.cart[sd->vending[i].index].id,
+ sd->vending[i].amount,
+ sd->vending[i].value
+ ))
+ Sql_ShowDebug(map->mysql_handle);
+ }
+ break;
+ }
+}
+/**
+ * Handles characters upon @autotrade usage
+ **/
+void pc_autotrade_prepare(struct map_session_data *sd) {
+ struct autotrade_vending *data;
+ int i, cursor = 0;
+ int account_id, char_id;
+ char title[MESSAGE_SIZE];
+ unsigned char sex;
+
+ CREATE(data, struct autotrade_vending, 1);
+
+ memcpy(data->vending, sd->vending, sizeof(sd->vending));
+
+ for(i = 0; i < sd->vend_num; i++) {
+ if( sd->vending[i].amount ) {
+ memcpy(&data->list[cursor],&sd->status.cart[sd->vending[i].index],sizeof(struct item));
+ cursor++;
+ }
+ }
+
+ data->vend_num = (unsigned char)cursor;
+
+ idb_put(pc->at_db, sd->status.char_id, data);
+
+ account_id = sd->status.account_id;
+ char_id = sd->status.char_id;
+ sex = sd->status.sex;
+ safestrncpy(title, sd->message, sizeof(title));
+
+ map->quit(sd);
+ chrif->auth_delete(account_id, char_id, ST_LOGOUT);
+
+ CREATE(sd, TBL_PC, 1);
+
+ pc->setnewpc(sd, account_id, char_id, 0, 0, sex, 0);
+
+ safestrncpy(sd->message, title, MESSAGE_SIZE);
+
+ sd->state.standalone = 1;
+ sd->group = pcg->get_dummy_group();
+
+ chrif->authreq(sd,true);
+}
+/**
+ * Prepares autotrade data from pc->at_db from a player that has already returned from char server
+ **/
+void pc_autotrade_populate(struct map_session_data *sd) {
+ struct autotrade_vending *data;
+ int i, j, cursor = 0;
+
+ if( !(data = idb_get(pc->at_db,sd->status.char_id)) )
+ return;
+
+ for(i = 0; i < data->vend_num; i++) {
+ if( !data->vending[i].amount )
+ continue;
+
+ ARR_FIND(0, MAX_CART, j, !memcmp((char*)(&data->list[i]) + sizeof(data->list[0].id), (char*)(&sd->status.cart[j]) + sizeof(data->list[0].id), sizeof(struct item) - sizeof(data->list[0].id)));
+
+ if( j != MAX_CART ) {
+ sd->vending[cursor].index = j;
+ sd->vending[cursor].amount = data->vending[i].amount;
+ sd->vending[cursor].value = data->vending[i].value;
+
+ cursor++;
+ }
+ }
+
+ sd->vend_num = cursor;
+
+ pc->autotrade_update(sd,PAUC_START);
+
+ idb_remove(pc->at_db, sd->status.char_id);
+}
+void do_final_pc(void) {
+
+ db_destroy(pc->itemcd_db);
+ db_destroy(pc->at_db);
+
pcg->final();
ers_destroy(pc->sc_display_ers);
@@ -10359,11 +10562,12 @@ void do_final_pc(void) {
void do_init_pc(bool minimal) {
if (minimal)
return;
-
+
pc->itemcd_db = idb_alloc(DB_OPT_RELEASE_DATA);
-
+ pc->at_db = idb_alloc(DB_OPT_RELEASE_DATA);
+
pc->readdb();
-
+
timer->add_func_list(pc->invincible_timer, "pc_invincible_timer");
timer->add_func_list(pc->eventtimer, "pc_eventtimer");
timer->add_func_list(pc->inventory_rental_end, "pc_inventory_rental_end");
@@ -10375,28 +10579,27 @@ void do_init_pc(bool minimal) {
timer->add_func_list(pc->charm_timer, "pc_charm_timer");
timer->add_func_list(pc->global_expiration_timer,"pc_global_expiration_timer");
timer->add_func_list(pc->expiration_timer,"pc_expiration_timer");
-
+
timer->add(timer->gettick() + map->autosave_interval, pc->autosave, 0, 0);
-
+
// 0=day, 1=night [Yor]
map->night_flag = battle_config.night_at_start ? 1 : 0;
-
+
if (battle_config.day_duration > 0 && battle_config.night_duration > 0) {
int day_duration = battle_config.day_duration;
int night_duration = battle_config.night_duration;
// add night/day timer [Yor]
timer->add_func_list(pc->map_day_timer, "pc_map_day_timer");
timer->add_func_list(pc->map_night_timer, "pc_map_night_timer");
-
+
pc->day_timer_tid = timer->add_interval(timer->gettick() + (map->night_flag ? 0 : day_duration) + night_duration, pc->map_day_timer, 0, 0, day_duration + night_duration);
pc->night_timer_tid = timer->add_interval(timer->gettick() + day_duration + (map->night_flag ? night_duration : 0), pc->map_night_timer, 0, 0, day_duration + night_duration);
}
-
+
pcg->init();
pc->sc_display_ers = ers_new(sizeof(struct sc_display_entry), "pc.c:sc_display_ers", ERS_OPT_NONE);
}
-
/*=====================================
* Default Functions : pc.h
* Generated by HerculesInterfaceMaker
@@ -10414,6 +10617,7 @@ void pc_defaults(void) {
pc = &pc_s;
/* vars */
+ pc->at_db = NULL;
pc->itemcd_db = NULL;
/* */
pc->day_timer_tid = INVALID_TIMER;
@@ -10681,4 +10885,13 @@ void pc_defaults(void) {
pc->expiration_timer = pc_expiration_timer;
pc->global_expiration_timer = pc_global_expiration_timer;
pc->expire_check = pc_expire_check;
+
+ /**
+ * Autotrade persistency [Ind/Hercules <3]
+ **/
+ pc->autotrade_load = pc_autotrade_load;
+ pc->autotrade_update = pc_autotrade_update;
+ pc->autotrade_start = pc_autotrade_start;
+ pc->autotrade_prepare = pc_autotrade_prepare;
+ pc->autotrade_populate = pc_autotrade_populate;
}
diff --git a/src/map/pc.h b/src/map/pc.h
index 5264fa557..2f143f15c 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -180,6 +180,7 @@ struct map_session_data {
unsigned int snovice_call_flag : 3; //Summon Angel (stage 1~3)
unsigned int hpmeter_visible : 1;
unsigned int itemcheck : 1;
+ unsigned int standalone : 1;/* [Ind/Hercules <3] */
} state;
struct {
unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
@@ -724,6 +725,21 @@ struct item_cd {
short nameid[MAX_ITEMDELAYS];//skill id
};
+enum e_pc_autotrade_update_action {
+ PAUC_START,
+ PAUC_REFRESH,
+ PAUC_REMOVE,
+};
+
+/**
+ * Used to temporarily remember vending data
+ **/
+struct autotrade_vending {
+ struct item list[MAX_VENDING];
+ struct s_vending vending[MAX_VENDING];
+ unsigned char vend_num;
+};
+
/*=====================================
* Interface : pc.h
* Generated by HerculesInterfaceMaker
@@ -732,6 +748,8 @@ struct item_cd {
struct pc_interface {
/* */
+ DBMap *at_db;/* char id -> struct autotrade_vending */
+ /* */
DBMap* itemcd_db;
/* */
int day_timer_tid;
@@ -1000,6 +1018,15 @@ struct pc_interface {
int (*expiration_timer) (int tid, int64 tick, int id, intptr_t data);
int (*global_expiration_timer) (int tid, int64 tick, int id, intptr_t data);
void (*expire_check) (struct map_session_data *sd);
+
+ /**
+ * Autotrade persistency [Ind/Hercules <3]
+ **/
+ void (*autotrade_load) (void);
+ void (*autotrade_update) (struct map_session_data *sd, enum e_pc_autotrade_update_action action);
+ void (*autotrade_start) (struct map_session_data *sd);
+ void (*autotrade_prepare) (struct map_session_data *sd);
+ void (*autotrade_populate) (struct map_session_data *sd);
};
struct pc_interface *pc;
diff --git a/src/map/vending.c b/src/map/vending.c
index 7d248351c..78bc79a31 100644
--- a/src/map/vending.c
+++ b/src/map/vending.c
@@ -212,7 +212,8 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid,
//Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
vending->close(vsd);
map->quit(vsd); //They have no reason to stay around anymore, do they?
- }
+ } else
+ pc->autotrade_update(vsd,PAUC_REFRESH);
}
}