Browse Source

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

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@7568 54d463be-8e91-2dee-dedb-b68131a5f0ec
skotlex 19 years ago
parent
commit
e0afdded8b
7 changed files with 82 additions and 19 deletions
  1. 9 0
      Changelog-Trunk.txt
  2. 7 1
      src/char/char.c
  3. 7 1
      src/char_sql/char.c
  4. 21 4
      src/map/chrif.c
  5. 9 10
      src/map/clif.c
  6. 27 3
      src/map/map.c
  7. 2 0
      src/map/map.h

+ 9 - 0
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.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
 
 2006/07/07
 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
 	* 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
 	  everyone offline on reconnect, instead it will send the list of online
 	  accounts to the login server. [Skotlex]
 	  accounts to the login server. [Skotlex]

+ 7 - 1
src/char/char.c

@@ -2752,8 +2752,14 @@ int parse_frommap(int fd) {
 			}
 			}
 			if (i != char_num)
 			if (i != char_num)
 				memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus));
 				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));
 				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));
 			RFIFOSKIP(fd,RFIFOW(fd,2));
 			break;
 			break;
 
 

+ 7 - 1
src/char_sql/char.c

@@ -2615,8 +2615,14 @@ int parse_frommap(int fd) {
 				mmo_char_tosql(cid, &char_dat);
 				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);
 				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);
 			RFIFOSKIP(fd,size);
 			break;
 			break;
 		}
 		}

+ 21 - 4
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
 	 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
 	 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, 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:
 //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]
 //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]
 //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]
 //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 chrif_connected;
 int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
 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)
 int chrif_save(struct map_session_data *sd, int flag)
 {
 {
 	nullpo_retr(-1, sd);
 	nullpo_retr(-1, sd);
-	chrif_check(-1);
+	
 	pc_makesavestatus(sd);
 	pc_makesavestatus(sd);
+	if(!chrif_isconnect())
+  	{
+		if (flag) sd->state.finalsave = 1; //Will save character on reconnect.
+		return -1;
+	}
 
 
 	if (sd->state.finalsave)
 	if (sd->state.finalsave)
 		return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
 		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);
 		storage_storage_save(sd->status.account_id, flag);
 	else if (sd->state.storage_flag == 2)
 	else if (sd->state.storage_flag == 2)
 		storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
 		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. 
 	//Saving of registry values. 
 	if (sd->state.reg_dirty&4)
 	if (sd->state.reg_dirty&4)
@@ -306,6 +313,14 @@ int chrif_removemap(int fd){
 	return 0;
 	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);
 	send_users_tochar(-1, gettick(), 0, 0);
 	
 	
 	//Re-save any storages that were modified in the disconnection time. [Skotlex]
 	//Re-save any storages that were modified in the disconnection time. [Skotlex]
+	do_reconnect_map();
 	do_reconnect_storage();
 	do_reconnect_storage();
 
 
 	return 0;
 	return 0;
@@ -1493,7 +1509,8 @@ int chrif_parse(int fd)
 		case 0x2b1d: chrif_load_scdata(fd); break;
 		case 0x2b1d: chrif_load_scdata(fd); break;
 		case 0x2b1e: chrif_update_ip(fd); break;
 		case 0x2b1e: chrif_update_ip(fd); break;
 		case 0x2b1f: chrif_disconnectplayer(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:
 		default:
 			if (battle_config.error_log)
 			if (battle_config.error_log)

+ 9 - 10
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) {
 static int clif_delayquit(int tid, unsigned int tick, int id, int data) {
 	struct map_session_data *sd = NULL;
 	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;
 	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)
 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);
 		map_quit(sd);
 	else if (sd->fd)
 	else if (sd->fd)
 	{	//Disassociate session from player (session is deleted after this function was called)
 	{	//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;
 		session[fd]->session_data = NULL;
 		sd->fd = 0;
 		sd->fd = 0;
 		add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 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 ((old_sd = map_id2sd(account_id)) != NULL)
 		{	// if same account already connected, we disconnect the 2 sessions
 		{	// if same account already connected, we disconnect the 2 sessions
 			//Check for characters with no connection (includes those that are using autotrade) [durf],[Skotlex]
 			//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);
 				map_quit(old_sd);
 			else 
 			else 
 				clif_authfail_fd(old_sd->fd, 2); // same id
 				clif_authfail_fd(old_sd->fd, 2); // same id

+ 27 - 3
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.
 	//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]
 	//Learn to use proper coding and stop relying on nullpo_'s for safety :P [Skotlex]
-
 	if(!sd->state.waitingdisconnect) {
 	if(!sd->state.waitingdisconnect) {
 		if (sd->npc_timer_id != -1) //Cancel the event timer.
 		if (sd->npc_timer_id != -1) //Cancel the event timer.
 			npc_timerevent_quit(sd);
 			npc_timerevent_quit(sd);
@@ -1680,7 +1679,6 @@ int map_quit(struct map_session_data *sd) {
 	//Do we really need to remove the name?
 	//Do we really need to remove the name?
 	idb_remove(charid_db,sd->status.char_id);
 	idb_remove(charid_db,sd->status.char_id);
 	idb_remove(id_db,sd->bl.id);
 	idb_remove(id_db,sd->bl.id);
-	idb_remove(pc_db,sd->bl.id);
 
 
 	if(sd->reg)
 	if(sd->reg)
 	{	//Double logout already freed pointer fix... [Skotlex]
 	{	//Double logout already freed pointer fix... [Skotlex]
@@ -1694,12 +1692,38 @@ int map_quit(struct map_session_data *sd) {
 		sd->regstr = NULL;
 		sd->regstr = NULL;
 		sd->regstr_num = 0;
 		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);
 		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;
 	return 0;
 }
 }
 
 
+void do_reconnect_map(void) {
+	pc_db->foreach(pc_db,do_reconnect_map_sub);
+}
+
 /*==========================================
 /*==========================================
  * id番?のPCを探す。居なければNULL
  * id番?のPCを探す。居なければNULL
  *------------------------------------------
  *------------------------------------------

+ 2 - 0
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_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 *);
 int map_quit(struct map_session_data *);
+void map_quit_ack(struct map_session_data *);
 // npc
 // npc
 int map_addnpc(int,struct npc_data *);
 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]
 int map_addmobtolist(unsigned short m, struct spawn_data *spawn);	// [Wizputer]
 void map_spawnmobs(int); // [Wizputer]
 void map_spawnmobs(int); // [Wizputer]
 void map_removemobs(int); // [Wizputer]
 void map_removemobs(int); // [Wizputer]
+void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex]
 
 
 //Added for own save method
 //Added for own save method
 int charsql_db_init(int method);
 int charsql_db_init(int method);