Просмотр исходного кода

- Fixed new guilds displaying online-connect member count at 0 rather than 1, and the guild master not knowing it is one (eg: it cannot edit the guild notice of a newly created guild until relogging).
- Fixed acc_reg2 parsing screwing up the char_id and subtracting 2 from it rather than passing it as it is.
- Extended the auth_node/auth_db system in chrif.c to handle log in/out and mapserver-change procedures. This way players are not in the main dbs when they are not "active", which blocks potential invalid accesses to them.
- Replaced states auth, waiting_disconnect and finalsave with active.
- Cleaned some the party/guild login and creation procedures, removed the party_sent/guild_sent states.
- Removed a redundant guild_check_member call which is beyond not-needed and into the realm of wasting resources.
- clif_parse will no longer process packets from !sd->state.active players, this also makes checking for finalsave uneccessary (since players re already removed from the maps and dbs by this point, so you can't access them in any other way)
- Separated the roles of unit_free and map_quit, the former will handle cleaning structures from the player so it can be free'd safely, while the latter performs additional routines which are unique to characters logging out normally (map-server changes will invoke unit_free and bypass map_quit).
- Removed pc_isplaying, quit_db, map_knowsaccount, MAPIT_PCISPLAYING among other functions/defines which are no longer needed due to the new login scheme.
- Cleand up a bit some code in the clif_send(_sub) functions.


git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@12223 54d463be-8e91-2dee-dedb-b68131a5f0ec

skotlex 17 лет назад
Родитель
Сommit
1e0a82fab3
24 измененных файлов с 603 добавлено и 625 удалено
  1. 11 0
      Changelog-Trunk.txt
  2. 1 0
      src/char/int_guild.c
  3. 1 1
      src/char/inter.c
  4. 2 0
      src/char_sql/int_guild.c
  5. 1 1
      src/char_sql/inter.c
  6. 1 1
      src/map/atcommand.c
  7. 212 105
      src/map/chrif.c
  8. 12 4
      src/map/chrif.h
  9. 90 93
      src/map/clif.c
  10. 46 25
      src/map/guild.c
  11. 1 0
      src/map/guild.h
  12. 10 21
      src/map/intif.c
  13. 1 1
      src/map/mail.c
  14. 61 135
      src/map/map.c
  15. 3 9
      src/map/map.h
  16. 44 31
      src/map/party.c
  17. 1 0
      src/map/party.h
  18. 49 122
      src/map/pc.c
  19. 2 3
      src/map/pc.h
  20. 0 6
      src/map/status.c
  21. 0 9
      src/map/storage.c
  22. 0 3
      src/map/trade.c
  23. 51 55
      src/map/unit.c
  24. 3 0
      src/map/unit.h

+ 11 - 0
Changelog-Trunk.txt

@@ -3,6 +3,17 @@ Date	Added
 AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
+2007/02/21
+	* Restructured the login mechanism of the map-server. The goal was to make
+	  sure players are not found  in the different dbs of the map while the
+	  player has not yet been fully authed or while it is quitting, to avoid the
+	  rest of the code from accessing and modifying it. It is a rather extensive
+	  change, and I only had time to test the basic functionality, so if use with
+	  care and report any bugs found.
+	* Replaced player states auth, waiting_disconnect, finalsave with active,
+	  and removed states party_sent/guild_sent.
+	* Removed several of the old login methods/constants/player states which
+	  are no longer needed due to this cleanup. [Skotlex]
 2007/02/19
 	* Rev. 12219 Fixed a typo in /map/clif.c (acount_id -> account_id) [L0ne_W0lf]
 	* Forgotten update to the definition of script command 'input'.

+ 1 - 0
src/char/int_guild.c

@@ -1004,6 +1004,7 @@ int mapif_parse_CreateGuild(int fd, int account_id, char *name, struct guild_mem
 	// ここでギルド情報計算が必要と思われる
 	g->max_member = 16;
 	g->average_lv = master->lv;
+	g->connect_member = 1;
 	for(i = 0; i < MAX_GUILDSKILL; i++)
 		g->skill[i].id=i + GD_SKILLBASE;
 

+ 1 - 1
src/char/inter.c

@@ -611,7 +611,7 @@ int mapif_parse_RegistryRequest(int fd)
 		mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6));
 	//Ask Login Server for Account2 values.
 	if (RFIFOB(fd,10))
-		request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2);
+		request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6));
 	return 1;
 }
 

+ 2 - 0
src/char_sql/int_guild.c

@@ -1282,6 +1282,8 @@ int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member
 	// Initialize guild property
 	g->max_member=16;
 	g->average_lv=master->lv;
+	g->connect_member=1;
+
 	for(i=0;i<MAX_GUILDSKILL;i++)
 		g->skill[i].id=i + GD_SKILLBASE;
 	g->guild_id= -1; //Request to create guild.

+ 1 - 1
src/char_sql/inter.c

@@ -786,7 +786,7 @@ int mapif_parse_RegistryRequest(int fd)
 	//Load Account Registry
 	if (RFIFOB(fd,11)) mapif_account_reg_reply(fd,RFIFOL(fd,2),RFIFOL(fd,6),2);
 	//Ask Login Server for Account2 values.
-	if (RFIFOB(fd,10)) request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6)-2);
+	if (RFIFOB(fd,10)) request_accreg2(RFIFOL(fd,2),RFIFOL(fd,6));
 	return 1;
 }
 

+ 1 - 1
src/map/atcommand.c

@@ -3533,7 +3533,7 @@ int atcommand_doommap(const int fd, struct map_session_data* sd, const char* com
  *------------------------------------------*/
 static void atcommand_raise_sub(struct map_session_data* sd)
 {
-	if (!sd->state.auth || !status_isdead(&sd->bl))
+	if (!status_isdead(&sd->bl))
 		return;
 
 	if(!status_revive(&sd->bl, 100, 100))

+ 212 - 105
src/map/chrif.c

@@ -8,6 +8,7 @@
 #include "../common/nullpo.h"
 #include "../common/showmsg.h"
 #include "../common/strlib.h"
+#include "../common/ers.h"
 
 #include "map.h"
 #include "battle.h"
@@ -25,7 +26,8 @@
 #include <sys/types.h>
 #include <time.h>
 
-DBMap* auth_db; // int id -> struct auth_node*
+static struct eri *auth_db_ers; //For reutilizing player login structures.
+static DBMap* auth_db; // int id -> struct auth_node*
 
 static const int packet_len_table[0x3d] = { // U - used, F - free
 	60, 3,-1,27,10,-1, 6,-1,	// 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
@@ -101,7 +103,77 @@ int other_mapserver_count=0; //Holds count of how many other map servers are onl
 //This define should spare writing the check in every function. [Skotlex]
 #define chrif_check(a) { if(!chrif_isconnected()) return a; }
 
+struct auth_node* chrif_search(int account_id) {
+	return idb_get(auth_db, account_id);
+}
+
+struct auth_node* chrif_auth_check(int account_id, int char_id, enum sd_state state) {
+	struct auth_node *node = chrif_search(account_id);
+	return (node && node->char_id == char_id && node->state == state)?node:NULL;
+}
+
+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)))
+	{
+		if (node->fd && session[node->fd] && node->sd &&
+			session[node->fd]->session_data == node->sd)
+			session[node->fd]->session_data = NULL;
+		if (node->char_dat) aFree(node->char_dat);
+		if (node->sd) aFree(node->sd);
+		ers_free(auth_db_ers, node);
+		idb_remove(auth_db,account_id);
+		return true;
+	}
+	return false;
+}
+
+//Moves the sd character to the auth_db structure.
+static bool chrif_sd_to_auth(TBL_PC* sd, enum sd_state state)
+{
+	struct auth_node *node;
+	if (chrif_search(sd->status.account_id))
+		return false; //Already exists?
+
+	node = ers_alloc(auth_db_ers, struct auth_node);
+	memset(node, 0, sizeof(struct auth_node));
+	node->account_id = sd->status.account_id;
+	node->char_id = sd->status.char_id;
+	node->login_id1 = sd->login_id1;
+	node->login_id2 = sd->login_id2;
+	node->sex = sd->status.sex;
+	node->fd = sd->fd;
+	node->sd = sd;	//Data from logged on char.
+	node->node_created = gettick(); //timestamp for node timeouts
+	node->state = state;
+
+	sd->state.active = 0;
+	idb_put(auth_db, node->account_id, node);
+	return true;
+}
+
+static bool chrif_auth_logout(TBL_PC* sd, enum sd_state state)
+{
+	if(sd->fd && state == ST_LOGOUT)
+  	{	//Disassociate player, and free it after saving ack returns. [Skotlex]
+		//fd info must not be lost for ST_MAPCHANGE as a final packet needs to be sent to the player.
+		if (session[sd->fd])
+			session[sd->fd]->session_data = NULL;
+		sd->fd = 0;
+	}
+	return chrif_sd_to_auth(sd, state);
+}
 
+bool chrif_auth_finished(TBL_PC* sd)
+{
+	struct auth_node *node= chrif_search(sd->status.account_id);
+	if (node && node->sd == sd && node->state == ST_LOGIN) {
+		node->sd = NULL;
+		chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
+		return true;
+	}
+	return false;
+}
 // sets char-server's user id
 void chrif_setuserid(char *id)
 {
@@ -165,15 +237,17 @@ int chrif_save(struct map_session_data *sd, int flag)
 
 	if (!flag) //The flag check is needed to prevent 'nosave' taking effect when a jailed player logs out.
 		pc_makesavestatus(sd);
+	
+	if (flag && sd->state.active) //Store player data which is quitting.
+	{
+		//FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
+		if (chrif_isconnected()) chrif_save_scdata(sd);
+		chrif_auth_logout(sd, flag==1?ST_LOGOUT:ST_MAPCHANGE);
+	}
 
 	if(!chrif_isconnected())
-  	{
-		if (flag) sd->state.finalsave = 1; //Will save character on reconnect.
-		return -1;
-	}
+		return -1; //Character is saved on reconnect.
 
-	if (sd->state.finalsave)
-		return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
 	//For data sync
 	if (sd->state.storage_flag == 1)
 		storage_storage_save(sd->status.account_id, flag);
@@ -198,11 +272,13 @@ int chrif_save(struct map_session_data *sd, int flag)
 	memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
 	WFIFOSET(char_fd, WFIFOW(char_fd,2));
 
+
+	if(sd->status.pet_id > 0 && sd->pd)
+		intif_save_petdata(sd->status.account_id,&sd->pd->pet);
+
 	if (sd->hd && merc_is_hom_active(sd->hd))
 		merc_save(sd->hd);
 
-	if (flag)
-		sd->state.finalsave = 1; //Mark the last save as done.
 	return 0;
 }
 
@@ -271,40 +347,38 @@ int chrif_removemap(int fd)
 }
 
 // received after a character has been "final saved" on the char-server
-int chrif_save_ack(int fd)
+static void chrif_save_ack(int fd)
 {
-	map_quit_ack(RFIFOL(fd,2), RFIFOL(fd,6));
-	return 0;
+	chrif_auth_delete(RFIFOL(fd,2), RFIFOL(fd,6), ST_LOGOUT);
 }
 
 // request to move a character between mapservers
-int chrif_changemapserver(struct map_session_data* sd, short map, int x, int y, uint32 ip, uint16 port)
+int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port)
 {
 	nullpo_retr(-1, sd);
 
-	chrif_check(-1);
-
 	if (other_mapserver_count < 1)
 	{	//No other map servers are online!
 		clif_authfail_fd(sd->fd, 0);
 		return -1;
 	}
 
+	chrif_check(-1);
+
 	WFIFOHEAD(char_fd,35);
 	WFIFOW(char_fd, 0) = 0x2b05;
 	WFIFOL(char_fd, 2) = sd->bl.id;
 	WFIFOL(char_fd, 6) = sd->login_id1;
 	WFIFOL(char_fd,10) = sd->login_id2;
 	WFIFOL(char_fd,14) = sd->status.char_id;
-	WFIFOW(char_fd,18) = map;
-	WFIFOW(char_fd,20) = x;
-	WFIFOW(char_fd,22) = y;
+	WFIFOW(char_fd,18) = sd->mapindex;
+	WFIFOW(char_fd,20) = sd->bl.x;
+	WFIFOW(char_fd,22) = sd->bl.y;
 	WFIFOL(char_fd,24) = htonl(ip);
 	WFIFOW(char_fd,28) = htons(port);
 	WFIFOB(char_fd,30) = sd->status.sex;
 	WFIFOL(char_fd,31) = 0; // sd's IP, not used anymore
 	WFIFOSET(char_fd,35);
-
 	return 0;
 }
 
