/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "common/setup_before.h" #include "setup.h" #ifdef HAVE_STDDEF_H # include #else # ifndef NULL # define NULL ((void *)0) # endif #endif #ifdef STDC_HEADERS # include #else # ifdef HAVE_MALLOC_H # include # endif #endif #ifdef HAVE_STRING_H # include #else # ifdef HAVE_STRINGS_H # include # endif #endif #include "compat/strsep.h" #include "compat/char_bit.h" #ifdef HAVE_SYS_TYPES_H # include /* needed to include netinet/in.h */ #endif #ifdef HAVE_SYS_SOCKET_H # include #endif #include "compat/socket.h" #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #include "compat/netinet_in.h" #ifdef HAVE_ARPA_INET_H # include /* FIXME: probably not needed... do some systems put types in here or something? */ #endif #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #include "compat/psock.h" #include "d2gs.h" #include "game.h" #include "net.h" #include "bit.h" #include "prefs.h" #include "connection.h" #include "common/introtate.h" #include "common/addr.h" #include "common/list.h" #include "common/eventlog.h" #include "common/xalloc.h" #include "common/setup_after.h" static t_list * d2gslist_head=NULL; static unsigned int d2gs_id=0; static unsigned int total_d2gs=0; extern t_list * d2gslist(void) { return d2gslist_head; } extern int d2gslist_create(void) { d2gslist_head=list_create(); return d2gslist_reload(prefs_get_d2gs_list()); } extern int d2gslist_reload(char const * gslist) { t_addrlist * gsaddrs; t_d2gs * gs; if (!d2gslist_head) return -1; BEGIN_LIST_TRAVERSE_DATA(d2gslist_head,gs) { BIT_CLR_FLAG(gs->flag, D2GS_FLAG_VALID); } END_LIST_TRAVERSE_DATA() gsaddrs = addrlist_create(gslist, INADDR_ANY, 0); if (gsaddrs) { t_elem const *acurr; t_addr * curr_laddr; LIST_TRAVERSE_CONST(gsaddrs, acurr) { curr_laddr = (t_addr*)elem_get_data(acurr); if (!curr_laddr) { eventlog(eventlog_level_error, __FUNCTION__, "found NULL value in gslist"); continue; } if (!(gs=d2gslist_find_gs_by_ip(addr_get_ip(curr_laddr)))) gs = d2gs_create(addr_num_to_ip_str(addr_get_ip(curr_laddr))); if (gs) BIT_SET_FLAG(gs->flag, D2GS_FLAG_VALID); } addrlist_destroy(gsaddrs); } BEGIN_LIST_TRAVERSE_DATA(d2gslist_head,gs) { if (!BIT_TST_FLAG(gs->flag, D2GS_FLAG_VALID)) { d2gs_destroy(gs,&curr_elem_); } } END_LIST_TRAVERSE_DATA() return 0; } extern int d2gslist_destroy(void) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head,gs) { d2gs_destroy(gs,(t_elem **)&curr_elem_); } END_LIST_TRAVERSE_DATA_CONST() d2cs_connlist_reap(); if (list_destroy(d2gslist_head)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error destroy d2gs list"); return -1; } d2gslist_head=NULL; return 0; } extern t_d2gs * d2gslist_find_gs_by_ip(unsigned int ip) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head,gs) { if (gs->ip==ip) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gslist_find_gs(unsigned int id) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head,gs) { if (gs->id==id) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gs_create(char const * ipaddr) { t_d2gs * gs; unsigned int ip; ASSERT(ipaddr,NULL); if ((ip=net_inet_addr(ipaddr))==~0U) { eventlog(eventlog_level_error,__FUNCTION__,"got bad ip address %s",ipaddr); return NULL; } if (d2gslist_find_gs_by_ip(ntohl(ip))) { eventlog(eventlog_level_error,__FUNCTION__,"game server %s already in list",ipaddr); return NULL; } gs=xmalloc(sizeof(t_d2gs)); gs->ip=ntohl(ip); gs->id=++d2gs_id; gs->active=0; gs->token=0; gs->state=d2gs_state_none; gs->gamenum=0; gs->maxgame=0; gs->connection=NULL; if (list_append_data(d2gslist_head,gs)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error add gs to list"); xfree(gs); return NULL; } eventlog(eventlog_level_info,__FUNCTION__,"added game server %s (id: %d) to list",ipaddr,gs->id); return gs; } extern int d2gs_destroy(t_d2gs * gs, t_elem ** curr) { ASSERT(gs,-1); if (list_remove_data(d2gslist_head,gs,curr)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error remove gs from list"); return -1; } if (gs->active && gs->connection) { d2cs_conn_set_state(gs->connection, conn_state_destroy); d2gs_deactive(gs, gs->connection); } eventlog(eventlog_level_info,__FUNCTION__,"removed game server %s (id: %d) from list",addr_num_to_ip_str(gs->ip),gs->id); xfree(gs); return 0; } extern t_d2gs * d2gslist_get_server_by_id(unsigned int id) { t_d2gs * gs; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head,gs) { if (gs->id==id) return gs; } END_LIST_TRAVERSE_DATA_CONST() return NULL; } extern t_d2gs * d2gslist_choose_server(void) { t_d2gs * gs; t_d2gs * ogs; unsigned int percent; unsigned int min_percent=100; ogs=NULL; BEGIN_LIST_TRAVERSE_DATA_CONST(d2gslist_head,gs) { if (!gs->active) continue; if (!gs->connection) continue; if (gs->state!=d2gs_state_authed) continue; if (!gs->maxgame) continue; if (gs->gamenum>=gs->maxgame) continue; percent=100*gs->gamenum/gs->maxgame; if (percentstate=state; return 0; } extern t_d2gs_state d2gs_get_state(t_d2gs const * gs) { ASSERT(gs,d2gs_state_none); return gs->state; } extern int d2gs_add_gamenum(t_d2gs * gs, int number) { ASSERT(gs,-1); gs->gamenum += number; return 0; } extern unsigned int d2gs_get_gamenum(t_d2gs const * gs) { ASSERT(gs,0); return gs->gamenum; } extern int d2gs_set_maxgame(t_d2gs * gs,unsigned int maxgame) { ASSERT(gs,-1); gs->maxgame=maxgame; return 0; } extern unsigned int d2gs_get_maxgame(t_d2gs const * gs) { ASSERT(gs,0); return gs->maxgame; } extern unsigned int d2gs_get_id(t_d2gs const * gs) { ASSERT(gs,0); return gs->id; } extern unsigned int d2gs_get_ip(t_d2gs const * gs) { ASSERT(gs,0); return gs->ip; } extern unsigned int d2gs_get_token(t_d2gs const * gs) { return gs->token; } extern unsigned int d2gs_make_token(t_d2gs * gs) { return ((unsigned int)rand())^((++(gs->token))+((unsigned int)time(NULL))); } extern t_connection * d2gs_get_connection(t_d2gs const * gs) { ASSERT(gs,NULL); return gs->connection; } extern int d2gs_active(t_d2gs * gs, t_connection * c) { ASSERT(gs,-1); ASSERT(c,-1); if (gs->active && gs->connection) { eventlog(eventlog_level_warn,__FUNCTION__,"game server %d is already actived, deactive previous connection first",gs->id); d2gs_deactive(gs, gs->connection); } total_d2gs++; eventlog(eventlog_level_info,__FUNCTION__,"game server %s (id: %d) actived (%d total)",addr_num_to_addr_str(d2cs_conn_get_addr(c), d2cs_conn_get_port(c)),gs->id,total_d2gs); gs->state=d2gs_state_authed; gs->connection=c; gs->active=1; gs->gamenum=0; gs->maxgame=0; return 0; } extern int d2gs_deactive(t_d2gs * gs, t_connection * c) { t_game * game; ASSERT(gs,-1); if (!gs->active || !gs->connection) { eventlog(eventlog_level_warn,__FUNCTION__,"game server %d is not actived yet", gs->id); return -1; } if (gs->connection != c) { eventlog(eventlog_level_debug,__FUNCTION__,"game server %d connection mismatch,ignore it", gs->id); return 0; } total_d2gs--; eventlog(eventlog_level_info,__FUNCTION__,"game server %s (id: %d) deactived (%d left)",addr_num_to_addr_str(d2cs_conn_get_addr(gs->connection),d2cs_conn_get_port(gs->connection)),gs->id,total_d2gs); gs->state=d2gs_state_none; gs->connection=NULL; gs->active=0; gs->maxgame=0; eventlog(eventlog_level_info,__FUNCTION__,"destroying all games on game server %d",gs->id); BEGIN_LIST_TRAVERSE_DATA(d2cs_gamelist(),game) { if (game_get_d2gs(game)==gs) game_destroy(game,&curr_elem_); } END_LIST_TRAVERSE_DATA() if (gs->gamenum!=0) { eventlog(eventlog_level_error,__FUNCTION__,"game server %d deactived but still with games left",gs->id); } gs->gamenum=0; return 0; } extern unsigned int d2gs_calc_checksum(t_connection * c) { unsigned int sessionnum, checksum, port, addr; unsigned int i, len, ch; char const * realmname; char const * password; ASSERT(c,0); sessionnum=d2cs_conn_get_sessionnum(c); checksum=prefs_get_d2gs_checksum(); port=d2cs_conn_get_port(c); addr=d2cs_conn_get_addr(c); realmname=prefs_get_realmname(); password=prefs_get_d2gs_password(); len=strlen(realmname); for (i=0; iu.d2cs_d2gs.h.seqno,0); BEGIN_LIST_TRAVERSE_DATA(d2gslist_head,gs) { if (gs->active && gs->connection) { conn_push_outqueue(gs->connection,packet); } } END_LIST_TRAVERSE_DATA() packet_del_ref(packet); return 0; } extern int d2gs_restart_all_gs(void) { t_packet * packet; t_d2gs * gs; if (!(packet=packet_create(packet_class_d2gs))) { eventlog(eventlog_level_error, __FUNCTION__, "error creating packet"); return -1; } packet_set_size(packet,sizeof(t_d2cs_d2gs_control)); packet_set_type(packet,D2CS_D2GS_CONTROL); /* FIXME: sequence number not set */ bn_int_set(&packet->u.d2cs_d2gs.h.seqno,0); bn_int_set(&packet->u.d2cs_d2gs_control.cmd, D2CS_D2GS_CONTROL_CMD_RESTART); bn_int_set(&packet->u.d2cs_d2gs_control.value, prefs_get_d2gs_restart_delay()); BEGIN_LIST_TRAVERSE_DATA(d2gslist_head,gs) { if (gs->connection) { conn_push_outqueue(gs->connection,packet); } } END_LIST_TRAVERSE_DATA() packet_del_ref(packet); return 0; }