summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/atcommand.c2
-rw-r--r--src/map/chrif.c317
-rw-r--r--src/map/chrif.h16
-rw-r--r--src/map/clif.c183
-rw-r--r--src/map/guild.c71
-rw-r--r--src/map/guild.h1
-rw-r--r--src/map/intif.c31
-rw-r--r--src/map/mail.c2
-rw-r--r--src/map/map.c196
-rw-r--r--src/map/map.h12
-rw-r--r--src/map/party.c75
-rw-r--r--src/map/party.h1
-rw-r--r--src/map/pc.c171
-rw-r--r--src/map/pc.h5
-rw-r--r--src/map/status.c6
-rw-r--r--src/map/storage.c9
-rw-r--r--src/map/trade.c3
-rw-r--r--src/map/unit.c106
-rw-r--r--src/map/unit.h3
19 files changed, 587 insertions, 623 deletions
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 8da0f1265..13bbb5ef0 100644
--- a/src/map/atcommand.c
+++ b/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))
diff --git a/src/map/chrif.c b/src/map/chrif.c
index e88ee3c5d..005285da5 100644
--- a/src/map/chrif.c
+++ b/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");
diff --git a/src/map/chrif.h b/src/map/chrif.h
index b440fcac6..bf1dbb108 100644
--- a/src/map/chrif.h
+++ b/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);
diff --git a/src/map/clif.c b/src/map/clif.c
index ed5e46bbd..5d6daaa26 100644
--- a/src/map/clif.c
+++ b/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);
diff --git a/src/map/guild.c b/src/map/guild.c
index e591fc838..5644793b2 100644
--- a/src/map/guild.c
+++ b/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]
}
diff --git a/src/map/guild.h b/src/map/guild.h
index 48dcdb550..1035818d9 100644
--- a/src/map/guild.h
+++ b/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);
diff --git a/src/map/intif.c b/src/map/intif.c
index 3566e38c4..e04710218 100644
--- a/src/map/intif.c
+++ b/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;
diff --git a/src/map/mail.c b/src/map/mail.c
index 5fef604c0..0640fa521 100644
--- a/src/map/mail.c
+++ b/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);
diff --git a/src/map/map.c b/src/map/map.c
index c02a896a9..78e67d3d4 100644
--- a/src/map/map.c
+++ b/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 */
diff --git a/src/map/map.h b/src/map/map.h
index 81b55bb7a..269319ab8 100644
--- a/src/map/map.h
+++ b/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)
diff --git a/src/map/party.c b/src/map/party.c
index 7928a3ad3..b8cd03811 100644
--- a/src/map/party.c
+++ b/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
diff --git a/src/map/party.h b/src/map/party.h
index 8329647b2..b04797fb1 100644
--- a/src/map/party.h
+++ b/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);
diff --git a/src/map/pc.c b/src/map/pc.c
index f76df3d0b..d1bb81c3e 100644
--- a/src/map/pc.c
+++ b/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;
}
diff --git a/src/map/pc.h b/src/map/pc.h
index e3eb9afdb..dc55b53bd 100644
--- a/src/map/pc.h
+++ b/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);
diff --git a/src/map/status.c b/src/map/status.c
index 6b6174293..10129129a 100644
--- a/src/map/status.c
+++ b/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)) )
{
diff --git a/src/map/storage.c b/src/map/storage.c
index 50024fbbe..06c052d74 100644
--- a/src/map/storage.c
+++ b/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.
diff --git a/src/map/trade.c b/src/map/trade.c
index 5c063621d..a9f2a2fd5 100644
--- a/src/map/trade.c
+++ b/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;
diff --git a/src/map/unit.c b/src/map/unit.c
index d0d06ff06..66f362085 100644
--- a/src/map/unit.c
+++ b/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;
}
diff --git a/src/map/unit.h b/src/map/unit.h
index 2c569607d..7575d3f5e 100644
--- a/src/map/unit.h
+++ b/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);