@@ -312,23 +386,18 @@ int chrif_changemapserver(struct map_session_data* sd, short map, int x, int y,
 /// R 2b06 <account_id>.L <login_id1>.L <login_id2>.L <char_id>.L <map_index>.W <x>.W <y>.W <ip>.L <port>.W
 int chrif_changemapserverack(int account_id, int login_id1, int login_id2, int char_id, short map_index, short x, short y, uint32 ip, uint16 port)
 {
-	struct map_session_data *sd;
-	sd = map_id2sd(account_id);
-
-	if (sd == NULL || sd->status.char_id != char_id)
+	struct auth_node *node;
+	if (!(node=chrif_auth_check(account_id, char_id, ST_MAPCHANGE)))
 		return -1;
 
-	if (login_id1 == 1) { //FIXME: charserver says '0'! [ultramage]
+	if (!login_id1) {
 		ShowError("map server change failed.\n");
-		clif_authfail_fd(sd->fd, 0);
-		return 0;
-	}
-
-	clif_changemapserver(sd, map_index, x, y, ntohl(ip), ntohs(port));
+		clif_authfail_fd(node->fd, 0);
+	} else
+		clif_changemapserver(node->sd, map_index, x, y, ntohl(ip), ntohs(port));
 
-	//Player has been saved already, remove him from memory. [Skotlex]	
-	map_quit(sd);
-	map_quit_ack(sd->status.account_id, sd->status.char_id);
+	//Player has been saved already, remove him from memory. [Skotlex]
+	chrif_auth_delete(account_id, char_id, ST_MAPCHANGE);
 	return 0;
 }
 
@@ -359,6 +428,36 @@ int chrif_connectack(int fd)
 
 	return 0;
 }
+static int chrif_reconnect(DBKey key,void *data,va_list ap)
+{
+	struct auth_node *node=(struct auth_node*)data;
+	switch (node->state) {
+	case ST_LOGIN:
+		if (node->sd && node->char_dat == NULL)
+		{	//Since there is no way to request the char auth, make it fail.
+			pc_authfail(node->sd);
+			chrif_char_offline(node->sd);
+			chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN);
+		}
+		break;
+	case ST_LOGOUT:
+		//Re-send final save
+		chrif_save(node->sd, 1);
+		break;
+	case ST_MAPCHANGE:
+		{	//Re-send map-change request.
+		struct map_session_data *sd = node->sd;
+		uint32 ip;
+		uint16 port;
+		if(map_mapname2ipport(sd->mapindex,&ip,&port)==0)
+			chrif_changemapserver(sd, ip, port);
+		else //too much lag/timeout is the closest explanation for this error.
+			clif_authfail_fd(sd->fd, 3);
+		break;
+		}
+	}
+	return 0;
+}
 
 /*==========================================
  *
@@ -378,7 +477,7 @@ int chrif_sendmapack(int fd)
 	send_users_tochar();
 	
 	//Re-save any storages that were modified in the disconnection time. [Skotlex]
-	do_reconnect_map();
+	auth_db->foreach(auth_db,chrif_reconnect);
 	do_reconnect_storage();
 
 	return 0;
@@ -406,30 +505,31 @@ int chrif_scdata_request(int account_id, int char_id)
  *------------------------------------------*/
 void chrif_authreq(struct map_session_data *sd)
 {
-	struct auth_node *auth_data;
-	auth_data=idb_get(auth_db, sd->bl.id);
-
-	if(auth_data) {
-		if(auth_data->char_dat &&
-			auth_data->account_id== sd->bl.id &&
-			auth_data->login_id1 == sd->login_id1)
-		{	//auth ok
-			pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat);
-		} else { //auth failed
-			pc_authfail(sd);
-			chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+	struct auth_node *node= chrif_search(sd->bl.id);
+
+	if(!node) {
+		//data from char server has not arrived yet.
+		chrif_sd_to_auth(sd, ST_LOGIN);
+		return;
+	}
+
+	if(node->state == ST_LOGIN &&
+		node->char_dat &&
+		node->account_id== sd->status.account_id &&
+		node->login_id1 == sd->login_id1)
+	{	//auth ok
+		if (!pc_authok(sd, node->login_id2, node->connect_until_time, node->char_dat))
+			chrif_auth_delete(sd->status.account_id, sd->status.char_id, ST_LOGIN);
+		else {
+			//char_dat no longer needed, but player auth is not completed yet.
+			aFree(node->char_dat);
+			node->char_dat = NULL;
+			node->sd = sd;
 		}
-		if (auth_data->char_dat)
-			aFree(auth_data->char_dat);
-		idb_remove(auth_db, sd->bl.id);
-	} else { //data from char server has not arrived yet.
-		auth_data = aCalloc(1,sizeof(struct auth_node));
-		auth_data->sd = sd;
-		auth_data->fd = sd->fd;
-		auth_data->account_id = sd->bl.id;
-		auth_data->login_id1 = sd->login_id1;
-		auth_data->node_created = gettick();
-		idb_put(auth_db, sd->bl.id, auth_data);
+	} else { //auth failed
+		pc_authfail(sd);
+		chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+		chrif_auth_delete(sd->status.account_id, sd->status.char_id, ST_LOGIN);
 	}
 	return;
 }
@@ -437,65 +537,69 @@ void chrif_authreq(struct map_session_data *sd)
 //character selected, insert into auth db
 void chrif_authok(int fd)
 {
-	struct auth_node *auth_data;
+	struct auth_node *node;
+	int account_id = RFIFOL(fd, 4);
+	struct mmo_charstatus *status = (struct mmo_charstatus *)RFIFOP(fd, 20);
+	int char_id = status->char_id;
 	TBL_PC* sd;
 	//Check if we don't already have player data in our server
-	//(prevents data that is to be saved from being overwritten by
-	//this received status data if this auth is later successful) [Skotlex]
-	if ((sd = map_id2sd(RFIFOL(fd, 4))) != NULL)
-	{
-		struct mmo_charstatus *status = (struct mmo_charstatus *)RFIFOP(fd, 20);
-		//Auth check is because this could be the very same sd that is waiting char-server authorization.
-		if (sd->state.auth && sd->status.char_id == status->char_id)
-			return;
-	}
+	//Causes problems if the currently connected player tries to quit or this data belongs to an already connected player which is trying to re-auth.
+	if ((sd = map_id2sd(account_id)) != NULL)
+		return;
 	
-	if ((auth_data =idb_get(auth_db, RFIFOL(fd, 4))) != NULL)
+	if ((node = chrif_search(account_id)))
 	{	//Is the character already awaiting authorization?
-		if (auth_data->sd)
+		if (node->state == ST_LOGIN && node->sd)
 		{
-			//First, check to see if the session data still exists (avoid dangling pointers)
-			if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd)
-			{	
-				if (auth_data->char_dat == NULL &&
-					auth_data->account_id == RFIFOL(fd, 4) &&
-					auth_data->login_id1 == RFIFOL(fd, 8))
-				{ //Auth Ok
-					pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20));
-				} else { //Auth Failed
-					pc_authfail(auth_data->sd);
-					chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already.
-				}
-			} //else: Character no longer exists, just go through.
+			sd = node->sd;
+			if(node->char_dat == NULL &&
+				node->account_id == account_id &&
+				node->char_id == char_id &&
+				node->login_id1 == RFIFOL(fd, 8))
+			{ //Auth Ok
+				if (!pc_authok(sd, RFIFOL(fd, 16), RFIFOL(fd, 12), status))
+					chrif_auth_delete(account_id, char_id, ST_LOGIN);
+			} else { //Auth Failed
+				pc_authfail(sd);
+				chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+				chrif_auth_delete(account_id, char_id, ST_LOGIN);
+			}
 		}
-		//Delete the data of this node...
-		if (auth_data->char_dat)
-			aFree (auth_data->char_dat);
-		idb_remove(auth_db, RFIFOL(fd, 4));
+		//Otherwise discard the entry received as we already have information.
 		return;
 	}
-	// Awaiting for client to connect.
-	auth_data = (struct auth_node *)aCalloc(1,sizeof(struct auth_node));
-	auth_data->char_dat = (struct mmo_charstatus *) aMalloc(sizeof(struct mmo_charstatus));
 
-	auth_data->account_id=RFIFOL(fd, 4);
-	auth_data->login_id1=RFIFOL(fd, 8);
-	auth_data->connect_until_time=RFIFOL(fd, 12);
-	auth_data->login_id2=RFIFOL(fd, 16);
-	memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus));
-	auth_data->node_created=gettick();
-	idb_put(auth_db, RFIFOL(fd, 4), auth_data);
+	// Awaiting for client to connect.
+	node = ers_alloc(auth_db_ers, struct auth_node);
+	memset(node, 0, sizeof(struct auth_node));
+	node->char_dat = (struct mmo_charstatus *) aMalloc(sizeof(struct mmo_charstatus));
+
+	node->account_id=account_id;
+	node->char_id=char_id;
+	node->login_id1=RFIFOL(fd, 8);
+	node->connect_until_time=RFIFOL(fd, 12);
+	node->login_id2=RFIFOL(fd, 16);
+	memcpy(node->char_dat,status,sizeof(struct mmo_charstatus));
+	node->node_created=gettick();
+	idb_put(auth_db, account_id, node);
 }
 
 int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
 {
 	struct auth_node *node=(struct auth_node*)data;
 
-	if(DIFF_TICK(gettick(),node->node_created)>30000) {
-		ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id);
-		if (node->char_dat)
-			aFree(node->char_dat);
-		db_remove(auth_db, key);
+	if(DIFF_TICK(gettick(),node->node_created)>60000) {
+		switch (node->state)
+		{
+		case ST_LOGOUT:
+			//Re-save attempt (->sd should never be null here).
+			chrif_save(node->sd, 1);
+			break;
+		default:
+			//Clear data. any connected players should have timed out by now.
+			chrif_auth_delete(node->account_id, node->char_id, node->state);
+			break;
+		}
 		return 1;
 	}
 	return 0;
@@ -503,11 +607,11 @@ int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
 
 int auth_db_cleanup(int tid, unsigned int tick, int id, int data)
 {
+	if(!chrif_isconnected()) return 0;
 	auth_db->foreach(auth_db, auth_db_cleanup_sub);
 	return 0;
 }
 
-
 /*==========================================
  *
  *------------------------------------------*/
@@ -1010,9 +1114,6 @@ int chrif_save_scdata(struct map_session_data *sd)
 	struct status_change *sc = &sd->sc;
 	const struct TimerData *timer;
 
-	if (sd->state.finalsave) //Character was already saved?
-		return -1;
-	
 	chrif_check(-1);
 	tick = gettick();
 	
@@ -1428,6 +1529,9 @@ int auth_db_final(DBKey k,void *d,va_list ap)
 	struct auth_node *node=(struct auth_node*)d;
 	if (node->char_dat)
 		aFree(node->char_dat);
+	if (node->sd)
+		aFree(node->sd);
+	ers_free(auth_db_ers, node);
 	return 0;
 }
 
@@ -1438,7 +1542,9 @@ int do_final_chrif(void)
 {
 	if (char_fd > 0)
 		do_close(char_fd);
+
 	auth_db->destroy(auth_db, auth_db_final);
+	ers_destroy(auth_db_ers);
 	return 0;
 }
 
