From e054cbbdb28a9b555faafd689282140c01d55b3e Mon Sep 17 00:00:00 2001 From: flaviojs Date: Thu, 16 Jun 2011 10:22:35 +0000 Subject: * 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 --- src/char/char.c | 371 +++++++++++++++++++++++++++++++++++++-------------- src/char/char.h | 8 ++ src/char_sql/char.c | 376 ++++++++++++++++++++++++++++++++++++++-------------- src/char_sql/char.h | 9 ++ src/common/core.c | 14 +- src/common/core.h | 13 ++ src/common/db.c | 60 ++++++++- src/common/db.h | 14 ++ src/common/socket.c | 3 + src/common/strlib.c | 136 ++++++++++++------- src/common/strlib.h | 21 +++ src/login/login.c | 122 +++++++++++++---- src/login/login.h | 8 ++ src/map/atcommand.c | 16 +-- src/map/chrif.c | 99 +++++++++----- src/map/chrif.h | 2 +- src/map/clif.c | 16 ++- src/map/clif.h | 2 +- src/map/map.c | 29 +++- src/map/map.h | 10 ++ 20 files changed, 993 insertions(+), 336 deletions(-) (limited to 'src') 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 #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. * @@ -1401,6 +1403,57 @@ static DBIterator* db_obj_iterator(DBMap* self) return &it->vtable; } +/** + * 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 @@ -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 @@ -359,6 +359,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 @@ -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 #include +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_ */ -- cgit v1.2.3-60-g2f50