2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 12:35:38 +00:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file mserv.c
/// \brief Commands used for communicate with the master server
# ifdef __GNUC__
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
# endif
# if !defined (UNDER_CE)
# include <time.h>
# endif
# if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET)
# define NONET
# endif
# ifndef NONET
# ifndef NO_IPV6
# define HAVE_IPV6
# endif
# if (defined (_WIN32) || defined (_WIN32_WCE)) && !defined (_XBOX)
# define RPC_NO_WINDOWS_H
# ifdef HAVE_IPV6
# include <ws2tcpip.h>
# else
# include <winsock.h> // socket(),...
# endif //!HAVE_IPV6
# else
# ifdef __OS2__
# include <sys/types.h>
# endif // __OS2__
# ifdef HAVE_LWIP
# include <lwip/inet.h>
# include <kos/net.h>
# include <lwip/lwip.h>
# define ioctl lwip_ioctl
# else
# include <arpa/inet.h>
# ifdef __APPLE_CC__
# ifndef _BSD_SOCKLEN_T_
# define _BSD_SOCKLEN_T_
# endif
# endif
# include <sys/socket.h> // socket(),...
# include <netinet/in.h> // sockaddr_in
# ifdef _PS3
# include <net/select.h>
# elif !defined(_arch_dreamcast)
# include <netdb.h> // getaddrinfo(),...
# include <sys/ioctl.h>
# endif
# endif
# ifdef _arch_dreamcast
2017-09-14 20:15:38 +00:00
# include "sdl12/SRB2DC/dchelp.h"
2014-03-15 16:59:03 +00:00
# endif
# include <sys/time.h> // timeval,... (TIMEOUT)
# include <errno.h>
# endif // _WIN32/_WIN32_WCE
# ifdef __OS2__
# include <errno.h>
# endif // __OS2__
# endif // !NONET
# include "doomstat.h"
# include "doomdef.h"
# include "command.h"
# include "i_net.h"
# include "console.h"
# include "mserv.h"
# include "d_net.h"
# include "i_tcp.h"
# include "i_system.h"
# include "byteptr.h"
# include "m_menu.h"
# include "m_argv.h" // Alam is going to kill me <3
# include "m_misc.h" // GetRevisionString()
# ifdef _WIN32_WCE
2017-09-14 20:15:38 +00:00
# include "sdl12/SRB2CE/cehelp.h"
2014-03-15 16:59:03 +00:00
# endif
# include "i_addrinfo.h"
2020-03-01 10:36:56 +00:00
/* HTTP */
int HMS_in_use ( void ) ;
2020-03-01 19:19:59 +00:00
void HMS_fetch_rooms ( int joining ) ;
2020-03-01 10:36:56 +00:00
int HMS_register ( void ) ;
void HMS_unlist ( void ) ;
void HMS_update ( void ) ;
void HMS_list_servers ( void ) ;
void HMS_fetch_servers ( msg_server_t * list , int room ) ;
2014-03-15 16:59:03 +00:00
// ================================ DEFINITIONS ===============================
# define PACKET_SIZE 1024
2017-12-27 00:56:50 +00:00
# define MS_NO_ERROR 0
# define MS_SOCKET_ERROR -201
# define MS_CONNECT_ERROR -203
# define MS_WRITE_ERROR -210
# define MS_READ_ERROR -211
# define MS_CLOSE_ERROR -212
# define MS_GETHOSTBYNAME_ERROR -220
# define MS_GETHOSTNAME_ERROR -221
# define MS_TIMEOUT_ERROR -231
2014-03-15 16:59:03 +00:00
// see master server code for the values
2017-12-27 00:56:50 +00:00
# define ADD_SERVER_MSG 101
# define REMOVE_SERVER_MSG 103
# define ADD_SERVERv2_MSG 104
# define GET_SERVER_MSG 200
# define GET_SHORT_SERVER_MSG 205
# define ASK_SERVER_MSG 206
# define ANSWER_ASK_SERVER_MSG 207
# define ASK_SERVER_MSG 206
# define ANSWER_ASK_SERVER_MSG 207
# define GET_MOTD_MSG 208
# define SEND_MOTD_MSG 209
# define GET_ROOMS_MSG 210
# define SEND_ROOMS_MSG 211
# define GET_ROOMS_HOST_MSG 212
# define GET_VERSION_MSG 213
# define SEND_VERSION_MSG 214
# define GET_BANNED_MSG 215 // Someone's been baaaaaad!
# define PING_SERVER_MSG 216
2014-03-15 16:59:03 +00:00
# define HEADER_SIZE (sizeof (INT32)*4)
# define HEADER_MSG_POS 0
# define IP_MSG_POS 16
# define PORT_MSG_POS 32
# define HOSTNAME_MSG_POS 40
# if defined(_MSC_VER)
# pragma pack(1)
# endif
/** A message to be exchanged with the master server.
*/
typedef struct
{
INT32 id ; ///< Unused?
INT32 type ; ///< Type of message.
INT32 room ; ///< Because everyone needs a roomie.
UINT32 length ; ///< Length of the message.
char buffer [ PACKET_SIZE ] ; ///< Actual contents of the message.
} ATTRPACK msg_t ;
# if defined(_MSC_VER)
# pragma pack()
# endif
typedef struct Copy_CVarMS_t
{
char ip [ 64 ] ;
char port [ 8 ] ;
char name [ 64 ] ;
} Copy_CVarMS_s ;
static Copy_CVarMS_s registered_server ;
static time_t MSLastPing ;
# if defined(_MSC_VER)
# pragma pack(1)
# endif
typedef struct
{
char ip [ 16 ] ; // Big enough to hold a full address.
UINT16 port ;
UINT8 padding1 [ 2 ] ;
tic_t time ;
} ATTRPACK ms_holepunch_packet_t ;
# if defined(_MSC_VER)
# pragma pack()
# endif
// win32 or djgpp
# if defined (_WIN32) || defined (_WIN32_WCE) || defined (__DJGPP__)
# define ioctl ioctlsocket
# define close closesocket
# ifdef WATTCP
# define strerror strerror_s
# endif
# if defined (_WIN32) || defined (_WIN32_WCE)
# undef errno
# define errno h_errno // some very strange things happen when not using h_error
# endif
# ifndef AI_ADDRCONFIG
# define AI_ADDRCONFIG 0x00000400
# endif
# endif
# ifndef NONET
static void Command_Listserv_f ( void ) ;
# endif
static void MasterServer_OnChange ( void ) ;
static void ServerName_OnChange ( void ) ;
# define DEF_PORT "28900"
consvar_t cv_masterserver = { " masterserver " , " ms.srb2.org: " DEF_PORT , CV_SAVE , NULL , MasterServer_OnChange , 0 , NULL , NULL , 0 , 0 , NULL } ;
2018-11-29 13:49:50 +00:00
consvar_t cv_servername = { " servername " , " SRB2Kart server " , CV_SAVE | CV_CALL | CV_NOINIT , NULL , ServerName_OnChange , 0 , NULL , NULL , 0 , 0 , NULL } ;
2014-03-15 16:59:03 +00:00
INT16 ms_RoomId = - 1 ;
static enum { MSCS_NONE , MSCS_WAITING , MSCS_REGISTERED , MSCS_FAILED } con_state = MSCS_NONE ;
static INT32 msnode = - 1 ;
UINT16 current_port = 0 ;
# if (defined (_WIN32) || defined (_WIN32_WCE) || defined (_WIN32)) && !defined (NONET)
typedef SOCKET SOCKET_TYPE ;
# define ERRSOCKET (SOCKET_ERROR)
# else
# if (defined (__unix__) && !defined (MSDOS)) || defined (__APPLE__) || defined (__HAIKU__) || defined (_PS3)
typedef int SOCKET_TYPE ;
# else
typedef unsigned long SOCKET_TYPE ;
# endif
# define ERRSOCKET (-1)
# endif
# if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (_WIN32)
typedef int socklen_t ;
# endif
# ifndef NONET
2017-12-27 00:56:50 +00:00
static SOCKET_TYPE socket_fd = ERRSOCKET ; // WINSOCK socket
2014-03-15 16:59:03 +00:00
static struct timeval select_timeout ;
static fd_set wset ;
static size_t recvfull ( SOCKET_TYPE s , char * buf , size_t len , int flags ) ;
# endif
// Room list is an external variable now.
// Avoiding having to get info ten thousand times...
msg_rooms_t room_list [ NUM_LIST_ROOMS + 1 ] ; // +1 for easy test
/** Adds variables and commands relating to the master server.
*
* \ sa cv_masterserver , cv_servername ,
* Command_Listserv_f
*/
void AddMServCommands ( void )
{
# ifndef NONET
CV_RegisterVar ( & cv_masterserver ) ;
2020-03-01 10:36:56 +00:00
CV_RegisterVar ( & cv_http_masterserver ) ;
CV_RegisterVar ( & cv_masterserver_token ) ;
2014-03-15 16:59:03 +00:00
CV_RegisterVar ( & cv_servername ) ;
COM_AddCommand ( " listserv " , Command_Listserv_f ) ;
# endif
}
/** Closes the connection to the master server.
*
* \ todo Fix for Windows ?
*/
static void CloseConnection ( void )
{
# ifndef NONET
2017-12-27 00:56:50 +00:00
if ( socket_fd ! = ( SOCKET_TYPE ) ERRSOCKET )
2014-03-15 16:59:03 +00:00
close ( socket_fd ) ;
2017-12-27 00:56:50 +00:00
socket_fd = ERRSOCKET ;
2014-03-15 16:59:03 +00:00
# endif
}
//
// MS_Write():
//
static INT32 MS_Write ( msg_t * msg )
{
# ifdef NONET
( void ) msg ;
return MS_WRITE_ERROR ;
# else
size_t len ;
if ( msg - > length = = 0 )
msg - > length = ( INT32 ) strlen ( msg - > buffer ) ;
len = msg - > length + HEADER_SIZE ;
msg - > type = htonl ( msg - > type ) ;
msg - > length = htonl ( msg - > length ) ;
msg - > room = htonl ( msg - > room ) ;
if ( ( size_t ) send ( socket_fd , ( char * ) msg , ( int ) len , 0 ) ! = len )
return MS_WRITE_ERROR ;
return 0 ;
# endif
}
//
// MS_Read():
//
static INT32 MS_Read ( msg_t * msg )
{
# ifdef NONET
( void ) msg ;
return MS_READ_ERROR ;
# else
if ( recvfull ( socket_fd , ( char * ) msg , HEADER_SIZE , 0 ) ! = HEADER_SIZE )
return MS_READ_ERROR ;
msg - > type = ntohl ( msg - > type ) ;
msg - > length = ntohl ( msg - > length ) ;
msg - > room = ntohl ( msg - > room ) ;
if ( ! msg - > length ) // fix a bug in Windows 2000
return 0 ;
if ( recvfull ( socket_fd , ( char * ) msg - > buffer , msg - > length , 0 ) ! = msg - > length )
return MS_READ_ERROR ;
return 0 ;
# endif
}
# ifndef NONET
/** Gets a list of game servers from the master server.
*/
static INT32 GetServersList ( void )
{
msg_t msg ;
INT32 count = 0 ;
msg . type = GET_SERVER_MSG ;
msg . length = 0 ;
msg . room = 0 ;
if ( MS_Write ( & msg ) < 0 )
return MS_WRITE_ERROR ;
while ( MS_Read ( & msg ) > = 0 )
{
if ( ! msg . length )
{
if ( ! count )
CONS_Alert ( CONS_NOTICE , M_GetText ( " No servers currently running. \n " ) ) ;
return MS_NO_ERROR ;
}
count + + ;
CONS_Printf ( " %s " , msg . buffer ) ;
}
return MS_READ_ERROR ;
}
# endif
//
// MS_Connect()
//
static INT32 MS_Connect ( const char * ip_addr , const char * str_port , INT32 async )
{
# ifdef NONET
( void ) ip_addr ;
( void ) str_port ;
( void ) async ;
# else
struct my_addrinfo * ai , * runp , hints ;
int gaie ;
memset ( & hints , 0x00 , sizeof ( hints ) ) ;
# ifdef AI_ADDRCONFIG
hints . ai_flags = AI_ADDRCONFIG ;
# endif
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_protocol = IPPROTO_TCP ;
//I_InitTcpNetwork(); this is already done on startup in D_SRB2Main()
if ( ! I_InitTcpDriver ( ) ) // this is done only if not already done
return MS_SOCKET_ERROR ;
gaie = I_getaddrinfo ( ip_addr , str_port , & hints , & ai ) ;
if ( gaie ! = 0 )
return MS_GETHOSTBYNAME_ERROR ;
else
runp = ai ;
while ( runp ! = NULL )
{
socket_fd = socket ( runp - > ai_family , runp - > ai_socktype , runp - > ai_protocol ) ;
2017-12-27 00:56:50 +00:00
if ( socket_fd ! = ( SOCKET_TYPE ) ERRSOCKET )
2014-03-15 16:59:03 +00:00
{
if ( async ) // do asynchronous connection
{
# ifdef FIONBIO
# ifdef WATTCP
char res = 1 ;
# else
unsigned long res = 1 ;
# endif
ioctl ( socket_fd , FIONBIO , & res ) ;
# endif
if ( connect ( socket_fd , runp - > ai_addr , ( socklen_t ) runp - > ai_addrlen ) = = ERRSOCKET )
{
# ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows)
if ( WSAGetLastError ( ) ! = WSAEWOULDBLOCK )
# else
if ( errno ! = EINPROGRESS )
# endif
{
con_state = MSCS_FAILED ;
CloseConnection ( ) ;
I_freeaddrinfo ( ai ) ;
return MS_CONNECT_ERROR ;
}
}
con_state = MSCS_WAITING ;
FD_ZERO ( & wset ) ;
FD_SET ( socket_fd , & wset ) ;
select_timeout . tv_sec = 0 , select_timeout . tv_usec = 0 ;
I_freeaddrinfo ( ai ) ;
return 0 ;
}
else if ( connect ( socket_fd , runp - > ai_addr , ( socklen_t ) runp - > ai_addrlen ) ! = ERRSOCKET )
{
I_freeaddrinfo ( ai ) ;
return 0 ;
}
}
runp = runp - > ai_next ;
}
I_freeaddrinfo ( ai ) ;
# endif
return MS_CONNECT_ERROR ;
}
# define NUM_LIST_SERVER MAXSERVERLIST
const msg_server_t * GetShortServersList ( INT32 room )
{
static msg_server_t server_list [ NUM_LIST_SERVER + 1 ] ; // +1 for easy test
msg_t msg ;
INT32 i ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
HMS_fetch_servers ( server_list , room ) ;
return server_list ;
}
2014-03-15 16:59:03 +00:00
// we must be connected to the master server before writing to it
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
M_StartMessage ( M_GetText ( " There was a problem connecting to \n the Master Server \n " ) , NULL , MM_NOTHING ) ;
return NULL ;
}
msg . type = GET_SHORT_SERVER_MSG ;
msg . length = 0 ;
msg . room = room ;
if ( MS_Write ( & msg ) < 0 )
return NULL ;
for ( i = 0 ; i < NUM_LIST_SERVER & & MS_Read ( & msg ) > = 0 ; i + + )
{
if ( ! msg . length )
{
server_list [ i ] . header . buffer [ 0 ] = 0 ;
CloseConnection ( ) ;
return server_list ;
}
M_Memcpy ( & server_list [ i ] , msg . buffer , sizeof ( msg_server_t ) ) ;
server_list [ i ] . header . buffer [ 0 ] = 1 ;
}
CloseConnection ( ) ;
if ( i = = NUM_LIST_SERVER )
{
server_list [ i ] . header . buffer [ 0 ] = 0 ;
return server_list ;
}
else
return NULL ;
}
INT32 GetRoomsList ( boolean hosting )
{
static msg_ban_t banned_info [ 1 ] ;
msg_t msg ;
INT32 i ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
2020-03-01 19:19:59 +00:00
HMS_fetch_rooms ( ! hosting ) ;
2020-03-01 10:36:56 +00:00
return 1 ;
}
2014-03-15 16:59:03 +00:00
// we must be connected to the master server before writing to it
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
M_StartMessage ( M_GetText ( " There was a problem connecting to \n the Master Server \n " ) , NULL , MM_NOTHING ) ;
return - 1 ;
}
if ( hosting )
msg . type = GET_ROOMS_HOST_MSG ;
else
msg . type = GET_ROOMS_MSG ;
msg . length = 0 ;
msg . room = 0 ;
if ( MS_Write ( & msg ) < 0 )
{
room_list [ 0 ] . id = 1 ;
strcpy ( room_list [ 0 ] . motd , " Master Server Offline. " ) ;
strcpy ( room_list [ 0 ] . name , " Offline " ) ;
return - 1 ;
}
for ( i = 0 ; i < NUM_LIST_ROOMS & & MS_Read ( & msg ) > = 0 ; i + + )
{
if ( msg . type = = GET_BANNED_MSG )
{
char banmsg [ 1000 ] ;
M_Memcpy ( & banned_info [ 0 ] , msg . buffer , sizeof ( msg_ban_t ) ) ;
if ( hosting )
sprintf ( banmsg , M_GetText ( " You have been banned from \n hosting netgames. \n \n Under the following IP Range: \n %s - %s \n \n For the following reason: \n %s \n \n Your ban will expire on: \n %s " ) , banned_info [ 0 ] . ipstart , banned_info [ 0 ] . ipend , banned_info [ 0 ] . reason , banned_info [ 0 ] . endstamp ) ;
else
sprintf ( banmsg , M_GetText ( " You have been banned from \n joining netgames. \n \n Under the following IP Range: \n %s - %s \n \n For the following reason: \n %s \n \n Your ban will expire on: \n %s " ) , banned_info [ 0 ] . ipstart , banned_info [ 0 ] . ipend , banned_info [ 0 ] . reason , banned_info [ 0 ] . endstamp ) ;
M_StartMessage ( banmsg , NULL , MM_NOTHING ) ;
ms_RoomId = - 1 ;
return - 2 ;
}
if ( ! msg . length )
{
room_list [ i ] . header . buffer [ 0 ] = 0 ;
CloseConnection ( ) ;
return 1 ;
}
M_Memcpy ( & room_list [ i ] , msg . buffer , sizeof ( msg_rooms_t ) ) ;
room_list [ i ] . header . buffer [ 0 ] = 1 ;
}
CloseConnection ( ) ;
if ( i = = NUM_LIST_ROOMS )
{
room_list [ i ] . header . buffer [ 0 ] = 0 ;
return 1 ;
}
else
{
room_list [ 0 ] . id = 1 ;
strcpy ( room_list [ 0 ] . motd , M_GetText ( " Master Server Offline. " ) ) ;
strcpy ( room_list [ 0 ] . name , M_GetText ( " Offline " ) ) ;
return - 1 ;
}
}
# ifdef UPDATE_ALERT
const char * GetMODVersion ( void )
{
static msg_t msg ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
return NULL ;
2014-03-15 16:59:03 +00:00
// we must be connected to the master server before writing to it
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
M_StartMessage ( M_GetText ( " There was a problem connecting to \n the Master Server \n " ) , NULL , MM_NOTHING ) ;
return NULL ;
}
msg . type = GET_VERSION_MSG ;
msg . length = sizeof MODVERSION ;
msg . room = MODID ; // Might as well use it for something.
sprintf ( msg . buffer , " %d " , MODVERSION ) ;
if ( MS_Write ( & msg ) < 0 )
2018-11-19 17:46:38 +00:00
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Could not send to the Master Server \n " ) ) ;
M_StartMessage ( M_GetText ( " Could not send to the Master Server \n " ) , NULL , MM_NOTHING ) ;
CloseConnection ( ) ;
2014-03-15 16:59:03 +00:00
return NULL ;
2018-11-19 17:46:38 +00:00
}
if ( MS_Read ( & msg ) < 0 )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No reply from the Master Server \n " ) ) ;
M_StartMessage ( M_GetText ( " No reply from the Master Server \n " ) , NULL , MM_NOTHING ) ;
CloseConnection ( ) ;
2014-03-15 16:59:03 +00:00
return NULL ;
2018-11-19 17:46:38 +00:00
}
2014-03-15 16:59:03 +00:00
CloseConnection ( ) ;
if ( strcmp ( msg . buffer , " NULL " ) ! = 0 )
{
return msg . buffer ;
}
else
return NULL ;
}
// Console only version of the above (used before game init)
void GetMODVersion_Console ( void )
{
static msg_t msg ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
return ;
2014-03-15 16:59:03 +00:00
// we must be connected to the master server before writing to it
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
return ;
}
msg . type = GET_VERSION_MSG ;
msg . length = sizeof MODVERSION ;
msg . room = MODID ; // Might as well use it for something.
sprintf ( msg . buffer , " %d " , MODVERSION ) ;
if ( MS_Write ( & msg ) < 0 )
2018-11-19 17:46:38 +00:00
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Could not send to the Master Server \n " ) ) ;
CloseConnection ( ) ;
2014-03-15 16:59:03 +00:00
return ;
2018-11-19 17:46:38 +00:00
}
if ( MS_Read ( & msg ) < 0 )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " No reply from the Master Server \n " ) ) ;
CloseConnection ( ) ;
2014-03-15 16:59:03 +00:00
return ;
2018-11-19 17:46:38 +00:00
}
2014-03-15 16:59:03 +00:00
CloseConnection ( ) ;
if ( strcmp ( msg . buffer , " NULL " ) ! = 0 )
I_Error ( UPDATE_ALERT_STRING_CONSOLE , VERSIONSTRING , msg . buffer ) ;
}
# endif
# ifndef NONET
/** Gets a list of game servers. Called from console.
*/
static void Command_Listserv_f ( void )
{
if ( con_state = = MSCS_WAITING )
{
CONS_Alert ( CONS_NOTICE , M_GetText ( " Not yet connected to the Master Server. \n " ) ) ;
return ;
}
CONS_Printf ( M_GetText ( " Retrieving server list... \n " ) ) ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
HMS_list_servers ( ) ;
return ;
}
2014-03-15 16:59:03 +00:00
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
return ;
}
if ( GetServersList ( ) )
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot get server list \n " ) ) ;
CloseConnection ( ) ;
}
# endif
FUNCMATH static const char * int2str ( INT32 n )
{
INT32 i ;
static char res [ 16 ] ;
res [ 15 ] = ' \0 ' ;
res [ 14 ] = ( char ) ( ( char ) ( n % 10 ) + ' 0 ' ) ;
for ( i = 13 ; ( n / = 10 ) ; i - - )
res [ i ] = ( char ) ( ( char ) ( n % 10 ) + ' 0 ' ) ;
return & res [ i + 1 ] ;
}
# ifndef NONET
static INT32 ConnectionFailed ( void )
{
con_state = MSCS_FAILED ;
CONS_Alert ( CONS_ERROR , M_GetText ( " Connection to Master Server failed \n " ) ) ;
CloseConnection ( ) ;
return MS_CONNECT_ERROR ;
}
# endif
/** Tries to register the local game server on the master server.
*/
static INT32 AddToMasterServer ( boolean firstadd )
{
# ifdef NONET
( void ) firstadd ;
# else
static INT32 retry = 0 ;
int i , res ;
socklen_t j ;
msg_t msg ;
msg_server_t * info = ( msg_server_t * ) msg . buffer ;
INT32 room = - 1 ;
fd_set tset ;
time_t timestamp = time ( NULL ) ;
UINT32 signature , tmp ;
const char * insname ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
HMS_update ( ) ;
return MS_NO_ERROR ;
}
2014-03-15 16:59:03 +00:00
M_Memcpy ( & tset , & wset , sizeof ( tset ) ) ;
res = select ( 255 , NULL , & tset , NULL , & select_timeout ) ;
if ( res ! = ERRSOCKET & & ! res )
{
if ( retry + + > 30 ) // an about 30 second timeout
{
retry = 0 ;
CONS_Alert ( CONS_ERROR , M_GetText ( " Master Server timed out \n " ) ) ;
MSLastPing = timestamp ;
return ConnectionFailed ( ) ;
}
return MS_CONNECT_ERROR ;
}
retry = 0 ;
2019-02-09 07:50:26 +00:00
/*
Somehow we can still select our old socket despite it being closed ( ? ) .
Atleast , that ' s what I THINK is happening . Anyway , we have to check that we
haven ' t open a socket , and actually open it !
*/
/*if (res == ERRSOCKET)*/ /* wtf? no! */
2019-06-19 20:54:11 +00:00
if ( socket_fd = = ( SOCKET_TYPE ) ERRSOCKET )
2014-03-15 16:59:03 +00:00
{
if ( MS_Connect ( GetMasterServerIP ( ) , GetMasterServerPort ( ) , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Master Server socket error #%u: %s \n " ) , errno , strerror ( errno ) ) ;
MSLastPing = timestamp ;
return ConnectionFailed ( ) ;
}
}
// so, the socket is writable, but what does that mean, that the connection is
// ok, or bad... let see that!
j = ( socklen_t ) sizeof ( i ) ;
getsockopt ( socket_fd , SOL_SOCKET , SO_ERROR , ( char * ) & i , & j ) ;
2019-02-09 07:50:26 +00:00
/*
This is also wrong . If getsockopt fails , i doesn ' t have to be set . Plus , if
it is set ( which it appearantly is on linux ) , we check errno anyway . And in
the case that i is returned as normal , we don ' t even report the correct
value ! So we accomplish NOTHING , except returning due to dumb luck .
If you care , fix this - - I don ' t . - James ( R . )
*/
2014-03-15 16:59:03 +00:00
if ( i ) // it was bad
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Master Server socket error #%u: %s \n " ) , errno , strerror ( errno ) ) ;
MSLastPing = timestamp ;
return ConnectionFailed ( ) ;
}
# ifdef PARANOIA
if ( ms_RoomId < = 0 )
I_Error ( " Attmepted to host in room \" All \" ! \n " ) ;
# endif
room = ms_RoomId ;
for ( signature = 0 , insname = cv_servername . string ; * insname ; signature + = * insname + + ) ;
tmp = ( UINT32 ) ( signature * ( size_t ) & MSLastPing ) ;
signature * = tmp ;
signature & = 0xAAAAAAAA ;
M_Memcpy ( & info - > header . signature , & signature , sizeof ( UINT32 ) ) ;
strcpy ( info - > ip , " " ) ;
strcpy ( info - > port , int2str ( current_port ) ) ;
strcpy ( info - > name , cv_servername . string ) ;
M_Memcpy ( & info - > room , & room , sizeof ( INT32 ) ) ;
# if VERSION > 0 || SUBVERSION > 0
sprintf ( info - > version , " %d.%d.%d " , VERSION / 100 , VERSION % 100 , SUBVERSION ) ;
# else // Trunk build, send revision info
strcpy ( info - > version , GetRevisionString ( ) ) ;
# endif
strcpy ( registered_server . name , cv_servername . string ) ;
if ( firstadd )
msg . type = ADD_SERVER_MSG ;
else
msg . type = PING_SERVER_MSG ;
msg . length = ( UINT32 ) sizeof ( msg_server_t ) ;
msg . room = 0 ;
if ( MS_Write ( & msg ) < 0 )
{
MSLastPing = timestamp ;
return ConnectionFailed ( ) ;
}
if ( con_state ! = MSCS_REGISTERED )
CONS_Printf ( M_GetText ( " Master Server update successful. \n " ) ) ;
MSLastPing = timestamp ;
con_state = MSCS_REGISTERED ;
CloseConnection ( ) ;
# endif
return MS_NO_ERROR ;
}
static INT32 RemoveFromMasterSever ( void )
{
msg_t msg ;
msg_server_t * info = ( msg_server_t * ) msg . buffer ;
strcpy ( info - > header . buffer , " " ) ;
strcpy ( info - > ip , " " ) ;
strcpy ( info - > port , int2str ( current_port ) ) ;
strcpy ( info - > name , registered_server . name ) ;
sprintf ( info - > version , " %d.%d.%d " , VERSION / 100 , VERSION % 100 , SUBVERSION ) ;
msg . type = REMOVE_SERVER_MSG ;
msg . length = ( UINT32 ) sizeof ( msg_server_t ) ;
msg . room = 0 ;
if ( MS_Write ( & msg ) < 0 )
return MS_WRITE_ERROR ;
return MS_NO_ERROR ;
}
const char * GetMasterServerPort ( void )
{
const char * t = cv_masterserver . string ;
while ( ( * t ! = ' : ' ) & & ( * t ! = ' \0 ' ) )
t + + ;
if ( * t )
return + + t ;
else
return DEF_PORT ;
}
/** Gets the IP address of the master server. Actually, it seems to just
* return the hostname , instead ; the lookup is done elsewhere .
*
* \ return Hostname of the master server , without port number on the end .
* \ todo Rename function ?
*/
const char * GetMasterServerIP ( void )
{
static char str_ip [ 64 ] ;
char * t = str_ip ;
if ( strstr ( cv_masterserver . string , " srb2.ssntails.org:28910 " )
| | strstr ( cv_masterserver . string , " srb2.servegame.org:28910 " )
| | strstr ( cv_masterserver . string , " srb2.servegame.org:28900 " )
)
{
// replace it with the current default one
CV_Set ( & cv_masterserver , cv_masterserver . defaultvalue ) ;
}
strcpy ( t , cv_masterserver . string ) ;
while ( ( * t ! = ' : ' ) & & ( * t ! = ' \0 ' ) )
t + + ;
* t = ' \0 ' ;
return str_ip ;
}
void MSOpenUDPSocket ( void )
{
# ifndef NONET
if ( I_NetMakeNodewPort )
{
// If it's already open, there's nothing to do.
if ( msnode < 0 )
msnode = I_NetMakeNodewPort ( GetMasterServerIP ( ) , GetMasterServerPort ( ) ) ;
}
else
# endif
msnode = - 1 ;
}
void MSCloseUDPSocket ( void )
{
if ( msnode ! = INT16_MAX ) I_NetFreeNodenum ( msnode ) ;
msnode = - 1 ;
}
void RegisterServer ( void )
{
if ( con_state = = MSCS_REGISTERED | | con_state = = MSCS_WAITING )
return ;
CONS_Printf ( M_GetText ( " Registering this server on the Master Server... \n " ) ) ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
if ( HMS_register ( ) )
con_state = MSCS_REGISTERED ;
return ;
}
2014-03-15 16:59:03 +00:00
strcpy ( registered_server . ip , GetMasterServerIP ( ) ) ;
strcpy ( registered_server . port , GetMasterServerPort ( ) ) ;
if ( MS_Connect ( registered_server . ip , registered_server . port , 1 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
return ;
}
MSOpenUDPSocket ( ) ;
// keep the TCP connection open until AddToMasterServer() is completed;
}
static inline void SendPingToMasterServer ( void )
{
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
return ;
2014-03-15 16:59:03 +00:00
/* static tic_t next_time = 0;
tic_t cur_time ;
char * inbuffer = ( char * ) netbuffer ;
cur_time = I_GetTime ( ) ;
if ( ! netgame )
UnregisterServer ( ) ;
else if ( cur_time > next_time ) // ping every 2 second if possible
{
next_time = cur_time + 2 * TICRATE ;
if ( con_state = = MSCS_WAITING )
AddToMasterServer ( ) ;
if ( con_state ! = MSCS_REGISTERED )
return ;
// cur_time is just a dummy data to send
WRITEUINT32 ( inbuffer , cur_time ) ;
doomcom - > datalength = sizeof ( cur_time ) ;
doomcom - > remotenode = ( INT16 ) msnode ;
I_NetSend ( ) ;
}
*/
// Here, have a simpler MS Ping... - Cue
if ( time ( NULL ) > ( MSLastPing + ( 60 * 2 ) ) & & con_state ! = MSCS_NONE )
{
//CONS_Debug(DBG_NETPLAY, "%ld (current time) is greater than %d (Last Ping Time)\n", time(NULL), MSLastPing);
if ( MSLastPing < 1 )
AddToMasterServer ( true ) ;
else
AddToMasterServer ( false ) ;
}
}
void SendAskInfoViaMS ( INT32 node , tic_t asktime )
{
const char * address ;
UINT16 port ;
char * inip ;
ms_holepunch_packet_t mshpp ;
MSOpenUDPSocket ( ) ;
// This must be called after calling MSOpenUDPSocket, due to the
// static buffer.
address = I_GetNodeAddress ( node ) ;
// no address?
if ( ! address )
return ;
// Copy the IP address into the buffer.
inip = mshpp . ip ;
while ( * address & & * address ! = ' : ' ) * inip + + = * address + + ;
* inip = ' \0 ' ;
// Get the port.
port = ( UINT16 ) ( * address + + ? atoi ( address ) : 0 ) ;
mshpp . port = SHORT ( port ) ;
// Set the time for ping calculation.
mshpp . time = LONG ( asktime ) ;
// Send to the MS.
M_Memcpy ( netbuffer , & mshpp , sizeof ( mshpp ) ) ;
doomcom - > datalength = sizeof ( ms_holepunch_packet_t ) ;
doomcom - > remotenode = ( INT16 ) msnode ;
I_NetSend ( ) ;
}
void UnregisterServer ( void )
{
if ( con_state ! = MSCS_REGISTERED )
{
con_state = MSCS_NONE ;
CloseConnection ( ) ;
return ;
}
con_state = MSCS_NONE ;
CONS_Printf ( M_GetText ( " Removing this server from the Master Server... \n " ) ) ;
2020-03-01 10:36:56 +00:00
if ( HMS_in_use ( ) )
{
HMS_unlist ( ) ;
return ;
}
2014-03-15 16:59:03 +00:00
if ( MS_Connect ( registered_server . ip , registered_server . port , 0 ) )
{
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot connect to the Master Server \n " ) ) ;
return ;
}
if ( RemoveFromMasterSever ( ) < 0 )
CONS_Alert ( CONS_ERROR , M_GetText ( " Cannot remove this server from the Master Server \n " ) ) ;
CloseConnection ( ) ;
MSCloseUDPSocket ( ) ;
MSLastPing = 0 ;
}
void MasterClient_Ticker ( void )
{
if ( server & & ms_RoomId > 0 )
SendPingToMasterServer ( ) ;
}
static void ServerName_OnChange ( void )
{
2018-11-25 02:24:05 +00:00
if ( con_state = = MSCS_REGISTERED )
AddToMasterServer ( false ) ;
2014-03-15 16:59:03 +00:00
}
static void MasterServer_OnChange ( void )
{
UnregisterServer ( ) ;
RegisterServer ( ) ;
}
# ifndef NONET
// Like recv, but waits until we've got enough data to fill the buffer.
static size_t recvfull ( SOCKET_TYPE s , char * buf , size_t len , int flags )
{
/* Total received. */
size_t totallen = 0 ;
while ( totallen < len )
{
ssize_t ret = ( ssize_t ) recv ( s , buf + totallen , ( int ) ( len - totallen ) , flags ) ;
/* Error. */
if ( ret = = - 1 )
return ( size_t ) - 1 ;
totallen + = ret ;
}
return totallen ;
}
# endif