@@ -1447,7 +1553,8 @@ int do_final_chrif(void)
  *------------------------------------------*/
 int do_init_chrif(void)
 {
-	auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
+	auth_db = idb_alloc(DB_OPT_BASE);
+	auth_db_ers = ers_new(sizeof(struct auth_node));
 
 	add_timer_func_list(check_connect_char_server, "check_connect_char_server");
 	add_timer_func_list(ping_char_server, "ping_char_server");

+ 12 - 4
src/map/chrif.h

@@ -7,12 +7,15 @@
 #include "../common/cbasetypes.h"
 #include <time.h>
 
-struct auth_node{
-	int account_id, login_id1, login_id2, sex, fd;
+enum sd_state { ST_LOGIN, ST_LOGOUT, ST_MAPCHANGE };
+struct auth_node {
+	int account_id, char_id;
+	int login_id1, login_id2, sex, fd;
 	time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
 	struct map_session_data *sd;	//Data from logged on char.
 	struct mmo_charstatus *char_dat;	//Data from char server.
-	unsigned int node_created; //For node auto-deleting
+	unsigned int node_created; //timestamp for node timeouts
+	enum sd_state state; //To track whether player was login in/out or changing maps.	
 };
 
 void chrif_setuserid(char* id);
@@ -26,12 +29,17 @@ int chrif_isconnected(void);
 extern int chrif_connected;
 extern int other_mapserver_count;
 
+struct auth_node* chrif_search(int account_id);
+struct auth_node* chrif_auth_check(int account_id, int char_id, enum sd_state state);
+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_authok(int fd);
 int chrif_scdata_request(int account_id, int char_id);
 int chrif_save(struct map_session_data* sd, int flag);
 int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip);
-int chrif_changemapserver(struct map_session_data* sd, short map, int x, int y, uint32 ip, uint16 port);
+int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port);
 
 int chrif_searchcharid(int char_id);
 int chrif_changegm(int id,const char *pass,int len);

+ 90 - 93
src/map/clif.c

@@ -175,7 +175,7 @@ int clif_countusers(void)
 	for(i = 0; i < fd_max; i++) {
 		if (session[i] && session[i]->func_parse == clif_parse &&
 			(sd = (struct map_session_data*)session[i]->session_data) &&
-		  	sd->state.auth && !(battle_config.hide_GM_session && pc_isGM(sd)))
+		  	sd->state.active && !(battle_config.hide_GM_session && pc_isGM(sd)))
 			users++;
 	}
 	return users;
@@ -195,7 +195,7 @@ int clif_foreachclient(int (*func)(struct map_session_data*, va_list),...) //rec
 	for(i = 0; i < fd_max; i++) {
 		if ( session[i] && session[i]->func_parse == clif_parse) {
 			sd = (struct map_session_data*)session[i]->session_data;
-			if ( sd && sd->state.auth && !sd->state.waitingdisconnect )
+			if ( sd && sd->state.active )
 				func(sd, ap);
 		}
 	}
@@ -246,20 +246,23 @@ int clif_send_sub(struct block_list *bl, va_list ap)
 	break;
 	}
 
-	if (session[fd] != NULL) {
-		WFIFOHEAD(fd, len);
-		if (WFIFOP(fd,0) == buf) {
-			ShowError("WARNING: Invalid use of clif_send function\n");
-			ShowError("         Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
-			ShowError("         Please correct your code.\n");
-			// don't send to not move the pointer of the packet for next sessions in the loop
-			WFIFOSET(fd,0);//## TODO is this ok?
-		} else {
-			if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
-				memcpy(WFIFOP(fd,0), buf, len);
-				WFIFOSET(fd,len);
-			}
-		}
+	if (session[fd] == NULL)
+		return 0;
+
+	WFIFOHEAD(fd, len);
+	if (WFIFOP(fd,0) == buf) {
+		ShowError("WARNING: Invalid use of clif_send function\n");
+		ShowError("         Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
+		ShowError("         Please correct your code.\n");
+		// don't send to not move the pointer of the packet for next sessions in the loop
+		//WFIFOSET(fd,0);//## TODO is this ok?
+		//NO. It is not ok. There is the chance WFIFOSET actually sends the buffer data, and shifts elements around, which will corrupt the buffer.
+		return 0;
+	}
+
+	if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+		memcpy(WFIFOP(fd,0), buf, len);
+		WFIFOSET(fd,len);
 	}
 
 	return 0;
@@ -285,13 +288,13 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 	case ALL_CLIENT: //All player clients.
 		for (i = 0; i < fd_max; i++) {
 			if (session[i] && session[i]->func_parse == clif_parse &&
-				(sd = (struct map_session_data *)session[i]->session_data) != NULL&&
-				sd->state.auth) {
-				if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
-					WFIFOHEAD(i, len);
-					memcpy(WFIFOP(i,0), buf, len);
-					WFIFOSET(i,len);
-				}
+				(sd = (struct map_session_data *)session[i]->session_data) != NULL &&
+				sd->state.active &&
+				packet_db[sd->packet_ver][RBUFW(buf,0)].len)
+			{ // packet must exist for the client version
+				WFIFOHEAD(i, len);
+				memcpy(WFIFOP(i,0), buf, len);
+				WFIFOSET(i,len);
 			}
 		}
 		break;
@@ -299,12 +302,12 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 		for(i = 0; i < fd_max; i++) {
 			if (session[i] && session[i]->func_parse == clif_parse &&
 				(sd = (struct map_session_data*)session[i]->session_data) != NULL &&
-				sd->state.auth && sd->bl.m == bl->m) {
-				if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
-					WFIFOHEAD(i,len);
-					memcpy(WFIFOP(i,0), buf, len);
-					WFIFOSET(i,len);
-				}
+				sd->state.active && sd->bl.m == bl->m &&
+				packet_db[sd->packet_ver][RBUFW(buf,0)].len)
+			{ // packet must exist for the client version
+				WFIFOHEAD(i,len);
+				memcpy(WFIFOP(i,0), buf, len);
+				WFIFOSET(i,len);
 			}
 		}
 		break;
@@ -350,11 +353,11 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 		for(i=1; i<fd_max; i++) {
 			if (session[i] && session[i]->func_parse == clif_parse &&
 				(sd = (struct map_session_data*)session[i]->session_data) != NULL &&
-				sd->state.mainchat && !sd->chatID && (fd=sd->fd))
+				sd->state.active && sd->state.mainchat && !sd->chatID)
 			{
-				WFIFOHEAD(fd,len);
-				memcpy(WFIFOP(fd,0), buf, len);
-				WFIFOSET(fd, len);
+				WFIFOHEAD(i,len);
+				memcpy(WFIFOP(i,0), buf, len);
+				WFIFOSET(i, len);
 			}
 		}
 		break;
@@ -376,12 +379,9 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 				if( (sd = p->data[i].sd) == NULL )
 					continue;
 
-				if( !(fd=sd->fd) || fd <= 0 || fd >= fd_max )
+				if( !(fd=sd->fd) )
 					continue;
 
-				if( session[fd] == NULL || sd->state.auth == 0 || session[fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER )
-					continue;
-				
 				if( sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS) )
 					continue;
 				
@@ -404,13 +404,12 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 
 				if (session[i] && session[i]->func_parse == clif_parse &&
 					(sd = (struct map_session_data*)session[i]->session_data) != NULL &&
-				  	sd->state.auth && (fd=sd->fd) && sd->partyspy == p->party.party_id)
-		  		{
-					if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
-						WFIFOHEAD(fd,len);
-						memcpy(WFIFOP(fd,0), buf, len);
-						WFIFOSET(fd,len);
-					}
+				  	sd->state.active && sd->partyspy == p->party.party_id &&
+					packet_db[sd->packet_ver][RBUFW(buf,0)].len)
+				{ // packet must exist for the client version
+					WFIFOHEAD(i,len);
+					memcpy(WFIFOP(i,0), buf, len);
+					WFIFOSET(i,len);
 				}
 			}
 		}
