summaryrefslogtreecommitdiff
path: root/src/map/chrif.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/chrif.c')
-rw-r--r--src/map/chrif.c317
1 files changed, 212 insertions, 105 deletions
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");