summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog-Trunk.txt9
-rw-r--r--src/char/char.c8
-rw-r--r--src/char_sql/char.c8
-rw-r--r--src/map/chrif.c25
-rw-r--r--src/map/clif.c19
-rw-r--r--src/map/map.c30
-rw-r--r--src/map/map.h2
7 files changed, 82 insertions, 19 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index bae718cf6..9fdbe6d02 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -4,6 +4,15 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2006/07/07
+ * Okay, added a chrif_save_ack packet to the char-server so now the map
+ server will know once a character was "final saved", and only then the
+ character will be removed from memory. On char-server reconnection, all
+ chars that are in final-save state are resent to save (if they are still in
+ memory, it's because the ack hasn't gotten here from the char-server). This
+ should effectively block all dupe problems due to heavy inter-server lag,
+ however as it's untested, it currently prints some debug messages when
+ people are saved and then removed from memory. Need testers so this can be
+ debugged and merged to stable! [Skotlex]
* Now, when the login-char connection is cut, the char-server won't set
everyone offline on reconnect, instead it will send the list of online
accounts to the login server. [Skotlex]
diff --git a/src/char/char.c b/src/char/char.c
index 1556c10a3..a0d34e14e 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -2752,8 +2752,14 @@ int parse_frommap(int fd) {
}
if (i != char_num)
memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
- if (RFIFOB(fd,12)) //Flag, set character offline. [Skotlex]
+ if (RFIFOB(fd,12))
+ { //Flag, set character offline. [Skotlex]
set_char_offline(RFIFOL(fd,8),RFIFOL(fd,4));
+ WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save.
+ WFIFOL(fd, 2) = RFIFOL(fd,4);
+ WFIFOL(fd, 6) = RFIFOL(fd,8);
+ WFIFOSET(fd, 10);
+ }
RFIFOSKIP(fd,RFIFOW(fd,2));
break;
diff --git a/src/char_sql/char.c b/src/char_sql/char.c
index fb024a4ea..98b6534c3 100644
--- a/src/char_sql/char.c
+++ b/src/char_sql/char.c
@@ -2615,8 +2615,14 @@ int parse_frommap(int fd) {
mmo_char_tosql(cid, &char_dat);
}
- if (RFIFOB(fd,12)) //Flag? Set character offline after saving [Skotlex]
+ if (RFIFOB(fd,12))
+ { //Flag? Set character offline after saving [Skotlex]
set_char_offline(cid, aid);
+ WFIFOW(fd, 0) = 0x2b21; //Save ack only needed on final save.
+ WFIFOL(fd, 2) = aid;
+ WFIFOL(fd, 6) = cid;
+ WFIFOSET(fd, 10);
+ }
RFIFOSKIP(fd,size);
break;
}
diff --git a/src/map/chrif.c b/src/map/chrif.c
index 211dcb6f4..0e11c6741 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -37,7 +37,7 @@ static const int packet_len_table[0x3d] = {
6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
-1,-1,-1,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
- -1,-1,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, F->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
+ -1,10,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, U->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
};
//Used Packets:
@@ -82,7 +82,8 @@ static const int packet_len_table[0x3d] = {
//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance]
//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
-//2b21-2b27: FREE
+//2b21: Incomming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex]
+//2b22-2b27: FREE
int chrif_connected;
int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
@@ -183,8 +184,13 @@ int chrif_isconnect(void)
int chrif_save(struct map_session_data *sd, int flag)
{
nullpo_retr(-1, sd);
- chrif_check(-1);
+
pc_makesavestatus(sd);
+ if(!chrif_isconnect())
+ {
+ if (flag) sd->state.finalsave = 1; //Will save character on reconnect.
+ return -1;
+ }
if (sd->state.finalsave)
return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
@@ -193,6 +199,7 @@ int chrif_save(struct map_session_data *sd, int flag)
storage_storage_save(sd->status.account_id, flag);
else if (sd->state.storage_flag == 2)
storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
+ if (flag) sd->state.storage_flag = 0; //Force close it.
//Saving of registry values.
if (sd->state.reg_dirty&4)
@@ -306,6 +313,14 @@ int chrif_removemap(int fd){
return 0;
}
+int chrif_save_ack(int fd) {
+ int aid = RFIFOL(fd,2), cid = RFIFOL(fd,6);
+ struct map_session_data *sd = map_id2sd(aid);
+ if (sd && sd->status.char_id == cid)
+ map_quit_ack(sd);
+ return 0;
+}
+
/*==========================================
* マップ鯖間移動のためのデータ準備要求
*------------------------------------------
@@ -416,6 +431,7 @@ int chrif_sendmapack(int fd)
send_users_tochar(-1, gettick(), 0, 0);
//Re-save any storages that were modified in the disconnection time. [Skotlex]
+ do_reconnect_map();
do_reconnect_storage();
return 0;
@@ -1493,7 +1509,8 @@ int chrif_parse(int fd)
case 0x2b1d: chrif_load_scdata(fd); break;
case 0x2b1e: chrif_update_ip(fd); break;
case 0x2b1f: chrif_disconnectplayer(fd); break;
- case 0x2b20: chrif_removemap(fd); break; //Remove maps of a server [Sirius]
+ case 0x2b20: chrif_removemap(fd); break;
+ case 0x2b21: chrif_save_ack(fd); break;
default:
if (battle_config.error_log)
diff --git a/src/map/clif.c b/src/map/clif.c
index 522199539..69dabc00e 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -1683,12 +1683,9 @@ int clif_move(struct block_list *bl) {
static int clif_delayquit(int tid, unsigned int tick, int id, int data) {
struct map_session_data *sd = NULL;
- if (chrif_isconnect())
- { //Remove player from map server
- if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
- map_quit(sd);
- } else //Save later.
- add_timer(tick + 10000, clif_delayquit, id, 0);
+ //Remove player from map server
+ if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
+ map_quit(sd);
return 0;
}
@@ -1698,12 +1695,12 @@ static int clif_delayquit(int tid, unsigned int tick, int id, int data) {
*/
void clif_quitsave(int fd,struct map_session_data *sd)
{
- if (chrif_isconnect() && (sd->state.waitingdisconnect || //Was already waiting to be disconnected.
- !battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout))
+ if (sd->state.waitingdisconnect || //Was already waiting to be disconnected.
+ !battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
map_quit(sd);
else if (sd->fd)
{ //Disassociate session from player (session is deleted after this function was called)
- //And set a timer to delete this player later.
+ //And set a timer to make him quit later.
session[fd]->session_data = NULL;
sd->fd = 0;
add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
@@ -8018,7 +8015,9 @@ void clif_parse_WantToConnection(int fd, struct map_session_data *sd)
if ((old_sd = map_id2sd(account_id)) != NULL)
{ // if same account already connected, we disconnect the 2 sessions
//Check for characters with no connection (includes those that are using autotrade) [durf],[Skotlex]
- if (!old_sd->fd)
+ if (old_sd->state.finalsave)
+ ; //Ack has not arrived yet from char-server, be patient!
+ else if (!old_sd->fd)
map_quit(old_sd);
else
clif_authfail_fd(old_sd->fd, 2); // same id
diff --git a/src/map/map.c b/src/map/map.c
index c5a4bb314..9c23215a2 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -1657,7 +1657,6 @@ int map_quit(struct map_session_data *sd) {
//nullpo_retr(0, sd); //Utterly innecessary, all invokations to this function already have an SD non-null check.
//Learn to use proper coding and stop relying on nullpo_'s for safety :P [Skotlex]
-
if(!sd->state.waitingdisconnect) {
if (sd->npc_timer_id != -1) //Cancel the event timer.
npc_timerevent_quit(sd);
@@ -1680,7 +1679,6 @@ int map_quit(struct map_session_data *sd) {
//Do we really need to remove the name?
idb_remove(charid_db,sd->status.char_id);
idb_remove(id_db,sd->bl.id);
- idb_remove(pc_db,sd->bl.id);
if(sd->reg)
{ //Double logout already freed pointer fix... [Skotlex]
@@ -1694,12 +1692,38 @@ int map_quit(struct map_session_data *sd) {
sd->regstr = NULL;
sd->regstr_num = 0;
}
+ if(sd->fd)
+ { //Player will be free'd on save-ack. [Skotlex]
+ if (session[sd->fd] && session[sd->fd]->session_data == sd)
+ session[sd->fd]->session_data = NULL;
+ sd->fd = 0;
+ }
+
+ return 0;
+}
- if(!sd->fd) //There is no session connected, and as such socket.c won't free the data, we must do it. [Skotlex]
+void map_quit_ack(struct map_session_data *sd) {
+ if (sd && sd->state.finalsave) {
+ idb_remove(pc_db,sd->status.account_id);
aFree(sd);
+ ShowDebug("Final Save Ack for character %d:%d\n", sd->status.account_id, sd->status.char_id);
+ }
+}
+
+static int do_reconnect_map_sub(DBKey key,void *data,va_list va) {
+ struct map_session_data *sd = (TBL_PC*)data;
+ if (sd->state.finalsave) {
+ sd->state.finalsave = 0;
+ chrif_save(sd, 1); //Resend to save!
+ return 1;
+ }
return 0;
}
+void do_reconnect_map(void) {
+ pc_db->foreach(pc_db,do_reconnect_map_sub);
+}
+
/*==========================================
* id番?のPCを探す。居なければNULL
*------------------------------------------
diff --git a/src/map/map.h b/src/map/map.h
index d7456b742..c247d0a16 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -1304,6 +1304,7 @@ void map_foreachobject(int (*)(struct block_list*,va_list),int,...);
int map_search_freecell(struct block_list *src, int m, short *x, short *y, int rx, int ry, int flag);
//
int map_quit(struct map_session_data *);
+void map_quit_ack(struct map_session_data *);
// npc
int map_addnpc(int,struct npc_data *);
@@ -1375,6 +1376,7 @@ int map_delmap(char *mapname);
int map_addmobtolist(unsigned short m, struct spawn_data *spawn); // [Wizputer]
void map_spawnmobs(int); // [Wizputer]
void map_removemobs(int); // [Wizputer]
+void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex]
//Added for own save method
int charsql_db_init(int method);