@@ -423,7 +422,7 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 		for (i = 0; i < fd_max; i++) {
 			if (session[i] && session[i]->func_parse == clif_parse &&
 				(sd = (struct map_session_data *)session[i]->session_data) != NULL &&
-				sd->state.auth && sd->duel_group == x0) {
+				sd->state.active && sd->duel_group == x0) {
 				if (type == DUEL_WOS && bl->id == sd->bl.id)
 					continue;
 				if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { 
@@ -460,10 +459,7 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 			for(i = 0; i < g->max_member; i++) {
 				if( (sd = g->member[i].sd) != NULL )
 				{
-					if( !(fd=sd->fd) || fd <= 0 || fd >= fd_max )
-						continue;
-					
-					if( session[fd] == NULL || sd->state.auth == 0 || session[fd]->session_data == NULL || sd->packet_ver > MAX_PACKET_VER )
+					if( !(fd=sd->fd) )
 						continue;
 					
 					if( sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS) )
@@ -488,12 +484,12 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target
 			for (i = 1; i < fd_max; i++){ // guildspy [Syrus22]
 				if (session[i] && session[i]->func_parse == clif_parse &&
 					(sd = (struct map_session_data*)session[i]->session_data) != NULL &&
-				  	sd->state.auth && (fd=sd->fd) && sd->guildspy == g->guild_id) {
-					if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
-						WFIFOHEAD(fd,len);
-						memcpy(WFIFOP(fd,0), buf, len);
-						WFIFOSET(fd,len);
-					}
+				  	sd->state.active && sd->guildspy == g->guild_id &&
+					packet_db[sd->packet_ver][RBUFW(buf,0)].len)
+				{ // packet must exist for the client version
+					WFIFOHEAD(fd,len);
+					memcpy(WFIFOP(fd,0), buf, len);
+					WFIFOSET(fd,len);
 				}
 			}
 		}
@@ -541,6 +537,7 @@ int clif_authok(struct map_session_data *sd)
  *  3 - timeout/too much lag -> MsgStringTable[241]
  *  4 - server full -> MsgStringTable[264]
  *  5 - underaged -> MsgStringTable[305]
+ *  8 - Server sill recognizes last connection -> MsgStringTable[441]
  *  9 - too many connections from this ip -> MsgStringTable[529]
  *  10 - out of available time paid for -> MsgStringTable[530]
  *  15 - disconnected by a GM -> if( servicetype == taiwan ) MsgStringTable[579]
@@ -1038,7 +1035,7 @@ int clif_weather(int m)
 	for(i = 0; i < fd_max; i++) {
 		if (session[i] && session[i]->func_parse == clif_parse &&	
 			(sd = session[i]->session_data) != NULL &&
-		  	sd->state.auth && sd->bl.m == m) {
+		  	sd->state.active && sd->bl.m == m) {
 			clif_weather_check(sd);
 		}
 	}
@@ -1327,8 +1324,7 @@ static int clif_delayquit(int tid, unsigned int tick, int id, int data)
  *------------------------------------------*/
 void clif_quitsave(int fd,struct map_session_data *sd)
 {
-	if (sd->state.waitingdisconnect || //Was already waiting to be disconnected.
-		!battle_config.prevent_logout ||
+	if (!battle_config.prevent_logout ||
 	  	DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
 		map_quit(sd);
 	else if (sd->fd)
@@ -1356,10 +1352,9 @@ static int clif_waitclose(int tid, unsigned int tick, int id, int data)
  *------------------------------------------*/
 void clif_setwaitclose(int fd)
 {
-	struct map_session_data *sd;
 
 	// if player is not already in the game (double connection probably)
-	if ((sd = (struct map_session_data*)session[fd]->session_data) == NULL) {
+	if (session[fd]->session_data == NULL) {
 		// limited timer, just to send information.
 		add_timer(gettick() + 1000, clif_waitclose, fd, 0);
 	} else
@@ -3775,7 +3770,7 @@ void clif_01ac(struct block_list* bl)
 
 	sd=va_arg(ap,struct map_session_data*);
 
-	if (sd == NULL || !sd->fd || session[sd->fd] == NULL)
+	if (sd == NULL || !sd->fd)
 		return 0;
 
 	switch(bl->type){
@@ -5614,7 +5609,7 @@ int clif_hpmeter(struct map_session_data *sd)
 	for (i = 0; i < fd_max; i++) {
 		if (session[i] && session[i]->func_parse == clif_parse &&	
 			(sd2 = (struct map_session_data*)session[i]->session_data) &&
-			sd != sd2 && sd2->state.auth) {
+			sd != sd2 && sd2->state.active) {
 			if (sd2->bl.m != sd->bl.m || 
 				sd2->bl.x < x0 || sd2->bl.y < y0 ||
 				sd2->bl.x > x1 || sd2->bl.y > y1 ||
@@ -7616,12 +7611,14 @@ static int clif_guess_PacketVer(int fd, int get_previous, int *error)
  *------------------------------------------*/
 void clif_parse_WantToConnection(int fd, TBL_PC* sd)
 {
+	struct block_list* bl;
+	struct auth_node* node;
 	int cmd, account_id, char_id, login_id1, sex;
 	unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
 	int packet_ver;	// 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
 
 	if (sd) {
-		ShowError("clif_parse_WantToConnection : invalid request (character already logged in)?\n");
+		ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n");
 		return;
 	}
 
@@ -7646,13 +7643,12 @@ void clif_parse_WantToConnection(int fd, TBL_PC* sd)
 		WFIFOSET(fd,packet_len(0x6a));
 		clif_setwaitclose(fd);
 		return;
-	} else if( map_knowsaccount(account_id) )
-	{// double login
-		sd = map_id2sd(account_id);
-		if( sd  && sd->state.autotrade )
-			map_quit(sd);// kick autotrading character
-		else
-			ShowError("clif_parse_WantToConnection: double login attempt AID/CID: %d/%d, rejecting...\n", account_id, char_id);
+	}
+
+	//Check for double login.
+	bl = map_id2bl(account_id);
+	if(bl && bl->type != BL_PC) {
+		ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id);
 		WFIFOHEAD(fd,packet_len(0x6a));
 		WFIFOW(fd,0) = 0x6a;
 		WFIFOB(fd,2) = 3; // Rejected by server
@@ -7660,19 +7656,22 @@ void clif_parse_WantToConnection(int fd, TBL_PC* sd)
 		clif_setwaitclose(fd);
 		return;
 	}
-	else
-	{// packet version accepted
-		struct block_list* bl;
-		if( (bl=map_id2bl(account_id)) != NULL && bl->type != BL_PC )
-		{// non-player object already has that id
-			ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id);
-			WFIFOHEAD(fd,packet_len(0x6a));
-			WFIFOW(fd,0) = 0x6a;
-			WFIFOB(fd,2) = 3; // Rejected by server
-			WFIFOSET(fd,packet_len(0x6a));
-			clif_setwaitclose(fd);
-			return;
-		}
+
+	if (bl || 
+		((node=chrif_search(account_id)) && //An already existing node is valid only if it is for this login.
+			!(node->account_id == account_id && node->char_id == char_id && node->state == ST_LOGIN))) {
+		sd = BL_CAST(BL_PC, bl);
+		if (!sd)
+			;	//We have another char with the same account logging in/out.
+		else //Already connected player.
+		if (sd->fd)
+			clif_authfail_fd(sd->fd, 2); //someone else logged in
+		else
+		if(sd->state.autotrade)
+			map_quit(sd);// kick autotrading character
+		//Else do not kick character, it could be on its 10 sec penalty for Alt+F4
+		clif_authfail_fd(fd, 8); //Still recognizes last connection
+		return;
 	}
 
 	CREATE(sd, TBL_PC, 1);
@@ -7706,7 +7705,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 	if(sd->bl.prev != NULL)
 		return;
 	
-	if (!sd->state.auth)
+	if (!sd->state.active)
 	{	//Character loading is not complete yet!
 		//Let pc_reg_received reinvoke this when ready.
 		sd->state.connect_new = 0;
@@ -11652,12 +11651,13 @@ int clif_parse(int fd)
 				sd->fd = 0;
 				ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", (pc_isGM(sd))?"GM ":"", sd->status.name);
 			} else
-			if (sd->state.auth) {
+			if (sd->state.active) {
 				 // Player logout display [Valaris]
 				ShowInfo("%sCharacter '"CL_WHITE"%s"CL_RESET"' logged off.\n", (pc_isGM(sd))?"GM ":"", sd->status.name);
 				clif_quitsave(fd, sd);
 			} else {
-				ShowInfo("Player AID:%d/CID:%d (not authenticated) logged off.\n", sd->bl.id, sd->status.char_id);
+				//Unusual logout (during log on/off/map-changer procedure)
+				ShowInfo("Player AID:%d/CID:%d logged off.\n", sd->status.account_id, sd->status.char_id);
 				map_quit(sd);
 			}
 		} else {
@@ -11723,17 +11723,14 @@ int clif_parse(int fd)
 	if ((int)RFIFOREST(fd) < packet_len)
 		return 0; // not enough data received to form the packet
 
-	if (sd && sd->state.waitingdisconnect == 1) {
-		// 切断待ちの場合パケットを処理しない
-	} else
 	if (packet_db[packet_ver][cmd].func) {
 		if (sd && sd->bl.prev == NULL && packet_db[packet_ver][cmd].func != clif_parse_LoadEndAck)
 			; //Only valid packet when player is not on a map is the finish-loading packet.
 		else
-		if (sd
+		if ((sd && sd->state.active)
 			|| packet_db[packet_ver][cmd].func == clif_parse_WantToConnection
 			|| packet_db[packet_ver][cmd].func == clif_parse_debug
-		)	//Only execute the function when there's an sd (except for debug/wanttoconnect packets)
+		)	//Only execute the function when there's an active sd (except for debug/wanttoconnect packets)
 			packet_db[packet_ver][cmd].func(fd, sd);
 	}
 #if DUMP_UNKNOWN_PACKET
@@ -11750,7 +11747,7 @@ int clif_parse(int fd)
 			return 1;
 		} else {
 			time(&now);
-			if (sd && sd->state.auth) {
+			if (sd && sd->state.active) {
 				fprintf(fp, "%sPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n",
 					asctime(localtime(&now)), sd->status.account_id, sd->status.char_id, sd->status.name);
 			} else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified)
@@ -11779,7 +11776,7 @@ int clif_parse(int fd)
 			ShowMessage("%02X ", RFIFOB(fd,i));
 		}
 		ShowMessage("\n");
-		if (sd && sd->state.auth) {
+		if (sd && sd->state.active) {
 			if (sd->status.name != NULL)
 				ShowMessage("\nAccount ID %d, character ID %d, player name %s.\n",
 				sd->status.account_id, sd->status.char_id, sd->status.name);

+ 46 - 25
src/map/guild.c

@@ -76,7 +76,7 @@ static TBL_PC* guild_sd_check(int guild_id, int account_id, int char_id)
 {
 	TBL_PC* sd = map_id2sd(account_id);
 
-	if (!(sd && sd->status.char_id == char_id && sd->state.auth && !sd->state.waitingdisconnect))
+	if (!(sd && sd->status.char_id == char_id))
 		return NULL;
 
 	if (sd->status.guild_id != guild_id)
@@ -431,7 +431,6 @@ int guild_created(int account_id,int guild_id)
 	}
 	//struct guild *g;
 	sd->status.guild_id=guild_id;
-	sd->state.guild_sent=0;
 	clif_guild_created(sd,0);
 	if(battle_config.guild_emperium_check)
 		pc_delitem(sd,pc_search_inventory(sd,714),1,0);	// エンペリウム消耗
@@ -485,7 +484,6 @@ int guild_check_member(struct guild *g)
 		i = guild_getindex(g,sd->status.account_id,sd->status.char_id);
 		if (i < 0) {
 			sd->status.guild_id=0;
-			sd->state.guild_sent=0;
 			sd->guild_emblem_id=0;
 			ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
 		}
@@ -519,10 +517,12 @@ int guild_recv_info(struct guild *sg)
 	int i,bm,m;
 	struct eventlist *ev,*ev2;
 	struct map_session_data *sd;
+	bool guild_new = false;
 
 	nullpo_retr(0, sg);
 
 	if((g=idb_get(guild_db,sg->guild_id))==NULL){
+		guild_new = true;
 		g=(struct guild *)aCalloc(1,sizeof(struct guild));
 		idb_put(guild_db,sg->guild_id,g);
 		before=*sg;
@@ -538,7 +538,8 @@ int guild_recv_info(struct guild *sg)
 
 			//Also set the guild master flag.
 			sd->state.gmaster_flag = g;
-			clif_charnameupdate(sd); // [LuzZza]			
+			clif_charnameupdate(sd); // [LuzZza]
+			clif_guild_masterormember(sd);
 		}
 	}else
 		before=*g;
@@ -579,11 +580,10 @@ int guild_recv_info(struct guild *sg)
 		if( before.skill_point!=g->skill_point)
 			clif_guild_skillinfo(sd);	// スキル情報送信
 
-		if( sd->state.guild_sent==0){	// 未送信なら所属情報も送る
+		if( guild_new ){	// 未送信なら所属情報も送る
 			clif_guild_belonginfo(sd,g);
 			clif_guild_notice(sd,g);
 			sd->guild_emblem_id=g->emblem_id;
-			sd->state.guild_sent=1;
 		}
 	}
 
@@ -702,6 +702,34 @@ int guild_reply_invite(struct map_session_data* sd, int guild_id, int flag)
 
 	return 0;
 }
+
+//Invoked when a player joins.
+//- If guild is not in memory, it is requested
+//- Otherwise sd pointer is set up.
+//- Player must be authed and must belong to a guild before invoking this method
+void guild_member_joined(struct map_session_data *sd)
+{
+	struct guild* g;
+	int i;
+	g=guild_search(sd->status.guild_id);
+	if (!g) {
+		guild_request_info(sd->status.guild_id);
+		return;
+	}
+	if (strcmp(sd->status.name,g->master) == 0)
+	{	// set the Guild Master flag
+		sd->state.gmaster_flag = g;
+		// prevent Guild Skills from being used directly after relog
+		if( battle_config.guild_skill_relog_delay )
+			guild_block_skill(sd, 300000);
+	}
+	i = guild_getindex(g, sd->status.account_id, sd->status.char_id);
+	if (i == -1)
+		sd->status.guild_id = 0;
+	else
+		g->member[i].sd = sd;
+}
+
 // ギルドメンバが追加された
 int guild_member_added(int guild_id,int account_id,int char_id,int flag)
 {
@@ -730,9 +758,12 @@ int guild_member_added(int guild_id,int account_id,int char_id,int flag)
 	}
 
 		// 成功
-	sd->state.guild_sent = 0;
 	sd->status.guild_id = g->guild_id;
 	sd->guild_emblem_id = g->emblem_id;
+	//Packets which were sent in the previous 'guild_sent' implementation.
+	clif_guild_belonginfo(sd,g);
+	clif_guild_notice(sd,g);
+
 	//TODO: send new emblem info to others
 
 	if( sd2!=NULL )
@@ -836,7 +867,6 @@ int guild_member_leaved(int guild_id, int account_id, int char_id, int flag, con
 
 		sd->status.guild_id = 0;
 		sd->guild_emblem_id = 0;
-		sd->state.guild_sent = 0;
 		
 		clif_charnameupdate(sd); //Update display name [Skotlex]
 		//TODO: send emblem update to self and people around
@@ -850,14 +880,10 @@ int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 	
 	nullpo_retr(0, sd);
 		
-	if(!(g = guild_search(sd->status.guild_id)))
+	if(sd->status.guild_id <= 0)
 		return 0;
 
-	//Moved to place before intif_guild_memberinfoshort because
-	//If it's not a member, needn't send it's info to intif. [LuzZza]
-	guild_check_member(g);
-	
-	if(sd->status.guild_id <= 0)
+	if(!(g = guild_search(sd->status.guild_id)))
 		return 0;
 
 	intif_guild_memberinfoshort(g->guild_id,
@@ -872,15 +898,12 @@ int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 		return 0;
 	}
 	
-	if(sd->state.guild_sent)
-		return 0;
-
-	clif_guild_belonginfo(sd,g);
-	clif_guild_notice(sd,g);
-	
-	sd->state.guild_sent = 1;
-	sd->guild_emblem_id = g->emblem_id;
-	
+	if(sd->state.connect_new)
+	{	//Note that this works because it is invoked in parse_LoadEndAck before connect_new is cleared.
+		clif_guild_belonginfo(sd,g);
+		clif_guild_notice(sd,g);
+		sd->guild_emblem_id = g->emblem_id;
+	}
 	return 0;
 }
 
@@ -915,7 +938,6 @@ int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int onlin
 		if(sd && sd->status.char_id == char_id) {
 			sd->status.guild_id=0;
 			sd->guild_emblem_id=0;
-			sd->state.guild_sent=0;
 		}
 		ShowWarning("guild: not found member %d,%d on %d[%s]\n",	account_id,char_id,guild_id,g->name);
 		return 0;
@@ -1535,7 +1557,6 @@ int guild_broken(int guild_id,int flag)
 			if(sd->state.storage_flag == 2)
 				storage_guild_storage_quit(sd,1);
 			sd->status.guild_id=0;
-			sd->state.guild_sent=0;
 			clif_guild_broken(g->member[i].sd,0);
 			clif_charnameupdate(sd); // [LuzZza]
 		}

+ 1 - 0
src/map/guild.h

@@ -42,6 +42,7 @@ int guild_recv_info(struct guild *sg);
 int guild_npc_request_info(int guild_id,const char *ev);
 int guild_invite(struct map_session_data *sd,struct map_session_data *tsd);
 int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag);
+void guild_member_joined(struct map_session_data *sd);
 int guild_member_added(int guild_id,int account_id,int char_id,int flag);
 int guild_leave(struct map_session_data *sd,int guild_id,
 	int account_id,int char_id,const char *mes);

+ 10 - 21
src/map/intif.c

@@ -919,12 +919,16 @@ int intif_parse_Registers(int fd)
 	struct map_session_data *sd;
 	struct global_reg *reg;
 	int *qty;
-
-	if( (sd=map_id2sd(RFIFOL(fd,4)))==NULL)
-		return 1;
-
-	if (RFIFOB(fd,12) == 3 && sd->status.char_id != RFIFOL(fd,8))
-		return 1; //Character registry from another character.
+	int account_id = RFIFOL(fd,4), char_id = RFIFOL(fd,8);
+	struct auth_node *node = chrif_auth_check(account_id, char_id, ST_LOGIN);
+	if (node)
+		sd = node->sd;
+	else { //Normally registries should arrive for in log-in chars.
+		sd = map_id2sd(account_id);
+		if (sd && RFIFOB(fd,12) == 3 && sd->status.char_id != char_id)
+			sd = NULL; //Character registry from another character.
+	}
+	if (!sd) return 1;
 
 	flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1);
 
@@ -975,9 +979,6 @@ int intif_parse_LoadStorage(int fd)
 		return 1;
 	}
 
-	if (sd->state.finalsave)
-		return 1; //Player is already scheduled to leave the server.
-
 	stor = account2storage( RFIFOL(fd,4));
 
 	if (stor->storage_status == 1) { // Already open.. lets ignore this update
@@ -1434,9 +1435,6 @@ int intif_parse_Mail_inboxreceived(int fd)
 		return 1;
 	}
 
-	if (sd->state.finalsave)
-		return 1;
-
 	if (RFIFOW(fd,2) - 9 != sizeof(struct mail_data))
 	{
 		ShowError("intif_parse_Mail_inboxreceived: data size error %d %d\n", RFIFOW(fd,2) - 9, sizeof(struct mail_data));
@@ -1502,9 +1500,6 @@ int intif_parse_Mail_getattach(int fd)
 		return 1;
 	}
 
-	if (sd->state.finalsave)
-		return 1;
-
 	if (RFIFOW(fd,2) - 12 != sizeof(struct item))
 	{
 		ShowError("intif_parse_Mail_getattach: data size error %d %d\n", RFIFOW(fd,2) - 16, sizeof(struct item));
@@ -1546,9 +1541,6 @@ int intif_parse_Mail_delete(int fd)
 		return 1;
 	}
 
-	if (sd->state.finalsave)
-		return 1;
-
 	if (!failed)
 	{
 		int i;
@@ -1595,9 +1587,6 @@ int intif_parse_Mail_return(int fd)
 		return 1;
 	}
 
-	if( sd->state.finalsave )
-		return 1;
-
 	if( !fail )
 	{
 		int i;

+ 1 - 1
src/map/mail.c

@@ -153,7 +153,7 @@ int mail_openmail(struct map_session_data *sd)
 {
 	nullpo_retr(0,sd);
 
-	if( sd->state.finalsave == 1 || sd->state.storage_flag || sd->vender_id || sd->state.trading )
+	if( sd->state.storage_flag || sd->vender_id || sd->state.trading )
 		return 0;
 
 	clif_Mail_window(sd->fd, 0);

+ 61 - 135
src/map/map.c

@@ -102,7 +102,6 @@ static DBMap* mobid_db=NULL; // int id -> struct mob_data*
 static DBMap* map_db=NULL; // unsigned int mapindex -> struct map_data*
 static DBMap* nick_db=NULL; // int char_id -> struct charid2nick* (requested names of offline characters)
 static DBMap* charid_db=NULL; // int char_id -> struct map_session_data*
-static DBMap* quit_db=NULL; // int account_id -> struct map_session_data* (players that are quitting or changing map-server)
 
 static int map_users=0;
 static struct block_list *objects[MAX_FLOORITEM];
@@ -1540,117 +1539,76 @@ void map_deliddb(struct block_list *bl)
 	idb_remove(id_db,bl->id);
 }
 
-/// Returns true if the map server knows the account (to reject logins).
-bool map_knowsaccount(int account_id)
-{
-	return (map_id2sd(account_id) || idb_get(quit_db,account_id) ? true : false);
-}
-
 /*==========================================
- * PCのquit?理 map.c?分
- *
- * quit?理の主?が違うような?もしてきた
+ * Standard call when a player connection is closed.
  *------------------------------------------*/
 int map_quit(struct map_session_data *sd)
 {
-	struct map_session_data* sd2;
-	if(!sd->state.auth) { //Removing a player that hasn't even finished loading
-		TBL_PC *sd2 = map_id2sd(sd->status.account_id);
-		if (sd->pd) unit_free(&sd->pd->bl,-1);
-		if (sd->hd) unit_free(&sd->hd->bl,-1);
-		//Double login, let original do the cleanups below.
-		if (sd2 && sd2 != sd)
-			return 0;
-		map_deliddb(&sd->bl);
+	if(!sd->state.active) { //Removing a player that is not active.
+		struct auth_node *node = chrif_search(sd->status.account_id);
+		if (node && node->char_id == sd->status.char_id &&
+			node->state != ST_LOGOUT)
+			//Except when logging out, clear the auth-connect data immediately.
+			chrif_auth_delete(node->account_id, node->char_id, node->state);
+		//Non-active players should not have loaded any data yet (or it was cleared already) so no additional cleanups are needed.
 		return 0;
 	}
-	if(!sd->state.waitingdisconnect) {
-		if (sd->npc_timer_id != -1) //Cancel the event timer.
-			npc_timerevent_quit(sd);
-
-		npc_script_event(sd, NPCE_LOGOUT);
-		sd->state.waitingdisconnect = 1;
-		if (sd->pd) unit_free(&sd->pd->bl,0);
-		if (sd->hd) unit_free(&sd->hd->bl,0);
-		unit_free(&sd->bl,3);
-		chrif_save(sd,1);
-	} else { //Try to free some data, without saving anything (this could be invoked on map server change. [Skotlex]
-		if (sd->bl.prev != NULL)
-			unit_remove_map(&sd->bl, 0);
-		if (sd->pd && sd->pd->bl.prev != NULL)
-			unit_remove_map(&sd->pd->bl, 0);
-		if (sd->hd && sd->hd->bl.prev != NULL)
-			unit_remove_map(&sd->hd->bl, 0);
-	}
-
-	map_deliddb(&sd->bl);
-	if( (sd2=(struct map_session_data*)idb_put(quit_db, sd->status.account_id, sd)) )
-	{
-		ShowDebug("map_quit: Possible double login AID/CID: %d/%d AID/CID: %d/%d\n", sd2->status.account_id, sd2->status.char_id, sd->status.account_id, sd->status.char_id);
-		aFree(sd2);
-	}
 
-	if(sd->reg)
-	{	//Double logout already freed pointer fix... [Skotlex]
-		aFree(sd->reg);
-		sd->reg = NULL;
-		sd->reg_num = 0;
-	}
-	if(sd->regstr)
-	{
-		int i;
-		for( i = 0; i < sd->regstr_num; ++i )
-			if( sd->regstr[i].data )
-				aFree(sd->regstr[i].data);
-		aFree(sd->regstr);
-		sd->regstr = NULL;
-		sd->regstr_num = 0;
-	}
-	if (sd->st) {
-		if (sd->st->stack)
-			script_free_stack (sd->st->stack);
-		aFree(sd->st);
-		sd->st = NULL;
-		sd->npc_id = 0;
-	}
-	if(sd->fd)
-  	{	//Player will be free'd on save-ack. [Skotlex]
-		if (session[sd->fd])
-			session[sd->fd]->session_data = NULL;
-		sd->fd = 0;
-	}
-	return 0;
-}
-
-void map_quit_ack(int account_id, int char_id)
-{
-	struct map_session_data* sd = (struct map_session_data*)idb_get(quit_db,account_id);
-	if( sd )
-	{
-		if( sd->status.char_id != char_id )
-			ShowDebug("map_quit_ack: Possible double login AID/CID: %d/%d AID/CID: %d/%d\n", account_id, char_id, sd->status.account_id, sd->status.char_id);
-		else
-			idb_remove(quit_db,account_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;
+	if (sd->npc_timer_id != -1) //Cancel the event timer.
+		npc_timerevent_quit(sd);
+
+	npc_script_event(sd, NPCE_LOGOUT);
+
+	//Unit_free handles clearing the player related data, 
+	//map_quit handles extra specific data which is related to quitting normally
+	//(changing map-servers invokes unit_free but bypasses map_quit)
+	if(sd->sc.count) {
+		//Status that are not saved...
+		if(sd->sc.data[SC_SPURT])
+			status_change_end(&sd->bl,SC_SPURT,-1);
+		if(sd->sc.data[SC_BERSERK])
+			status_change_end(&sd->bl,SC_BERSERK,-1);
+		if(sd->sc.data[SC_TRICKDEAD])
+			status_change_end(&sd->bl,SC_TRICKDEAD,-1);
+		if(sd->sc.data[SC_GUILDAURA])
+			status_change_end(&sd->bl,SC_GUILDAURA,-1);
+		if (battle_config.debuff_on_logout&1) {
+			if(sd->sc.data[SC_ORCISH])
+				status_change_end(&sd->bl,SC_ORCISH,-1);
+			if(sd->sc.data[SC_STRIPWEAPON])
+				status_change_end(&sd->bl,SC_STRIPWEAPON,-1);
+			if(sd->sc.data[SC_STRIPARMOR])
+				status_change_end(&sd->bl,SC_STRIPARMOR,-1);
+			if(sd->sc.data[SC_STRIPSHIELD])
+				status_change_end(&sd->bl,SC_STRIPSHIELD,-1);
+			if(sd->sc.data[SC_STRIPHELM])
+				status_change_end(&sd->bl,SC_STRIPHELM,-1);
+			if(sd->sc.data[SC_EXTREMITYFIST])
+				status_change_end(&sd->bl,SC_EXTREMITYFIST,-1);
+			if(sd->sc.data[SC_EXPLOSIONSPIRITS])
+				status_change_end(&sd->bl,SC_EXPLOSIONSPIRITS,-1);
+			if(sd->sc.data[SC_REGENERATION] && sd->sc.data[SC_REGENERATION]->val4)
+				status_change_end(&sd->bl,SC_REGENERATION,-1);
+		}
+		if (battle_config.debuff_on_logout&2)
+		{
+			if(sd->sc.data[SC_MAXIMIZEPOWER])
+				status_change_end(&sd->bl,SC_MAXIMIZEPOWER,-1);
+			if(sd->sc.data[SC_MAXOVERTHRUST])
+				status_change_end(&sd->bl,SC_MAXOVERTHRUST,-1);
+			if(sd->sc.data[SC_STEELBODY])
+				status_change_end(&sd->bl,SC_STEELBODY,-1);
+		}
 	}
+	
+	unit_remove_map_pc(sd,3);
+	pc_makesavestatus(sd);
+	pc_clean_skilltree(sd);
+	chrif_save(sd,1);
+	unit_free_pc(sd);
 	return 0;
 }
 
-void do_reconnect_map(void)
-{
-	pc_db->foreach(pc_db,do_reconnect_map_sub);
-	pc_db->foreach(quit_db,do_reconnect_map_sub);//## FIXME possible loss of data [FlavioJS]
-}
-
 /*==========================================
  * id番?のPCを探す。居なければNULL
  *------------------------------------------*/
@@ -1765,15 +1723,6 @@ struct block_list * map_id2bl(int id)
 	return bl;
 }
 
-static int map_getallpc_sub(DBKey key,void * data,va_list ap)
-{
-	struct map_session_data *sd = (struct map_session_data*) data;
-	if (!sd->state.auth || sd->state.waitingdisconnect || sd->state.finalsave)
-		return 1; //Do not count in not-yet authenticated characters or ready to disconnect ones.
-
-	return 0;
-}
-
 /*==========================================
  * Returns an array of all players in the server (includes non connected ones) [Skotlex]
  * The int pointer given returns the count of elements in the array.
@@ -1805,7 +1754,7 @@ struct map_session_data** map_getallusers(int *users)
 		RECREATE(all_sd, struct map_session_data*, all_count);
 	}
 
-	*users = pc_db->getall(pc_db,(void**)all_sd,all_count,map_getallpc_sub);
+	*users = pc_db->getall(pc_db,(void**)all_sd,all_count,NULL);
 	if (*users > (signed int)all_count) //Which should be impossible...
 		*users = all_count;
 
@@ -1857,8 +1806,7 @@ struct s_mapiterator
 /// @return true if it matches
 #define MAPIT_MATCHES(_mapit_,_bl_) \
 	( \
-		( (_bl_)->type & (_mapit_)->types /* type matches */ ) && \
-		( (_bl_)->type != BL_PC /* not a pc */ || !((_mapit_)->flags & MAPIT_PCISPLAYING) /* any pc state */ || pc_isplaying((TBL_PC*)(_bl_)) /* pc is playing */ ) \
+		( (_bl_)->type & (_mapit_)->types /* type matches */ ) \
 	)
 
 /// Allocates a new iterator.
@@ -1874,7 +1822,6 @@ struct s_mapiterator* mapit_alloc(enum e_mapitflags flags, enum bl_type types)
 	struct s_mapiterator* mapit;
 
 	CREATE(mapit, struct s_mapiterator, 1);
-	if( !(types & BL_PC) && (flags & MAPIT_PCISPLAYING) ) flags ^= MAPIT_PCISPLAYING;// incompatible flag
 	mapit->flags = flags;
 	mapit->types = types;
 	if( types == BL_PC )       mapit->dbi = db_iterator(pc_db);
@@ -3127,19 +3074,6 @@ static int cleanup_db_sub(DBKey key,void *data,va_list va)
 	return cleanup_sub((struct block_list*)data, NULL);
 }
 
-static int cleanup_db_subpc(DBKey key,void *data,va_list va)
-{
-	struct map_session_data *sd = (TBL_PC*)data;
-	if (!sd->state.finalsave)
-  	{	//Error?
-		ShowError("do_final: Player character in DB which was not sent to save! %d:%d\n", sd->status.account_id, sd->status.char_id);
-		map_quit(sd); //Attempt force-save
-	}
-	//Force remove from memory...
-	map_quit_ack(sd->status.account_id, sd->status.char_id);
-	return 1;
-}
-
 /*==========================================
  * map鯖終了・理
  *------------------------------------------*/
@@ -3164,9 +3098,6 @@ void do_final(void)
 	id_db->foreach(id_db,cleanup_db_sub);
 	chrif_char_reset_offline();
 	chrif_flush_fifo();
-  	//Online players were sent to save, but the ack will not arrive on time!
-	//They have to be removed from memory, and assume the char-server saved them.
-	pc_db->foreach(pc_db,cleanup_db_subpc);
 
 	do_final_atcommand();
 	do_final_battle();
@@ -3210,7 +3141,6 @@ void do_final(void)
 	mobid_db->destroy(mobid_db, NULL);
 	nick_db->destroy(nick_db, nick_db_final);
 	charid_db->destroy(charid_db, NULL);
-	db_destroy(quit_db);
 
 #ifndef TXT_ONLY
     map_sql_close();
@@ -3222,9 +3152,6 @@ static int map_abort_sub(DBKey key,void * data,va_list ap)
 {
 	struct map_session_data *sd = (TBL_PC*)data;
 
-	if (!sd->state.auth || sd->state.waitingdisconnect || sd->state.finalsave) 
-		return 0;
-
 	chrif_save(sd,1);
 	return 1;
 }
@@ -3389,7 +3316,6 @@ int do_init(int argc, char *argv[])
 	map_db = uidb_alloc(DB_OPT_BASE);
 	nick_db = idb_alloc(DB_OPT_BASE);
 	charid_db = idb_alloc(DB_OPT_BASE);
-	quit_db = idb_alloc(DB_OPT_RELEASE_DATA);
 #ifndef TXT_ONLY
 	map_sql_init();
 #endif /* not TXT_ONLY */

+ 3 - 9
src/map/map.h

@@ -514,10 +514,9 @@ struct map_session_data {
 	//NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in
 	//status_calc_pc, while special_state is recalculated in each call. [Skotlex]
 	struct {
-		unsigned auth : 1;
+		unsigned active : 1; //Marks active player (not active is logging in/out, or changing map servers)
 		unsigned menu_or_input : 1;// if a script is waiting for feedback from the player
 		unsigned dead_sit : 2;
-		unsigned waitingdisconnect : 1;
 		unsigned lr_flag : 2;
 		unsigned connect_new : 1;
 		unsigned arrow_atk : 1;
@@ -537,12 +536,9 @@ struct map_session_data {
 		unsigned noask :1; // [LuzZza]
 		unsigned trading :1; //[Skotlex] is 1 only after a trade has started.
 		unsigned deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE
-		unsigned party_sent :1;
-		unsigned guild_sent :1;
 		unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
 		unsigned size :2; // for tiny/large types
 		unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
-		unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex]
 		unsigned blockedmove :1;
 		unsigned using_fake_npc :1;
 		unsigned rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex]
@@ -1295,9 +1291,7 @@ int map_delobjectnofree(int id);
 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);
 //
-bool map_knowsaccount(int account_id);
 int map_quit(struct map_session_data *);
-void map_quit_ack(int account_id, int char_id);
 // npc
 bool map_addnpc(int,struct npc_data *);
 
@@ -1338,7 +1332,7 @@ struct map_session_data * map_nick2sd(const char*);
 enum e_mapitflags
 {
 	MAPIT_NORMAL = 0,
-	MAPIT_PCISPLAYING = 1,// player is authed, not waiting disconnect and not in final save
+//	MAPIT_PCISPLAYING = 1,// Unneeded as pc_db/id_db will only hold auth'ed, active players.
 };
 struct s_mapiterator;
 struct s_mapiterator*   mapit_alloc(enum e_mapitflags flags, enum bl_type types);
@@ -1348,7 +1342,7 @@ struct block_list*      mapit_last(struct s_mapiterator* mapit);
 struct block_list*      mapit_next(struct s_mapiterator* mapit);
 struct block_list*      mapit_prev(struct s_mapiterator* mapit);
 bool                    mapit_exists(struct s_mapiterator* mapit);
-#define mapit_getallusers() mapit_alloc(MAPIT_PCISPLAYING,BL_PC)
+#define mapit_getallusers() mapit_alloc(MAPIT_NORMAL,BL_PC)
 #define mapit_geteachpc()   mapit_alloc(MAPIT_NORMAL,BL_PC)
 #define mapit_geteachmob()  mapit_alloc(MAPIT_NORMAL,BL_MOB)
 #define mapit_geteachiddb() mapit_alloc(MAPIT_NORMAL,BL_ALL)

+ 44 - 31
src/map/party.c

@@ -54,7 +54,7 @@ static TBL_PC* party_sd_check(int party_id, int account_id, int char_id)
 {
 	TBL_PC* sd = map_id2sd(account_id);
 
-	if (!(sd && sd->status.char_id == char_id && sd->state.auth && !sd->state.waitingdisconnect))
+	if (!(sd && sd->status.char_id == char_id))
 		return NULL;
 
 	if (sd->status.party_id != party_id)
@@ -125,7 +125,6 @@ int party_create(struct map_session_data *sd,char *name,int item,int item2)
 int party_created(int account_id,int char_id,int fail,int party_id,char *name)
 {
 	struct map_session_data *sd;
-	struct party_data *p;
 	sd=map_id2sd(account_id);
 
 	if (!sd || sd->status.char_id != char_id)
@@ -140,16 +139,8 @@ int party_created(int account_id,int char_id,int fail,int party_id,char *name)
 		return 0; // "party name already exists"
 	}
 	sd->status.party_id=party_id;
-	if(idb_get(party_db,party_id)!=NULL){
-		ShowFatalError("party: id already exists!\n");
-		exit(EXIT_FAILURE);
-	}
-	p=(struct party_data *)aCalloc(1,sizeof(struct party_data));
-	p->party.party_id=party_id;
-	memcpy(p->party.name, name, NAME_LENGTH);
-	idb_put(party_db,party_id,p);
 	clif_party_created(sd,0); //Success message
-	clif_charnameupdate(sd); //Update other people's display. [Skotlex]
+	//We don't do any further work here because the char-server sends a party info packet right after creating the party.
 	return 1;
 }
 
@@ -239,15 +230,18 @@ static void party_check_state(struct party_data *p)
 
 int party_recv_info(struct party *sp)
 {
-	struct map_session_data *sd;
 	struct party_data *p;
 	int i;
+	bool party_new = false;
 	
 	nullpo_retr(0, sp);
 
 	p= idb_ensure(party_db, sp->party_id, create_party);
 	if (!p->party.party_id) //party just received.
+	{
+		party_new = true;
 		party_check_member(sp);
+	}
 	memcpy(&p->party,sp,sizeof(struct party));
 	memset(&p->state, 0, sizeof(p->state));
 	memset(&p->data, 0, sizeof(p->data));
@@ -257,14 +251,17 @@ int party_recv_info(struct party *sp)
 		p->data[i].sd = party_sd_check(p->party.party_id, p->party.member[i].account_id, p->party.member[i].char_id);
 	}
 	party_check_state(p);
-	for(i=0;i<MAX_PARTY;i++){
-		sd = p->data[i].sd;
-		if(!sd || sd->state.party_sent)
-			continue;
-		clif_party_member_info(p,sd);
-		clif_party_option(p,sd,0x100);
-		clif_party_info(p,NULL);
-		sd->state.party_sent=1;
+	if (party_new) {
+		//Send party data to all players.
+		struct map_session_data *sd;
+		for(i=0;i<MAX_PARTY;i++){
+			sd = p->data[i].sd;
+			if(!sd) continue;
+			clif_charnameupdate(sd); //Update other people's display. [Skotlex]
+			clif_party_member_info(p,sd);
+			clif_party_option(p,sd,0x100);
+			clif_party_info(p,NULL);
+		}
 	}
 	
 	return 0;
@@ -349,6 +346,26 @@ int party_reply_invite(struct map_session_data *sd,int account_id,int flag)
 	return 1;
 }
 
+//Invoked when a player joins:
+//- Loads up party data if not in server
+//- Sets up the pointer to him
+//- Player must be authed/active and belong to a party before calling this method
+void party_member_joined(struct map_session_data *sd)
+{
+	struct party_data* p = party_search(sd->status.party_id);
+	int i;
+	if (!p)
+	{
+		party_request_info(sd->status.party_id);
+		return;
+	}
+	ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id );
+	if (i < MAX_PARTY)
+		p->data[i].sd = sd;
+	else
+		sd->status.party_id = 0; //He does not belongs to the party really?
+}
+
 /// Invoked (from char-server) when a new member is added to the party.
 int party_member_added(int party_id,int account_id,int char_id, int flag)
 {
@@ -371,9 +388,10 @@ int party_member_added(int party_id,int account_id,int char_id, int flag)
 	}
 
 	if(!flag) {
-		sd->state.party_sent=0;
 		sd->status.party_id=party_id;
 		party_check_conflict(sd);
+		clif_party_option(p,sd,0x100);
+		clif_party_info(p,sd);
 		clif_party_member_info(p,sd);
 		for( i = 0; i < ARRAYLENGTH(p->data); ++i )
 		{// hp of the other party members
@@ -458,7 +476,6 @@ int party_member_leaved(int party_id, int account_id, int char_id)
 	if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id )
 	{
 		sd->status.party_id = 0;
-		sd->state.party_sent = 0;
 		clif_charnameupdate(sd); //Update name display [Skotlex]
 		//TODO: hp bars should be cleared too
 	}
@@ -479,7 +496,6 @@ int party_broken(int party_id)
 		if(p->data[i].sd!=NULL){
 			clif_party_leaved(p,p->data[i].sd,p->party.member[i].account_id,p->party.member[i].name,0x10);
 			p->data[i].sd->status.party_id=0;
-			p->data[i].sd->state.party_sent=0;
 		}
 	}
 	idb_remove(party_db,party_id);
@@ -561,14 +577,11 @@ void party_send_movemap(struct map_session_data *sd)
 	p=party_search(sd->status.party_id);
 	if (!p) return;
 
-	if(!sd->state.party_sent) {
-		party_check_member(&p->party);
-		if(sd->status.party_id==p->party.party_id){
-			clif_party_option(p,sd,0x100);
-			clif_party_info(p,sd);
-			clif_party_member_info(p,sd);
-			sd->state.party_sent=1;
-		}
+	if(sd->state.connect_new) {
+		//Note that this works because this function is invoked before connect_new is cleared.
+		clif_party_option(p,sd,0x100);
+		clif_party_info(p,sd);
+		clif_party_member_info(p,sd);
 	}
 
 	if (sd->fd) { // synchronize minimap positions with the rest of the party

+ 1 - 0
src/map/party.h

@@ -24,6 +24,7 @@ int party_create(struct map_session_data *sd,char *name, int item, int item2);
 int party_created(int account_id,int char_id,int fail,int party_id,char *name);
 int party_request_info(int party_id);
 int party_invite(struct map_session_data *sd,struct map_session_data *tsd);
+void party_member_joined(struct map_session_data *sd);
 int party_member_added(int party_id,int account_id,int char_id,int flag);
 int party_leave(struct map_session_data *sd);
 int party_removemember(struct map_session_data *sd,int account_id,char *name);

+ 49 - 122
src/map/pc.c

@@ -333,9 +333,6 @@ int pc_makesavestatus(struct map_session_data *sd)
 {
 	nullpo_retr(0, sd);
 
-	if (sd->state.finalsave)
-		return 0; //Nothing to change.
-	
 	if(!battle_config.save_clothcolor)
 		sd->status.clothes_color=0;
 
@@ -392,10 +389,9 @@ int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int lo
 	sd->login_id1    = login_id1;
 	sd->login_id2    = 0; // at this point, we can not know the value :(
 	sd->client_tick  = client_tick;
-	sd->state.auth   = 0;
+	sd->state.active = 0; //to be set to 1 after player is fully authed and loaded.
 	sd->bl.type      = BL_PC;
 	sd->canlog_tick  = gettick();
-	sd->state.waitingdisconnect = 0;
 	//Required to prevent homunculus copuing a base speed of 0.
 	sd->battle_status.speed = sd->base_status.speed = DEFAULT_WALK_SPEED;
 	return 0;
@@ -607,35 +603,17 @@ int pc_isequip(struct map_session_data *sd,int n)
  * session idに問題無し
  * char鯖から送られてきたステ?タスを設定
  *------------------------------------------*/
-int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_time, struct mmo_charstatus *st)
+bool pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_time, struct mmo_charstatus *st)
 {
-	TBL_PC* old_sd;
 	int i;
 	unsigned long tick = gettick();
 
-	if (sd->state.auth) //Temporary debug. [Skotlex]
-	{
-		ShowDebug("pc_authok: Received auth ok for already authorized client (account id %d)!\n", sd->bl.id);
-		return 1;
-	}
-
 	sd->login_id2 = login_id2;
 	memcpy(&sd->status, st, sizeof(*st));
 
 	if (st->sex != sd->status.sex) {
 		clif_authfail_fd(sd->fd, 0);
-		return 1;
-	}
-
-	if( (old_sd=map_id2sd(st->account_id)) != NULL ){
-		if (old_sd->state.finalsave || !old_sd->state.auth)
-			; //Previous player is not done loading/quiting, No need to kick.
-		else if (old_sd->fd)
-			clif_authfail_fd(old_sd->fd, 2); // same id
-		else 
-			map_quit(old_sd);
-		clif_authfail_fd(sd->fd, 8); // still recognizes last connection
-		return 1;
+		return false;
 	}
 
 	//Set the map-server used job id. [Skotlex]
@@ -673,9 +651,6 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t
 	if (!(battle_config.display_skill_fail&2))
 		sd->state.showdelay = 1;
 		
-	// Request all registries.
-	intif_request_registry(sd,7);
-
 	// アイテムチェック
 	pc_setinventorydata(sd);
 	pc_checkitem(sd);
@@ -709,21 +684,11 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t
 		if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, 0) != 0) {
 			// if we fail again
 			clif_authfail_fd(sd->fd, 0);
-			return 1;
+			return false;
 		}
 	}
 
-	// pet
-	if (sd->status.pet_id > 0)
-		intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id);
-
-	// Homunculus [albator]
-	if (sd->status.hom_id > 0)
-		intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
-
 	clif_authok(sd);
-	map_addiddb(&sd->bl);
-	map_delnickdb(sd->status.char_id, sd->status.name);
 
 	//Prevent S. Novices from getting the no-death bonus just yet. [Skotlex]
 	sd->die_counter=-1;
@@ -777,22 +742,18 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t
 		clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1);
 	}
 
-	return 0;
+	// Request all registries (auth is considered completed whence they arrive)
+	intif_request_registry(sd,7);
+	return true;
 }
 
 /*==========================================
  * Closes a connection because it failed to be authenticated from the char server.
  *------------------------------------------*/
