summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorflaviojs <flaviojs@54d463be-8e91-2dee-dedb-b68131a5f0ec>2011-06-16 10:22:35 +0000
committerflaviojs <flaviojs@54d463be-8e91-2dee-dedb-b68131a5f0ec>2011-06-16 10:22:35 +0000
commite054cbbdb28a9b555faafd689282140c01d55b3e (patch)
treea0a637c944d902df0086900009a644c5b84d39e2
parent83c890478d5588179ffd81b29a95d4bafd0fb118 (diff)
downloadhercules-e054cbbdb28a9b555faafd689282140c01d55b3e.tar.gz
hercules-e054cbbdb28a9b555faafd689282140c01d55b3e.tar.bz2
hercules-e054cbbdb28a9b555faafd689282140c01d55b3e.tar.xz
hercules-e054cbbdb28a9b555faafd689282140c01d55b3e.zip
* Merges from charmerge:
- Added DBMap::exists. (r14090) - Added sv_parse_next, a stepped version of sv_parse (delim-separated parser). (r14100 r14104) - Added missing fd check to do_close. (r14145) - Normalized, refactored and restructured some code (in preparation for shutdown/reconnect code). (r14145 r14150) - Changed the char select request code to allow the char-server to reject it. (player in map-server trying to go to char select) (r14150) - Added shutdown support to the servers. (incomplete) (r14152) git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@14851 54d463be-8e91-2dee-dedb-b68131a5f0ec
-rw-r--r--Changelog-Trunk.txt8
-rw-r--r--src/char/char.c371
-rw-r--r--src/char/char.h8
-rw-r--r--src/char_sql/char.c376
-rw-r--r--src/char_sql/char.h9
-rw-r--r--src/common/core.c14
-rw-r--r--src/common/core.h13
-rw-r--r--src/common/db.c60
-rw-r--r--src/common/db.h14
-rw-r--r--src/common/socket.c3
-rw-r--r--src/common/strlib.c136
-rw-r--r--src/common/strlib.h21
-rw-r--r--src/login/login.c122
-rw-r--r--src/login/login.h8
-rw-r--r--src/map/atcommand.c16
-rw-r--r--src/map/chrif.c99
-rw-r--r--src/map/chrif.h2
-rw-r--r--src/map/clif.c16
-rw-r--r--src/map/clif.h2
-rw-r--r--src/map/map.c29
-rw-r--r--src/map/map.h10
21 files changed, 1001 insertions, 336 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index c36700b5e..dde0c152b 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -1,5 +1,13 @@
Date Added
+2011/06/16
+ * Merges from charmerge:
+ - Added DBMap::exists. (r14090)
+ - Added sv_parse_next, a stepped version of sv_parse (delim-separated parser). (r14100 r14104)
+ - Added missing fd check to do_close. (r14145)
+ - Normalized, refactored and restructured some code (in preparation for shutdown/reconnect code). (r14145 r14150)
+ - Changed the char select request code to allow the char-server to reject it. (player in map-server trying to go to char select) (r14150)
+ - Added shutdown support to the servers. (incomplete) (r14152)
2011/06/13
* Fixed pet's equip would visually disappear when it's walk speed was changed while it was standing (related r14838). [Ai4rei]
2011/06/11
diff --git a/src/char/char.c b/src/char/char.c
index 6599a6258..c01c6bf30 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -1782,7 +1782,7 @@ int count_users(void)
int i, users;
users = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
if (server[i].fd > 0) {
users += server[i].users;
}
@@ -2061,23 +2061,73 @@ static void char_auth_ok(int fd, struct char_session_data *sd)
}
int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data);
+void mapif_server_reset(int id);
+
+
+/// Resets all the data.
+void loginif_reset(void)
+{
+ int id;
+ // TODO kick everyone out and reset everything or wait for connect and try to reaquire locks [FlavioJS]
+ for( id = 0; id < ARRAYLENGTH(server); ++id )
+ mapif_server_reset(id);
+ flush_fifos();
+ exit(EXIT_FAILURE);
+}
+
+
+/// Checks the conditions for the server to stop.
+/// If all the conditions are met, it stops the core loop.
+void loginif_check_shutdown(void)
+{
+ if( runflag != CHARSERVER_ST_SHUTDOWN )
+ return;
+ runflag = CORE_ST_STOP;
+}
+
+
+/// Called when the connection to Login Server is disconnected.
+void loginif_on_disconnect(void)
+{
+ ShowWarning("Connection to Login Server lost.\n\n");
+}
+
+
+/// Called when all the connection steps are completed.
+void loginif_on_ready(void)
+{
+ int i;
+
+ loginif_check_shutdown();
+
+ //Send online accounts to login server.
+ send_accounts_tologin(INVALID_TIMER, gettick(), 0, 0);
+
+ // if no map-server already connected, display a message...
+ ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd > 0 && server[i].map[0] );
+ if( i == ARRAYLENGTH(server) )
+ ShowStatus("Awaiting maps from map-server.\n");
+}
+
int parse_fromlogin(int fd)
{
+ struct char_session_data* sd = NULL;
int i;
- struct char_session_data *sd;
- // only login-server can have an access to here.
- // so, if it isn't the login-server, we disconnect the session.
+ // only process data from the login-server
if( fd != login_fd )
- set_eof(fd);
+ {
+ ShowDebug("parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd);
+ do_close(fd);
+ return 0;
+ }
- if(session[fd]->flag.eof) {
- if (fd == login_fd) {
- ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
- login_fd = -1;
- }
+ if( session[fd]->flag.eof )
+ {
do_close(fd);
+ login_fd = -1;
+ loginif_on_disconnect();
return 0;
}
@@ -2101,16 +2151,11 @@ int parse_fromlogin(int fd)
ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
ShowError("Also, please make sure your accounts file (default: accounts.txt) has the correct communication username/passwords and the gender of the account is S.\n");
ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n");
+ set_eof(fd);
+ return 0;
} else {
ShowStatus("Connected to login-server (connection #%d).\n", fd);
-
- //Send online accounts to login server.
- send_accounts_tologin(INVALID_TIMER, gettick(), 0, 0);
-
- // if no map-server already connected, display a message...
- ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd > 0 && server[i].map[0] );
- if( i == MAX_MAP_SERVERS )
- ShowStatus("Awaiting maps from map-server.\n");
+ loginif_on_ready();
}
RFIFOSKIP(fd,3);
break;
@@ -2377,6 +2422,34 @@ int parse_fromlogin(int fd)
return 0;
}
+int check_connect_login_server(int tid, unsigned int tick, int id, intptr data);
+int ping_login_server(int tid, unsigned int tick, int id, intptr data);
+int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data);
+
+void do_init_loginif(void)
+{
+ // establish char-login connection if not present
+ add_timer_func_list(check_connect_login_server, "check_connect_login_server");
+ add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
+
+ // keep the char-login connection alive
+ add_timer_func_list(ping_login_server, "ping_login_server");
+ add_timer_interval(gettick() + 1000, ping_login_server, 0, 0, ((int)stall_time-2) * 1000);
+
+ // send a list of all online account IDs to login server
+ add_timer_func_list(send_accounts_tologin, "send_accounts_tologin");
+ add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour
+}
+
+void do_final_loginif(void)
+{
+ if( login_fd != -1 )
+ {
+ do_close(login_fd);
+ login_fd = -1;
+ }
+}
+
int request_accreg2(int account_id, int char_id)
{
if (login_fd > 0) {
@@ -2592,36 +2665,76 @@ int char_loadName(int char_id, char* name)
int search_mapserver(unsigned short map, uint32 ip, uint16 port);
+
+/// Initializes a server structure.
+void mapif_server_init(int id)
+{
+ memset(&server[id], 0, sizeof(server[id]));
+ server[id].fd = -1;
+}
+
+
+/// Destroys a server structure.
+void mapif_server_destroy(int id)
+{
+ if( server[id].fd == -1 )
+ {
+ do_close(server[id].fd);
+ server[id].fd = -1;
+ }
+}
+
+
+/// Resets all the data related to a server.
+void mapif_server_reset(int id)
+{
+ int i,j;
+ unsigned char buf[16384];
+ int fd = server[id].fd;
+ //Notify other map servers that this one is gone. [Skotlex]
+ WBUFW(buf,0) = 0x2b20;
+ WBUFL(buf,4) = htonl(server[id].ip);
+ WBUFW(buf,8) = htons(server[id].port);
+ j = 0;
+ for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ if (server[id].map[i])
+ WBUFW(buf,10+(j++)*4) = server[id].map[i];
+ if (j > 0) {
+ WBUFW(buf,2) = j * 4 + 10;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+ }
+ online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
+ create_online_files();
+ mapif_server_destroy(id);
+ mapif_server_init(id);
+}
+
+
+/// Called when the connection to a Map Server is disconnected.
+void mapif_on_disconnect(int id)
+{
+ ShowStatus("Map-server #%d has disconnected.\n", id);
+ mapif_server_reset(id);
+}
+
+
int parse_frommap(int fd)
{
int i, j;
int id;
- ARR_FIND( 0, MAX_MAP_SERVERS, id, server[id].fd == fd );
- if(id == MAX_MAP_SERVERS)
- set_eof(fd);
- if(session[fd]->flag.eof) {
- if (id < MAX_MAP_SERVERS) {
- unsigned char buf[16384];
- ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd);
- //Notify other map servers that this one is gone. [Skotlex]
- WBUFW(buf,0) = 0x2b20;
- WBUFL(buf,4) = htonl(server[id].ip);
- WBUFW(buf,8) = htons(server[id].port);
- j = 0;
- for(i = 0; i < MAX_MAP_PER_SERVER; i++)
- if (server[id].map[i])
- WBUFW(buf,10+(j++)*4) = server[id].map[i];
- if (j > 0) {
- WBUFW(buf,2) = j * 4 + 10;
- mapif_sendallwos(fd, buf, WBUFW(buf,2));
- }
- memset(&server[id], 0, sizeof(struct mmo_map_server));
- server[id].fd = -1;
- online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
- }
+ ARR_FIND( 0, ARRAYLENGTH(server), id, server[id].fd == fd );
+ if( id == ARRAYLENGTH(server) )
+ {// not a map server
+ ShowDebug("parse_frommap: Disconnecting invalid session #%d (is not a map-server)\n", fd);
do_close(fd);
- create_online_files();
+ return 0;
+ }
+ if( session[fd]->flag.eof )
+ {
+ do_close(fd);
+ server[id].fd = -1;
+ mapif_on_disconnect(id);
return 0;
}
@@ -2672,14 +2785,14 @@ int parse_frommap(int fd)
mapif_sendallwos(fd, buf, WBUFW(buf,2));
}
// Transmitting the maps of the other map-servers to the new map-server
- for(x = 0; x < MAX_MAP_SERVERS; x++) {
+ for(x = 0; x < ARRAYLENGTH(server); x++) {
if (server[x].fd > 0 && x != id) {
- WFIFOHEAD(fd,10 +4*MAX_MAP_PER_SERVER);
+ WFIFOHEAD(fd,10 +4*ARRAYLENGTH(server));
WFIFOW(fd,0) = 0x2b04;
WFIFOL(fd,4) = htonl(server[x].ip);
WFIFOW(fd,8) = htons(server[x].port);
j = 0;
- for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ for(i = 0; i < ARRAYLENGTH(server); i++)
if (server[x].map[i])
WFIFOW(fd,10+(j++)*4) = server[x].map[i];
if (j > 0) {
@@ -2802,26 +2915,37 @@ int parse_frommap(int fd)
uint32 ip = RFIFOL(fd,14);
RFIFOSKIP(fd,18);
- // create temporary auth entry
- CREATE(node, struct auth_node, 1);
- node->account_id = account_id;
- node->char_id = 0;
- node->login_id1 = login_id1;
- node->login_id2 = login_id2;
- //node->sex = 0;
- node->ip = ntohl(ip);
- //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
- //node->gmlevel = 0;
- idb_put(auth_db, account_id, node);
-
- //Set char to "@ char select" in online db [Kevin]
- set_char_charselect(account_id);
-
- WFIFOHEAD(fd,7);
- WFIFOW(fd,0) = 0x2b03;
- WFIFOL(fd,2) = account_id;
- WFIFOB(fd,6) = 0;
- WFIFOSET(fd,7);
+ if( runflag != CHARSERVER_ST_RUNNING )
+ {
+ WFIFOHEAD(fd,7);
+ WFIFOW(fd,0) = 0x2b03;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 0;// not ok
+ WFIFOSET(fd,7);
+ }
+ else
+ {
+ // create temporary auth entry
+ CREATE(node, struct auth_node, 1);
+ node->account_id = account_id;
+ node->char_id = 0;
+ node->login_id1 = login_id1;
+ node->login_id2 = login_id2;
+ //node->sex = 0;
+ node->ip = ntohl(ip);
+ //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
+ //node->gmlevel = 0;
+ idb_put(auth_db, account_id, node);
+
+ //Set char to "@ char select" in online db [Kevin]
+ set_char_charselect(account_id);
+
+ WFIFOHEAD(fd,7);
+ WFIFOW(fd,0) = 0x2b03;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 1;// ok
+ WFIFOSET(fd,7);
+ }
}
break;
@@ -2839,7 +2963,9 @@ int parse_frommap(int fd)
char_data = search_character(RFIFOL(fd,2), RFIFOL(fd,14));
- if (map_fd >= 0 && session[map_fd] && char_data)
+ if( runflag == CHARSERVER_ST_RUNNING &&
+ session_isActive(map_fd) &&
+ char_data )
{ //Send the map server the auth of this player.
struct auth_node* node;
@@ -3140,7 +3266,9 @@ int parse_frommap(int fd)
node = (struct auth_node*)idb_get(auth_db, account_id);
cd = search_character(account_id, char_id);
- if( node != NULL && cd != NULL &&
+ if( runflag == CHARSERVER_ST_RUNNING &&
+ cd != NULL &&
+ node != NULL &&
node->account_id == account_id &&
node->char_id == char_id &&
node->login_id1 == login_id1 &&
@@ -3204,13 +3332,27 @@ int parse_frommap(int fd)
return 0;
}
+void do_init_mapif(void)
+{
+ int i;
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ mapif_server_init(i);
+}
+
+void do_final_mapif(void)
+{
+ int i;
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ mapif_server_destroy(i);
+}
+
// Searches for the mapserver that has a given map (and optionally ip/port, if not -1).
// If found, returns the server's index in the 'server' array (otherwise returns -1).
int search_mapserver(unsigned short map, uint32 ip, uint16 port)
{
int i, j;
- for(i = 0; i < MAX_MAP_SERVERS; i++)
+ for(i = 0; i < ARRAYLENGTH(server); i++)
{
if (server[i].fd > 0
&& (ip == (uint32)-1 || server[i].ip == ip)
@@ -3526,6 +3668,15 @@ int parse_char(int fd)
WFIFOL(fd,0) = account_id;
WFIFOSET(fd,4);
+ if( runflag != CHARSERVER_ST_RUNNING )
+ {
+ WFIFOHEAD(fd,3);
+ WFIFOW(fd,0) = 0x6c;
+ WFIFOB(fd,2) = 0;// rejected from server
+ WFIFOSET(fd,3);
+ break;
+ }
+
// search authentification
node = (struct auth_node*)idb_get(auth_db, account_id);
if( node != NULL &&
@@ -3602,8 +3753,8 @@ int parse_char(int fd)
if (i < 0) {
unsigned short j;
//First check that there's actually a map server online.
- ARR_FIND( 0, MAX_MAP_SERVERS, j, server[j].fd >= 0 && server[j].map[0] );
- if (j == MAX_MAP_SERVERS) {
+ ARR_FIND( 0, ARRAYLENGTH(server), j, server[j].fd >= 0 && server[j].map[0] );
+ if (j == ARRAYLENGTH(server)) {
ShowInfo("Connection Closed. No map servers available.\n");
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x81;
@@ -3927,8 +4078,12 @@ int parse_char(int fd)
char* l_pass = (char*)RFIFOP(fd,26);
l_user[23] = '\0';
l_pass[23] = '\0';
- ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd <= 0 );
- if (i == MAX_MAP_SERVERS || strcmp(l_user, userid) || strcmp(l_pass, passwd)) {
+ ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd <= 0 );
+ if( runflag != CHARSERVER_ST_RUNNING ||
+ i == ARRAYLENGTH(server) ||
+ strcmp(l_user, userid) != 0 ||
+ strcmp(l_pass, passwd) != 0 )
+ {
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x2af9;
WFIFOB(fd,2) = 3;
@@ -3991,7 +4146,7 @@ int mapif_sendall(unsigned char *buf, unsigned int len)
int i, c;
c = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
int fd;
if ((fd = server[i].fd) > 0) {
WFIFOHEAD(fd,len);
@@ -4009,7 +4164,7 @@ int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len)
int i, c;
c = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
int fd;
if ((fd = server[i].fd) > 0 && fd != sfd) {
WFIFOHEAD(fd,len);
@@ -4027,8 +4182,8 @@ int mapif_send(int fd, unsigned char *buf, unsigned int len)
int i;
if (fd >= 0) {
- ARR_FIND( 0, MAX_MAP_SERVERS, i, fd == server[i].fd );
- if( i < MAX_MAP_SERVERS )
+ ARR_FIND( 0, ARRAYLENGTH(server), i, fd == server[i].fd );
+ if( i < ARRAYLENGTH(server) )
{
WFIFOHEAD(fd,len);
memcpy(WFIFOP(fd,0), buf, len);
@@ -4431,12 +4586,16 @@ int char_config_read(const char *cfgName)
#ifndef TXT_SQL_CONVERT
void do_final(void)
{
- ShowStatus("Terminating server.\n");
+ ShowStatus("Terminating...\n");
mmo_char_sync();
inter_save();
set_all_offline(-1);
flush_fifos();
+
+ do_final_mapif();
+ do_final_loginif();
+
// write online players files with no player
online_char_db->clear(online_char_db, NULL);
create_online_files();
@@ -4445,11 +4604,12 @@ void do_final(void)
auth_db->destroy(auth_db, NULL);
if(char_dat) aFree(char_dat);
-
- if (login_fd > 0)
- do_close(login_fd);
- if (char_fd > 0)
+
+ if( char_fd != -1 )
+ {
do_close(char_fd);
+ char_fd = -1;
+ }
#ifdef ENABLE_SC_SAVING
status_final();
@@ -4458,6 +4618,7 @@ void do_final(void)
mapindex_final();
char_log("----End of char-server (normal end with closing of all files).\n");
+ ShowStatus("Finished.\n");
}
//------------------------------
@@ -4473,15 +4634,27 @@ void set_server_type(void)
SERVER_TYPE = ATHENA_SERVER_CHAR;
}
-int do_init(int argc, char **argv)
-{
- int i;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
- memset(&server[i], 0, sizeof(struct mmo_map_server));
- server[i].fd = -1;
+/// Called when a terminate signal is received.
+void do_shutdown(void)
+{
+ if( runflag != CHARSERVER_ST_SHUTDOWN )
+ {
+ int id;
+ runflag = CHARSERVER_ST_SHUTDOWN;
+ ShowStatus("Shutting down...\n");
+ // TODO proper shutdown procedure; wait for acks?, kick all characters, ... [FlavoJS]
+ for( id = 0; id < ARRAYLENGTH(server); ++id )
+ mapif_server_reset(id);
+ loginif_check_shutdown();
+ flush_fifos();
+ runflag = CORE_ST_STOP;
}
+}
+
+int do_init(int argc, char **argv)
+{
//Read map indexes
mapindex_init();
start_point.map = mapindex_name2id("new_zone01");
@@ -4512,8 +4685,6 @@ int do_init(int argc, char **argv)
inter_init_txt((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化
ShowInfo("char server initialized.\n");
- set_defaultparse(parse_char);
-
if ((naddr_ != 0) && (!login_ip || !char_ip))
{
char ip_str[16];
@@ -4533,22 +4704,13 @@ int do_init(int argc, char **argv)
}
}
- // establish char-login connection if not present
- add_timer_func_list(check_connect_login_server, "check_connect_login_server");
- add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
-
- // keep the char-login connection alive
- add_timer_func_list(ping_login_server, "ping_login_server");
- add_timer_interval(gettick() + 1000, ping_login_server, 0, 0, ((int)stall_time-2) * 1000);
+ do_init_loginif();
+ do_init_mapif();
// periodically update the overall user count on all mapservers + login server
add_timer_func_list(broadcast_user_count, "broadcast_user_count");
add_timer_interval(gettick() + 1000, broadcast_user_count, 0, 0, 5 * 1000);
- // send a list of all online account IDs to login server
- add_timer_func_list(send_accounts_tologin, "send_accounts_tologin");
- add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour
-
// ???
add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect");
@@ -4564,10 +4726,17 @@ int do_init(int argc, char **argv)
{
//##TODO invoke a CONSOLE_START plugin event
}
-
+
+ set_defaultparse(parse_char);
char_fd = make_listen_bind(bind_ip, char_port);
char_log("The char-server is ready (Server is listening on the port %d).\n", char_port);
ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port);
+
+ if( runflag != CORE_ST_STOP )
+ {
+ shutdown_callback = do_shutdown;
+ runflag = CHARSERVER_ST_RUNNING;
+ }
return 0;
}
diff --git a/src/char/char.h b/src/char/char.h
index 18bb7b1b5..f865722ba 100644
--- a/src/char/char.h
+++ b/src/char/char.h
@@ -4,8 +4,16 @@
#ifndef _CHAR_H_
#define _CHAR_H_
+#include "../common/core.h" // CORE_ST_LAST
#include "../common/mmo.h"
+enum E_CHARSERVER_ST
+{
+ CHARSERVER_ST_RUNNING = CORE_ST_LAST,
+ CHARSERVER_ST_SHUTDOWN,
+ CHARSERVER_ST_LAST
+};
+
#define MAX_MAP_SERVERS 30
#define DEFAULT_AUTOSAVE_INTERVAL 300*1000
diff --git a/src/char_sql/char.c b/src/char_sql/char.c
index 5ad5a273b..7e44890cb 100644
--- a/src/char_sql/char.c
+++ b/src/char_sql/char.c
@@ -1543,7 +1543,7 @@ int count_users(void)
int i, users;
users = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
if (server[i].fd > 0) {
users += server[i].users;
}
@@ -1779,23 +1779,74 @@ static void char_auth_ok(int fd, struct char_session_data *sd)
}
int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data);
+void mapif_server_reset(int id);
-int parse_fromlogin(int fd)
+
+/// Resets all the data.
+void loginif_reset(void)
+{
+ int id;
+ // TODO kick everyone out and reset everything or wait for connect and try to reaquire locks [FlavioJS]
+ for( id = 0; id < ARRAYLENGTH(server); ++id )
+ mapif_server_reset(id);
+ flush_fifos();
+ exit(EXIT_FAILURE);
+}
+
+
+/// Checks the conditions for the server to stop.
+/// Releases the cookie when all characters are saved.
+/// If all the conditions are met, it stops the core loop.
+void loginif_check_shutdown(void)
+{
+ if( runflag != CHARSERVER_ST_SHUTDOWN )
+ return;
+ runflag = CORE_ST_STOP;
+}
+
+
+/// Called when the connection to Login Server is disconnected.
+void loginif_on_disconnect(void)
+{
+ ShowWarning("Connection to Login Server lost.\n\n");
+}
+
+
+/// Called when all the connection steps are completed.
+void loginif_on_ready(void)
{
int i;
- struct char_session_data *sd;
- // only login-server can have an access to here.
- // so, if it isn't the login-server, we disconnect the session.
+ loginif_check_shutdown();
+
+ //Send online accounts to login server.
+ send_accounts_tologin(INVALID_TIMER, gettick(), 0, 0);
+
+ // if no map-server already connected, display a message...
+ ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd > 0 && server[i].map[0] );
+ if( i == ARRAYLENGTH(server) )
+ ShowStatus("Awaiting maps from map-server.\n");
+}
+
+
+int parse_fromlogin(int fd)
+{
+ struct char_session_data* sd = NULL;
+ int i;
+
+ // only process data from the login-server
if( fd != login_fd )
- set_eof(fd);
+ {
+ ShowDebug("parse_fromlogin: Disconnecting invalid session #%d (is not the login-server)\n", fd);
+ do_close(fd);
+ return 0;
+ }
- if(session[fd]->flag.eof) {
- if (fd == login_fd) {
- ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
- login_fd = -1;
- }
+ if( session[fd]->flag.eof )
+ {
do_close(fd);
+ login_fd = -1;
+ loginif_on_disconnect();
return 0;
}
@@ -1819,16 +1870,11 @@ int parse_fromlogin(int fd)
ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n");
ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n");
+ set_eof(fd);
+ return 0;
} else {
ShowStatus("Connected to login-server (connection #%d).\n", fd);
-
- //Send online accounts to login server.
- send_accounts_tologin(INVALID_TIMER, gettick(), 0, 0);
-
- // if no map-server already connected, display a message...
- ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd > 0 && server[i].map[0] );
- if( i == MAX_MAP_SERVERS )
- ShowStatus("Awaiting maps from map-server.\n");
+ loginif_on_ready();
}
RFIFOSKIP(fd,3);
break;
@@ -2096,6 +2142,34 @@ int parse_fromlogin(int fd)
return 0;
}
+int check_connect_login_server(int tid, unsigned int tick, int id, intptr data);
+int ping_login_server(int tid, unsigned int tick, int id, intptr data);
+int send_accounts_tologin(int tid, unsigned int tick, int id, intptr data);
+
+void do_init_loginif(void)
+{
+ // establish char-login connection if not present
+ add_timer_func_list(check_connect_login_server, "check_connect_login_server");
+ add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
+
+ // keep the char-login connection alive
+ add_timer_func_list(ping_login_server, "ping_login_server");
+ add_timer_interval(gettick() + 1000, ping_login_server, 0, 0, ((int)stall_time-2) * 1000);
+
+ // send a list of all online account IDs to login server
+ add_timer_func_list(send_accounts_tologin, "send_accounts_tologin");
+ add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour
+}
+
+void do_final_loginif(void)
+{
+ if( login_fd != -1 )
+ {
+ do_close(login_fd);
+ login_fd = -1;
+ }
+}
+
int request_accreg2(int account_id, int char_id)
{
if (login_fd > 0) {
@@ -2252,37 +2326,77 @@ int char_loadName(int char_id, char* name)
int search_mapserver(unsigned short map, uint32 ip, uint16 port);
+
+/// Initializes a server structure.
+void mapif_server_init(int id)
+{
+ memset(&server[id], 0, sizeof(server[id]));
+ server[id].fd = -1;
+}
+
+
+/// Destroys a server structure.
+void mapif_server_destroy(int id)
+{
+ if( server[id].fd == -1 )
+ {
+ do_close(server[id].fd);
+ server[id].fd = -1;
+ }
+}
+
+
+/// Resets all the data related to a server.
+void mapif_server_reset(int id)
+{
+ int i,j;
+ unsigned char buf[16384];
+ int fd = server[id].fd;
+ //Notify other map servers that this one is gone. [Skotlex]
+ WBUFW(buf,0) = 0x2b20;
+ WBUFL(buf,4) = htonl(server[id].ip);
+ WBUFW(buf,8) = htons(server[id].port);
+ j = 0;
+ for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ if (server[id].map[i])
+ WBUFW(buf,10+(j++)*4) = server[id].map[i];
+ if (j > 0) {
+ WBUFW(buf,2) = j * 4 + 10;
+ mapif_sendallwos(fd, buf, WBUFW(buf,2));
+ }
+ if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server[id].fd) )
+ Sql_ShowDebug(sql_handle);
+ online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
+ mapif_server_destroy(id);
+ mapif_server_init(id);
+}
+
+
+/// Called when the connection to a Map Server is disconnected.
+void mapif_on_disconnect(int id)
+{
+ ShowStatus("Map-server #%d has disconnected.\n", id);
+ mapif_server_reset(id);
+}
+
+
int parse_frommap(int fd)
{
int i, j;
int id;
- ARR_FIND( 0, MAX_MAP_SERVERS, id, server[id].fd == fd );
- if(id == MAX_MAP_SERVERS)
- set_eof(fd);
- if(session[fd]->flag.eof) {
- if (id < MAX_MAP_SERVERS) {
- unsigned char buf[16384];
- ShowStatus("Map-server %d (session #%d) has disconnected.\n", id, fd);
- //Notify other map servers that this one is gone. [Skotlex]
- WBUFW(buf,0) = 0x2b20;
- WBUFL(buf,4) = htonl(server[id].ip);
- WBUFW(buf,8) = htons(server[id].port);
- j = 0;
- for(i = 0; i < MAX_MAP_PER_SERVER; i++)
- if (server[id].map[i])
- WBUFW(buf,10+(j++)*4) = server[id].map[i];
- if (j > 0) {
- WBUFW(buf,2) = j * 4 + 10;
- mapif_sendallwos(fd, buf, WBUFW(buf,2));
- }
- memset(&server[id], 0, sizeof(struct mmo_map_server));
- if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server[id].fd) )
- Sql_ShowDebug(sql_handle);
- server[id].fd = -1;
- online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server.
- }
+ ARR_FIND( 0, ARRAYLENGTH(server), id, server[id].fd == fd );
+ if( id == ARRAYLENGTH(server) )
+ {// not a map server
+ ShowDebug("parse_frommap: Disconnecting invalid session #%d (is not a map-server)\n", fd);
+ do_close(fd);
+ return 0;
+ }
+ if( session[fd]->flag.eof )
+ {
do_close(fd);
+ server[id].fd = -1;
+ mapif_on_disconnect(id);
return 0;
}
@@ -2330,14 +2444,14 @@ int parse_frommap(int fd)
mapif_sendallwos(fd, buf, WBUFW(buf,2));
}
// Transmitting the maps of the other map-servers to the new map-server
- for(x = 0; x < MAX_MAP_SERVERS; x++) {
+ for(x = 0; x < ARRAYLENGTH(server); x++) {
if (server[x].fd > 0 && x != id) {
- WFIFOHEAD(fd,10 +4*MAX_MAP_PER_SERVER);
+ WFIFOHEAD(fd,10 +4*ARRAYLENGTH(server));
WFIFOW(fd,0) = 0x2b04;
WFIFOL(fd,4) = htonl(server[x].ip);
WFIFOW(fd,8) = htons(server[x].port);
j = 0;
- for(i = 0; i < MAX_MAP_PER_SERVER; i++)
+ for(i = 0; i < ARRAYLENGTH(server); i++)
if (server[x].map[i])
WFIFOW(fd,10+(j++)*4) = server[x].map[i];
if (j > 0) {
@@ -2491,27 +2605,38 @@ int parse_frommap(int fd)
uint32 login_id2 = RFIFOL(fd,10);
uint32 ip = RFIFOL(fd,14);
RFIFOSKIP(fd,18);
-
- // create temporary auth entry
- CREATE(node, struct auth_node, 1);
- node->account_id = account_id;
- node->char_id = 0;
- node->login_id1 = login_id1;
- node->login_id2 = login_id2;
- //node->sex = 0;
- node->ip = ntohl(ip);
- //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
- //node->gmlevel = 0;
- idb_put(auth_db, account_id, node);
-
- //Set char to "@ char select" in online db [Kevin]
- set_char_charselect(account_id);
-
- WFIFOHEAD(fd,7);
- WFIFOW(fd,0) = 0x2b03;
- WFIFOL(fd,2) = account_id;
- WFIFOB(fd,6) = 0;
- WFIFOSET(fd,7);
+
+ if( runflag != CHARSERVER_ST_RUNNING )
+ {
+ WFIFOHEAD(fd,7);
+ WFIFOW(fd,0) = 0x2b03;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 0;// not ok
+ WFIFOSET(fd,7);
+ }
+ else
+ {
+ // create temporary auth entry
+ CREATE(node, struct auth_node, 1);
+ node->account_id = account_id;
+ node->char_id = 0;
+ node->login_id1 = login_id1;
+ node->login_id2 = login_id2;
+ //node->sex = 0;
+ node->ip = ntohl(ip);
+ //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server)
+ //node->gmlevel = 0;
+ idb_put(auth_db, account_id, node);
+
+ //Set char to "@ char select" in online db [Kevin]
+ set_char_charselect(account_id);
+
+ WFIFOHEAD(fd,7);
+ WFIFOW(fd,0) = 0x2b03;
+ WFIFOL(fd,2) = account_id;
+ WFIFOB(fd,6) = 1;// ok
+ WFIFOSET(fd,7);
+ }
}
break;
@@ -2534,8 +2659,10 @@ int parse_frommap(int fd)
mmo_char_fromsql(RFIFOL(fd,14), &char_dat, true);
char_data = (struct mmo_charstatus*)uidb_get(char_db_,RFIFOL(fd,14));
}
-
- if (map_fd >= 0 && session[map_fd] && char_data)
+
+ if( runflag == CHARSERVER_ST_RUNNING &&
+ session_isActive(map_fd) &&
+ char_data )
{ //Send the map server the auth of this player.
struct auth_node* node;
@@ -2879,7 +3006,9 @@ int parse_frommap(int fd)
mmo_char_fromsql(char_id, &char_dat, true);
cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id);
}
- if( node != NULL && cd != NULL &&
+ if( runflag == CHARSERVER_ST_RUNNING &&
+ cd != NULL &&
+ node != NULL &&
node->account_id == account_id &&
node->char_id == char_id &&
node->login_id1 == login_id1 &&
@@ -2942,13 +3071,27 @@ int parse_frommap(int fd)
return 0;
}
+void do_init_mapif(void)
+{
+ int i;
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ mapif_server_init(i);
+}
+
+void do_final_mapif(void)
+{
+ int i;
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ mapif_server_destroy(i);
+}
+
// Searches for the mapserver that has a given map (and optionally ip/port, if not -1).
// If found, returns the server's index in the 'server' array (otherwise returns -1).
int search_mapserver(unsigned short map, uint32 ip, uint16 port)
{
int i, j;
- for(i = 0; i < MAX_MAP_SERVERS; i++)
+ for(i = 0; i < ARRAYLENGTH(server); i++)
{
if (server[i].fd > 0
&& (ip == (uint32)-1 || server[i].ip == ip)
@@ -3276,6 +3419,15 @@ int parse_char(int fd)
WFIFOL(fd,0) = account_id;
WFIFOSET(fd,4);
+ if( runflag != CHARSERVER_ST_RUNNING )
+ {
+ WFIFOHEAD(fd,3);
+ WFIFOW(fd,0) = 0x6c;
+ WFIFOB(fd,2) = 0;// rejected from server
+ WFIFOSET(fd,3);
+ break;
+ }
+
// search authentification
node = (struct auth_node*)idb_get(auth_db, account_id);
if( node != NULL &&
@@ -3361,8 +3513,8 @@ int parse_char(int fd)
if (i < 0) {
unsigned short j;
//First check that there's actually a map server online.
- ARR_FIND( 0, MAX_MAP_SERVERS, j, server[j].fd >= 0 && server[j].map[0] );
- if (j == MAX_MAP_SERVERS) {
+ ARR_FIND( 0, ARRAYLENGTH(server), j, server[j].fd >= 0 && server[j].map[0] );
+ if (j == ARRAYLENGTH(server)) {
ShowInfo("Connection Closed. No map servers available.\n");
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x81;
@@ -3665,8 +3817,12 @@ int parse_char(int fd)
char* l_pass = (char*)RFIFOP(fd,26);
l_user[23] = '\0';
l_pass[23] = '\0';
- ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd <= 0 );
- if (i == MAX_MAP_SERVERS || strcmp(l_user, userid) || strcmp(l_pass, passwd)) {
+ ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd <= 0 );
+ if( runflag != CHARSERVER_ST_RUNNING ||
+ i == ARRAYLENGTH(server) ||
+ strcmp(l_user, userid) != 0 ||
+ strcmp(l_pass, passwd) != 0 )
+ {
WFIFOHEAD(fd,3);
WFIFOW(fd,0) = 0x2af9;
WFIFOB(fd,2) = 3;
@@ -3729,7 +3885,7 @@ int mapif_sendall(unsigned char *buf, unsigned int len)
int i, c;
c = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
int fd;
if ((fd = server[i].fd) > 0) {
WFIFOHEAD(fd,len);
@@ -3747,7 +3903,7 @@ int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len)
int i, c;
c = 0;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ for(i = 0; i < ARRAYLENGTH(server); i++) {
int fd;
if ((fd = server[i].fd) > 0 && fd != sfd) {
WFIFOHEAD(fd,len);
@@ -3765,8 +3921,8 @@ int mapif_send(int fd, unsigned char *buf, unsigned int len)
int i;
if (fd >= 0) {
- ARR_FIND( 0, MAX_MAP_SERVERS, i, fd == server[i].fd );
- if( i < MAX_MAP_SERVERS )
+ ARR_FIND( 0, ARRAYLENGTH(server), i, fd == server[i].fd );
+ if( i < ARRAYLENGTH(server) )
{
WFIFOHEAD(fd,len);
memcpy(WFIFOP(fd,0), buf, len);
@@ -4211,7 +4367,7 @@ int char_config_read(const char* cfgName)
void do_final(void)
{
- ShowStatus("Terminating server.\n");
+ ShowStatus("Terminating...\n");
set_all_offline(-1);
set_all_offline_sql();
@@ -4219,6 +4375,9 @@ void do_final(void)
inter_final();
flush_fifos();
+
+ do_final_mapif();
+ do_final_loginif();
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo`") )
Sql_ShowDebug(sql_handle);
@@ -4227,13 +4386,16 @@ void do_final(void)
online_char_db->destroy(online_char_db, NULL);
auth_db->destroy(auth_db, NULL);
- if (login_fd > 0)
- do_close(login_fd);
- if (char_fd > 0)
+ if( char_fd != -1 )
+ {
do_close(char_fd);
+ char_fd = -1;
+ }
Sql_Free(sql_handle);
mapindex_final();
+
+ ShowStatus("Finished.\n");
}
//------------------------------
@@ -4249,15 +4411,27 @@ void set_server_type(void)
SERVER_TYPE = ATHENA_SERVER_CHAR;
}
-int do_init(int argc, char **argv)
-{
- int i;
- for(i = 0; i < MAX_MAP_SERVERS; i++) {
- memset(&server[i], 0, sizeof(struct mmo_map_server));
- server[i].fd = -1;
+/// Called when a terminate signal is received.
+void do_shutdown(void)
+{
+ if( runflag != CHARSERVER_ST_SHUTDOWN )
+ {
+ int id;
+ runflag = CHARSERVER_ST_SHUTDOWN;
+ ShowStatus("Shutting down...\n");
+ // TODO proper shutdown procedure; wait for acks?, kick all characters, ... [FlavoJS]
+ for( id = 0; id < ARRAYLENGTH(server); ++id )
+ mapif_server_reset(id);
+ loginif_check_shutdown();
+ flush_fifos();
+ runflag = CORE_ST_STOP;
}
+}
+
+int do_init(int argc, char **argv)
+{
//Read map indexes
mapindex_init();
start_point.map = mapindex_name2id("new_zone01");
@@ -4284,8 +4458,6 @@ int do_init(int argc, char **argv)
char_read_fame_list(); //Read fame lists.
ShowInfo("char server initialized.\n");
- set_defaultparse(parse_char);
-
if ((naddr_ != 0) && (!login_ip || !char_ip))
{
char ip_str[16];
@@ -4305,22 +4477,13 @@ int do_init(int argc, char **argv)
}
}
- // establish char-login connection if not present
- add_timer_func_list(check_connect_login_server, "check_connect_login_server");
- add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000);
-
- // keep the char-login connection alive
- add_timer_func_list(ping_login_server, "ping_login_server");
- add_timer_interval(gettick() + 1000, ping_login_server, 0, 0, ((int)stall_time-2) * 1000);
+ do_init_loginif();
+ do_init_mapif();
// periodically update the overall user count on all mapservers + login server
add_timer_func_list(broadcast_user_count, "broadcast_user_count");
add_timer_interval(gettick() + 1000, broadcast_user_count, 0, 0, 5 * 1000);
- // send a list of all online account IDs to login server
- add_timer_func_list(send_accounts_tologin, "send_accounts_tologin");
- add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour
-
// ???
add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect");
@@ -4351,9 +4514,16 @@ int do_init(int argc, char **argv)
ShowInfo("End of char server initilization function.\n");
+ set_defaultparse(parse_char);
ShowInfo("open port %d.....\n",char_port);
char_fd = make_listen_bind(bind_ip, char_port);
ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port);
+
+ if( runflag != CORE_ST_STOP )
+ {
+ shutdown_callback = do_shutdown;
+ runflag = CHARSERVER_ST_RUNNING;
+ }
return 0;
}
diff --git a/src/char_sql/char.h b/src/char_sql/char.h
index a40340cfb..73c2f9f72 100644
--- a/src/char_sql/char.h
+++ b/src/char_sql/char.h
@@ -4,6 +4,15 @@
#ifndef _CHAR_SQL_H_
#define _CHAR_SQL_H_
+#include "../common/core.h" // CORE_ST_LAST
+
+enum E_CHARSERVER_ST
+{
+ CHARSERVER_ST_RUNNING = CORE_ST_LAST,
+ CHARSERVER_ST_SHUTDOWN,
+ CHARSERVER_ST_LAST
+};
+
struct mmo_charstatus;
#define MAX_MAP_SERVERS 30
diff --git a/src/common/core.c b/src/common/core.c
index b89cc3841..bfa563d8c 100644
--- a/src/common/core.c
+++ b/src/common/core.c
@@ -24,7 +24,12 @@
#include <unistd.h>
#endif
-int runflag = 1;
+
+/// Called when a terminate signal is received.
+void (*shutdown_callback)(void) = NULL;
+
+
+int runflag = CORE_ST_RUN;
int arg_c = 0;
char **arg_v = NULL;
@@ -78,7 +83,10 @@ static void sig_proc(int sn)
case SIGTERM:
if (++is_called > 3)
exit(EXIT_SUCCESS);
- runflag = 0;
+ if( shutdown_callback != NULL )
+ shutdown_callback();
+ else
+ runflag = CORE_ST_STOP;// auto-shutdown
break;
case SIGSEGV:
case SIGFPE:
@@ -249,7 +257,7 @@ int main (int argc, char **argv)
{// Main runtime cycle
int next;
- while (runflag) {
+ while (runflag != CORE_ST_STOP) {
next = do_timer(gettick_nocache());
do_sockets(next);
}
diff --git a/src/common/core.h b/src/common/core.h
index fc4af3e3e..beb72d080 100644
--- a/src/common/core.h
+++ b/src/common/core.h
@@ -7,6 +7,7 @@
extern int arg_c;
extern char **arg_v;
+/// @see E_CORE_ST
extern int runflag;
extern char *SERVER_NAME;
extern char SERVER_TYPE;
@@ -18,4 +19,16 @@ extern void set_server_type(void);
extern void do_abort(void);
extern void do_final(void);
+/// The main loop continues until runflag is CORE_ST_STOP
+enum E_CORE_ST
+{
+ CORE_ST_STOP = 0,
+ CORE_ST_RUN,
+ CORE_ST_LAST
+};
+
+/// Called when a terminate signal is received. (Ctrl+C pressed)
+/// If NULL, runflag is set to CORE_ST_STOP instead.
+extern void (*shutdown_callback)(void);
+
#endif /* _CORE_H_ */
diff --git a/src/common/db.c b/src/common/db.c
index 595ed241d..935fe472f 100644
--- a/src/common/db.c
+++ b/src/common/db.c
@@ -271,6 +271,7 @@ static struct db_stats {
uint32 dbit_remove;
uint32 dbit_destroy;
uint32 db_iterator;
+ uint32 db_exists;
uint32 db_get;
uint32 db_getall;
uint32 db_vgetall;
@@ -304,7 +305,7 @@ static struct db_stats {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#define DB_COUNTSTAT(token) if (stats. ## token != UINT32_MAX) ++stats. ## token
#else /* !defined(DB_ENABLE_STATS) */
@@ -1087,6 +1088,7 @@ static void db_release_both(DBKey key, void *data, DBRelease which)
* dbit_obj_destroy - Destroys the iterator, unlocking the database and *
* freeing used memory. *
* db_obj_iterator - Return a new databse iterator. *
+ * db_obj_exists - Checks if an entry exists. *
* db_obj_get - Get the data identified by the key. *
* db_obj_vgetall - Get the data of the matched entries. *
* db_obj_getall - Get the data of the matched entries. *
@@ -1402,6 +1404,57 @@ static DBIterator* db_obj_iterator(DBMap* self)
}
/**
+ * Returns true if the entry exists.
+ * @param self Interface of the database
+ * @param key Key that identifies the entry
+ * @return true is the entry exists
+ * @protected
+ * @see DBMap#exists
+ */
+static bool db_obj_exists(DBMap* self, DBKey key)
+{
+ DBMap_impl* db = (DBMap_impl*)self;
+ DBNode node;
+ int c;
+ bool found = false;
+
+ DB_COUNTSTAT(db_exists);
+ if (db == NULL) return false; // nullpo candidate
+ if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) {
+ return false; // nullpo candidate
+ }
+
+ if (db->cache && db->cmp(key, db->cache->key, db->maxlen) == 0) {
+#if defined(DEBUG)
+ if (db->cache->deleted) {
+ ShowDebug("db_exists: Cache contains a deleted node. Please report this!!!\n");
+ return false;
+ }
+#endif
+ return true; // cache hit
+ }
+
+ db_free_lock(db);
+ node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE];
+ while (node) {
+ c = db->cmp(key, node->key, db->maxlen);
+ if (c == 0) {
+ if (!(node->deleted)) {
+ db->cache = node;
+ found = true;
+ }
+ break;
+ }
+ if (c < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+ db_free_unlock(db);
+ return found;
+}
+
+/**
* Get the data of the entry identifid by the key.
* @param self Interface of the database
* @param key Key that identifies the entry
@@ -2351,6 +2404,7 @@ DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsi
options = db_fix_options(type, options);
/* Interface of the database */
db->vtable.iterator = db_obj_iterator;
+ db->vtable.exists = db_obj_exists;
db->vtable.get = db_obj_get;
db->vtable.getall = db_obj_getall;
db->vtable.vgetall = db_obj_vgetall;
@@ -2493,7 +2547,7 @@ void db_final(void)
"dbit_next %10u, dbit_prev %10u,\n"
"dbit_exists %10u, dbit_remove %10u,\n"
"dbit_destroy %10u, db_iterator %10u,\n"
- "db_get %10u,\n"
+ "db_exits %10u, db_get %10u,\n"
"db_getall %10u, db_vgetall %10u,\n"
"db_ensure %10u, db_vensure %10u,\n"
"db_put %10u, db_remove %10u,\n"
@@ -2523,7 +2577,7 @@ void db_final(void)
stats.dbit_next, stats.dbit_prev,
stats.dbit_exists, stats.dbit_remove,
stats.dbit_destroy, stats.db_iterator,
- stats.db_get,
+ stats.db_exists, stats.db_get,
stats.db_getall, stats.db_vgetall,
stats.db_ensure, stats.db_vensure,
stats.db_put, stats.db_remove,
diff --git a/src/common/db.h b/src/common/db.h
index c1b224bcd..e02de2e08 100644
--- a/src/common/db.h
+++ b/src/common/db.h
@@ -360,6 +360,15 @@ struct DBMap {
DBIterator* (*iterator)(DBMap* self);
/**
+ * Returns true if the entry exists.
+ * @param self Database
+ * @param key Key that identifies the entry
+ * @return true is the entry exists
+ * @protected
+ */
+ bool (*exists)(DBMap* self, DBKey key);
+
+ /**
* Get the data of the entry identifid by the key.
* @param self Database
* @param key Key that identifies the entry
@@ -580,6 +589,11 @@ struct DBMap {
# define str2key(k) ((DBKey)(const char *)(k))
#endif /* not DB_MANUAL_CAST_TO_UNION */
+#define db_exists(db,k) ( (db)->exists((db),(k)) )
+#define idb_exists(db,k) ( (db)->exists((db),i2key(k)) )
+#define uidb_exists(db,k) ( (db)->exists((db),ui2key(k)) )
+#define strdb_exists(db,k) ( (db)->exists((db),str2key(k)) )
+
#define db_get(db,k) ( (db)->get((db),(k)) )
#define idb_get(db,k) ( (db)->get((db),i2key(k)) )
#define uidb_get(db,k) ( (db)->get((db),ui2key(k)) )
diff --git a/src/common/socket.c b/src/common/socket.c
index 262351dcf..8f60f8df5 100644
--- a/src/common/socket.c
+++ b/src/common/socket.c
@@ -1118,6 +1118,9 @@ void socket_final(void)
/// Closes a socket.
void do_close(int fd)
{
+ if( fd <= 0 ||fd >= FD_SETSIZE )
+ return;// invalid
+
flush_fifo(fd); // Try to send what's left (although it might not succeed since it's a nonblocking socket)
sFD_CLR(fd, &readfds);// this needs to be done before closing the socket
sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes
diff --git a/src/common/strlib.c b/src/common/strlib.c
index 66f281ffc..097f499e6 100644
--- a/src/common/strlib.c
+++ b/src/common/strlib.c
@@ -441,30 +441,13 @@ bool bin2hex(char* output, unsigned char* input, size_t count)
/////////////////////////////////////////////////////////////////////
-/// Parses a delim-separated string.
-/// Starts parsing at startoff and fills the pos array with position pairs.
-/// out_pos[0] and out_pos[1] are the start and end of line.
-/// Other position pairs are the start and end of fields.
-/// Returns the number of fields found or -1 if an error occurs.
-///
-/// out_pos can be NULL.
-/// If a line terminator is found, the end position is placed there.
-/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
-/// for the seconds field and so on.
-/// Unfilled positions are set to -1.
-///
-/// @param str String to parse
-/// @param len Length of the string
-/// @param startoff Where to start parsing
-/// @param delim Field delimiter
-/// @param out_pos Array of resulting positions
-/// @param npos Size of the pos array
-/// @param opt Options that determine the parsing behaviour
-/// @return Number of fields found in the string or -1 if an error occured
-int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt)
+/// Parses a single field in a delim-separated string.
+/// The delimiter after the field is skipped.
+///
+/// @param sv Parse state
+/// @return 1 if a field was parsed, 0 if already done, -1 on error.
+int sv_parse_next(struct s_svstate* sv)
{
- int i;
- int count;
enum {
START_OF_FIELD,
PARSING_FIELD,
@@ -473,27 +456,37 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
TERMINATE,
END
} state;
+ const char* str;
+ int len;
+ enum e_svopt opt;
+ char delim;
+ int i;
- // check pos/npos
- if( out_pos == NULL ) npos = 0;
- for( i = 0; i < npos; ++i )
- out_pos[i] = -1;
+ if( sv == NULL )
+ return -1;// error
+
+ str = sv->str;
+ len = sv->len;
+ opt = sv->opt;
+ delim = sv->delim;
// check opt
if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
{
- ShowError("sv_parse: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n");
+ ShowError("sv_parse_next: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n");
return -1;// error
}
if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) )
{
- ShowError("sv_parse: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
+ ShowError("sv_parse_next: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
return -1;// error
}
- // check str
- if( str == NULL )
+ if( sv->done || str == NULL )
+ {
+ sv->done = true;
return 0;// nothing to parse
+ }
#define IS_END() ( i >= len )
#define IS_DELIM() ( str[i] == delim )
@@ -502,16 +495,13 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
((opt&SV_TERMINATE_CR) && str[i] == '\r') || \
((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') )
#define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
-#define SET_FIELD_START() if( npos > count*2+2 ) out_pos[count*2+2] = i
-#define SET_FIELD_END() if( npos > count*2+3 ) out_pos[count*2+3] = i; ++count
+#define SET_FIELD_START() sv->start = i
+#define SET_FIELD_END() sv->end = i
- i = startoff;
- count = 0;
+ i = sv->off;
state = START_OF_FIELD;
- if( npos > 0 ) out_pos[0] = startoff;// start
while( state != END )
{
- if( npos > 1 ) out_pos[1] = i;// end
switch( state )
{
case START_OF_FIELD:// record start of field and start parsing it
@@ -533,7 +523,7 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
++i;// '\\'
if( IS_END() )
{
- ShowError("sv_parse: empty escape sequence\n");
+ ShowError("sv_parse_next: empty escape sequence\n");
return -1;
}
if( str[i] == 'x' )
@@ -541,7 +531,7 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
++i;// 'x'
if( IS_END() || !ISXDIGIT(str[i]) )
{
- ShowError("sv_parse: \\x with no following hex digits\n");
+ ShowError("sv_parse_next: \\x with no following hex digits\n");
return -1;
}
do{
@@ -562,26 +552,22 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
}
else
{
- ShowError("sv_parse: unknown escape sequence \\%c\n", str[i]);
+ ShowError("sv_parse_next: unknown escape sequence \\%c\n", str[i]);
return -1;
}
state = PARSING_FIELD;
break;
}
- case END_OF_FIELD:// record end of field and continue
+ case END_OF_FIELD:// record end of field and stop
SET_FIELD_END();
+ state = END;
if( IS_END() )
- state = END;
+ ;// nothing else
else if( IS_DELIM() )
- {
++i;// delim
- state = START_OF_FIELD;
- }
else if( IS_TERMINATOR() )
state = TERMINATE;
- else
- state = START_OF_FIELD;
break;
case TERMINATE:
@@ -592,10 +578,14 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
else
++i;// CR or LF
#endif
+ sv->done = true;
state = END;
break;
}
}
+ if( IS_END() )
+ sv->done = true;
+ sv->off = i;
#undef IS_END
#undef IS_DELIM
@@ -604,6 +594,58 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i
#undef SET_FIELD_START
#undef SET_FIELD_END
+ return 1;
+}
+
+
+/// Parses a delim-separated string.
+/// Starts parsing at startoff and fills the pos array with position pairs.
+/// out_pos[0] and out_pos[1] are the start and end of line.
+/// Other position pairs are the start and end of fields.
+/// Returns the number of fields found or -1 if an error occurs.
+///
+/// out_pos can be NULL.
+/// If a line terminator is found, the end position is placed there.
+/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
+/// for the seconds field and so on.
+/// Unfilled positions are set to -1.
+///
+/// @param str String to parse
+/// @param len Length of the string
+/// @param startoff Where to start parsing
+/// @param delim Field delimiter
+/// @param out_pos Array of resulting positions
+/// @param npos Size of the pos array
+/// @param opt Options that determine the parsing behaviour
+/// @return Number of fields found in the string or -1 if an error occured
+int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt)
+{
+ struct s_svstate sv;
+ int count;
+
+ // initialize
+ if( out_pos == NULL ) npos = 0;
+ for( count = 0; count < npos; ++count )
+ out_pos[count] = -1;
+ sv.str = str;
+ sv.len = len;
+ sv.off = startoff;
+ sv.opt = opt;
+ sv.delim = delim;
+ sv.done = false;
+
+ // parse
+ count = 0;
+ if( npos > 0 ) out_pos[0] = startoff;
+ while( !sv.done )
+ {
+ ++count;
+ if( sv_parse_next(&sv) <= 0 )
+ return -1;// error
+ if( npos > count*2 ) out_pos[count*2] = sv.start;
+ if( npos > count*2+1 ) out_pos[count*2+1] = sv.end;
+ }
+ if( npos > 1 ) out_pos[1] = sv.off;
return count;
}
diff --git a/src/common/strlib.h b/src/common/strlib.h
index 3f4f984cf..f5819bbcb 100644
--- a/src/common/strlib.h
+++ b/src/common/strlib.h
@@ -78,6 +78,27 @@ typedef enum e_svopt
/// Other escape sequences supported by the C compiler.
#define SV_ESCAPE_C_SUPPORTED "abtnvfr\?\"'\\"
+/// Parse state.
+/// The field is [start,end[
+struct s_svstate
+{
+ const char* str; //< string to parse
+ int len; //< string length
+ int off; //< current offset in the string
+ int start; //< where the field starts
+ int end; //< where the field ends
+ enum e_svopt opt; //< parse options
+ char delim; //< field delimiter
+ bool done; //< if all the text has been parsed
+};
+
+/// Parses a single field in a delim-separated string.
+/// The delimiter after the field is skipped.
+///
+/// @param sv Parse state
+/// @return 1 if a field was parsed, 0 if done, -1 on error.
+int sv_parse_next(struct s_svstate* sv);
+
/// Parses a delim-separated string.
/// Starts parsing at startoff and fills the pos array with position pairs.
/// out_pos[0] and out_pos[1] are the start and end of line.
diff --git a/src/login/login.c b/src/login/login.c
index 6d8043baf..ad12e6c43 100644
--- a/src/login/login.c
+++ b/src/login/login.c
@@ -190,7 +190,7 @@ int charif_sendallwos(int sfd, uint8* buf, size_t len)
{
int i, c;
- for( i = 0, c = 0; i < MAX_SERVERS; ++i )
+ for( i = 0, c = 0; i < ARRAYLENGTH(server); ++i )
{
int fd = server[i].fd;
if( session_isValid(fd) && fd != sfd )
@@ -206,6 +206,42 @@ int charif_sendallwos(int sfd, uint8* buf, size_t len)
}
+/// Initializes a server structure.
+void chrif_server_init(int id)
+{
+ memset(&server[id], 0, sizeof(server[id]));
+ server[id].fd = -1;
+}
+
+
+/// Destroys a server structure.
+void chrif_server_destroy(int id)
+{
+ if( server[id].fd != -1 )
+ {
+ do_close(server[id].fd);
+ server[id].fd = -1;
+ }
+}
+
+
+/// Resets all the data related to a server.
+void chrif_server_reset(int id)
+{
+ online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char server to offline.
+ chrif_server_destroy(id);
+ chrif_server_init(id);
+}
+
+
+/// Called when the connection to Char Server is disconnected.
+void chrif_on_disconnect(int id)
+{
+ ShowStatus("Char-server '%s' has disconnected.\n", server[id].name);
+ chrif_server_reset(id);
+}
+
+
//-----------------------------------------------------
// periodic ip address synchronization
//-----------------------------------------------------
@@ -381,9 +417,10 @@ int parse_fromchar(int fd)
uint32 ipl;
char ip[16];
- ARR_FIND( 0, MAX_SERVERS, id, server[id].fd == fd );
- if( id == MAX_SERVERS )
+ ARR_FIND( 0, ARRAYLENGTH(server), id, server[id].fd == fd );
+ if( id == ARRAYLENGTH(server) )
{// not a char server
+ ShowDebug("parse_fromchar: Disconnecting invalid session #%d (is not a char-server)\n", fd);
set_eof(fd);
do_close(fd);
return 0;
@@ -391,11 +428,9 @@ int parse_fromchar(int fd)
if( session[fd]->flag.eof )
{
- ShowStatus("Char-server '%s' has disconnected.\n", server[id].name);
- online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char server to offline.
- memset(&server[id], 0, sizeof(struct mmo_char_server));
- server[id].fd = -1;
do_close(fd);
+ server[id].fd = -1;
+ chrif_on_disconnect(id);
return 0;
}
@@ -424,8 +459,9 @@ int parse_fromchar(int fd)
RFIFOSKIP(fd,23);
node = (struct auth_node*)idb_get(auth_db, account_id);
- if( node != NULL &&
- node->account_id == account_id &&
+ if( runflag == LOGINSERVER_ST_RUNNING &&
+ node != NULL &&
+ node->account_id == account_id &&
node->login_id1 == login_id1 &&
node->login_id2 == login_id2 &&
node->sex == sex_num2str(sex) /*&&
@@ -1059,6 +1095,16 @@ void login_auth_ok(struct login_session_data* sd)
struct auth_node* node;
int i;
+ if( runflag != LOGINSERVER_ST_RUNNING )
+ {
+ // players can only login while running
+ WFIFOHEAD(fd,3);
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = 1;// server closed
+ WFIFOSET(fd,3);
+ return;
+ }
+
if( sd->level < login_config.min_level_to_connect )
{
ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d).\n", login_config.min_level_to_connect, sd->userid, sd->level);
@@ -1070,8 +1116,8 @@ void login_auth_ok(struct login_session_data* sd)
}
server_num = 0;
- for( i = 0; i < MAX_SERVERS; ++i )
- if( session_isValid(server[i].fd) )
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ if( session_isActive(server[i].fd) )
server_num++;
if( server_num == 0 )
@@ -1133,7 +1179,7 @@ void login_auth_ok(struct login_session_data* sd)
memset(WFIFOP(fd,20), 0, 24);
WFIFOW(fd,44) = 0; // unknown
WFIFOB(fd,46) = sex_str2num(sd->sex);
- for( i = 0, n = 0; i < MAX_SERVERS; ++i )
+ for( i = 0, n = 0; i < ARRAYLENGTH(server); ++i )
{
if( !session_isValid(server[i].fd) )
continue;
@@ -1404,7 +1450,11 @@ int parse_login(int fd)
login_log(session[fd]->client_addr, sd->userid, 100, message);
result = mmo_auth(sd);
- if( result == -1 && sd->sex == 'S' && sd->account_id < MAX_SERVERS && server[sd->account_id].fd == -1 )
+ if( runflag == LOGINSERVER_ST_RUNNING &&
+ result == -1 &&
+ sd->sex == 'S' &&
+ sd->account_id >= 0 && sd->account_id < ARRAYLENGTH(server) &&
+ !session_isValid(server[sd->account_id].fd) )
{
ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
safestrncpy(server[sd->account_id].name, server_name, sizeof(server[sd->account_id].name));
@@ -1592,7 +1642,7 @@ static AccountDB* get_account_engine(void)
//--------------------------------------
void do_final(void)
{
- int i, fd;
+ int i;
login_log(0, "login server", 100, "login server shutdown");
ShowStatus("Terminating...\n");
@@ -1614,15 +1664,15 @@ void do_final(void)
accounts = NULL; // destroyed in account_engines
online_db->destroy(online_db, NULL);
auth_db->destroy(auth_db, NULL);
+
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ chrif_server_destroy(i);
- for (i = 0; i < MAX_SERVERS; i++) {
- if ((fd = server[i].fd) >= 0) {
- memset(&server[i], 0, sizeof(struct mmo_char_server));
- server[i].fd = -1;
- do_close(fd);
- }
+ if( login_fd != -1 )
+ {
+ do_close(login_fd);
+ login_fd = -1;
}
- do_close(login_fd);
ShowStatus("Finished.\n");
}
@@ -1640,6 +1690,24 @@ void set_server_type(void)
SERVER_TYPE = ATHENA_SERVER_LOGIN;
}
+
+/// Called when a terminate signal is received.
+void do_shutdown(void)
+{
+ if( runflag != LOGINSERVER_ST_SHUTDOWN )
+ {
+ int id;
+ runflag = LOGINSERVER_ST_SHUTDOWN;
+ ShowStatus("Shutting down...\n");
+ // TODO proper shutdown procedure; kick all characters, wait for acks, ... [FlavioJS]
+ for( id = 0; id < ARRAYLENGTH(server); ++id )
+ chrif_server_reset(id);
+ flush_fifos();
+ runflag = CORE_ST_STOP;
+ }
+}
+
+
//------------------------------
// Login server initialization
//------------------------------
@@ -1657,9 +1725,9 @@ int do_init(int argc, char** argv)
login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME);
srand((unsigned int)time(NULL));
-
- for( i = 0; i < MAX_SERVERS; i++ )
- server[i].fd = -1;
+
+ for( i = 0; i < ARRAYLENGTH(server); ++i )
+ chrif_server_init(i);
// initialize logging
if( login_config.log_login )
@@ -1713,6 +1781,12 @@ int do_init(int argc, char** argv)
// server port open & binding
login_fd = make_listen_bind(login_config.login_ip, login_config.login_port);
+
+ if( runflag != CORE_ST_STOP )
+ {
+ shutdown_callback = do_shutdown;
+ runflag = LOGINSERVER_ST_RUNNING;
+ }
ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %u).\n\n", login_config.login_port);
login_log(0, "login server", 100, "login server started");
diff --git a/src/login/login.h b/src/login/login.h
index f338a09e1..07f05f18b 100644
--- a/src/login/login.h
+++ b/src/login/login.h
@@ -5,6 +5,14 @@
#define _LOGIN_H_
#include "../common/mmo.h" // NAME_LENGTH,SEX_*
+#include "../common/core.h" // CORE_ST_LAST
+
+enum E_LOGINSERVER_ST
+{
+ LOGINSERVER_ST_RUNNING = CORE_ST_LAST,
+ LOGINSERVER_ST_SHUTDOWN,
+ LOGINSERVER_ST_LAST
+};
#define LOGIN_CONF_NAME "conf/login_athena.conf"
#define LAN_CONF_NAME "conf/subnet_athena.conf"
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index e58f07880..04cc872b7 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -3903,23 +3903,9 @@ ACMD_FUNC(agitend2)
*------------------------------------------*/
ACMD_FUNC(mapexit)
{
- struct map_session_data* pl_sd;
- struct s_mapiterator* iter;
-
nullpo_retr(-1, sd);
- iter = mapit_getallusers();
- for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
- if (sd->status.account_id != pl_sd->status.account_id)
- clif_GM_kick(NULL, pl_sd);
- mapit_free(iter);
-
- clif_GM_kick(NULL, sd);
-
- flush_fifos();
-
- runflag = 0;
-
+ do_shutdown();
return 0;
}
diff --git a/src/map/chrif.c b/src/map/chrif.c
index c4eeea3d9..8edeb9efb 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -31,6 +31,8 @@
#include <sys/types.h>
#include <time.h>
+static int check_connect_char_server(int tid, unsigned int tick, int id, intptr data);
+
static struct eri *auth_db_ers; //For reutilizing player login structures.
static DBMap* auth_db; // int id -> struct auth_node*
@@ -94,7 +96,7 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
//2b27: Incoming, chrif_authfail -> 'client authentication failed'
int chrif_connected = 0;
-int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
+int char_fd = -1;
int srvinfo;
static char char_ip_str[128];
static uint32 char_ip = 0;
@@ -110,6 +112,28 @@ 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; }
+
+/// Resets all the data.
+void chrif_reset(void)
+{
+ // TODO kick everyone out and reset everything [FlavioJS]
+ exit(EXIT_FAILURE);
+}
+
+
+/// Checks the conditions for the server to stop.
+/// Releases the cookie when all characters are saved.
+/// If all the conditions are met, it stops the core loop.
+void chrif_check_shutdown(void)
+{
+ if( runflag != MAPSERVER_ST_SHUTDOWN )
+ return;
+ if( auth_db->size(auth_db) > 0 )
+ return;
+ runflag = CORE_ST_STOP;
+}
+
+
struct auth_node* chrif_search(int account_id)
{
return (struct auth_node*)idb_get(auth_db, account_id);
@@ -363,6 +387,7 @@ int chrif_removemap(int fd)
static void chrif_save_ack(int fd)
{
chrif_auth_delete(RFIFOL(fd,2), RFIFOL(fd,6), ST_LOGOUT);
+ chrif_check_shutdown();
}
// request to move a character between mapservers
@@ -472,19 +497,13 @@ static int chrif_reconnect(DBKey key,void *data,va_list ap)
return 0;
}
-/*==========================================
- *
- *------------------------------------------*/
-int chrif_sendmapack(int fd)
-{
- if (RFIFOB(fd,2)) {
- ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
- exit(EXIT_FAILURE);
- }
- memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
- ShowStatus("Map sending complete. Map Server is now online.\n");
+/// Called when all the connection steps are completed.
+void chrif_on_ready(void)
+{
+ ShowStatus("Map Server is now online.\n");
chrif_state = 2;
+ chrif_check_shutdown();
//If there are players online, send them to the char-server. [Skotlex]
send_users_tochar();
@@ -494,7 +513,21 @@ int chrif_sendmapack(int fd)
//Re-save any storages that were modified in the disconnection time. [Skotlex]
do_reconnect_storage();
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int chrif_sendmapack(int fd)
+{
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
+ exit(EXIT_FAILURE);
+ }
+ memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
+ chrif_on_ready();
return 0;
}
@@ -592,7 +625,8 @@ void chrif_authok(int fd)
}
sd = node->sd;
- if(node->char_dat == NULL &&
+ if( runflag == MAPSERVER_ST_RUNNING &&
+ node->char_dat == NULL &&
node->account_id == account_id &&
node->char_id == char_id &&
node->login_id1 == login_id1 )
@@ -1292,22 +1326,22 @@ int chrif_char_online(struct map_session_data *sd)
return 0;
}
-int chrif_disconnect(int fd)
+
+/// Called when the connection to Char Server is disconnected.
+void chrif_on_disconnect(void)
{
- if(fd == char_fd) {
- char_fd = 0;
- ShowWarning("Map Server disconnected from Char Server.\n\n");
- chrif_connected = 0;
-
- other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
- map_eraseallipport();
+ if( chrif_connected != 1 )
+ ShowWarning("Connection to Char Server lost.\n\n");
+ chrif_connected = 0;
+
+ other_mapserver_count = 0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
+ map_eraseallipport();
- //Attempt to reconnect in a second. [Skotlex]
- add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
- }
- return 0;
+ //Attempt to reconnect in a second. [Skotlex]
+ add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
}
+
void chrif_update_ip(int fd)
{
uint32 new_ip;
@@ -1352,10 +1386,9 @@ int chrif_parse(int fd)
if (session[fd]->flag.eof)
{
- if (chrif_connected == 1)
- chrif_disconnect(fd);
-
do_close(fd);
+ char_fd = -1;
+ chrif_on_disconnect();
return 0;
}
@@ -1393,7 +1426,7 @@ int chrif_parse(int fd)
case 0x2afb: chrif_sendmapack(fd); break;
case 0x2afd: chrif_authok(fd); break;
case 0x2b00: map_setusers(RFIFOL(fd,2)); chrif_keepalive(fd); break;
- case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
+ case 0x2b03: clif_charselectok(RFIFOL(fd,2), RFIFOB(fd,6)); break;
case 0x2b04: chrif_recvmap(fd); break;
case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break;
case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
@@ -1476,7 +1509,7 @@ int send_users_tochar(void)
* timer関数
* char鯖との接続を確認し、もし切れていたら再度接続する
*------------------------------------------*/
-int check_connect_char_server(int tid, unsigned int tick, int id, intptr data)
+static int check_connect_char_server(int tid, unsigned int tick, int id, intptr data)
{
static int displayed = 0;
if (char_fd <= 0 || session[char_fd] == NULL)
@@ -1491,7 +1524,6 @@ int check_connect_char_server(int tid, unsigned int tick, int id, intptr data)
char_fd = make_connection(char_ip, char_port);
if (char_fd == -1)
{ //Attempt to connect later. [Skotlex]
- char_fd = 0;
return 0;
}
@@ -1532,8 +1564,11 @@ int auth_db_final(DBKey k,void *d,va_list ap)
*------------------------------------------*/
int do_final_chrif(void)
{
- if (char_fd > 0)
+ if( char_fd != -1 )
+ {
do_close(char_fd);
+ char_fd = -1;
+ }
auth_db->destroy(auth_db, auth_db_final);
ers_destroy(auth_db_ers);
diff --git a/src/map/chrif.h b/src/map/chrif.h
index 9ff5b9a0e..1f11cc6f2 100644
--- a/src/map/chrif.h
+++ b/src/map/chrif.h
@@ -25,6 +25,7 @@ int chrif_setip(const char* ip);
void chrif_setport(uint16 port);
int chrif_isconnected(void);
+void chrif_check_shutdown(void);
extern int chrif_connected;
extern int other_mapserver_count;
@@ -55,7 +56,6 @@ int send_users_tochar(void);
int chrif_char_online(struct map_session_data *sd);
int chrif_changesex(struct map_session_data *sd);
int chrif_chardisconnect(struct map_session_data *sd);
-int check_connect_char_server(int tid, unsigned int tick, int id, intptr data);
int chrif_divorce(int partner_id1, int partner_id2);
int do_final_chrif(void);
diff --git a/src/map/clif.c b/src/map/clif.c
index bffb708d5..ac8382a63 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -581,10 +581,10 @@ int clif_authfail_fd(int fd, int type)
return 0;
}
-/*==========================================
- *
- *------------------------------------------*/
-int clif_charselectok(int id)
+/// Reply from char-server.
+/// Tells the player if it can connect to the char-server to select a character.
+/// ok=1 : client disconnects and tries to connect to the char-server
+int clif_charselectok(int id, uint8 ok)
{
struct map_session_data* sd;
int fd;
@@ -595,7 +595,7 @@ int clif_charselectok(int id)
fd = sd->fd;
WFIFOHEAD(fd,packet_len(0xb3));
WFIFOW(fd,0) = 0xb3;
- WFIFOB(fd,2) = 1;
+ WFIFOB(fd,2) = ok;
WFIFOSET(fd,packet_len(0xb3));
return 0;
@@ -8392,6 +8392,12 @@ void clif_parse_WantToConnection(int fd, TBL_PC* sd)
return;
}
+ if( runflag != MAPSERVER_ST_RUNNING )
+ {// not allowed
+ clif_authfail_fd(fd,1);// server closed
+ return;
+ }
+
//Check for double login.
bl = map_id2bl(account_id);
if(bl && bl->type != BL_PC) {
diff --git a/src/map/clif.h b/src/map/clif.h
index 1d50461a6..0feeb22b5 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -220,7 +220,7 @@ uint16 clif_getport(void);
int clif_authok(struct map_session_data *);
int clif_authfail_fd(int fd,int type);
-int clif_charselectok(int);
+int clif_charselectok(int id, uint8 ok);
int clif_dropflooritem(struct flooritem_data *);
int clif_clearflooritem(struct flooritem_data *,int);
diff --git a/src/map/map.c b/src/map/map.c
index c086b73c0..8209c1033 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -3491,7 +3491,7 @@ void do_final(void)
#ifndef TXT_ONLY
map_sql_close();
#endif /* not TXT_ONLY */
- ShowStatus("Successfully terminated.\n");
+ ShowStatus("Finished.\n");
}
static int map_abort_sub(struct map_session_data* sd, va_list ap)
@@ -3573,6 +3573,27 @@ void set_server_type(void)
SERVER_TYPE = ATHENA_SERVER_MAP;
}
+
+/// Called when a terminate signal is received.
+void do_shutdown(void)
+{
+ if( runflag != MAPSERVER_ST_SHUTDOWN )
+ {
+ runflag = MAPSERVER_ST_SHUTDOWN;
+ ShowStatus("Shutting down...\n");
+ {
+ struct map_session_data* sd;
+ struct s_mapiterator* iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ clif_GM_kick(NULL, sd);
+ mapit_free(iter);
+ flush_fifos();
+ }
+ chrif_check_shutdown();
+ }
+}
+
+
int do_init(int argc, char *argv[])
{
int i;
@@ -3710,6 +3731,12 @@ int do_init(int argc, char *argv[])
ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n");
ShowStatus("Server is '"CL_GREEN"ready"CL_RESET"' and listening on port '"CL_WHITE"%d"CL_RESET"'.\n\n", map_port);
+
+ if( runflag != CORE_ST_STOP )
+ {
+ shutdown_callback = do_shutdown;
+ runflag = MAPSERVER_ST_RUNNING;
+ }
return 0;
}
diff --git a/src/map/map.h b/src/map/map.h
index f1e7e49b3..06514f5d2 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -7,6 +7,7 @@
#ifndef _CBASETYPES_H_
#include "../common/cbasetypes.h"
#endif
+#include "../common/core.h" // CORE_ST_LAST
#include "../common/mmo.h"
#include "../common/mapindex.h"
#include "../common/db.h"
@@ -16,6 +17,13 @@
struct npc_data;
struct item_data;
+enum E_MAPSERVER_ST
+{
+ MAPSERVER_ST_RUNNING = CORE_ST_LAST,
+ MAPSERVER_ST_SHUTDOWN,
+ MAPSERVER_ST_LAST
+};
+
//Uncomment to enable the Cell Stack Limit mod.
//It's only config is the battle_config cell_stack_limit.
//Only chars affected are those defined in BL_CHAR (mobs and players currently)
@@ -695,4 +703,6 @@ extern char mob_db2_db[32];
#endif /* not TXT_ONLY */
+void do_shutdown(void);
+
#endif /* _MAP_H_ */