2004-08-23 00:15:46 +00:00
/*
Copyright ( C ) 1996 - 1997 Id Software , Inc .
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
2005-07-03 15:16:20 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
2004-08-23 00:15:46 +00:00
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 "qwsvdef.h"
2006-05-09 00:02:05 +00:00
# include "netinc.h"
2005-12-21 03:07:33 +00:00
# include <sys/types.h>
2004-11-29 01:21:00 +00:00
# ifndef CLIENTONLY
2004-08-23 00:15:46 +00:00
# define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+i*ge->edict_size)
# ifdef _WIN32
# include "winquake.h"
# else
# include <netinet/in.h>
# endif
void SV_Savegame_f ( void ) ;
void SV_Loadgame_f ( void ) ;
# define INVIS_CHAR1 12
# define INVIS_CHAR2 (char)138
# define INVIS_CHAR3 (char)160
quakeparms_t host_parms ;
qboolean host_initialized ; // true if into command execution (compatability)
double host_frametime ;
double realtime ; // without any filtering or bounding
int host_hunklevel ;
2006-05-07 05:31:01 +00:00
// callbacks
void SV_Masterlist_Callback ( struct cvar_s * var , char * oldvalue ) ;
void SV_Tcpport_Callback ( struct cvar_s * var , char * oldvalue ) ;
2006-05-09 00:02:05 +00:00
void SV_Port_Callback ( struct cvar_s * var , char * oldvalue ) ;
void SV_PortIPv6_Callback ( struct cvar_s * var , char * oldvalue ) ;
void SV_PortIPX_Callback ( struct cvar_s * var , char * oldvalue ) ;
2006-05-07 05:31:01 +00:00
2005-05-26 12:55:34 +00:00
typedef struct {
qboolean isdp ;
cvar_t cv ;
netadr_t adr ;
} sv_masterlist_t ;
sv_masterlist_t sv_masterlist [ ] = {
2006-05-07 05:31:01 +00:00
{ false , SCVARC ( " sv_master1 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master2 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master3 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master4 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master5 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master6 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master7 " , " " , SV_Masterlist_Callback ) } ,
{ false , SCVARC ( " sv_master8 " , " " , SV_Masterlist_Callback ) } ,
{ true , SCVARC ( " sv_masterextra1 " , " ghdigital.com " , SV_Masterlist_Callback ) } , //69.59.212.88
{ true , SCVARC ( " sv_masterextra2 " , " dpmaster.deathmask.net " , SV_Masterlist_Callback ) } , //209.164.24.243
{ true , SCVARC ( " sv_masterextra3 " , " 12.166.196.192 " , SV_Masterlist_Callback ) } , //blaze.mindphukd.org (doesn't resolve currently but works as an ip)
2006-02-11 14:58:48 +00:00
{ false , SCVAR ( NULL , NULL ) }
2005-05-26 12:55:34 +00:00
} ;
2004-08-23 00:15:46 +00:00
client_t * host_client ; // current client
2005-04-26 16:04:12 +00:00
// bound the size of the physics time tic
# ifdef SERVERONLY
2006-02-11 02:09:43 +00:00
cvar_t sv_mintic = SCVAR ( " sv_mintic " , " 0.03 " ) ;
2005-04-26 16:04:12 +00:00
# else
2006-02-11 02:09:43 +00:00
cvar_t sv_mintic = SCVAR ( " sv_mintic " , " 0 " ) ; //client builds can think as often as they want.
2005-04-26 16:04:12 +00:00
# endif
2006-02-11 02:09:43 +00:00
cvar_t sv_maxtic = SCVAR ( " sv_maxtic " , " 0.1 " ) ;
cvar_t sv_nailhack = SCVAR ( " sv_nailhack " , " 0 " ) ;
2004-08-23 00:15:46 +00:00
2006-02-11 02:09:43 +00:00
cvar_t timeout = SCVAR ( " timeout " , " 65 " ) ; // seconds without any message
cvar_t zombietime = SCVAR ( " zombietime " , " 2 " ) ; // seconds to sink messages
2004-08-23 00:15:46 +00:00
// after disconnect
# ifdef SERVERONLY
2006-02-11 02:09:43 +00:00
cvar_t developer = SCVAR ( " developer " , " 0 " ) ; // show extra messages
2004-08-23 00:15:46 +00:00
2006-06-12 22:05:41 +00:00
cvar_t rcon_password = SCVARF ( " rcon_password " , " " , CVAR_NOUNSAFEEXPAND ) ; // password for remote server commands
cvar_t password = SCVARF ( " password " , " " , CVAR_NOUNSAFEEXPAND ) ; // password for entering the game
2004-08-23 00:15:46 +00:00
# else
extern cvar_t developer ;
extern cvar_t rcon_password ;
extern cvar_t password ;
# endif
2006-06-12 22:05:41 +00:00
cvar_t spectator_password = SCVARF ( " spectator_password " , " " , CVAR_NOUNSAFEEXPAND ) ; // password for entering as a sepctator
2006-02-11 02:09:43 +00:00
cvar_t allow_download = SCVAR ( " allow_download " , " 1 " ) ;
cvar_t allow_download_skins = SCVAR ( " allow_download_skins " , " 1 " ) ;
cvar_t allow_download_models = SCVAR ( " allow_download_models " , " 1 " ) ;
cvar_t allow_download_sounds = SCVAR ( " allow_download_sounds " , " 1 " ) ;
cvar_t allow_download_demos = SCVAR ( " allow_download_demos " , " 1 " ) ;
cvar_t allow_download_maps = SCVAR ( " allow_download_maps " , " 1 " ) ;
cvar_t allow_download_anymap = SCVAR ( " allow_download_pakmaps " , " 0 " ) ;
cvar_t allow_download_pakcontents = SCVAR ( " allow_download_pakcontents " , " 1 " ) ;
cvar_t allow_download_root = SCVAR ( " allow_download_root " , " 0 " ) ;
cvar_t allow_download_textures = SCVAR ( " allow_download_textures " , " 1 " ) ;
cvar_t allow_download_pk3s = SCVAR ( " allow_download_pk3s " , " 1 " ) ;
cvar_t allow_download_wads = SCVAR ( " allow_download_wads " , " 1 " ) ;
cvar_t allow_download_configs = SCVAR ( " allow_download_configs " , " 0 " ) ;
cvar_t sv_public = SCVAR ( " sv_public " , " 0 " ) ;
2007-06-10 05:14:38 +00:00
cvar_t sv_listen_qw = FCVAR ( " sv_listen_qw " , " sv_listen " , " 1 " , 0 ) ;
cvar_t sv_listen_nq = SCVAR ( " sv_listen_nq " , " 0 " ) ;
cvar_t sv_listen_dp = SCVAR ( " sv_listen_dp " , " 1 " ) ;
2006-02-11 02:09:43 +00:00
cvar_t sv_reportheartbeats = SCVAR ( " sv_reportheartbeats " , " 1 " ) ;
cvar_t sv_highchars = SCVAR ( " sv_highchars " , " 1 " ) ;
cvar_t sv_loadentfiles = SCVAR ( " sv_loadentfiles " , " 1 " ) ;
cvar_t sv_maxrate = SCVAR ( " sv_maxrate " , " 10000 " ) ;
cvar_t sv_maxdrate = SCVAR ( " sv_maxdrate " , " 10000 " ) ;
cvar_t sv_bigcoords = SCVARF ( " sv_bigcoords " , " " , CVAR_SERVERINFO ) ;
cvar_t sv_phs = SCVAR ( " sv_phs " , " 1 " ) ;
cvar_t sv_resetparms = SCVAR ( " sv_resetparms " , " 0 " ) ;
cvar_t sv_master = SCVAR ( " sv_master " , " 0 " ) ;
cvar_t sv_masterport = SCVAR ( " sv_masterport " , " 0 " ) ;
cvar_t sv_voicechat = SCVAR ( " sv_voicechat " , " 0 " ) ; //still development.
cvar_t sv_gamespeed = SCVAR ( " sv_gamespeed " , " 1 " ) ;
cvar_t sv_csqcdebug = SCVAR ( " sv_csqcdebug " , " 0 " ) ;
2006-05-09 00:02:05 +00:00
# ifdef TCPCONNECT
cvar_t sv_port_tcp = SCVARC ( " sv_port_tcp " , " 0 " , SV_Tcpport_Callback ) ;
# endif
cvar_t sv_port = SCVARC ( " sv_port " , " 27500 " , SV_Port_Callback ) ;
# ifdef IPPROTO_IPV6
cvar_t sv_port_ipv6 = SCVARC ( " sv_port_ipv6 " , " 27500 " , SV_PortIPv6_Callback ) ;
# endif
# ifdef USEIPX
cvar_t sv_port_ipx = SCVARC ( " sv_port_ipx " , " 27500 " , SV_PortIPX_Callback ) ;
# endif
2006-02-11 02:09:43 +00:00
cvar_t pausable = SCVAR ( " pausable " , " 1 " ) ;
2004-08-23 00:15:46 +00:00
//
// game rules mirrored in svs.info
//
2006-02-11 02:09:43 +00:00
cvar_t fraglimit = SCVARF ( " fraglimit " , " " , CVAR_SERVERINFO ) ;
cvar_t timelimit = SCVARF ( " timelimit " , " " , CVAR_SERVERINFO ) ;
cvar_t teamplay = SCVARF ( " teamplay " , " " , CVAR_SERVERINFO ) ;
cvar_t samelevel = SCVARF ( " samelevel " , " " , CVAR_SERVERINFO ) ;
cvar_t maxclients = SCVARF ( " maxclients " , " 8 " , CVAR_SERVERINFO ) ;
cvar_t maxspectators = SCVARF ( " maxspectators " , " 8 " , CVAR_SERVERINFO ) ;
2004-08-23 00:15:46 +00:00
# ifdef SERVERONLY
2006-02-11 02:09:43 +00:00
cvar_t deathmatch = SCVARF ( " deathmatch " , " 1 " , CVAR_SERVERINFO ) ; // 0, 1, or 2
2004-08-23 00:15:46 +00:00
# else
2006-02-11 02:09:43 +00:00
cvar_t deathmatch = SCVARF ( " deathmatch " , " 0 " , CVAR_SERVERINFO ) ; // 0, 1, or 2
2004-08-23 00:15:46 +00:00
# endif
2006-02-11 02:09:43 +00:00
cvar_t coop = SCVARF ( " coop " , " " , CVAR_SERVERINFO ) ;
cvar_t skill = SCVARF ( " skill " , " " , CVAR_SERVERINFO ) ; // 0, 1, 2 or 3
cvar_t spawn = SCVARF ( " spawn " , " " , CVAR_SERVERINFO ) ;
cvar_t watervis = SCVARF ( " watervis " , " " , CVAR_SERVERINFO ) ;
cvar_t rearview = SCVARF ( " rearview " , " " , CVAR_SERVERINFO ) ;
cvar_t allow_luma = SCVARF ( " allow_luma " , " 1 " , CVAR_SERVERINFO ) ;
cvar_t allow_bump = SCVARF ( " allow_bump " , " 1 " , CVAR_SERVERINFO ) ;
cvar_t allow_skybox = SCVARF ( " allow_skybox " , " " , CVAR_SERVERINFO ) ;
cvar_t sv_allow_splitscreen = SCVARF ( " allow_splitscreen " , " " , CVAR_SERVERINFO ) ;
cvar_t fbskins = SCVARF ( " fbskins " , " 1 " , CVAR_SERVERINFO ) ; //to get rid of lame fuhquake fbskins
cvar_t mirrors = SCVARF ( " mirrors " , " " , CVAR_SERVERINFO ) ;
2004-08-23 00:15:46 +00:00
2006-02-11 02:09:43 +00:00
cvar_t sv_motd [ ] = { SCVAR ( " sv_motd1 " , " " ) ,
SCVAR ( " sv_motd2 " , " " ) ,
SCVAR ( " sv_motd3 " , " " ) ,
SCVAR ( " sv_motd4 " , " " ) } ;
2004-08-23 00:15:46 +00:00
2006-02-11 02:09:43 +00:00
cvar_t sv_compatablehulls = SCVAR ( " sv_compatablehulls " , " 1 " ) ;
2004-08-23 00:15:46 +00:00
2006-02-11 02:09:43 +00:00
cvar_t hostname = SCVARF ( " hostname " , " unnamed " , CVAR_SERVERINFO ) ;
2004-08-23 00:15:46 +00:00
2006-02-11 02:09:43 +00:00
cvar_t secure = SCVARF ( " secure " , " " , CVAR_SERVERINFO ) ;
2004-08-23 00:15:46 +00:00
extern cvar_t sv_nomsec ;
char cvargroup_serverpermissions [ ] = " server permission variables " ;
char cvargroup_serverinfo [ ] = " serverinfo variables " ;
char cvargroup_serverphysics [ ] = " server physics variables " ;
char cvargroup_servercontrol [ ] = " server control variables " ;
2005-12-21 03:07:33 +00:00
vfsfile_t * sv_fraglogfile ;
2004-08-23 00:15:46 +00:00
void SV_FixupName ( char * in , char * out ) ;
void SV_AcceptClient ( netadr_t adr , int userid , char * userinfo ) ;
void Master_Shutdown ( void ) ;
2006-04-16 03:55:55 +00:00
void PR_SetPlayerClass ( client_t * cl , int classnum , qboolean fromqc ) ;
2006-05-25 04:47:03 +00:00
bannedips_t * SV_BannedAddress ( netadr_t * a ) ;
2004-08-23 00:15:46 +00:00
//============================================================================
qboolean ServerPaused ( void )
{
return sv . paused ;
}
/*
= = = = = = = = = = = = = = = =
SV_Shutdown
Quake calls this before calling Sys_Quit or Sys_Error
= = = = = = = = = = = = = = = =
*/
void SV_Shutdown ( void )
{
Master_Shutdown ( ) ;
if ( sv_fraglogfile )
{
2005-12-21 03:07:33 +00:00
VFS_CLOSE ( sv_fraglogfile ) ;
2004-08-23 00:15:46 +00:00
sv_fraglogfile = NULL ;
}
PR_Deinit ( ) ;
if ( sv . mvdrecording )
SV_MVDStop_f ( ) ;
NET_Shutdown ( ) ;
# ifdef IWEB_H__
IWebShutdown ( ) ;
# endif
}
/*
= = = = = = = = = = = = = = = =
SV_Error
Sends a datagram to all the clients informing them of the server crash ,
then exits
= = = = = = = = = = = = = = = =
*/
void VARGS SV_Error ( char * error , . . . )
{
va_list argptr ;
static char string [ 1024 ] ;
static qboolean inerror = false ;
if ( inerror )
2004-11-17 17:43:07 +00:00
{
2004-08-23 00:15:46 +00:00
Sys_Error ( " SV_Error: recursively entered (%s) " , string ) ;
2004-11-17 17:43:07 +00:00
}
2004-08-23 00:15:46 +00:00
inerror = true ;
va_start ( argptr , error ) ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string , sizeof ( string ) - 1 , error , argptr ) ;
2004-08-23 00:15:46 +00:00
va_end ( argptr ) ;
2005-06-14 04:52:10 +00:00
{
2005-08-19 19:06:22 +00:00
extern cvar_t pr_ssqc_coreonerror ;
if ( svprogfuncs & & pr_ssqc_coreonerror . value )
{
int size = 1024 * 1024 * 8 ;
char * buffer = BZ_Malloc ( size ) ;
svprogfuncs - > save_ents ( svprogfuncs , buffer , & size , 3 ) ;
COM_WriteFile ( " ssqccore.txt " , buffer , size ) ;
BZ_Free ( buffer ) ;
}
2005-06-14 04:52:10 +00:00
}
2004-08-23 00:15:46 +00:00
SV_EndRedirect ( ) ;
Con_Printf ( " SV_Error: %s \n " , string ) ;
if ( sv . state )
SV_FinalMessage ( va ( " server crashed: %s \n " , string ) ) ;
SV_UnspawnServer ( ) ;
# ifndef SERVERONLY
if ( cls . state )
{
if ( sv . state )
SV_UnspawnServer ( ) ;
inerror = false ;
Host_EndGame ( " SV_Error: %s \n " , string ) ;
return ;
}
if ( ! isDedicated ) //dedicated servers crash...
{
extern jmp_buf host_abort ;
SCR_EndLoadingPlaque ( ) ;
inerror = false ;
longjmp ( host_abort , 1 ) ;
return ;
}
# endif
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
SV_Shutdown ( ) ;
Sys_Error ( " SV_Error: %s \n " , string ) ;
}
/*
= = = = = = = = = = = = = = = = = =
SV_FinalMessage
Used by SV_Error and SV_Quit_f to send a final message to all connected
clients before the server goes down . The messages are sent immediately ,
not just stuck on the outgoing message list , because the server is going
to totally exit after returning from this function .
= = = = = = = = = = = = = = = = = =
*/
void SV_FinalMessage ( char * message )
{
int i ;
client_t * cl ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
SZ_Clear ( & sv . datagram ) ;
MSG_WriteByte ( & sv . datagram , svc_print ) ;
MSG_WriteByte ( & sv . datagram , PRINT_HIGH ) ;
MSG_WriteString ( & sv . datagram , message ) ;
MSG_WriteByte ( & sv . datagram , svc_disconnect ) ;
for ( i = 0 , cl = svs . clients ; i < MAX_CLIENTS ; i + + , cl + + )
if ( cl - > state > = cs_spawned )
2005-06-14 04:52:10 +00:00
if ( ISNQCLIENT ( cl ) | | ISQWCLIENT ( cl ) )
Netchan_Transmit ( & cl - > netchan , sv . datagram . cursize
, sv . datagram . data , 10000 ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = =
SV_DropClient
Called when the player is totally leaving the server , either willingly
or unwillingly . This is NOT called if the entire server is quiting
or crashing .
= = = = = = = = = = = = = = = = = = = = =
*/
void SV_DropClient ( client_t * drop )
{
if ( drop - > controller )
{
SV_DropClient ( drop - > controller ) ;
return ;
}
// add the disconnect
2005-03-18 06:13:11 +00:00
if ( drop - > state ! = cs_zombie )
2005-05-26 12:55:34 +00:00
{
switch ( drop - > protocol )
{
case SCP_QUAKE2 :
MSG_WriteByte ( & drop - > netchan . message , svcq2_disconnect ) ;
break ;
case SCP_QUAKEWORLD :
case SCP_NETQUAKE :
2005-05-27 05:41:07 +00:00
case SCP_DARKPLACES6 :
case SCP_DARKPLACES7 :
2005-05-26 12:55:34 +00:00
MSG_WriteByte ( & drop - > netchan . message , svc_disconnect ) ;
break ;
2007-08-07 19:16:32 +00:00
case SCP_BAD :
break ;
case SCP_QUAKE3 :
break ;
2005-05-26 12:55:34 +00:00
}
}
2004-08-23 00:15:46 +00:00
# ifdef SVRANKING
if ( drop - > state = = cs_spawned )
{
int j ;
rankstats_t rs ;
if ( drop - > rankid )
{
if ( Rank_GetPlayerStats ( drop - > rankid , & rs ) )
{
rs . timeonserver + = realtime - drop - > stats_started ;
drop - > stats_started = realtime ;
rs . kills + = drop - > kills ;
rs . deaths + = drop - > deaths ;
rs . flags1 & = ~ ( RANK_CUFFED | RANK_MUTED | RANK_CRIPPLED ) ;
if ( drop - > iscuffed = = 2 )
rs . flags1 | = RANK_CUFFED ;
if ( drop - > ismuted = = 2 )
rs . flags1 | = RANK_MUTED ;
if ( drop - > iscrippled = = 2 )
rs . flags1 | = RANK_CRIPPLED ;
drop - > kills = 0 ;
drop - > deaths = 0 ;
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , drop - > edict ) ;
if ( pr_nqglobal_struct - > SetChangeParms )
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > SetChangeParms ) ;
for ( j = 0 ; j < NUM_SPAWN_PARMS ; j + + )
2005-07-01 19:23:00 +00:00
if ( spawnparamglobals [ j ] )
rs . parm [ j ] = * spawnparamglobals [ j ] ;
2004-08-23 00:15:46 +00:00
Rank_SetPlayerStats ( drop - > rankid , & rs ) ;
}
}
}
# endif
# ifdef SVCHAT
SV_WipeChat ( drop ) ;
# endif
2005-08-26 22:56:51 +00:00
switch ( svs . gametype )
{
2007-08-07 19:16:32 +00:00
case GT_MAX :
2005-08-26 22:56:51 +00:00
break ;
case GT_PROGS :
if ( svprogfuncs )
2004-08-23 00:15:46 +00:00
{
2005-08-26 22:56:51 +00:00
if ( drop - > state = = cs_spawned )
2004-08-23 00:15:46 +00:00
{
2005-08-26 22:56:51 +00:00
if ( ! drop - > spectator )
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , drop - > edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > ClientDisconnect ) ;
}
else if ( SpectatorDisconnect )
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , drop - > edict ) ;
PR_ExecuteProgram ( svprogfuncs , SpectatorDisconnect ) ;
}
2004-08-23 00:15:46 +00:00
}
2005-08-26 22:56:51 +00:00
if ( drop - > spawninfo )
Z_Free ( drop - > spawninfo ) ;
drop - > spawninfo = NULL ;
}
break ;
2007-08-07 19:16:32 +00:00
case GT_QUAKE2 :
# ifdef Q2SERVER
if ( ge )
ge - > ClientDisconnect ( drop - > q2edict ) ;
# endif
break ;
case GT_QUAKE3 :
# ifdef Q3SERVER
SVQ3_DropClient ( drop ) ;
# endif
break ;
2004-08-23 00:15:46 +00:00
}
if ( drop - > spectator )
Con_Printf ( " Spectator %s removed \n " , drop - > name ) ;
else
Con_Printf ( " Client %s removed \n " , drop - > name ) ;
if ( drop - > download )
{
2005-12-21 03:07:33 +00:00
VFS_CLOSE ( drop - > download ) ;
2004-08-23 00:15:46 +00:00
drop - > download = NULL ;
}
if ( drop - > upload )
{
2005-12-21 03:07:33 +00:00
VFS_CLOSE ( drop - > upload ) ;
2004-08-23 00:15:46 +00:00
drop - > upload = NULL ;
}
* drop - > uploadfn = 0 ;
# ifndef SERVERONLY
if ( drop - > netchan . remote_address . type = = NA_LOOPBACK )
{
2004-10-19 16:10:14 +00:00
Netchan_Transmit ( & drop - > netchan , 0 , " " , SV_RateForClient ( drop ) ) ;
2004-08-23 00:15:46 +00:00
CL_Disconnect ( ) ;
drop - > state = cs_free ; //don't do zombie stuff
}
else
# endif
{
drop - > state = cs_zombie ; // become free in a few seconds
drop - > connection_started = realtime ; // for zombie timeout
}
drop - > istobeloaded = false ;
drop - > old_frags = 0 ;
2005-11-03 23:40:51 +00:00
# ifdef SVRANKING
2004-08-23 00:15:46 +00:00
drop - > kills = 0 ;
drop - > deaths = 0 ;
2005-11-03 23:40:51 +00:00
# endif
2004-09-20 23:25:38 +00:00
if ( svprogfuncs & & drop - > edict )
2005-03-28 00:11:59 +00:00
drop - > edict - > v - > frags = 0 ;
2006-01-02 22:33:23 +00:00
drop - > namebuf [ 0 ] = 0 ;
drop - > name = drop - > namebuf ;
2004-08-23 00:15:46 +00:00
memset ( drop - > userinfo , 0 , sizeof ( drop - > userinfo ) ) ;
2005-12-15 19:15:39 +00:00
memset ( drop - > userinfobasic , 0 , sizeof ( drop - > userinfobasic ) ) ;
2004-08-23 00:15:46 +00:00
2006-02-17 02:51:59 +00:00
if ( drop - > frameunion . frames ) //union of the same sort of structure
2004-08-23 00:15:46 +00:00
{
2006-02-17 02:51:59 +00:00
Z_Free ( drop - > frameunion . frames ) ;
drop - > frameunion . frames = NULL ;
2004-08-23 00:15:46 +00:00
}
2006-10-05 22:10:09 +00:00
if ( svs . gametype = = GT_PROGS ) //gamecode should do it all for us.
{
2004-08-23 00:15:46 +00:00
// send notification to all remaining clients
2006-10-05 22:10:09 +00:00
SV_FullClientUpdate ( drop , & sv . reliable_datagram , 0 ) ;
2004-08-23 00:15:46 +00:00
# ifdef NQPROT
2006-10-05 22:10:09 +00:00
SVNQ_FullClientUpdate ( drop , & sv . nqreliable_datagram ) ;
2004-08-23 00:15:46 +00:00
# endif
2006-10-05 22:10:09 +00:00
}
2004-08-23 00:15:46 +00:00
if ( drop - > controlled )
{
drop - > controlled - > controller = NULL ;
SV_DropClient ( drop - > controlled ) ;
}
}
2005-12-15 19:15:39 +00:00
//====================================================================
typedef struct pinnedmessages_s {
struct pinnedmessages_s * next ;
char setby [ 64 ] ;
char message [ 1024 ] ;
} pinnedmessages_t ;
pinnedmessages_t * pinned ;
qboolean dopinnedload = true ;
void PIN_DeleteOldestMessage ( void ) ;
void PIN_MakeMessage ( char * from , char * msg ) ;
void PIN_LoadMessages ( void )
{
char setby [ 64 ] ;
char message [ 1024 ] ;
int i ;
char * file ;
char * lstart ;
dopinnedload = false ;
while ( pinned )
PIN_DeleteOldestMessage ( ) ;
file = COM_LoadMallocFile ( " pinned.txt " ) ;
if ( ! file )
return ;
2006-03-04 15:47:58 +00:00
2005-12-15 19:15:39 +00:00
lstart = file ;
for ( ; ; )
{
while ( * lstart < = ' ' & & * lstart )
lstart + + ;
for ( i = 0 ; * lstart & & i < sizeof ( message ) - 1 ; i + + )
{
if ( * lstart = = ' \n ' | | * lstart = = ' \r ' )
break ;
message [ i ] = * lstart + + ;
}
message [ i ] = ' \0 ' ;
while ( * lstart < = ' ' & & * lstart )
lstart + + ;
for ( i = 0 ; * lstart & & i < sizeof ( setby ) - 1 ; i + + )
{
if ( * lstart = = ' \n ' | | * lstart = = ' \r ' )
break ;
setby [ i ] = * lstart + + ;
}
setby [ i ] = ' \0 ' ;
if ( ! * setby )
break ;
PIN_MakeMessage ( setby , message ) ;
}
BZ_Free ( file ) ;
}
void PIN_SaveMessages ( void )
{
pinnedmessages_t * p ;
FILE * f ;
f = COM_WriteFileOpen ( " pinned.txt " ) ;
if ( ! f )
{
Con_Printf ( " couldn't write anything \n " ) ;
return ;
}
for ( p = pinned ; p ; p = p - > next )
fprintf ( f , " %s \r \n \t %s \r \n \n " , p - > message , p - > setby ) ;
fclose ( f ) ;
}
void PIN_DeleteOldestMessage ( void )
{
pinnedmessages_t * old = pinned ;
2006-02-22 23:30:38 +00:00
if ( old )
{
pinned = pinned - > next ;
Z_Free ( old ) ;
}
2005-12-15 19:15:39 +00:00
}
void PIN_MakeMessage ( char * from , char * msg )
{
pinnedmessages_t * p ;
2006-06-02 17:42:36 +00:00
pinnedmessages_t * newp ;
2005-12-15 19:15:39 +00:00
2006-06-02 17:42:36 +00:00
newp = BZ_Malloc ( sizeof ( pinnedmessages_t ) ) ;
Q_strncpyz ( newp - > setby , from , sizeof ( newp - > setby ) ) ;
Q_strncpyz ( newp - > message , msg , sizeof ( newp - > message ) ) ;
newp - > next = NULL ;
2005-12-15 19:15:39 +00:00
if ( ! pinned )
2006-06-02 17:42:36 +00:00
pinned = newp ;
2005-12-15 19:15:39 +00:00
else
{
for ( p = pinned ; ; p = p - > next )
{
if ( ! p - > next )
{
2006-06-02 17:42:36 +00:00
p - > next = newp ;
2005-12-15 19:15:39 +00:00
break ;
}
}
}
}
void PIN_ShowMessages ( client_t * cl )
{
pinnedmessages_t * p ;
if ( dopinnedload )
PIN_LoadMessages ( ) ;
if ( ! pinned )
return ;
2006-03-04 15:47:58 +00:00
SV_ClientPrintf ( cl , PRINT_HIGH , " \n \n \n " ) ;
2005-12-15 19:15:39 +00:00
for ( p = pinned ; p ; p = p - > next )
{
2006-03-04 15:47:58 +00:00
SV_ClientPrintf ( cl , PRINT_HIGH , " %s \n \n %s \n " , p - > message , p - > setby ) ;
SV_ClientPrintf ( cl , PRINT_HIGH , " \n \n \n " ) ;
2005-12-15 19:15:39 +00:00
}
}
2004-08-23 00:15:46 +00:00
//====================================================================
/*
= = = = = = = = = = = = = = = = = = =
SV_CalcPing
= = = = = = = = = = = = = = = = = = =
*/
int SV_CalcPing ( client_t * cl )
{
float ping ;
int i ;
int count ;
register client_frame_t * frame ;
2006-02-17 02:51:59 +00:00
if ( ! cl - > frameunion . frames )
2004-08-23 00:15:46 +00:00
return 0 ;
2005-05-26 12:55:34 +00:00
switch ( cl - > protocol )
2004-08-23 00:15:46 +00:00
{
2007-08-07 19:16:32 +00:00
case SCP_BAD :
break ;
case SCP_QUAKE2 :
break ;
case SCP_QUAKE3 :
break ;
2005-05-27 05:41:07 +00:00
case SCP_DARKPLACES6 :
case SCP_DARKPLACES7 :
2007-08-07 19:16:32 +00:00
case SCP_NETQUAKE :
2005-05-26 12:55:34 +00:00
case SCP_QUAKEWORLD :
ping = 0 ;
count = 0 ;
2006-02-17 02:51:59 +00:00
for ( frame = cl - > frameunion . frames , i = 0 ; i < UPDATE_BACKUP ; i + + , frame + + )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
if ( frame - > ping_time > 0 )
{
ping + = frame - > ping_time ;
count + + ;
}
2004-08-23 00:15:46 +00:00
}
2005-05-26 12:55:34 +00:00
if ( ! count )
return 9999 ;
ping / = count ;
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
return ping * 1000 ;
}
return 0 ;
2004-08-23 00:15:46 +00:00
}
2005-12-15 19:15:39 +00:00
void SV_GenerateBasicUserInfo ( client_t * cl )
{
char * key , * s ;
int i ;
for ( i = 1 ; ( key = Info_KeyForNumber ( cl - > userinfo , i ) ) ; i + + )
{
if ( ! * key )
break ;
if ( ! SV_UserInfoIsBasic ( key ) )
continue ;
s = Info_ValueForKey ( cl - > userinfo , key ) ;
Info_SetValueForStarKey ( cl - > userinfobasic , key , s , sizeof ( cl - > userinfobasic ) ) ;
}
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = =
SV_FullClientUpdate
Writes all update values to a sizebuf
= = = = = = = = = = = = = = = = = = =
*/
2005-12-15 19:15:39 +00:00
void SV_FullClientUpdate ( client_t * client , sizebuf_t * buf , unsigned int ftepext )
2004-08-23 00:15:46 +00:00
{
int i ;
char info [ MAX_INFO_STRING ] ;
i = client - svs . clients ;
if ( sv . demofile )
{
MSG_WriteByte ( buf , svc_updatefrags ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteShort ( buf , sv . recordedplayer [ i ] . frags ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updateping ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteShort ( buf , sv . recordedplayer [ i ] . ping ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updatepl ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteByte ( buf , sv . recordedplayer [ i ] . pl ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updateentertime ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteFloat ( buf , 0 ) ;
strcpy ( info , sv . recordedplayer [ i ] . userinfo ) ;
Info_RemoveKey ( info , " password " ) ; //main password key
Info_RemoveKey ( info , " *ip " ) ; //don't broadcast this in playback
2005-07-03 15:16:20 +00:00
Info_RemovePrefixedKeys ( info , ' _ ' ) ; // server passwords, etc
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updateuserinfo ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteLong ( buf , sv . recordedplayer [ i ] . userid ) ;
MSG_WriteString ( buf , info ) ;
return ;
}
//Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i);
MSG_WriteByte ( buf , svc_updatefrags ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteShort ( buf , client - > old_frags ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updateping ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteShort ( buf , SV_CalcPing ( client ) ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updatepl ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteByte ( buf , client - > lossage ) ;
MSG_WriteByte ( buf , svc_updateentertime ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteFloat ( buf , realtime - client - > connection_started ) ;
2006-01-13 20:29:34 +00:00
if ( ftepext & PEXT_BIGUSERINFOS )
2005-12-15 19:15:39 +00:00
strcpy ( info , client - > userinfo ) ;
else
strcpy ( info , client - > userinfobasic ) ;
2004-08-23 00:15:46 +00:00
Info_RemoveKey ( info , " password " ) ; //main password key
2005-07-03 15:16:20 +00:00
Info_RemovePrefixedKeys ( info , ' _ ' ) ; // server passwords, etc
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( buf , svc_updateuserinfo ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteLong ( buf , client - > userid ) ;
MSG_WriteString ( buf , info ) ;
}
# ifdef NQPROT
void SVNQ_FullClientUpdate ( client_t * client , sizebuf_t * buf )
{
int playercolor , top , bottom ;
int i ;
i = client - svs . clients ;
if ( i > = 16 )
return ;
//Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i);
MSG_WriteByte ( buf , svc_updatefrags ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteShort ( buf , client - > old_frags ) ;
MSG_WriteByte ( buf , svc_updatename ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteString ( buf , Info_ValueForKey ( client - > userinfo , " name " ) ) ;
top = atoi ( Info_ValueForKey ( client - > userinfo , " topcolor " ) ) ;
bottom = atoi ( Info_ValueForKey ( client - > userinfo , " bottomcolor " ) ) ;
top & = 15 ;
if ( top > 13 )
top = 13 ;
bottom & = 15 ;
if ( bottom > 13 )
bottom = 13 ;
playercolor = top * 16 + bottom ;
MSG_WriteByte ( buf , svc_updatecolors ) ;
MSG_WriteByte ( buf , i ) ;
MSG_WriteByte ( buf , playercolor ) ;
}
# endif
/*
= = = = = = = = = = = = = = = = = = =
SV_FullClientUpdateToClient
Writes all update values to a client ' s reliable stream
= = = = = = = = = = = = = = = = = = =
*/
void SV_FullClientUpdateToClient ( client_t * client , client_t * cl )
{
# ifdef NQPROT
2005-05-26 12:55:34 +00:00
if ( ! ISQWCLIENT ( cl ) )
2004-08-23 00:15:46 +00:00
{
ClientReliableCheckBlock ( cl , 24 + strlen ( client - > userinfo ) ) ;
if ( cl - > num_backbuf ) {
SVNQ_FullClientUpdate ( client , & cl - > backbuf ) ;
ClientReliable_FinishWrite ( cl ) ;
} else
SVNQ_FullClientUpdate ( client , & cl - > netchan . message ) ;
}
else
# endif
{
2004-12-05 15:38:18 +00:00
if ( sv . demofile )
{
int i = client - svs . clients ;
ClientReliableCheckBlock ( cl , 24 + strlen ( sv . recordedplayer [ i ] . userinfo ) ) ;
}
else
ClientReliableCheckBlock ( cl , 24 + strlen ( client - > userinfo ) ) ;
2004-08-23 00:15:46 +00:00
if ( cl - > num_backbuf ) {
2005-12-15 19:15:39 +00:00
SV_FullClientUpdate ( client , & cl - > backbuf , cl - > fteprotocolextensions ) ;
2004-08-23 00:15:46 +00:00
ClientReliable_FinishWrite ( cl ) ;
} else
2005-12-15 19:15:39 +00:00
SV_FullClientUpdate ( client , & cl - > netchan . message , cl - > fteprotocolextensions ) ;
2004-08-23 00:15:46 +00:00
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
CONNECTIONLESS COMMANDS
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2005-10-07 16:48:12 +00:00
# define STATUS_OLDSTYLE 0
# define STATUS_SERVERINFO 1
# define STATUS_PLAYERS 2
# define STATUS_SPECTATORS 4
# define STATUS_SPECTATORS_AS_PLAYERS 8 //for ASE - change only frags: show as "S"
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = =
SVC_Status
Responds with all the info that qplug or qspy can see
This message can be up to around 5 k with worst case string lengths .
= = = = = = = = = = = = = = = =
*/
void SVC_Status ( void )
{
2005-10-07 16:27:40 +00:00
int displayflags ;
2004-08-23 00:15:46 +00:00
int i ;
client_t * cl ;
2005-10-07 16:48:12 +00:00
char * name ;
2004-08-23 00:15:46 +00:00
int ping ;
int top , bottom ;
2005-10-07 16:48:12 +00:00
char frags [ 64 ] ;
2004-08-23 00:15:46 +00:00
int slots = 0 ;
2005-10-07 16:48:12 +00:00
displayflags = atoi ( Cmd_Argv ( 1 ) ) ;
if ( displayflags = = STATUS_OLDSTYLE )
displayflags = STATUS_SERVERINFO | STATUS_PLAYERS ;
2005-10-07 16:27:40 +00:00
2004-12-08 04:14:52 +00:00
Cmd_TokenizeString ( " status " , false , false ) ;
2005-01-16 00:59:48 +00:00
SV_BeginRedirect ( RD_PACKET , LANGDEFAULT ) ;
2005-10-07 16:48:12 +00:00
if ( displayflags & STATUS_SERVERINFO )
2005-10-07 16:27:40 +00:00
Con_Printf ( " %s \n " , svs . info ) ;
2004-08-23 00:15:46 +00:00
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
cl = & svs . clients [ i ] ;
2005-10-07 16:48:12 +00:00
if ( ( cl - > state = = cs_connected | | cl - > state = = cs_spawned | | cl - > name [ 0 ] ) & & ( ( cl - > spectator & & displayflags & STATUS_SPECTATORS ) | | ( ! cl - > spectator & & displayflags & STATUS_PLAYERS ) ) )
2004-08-23 00:15:46 +00:00
{
top = atoi ( Info_ValueForKey ( cl - > userinfo , " topcolor " ) ) ;
bottom = atoi ( Info_ValueForKey ( cl - > userinfo , " bottomcolor " ) ) ;
top = ( top < 0 ) ? 0 : ( ( top > 13 ) ? 13 : top ) ;
bottom = ( bottom < 0 ) ? 0 : ( ( bottom > 13 ) ? 13 : bottom ) ;
ping = SV_CalcPing ( cl ) ;
2005-10-07 16:48:12 +00:00
name = cl - > name ;
2004-08-23 00:15:46 +00:00
if ( ! cl - > state ) //show bots differently. Just to be courteous.
2005-07-03 15:16:20 +00:00
Con_Printf ( " %i %i %i %i \" BOT:%s \" \" %s \" %i %i \n " , cl - > userid ,
2004-08-23 00:15:46 +00:00
cl - > old_frags , ( int ) ( realtime - cl - > connection_started ) / 60 ,
ping , cl - > name , Info_ValueForKey ( cl - > userinfo , " skin " ) , top , bottom ) ;
else
2005-10-07 16:48:12 +00:00
{
if ( cl - > spectator )
{ //silly mvdsv stuff
if ( displayflags & STATUS_SPECTATORS_AS_PLAYERS )
{
frags [ 0 ] = ' S ' ;
frags [ 1 ] = ' \0 ' ;
}
else
{
ping = - ping ;
sprintf ( frags , " %i " , - 9999 ) ;
name = va ( " \\ s \\ %s " , name ) ;
}
}
else
sprintf ( frags , " %i " , cl - > old_frags ) ;
Con_Printf ( " %i %s %i %i \" %s \" \" %s \" %i %i \n " , cl - > userid ,
frags , ( int ) ( realtime - cl - > connection_started ) / 60 ,
ping , name , Info_ValueForKey ( cl - > userinfo , " skin " ) , top , bottom ) ;
}
2004-08-23 00:15:46 +00:00
}
else
slots + + ;
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
//No. Not a good idea.
/* if (slots>16)
Con_Printf ( " 5016 35 54 114 \" annigilator \" \" soldier \" 0 0 \n " ) ;
if ( slots > 14 )
Con_Printf ( " 5012 32 85 162 \" FatBastard \" \" hacker \" 1 4 \n " ) ;
if ( slots > 12 )
Con_Printf ( " 5013 23 64 94 \" DeathBunny \" \" soldier \" 13 13 \n " ) ;
if ( slots > 10 )
2005-01-24 23:47:32 +00:00
Con_Printf ( " 5010 32 85 162 \" <EFBFBD> <EFBFBD> \" \" hacker \" 13 13 \n " ) ;
2004-08-23 00:15:46 +00:00
if ( slots > 8 )
2005-01-24 23:47:32 +00:00
Con_Printf ( " 5011 32 85 162 \" <EFBFBD> <EFBFBD> a<EFBFBD> <EFBFBD> <EFBFBD> \" \" hacker \" 4 4 \n " ) ;
2004-08-23 00:15:46 +00:00
*/
SV_EndRedirect ( ) ;
}
2005-07-03 15:16:20 +00:00
# ifdef NQPROT
2005-05-26 12:55:34 +00:00
void SVC_GetInfo ( char * challenge )
{
//dpmaster support
client_t * cl ;
int numclients = 0 ;
int i ;
char * resp ;
if ( ! * challenge )
challenge = NULL ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
cl = & svs . clients [ i ] ;
if ( ( cl - > state = = cs_connected | | cl - > state = = cs_spawned | | cl - > name [ 0 ] ) & & ! cl - > spectator )
numclients + + ;
}
resp = va ( " \377 \377 \377 \377 infoResponse \x0A "
" \\ gamename \\ %s "
" \\ protocol \\ %i "
" \\ clients \\ %d "
" \\ sv_maxclients \\ %s \\ mapname \\ %s "
" %s "
" %s%s " ,
com_gamename . string ,
NET_PROTOCOL_VERSION ,
numclients ,
maxclients . string , Info_ValueForKey ( svs . info , " map " ) ,
svs . info ,
challenge ? " \\ challenge \\ " : " " , challenge ? challenge : " " ) ;
Info_RemoveKey ( resp + 17 , " maxclients " ) ;
Info_RemoveKey ( resp + 17 , " map " ) ;
NET_SendPacket ( NS_SERVER , strlen ( resp ) , resp , net_from ) ;
}
2005-07-03 15:16:20 +00:00
# endif
2005-05-26 12:55:34 +00:00
2005-07-03 15:16:20 +00:00
# ifdef Q2SERVER
2004-08-23 00:15:46 +00:00
void SVC_InfoQ2 ( void )
{
char string [ 64 ] ;
int i , count ;
int version ;
if ( maxclients . value = = 1 )
return ; // ignore in single player
version = atoi ( Cmd_Argv ( 1 ) ) ;
if ( version ! = PROTOCOL_VERSION_Q2 )
2006-03-06 01:41:09 +00:00
snprintf ( string , sizeof ( string ) , " %s: wrong version \n " , hostname . string ) ;
2004-08-23 00:15:46 +00:00
else
{
count = 0 ;
for ( i = 0 ; i < maxclients . value ; i + + )
if ( svs . clients [ i ] . state > = cs_connected )
count + + ;
2006-03-06 01:41:09 +00:00
snprintf ( string , sizeof ( string ) , " %16s %8s %2i/%2i \n " , hostname . string , sv . name , count , ( int ) maxclients . value ) ;
2004-08-23 00:15:46 +00:00
}
Netchan_OutOfBandPrint ( NS_SERVER , net_from , " info \n %s " , string ) ;
}
2005-07-03 15:16:20 +00:00
# endif
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = =
SV_CheckLog
= = = = = = = = = = = = = = = = = = =
*/
# define LOG_HIGHWATER 4096
# define LOG_FLUSH 10*60
void SV_CheckLog ( void )
{
sizebuf_t * sz ;
sz = & svs . log [ svs . logsequence & 1 ] ;
// bump sequence if allmost full, or ten minutes have passed and
// there is something still sitting there
if ( sz - > cursize > LOG_HIGHWATER
| | ( realtime - svs . logtime > LOG_FLUSH & & sz - > cursize ) )
{
// swap buffers and bump sequence
svs . logtime = realtime ;
svs . logsequence + + ;
sz = & svs . log [ svs . logsequence & 1 ] ;
sz - > cursize = 0 ;
Con_Printf ( " beginning fraglog sequence %i \n " , svs . logsequence ) ;
}
}
/*
= = = = = = = = = = = = = = = =
SVC_Log
Responds with all the logged frags for ranking programs .
If a sequence number is passed as a parameter and it is
the same as the current sequence , an A2A_NACK will be returned
instead of the data .
= = = = = = = = = = = = = = = =
*/
void SVC_Log ( void )
{
int seq ;
char data [ MAX_DATAGRAM + 64 ] ;
if ( Cmd_Argc ( ) = = 2 )
seq = atoi ( Cmd_Argv ( 1 ) ) ;
else
seq = - 1 ;
if ( seq = = svs . logsequence - 1 | | ! sv_fraglogfile )
2005-07-28 15:22:15 +00:00
{ // they already have this data, or we aren't logging frags
2004-08-23 00:15:46 +00:00
data [ 0 ] = A2A_NACK ;
NET_SendPacket ( NS_SERVER , 1 , data , net_from ) ;
return ;
}
Con_DPrintf ( " sending log %i to %s \n " , svs . logsequence - 1 , NET_AdrToString ( net_from ) ) ;
sprintf ( data , " stdlog %i \n " , svs . logsequence - 1 ) ;
strcat ( data , ( char * ) svs . log_buf [ ( ( svs . logsequence - 1 ) & 1 ) ] ) ;
NET_SendPacket ( NS_SERVER , strlen ( data ) + 1 , data , net_from ) ;
}
/*
= = = = = = = = = = = = = = = =
SVC_Ping
Just responds with an acknowledgement
= = = = = = = = = = = = = = = =
*/
void SVC_Ping ( void )
{
char data ;
data = A2A_ACK ;
NET_SendPacket ( NS_SERVER , 1 , & data , net_from ) ;
}
2005-05-26 12:55:34 +00:00
//from net_from
int SV_NewChallenge ( void )
{
int i ;
int oldest ;
int oldestTime ;
oldest = 0 ;
oldestTime = 0x7fffffff ;
// see if we already have a challenge for this ip
for ( i = 0 ; i < MAX_CHALLENGES ; i + + )
{
if ( NET_CompareBaseAdr ( net_from , svs . challenges [ i ] . adr ) )
return svs . challenges [ i ] . challenge ;
if ( svs . challenges [ i ] . time < oldestTime )
{
oldestTime = svs . challenges [ i ] . time ;
oldest = i ;
}
}
// overwrite the oldest
svs . challenges [ oldest ] . challenge = ( rand ( ) < < 16 ) ^ rand ( ) ;
svs . challenges [ oldest ] . adr = net_from ;
svs . challenges [ oldest ] . time = realtime ;
return svs . challenges [ oldest ] . challenge ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = =
SVC_GetChallenge
Returns a challenge number that can be used
in a subsequent client_connect command .
We do this to prevent denial of service attacks that
flood the server with invalid connection IPs . With a
challenge , they must give a valid IP address .
= = = = = = = = = = = = = = = = =
*/
void SVC_GetChallenge ( void )
{
# ifdef HUFFNETWORK
int compressioncrc ;
# endif
int i ;
int oldest ;
int oldestTime ;
oldest = 0 ;
oldestTime = 0x7fffffff ;
// see if we already have a challenge for this ip
for ( i = 0 ; i < MAX_CHALLENGES ; i + + )
{
if ( NET_CompareBaseAdr ( net_from , svs . challenges [ i ] . adr ) )
break ;
if ( svs . challenges [ i ] . time < oldestTime )
{
oldestTime = svs . challenges [ i ] . time ;
oldest = i ;
}
}
if ( i = = MAX_CHALLENGES )
{
// overwrite the oldest
svs . challenges [ oldest ] . challenge = ( rand ( ) < < 16 ) ^ rand ( ) ;
svs . challenges [ oldest ] . adr = net_from ;
svs . challenges [ oldest ] . time = realtime ;
i = oldest ;
}
// send it back
{
char * buf ;
int lng ;
2005-03-07 08:58:26 +00:00
char * over ;
# ifdef Q3SERVER
if ( svs . gametype = = GT_QUAKE3 ) //q3 servers
buf = va ( " challengeResponse %i " , svs . challenges [ i ] . challenge ) ;
else
# endif
# ifdef Q2SERVER
if ( svs . gametype = = GT_QUAKE2 ) //quake 2 servers give a different challenge responce
2004-08-23 00:15:46 +00:00
buf = va ( " challenge %i " , svs . challenges [ i ] . challenge ) ;
else
2005-03-07 08:58:26 +00:00
# endif
2004-08-23 00:15:46 +00:00
buf = va ( " %c%i " , S2C_CHALLENGE , svs . challenges [ i ] . challenge ) ;
over = buf + strlen ( buf ) + 1 ;
2004-11-20 00:51:57 +00:00
2005-03-07 08:58:26 +00:00
if ( svs . gametype = = GT_PROGS )
2004-08-23 00:15:46 +00:00
{
2004-11-20 00:51:57 +00:00
# ifdef PROTOCOL_VERSION_FTE
//tell the client what fte extensions we support
if ( svs . fteprotocolextensions )
{
lng = LittleLong ( PROTOCOL_VERSION_FTE ) ;
memcpy ( over , & lng , sizeof ( int ) ) ;
over + = 4 ;
2004-08-23 00:15:46 +00:00
2004-11-20 00:51:57 +00:00
lng = LittleLong ( svs . fteprotocolextensions ) ;
memcpy ( over , & lng , sizeof ( long ) ) ;
over + = 4 ;
}
2004-08-23 00:15:46 +00:00
# endif
# ifdef HUFFNETWORK
2004-11-20 00:51:57 +00:00
compressioncrc = Huff_PreferedCompressionCRC ( ) ;
if ( compressioncrc )
{
lng = LittleLong ( ( ( ' H ' < < 0 ) + ( ' U ' < < 8 ) + ( ' F ' < < 16 ) + ( ' F ' < < 24 ) ) ) ;
memcpy ( over , & lng , sizeof ( int ) ) ;
over + = 4 ;
2004-08-23 00:15:46 +00:00
2004-11-20 00:51:57 +00:00
lng = LittleLong ( compressioncrc ) ;
memcpy ( over , & lng , sizeof ( long ) ) ;
over + = 4 ;
}
2004-08-23 00:15:46 +00:00
# endif
2004-11-20 00:51:57 +00:00
}
2007-06-10 21:33:24 +00:00
if ( sv_listen_qw . value | | svs . gametype ! = GT_PROGS )
2007-06-10 05:14:38 +00:00
Netchan_OutOfBand ( NS_SERVER , net_from , over - buf , buf ) ;
2004-08-23 00:15:46 +00:00
2007-07-27 21:24:31 +00:00
if ( sv_listen_dp . value & & ( sv_listen_nq . value | | sv_bigcoords . value | | ! sv_listen_qw . value ) )
2005-06-14 04:52:10 +00:00
{
2007-06-10 05:14:38 +00:00
//dp (protocol6 upwards) can respond to this (and fte won't get confused because the challenge will be wrong)
2005-11-26 21:15:39 +00:00
buf = va ( " challenge " DISTRIBUTION " %i " , svs . challenges [ i ] . challenge ) ;
2005-06-14 04:52:10 +00:00
Netchan_OutOfBand ( NS_SERVER , net_from , strlen ( buf ) + 1 , buf ) ;
}
2004-08-23 00:15:46 +00:00
}
2005-07-03 15:16:20 +00:00
// Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE,
2004-08-23 00:15:46 +00:00
// svs.challenges[i].challenge);
}
void SV_GetNewSpawnParms ( client_t * cl )
{
int i ;
if ( svprogfuncs ) //q2 dlls don't use parms in this mannor. It's all internal to the dll.
{
// call the progs to get default spawn parms for the new client
if ( pr_nqglobal_struct - > SetNewParms )
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > SetNewParms ) ;
for ( i = 0 ; i < NUM_SPAWN_PARMS ; i + + )
2005-06-22 17:10:13 +00:00
{
if ( spawnparamglobals [ i ] )
2005-07-03 15:16:20 +00:00
cl - > spawn_parms [ i ] = * spawnparamglobals [ i ] ;
2005-06-22 17:10:13 +00:00
else
cl - > spawn_parms [ i ] = 0 ;
}
2004-08-23 00:15:46 +00:00
}
}
void VARGS SV_OutOfBandPrintf ( int q2 , netadr_t adr , char * format , . . . )
{
va_list argptr ;
static char string [ 8192 ] ; // ??? why static?
va_start ( argptr , format ) ;
if ( q2 )
{
strcpy ( string , " print \n " ) ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 6 , sizeof ( string ) - 1 - 6 , format + 1 , argptr ) ;
2004-08-23 00:15:46 +00:00
}
else
{
string [ 0 ] = A2C_PRINT ;
string [ 1 ] = ' \n ' ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 2 , sizeof ( string ) - 1 - 2 , format , argptr ) ;
2004-08-23 00:15:46 +00:00
}
va_end ( argptr ) ;
Netchan_OutOfBand ( NS_SERVER , adr , strlen ( string ) , ( qbyte * ) string ) ;
}
void VARGS SV_OutOfBandTPrintf ( int q2 , netadr_t adr , int language , translation_t text , . . . )
{
va_list argptr ;
static char string [ 8192 ] ; // ??? why static?
char * format = langtext ( text , language ) ;
va_start ( argptr , text ) ;
if ( q2 )
{
strcpy ( string , " print \n " ) ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 6 , sizeof ( string ) - 1 - 6 , format + 1 , argptr ) ;
2004-08-23 00:15:46 +00:00
}
else
{
string [ 0 ] = A2C_PRINT ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 1 , sizeof ( string ) - 1 - 1 , format , argptr ) ;
2004-08-23 00:15:46 +00:00
}
va_end ( argptr ) ;
Netchan_OutOfBand ( NS_SERVER , adr , strlen ( string ) , ( qbyte * ) string ) ;
}
2005-03-07 08:58:26 +00:00
qboolean SV_ChallengePasses ( int challenge )
{
int i ;
for ( i = 0 ; i < MAX_CHALLENGES ; i + + )
{ //one per ip.
if ( NET_CompareBaseAdr ( net_from , svs . challenges [ i ] . adr ) )
{
if ( challenge = = svs . challenges [ i ] . challenge )
return true ;
return false ;
}
}
return false ;
}
2005-08-03 23:14:59 +00:00
void VARGS SV_RejectMessage ( int protocol , char * format , . . . )
2005-05-26 12:55:34 +00:00
{
va_list argptr ;
char string [ 8192 ] ;
int len ;
va_start ( argptr , format ) ;
switch ( protocol )
{
# ifdef NQPROT
case SCP_NETQUAKE :
string [ 4 ] = CCREP_REJECT ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 5 , sizeof ( string ) - 1 - 5 , format , argptr ) ;
2005-05-26 12:55:34 +00:00
len = strlen ( string + 4 ) + 1 + 4 ;
* ( int * ) string = BigLong ( NETFLAG_CTL | len ) ;
NET_SendPacket ( NS_SERVER , len , string , net_from ) ;
return ;
2005-05-27 05:41:07 +00:00
case SCP_DARKPLACES6 :
case SCP_DARKPLACES7 :
2005-05-26 12:55:34 +00:00
strcpy ( string , " reject " ) ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 7 , sizeof ( string ) - 1 - 7 , format , argptr ) ;
2005-05-26 12:55:34 +00:00
len = strlen ( string ) ;
break ;
# endif
case SCP_QUAKE2 :
default :
strcpy ( string , " print \n " ) ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 6 , sizeof ( string ) - 1 - 6 , format , argptr ) ;
2005-05-26 12:55:34 +00:00
len = strlen ( string ) ;
break ;
case SCP_QUAKEWORLD :
string [ 0 ] = A2C_PRINT ;
string [ 1 ] = ' \n ' ;
2006-03-06 01:41:09 +00:00
vsnprintf ( string + 2 , sizeof ( string ) - 1 - 2 , format , argptr ) ;
2005-05-26 12:55:34 +00:00
len = strlen ( string ) ;
break ;
}
va_end ( argptr ) ;
Netchan_OutOfBand ( NS_SERVER , net_from , len , ( qbyte * ) string ) ;
}
void SV_AcceptMessage ( int protocol )
{
char string [ 8192 ] ;
sizebuf_t sb ;
int len ;
memset ( & sb , 0 , sizeof ( sb ) ) ;
sb . maxsize = sizeof ( string ) ;
sb . data = string ;
switch ( protocol )
{
# ifdef NQPROT
case SCP_NETQUAKE :
SZ_Clear ( & sb ) ;
MSG_WriteLong ( & sb , 0 ) ;
MSG_WriteByte ( & sb , CCREP_ACCEPT ) ;
2005-07-29 22:26:43 +00:00
MSG_WriteLong ( & sb , ShortSwap ( net_local_sv_ipadr . port ) ) ;
2005-05-26 12:55:34 +00:00
* ( int * ) sb . data = BigLong ( NETFLAG_CTL | sb . cursize ) ;
NET_SendPacket ( NS_SERVER , sb . cursize , sb . data , net_from ) ;
return ;
2005-05-27 05:41:07 +00:00
case SCP_DARKPLACES6 :
case SCP_DARKPLACES7 :
2005-05-26 12:55:34 +00:00
strcpy ( string , " accept " ) ;
len = strlen ( string ) ;
break ;
# endif
case SCP_QUAKE2 :
default :
2005-07-14 01:57:34 +00:00
strcpy ( string , " client_connect \n " ) ;
2005-05-26 12:55:34 +00:00
len = strlen ( string ) ;
break ;
case SCP_QUAKEWORLD :
string [ 0 ] = S2C_CONNECTION ;
string [ 1 ] = ' \n ' ;
string [ 2 ] = ' \0 ' ;
len = strlen ( string ) ;
break ;
}
Netchan_OutOfBand ( NS_SERVER , net_from , len , ( qbyte * ) string ) ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = =
SVC_DirectConnect
A connection request that did not come from the master
= = = = = = = = = = = = = = = = = =
*/
int nextuserid ;
2005-05-26 12:55:34 +00:00
client_t * SVC_DirectConnect ( void )
2004-08-23 00:15:46 +00:00
{
char userinfo [ MAX_CLIENTS ] [ 2048 ] ;
netadr_t adr ;
int i ;
client_t * cl , * newcl ;
client_t temp ;
edict_t * ent ;
# ifdef Q2SERVER
q2edict_t * q2ent ;
# endif
int edictnum ;
char * s ;
int clients , spectators ;
qboolean spectator ;
int qport ;
int version ;
int challenge ;
int huffcrc = 0 ;
int maxpacketentities ;
2005-05-26 12:55:34 +00:00
int numssclients = 1 ;
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
int protocol ;
2004-08-23 00:15:46 +00:00
unsigned int protextsupported = 0 ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
char * name ;
2005-05-26 12:55:34 +00:00
if ( * ( Cmd_Argv ( 0 ) + 7 ) = = ' \\ ' )
2004-08-23 00:15:46 +00:00
{
2007-06-10 05:14:38 +00:00
if ( ! sv_listen_dp . value )
2005-06-14 04:52:10 +00:00
return NULL ;
2005-05-26 12:55:34 +00:00
Q_strncpyz ( userinfo [ 0 ] , net_message . data + 11 , sizeof ( userinfo [ 0 ] ) - 1 ) ;
if ( strcmp ( Info_ValueForKey ( userinfo [ 0 ] , " protocol " ) , " darkplaces 3 " ) )
2004-08-23 00:15:46 +00:00
{
2005-10-04 21:08:06 +00:00
SV_RejectMessage ( SCP_BAD , " Server is " DISTRIBUTION " build %i. \n " , build_number ( ) ) ;
2005-05-26 12:55:34 +00:00
Con_Printf ( " * rejected connect from incompatable client \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
2005-05-27 05:41:07 +00:00
//it's a darkplaces client.
2004-08-23 00:15:46 +00:00
2005-05-27 05:41:07 +00:00
s = Info_ValueForKey ( userinfo [ 0 ] , " protocols " ) ;
2007-06-10 05:14:38 +00:00
if ( sizeofcoord ! = 4 )
{ //we allow nq with sv_listen_nq 0...
//reason: dp is too similar for concerns about unsupported code, while the main reason why we disable nq is because of the lack of challenges
//(and no, this isn't a way to bypass invalid challenges)
protocol = SCP_NETQUAKE ;
Con_Printf ( " * DP without sv_bigcoords 1 \n " ) ;
}
else if ( strstr ( s , " DP7 " ) )
2005-05-27 05:41:07 +00:00
protocol = SCP_DARKPLACES7 ;
else
protocol = SCP_DARKPLACES6 ;
2004-08-23 00:15:46 +00:00
2005-06-14 04:52:10 +00:00
s = Info_ValueForKey ( userinfo [ 0 ] , " challenge " ) ;
2005-11-14 01:32:21 +00:00
if ( ! strncmp ( s , DISTRIBUTION , strlen ( DISTRIBUTION ) ) )
2005-06-14 04:52:10 +00:00
challenge = atoi ( s + 3 ) ;
else
challenge = atoi ( s ) ;
s = Info_ValueForKey ( userinfo [ 0 ] , " name " ) ;
if ( ! * s )
Info_SetValueForKey ( userinfo [ 0 ] , " name " , " CONNECTING " , sizeof ( userinfo [ 0 ] ) ) ;
2005-05-26 12:55:34 +00:00
qport = 0 ;
2004-08-23 00:15:46 +00:00
}
2005-05-26 12:55:34 +00:00
else
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
if ( atoi ( Cmd_Argv ( 0 ) + 7 ) )
{
2005-05-27 05:41:07 +00:00
numssclients = atoi ( Cmd_Argv ( 0 ) + 7 ) ;
2005-05-26 12:55:34 +00:00
if ( numssclients < 1 | | numssclients > 4 )
{
2005-10-04 21:08:06 +00:00
SV_RejectMessage ( SCP_BAD , " Server is " DISTRIBUTION " build %i. \n " , build_number ( ) ) ;
2005-05-26 12:55:34 +00:00
Con_Printf ( " * rejected connect from broken client \n " ) ;
return NULL ;
}
}
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
version = atoi ( Cmd_Argv ( 1 ) ) ;
if ( version > = 31 & & version < = 34 )
{
numssclients = 1 ;
protocol = SCP_QUAKE2 ;
}
else if ( version = = 3 )
{
numssclients = 1 ;
protocol = SCP_NETQUAKE ;
}
else if ( version ! = PROTOCOL_VERSION )
{
2005-10-05 02:16:28 +00:00
SV_RejectMessage ( SCP_BAD , " Server is protocol version %i, received %i \n " , PROTOCOL_VERSION , version ) ;
2005-05-26 12:55:34 +00:00
Con_Printf ( " * rejected connect from version %i \n " , version ) ;
return NULL ;
}
else
protocol = SCP_QUAKEWORLD ;
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
qport = atoi ( Cmd_Argv ( 2 ) ) ;
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
challenge = atoi ( Cmd_Argv ( 3 ) ) ;
2004-08-23 00:15:46 +00:00
2005-05-26 12:55:34 +00:00
// note an extra qbyte is needed to replace spectator key
for ( i = 0 ; i < numssclients ; i + + )
Q_strncpyz ( userinfo [ i ] , Cmd_Argv ( 4 + i ) , sizeof ( userinfo [ i ] ) - 1 ) ;
}
if ( protocol = = SCP_QUAKEWORLD ) //readd?
2004-08-23 00:15:46 +00:00
{
2007-06-10 21:33:24 +00:00
if ( ! sv_listen_qw . value & & net_from . type ! = NA_LOOPBACK )
{
SV_RejectMessage ( SCP_BAD , " QuakeWorld protocols are not permitted on this server. \n " ) ;
2007-06-20 00:02:54 +00:00
Con_Printf ( " * rejected connect from quakeworld \n " ) ;
2007-06-10 21:33:24 +00:00
return NULL ;
}
2004-08-23 00:15:46 +00:00
while ( ! msg_badread )
{
2004-12-08 04:14:52 +00:00
Cmd_TokenizeString ( MSG_ReadStringLine ( ) , false , false ) ;
2004-08-23 00:15:46 +00:00
switch ( Q_atoi ( Cmd_Argv ( 0 ) ) )
{
case PROTOCOL_VERSION_FTE :
protextsupported = Q_atoi ( Cmd_Argv ( 1 ) ) ;
Con_DPrintf ( " Client supports 0x%x fte extensions \n " , protextsupported ) ;
break ;
case PROTOCOL_VERSION_HUFFMAN :
huffcrc = Q_atoi ( Cmd_Argv ( 1 ) ) ;
2005-10-05 02:16:28 +00:00
Con_DPrintf ( " Client supports huffman compression \n " , huffcrc ) ;
2004-08-23 00:15:46 +00:00
break ;
}
}
msg_badread = false ;
}
if ( protextsupported & PEXT_256PACKETENTITIES )
maxpacketentities = MAX_EXTENDED_PACKET_ENTITIES ;
else
maxpacketentities = MAX_STANDARD_PACKET_ENTITIES ;
if ( ! sv_allow_splitscreen . value )
numssclients = 1 ;
if ( ! ( protextsupported & PEXT_SPLITSCREEN ) )
numssclients = 1 ;
if ( sv . msgfromdemo | | net_from . type = = NA_LOOPBACK ) //normal rules don't apply
i = 0 ;
else
2005-03-07 08:58:26 +00:00
{
2004-08-23 00:15:46 +00:00
// see if the challenge is valid
2005-03-07 08:58:26 +00:00
if ( ! SV_ChallengePasses ( challenge ) )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " Bad challenge. \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
2005-03-07 08:58:26 +00:00
}
2004-08-23 00:15:46 +00:00
// check for password or spectator_password
if ( svprogfuncs )
{
s = Info_ValueForKey ( userinfo [ 0 ] , " spectator " ) ;
if ( s [ 0 ] & & strcmp ( s , " 0 " ) )
{
2005-07-03 15:16:20 +00:00
if ( spectator_password . string [ 0 ] & &
2004-08-23 00:15:46 +00:00
stricmp ( spectator_password . string , " none " ) & &
strcmp ( spectator_password . string , s ) )
{ // failed
Con_Printf ( " %s:spectator password failed \n " , NET_AdrToString ( net_from ) ) ;
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " requires a spectator password \n \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
Info_RemoveKey ( userinfo [ 0 ] , " spectator " ) ; // remove key
2005-03-07 08:58:26 +00:00
Info_SetValueForStarKey ( userinfo [ 0 ] , " *spectator " , " 1 " , sizeof ( userinfo [ 0 ] ) ) ;
2004-08-23 00:15:46 +00:00
spectator = true ;
}
else
{
s = Info_ValueForKey ( userinfo [ 0 ] , " password " ) ;
2005-07-03 15:16:20 +00:00
if ( password . string [ 0 ] & &
2004-08-23 00:15:46 +00:00
stricmp ( password . string , " none " ) & &
strcmp ( password . string , s ) )
{
Con_Printf ( " %s:password failed \n " , NET_AdrToString ( net_from ) ) ;
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " server requires a password \n \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
spectator = false ;
Info_RemoveKey ( userinfo [ 0 ] , " password " ) ; // remove passwd
Info_RemoveKey ( userinfo [ 0 ] , " *spectator " ) ; // remove key
}
}
else
spectator = false ; //q2 does all of it's checks internally, and deals with spectator ship too
adr = net_from ;
nextuserid + + ; // so every client gets a unique id
newcl = & temp ;
memset ( newcl , 0 , sizeof ( client_t ) ) ;
newcl - > userid = nextuserid ;
newcl - > fteprotocolextensions = protextsupported ;
2005-05-26 12:55:34 +00:00
newcl - > protocol = protocol ;
2004-08-23 00:15:46 +00:00
if ( sv . msgfromdemo )
newcl - > wasrecorded = true ;
// works properly
if ( ! sv_highchars . value )
{
qbyte * p , * q ;
for ( p = ( qbyte * ) newcl - > userinfo , q = ( qbyte * ) userinfo ;
* q & & p < ( qbyte * ) newcl - > userinfo + sizeof ( newcl - > userinfo ) - 1 ; q + + )
if ( * q > 31 & & * q < = 127 )
* p + + = * q ;
}
else
Q_strncpyS ( newcl - > userinfo , userinfo [ 0 ] , sizeof ( newcl - > userinfo ) - 1 ) ;
newcl - > userinfo [ sizeof ( newcl - > userinfo ) - 1 ] = ' \0 ' ;
2005-07-28 15:22:15 +00:00
// if there is already a slot for this ip, drop it
2004-08-23 00:15:46 +00:00
for ( i = 0 , cl = svs . clients ; i < MAX_CLIENTS ; i + + , cl + + )
{
if ( cl - > state = = cs_free )
continue ;
if ( NET_CompareBaseAdr ( adr , cl - > netchan . remote_address )
2005-07-03 15:16:20 +00:00
& & ( cl - > netchan . qport = = qport
2004-08-23 00:15:46 +00:00
| | adr . port = = cl - > netchan . remote_address . port ) )
{
2007-06-10 05:14:38 +00:00
if ( cl - > state = = cs_connected )
{
if ( cl - > protocol ! = protocol )
Con_Printf ( " %s: diff prot connect \n " , NET_AdrToString ( adr ) ) ;
else
Con_Printf ( " %s:dup connect \n " , NET_AdrToString ( adr ) ) ;
2004-08-23 00:15:46 +00:00
nextuserid - - ;
2005-05-26 12:55:34 +00:00
return NULL ;
2004-08-23 00:15:46 +00:00
}
{
Con_Printf ( " %s:reconnect \n " , NET_AdrToString ( adr ) ) ;
// SV_DropClient (cl);
}
break ;
}
}
name = Info_ValueForKey ( temp . userinfo , " name " ) ;
2005-05-26 12:55:34 +00:00
if ( protocol = = SCP_QUAKEWORLD & & ! atoi ( Info_ValueForKey ( temp . userinfo , " iknow " ) ) )
2004-08-23 00:15:46 +00:00
{
if ( sv . worldmodel - > fromgame = = fg_halflife & & ! ( newcl - > fteprotocolextensions & PEXT_HLBSP ) )
{
if ( atof ( Info_ValueForKey ( temp . userinfo , " *FuhQuake " ) ) < 0.3 )
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " The server is using a halflife level and we don't think your client supports this \n use 'setinfo iknow 1' to ignore this check \n You can go to " ENGINEWEBSITE " to get a compatable client \n \n You may need to enable an option \n \n " ) ;
2005-02-06 02:47:36 +00:00
// Con_Printf("player %s was dropped due to incompatable client\n", name);
// return;
2004-08-23 00:15:46 +00:00
}
}
# ifdef PEXT_Q2BSP
2004-11-17 17:43:07 +00:00
else if ( sv . worldmodel - > fromgame = = fg_quake2 & & ! ( newcl - > fteprotocolextensions & PEXT_Q2BSP ) )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " The server is using a quake 2 level and we don't think your client supports this \n use 'setinfo iknow 1' to ignore this check \n You can go to " ENGINEWEBSITE " to get a compatable client \n \n You may need to enable an option \n \n " ) ;
2005-02-06 02:47:36 +00:00
// Con_Printf("player %s was dropped due to incompatable client\n", name);
// return;
2004-08-23 00:15:46 +00:00
}
# endif
# ifdef PEXT_Q3BSP
else if ( sv . worldmodel - > fromgame = = fg_quake3 & & ! ( newcl - > fteprotocolextensions & PEXT_Q3BSP ) )
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " The server is using a quake 3 level and we don't think your client supports this \n use 'setinfo iknow 1' to ignore this check \n You can go to " ENGINEWEBSITE " to get a compatable client \n \n You may need to enable an option \n \n " ) ;
2005-02-06 02:47:36 +00:00
// Con_Printf("player %s was dropped due to incompatable client\n", name);
// return;
2004-08-23 00:15:46 +00:00
}
# endif
}
SV_FixupName ( name , name ) ;
if ( ! * name )
2005-06-14 04:52:10 +00:00
{
2004-08-23 00:15:46 +00:00
name = " Hidden " ;
2005-06-14 04:52:10 +00:00
}
2004-08-23 00:15:46 +00:00
else if ( ! stricmp ( name , " console " ) )
name = " Not Console " ; //have fun dudes.
// count up the clients and spectators
clients = 0 ;
spectators = 0 ;
newcl = NULL ;
if ( ! sv . allocated_client_slots )
Con_Printf ( " Apparently, there are no client slots allocated. This shouldn't be happening \n " ) ;
for ( i = 0 , cl = svs . clients ; i < sv . allocated_client_slots ; i + + , cl + + )
{
if ( cl - > state = = cs_free )
continue ;
if ( cl - > spectator )
spectators + + ;
else
clients + + ;
if ( cl - > istobeloaded & & cl - > state = = cs_zombie )
{
if ( ! newcl )
{
if ( ! strcmp ( cl - > name , name ) | | ! * cl - > name ) //named, or first come first serve.
{
newcl = cl ;
temp . istobeloaded = cl - > istobeloaded ;
break ;
}
}
}
}
if ( ! newcl ) //client has no slot. It's possible to bipass this if server is loading a game. (or a duplicated qsocket)
{
// if at server limits, refuse connection
if ( maxclients . value > MAX_CLIENTS )
Cvar_SetValue ( & maxclients , MAX_CLIENTS ) ;
if ( maxspectators . value > MAX_CLIENTS )
Cvar_SetValue ( & maxspectators , MAX_CLIENTS ) ;
// if (maxspectators.value + maxclients.value > MAX_CLIENTS) //maybe a server wishes to allow this sort of thing?
// Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value);
if ( svprogfuncs & & ( ( spectator & & spectators > = ( int ) maxspectators . value )
| | ( ! spectator & & clients > = ( int ) maxclients . value )
| | ( clients + spectators > = sv . allocated_client_slots ) ) )
{
Con_Printf ( " %s:full connect \n " , NET_AdrToString ( adr ) ) ;
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " \n server is full \n \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
// find a client slot
for ( i = 0 , cl = svs . clients ; i < sv . allocated_client_slots ; i + + , cl + + )
{
if ( cl - > state = = cs_free )
{
newcl = cl ;
break ;
}
}
if ( ! newcl )
{
if ( svprogfuncs )
Con_Printf ( " WARNING: miscounted available clients \n " ) ;
else
{
Con_Printf ( " %s:full connect \n " , NET_AdrToString ( adr ) ) ;
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " server is full \n \n " ) ;
2004-08-23 00:15:46 +00:00
}
2005-05-26 12:55:34 +00:00
return NULL ;
2004-08-23 00:15:46 +00:00
}
}
{
2006-05-25 04:47:03 +00:00
bannedips_t * banip = SV_BannedAddress ( & adr ) ;
if ( banip )
2004-08-23 00:15:46 +00:00
{
2006-05-25 04:47:03 +00:00
if ( banip - > reason [ 0 ] )
SV_RejectMessage ( protocol , " You were banned. \n Reason: %s \n " , banip - > reason ) ;
else
SV_RejectMessage ( protocol , " You were banned. \n " ) ;
return NULL ;
2004-08-23 00:15:46 +00:00
}
}
edictnum = ( newcl - svs . clients ) + 1 ;
2005-10-16 03:49:04 +00:00
switch ( svs . gametype )
2004-08-23 00:15:46 +00:00
{
2005-10-16 03:49:04 +00:00
case GT_PROGS :
2005-07-03 15:16:20 +00:00
ent = EDICT_NUM ( svprogfuncs , edictnum ) ;
2004-08-23 00:15:46 +00:00
# ifdef Q2SERVER
temp . q2edict = NULL ;
# endif
temp . edict = ent ;
// build a new connection
// accept the new client
// this is the only place a client_t is ever initialized
2006-02-17 02:51:59 +00:00
temp . frameunion . frames = newcl - > frameunion . frames ; //don't touch these.
if ( temp . frameunion . frames )
Z_Free ( temp . frameunion . frames ) ;
2004-08-23 00:15:46 +00:00
2006-02-17 02:51:59 +00:00
temp . frameunion . frames = Z_Malloc ( ( sizeof ( client_frame_t ) + sizeof ( entity_state_t ) * maxpacketentities ) * UPDATE_BACKUP ) ;
2004-08-23 00:15:46 +00:00
for ( i = 0 ; i < UPDATE_BACKUP ; i + + )
{
2006-02-17 02:51:59 +00:00
temp . frameunion . frames [ i ] . entities . max_entities = maxpacketentities ;
temp . frameunion . frames [ i ] . entities . entities = ( entity_state_t * ) ( temp . frameunion . frames + UPDATE_BACKUP ) + i * temp . frameunion . frames [ i ] . entities . max_entities ;
2004-08-23 00:15:46 +00:00
}
2005-10-16 03:49:04 +00:00
break ;
2004-08-23 00:15:46 +00:00
# ifdef Q2SERVER
2005-10-16 03:49:04 +00:00
case GT_QUAKE2 :
2005-07-03 15:16:20 +00:00
q2ent = Q2EDICT_NUM ( edictnum ) ;
2004-08-23 00:15:46 +00:00
temp . edict = NULL ;
temp . q2edict = q2ent ;
if ( ! ge - > ClientConnect ( q2ent , temp . userinfo ) )
2005-05-26 12:55:34 +00:00
return NULL ;
2004-08-23 00:15:46 +00:00
2005-03-20 02:57:11 +00:00
ge - > ClientUserinfoChanged ( q2ent , temp . userinfo ) ;
2004-08-23 00:15:46 +00:00
// build a new connection
// accept the new client
// this is the only place a client_t is ever initialized
2006-02-17 02:51:59 +00:00
temp . frameunion . q2frames = newcl - > frameunion . q2frames ; //don't touch these.
if ( temp . frameunion . q2frames )
Z_Free ( temp . frameunion . q2frames ) ;
2004-08-23 00:15:46 +00:00
2006-02-17 02:51:59 +00:00
temp . frameunion . q2frames = Z_Malloc ( sizeof ( q2client_frame_t ) * Q2UPDATE_BACKUP ) ;
2005-10-16 03:49:04 +00:00
break ;
2004-08-23 00:15:46 +00:00
# endif
2005-10-16 03:49:04 +00:00
default :
Sys_Error ( " Bad svs.gametype in SVC_DirectConnect " ) ;
break ;
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
2005-05-17 02:36:54 +00:00
{
char * n , * t ;
n = newcl - > name ;
t = newcl - > team ;
* newcl = temp ;
newcl - > name = n ;
newcl - > team = t ;
}
2004-08-23 00:15:46 +00:00
newcl - > zquake_extensions = atoi ( Info_ValueForKey ( newcl - > userinfo , " *z_ext " ) ) ;
2007-02-23 00:21:33 +00:00
if ( * Info_ValueForKey ( newcl - > userinfo , " *fuhquake " ) ) //fuhquake doesn't claim to support z_ext but does look at our z_ext serverinfo key.
{ //so switch on the bits that it should be sending.
newcl - > zquake_extensions | = Z_EXT_PM_TYPE | Z_EXT_PM_TYPE_NEW ;
}
2004-08-23 00:15:46 +00:00
//dmw - delayed - Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION );
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
Netchan_Setup ( NS_SERVER , & newcl - > netchan , adr , qport ) ;
if ( huffcrc )
newcl - > netchan . compress = true ;
else
newcl - > netchan . compress = false ;
2005-05-26 12:55:34 +00:00
newcl - > protocol = protocol ;
2005-07-16 00:53:08 +00:00
# ifdef NQPROT
2005-05-26 12:55:34 +00:00
newcl - > netchan . isnqprotocol = ISNQCLIENT ( newcl ) ;
2005-07-03 15:16:20 +00:00
# endif
2005-05-26 12:55:34 +00:00
2004-08-23 00:15:46 +00:00
newcl - > state = cs_connected ;
newcl - > datagram . allowoverflow = true ;
newcl - > datagram . data = newcl - > datagram_buf ;
newcl - > datagram . maxsize = sizeof ( newcl - > datagram_buf ) ;
// spectator mode can ONLY be set at join time
newcl - > spectator = spectator ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
// parse some info from the info strings
SV_ExtractFromUserinfo ( newcl ) ;
2005-12-15 19:15:39 +00:00
SV_GenerateBasicUserInfo ( newcl ) ;
2004-08-23 00:15:46 +00:00
// JACK: Init the floodprot stuff.
2006-05-29 06:12:12 +00:00
newcl - > floodprotmessage = 0.0 ;
newcl - > lastspoke = 0.0 ;
2004-08-23 00:15:46 +00:00
newcl - > lockedtill = 0 ;
# ifdef SVRANKING
if ( svs . demorecording | | ( svs . demoplayback & & newcl - > wasrecorded ) ) //disable rankings. Could cock things up.
{
SV_GetNewSpawnParms ( newcl ) ;
}
else
{
//rankid is figured out in extract from user info
if ( ! newcl - > rankid ) //failed to get a userid
{
if ( rank_needlogin . value )
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " Bad password/username \n This server requires logins. Please see the serverinfo for website and info on how to register. \n " ) ;
2004-08-23 00:15:46 +00:00
newcl - > state = cs_free ;
2005-05-26 12:55:34 +00:00
return NULL ;
2004-08-23 00:15:46 +00:00
}
// SV_OutOfBandPrintf (isquake2client, adr, "\nWARNING: You have not got a place on the ranking system, probably because a user with the same name has already connected and your pwds differ.\n\n");
SV_GetNewSpawnParms ( newcl ) ;
}
else
{
rankstats_t rs ;
if ( ! Rank_GetPlayerStats ( newcl - > rankid , & rs ) )
{
2005-05-26 12:55:34 +00:00
SV_RejectMessage ( protocol , " Rankings/Account system failed \n " ) ;
2005-03-12 23:40:42 +00:00
Con_Printf ( " banned player %s is trying to connect \n " , newcl - > name ) ;
newcl - > name [ 0 ] = 0 ;
memset ( newcl - > userinfo , 0 , sizeof ( newcl - > userinfo ) ) ;
2004-08-23 00:15:46 +00:00
newcl - > state = cs_free ;
2005-05-26 12:55:34 +00:00
return NULL ;
2004-08-23 00:15:46 +00:00
}
if ( rs . flags1 & RANK_MUTED )
{
2005-07-03 15:16:20 +00:00
SV_BroadcastTPrintf ( PRINT_MEDIUM , STL_CLIENTISSTILLMUTED , newcl - > name ) ;
2004-08-23 00:15:46 +00:00
}
if ( rs . flags1 & RANK_CUFFED )
{
2005-07-03 15:16:20 +00:00
SV_BroadcastTPrintf ( PRINT_LOW , STL_CLIENTISSTILLCUFFED , newcl - > name ) ;
2004-08-23 00:15:46 +00:00
}
if ( rs . flags1 & RANK_CRIPPLED )
{
2005-07-03 15:16:20 +00:00
SV_BroadcastTPrintf ( PRINT_HIGH , STL_CLIENTISSTILLCRIPPLED , newcl - > name ) ;
}
2004-08-23 00:15:46 +00:00
if ( rs . timeonserver )
{
if ( cl - > istobeloaded )
{ //do nothing.
}
else if ( sv_resetparms . value )
{
SV_GetNewSpawnParms ( newcl ) ;
}
else
{
for ( i = 0 ; i < NUM_SPAWN_PARMS ; i + + )
newcl - > spawn_parms [ i ] = rs . parm [ i ] ;
}
if ( rs . timeonserver > 3 * 60 ) //woo. Ages.
s = va ( langtext ( STL_BIGGREETING , newcl - > language ) , newcl - > name , ( int ) ( rs . timeonserver / ( 60 * 60 ) ) , ( int ) ( ( int ) ( rs . timeonserver / 60 ) % ( 60 ) ) ) ;
else //measure this guy in minuites.
s = va ( langtext ( STL_SHORTGREETING , newcl - > language ) , newcl - > name , ( int ) ( rs . timeonserver / 60 ) ) ;
2005-05-26 12:55:34 +00:00
SV_OutOfBandPrintf ( protocol = = SCP_QUAKE2 , adr , s ) ;
2004-08-23 00:15:46 +00:00
}
else if ( ! cl - > istobeloaded )
{
SV_GetNewSpawnParms ( newcl ) ;
2005-05-26 12:55:34 +00:00
SV_OutOfBandTPrintf ( protocol = = SCP_QUAKE2 , adr , newcl - > language , STL_FIRSTGREETING , newcl - > name , ( int ) rs . timeonserver ) ;
2004-08-23 00:15:46 +00:00
}
//else loaded players already have thier initial parms set
}
}
# else
// call the progs to get default spawn parms for the new client
if ( ! cl - > istobeloaded )
{
SV_GetNewSpawnParms ( newcl ) ;
}
# endif
if ( ! newcl - > wasrecorded )
{
2005-05-26 12:55:34 +00:00
SV_AcceptMessage ( protocol ) ;
2004-08-23 00:15:46 +00:00
if ( newcl - > spectator )
{
SV_BroadcastTPrintf ( PRINT_LOW , STL_SPECTATORCONNECTED , newcl - > name ) ;
// Con_Printf ("Spectator %s connected\n", newcl->name);
}
else
{
SV_BroadcastTPrintf ( PRINT_LOW , STL_CLIENTCONNECTED , newcl - > name ) ;
// Con_DPrintf ("Client %s connected\n", newcl->name);
}
}
else
{
if ( newcl - > spectator )
{
SV_BroadcastTPrintf ( PRINT_LOW , STL_RECORDEDSPECTATORCONNECTED , newcl - > name ) ;
// Con_Printf ("Recorded spectator %s connected\n", newcl->name);
}
else
{
SV_BroadcastTPrintf ( PRINT_LOW , STL_RECORDEDCLIENTCONNECTED , newcl - > name ) ;
// Con_DPrintf ("Recorded client %s connected\n", newcl->name);
}
}
newcl - > sendinfo = true ;
for ( i = 0 ; i < sizeof ( sv_motd ) / sizeof ( sv_motd [ 0 ] ) ; i + + )
{
if ( * sv_motd [ i ] . string )
SV_ClientPrintf ( newcl , PRINT_CHAT , " %s \n " , sv_motd [ i ] . string ) ;
}
newcl - > fteprotocolextensions & = ~ PEXT_SPLITSCREEN ;
for ( clients = 1 ; clients < numssclients ; clients + + )
{
for ( i = 0 , cl = svs . clients ; i < sv . allocated_client_slots ; i + + , cl + + )
{
if ( cl - > state = = cs_free )
{
break ;
}
}
if ( i = = sv . allocated_client_slots )
break ;
newcl - > fteprotocolextensions | = PEXT_SPLITSCREEN ;
2006-02-17 02:51:59 +00:00
temp . frameunion . frames = cl - > frameunion . frames ; //don't touch these.
2004-08-23 00:15:46 +00:00
temp . edict = cl - > edict ;
memcpy ( cl , newcl , sizeof ( client_t ) ) ;
2006-02-17 02:51:59 +00:00
cl - > frameunion . frames = temp . frameunion . frames ;
2004-08-23 00:15:46 +00:00
cl - > edict = temp . edict ;
cl - > fteprotocolextensions | = PEXT_SPLITSCREEN ;
if ( newcl - > controller )
{
newcl - > controller - > controlled = cl ;
newcl - > controller = cl ;
}
else
{
newcl - > controlled = cl ;
newcl - > controller = cl ;
}
cl - > controller = newcl ;
cl - > controlled = NULL ;
Q_strncpyS ( cl - > userinfo , userinfo [ clients ] , sizeof ( cl - > userinfo ) - 1 ) ;
cl - > userinfo [ sizeof ( cl - > userinfo ) - 1 ] = ' \0 ' ;
if ( spectator )
{
Info_RemoveKey ( cl - > userinfo , " spectator " ) ;
2005-03-07 08:58:26 +00:00
Info_SetValueForStarKey ( cl - > userinfo , " *spectator " , " 1 " , sizeof ( cl - > userinfo ) ) ;
2004-08-23 00:15:46 +00:00
}
else
Info_RemoveKey ( cl - > userinfo , " *spectator " ) ;
SV_ExtractFromUserinfo ( cl ) ;
SV_GetNewSpawnParms ( cl ) ;
}
newcl - > controller = NULL ;
Sys_ServerActivity ( ) ;
2005-12-15 19:15:39 +00:00
PIN_ShowMessages ( newcl ) ;
2005-05-26 12:55:34 +00:00
if ( ISNQCLIENT ( newcl ) )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
newcl - > netchan . message . maxsize = sizeof ( newcl - > netchan . message_buf ) ;
host_client = newcl ;
SVNQ_New_f ( ) ;
}
2004-08-23 00:15:46 +00:00
2005-12-15 19:15:39 +00:00
2005-05-26 12:55:34 +00:00
return newcl ;
}
2004-08-23 00:15:46 +00:00
int Rcon_Validate ( void )
{
if ( ! strlen ( rcon_password . string ) )
return 0 ;
if ( strcmp ( Cmd_Argv ( 1 ) , rcon_password . string ) )
return 0 ;
return 1 ;
}
/*
= = = = = = = = = = = = = = =
SVC_RemoteCommand
A client issued an rcon command .
Shift down the remaining args
Redirect all printfs
= = = = = = = = = = = = = = =
*/
void SVC_RemoteCommand ( void )
{
int i ;
char remaining [ 1024 ] ;
2006-05-25 04:47:03 +00:00
{
bannedips_t * banip = SV_BannedAddress ( & net_from ) ;
if ( banip )
{
Con_Printf ( " Rcon from banned ip %s \n " , NET_AdrToString ( net_from ) ) ;
return ;
}
}
2004-08-23 00:15:46 +00:00
if ( ! Rcon_Validate ( ) )
{
2005-11-03 23:40:51 +00:00
# ifdef SVRANKING
2005-03-12 23:40:42 +00:00
if ( cmd_allowaccess . value ) //try and find a username, match the numeric password
2004-08-23 00:15:46 +00:00
{
2005-03-12 23:40:42 +00:00
int rid ;
char * s = Cmd_Argv ( 1 ) ;
char * colon = NULL , * c2 ;
rankstats_t stats ;
c2 = s ;
for ( ; ; )
2004-08-23 00:15:46 +00:00
{
2005-03-12 23:40:42 +00:00
c2 = strchr ( c2 , ' : ' ) ;
if ( ! c2 )
break ;
colon = c2 ;
c2 + + ;
}
if ( colon ) //oh, could this be a specific username?
{
* colon = ' \0 ' ;
colon + + ;
rid = Rank_GetPlayerID ( s , atoi ( colon ) , false , true ) ;
if ( rid )
{
if ( ! Rank_GetPlayerStats ( rid , & stats ) )
return ;
2004-08-23 00:15:46 +00:00
2005-07-03 15:16:20 +00:00
2005-03-12 23:40:42 +00:00
Con_Printf ( " Rcon from %s: \n %s \n "
, NET_AdrToString ( net_from ) , net_message . data + 4 ) ;
2004-08-23 00:15:46 +00:00
2005-03-12 23:40:42 +00:00
SV_BeginRedirect ( RD_PACKET , LANGDEFAULT ) ;
2004-08-23 00:15:46 +00:00
2005-03-12 23:40:42 +00:00
remaining [ 0 ] = 0 ;
2004-08-23 00:15:46 +00:00
2005-03-12 23:40:42 +00:00
for ( i = 2 ; i < Cmd_Argc ( ) ; i + + )
2004-08-23 00:15:46 +00:00
{
2005-03-12 23:40:42 +00:00
if ( strlen ( remaining ) + strlen ( Cmd_Argv ( i ) ) > = sizeof ( remaining ) - 2 )
{
Con_Printf ( " Rcon was too long \n " ) ;
SV_EndRedirect ( ) ;
Con_Printf ( " Rcon from %s: \n %s \n "
, NET_AdrToString ( net_from ) , " Was too long - possible buffer overflow attempt " ) ;
return ;
}
strcat ( remaining , Cmd_Argv ( i ) ) ;
strcat ( remaining , " " ) ;
2004-08-23 00:15:46 +00:00
}
2005-03-12 23:40:42 +00:00
Cmd_ExecuteString ( remaining , stats . trustlevel ) ;
2004-08-23 00:15:46 +00:00
2005-03-12 23:40:42 +00:00
SV_EndRedirect ( ) ;
return ;
}
2004-08-23 00:15:46 +00:00
}
}
2005-11-03 23:40:51 +00:00
# endif
2004-08-23 00:15:46 +00:00
Con_Printf ( " Bad rcon from %s: \n %s \n "
, NET_AdrToString ( net_from ) , net_message . data + 4 ) ;
2005-01-16 00:59:48 +00:00
SV_BeginRedirect ( RD_PACKET , LANGDEFAULT ) ;
2004-08-23 00:15:46 +00:00
Con_Printf ( " Bad rcon_password. \n " ) ;
}
else
{
Con_Printf ( " Rcon from %s: \n %s \n "
, NET_AdrToString ( net_from ) , net_message . data + 4 ) ;
2005-01-16 00:59:48 +00:00
SV_BeginRedirect ( RD_PACKET , LANGDEFAULT ) ;
2004-08-23 00:15:46 +00:00
remaining [ 0 ] = 0 ;
for ( i = 2 ; i < Cmd_Argc ( ) ; i + + )
{
if ( strlen ( remaining ) + strlen ( Cmd_Argv ( i ) ) > = sizeof ( remaining ) - 2 )
{
Con_Printf ( " Rcon was too long \n " ) ;
SV_EndRedirect ( ) ;
Con_Printf ( " Rcon from %s: \n %s \n "
, NET_AdrToString ( net_from ) , " Was too long - possible buffer overflow attempt " ) ;
return ;
}
strcat ( remaining , Cmd_Argv ( i ) ) ;
strcat ( remaining , " " ) ;
}
Cmd_ExecuteString ( remaining , rcon_level . value ) ;
}
SV_EndRedirect ( ) ;
}
2006-09-17 00:59:22 +00:00
void SVC_RealIP ( void )
{
unsigned int slotnum ;
int cookie ;
slotnum = atoi ( Cmd_Argv ( 1 ) ) ;
cookie = atoi ( Cmd_Argv ( 2 ) ) ;
if ( slotnum > = MAX_CLIENTS )
{
//a malitious user
return ;
}
if ( cookie ! = svs . clients [ slotnum ] . realip_num )
{
//could be someone trying to kick someone else
//so we can't kick, as much as we might like to.
return ;
}
if ( svs . clients [ slotnum ] . realip_status )
return ;
svs . clients [ slotnum ] . realip_status = 1 ;
svs . clients [ slotnum ] . realip = net_from ;
}
void SVC_ACK ( void )
{
int slotnum ;
for ( slotnum = 0 ; slotnum < MAX_CLIENTS ; slotnum + + )
{
if ( svs . clients [ slotnum ] . state )
{
if ( svs . clients [ slotnum ] . realip_status = = 1 & & NET_CompareAdr ( svs . clients [ slotnum ] . realip , net_from ) )
{
if ( ! * Cmd_Argv ( 1 ) )
svs . clients [ slotnum ] . realip_status = 2 ;
else if ( atoi ( Cmd_Argv ( 1 ) ) = = svs . clients [ slotnum ] . realip_ping & &
atoi ( Cmd_Argv ( 2 ) ) = = svs . clients [ slotnum ] . realip_num )
{
svs . clients [ slotnum ] . realip_status = 3 ;
}
else
{
Netchan_OutOfBandPrint ( NS_SERVER , net_from , " realip not accepted. Please stop hacking. \n " ) ;
}
return ;
}
}
}
Con_Printf ( " A2A_ACK from %s \n " , NET_AdrToString ( net_from ) ) ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = =
SV_ConnectionlessPacket
A connectionless packet has four leading 0xff
characters to distinguish it from a game channel .
Clients that are in the game can still send
connectionless packets .
= = = = = = = = = = = = = = = = =
*/
qboolean SV_ConnectionlessPacket ( void )
{
char * s ;
char * c ;
MSG_BeginReading ( ) ;
if ( net_message . cursize > = MAX_QWMSGLEN ) //add a null term in message space
{
Con_Printf ( " Oversized packet from %s \n " , NET_AdrToString ( net_from ) ) ;
net_message . cursize = MAX_QWMSGLEN - 1 ;
}
net_message . data [ net_message . cursize ] = ' \0 ' ; //terminate it properly. Just in case.
MSG_ReadLong ( ) ; // skip the -1 marker
s = MSG_ReadStringLine ( ) ;
2004-12-08 04:14:52 +00:00
Cmd_TokenizeString ( s , false , false ) ;
2004-08-23 00:15:46 +00:00
c = Cmd_Argv ( 0 ) ;
if ( ! strcmp ( c , " ping " ) | | ( c [ 0 ] = = A2A_PING & & ( c [ 1 ] = = 0 | | c [ 1 ] = = ' \n ' ) ) )
SVC_Ping ( ) ;
else if ( c [ 0 ] = = A2A_ACK & & ( c [ 1 ] = = 0 | | c [ 1 ] = = ' \n ' ) )
2006-09-17 00:59:22 +00:00
SVC_ACK ( ) ;
2004-08-23 00:15:46 +00:00
else if ( ! strcmp ( c , " status " ) )
SVC_Status ( ) ;
else if ( ! strcmp ( c , " log " ) )
SVC_Log ( ) ;
2005-07-03 15:16:20 +00:00
# ifdef Q2SERVER
2004-08-23 00:15:46 +00:00
else if ( ! strcmp ( c , " info " ) )
SVC_InfoQ2 ( ) ;
2005-07-03 15:16:20 +00:00
# endif
2004-08-23 00:15:46 +00:00
else if ( ! strncmp ( c , " connect " , 7 ) )
{
2005-03-07 08:58:26 +00:00
# ifdef Q3SERVER
if ( svs . gametype = = GT_QUAKE3 )
{
SVQ3_DirectConnect ( ) ;
return true ;
}
2005-07-03 15:16:20 +00:00
else
2005-03-07 08:58:26 +00:00
# endif
if ( secure . value ) //FIXME: possible problem for nq clients when enabled
2004-08-23 00:15:46 +00:00
Netchan_OutOfBandPrint ( NS_SERVER , net_from , " %c \n This server requires client validation. \n Please use the " DISTRIBUTION " validation program \n " , A2C_PRINT ) ;
else
{
SVC_DirectConnect ( ) ;
return true ;
}
}
2005-08-03 23:14:59 +00:00
else if ( ! strcmp ( c , " \xad \xad \xad \xad " " getchallenge " ) )
{
SVC_GetChallenge ( ) ;
}
2004-08-23 00:15:46 +00:00
else if ( ! strcmp ( c , " getchallenge " ) )
{
2005-05-26 12:55:34 +00:00
SVC_GetChallenge ( ) ;
2004-08-23 00:15:46 +00:00
}
2005-07-03 15:16:20 +00:00
# ifdef NQPROT
2005-05-26 12:55:34 +00:00
else if ( ! strcmp ( c , " getinfo " ) )
SVC_GetInfo ( Cmd_Args ( ) ) ;
2005-07-03 15:16:20 +00:00
# endif
2004-08-23 00:15:46 +00:00
else if ( ! strcmp ( c , " rcon " ) )
SVC_RemoteCommand ( ) ;
2006-09-17 00:59:22 +00:00
else if ( ! strcmp ( c , " realip " ) )
SVC_RealIP ( ) ;
2006-01-21 00:06:49 +00:00
else if ( ! PR_GameCodePacket ( net_message . data + 4 ) )
2004-08-23 00:15:46 +00:00
Con_Printf ( " bad connectionless packet from %s: \n %s \n "
, NET_AdrToString ( net_from ) , s ) ;
return false ;
}
2005-06-14 04:52:10 +00:00
# ifdef NQPROT
2005-05-26 12:55:34 +00:00
void SVNQ_ConnectionlessPacket ( void )
{
sizebuf_t sb ;
int header ;
int length ;
char * str ;
char buffer [ 256 ] ;
2005-06-04 04:20:20 +00:00
if ( net_from . type = = NA_LOOPBACK )
return ;
2007-06-10 05:14:38 +00:00
if ( ! sv_listen_nq . value )
2005-05-26 12:55:34 +00:00
return ;
2005-07-01 19:23:00 +00:00
if ( sv_bigcoords . value )
return ; //no, start using dp7 instead.
2005-05-26 12:55:34 +00:00
MSG_BeginReading ( ) ;
2005-07-29 22:26:43 +00:00
header = LongSwap ( MSG_ReadLong ( ) ) ;
2005-05-26 12:55:34 +00:00
if ( ! ( header & NETFLAG_CTL ) )
return ; //no idea what it is.
length = header & NETFLAG_LENGTH_MASK ;
if ( length ! = net_message . cursize )
return ; //corrupt or not ours
switch ( MSG_ReadByte ( ) )
{
case CCREQ_CONNECT :
sb . maxsize = sizeof ( buffer ) ;
sb . data = buffer ;
if ( strcmp ( MSG_ReadString ( ) , NET_GAMENAME_NQ ) )
{
SZ_Clear ( & sb ) ;
MSG_WriteLong ( & sb , 0 ) ;
MSG_WriteByte ( & sb , CCREP_REJECT ) ;
MSG_WriteString ( & sb , " Incorrect game \n " ) ;
* ( int * ) sb . data = BigLong ( NETFLAG_CTL + sb . cursize ) ;
NET_SendPacket ( NS_SERVER , sb . cursize , sb . data , net_from ) ;
return ; //not our game.
}
if ( MSG_ReadByte ( ) ! = NET_PROTOCOL_VERSION )
{
SZ_Clear ( & sb ) ;
MSG_WriteLong ( & sb , 0 ) ;
MSG_WriteByte ( & sb , CCREP_REJECT ) ;
MSG_WriteString ( & sb , " Incorrect version \n " ) ;
* ( int * ) sb . data = BigLong ( NETFLAG_CTL + sb . cursize ) ;
NET_SendPacket ( NS_SERVER , sb . cursize , sb . data , net_from ) ;
return ; //not our version...
}
str = va ( " connect %i %i %i \" \\ name \\ unconnected \" " , NET_PROTOCOL_VERSION , 0 , SV_NewChallenge ( ) ) ;
Cmd_TokenizeString ( str , false , false ) ;
2005-06-04 04:20:20 +00:00
SVC_DirectConnect ( ) ;
2005-05-26 12:55:34 +00:00
break ;
}
}
2005-06-14 04:52:10 +00:00
# endif
2005-05-26 12:55:34 +00:00
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
PACKET FILTERING
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
You can add or remove addresses from the filter list with :
addip < ip >
removeip < ip >
The ip address is specified in dot format , and any unspecified digits will match any value , so you can specify an entire class C network with " addip 192.246.40 " .
Removeip will only remove an address specified exactly the same way . You cannot addip a subnet , then removeip a single host .
listip
Prints the current list of filters .
writeip
Dumps " addip <ip> " commands to listip . cfg so it can be execed at a later date . The filter lists are not saved and restored by default , because I beleive it would cause too much confusion .
filterban < 0 or 1 >
If 1 ( the default ) , then ip addresses matching the current list will be prohibited from entering the game . This is the default setting .
If 0 , then only addresses matching the list will be allowed . This lets you easily set up a private game , or a game that only allows players from your local network .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2006-02-11 02:09:43 +00:00
cvar_t filterban = SCVAR ( " filterban " , " 1 " ) ;
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = =
SV_FilterPacket
= = = = = = = = = = = = = = = = =
*/
2006-05-25 04:47:03 +00:00
qboolean SV_FilterPacket ( netadr_t * a )
2004-08-23 00:15:46 +00:00
{
2006-05-25 04:47:03 +00:00
filteredips_t * banip ;
2005-07-03 15:16:20 +00:00
2006-05-25 04:47:03 +00:00
if ( NET_IsLoopBackAddress ( * a ) )
return 0 ; // never filter loopback
2004-08-23 00:15:46 +00:00
2006-05-25 04:47:03 +00:00
for ( banip = svs . filteredips ; banip ; banip = banip - > next )
{
if ( NET_CompareAdrMasked ( * a , banip - > adr , banip - > adrmask ) )
2004-08-23 00:15:46 +00:00
return filterban . value ;
2006-05-25 04:47:03 +00:00
}
2004-08-23 00:15:46 +00:00
return ! filterban . value ;
}
2006-05-25 04:47:03 +00:00
// SV_BannedAdress, run through ban address list and return corresponding bannedips_t
// pointer, otherwise return NULL if not in the list
bannedips_t * SV_BannedAddress ( netadr_t * a )
{
bannedips_t * banip ;
for ( banip = svs . bannedips ; banip ; banip = banip - > next )
{
if ( NET_CompareAdrMasked ( * a , banip - > adr , banip - > adrmask ) )
return banip ;
}
return NULL ;
}
2004-08-23 00:15:46 +00:00
//send a network packet to a new non-connected client.
//this is to combat tunneling
void SV_OpenRoute_f ( void )
{
netadr_t to ;
char data [ 64 ] ;
NET_StringToAdr ( Cmd_Argv ( 1 ) , & to ) ;
if ( ! to . port )
to . port = PORT_CLIENT ;
sprintf ( data , " \xff \xff \xff \xff %c " , S2C_CONNECTION ) ;
Netchan_OutOfBandPrint ( NS_SERVER , to , " hello " ) ;
// NET_SendPacket (strlen(data)+1, data, to);
}
//============================================================================
/*
= = = = = = = = = = = = = = = = =
SV_ReadPackets
= = = = = = = = = = = = = = = = =
*/
//FIMXE: move to header
qboolean SV_GetPacket ( void ) ;
void SV_ReadPackets ( void )
{
int i ;
client_t * cl ;
qboolean good ;
int qport ;
good = false ;
while ( SV_GetPacket ( ) )
{
2006-05-25 04:47:03 +00:00
if ( SV_FilterPacket ( & net_from ) )
2004-08-23 00:15:46 +00:00
continue ;
// check for connectionless packet (0xffffffff) first
if ( * ( int * ) net_message . data = = - 1 )
{
2005-05-26 12:55:34 +00:00
SV_ConnectionlessPacket ( ) ;
2004-08-23 00:15:46 +00:00
continue ;
}
2005-03-07 08:58:26 +00:00
# ifdef Q3SERVER
if ( svs . gametype = = GT_QUAKE3 )
{
SVQ3_HandleClient ( ) ;
continue ;
}
# endif
2004-08-23 00:15:46 +00:00
// read the qport out of the message so we can fix up
// stupid address translating routers
MSG_BeginReading ( ) ;
MSG_ReadLong ( ) ; // sequence number
MSG_ReadLong ( ) ; // sequence number
qport = MSG_ReadShort ( ) & 0xffff ;
// check for packets from connected clients
for ( i = 0 , cl = svs . clients ; i < MAX_CLIENTS ; i + + , cl + + )
{
if ( cl - > state = = cs_free )
continue ;
if ( ! NET_CompareBaseAdr ( net_from , cl - > netchan . remote_address ) )
continue ;
2005-05-26 12:55:34 +00:00
# ifdef NQPROT
if ( ISNQCLIENT ( cl ) & & cl - > netchan . remote_address . port = = net_from . port )
{
if ( cl - > state ! = cs_zombie )
{
if ( NQNetChan_Process ( & cl - > netchan ) )
{
svs . stats . packets + + ;
SVNQ_ExecuteClientMessage ( cl ) ;
}
}
break ;
}
# endif
2004-08-23 00:15:46 +00:00
if ( cl - > netchan . qport ! = qport )
continue ;
if ( cl - > netchan . remote_address . port ! = net_from . port )
{
Con_DPrintf ( " SV_ReadPackets: fixing up a translated port \n " ) ;
cl - > netchan . remote_address . port = net_from . port ;
}
if ( Netchan_Process ( & cl - > netchan ) )
{ // this is a valid, sequenced packet, so process it
svs . stats . packets + + ;
good = true ;
if ( cl - > state ! = cs_zombie )
{
cl - > send_message = true ; // reply at end of frame
# ifdef Q2SERVER
2005-05-26 12:55:34 +00:00
if ( cl - > protocol = = SCP_QUAKE2 )
2004-08-23 00:15:46 +00:00
SVQ2_ExecuteClientMessage ( cl ) ;
else
# endif
SV_ExecuteClientMessage ( cl ) ;
}
}
break ;
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
if ( i ! = MAX_CLIENTS )
continue ;
2005-05-26 12:55:34 +00:00
# ifdef NQPROT
SVNQ_ConnectionlessPacket ( ) ;
# endif
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
// packet is not from a known client
// Con_Printf ("%s:sequenced packet without connection\n"
// ,NET_AdrToString(net_from));
}
}
/*
= = = = = = = = = = = = = = = = = =
SV_CheckTimeouts
If a packet has not been received from a client in timeout . value
seconds , drop the conneciton .
When a client is normally dropped , the client_t goes into a zombie state
for a few seconds to make sure any final reliable message gets resent
if necessary
= = = = = = = = = = = = = = = = = =
*/
void SV_CheckTimeouts ( void )
{
int i ;
client_t * cl ;
float droptime ;
int nclients ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
droptime = realtime - timeout . value ;
nclients = 0 ;
for ( i = 0 , cl = svs . clients ; i < MAX_CLIENTS ; i + + , cl + + )
{
if ( cl - > state = = cs_connected | | cl - > state = = cs_spawned ) {
if ( ! cl - > spectator )
nclients + + ;
2005-06-14 04:52:10 +00:00
if ( cl - > netchan . last_received < droptime & & cl - > netchan . remote_address . type ! = NA_LOOPBACK & & cl - > protocol ! = SCP_BAD ) {
2004-08-23 00:15:46 +00:00
SV_BroadcastTPrintf ( PRINT_HIGH , STL_CLIENTTIMEDOUT , cl - > name ) ;
2005-07-03 15:16:20 +00:00
SV_DropClient ( cl ) ;
2004-08-23 00:15:46 +00:00
cl - > state = cs_free ; // don't bother with zombie state
}
}
2005-07-03 15:16:20 +00:00
if ( cl - > state = = cs_zombie & &
2004-08-23 00:15:46 +00:00
realtime - cl - > connection_started > zombietime . value )
{
if ( cl - > connection_started = = - 1 )
{
continue ;
}
cl - > state = cs_free ; // can now be reused
if ( cl - > istobeloaded )
{
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , cl - > edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > ClientDisconnect ) ;
host_client - > istobeloaded = false ;
SV_BroadcastTPrintf ( PRINT_HIGH , STL_LOADZOMIBETIMEDOUT , cl - > name ) ;
}
}
}
if ( sv . paused & & ! nclients ) {
// nobody left, unpause the server
2006-01-21 00:06:49 +00:00
if ( SV_TogglePause ( NULL ) )
SV_BroadcastTPrintf ( PRINT_HIGH , STL_CLIENTLESSUNPAUSE ) ;
2004-08-23 00:15:46 +00:00
}
}
/*
= = = = = = = = = = = = = = = = = = =
SV_GetConsoleCommands
Add them exactly as if they had been typed at the console
= = = = = = = = = = = = = = = = = = =
*/
void SV_GetConsoleCommands ( void )
{
char * cmd ;
while ( 1 )
{
cmd = Sys_ConsoleInput ( ) ;
if ( ! cmd )
break ;
Cbuf_AddText ( cmd , RESTRICT_LOCAL ) ;
}
}
2004-10-19 16:10:14 +00:00
int SV_RateForClient ( client_t * cl )
{
int rate ;
2005-03-20 02:57:11 +00:00
if ( cl - > download & & cl - > drate )
2004-10-19 16:10:14 +00:00
{
rate = cl - > drate ;
if ( rate > sv_maxdrate . value )
rate = sv_maxdrate . value ;
}
else
{
rate = cl - > rate ;
if ( rate > sv_maxrate . value )
rate = sv_maxrate . value ;
}
if ( rate < 500 )
rate = 500 ;
return rate ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = =
SV_CheckVars
= = = = = = = = = = = = = = = = = = =
*/
void SV_CheckVars ( void )
{
static char * pw , * spw ;
int v ;
if ( password . string = = pw & & spectator_password . string = = spw )
return ;
pw = password . string ;
spw = spectator_password . string ;
v = 0 ;
if ( pw & & pw [ 0 ] & & strcmp ( pw , " none " ) )
v | = 1 ;
if ( spw & & spw [ 0 ] & & strcmp ( spw , " none " ) )
v | = 2 ;
Con_DPrintf ( " Updated needpass. \n " ) ;
if ( ! v )
Info_SetValueForKey ( svs . info , " needpass " , " " , MAX_SERVERINFO_STRING ) ;
else
Info_SetValueForKey ( svs . info , " needpass " , va ( " %i " , v ) , MAX_SERVERINFO_STRING ) ;
}
# ifdef Q2SERVER
void SVQ2_ClearEvents ( void )
{
q2edict_t * ent ;
int i ;
for ( i = 0 ; i < ge - > num_edicts ; i + + , ent + + )
{
ent = Q2EDICT_NUM ( i ) ;
// events only last for a single message
ent - > s . event = 0 ;
}
}
# endif
/*
= = = = = = = = = = = = = = = = = =
SV_Impulse_f
Spawns a client , uses an impulse , uses that clients think then removes the client .
= = = = = = = = = = = = = = = = = =
*/
void SV_Impulse_f ( void )
{
int i ;
for ( i = 0 ; i < sv . allocated_client_slots ; i + + )
{
if ( svs . clients [ i ] . state = = cs_free )
{
break ;
}
}
if ( i = = sv . allocated_client_slots )
{
Con_Printf ( " No empty player slots \n " ) ;
return ;
}
2005-03-18 06:13:11 +00:00
if ( ! svprogfuncs )
return ;
2004-08-23 00:15:46 +00:00
pr_global_struct - > time = sv . time ;
2005-10-04 21:19:00 +00:00
svs . clients [ i ] . state = cs_connected ;
2005-10-07 16:27:40 +00:00
SV_SetUpClientEdict ( & svs . clients [ i ] , svs . clients [ i ] . edict ) ;
2004-10-03 22:52:02 +00:00
2005-03-28 00:11:59 +00:00
svs . clients [ i ] . edict - > v - > netname = PR_SetString ( svprogfuncs , " Console " ) ;
2004-08-23 00:15:46 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > ClientConnect ) ;
pr_global_struct - > time = sv . time ;
2005-07-03 15:16:20 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
2004-08-23 00:15:46 +00:00
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > PutClientInServer ) ;
2005-01-04 08:04:42 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > PlayerPreThink ) ;
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
2005-03-28 00:11:59 +00:00
PR_ExecuteProgram ( svprogfuncs , svs . clients [ i ] . edict - > v - > think ) ;
2005-01-04 08:04:42 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > PlayerPostThink ) ;
2005-03-28 00:11:59 +00:00
svs . clients [ i ] . edict - > v - > impulse = atoi ( Cmd_Argv ( 1 ) ) ;
2004-08-23 00:15:46 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > PlayerPreThink ) ;
2004-10-03 22:52:02 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
2005-03-28 00:11:59 +00:00
PR_ExecuteProgram ( svprogfuncs , svs . clients [ i ] . edict - > v - > think ) ;
2004-08-23 00:15:46 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > PlayerPostThink ) ;
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , svs . clients [ i ] . edict ) ;
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > ClientDisconnect ) ;
2005-10-04 21:19:00 +00:00
svs . clients [ i ] . state = cs_free ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
SV_Frame
= = = = = = = = = = = = = = = = = =
*/
2005-08-07 18:08:13 +00:00
void SV_Frame ( void )
2004-08-23 00:15:46 +00:00
{
2005-07-01 19:23:00 +00:00
extern cvar_t pr_imitatemvdsv ;
2004-08-23 00:15:46 +00:00
static double start , end ;
2005-08-07 18:08:13 +00:00
float oldtime ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
start = Sys_DoubleTime ( ) ;
svs . stats . idle + = start - end ;
end = start ;
2006-12-26 18:19:35 +00:00
svs . framenum + + ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
// keep the random time dependent
rand ( ) ;
2005-03-20 02:57:11 +00:00
if ( ! sv . gamespeed )
sv . gamespeed = 1 ;
2004-08-23 00:15:46 +00:00
// decide the simulation time
2005-08-07 18:08:13 +00:00
{
oldtime = sv . time ;
sv . time = ( Sys_DoubleTime ( ) - sv . starttime ) * sv . gamespeed ;
if ( sv . time < oldtime )
sv . time = oldtime ; //urm
if ( sv . paused )
{
sv . starttime + = sv . time - oldtime ; //move the offset
sv . time = oldtime ; //and keep time as it was.
}
2004-08-23 00:15:46 +00:00
# ifndef SERVERONLY
if ( isDedicated )
# endif
2005-08-07 18:08:13 +00:00
realtime + = sv . time - oldtime ;
2005-03-20 02:57:11 +00:00
2004-08-23 00:15:46 +00:00
}
2005-08-07 18:08:13 +00:00
2004-08-23 00:15:46 +00:00
# ifdef IWEB_H__
IWebRun ( ) ;
# endif
if ( sv_master . value )
{
if ( sv_masterport . value )
SVM_Think ( sv_masterport . value ) ;
else
SVM_Think ( PORT_MASTER ) ;
}
2004-12-08 04:14:52 +00:00
{
void SV_MVDStream_Poll ( void ) ;
SV_MVDStream_Poll ( ) ;
}
2006-01-02 22:33:23 +00:00
if ( sv . state < ss_active | | ! sv . worldmodel )
2004-08-23 00:15:46 +00:00
{
# ifndef SERVERONLY
// check for commands typed to the host
if ( isDedicated )
# endif
{
SV_GetConsoleCommands ( ) ;
Cbuf_Execute ( ) ;
}
return ;
}
# ifdef VOICECHAT
SVVC_Frame ( sv_voicechat . value ) ;
# endif
// check timeouts
SV_CheckTimeouts ( ) ;
SV_CheckTimer ( ) ;
// toggle the log buffer if full
SV_CheckLog ( ) ;
// get packets
SV_ReadPackets ( ) ;
2005-07-01 19:23:00 +00:00
if ( pr_imitatemvdsv . value )
{
Cbuf_Execute ( ) ;
if ( sv . state < ss_active ) //whoops...
return ;
}
2004-08-23 00:15:46 +00:00
if ( sv . multicast . cursize )
{
Con_Printf ( " Unterminated multicast \n " ) ;
sv . multicast . cursize = 0 ;
}
// move autonomous things around if enough time has passed
if ( ! sv . paused )
2006-01-21 00:06:49 +00:00
{
2004-08-23 00:15:46 +00:00
if ( SV_Physics ( ) )
return ;
2006-01-21 00:06:49 +00:00
}
else
{
PR_GameCodePausedTic ( Sys_DoubleTime ( ) - sv . pausedstart ) ;
}
2004-08-23 00:15:46 +00:00
while ( SV_ReadMVD ( ) ) ;
if ( sv . multicast . cursize )
{
Con_Printf ( " Unterminated multicast \n " ) ;
sv . multicast . cursize = 0 ;
}
# ifndef SERVERONLY
// check for commands typed to the host
if ( isDedicated )
# endif
{
2005-12-15 19:15:39 +00:00
# ifdef PLUGINS
Plug_Tick ( ) ;
# endif
2004-08-23 00:15:46 +00:00
SV_GetConsoleCommands ( ) ;
// process console commands
2005-07-01 19:23:00 +00:00
if ( ! pr_imitatemvdsv . value )
2005-11-26 21:15:39 +00:00
Cbuf_Execute ( ) ;
2004-08-23 00:15:46 +00:00
}
2005-07-01 19:23:00 +00:00
2004-08-23 00:15:46 +00:00
if ( sv . state < ss_active ) //whoops...
return ;
SV_CheckVars ( ) ;
// send messages back to the clients that had packets read this frame
SV_SendClientMessages ( ) ;
// demo_start = Sys_DoubleTime ();
SV_SendMVDMessage ( ) ;
// demo_end = Sys_DoubleTime ();
// svs.stats.demo += demo_end - demo_start;
// send a heartbeat to the master if needed
Master_Heartbeat ( ) ;
# ifdef Q2SERVER
if ( ge & & ge - > edicts )
SVQ2_ClearEvents ( ) ;
# endif
// collect timing statistics
end = Sys_DoubleTime ( ) ;
svs . stats . active + = end - start ;
if ( + + svs . stats . count = = STATFRAMES )
{
svs . stats . latched_active = svs . stats . active ;
svs . stats . latched_idle = svs . stats . idle ;
svs . stats . latched_packets = svs . stats . packets ;
svs . stats . active = 0 ;
svs . stats . idle = 0 ;
svs . stats . packets = 0 ;
svs . stats . count = 0 ;
}
}
/*
= = = = = = = = = = = = = = =
SV_InitLocal
= = = = = = = = = = = = = = =
*/
2005-10-05 02:16:28 +00:00
extern void Log_Init ( void ) ;
2004-08-23 00:15:46 +00:00
void SV_InitLocal ( void )
{
2006-05-13 21:11:06 +00:00
int i , p ;
2004-08-23 00:15:46 +00:00
extern cvar_t sv_maxvelocity ;
extern cvar_t sv_gravity ;
extern cvar_t sv_aim ;
extern cvar_t sv_stopspeed ;
extern cvar_t sv_spectatormaxspeed ;
extern cvar_t sv_accelerate ;
extern cvar_t sv_airaccelerate ;
extern cvar_t sv_wateraccelerate ;
extern cvar_t sv_friction ;
extern cvar_t sv_waterfriction ;
2004-08-31 09:05:42 +00:00
extern cvar_t pr_allowbutton1 ;
2004-08-23 00:15:46 +00:00
extern cvar_t pm_bunnyspeedcap ;
extern cvar_t pm_ktjump ;
extern cvar_t pm_slidefix ;
extern cvar_t pm_airstep ;
2007-08-14 17:07:40 +00:00
extern cvar_t pm_pground ;
2005-01-04 08:04:42 +00:00
extern cvar_t pm_walljump ;
2005-05-17 02:36:54 +00:00
extern cvar_t pm_slidyslopes ;
2007-06-20 00:02:54 +00:00
extern cvar_t pm_stepheight ;
2004-08-23 00:15:46 +00:00
SV_InitOperatorCommands ( ) ;
SV_UserInit ( ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
# ifndef SERVERONLY
if ( isDedicated )
# endif
{
Cvar_Register ( & developer , cvargroup_servercontrol ) ;
Cvar_Register ( & password , cvargroup_servercontrol ) ;
Cvar_Register ( & rcon_password , cvargroup_servercontrol ) ;
2005-10-05 02:16:28 +00:00
Log_Init ( ) ;
2004-08-23 00:15:46 +00:00
}
2005-07-03 15:16:20 +00:00
rcon_password . restriction = RESTRICT_MAX ; //no cheatie rconers changing rcon passwords...
2004-08-23 00:15:46 +00:00
Cvar_Register ( & spectator_password , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_mintic , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_maxtic , cvargroup_servercontrol ) ;
Cvar_Register ( & fraglimit , cvargroup_serverinfo ) ;
Cvar_Register ( & timelimit , cvargroup_serverinfo ) ;
Cvar_Register ( & teamplay , cvargroup_serverinfo ) ;
Cvar_Register ( & coop , cvargroup_serverinfo ) ;
Cvar_Register ( & skill , cvargroup_serverinfo ) ;
Cvar_Register ( & samelevel , cvargroup_serverinfo ) ;
Cvar_Register ( & maxclients , cvargroup_serverinfo ) ;
Cvar_Register ( & maxspectators , cvargroup_serverinfo ) ;
Cvar_Register ( & hostname , cvargroup_serverinfo ) ;
Cvar_Register ( & deathmatch , cvargroup_serverinfo ) ;
Cvar_Register ( & spawn , cvargroup_servercontrol ) ;
//arguably cheats. Must be switched on to use.
Cvar_Register ( & watervis , cvargroup_serverinfo ) ;
Cvar_Register ( & rearview , cvargroup_serverinfo ) ;
Cvar_Register ( & mirrors , cvargroup_serverinfo ) ;
Cvar_Register ( & allow_luma , cvargroup_serverinfo ) ;
Cvar_Register ( & allow_bump , cvargroup_serverinfo ) ;
2005-07-03 15:16:20 +00:00
Cvar_Register ( & allow_skybox , cvargroup_serverinfo ) ;
Cvar_Register ( & sv_allow_splitscreen , cvargroup_serverinfo ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & fbskins , cvargroup_serverinfo ) ;
Cvar_Register ( & timeout , cvargroup_servercontrol ) ;
Cvar_Register ( & zombietime , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_loadentfiles , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_maxvelocity , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_gravity , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_stopspeed , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_maxspeed , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_spectatormaxspeed , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_accelerate , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_airaccelerate , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_wateraccelerate , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_friction , cvargroup_serverphysics ) ;
Cvar_Register ( & sv_waterfriction , cvargroup_serverphysics ) ;
2004-11-17 17:43:07 +00:00
Cvar_Register ( & sv_bigcoords , cvargroup_serverphysics ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & pm_bunnyspeedcap , cvargroup_serverphysics ) ;
Cvar_Register ( & pm_ktjump , cvargroup_serverphysics ) ;
Cvar_Register ( & pm_slidefix , cvargroup_serverphysics ) ;
2005-05-17 02:36:54 +00:00
Cvar_Register ( & pm_slidyslopes , cvargroup_serverphysics ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & pm_airstep , cvargroup_serverphysics ) ;
2007-08-14 17:07:40 +00:00
Cvar_Register ( & pm_pground , cvargroup_serverphysics ) ;
2005-01-04 08:04:42 +00:00
Cvar_Register ( & pm_walljump , cvargroup_serverphysics ) ;
2007-06-20 00:02:54 +00:00
Cvar_Register ( & pm_stepheight , cvargroup_serverphysics ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_compatablehulls , cvargroup_serverphysics ) ;
for ( i = 0 ; i < sizeof ( sv_motd ) / sizeof ( sv_motd [ 0 ] ) ; i + + )
Cvar_Register ( & sv_motd [ i ] , cvargroup_serverinfo ) ;
Cvar_Register ( & sv_aim , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_resetparms , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_public , cvargroup_servercontrol ) ;
2007-06-10 05:14:38 +00:00
Cvar_Register ( & sv_listen_qw , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_listen_nq , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_listen_dp , cvargroup_servercontrol ) ;
sv_listen_qw . restriction = RESTRICT_MAX ;
2006-05-09 00:02:05 +00:00
# ifdef TCPCONNECT
Cvar_Register ( & sv_port_tcp , cvargroup_servercontrol ) ;
2006-05-14 20:52:13 +00:00
sv_port_tcp . restriction = RESTRICT_MAX ;
2006-05-09 00:02:05 +00:00
# endif
# ifdef IPPROTO_IPV6
Cvar_Register ( & sv_port_ipv6 , cvargroup_servercontrol ) ;
2006-05-14 20:52:13 +00:00
sv_port_ipv6 . restriction = RESTRICT_MAX ;
2006-05-09 00:02:05 +00:00
# endif
# ifdef USEIPX
Cvar_Register ( & sv_port_ipx , cvargroup_servercontrol ) ;
2006-05-14 20:52:13 +00:00
sv_port_ipx . restriction = RESTRICT_MAX ;
2006-05-09 00:02:05 +00:00
# endif
Cvar_Register ( & sv_port , cvargroup_servercontrol ) ;
2006-05-14 20:52:13 +00:00
sv_port . restriction = RESTRICT_MAX ;
2006-05-09 00:02:05 +00:00
2005-06-09 16:29:32 +00:00
Cvar_Register ( & sv_reportheartbeats , cvargroup_servercontrol ) ;
2004-08-23 00:15:46 +00:00
# ifndef SERVERONLY
if ( isDedicated )
# endif
Cvar_Set ( & sv_public , " 1 " ) ;
Cvar_Register ( & sv_master , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_masterport , cvargroup_servercontrol ) ;
Cvar_Register ( & filterban , cvargroup_servercontrol ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
Cvar_Register ( & allow_download , cvargroup_serverpermissions ) ;
Cvar_Register ( & allow_download_skins , cvargroup_serverpermissions ) ;
Cvar_Register ( & allow_download_models , cvargroup_serverpermissions ) ;
Cvar_Register ( & allow_download_sounds , cvargroup_serverpermissions ) ;
Cvar_Register ( & allow_download_maps , cvargroup_serverpermissions ) ;
2004-08-30 05:34:41 +00:00
Cvar_Register ( & allow_download_demos , cvargroup_serverpermissions ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & allow_download_anymap , cvargroup_serverpermissions ) ;
2005-03-24 18:32:44 +00:00
Cvar_Register ( & allow_download_pakcontents , cvargroup_serverpermissions ) ;
2004-09-22 15:27:48 +00:00
Cvar_Register ( & allow_download_textures , cvargroup_serverpermissions ) ;
2005-12-21 03:07:33 +00:00
Cvar_Register ( & allow_download_configs , cvargroup_serverpermissions ) ;
2004-09-22 15:27:48 +00:00
Cvar_Register ( & allow_download_pk3s , cvargroup_serverpermissions ) ;
Cvar_Register ( & allow_download_wads , cvargroup_serverpermissions ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & allow_download_root , cvargroup_serverpermissions ) ;
Cvar_Register ( & secure , cvargroup_serverpermissions ) ;
Cvar_Register ( & sv_highchars , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_phs , cvargroup_servercontrol ) ;
2005-03-20 02:57:11 +00:00
Cvar_Register ( & sv_csqcdebug , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_gamespeed , cvargroup_serverphysics ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_nomsec , cvargroup_serverphysics ) ;
2004-08-31 09:05:42 +00:00
Cvar_Register ( & pr_allowbutton1 , cvargroup_servercontrol ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & pausable , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_voicechat , cvargroup_servercontrol ) ;
Cvar_Register ( & sv_maxrate , cvargroup_servercontrol ) ;
2004-11-17 17:43:07 +00:00
Cvar_Register ( & sv_maxdrate , cvargroup_servercontrol ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_nailhack , cvargroup_servercontrol ) ;
Cmd_AddCommand ( " sv_impulse " , SV_Impulse_f ) ;
Cmd_AddCommand ( " openroute " , SV_OpenRoute_f ) ;
Cmd_AddCommand ( " savegame " , SV_Savegame_f ) ;
Cmd_AddCommand ( " loadgame " , SV_Loadgame_f ) ;
Cmd_AddCommand ( " save " , SV_Savegame_f ) ;
Cmd_AddCommand ( " load " , SV_Loadgame_f ) ;
SV_MVDInit ( ) ;
for ( i = 0 ; i < MAX_MODELS ; i + + )
sprintf ( localmodels [ i ] , " *%i " , i ) ;
# ifdef PEXT_SCALE
svs . fteprotocolextensions | = PEXT_SCALE ;
# endif
# ifdef PEXT_LIGHTSTYLECOL
svs . fteprotocolextensions | = PEXT_LIGHTSTYLECOL ;
# endif
# ifdef PEXT_TRANS
svs . fteprotocolextensions | = PEXT_TRANS ;
# endif
# ifdef PEXT_VIEW2
svs . fteprotocolextensions | = PEXT_VIEW2 ;
# endif
# ifdef PEXT_BULLETENS
svs . fteprotocolextensions | = PEXT_BULLETENS ;
# endif
2005-11-30 01:20:53 +00:00
svs . fteprotocolextensions | = PEXT_ACCURATETIMINGS ;
2004-08-23 00:15:46 +00:00
# ifdef PEXT_ZLIBDL
svs . fteprotocolextensions | = PEXT_ZLIBDL ;
# endif
# ifdef PEXT_LIGHTUPDATES
svs . fteprotocolextensions | = PEXT_LIGHTUPDATES ;
# endif
# ifdef PEXT_FATNESS
svs . fteprotocolextensions | = PEXT_FATNESS ;
# endif
# ifdef PEXT_HLBSP
svs . fteprotocolextensions | = PEXT_HLBSP ;
# endif
# ifdef PEXT_Q2BSP
svs . fteprotocolextensions | = PEXT_Q2BSP ;
# endif
# ifdef PEXT_Q3BSP
svs . fteprotocolextensions | = PEXT_Q3BSP ;
# endif
# ifdef PEXT_TE_BULLET
svs . fteprotocolextensions | = PEXT_TE_BULLET ;
# endif
2005-07-03 15:16:20 +00:00
# ifdef PEXT_HULLSIZE
2004-08-23 00:15:46 +00:00
svs . fteprotocolextensions | = PEXT_HULLSIZE ;
# endif
# ifdef PEXT_SETVIEW
svs . fteprotocolextensions | = PEXT_SETVIEW ;
# endif
# ifdef PEXT_MODELDBL
svs . fteprotocolextensions | = PEXT_MODELDBL ;
# endif
2004-11-17 17:43:07 +00:00
# ifdef PEXT_FLOATCOORDS
svs . fteprotocolextensions | = PEXT_FLOATCOORDS ;
2005-02-12 18:56:04 +00:00
# endif
2004-08-23 00:15:46 +00:00
svs . fteprotocolextensions | = PEXT_SPLITSCREEN ;
svs . fteprotocolextensions | = PEXT_HEXEN2 ;
2006-02-27 00:42:25 +00:00
svs . fteprotocolextensions | = PEXT_COLOURMOD ;
2004-08-23 00:15:46 +00:00
svs . fteprotocolextensions | = PEXT_SPAWNSTATIC2 ;
svs . fteprotocolextensions | = PEXT_CUSTOMTEMPEFFECTS ;
svs . fteprotocolextensions | = PEXT_256PACKETENTITIES ;
2005-10-19 21:11:47 +00:00
svs . fteprotocolextensions | = PEXT_ENTITYDBL ;
svs . fteprotocolextensions | = PEXT_ENTITYDBL2 ;
2004-08-31 23:58:18 +00:00
// svs.fteprotocolextensions |= PEXT_64PLAYERS;
svs . fteprotocolextensions | = PEXT_SHOWPIC ;
2004-10-10 06:32:29 +00:00
svs . fteprotocolextensions | = PEXT_SETATTACHMENT ;
2005-07-03 15:16:20 +00:00
2005-01-24 23:47:32 +00:00
# ifdef PEXT_PK3DOWNLOADS
svs . fteprotocolextensions | = PEXT_PK3DOWNLOADS ;
2005-07-03 15:16:20 +00:00
# endif
2004-08-23 00:15:46 +00:00
2005-01-16 00:59:48 +00:00
# ifdef PEXT_CHUNKEDDOWNLOADS
svs . fteprotocolextensions | = PEXT_CHUNKEDDOWNLOADS ;
# endif
2005-02-28 07:16:19 +00:00
# ifdef PEXT_CSQC
svs . fteprotocolextensions | = PEXT_CSQC ;
# endif
2005-08-07 18:08:13 +00:00
# ifdef PEXT_DPFLAGS
svs . fteprotocolextensions | = PEXT_DPFLAGS ;
# endif
2005-02-28 07:16:19 +00:00
2004-08-23 00:15:46 +00:00
// if (svs.protocolextensions)
// Info_SetValueForStarKey (svs.info, "*"DISTRIBUTION"_ext", va("%x", svs.protocolextensions), MAX_SERVERINFO_STRING);
2005-10-04 21:08:06 +00:00
Info_SetValueForStarKey ( svs . info , " *version " , va ( " %s %i " , DISTRIBUTION , build_number ( ) ) , MAX_SERVERINFO_STRING ) ;
2004-08-23 00:15:46 +00:00
2005-01-16 02:23:31 +00:00
Info_SetValueForStarKey ( svs . info , " *z_ext " , va ( " %i " , SUPPORTED_Z_EXTENSIONS ) , MAX_SERVERINFO_STRING ) ;
2004-08-23 00:15:46 +00:00
// init fraglog stuff
svs . logsequence = 1 ;
svs . logtime = realtime ;
svs . log [ 0 ] . data = svs . log_buf [ 0 ] ;
svs . log [ 0 ] . maxsize = sizeof ( svs . log_buf [ 0 ] ) ;
svs . log [ 0 ] . cursize = 0 ;
svs . log [ 0 ] . allowoverflow = true ;
svs . log [ 1 ] . data = svs . log_buf [ 1 ] ;
svs . log [ 1 ] . maxsize = sizeof ( svs . log_buf [ 1 ] ) ;
svs . log [ 1 ] . cursize = 0 ;
svs . log [ 1 ] . allowoverflow = true ;
2006-05-13 21:11:06 +00:00
// parse params for cvars
p = COM_CheckParm ( " -port " ) ;
if ( ! p )
p = COM_CheckParm ( " -svport " ) ;
if ( p & & p < com_argc )
{
int port = atoi ( com_argv [ p + 1 ] ) ;
if ( ! port )
port = PORT_SERVER ;
Cvar_SetValue ( & sv_port , port ) ;
# ifdef IPPROTO_IPV6
Cvar_SetValue ( & sv_port_ipv6 , port ) ;
# endif
# ifdef USEIPX
Cvar_SetValue ( & sv_port_ipx , port ) ;
# endif
}
2004-08-23 00:15:46 +00:00
}
//============================================================================
2006-05-07 05:31:01 +00:00
void SV_Masterlist_Callback ( struct cvar_s * var , char * oldvalue )
{
int i ;
char data [ 2 ] ;
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
{
if ( var = = & sv_masterlist [ i ] . cv )
break ;
}
if ( ! sv_masterlist [ i ] . cv . name )
return ;
2006-05-25 21:32:32 +00:00
if ( ! * var - > string )
2006-05-07 05:31:01 +00:00
{
sv_masterlist [ i ] . adr . port = 0 ;
return ;
}
if ( ! NET_StringToAdr ( var - > string , & sv_masterlist [ i ] . adr ) )
{
sv_masterlist [ i ] . adr . port = 0 ;
Con_Printf ( " Couldn't resolve master \" %s \" \n " , var - > string ) ;
}
else
{
if ( sv_masterlist [ i ] . isdp )
{
if ( sv_masterlist [ i ] . adr . port = = 0 )
sv_masterlist [ i ] . adr . port = BigShort ( 27950 ) ;
}
else
{
if ( sv_masterlist [ i ] . adr . port = = 0 )
sv_masterlist [ i ] . adr . port = BigShort ( 27000 ) ;
data [ 0 ] = A2A_PING ;
data [ 1 ] = 0 ;
if ( sv . state )
NET_SendPacket ( NS_SERVER , 2 , data , sv_masterlist [ i ] . adr ) ;
}
}
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = =
Master_Heartbeat
Send a message to the master every few minutes to
let it know we are alive , and log information
= = = = = = = = = = = = = = = =
*/
# define HEARTBEAT_SECONDS 300
void Master_Heartbeat ( void )
{
char string [ 2048 ] ;
int active ;
2005-06-14 04:52:10 +00:00
int i , j ;
2005-05-26 12:55:34 +00:00
qboolean madeqwstring = false ;
if ( ! sv_public . value )
return ;
2004-08-23 00:15:46 +00:00
if ( realtime - svs . last_heartbeat < HEARTBEAT_SECONDS )
return ; // not time to send yet
svs . last_heartbeat = realtime ;
svs . heartbeat_sequence + + ;
// send to group master
2005-05-26 12:55:34 +00:00
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
{
if ( sv_masterlist [ i ] . adr . port )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
switch ( sv_masterlist [ i ] . isdp )
{
case false :
if ( ! madeqwstring )
{
// count active users
active = 0 ;
2005-06-14 04:52:10 +00:00
for ( j = 0 ; j < MAX_CLIENTS ; j + + )
if ( svs . clients [ j ] . state = = cs_connected | |
svs . clients [ j ] . state = = cs_spawned )
2005-05-26 12:55:34 +00:00
active + + ;
sprintf ( string , " %c \n %i \n %i \n " , S2M_HEARTBEAT ,
svs . heartbeat_sequence , active ) ;
madeqwstring = true ;
}
2005-06-09 16:29:32 +00:00
if ( sv_reportheartbeats . value )
Con_Printf ( " Sending heartbeat to %s \n " , NET_AdrToString ( sv_masterlist [ i ] . adr ) ) ;
2005-05-26 12:55:34 +00:00
NET_SendPacket ( NS_SERVER , strlen ( string ) , string , sv_masterlist [ i ] . adr ) ;
break ;
case true :
2007-06-10 05:14:38 +00:00
if ( sv_listen_dp . value ) //set listen to 1 to allow qw connections, 2 to allow nq connections too.
2005-05-26 12:55:34 +00:00
{
2005-06-09 16:29:32 +00:00
if ( sv_reportheartbeats . value )
Con_Printf ( " Sending heartbeat to %s \n " , NET_AdrToString ( sv_masterlist [ i ] . adr ) ) ;
2005-06-04 04:20:20 +00:00
{
char * str = " \377 \377 \377 \377 heartbeat DarkPlaces \x0A " ;
NET_SendPacket ( NS_SERVER , strlen ( str ) , str , sv_masterlist [ i ] . adr ) ;
}
2005-05-26 12:55:34 +00:00
}
break ;
}
2004-08-23 00:15:46 +00:00
}
2005-05-26 12:55:34 +00:00
}
}
void Master_Add ( char * stringadr )
{
int i ;
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
{
if ( ! * sv_masterlist [ i ] . cv . string )
break ;
}
if ( ! sv_masterlist [ i ] . cv . name )
{
Con_Printf ( " Too many masters \n " ) ;
return ;
}
Cvar_Set ( & sv_masterlist [ i ] . cv , stringadr ) ;
svs . last_heartbeat = - 99999 ;
}
void Master_ClearAll ( void )
{
int i ;
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
{
Cvar_Set ( & sv_masterlist [ i ] . cv , " " ) ;
}
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = =
Master_Shutdown
Informs all masters that this server is going down
= = = = = = = = = = = = = = = = =
*/
void Master_Shutdown ( void )
{
char string [ 2048 ] ;
int i ;
sprintf ( string , " %c \n " , S2M_SHUTDOWN ) ;
// send to group master
2005-05-26 12:55:34 +00:00
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
if ( sv_masterlist [ i ] . adr . port )
2004-08-23 00:15:46 +00:00
{
2005-05-26 12:55:34 +00:00
switch ( sv_masterlist [ i ] . isdp )
{
case false :
2005-06-09 16:29:32 +00:00
if ( sv_reportheartbeats . value )
Con_Printf ( " Sending heartbeat to %s \n " , NET_AdrToString ( sv_masterlist [ i ] . adr ) ) ;
2005-05-26 12:55:34 +00:00
NET_SendPacket ( NS_SERVER , strlen ( string ) , string , sv_masterlist [ i ] . adr ) ;
break ;
2007-08-07 19:16:32 +00:00
case true :
break ;
2005-05-26 12:55:34 +00:00
}
2004-08-23 00:15:46 +00:00
}
}
# define iswhite(c) (c == ' ' || c == INVIS_CHAR1 || c == INVIS_CHAR2 || c == INVIS_CHAR3)
# define isinvalid(c) (c == '\r' || c == '\n')
//is allowed to shorten, out must be as long as in and min of "unnamed"+1
void SV_FixupName ( char * in , char * out )
{
char * s , * p ;
s = out ;
while ( iswhite ( * in ) | | isinvalid ( * in ) )
in + + ;
while ( * in )
{
if ( isinvalid ( * in ) )
{
in + + ;
continue ;
}
* s + + = * in + + ;
}
* s = ' \0 ' ;
2007-06-20 00:02:54 +00:00
if ( ! * out )
{ //reached end and it was all whitespace
2004-08-23 00:15:46 +00:00
//white space only
strcpy ( out , " unnamed " ) ;
p = out ;
}
for ( p = out + strlen ( out ) - 1 ; p ! = out & & iswhite ( * p ) ; p - - )
;
p [ 1 ] = 0 ;
}
qboolean ReloadRanking ( client_t * cl , char * newname )
{
2005-11-03 23:40:51 +00:00
# ifdef SVRANKING
2004-08-23 00:15:46 +00:00
int newid ;
int j ;
rankstats_t rs ;
2005-03-12 23:40:42 +00:00
newid = Rank_GetPlayerID ( newname , atoi ( Info_ValueForKey ( cl - > userinfo , " _pwd " ) ) , true , false ) ; //'_' keys are always stripped. On any server. So try and use that so persistant data won't give out the password when connecting to a different server
2004-08-23 00:15:46 +00:00
if ( ! newid )
2005-03-12 23:40:42 +00:00
newid = Rank_GetPlayerID ( newname , atoi ( Info_ValueForKey ( cl - > userinfo , " password " ) ) , true , false ) ;
2004-08-23 00:15:46 +00:00
if ( newid )
{
if ( cl - > rankid & & cl - > state > = cs_spawned ) //apply current stats
{
if ( ! Rank_GetPlayerStats ( cl - > rankid , & rs ) )
return false ;
rs . timeonserver + = realtime - cl - > stats_started ;
cl - > stats_started = realtime ;
rs . kills + = cl - > kills ;
rs . deaths + = cl - > deaths ;
rs . flags1 & = ~ ( RANK_CUFFED | RANK_MUTED | RANK_CRIPPLED ) ;
if ( cl - > iscuffed = = 2 )
rs . flags1 | = RANK_CUFFED ;
if ( cl - > ismuted = = 2 )
rs . flags1 | = RANK_MUTED ;
if ( cl - > iscrippled = = 2 )
rs . flags1 | = RANK_CRIPPLED ;
cl - > kills = 0 ;
cl - > deaths = 0 ;
pr_global_struct - > self = EDICT_TO_PROG ( svprogfuncs , cl - > edict ) ;
if ( pr_nqglobal_struct - > SetChangeParms )
PR_ExecuteProgram ( svprogfuncs , pr_global_struct - > SetChangeParms ) ;
for ( j = 0 ; j < NUM_SPAWN_PARMS ; j + + )
2006-03-04 15:47:58 +00:00
if ( spawnparamglobals [ j ] )
rs . parm [ j ] = * spawnparamglobals [ j ] ;
2004-08-23 00:15:46 +00:00
Rank_SetPlayerStats ( cl - > rankid , & rs ) ;
}
if ( ! Rank_GetPlayerStats ( newid , & rs ) )
return false ;
cl - > rankid = newid ;
if ( rs . flags1 & RANK_CUFFED )
cl - > iscuffed = 2 ;
else if ( cl - > iscuffed ) //continue being cuffed, but don't inflict the new user with persistant cuffing.
cl - > iscuffed = 1 ;
if ( rs . flags1 & RANK_MUTED )
cl - > ismuted = 2 ;
else if ( cl - > ismuted )
cl - > ismuted = 1 ;
if ( rs . flags1 & RANK_CRIPPLED )
cl - > iscrippled = 2 ;
else if ( cl - > iscrippled )
cl - > iscrippled = 1 ;
cl - > trustlevel = rs . trustlevel ;
return true ;
}
2005-11-03 23:40:51 +00:00
# endif
2004-08-23 00:15:46 +00:00
return false ;
}
/*
= = = = = = = = = = = = = = = = =
SV_ExtractFromUserinfo
Pull specific info from a newly changed userinfo string
into a more C freindly form .
= = = = = = = = = = = = = = = = =
*/
void SV_ExtractFromUserinfo ( client_t * cl )
{
char * val , * p ;
int i ;
client_t * client ;
int dupc = 1 ;
char newname [ 80 ] ;
val = Info_ValueForKey ( cl - > userinfo , " team " ) ;
val [ 40 ] = 0 ; //trim to smallish length now (to allow for adding more.
2005-05-17 02:36:54 +00:00
Q_strncpyz ( cl - > team , val , sizeof ( cl - > teambuf ) ) ;
2004-08-23 00:15:46 +00:00
// name for C code
val = Info_ValueForKey ( cl - > userinfo , " name " ) ;
val [ 40 ] = 0 ; //trim to smallish length now (to allow for adding more.
2007-06-20 00:02:54 +00:00
if ( cl - > protocol ! = SCP_BAD | | * val )
SV_FixupName ( val , newname ) ;
else
newname [ 0 ] = 0 ;
2004-08-23 00:15:46 +00:00
2007-06-20 00:02:54 +00:00
if ( ! val [ 0 ] & & cl - > protocol ! = SCP_BAD )
2004-08-23 00:15:46 +00:00
strcpy ( newname , " Hidden " ) ;
else if ( ! stricmp ( val , " console " ) )
{
strcpy ( newname , " Not Console " ) ;
}
// check to see if another user by the same name exists
while ( 1 ) {
for ( i = 0 , client = svs . clients ; i < MAX_CLIENTS ; i + + , client + + ) {
2006-01-04 00:46:51 +00:00
if ( client - > state < cs_connected | | client = = cl )
2004-08-23 00:15:46 +00:00
continue ;
if ( ! stricmp ( client - > name , newname ) )
break ;
}
if ( i ! = MAX_CLIENTS ) { // dup name
2005-05-17 02:36:54 +00:00
if ( strlen ( newname ) > sizeof ( cl - > namebuf ) - 1 )
newname [ sizeof ( cl - > namebuf ) - 4 ] = 0 ;
2004-08-23 00:15:46 +00:00
p = newname ;
if ( newname [ 0 ] = = ' ( ' )
{
if ( newname [ 2 ] = = ' ) ' )
p = newname + 3 ;
else if ( val [ 3 ] = = ' ) ' )
p = newname + 4 ;
}
memmove ( newname + 10 , p , strlen ( p ) + 1 ) ;
sprintf ( newname , " (%d)%-.40s " , dupc + + , newname + 10 ) ;
} else
break ;
}
2005-07-03 15:16:20 +00:00
2005-05-17 02:36:54 +00:00
if ( strncmp ( newname , cl - > name , sizeof ( cl - > namebuf ) - 1 ) )
2005-07-03 15:16:20 +00:00
{
2006-10-05 22:10:09 +00:00
if ( cl - > ismuted & & * cl - > name )
2004-08-23 00:15:46 +00:00
SV_ClientTPrintf ( cl , PRINT_HIGH , STL_NONAMEASMUTE ) ;
else
{
2005-03-07 08:58:26 +00:00
Info_SetValueForKey ( cl - > userinfo , " name " , newname , sizeof ( cl - > userinfo ) ) ;
2006-10-05 22:10:09 +00:00
if ( ! sv . paused & & * cl - > name )
{
if ( ! cl - > lastnametime | | realtime - cl - > lastnametime > 5 )
{
2004-08-23 00:15:46 +00:00
cl - > lastnamecount = 0 ;
cl - > lastnametime = realtime ;
2006-10-05 22:10:09 +00:00
}
else if ( cl - > lastnamecount + + > 4 )
{
2004-08-23 00:15:46 +00:00
SV_BroadcastTPrintf ( PRINT_HIGH , STL_CLIENTKICKEDNAMESPAM , cl - > name ) ;
SV_ClientTPrintf ( cl , PRINT_HIGH , STL_YOUWEREKICKEDNAMESPAM ) ;
2005-07-03 15:16:20 +00:00
SV_DropClient ( cl ) ;
2004-08-23 00:15:46 +00:00
return ;
}
}
2006-10-05 22:10:09 +00:00
if ( * cl - > name & & cl - > state > = cs_spawned & & ! cl - > spectator )
2005-07-03 15:16:20 +00:00
{
2004-08-23 00:15:46 +00:00
SV_BroadcastTPrintf ( PRINT_HIGH , STL_CLIENTNAMECHANGE , cl - > name , val ) ;
}
2005-05-17 02:36:54 +00:00
Q_strncpyz ( cl - > name , newname , sizeof ( cl - > namebuf ) ) ;
2004-08-23 00:15:46 +00:00
# ifdef SVRANKING
if ( ReloadRanking ( cl , newname ) )
{
# endif
# ifdef SVRANKING
}
else if ( cl - > state > = cs_spawned )
SV_ClientPrintf ( cl , PRINT_HIGH , " Your rankings name has not been changed \n " ) ;
# endif
}
}
2005-03-07 08:58:26 +00:00
Info_SetValueForKey ( cl - > userinfo , " name " , newname , sizeof ( cl - > userinfo ) ) ;
2004-08-23 00:15:46 +00:00
val = Info_ValueForKey ( cl - > userinfo , " lang " ) ;
cl - > language = atoi ( val ) ;
if ( ! cl - > language )
cl - > language = LANGDEFAULT ;
val = Info_ValueForKey ( cl - > userinfo , " nogib " ) ;
if ( atoi ( val ) )
cl - > gibfilter = true ;
else
cl - > gibfilter = false ;
// rate command
val = Info_ValueForKey ( cl - > userinfo , " rate " ) ;
if ( strlen ( val ) )
2004-10-19 16:10:14 +00:00
cl - > rate = atoi ( val ) ;
else
cl - > rate = 2500 ;
val = Info_ValueForKey ( cl - > userinfo , " drate " ) ;
if ( strlen ( val ) )
cl - > drate = atoi ( val ) ;
else
2005-03-20 02:57:11 +00:00
cl - > drate = 0 ; //0 disables the downloading check
2004-08-23 00:15:46 +00:00
2006-03-23 19:22:12 +00:00
val = Info_ValueForKey ( cl - > userinfo , " cl_playerclass " ) ;
if ( val )
{
PR_SetPlayerClass ( cl , atoi ( val ) , false ) ;
}
2004-08-23 00:15:46 +00:00
// msg command
val = Info_ValueForKey ( cl - > userinfo , " msg " ) ;
if ( strlen ( val ) )
{
cl - > messagelevel = atoi ( val ) ;
}
# ifdef NQPROT
{
2005-09-08 22:52:46 +00:00
int top = atoi ( Info_ValueForKey ( cl - > userinfo , " topcolor " ) ) ;
int bottom = atoi ( Info_ValueForKey ( cl - > userinfo , " bottomcolor " ) ) ;
top & = 15 ;
if ( top > 13 )
top = 13 ;
bottom & = 15 ;
if ( bottom > 13 )
bottom = 13 ;
cl - > playercolor = top * 16 + bottom ;
if ( svs . gametype = = GT_PROGS )
{
cl - > edict - > v - > clientcolors = cl - > playercolor ;
MSG_WriteByte ( & sv . nqreliable_datagram , svc_updatecolors ) ;
MSG_WriteByte ( & sv . nqreliable_datagram , cl - svs . clients ) ;
MSG_WriteByte ( & sv . nqreliable_datagram , cl - > playercolor ) ;
}
2004-08-23 00:15:46 +00:00
}
# endif
}
//============================================================================
/*
= = = = = = = = = = = = = = = = = = = =
SV_InitNet
= = = = = = = = = = = = = = = = = = = =
*/
void SV_InitNet ( void )
{
2005-05-26 12:55:34 +00:00
int i ;
2004-08-23 00:15:46 +00:00
# ifndef SERVERONLY
if ( isDedicated )
# endif
{
NET_Init ( ) ;
Netchan_Init ( ) ;
}
2005-05-26 12:55:34 +00:00
for ( i = 0 ; sv_masterlist [ i ] . cv . name ; i + + )
Cvar_Register ( & sv_masterlist [ i ] . cv , " master servers " ) ;
2005-07-28 15:33:27 +00:00
// heartbeats will always be sent to the id master
2004-08-23 00:15:46 +00:00
svs . last_heartbeat = - 99999 ; // send immediately
// NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr);
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_Init
= = = = = = = = = = = = = = = = = = = =
*/
//FIXME: move to header
void SV_Demo_Init ( void ) ;
void SV_Init ( quakeparms_t * parms )
{
# ifndef SERVERONLY
if ( isDedicated )
# endif
{
COM_InitArgv ( parms - > argc , parms - > argv ) ;
if ( COM_CheckParm ( " -minmemory " ) )
parms - > memsize = MINIMUM_MEMORY ;
host_parms = * parms ;
// if (parms->memsize < MINIMUM_MEMORY)
// SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
2005-07-03 15:16:20 +00:00
Memory_Init ( parms - > membase , parms - > memsize ) ;
2004-08-23 00:15:46 +00:00
2007-06-20 00:02:54 +00:00
Sys_Init ( ) ;
2005-01-16 02:23:31 +00:00
COM_ParsePlusSets ( ) ;
2004-08-23 00:15:46 +00:00
Cbuf_Init ( ) ;
Cmd_Init ( ) ;
# ifndef SERVERONLY
R_SetRenderer ( QR_NONE ) ;
# endif
COM_Init ( ) ;
Mod_Init ( ) ;
}
2006-05-09 00:02:05 +00:00
2005-07-03 15:16:20 +00:00
PR_Init ( ) ;
2004-08-23 00:15:46 +00:00
SV_InitNet ( ) ;
SV_InitLocal ( ) ;
# ifdef IWEB_H__
IWebInit ( ) ;
# endif
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
SV_Demo_Init ( ) ;
# ifdef SVRANKING
Rank_RegisterCommands ( ) ;
# endif
Cbuf_AddText ( " alias restart \" map . \" \n alias newgame \" map start \" \n " , RESTRICT_LOCAL ) ;
# ifndef SERVERONLY
if ( isDedicated )
# endif
{
PM_Init ( ) ;
2005-12-15 19:15:39 +00:00
# ifdef PLUGINS
Plug_Init ( ) ;
# endif
2004-08-23 00:15:46 +00:00
Hunk_AllocName ( 0 , " -HOST_HUNKLEVEL- " ) ;
host_hunklevel = Hunk_LowMark ( ) ;
host_initialized = true ;
2005-07-03 15:16:20 +00:00
2005-12-15 19:15:39 +00:00
2004-08-23 00:15:46 +00:00
Con_TPrintf ( TL_EXEDATETIME , __DATE__ , __TIME__ ) ;
2005-07-03 15:16:20 +00:00
Con_TPrintf ( TL_HEAPSIZE , parms - > memsize / ( 1024 * 1024.0 ) ) ;
2004-08-23 00:15:46 +00:00
2005-10-04 21:08:06 +00:00
Con_TPrintf ( TL_VERSION , DISTRIBUTION , build_number ( ) ) ;
2004-08-23 00:15:46 +00:00
Con_TPrintf ( STL_INITED ) ;
2006-02-06 01:06:17 +00:00
Cbuf_InsertText ( " exec server.cfg \n exec ftesrv.cfg \n " , RESTRICT_LOCAL , false ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
// process command line arguments
Cbuf_Execute ( ) ;
2005-01-16 02:23:31 +00:00
Cmd_StuffCmds ( ) ;
2005-03-07 08:58:26 +00:00
Cbuf_Execute ( ) ;
2004-08-23 00:15:46 +00:00
// if a map wasn't specified on the command line, spawn start.map
if ( sv . state = = ss_dead )
Cmd_ExecuteString ( " map start " , RESTRICT_LOCAL ) ;
2005-07-01 19:23:00 +00:00
if ( sv . state = = ss_dead )
{
cvar_t * ml ;
ml = Cvar_Get ( " g_maplist " , " dm1 dm2 dm3 dm4 dm5 dm6 " , 0 , " " ) ;
Cmd_TokenizeString ( ml - > string , false , false ) ;
if ( Cmd_Argc ( ) )
Cmd_ExecuteString ( va ( " map %s " , Cmd_Argv ( rand ( ) % Cmd_Argc ( ) ) ) , RESTRICT_LOCAL ) ;
}
2004-08-23 00:15:46 +00:00
if ( sv . state = = ss_dead )
SV_Error ( " Couldn't spawn a server " ) ;
}
}
2004-11-29 01:21:00 +00:00
# endif