-int pc_authfail(struct map_session_data *sd)
+void pc_authfail(struct map_session_data *sd)
 {
-	if (sd->state.auth) //Temporary debug. [Skotlex]
-		ShowDebug("pc_authfail: Received auth fail for already authentified client (account id %d)!\n", sd->bl.id);
-
-	if (!sd->fd)
-		ShowDebug("pc_authfail: Received auth fail for a player with no connection (account id %d)!\n", sd->bl.id);
-
 	clif_authfail_fd(sd->fd, 0);
-	return 0;
+	return;
 }
 
 //Attempts to set a mob. 
@@ -827,7 +788,6 @@ int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl)
 int pc_reg_received(struct map_session_data *sd)
 {
 	int i,j;
-	struct guild *g = NULL;
 	
 	sd->change_level = pc_readglobalreg(sd,"jobchange_level");
 	sd->die_counter = pc_readglobalreg(sd,"PC_DIE_COUNTER");
@@ -863,28 +823,33 @@ int pc_reg_received(struct map_session_data *sd)
 	}
 
 	//Weird... maybe registries were reloaded?
-	if (sd->state.auth)
+	if (sd->state.active)
 		return 0;
-	sd->state.auth = 1;
+	sd->state.active = 1;
 
-	if (sd->status.party_id > 0 && party_search(sd->status.party_id) == NULL)
-		party_request_info(sd->status.party_id);
-	if (sd->status.guild_id > 0 && (g=guild_search(sd->status.guild_id)) == NULL)
-		guild_request_info(sd->status.guild_id);
-	else if (g && strcmp(sd->status.name,g->master) == 0)
-	{
-		// set the Guild Master flag
-		sd->state.gmaster_flag = g;
-		// prevent Guild Skills from being used directly after relog
-		if( battle_config.guild_skill_relog_delay )
-			guild_block_skill(sd, 300000);
-	}
+	if (sd->status.party_id)
+		party_member_joined(sd);
+	if (sd->status.guild_id)
+		guild_member_joined(sd);
+	
+	// pet
+	if (sd->status.pet_id > 0)
+		intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id);
+
+	// Homunculus [albator]
+	if (sd->status.hom_id > 0)
+		intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+
+	map_addiddb(&sd->bl);
+	map_delnickdb(sd->status.char_id, sd->status.name);
+	chrif_auth_finished(sd);
 
 	status_calc_pc(sd,1);
 	chrif_scdata_request(sd->status.account_id, sd->status.char_id);
 #ifndef TXT_ONLY
 	intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
 #endif
+
 	if (!sd->state.connect_new && sd->fd)
 	{	//Character already loaded map! Gotta trigger LoadEndAck manually.
 		sd->state.connect_new = 1;
@@ -2717,9 +2682,6 @@ int pc_payzeny(struct map_session_data *sd,int zeny)
 {
 	nullpo_retr(0, sd);
 
-	if( sd->state.finalsave )
-		return 1;
-
 	if( zeny < 0 )
 	  	return pc_getzeny(sd, -zeny);
 
@@ -2739,9 +2701,6 @@ int pc_getzeny(struct map_session_data *sd,int zeny)
 {
 	nullpo_retr(0, sd);
 
-	if( sd->state.finalsave )
-		return 1;
-
 	if( zeny < 0 )
 		return pc_payzeny(sd, -zeny);
 
@@ -2785,9 +2744,6 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount)
 	nullpo_retr(1, sd);
 	nullpo_retr(1, item_data);
 
-	if(sd->state.finalsave)
-		return 1;
-
 	if(item_data->nameid <= 0 || amount <= 0)
 		return 1;
 	if(amount > MAX_AMOUNT)
@@ -3135,9 +3091,6 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun
 	nullpo_retr(1, sd);
 	nullpo_retr(1, item_data);
 
-	if(sd->state.finalsave)
-		return 1;
-
 	if(item_data->nameid <= 0 || amount <= 0)
 		return 1;
 	data = itemdb_search(item_data->nameid);
@@ -3191,9 +3144,6 @@ int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type)
 {
 	nullpo_retr(1, sd);
 
-	if(sd->state.finalsave)
-		return 1;
-
 	if(sd->status.cart[n].nameid==0 ||
 	   sd->status.cart[n].amount<amount)
 		return 1;
@@ -3456,29 +3406,23 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 	}
 
 	if(m<0) {
-		if(sd->mapindex) {
-			uint32 ip;
-			uint16 port;
-			if(map_mapname2ipport(mapindex,&ip,&port)==0) {
-				unit_remove_map(&sd->bl,clrtype);
-				sd->mapindex = mapindex;
-				sd->bl.x=x;
-				sd->bl.y=y;
-				sd->state.waitingdisconnect=1;
-				pc_clean_skilltree(sd);
-				if(sd->status.pet_id > 0 && sd->pd) {
-					intif_save_petdata(sd->status.account_id,&sd->pd->pet);
-					unit_remove_map(&sd->pd->bl, clrtype);
-				}
-				if(merc_is_hom_active(sd->hd)) //Hom is auto-saved in chrif_save
-					unit_remove_map(&sd->hd->bl, clrtype);
-
-				chrif_save(sd,2);
-				chrif_changemapserver(sd, mapindex, x, y, ip, (short)port);
-				return 0;
-			}
-		}
-		return 2;
+		uint32 ip;
+		uint16 port;
+		//if can't find any map-servers, just abort setting position.
+		if(!sd->mapindex || map_mapname2ipport(mapindex,&ip,&port))
+			return 2;
+
+		sd->mapindex = mapindex;
+		sd->bl.x=x;
+		sd->bl.y=y;
+		pc_clean_skilltree(sd);
+		unit_remove_map_pc(sd,clrtype);
+		chrif_save(sd,2);
+		chrif_changemapserver(sd, ip, (short)port);
+		//It is important to invoke remove_map separately from unit_free before
+		//saving so that the data saved corresponds to that AFTER warping.
+		unit_free_pc(sd);
+		return 0;
 	}
 
 	if(x <0 || x >= map[m].xs || y <0 || y >= map[m].ys)
@@ -3500,13 +3444,9 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 	}
 
 	if(sd->bl.prev != NULL){
-		unit_remove_map(&sd->bl, clrtype);
-		if(sd->status.pet_id > 0 && sd->pd)
-			unit_remove_map(&sd->pd->bl, clrtype);
-		if(merc_is_hom_active(sd->hd))
-			unit_remove_map(&sd->hd->bl, clrtype);
+		unit_remove_map_pc(sd,clrtype);
 		clif_changemap(sd,map[m].index,x,y); // [MouseJstr]
-	} else if(sd->state.auth)
+	} else if(sd->state.active)
 		//Tag player for rewarping after map-loading is done. [Skotlex]
 		sd->state.rewarp = 1;
 	
@@ -6587,7 +6527,7 @@ int pc_checkitem(struct map_session_data *sd)
 	}
 
 	pc_setequipindex(sd);
-	if(calc_flag && sd->state.auth)
+	if(calc_flag && sd->state.active)
 	{
 		status_calc_pc(sd,0);
 		pc_equiplookall(sd);
@@ -6881,24 +6821,11 @@ static int pc_autosave_sub(DBKey key,void * data,va_list ap)
 	if(save_flag != 1) //Not our turn to save yet.
 		return 0;
 
-	if (sd->state.waitingdisconnect) //Invalid char to save.
-		return 0;
-
 	//Save char.
 	last_save_id = sd->bl.id;
 	save_flag=2;
 
-	// pet
-	if(sd->status.pet_id > 0 && sd->pd)
-		intif_save_petdata(sd->status.account_id,&sd->pd->pet);
-
-	if(sd->state.finalsave)
-  	{	//Save ack hasn't returned from char-server yet? Retry.
-		ShowDebug("pc_autosave: Resending to save logging out char %d:%d (save ack from char-server hasn't arrived yet)\n", sd->status.account_id, sd->status.char_id);
-		sd->state.finalsave = 0;
-		chrif_save(sd,1);
-	} else
-		chrif_save(sd,0);
+	chrif_save(sd,0);
 	return 1;
 }
 

+ 2 - 3
src/map/pc.h

@@ -126,7 +126,6 @@ extern int duel_count;
 #define pc_is50overweight(sd) ( (sd)->weight*100 >= (sd)->max_weight*battle_config.natural_heal_weight_rate )
 #define pc_is90overweight(sd) ( (sd)->weight*10 >= (sd)->max_weight*9 )
 #define pc_maxparameter(sd)   ( (sd)->class_&JOBL_BABY ? battle_config.max_baby_parameter : battle_config.max_parameter )
-#define pc_isplaying(sd)      ( (sd)->state.auth /* is authed */ && !(sd)->state.waitingdisconnect /* not waiting disconnect */ && !(sd)->state.finalsave /* not in final save */ )
 
 #define pc_stop_walking(sd, type) unit_stop_walking(&(sd)->bl, type)
 #define pc_stop_attack(sd) unit_stop_attack(&(sd)->bl)
@@ -145,8 +144,8 @@ bool pc_can_give_items(int level);
 int pc_setrestartvalue(struct map_session_data *sd,int type);
 int pc_makesavestatus(struct map_session_data *);
 int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int);
-int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
-int pc_authfail(struct map_session_data *);
+bool pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
+void pc_authfail(struct map_session_data *);
 int pc_reg_received(struct map_session_data *sd);
 
 int pc_isequip(struct map_session_data *sd,int n);

+ 0 - 6
src/map/status.c

@@ -1616,9 +1616,6 @@ int status_calc_pc(struct map_session_data* sd,int first)
 	int i,index;
 	int skill,refinedef=0;
 
-	if(!sd->state.auth && !(first&1)) //Shouldn't invoke yet until player is done loading.
-		return -1;
-
 	if (++calculating > 10) //Too many recursive calls!
 		return -1;
 
@@ -4623,9 +4620,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 
 	sd = BL_CAST(BL_PC, bl);
 
-	if(sd && sd->state.waitingdisconnect)
-		return 0; //Character logging out, all his SC were wiped already!
-
 	//Adjust tick according to status resistances
 	if( !(flag&(1|4)) )
 	{

+ 0 - 9
src/map/storage.c

@@ -132,9 +132,6 @@ int storage_storageopen(struct map_session_data *sd)
 	struct storage *stor;
 	nullpo_retr(0, sd);
 
-	if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
-		return 1;
-
 	if(sd->state.storage_flag)
 		return 1; //Already open?
 	
@@ -183,9 +180,6 @@ static int storage_additem(struct map_session_data *sd,struct storage *stor,stru
 	struct item_data *data;
 	int i;
 
-	if (sd->state.finalsave)
-		return 1;
-
 	if(item_data->nameid <= 0 || amount <= 0)
 		return 1;
 	
@@ -502,9 +496,6 @@ int storage_guild_storageopen(struct map_session_data *sd)
 	if(sd->status.guild_id <= 0)
 		return 2;
 
-	if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
-		return 1;
-	
 	if(sd->state.storage_flag)
 		return 1; //Can't open both storages at a time.
 	

+ 0 - 3
src/map/trade.c

@@ -232,9 +232,6 @@ int trade_check(struct map_session_data *sd, struct map_session_data *tsd)
 	int trade_i, i, n;
 	short amount;
 
-	if(sd->state.finalsave || tsd->state.finalsave)
-		return 0; //Item transferring fails 
-
 	// check zenys value against hackers (Zeny was already checked on time of adding, but you never know when you lost some zeny since then.
 	if(sd->deal.zeny > sd->status.zeny || (tsd->status.zeny > MAX_ZENY - sd->deal.zeny))
 		return 0;

+ 51 - 55
src/map/unit.c

@@ -1658,13 +1658,11 @@ int unit_remove_map(struct block_list *bl, int clrtype)
 			trade_tradecancel(sd);
 		if(sd->vender_id)
 			vending_closevending(sd);
-		if(!sd->state.waitingdisconnect)
-	  	{	//when quitting, let the final chrif_save handle storage saving.
-			if(sd->state.storage_flag == 1)
-				storage_storage_quit(sd,0);
-			else if (sd->state.storage_flag == 2)
-				storage_guild_storage_quit(sd,0);
-		}
+		if(sd->state.storage_flag == 1)
+			storage_storage_quit(sd,0);
+		else if (sd->state.storage_flag == 2)
+			storage_guild_storage_quit(sd,0);
+		sd->state.storage_flag = 0; //Force close it when being warped.
 		if(sd->party_invite>0)
 			party_reply_invite(sd,sd->party_invite_account,0);
 		if(sd->guild_invite>0)
@@ -1708,7 +1706,7 @@ int unit_remove_map(struct block_list *bl, int clrtype)
 	case BL_PET:
 	{
 		struct pet_data *pd = (struct pet_data*)bl;
-		if( pd->pet.intimate <= 0 && !(pd->msd && pd->msd->state.waitingdisconnect) )
+		if( pd->pet.intimate <= 0 && !(pd->msd && pd->msd->state.active) )
 		{	//If logging out, this is deleted on unit_free
 			clif_clearunit_area(bl,clrtype);
 			map_delblock(bl);
@@ -1723,7 +1721,7 @@ int unit_remove_map(struct block_list *bl, int clrtype)
 	{
 		struct homun_data *hd = (struct homun_data *) bl;
 		ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
-		if(!hd->homunculus.intimacy && !(hd->master && hd->master->state.waitingdisconnect) )
+		if(!hd->homunculus.intimacy && !(hd->master && hd->master->state.active) )
 		{	//If logging out, this is deleted on unit_free
 			clif_emotion(bl, 28) ;	//sob
 			clif_clearunit_area(bl,clrtype);
@@ -1744,6 +1742,25 @@ int unit_remove_map(struct block_list *bl, int clrtype)
 	return 1;
 }
 
+void unit_remove_map_pc(struct map_session_data *sd, int clrtype)
+{
+	unit_remove_map(&sd->bl,clrtype);
+
+	if (clrtype == 3) clrtype = 0; //3 is the warp from logging out, but pets/homunc need to just 'vanish' instead of showing the warping out animation.
+
+	if(sd->pd)
+		unit_remove_map(&sd->pd->bl, clrtype);
+	if(merc_is_hom_active(sd->hd))
+		unit_remove_map(&sd->hd->bl, clrtype);
+}
+
+void unit_free_pc(struct map_session_data *sd)
+{
+	if (sd->pd) unit_free(&sd->pd->bl,0);
+	if (sd->hd) unit_free(&sd->hd->bl,0);
+	unit_free(&sd->bl,3);
+}
+
 /*==========================================
  * Function to free all related resources to the bl
  * if unit is on map, it is removed using the clrtype specified
@@ -1765,44 +1782,6 @@ int unit_free(struct block_list *bl, int clrtype)
 			pc_setrestartvalue(sd,2);
 
 		pc_delinvincibletimer(sd);
-		//Status that are not saved...
-		if(sd->sc.count) {
-			if(sd->sc.data[SC_SPURT])
-				status_change_end(bl,SC_SPURT,-1);
-			if(sd->sc.data[SC_BERSERK])
-				status_change_end(bl,SC_BERSERK,-1);
-			if(sd->sc.data[SC_TRICKDEAD])
-				status_change_end(bl,SC_TRICKDEAD,-1);
-			if(sd->sc.data[SC_GUILDAURA])
-				status_change_end(bl,SC_GUILDAURA,-1);
-			if (battle_config.debuff_on_logout&1) {
-				if(sd->sc.data[SC_ORCISH])
-					status_change_end(bl,SC_ORCISH,-1);
-				if(sd->sc.data[SC_STRIPWEAPON])
-					status_change_end(bl,SC_STRIPWEAPON,-1);
-				if(sd->sc.data[SC_STRIPARMOR])
-					status_change_end(bl,SC_STRIPARMOR,-1);
-				if(sd->sc.data[SC_STRIPSHIELD])
-					status_change_end(bl,SC_STRIPSHIELD,-1);
-				if(sd->sc.data[SC_STRIPHELM])
-					status_change_end(bl,SC_STRIPHELM,-1);
-				if(sd->sc.data[SC_EXTREMITYFIST])
-					status_change_end(bl,SC_EXTREMITYFIST,-1);
-				if(sd->sc.data[SC_EXPLOSIONSPIRITS])
-					status_change_end(bl,SC_EXPLOSIONSPIRITS,-1);
-				if(sd->sc.data[SC_REGENERATION] && sd->sc.data[SC_REGENERATION]->val4)
-					status_change_end(bl,SC_REGENERATION,-1);
-			}
-			if (battle_config.debuff_on_logout&2)
-			{
-				if(sd->sc.data[SC_MAXIMIZEPOWER])
-					status_change_end(bl,SC_MAXIMIZEPOWER,-1);
-				if(sd->sc.data[SC_MAXOVERTHRUST])
-					status_change_end(bl,SC_MAXOVERTHRUST,-1);
-				if(sd->sc.data[SC_STEELBODY])
-					status_change_end(bl,SC_STEELBODY,-1);
-			}
-		}
 	
 		pc_autoscript_clear(sd->autoscript, ARRAYLENGTH(sd->autoscript));
 		pc_autoscript_clear(sd->autoscript2, ARRAYLENGTH(sd->autoscript2));
@@ -1819,10 +1798,29 @@ int unit_free(struct block_list *bl, int clrtype)
 		guild_send_memberinfoshort(sd,0);
 		pc_cleareventtimer(sd);
 		pc_delspiritball(sd,sd->spiritball,1);
-		if (clrtype >= 0) {
-			chrif_save_scdata(sd); //Save status changes, then clear'em out from memory. [Skotlex]
-			pc_makesavestatus(sd);
-			pc_clean_skilltree(sd);
+
+		if(sd->reg)
+		{	//Double logout already freed pointer fix... [Skotlex]
+			aFree(sd->reg);
+			sd->reg = NULL;
+			sd->reg_num = 0;
+		}
+		if(sd->regstr)
+		{
+			int i;
+			for( i = 0; i < sd->regstr_num; ++i )
+				if( sd->regstr[i].data )
+					aFree(sd->regstr[i].data);
+			aFree(sd->regstr);
+			sd->regstr = NULL;
+			sd->regstr_num = 0;
+		}
+		if (sd->st) {
+			if (sd->st->stack)
+				script_free_stack (sd->st->stack);
+			aFree(sd->st);
+			sd->st = NULL;
+			sd->npc_id = 0;
 		}
 	} else if( bl->type == BL_PET ) {
 		struct pet_data *pd = (struct pet_data*)bl;
@@ -1924,11 +1922,9 @@ int unit_free(struct block_list *bl, int clrtype)
 
 	skill_clear_unitgroup(bl);
 	status_change_clear(bl,1);
-	if (bl->type != BL_PC)
-  	{	//Players are handled by map_quit
-		map_deliddb(bl);
+	map_deliddb(bl);
+	if (bl->type != BL_PC) //Players are handled by map_quit
 		map_freeblock(bl);
-	}
 	map_freeblock_unlock();
 	return 0;
 }

+ 3 - 0
src/map/unit.h

@@ -7,6 +7,7 @@
 //#include "map.h"
 struct block_list;
 struct unit_data;
+struct map_session_data;
 
 // PC, MOB, PET に共通する処理を1つにまとめる計画
 
@@ -61,6 +62,8 @@ void unit_dataset(struct block_list *bl);
 int unit_fixdamage(struct block_list *src,struct block_list *target,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2);
 // その他
 struct unit_data* unit_bl2ud(struct block_list *bl);
+void unit_remove_map_pc(struct map_session_data *sd, int clrtype);
+void unit_free_pc(struct map_session_data *sd);
 int unit_remove_map(struct block_list *bl, int clrtype);
 int unit_free(struct block_list *bl, int clrtype);
 int unit_changeviewsize(struct block_list *bl,short size);