2011-05-09 22:00:00 +02:00
/*
sv_main . c - server main loop
Copyright ( C ) 2007 Uncle Mike
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 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
*/
2007-06-21 22:00:00 +02:00
2008-06-09 22:00:00 +02:00
# include "common.h"
2007-06-21 22:00:00 +02:00
# include "server.h"
2010-08-06 22:00:00 +02:00
# include "net_encode.h"
2007-06-21 22:00:00 +02:00
2011-04-05 22:00:00 +02:00
# define HEARTBEAT_SECONDS 300.0f // 300 seconds
2009-09-17 22:00:00 +02:00
2010-10-22 22:00:00 +02:00
convar_t * sv_zmax ;
2010-12-09 22:00:00 +01:00
convar_t * sv_novis ; // disable server culling entities by vis
2010-10-22 22:00:00 +02:00
convar_t * sv_unlag ;
convar_t * sv_maxunlag ;
convar_t * sv_unlagpush ;
convar_t * sv_unlagsamples ;
convar_t * sv_pausable ;
convar_t * sv_newunit ;
convar_t * sv_wateramp ;
convar_t * timeout ; // seconds without any message
convar_t * zombietime ; // seconds to sink messages after disconnect
convar_t * rcon_password ; // password for remote server commands
convar_t * sv_airaccelerate ;
convar_t * sv_wateraccelerate ;
convar_t * sv_maxvelocity ;
convar_t * sv_gravity ;
2011-01-10 22:00:00 +01:00
convar_t * sv_stepsize ;
2010-10-22 22:00:00 +02:00
convar_t * sv_rollangle ;
convar_t * sv_rollspeed ;
convar_t * sv_wallbounce ;
convar_t * sv_maxspeed ;
convar_t * sv_spectatormaxspeed ;
convar_t * sv_accelerate ;
convar_t * sv_friction ;
convar_t * sv_edgefriction ;
convar_t * sv_waterfriction ;
convar_t * sv_stopspeed ;
convar_t * hostname ;
2011-04-15 22:00:00 +02:00
convar_t * sv_fix_pushents ;
2010-10-22 22:00:00 +02:00
convar_t * sv_lighting_modulate ;
convar_t * sv_maxclients ;
convar_t * sv_check_errors ;
convar_t * sv_footsteps ;
2011-04-05 22:00:00 +02:00
convar_t * public_server ; // should heartbeats be sent
convar_t * sv_reconnect_limit ; // minimum seconds between connect messages
2010-10-22 22:00:00 +02:00
convar_t * sv_failuretime ;
2010-10-28 22:00:00 +02:00
convar_t * sv_allow_upload ;
convar_t * sv_allow_download ;
2011-02-05 22:00:00 +01:00
convar_t * sv_allow_studio_scaling ;
convar_t * sv_allow_studio_attachment_angles ;
2011-04-05 22:00:00 +02:00
convar_t * sv_allow_rotate_pushables ;
convar_t * sv_clienttrace ;
2010-10-28 22:00:00 +02:00
convar_t * sv_send_resources ;
convar_t * sv_send_logos ;
2010-12-27 22:00:00 +01:00
convar_t * sv_sendvelocity ;
2011-08-14 22:00:00 +02:00
convar_t * sv_airmove ;
2010-10-28 22:00:00 +02:00
convar_t * mp_consistency ;
2010-10-22 22:00:00 +02:00
convar_t * serverinfo ;
convar_t * physinfo ;
convar_t * clockwindow ;
2007-06-21 22:00:00 +02:00
2010-06-16 22:00:00 +02:00
// sky variables
2010-10-22 22:00:00 +02:00
convar_t * sv_skycolor_r ;
convar_t * sv_skycolor_g ;
convar_t * sv_skycolor_b ;
convar_t * sv_skyvec_x ;
convar_t * sv_skyvec_y ;
convar_t * sv_skyvec_z ;
convar_t * sv_skyname ;
2010-06-16 22:00:00 +02:00
void Master_Shutdown ( void ) ;
2007-06-21 22:00:00 +02:00
//============================================================================
/*
= = = = = = = = = = = = = = = = = = =
SV_CalcPings
Updates the cl - > ping variables
= = = = = = = = = = = = = = = = = = =
*/
2008-08-01 22:00:00 +02:00
void SV_CalcPings ( void )
2007-06-21 22:00:00 +02:00
{
2008-07-31 22:00:00 +02:00
int i , j ;
2008-07-09 22:00:00 +02:00
sv_client_t * cl ;
2008-07-31 22:00:00 +02:00
int total , count ;
2007-06-21 22:00:00 +02:00
2011-04-08 22:00:00 +02:00
if ( ! svs . clients ) return ;
2009-09-16 22:00:00 +02:00
// clamp fps counter
2009-09-25 22:00:00 +02:00
for ( i = 0 ; i < sv_maxclients - > integer ; i + + )
2007-06-21 22:00:00 +02:00
{
cl = & svs . clients [ i ] ;
2009-09-16 22:00:00 +02:00
if ( cl - > state ! = cs_spawned ) continue ;
2010-10-23 22:00:00 +02:00
if ( cl - > fakeclient ) continue ;
2009-09-15 22:00:00 +02:00
2009-09-16 22:00:00 +02:00
total = count = 0 ;
2009-11-25 22:00:00 +01:00
2010-04-02 22:00:00 +02:00
for ( j = 0 ; j < ( SV_UPDATE_BACKUP / 2 ) ; j + + )
2009-09-15 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
client_frame_t * frame ;
2010-10-23 22:00:00 +02:00
frame = & cl - > frames [ ( cl - > netchan . incoming_acknowledged - ( j + 1 ) ) & SV_UPDATE_MASK ] ;
2009-11-25 22:00:00 +01:00
if ( frame - > latency > 0 )
2009-09-16 22:00:00 +02:00
{
count + + ;
2009-11-25 22:00:00 +01:00
total + = frame - > latency ;
2009-09-16 22:00:00 +02:00
}
2009-09-15 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
2009-09-16 22:00:00 +02:00
if ( ! count ) cl - > ping = 0 ;
else cl - > ping = total / count ;
2009-11-25 22:00:00 +01:00
}
}
/*
= = = = = = = = = = = = = = = = = = =
SV_CalcPacketLoss
determine % of packets that were not ack ' d .
= = = = = = = = = = = = = = = = = = =
*/
int SV_CalcPacketLoss ( sv_client_t * cl )
{
int i , lost , count ;
float losspercent ;
register client_frame_t * frame ;
int numsamples ;
lost = 0 ;
count = 0 ;
2010-10-09 22:00:00 +02:00
if ( cl - > fakeclient )
2009-11-25 22:00:00 +01:00
return 0 ;
2007-06-21 22:00:00 +02:00
2010-04-02 22:00:00 +02:00
numsamples = SV_UPDATE_BACKUP / 2 ;
2009-11-25 22:00:00 +01:00
for ( i = 0 ; i < numsamples ; i + + )
{
2010-04-02 22:00:00 +02:00
frame = & cl - > frames [ ( cl - > netchan . incoming_acknowledged - 1 - i ) & SV_UPDATE_MASK ] ;
2009-11-25 22:00:00 +01:00
count + + ;
if ( frame - > latency = = - 1 )
lost + + ;
2007-06-21 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
if ( ! count ) return 100 ;
losspercent = 100.0 * ( float ) lost / ( float ) count ;
return ( int ) losspercent ;
2007-06-21 22:00:00 +02:00
}
2010-08-06 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = =
SV_HasActivePlayers
returns true if server have spawned players
= = = = = = = = = = = = = = = =
*/
2010-10-26 22:00:00 +02:00
qboolean SV_HasActivePlayers ( void )
2010-08-06 22:00:00 +02:00
{
int i ;
// server inactive
if ( ! svs . clients ) return false ;
for ( i = 0 ; i < sv_maxclients - > integer ; i + + )
{
if ( svs . clients [ i ] . state = = cs_spawned )
return true ;
}
return false ;
}
2009-11-10 22:00:00 +01:00
/*
= = = = = = = = = = = = = = = = = = =
SV_UpdateMovevars
check movevars for changes every frame
send updates to client if changed
= = = = = = = = = = = = = = = = = = =
*/
void SV_UpdateMovevars ( void )
{
2010-02-07 22:00:00 +01:00
if ( ! physinfo - > modified ) return ;
2010-12-14 22:00:00 +01:00
// check range
if ( sv_zmax - > value < 256.0f ) Cvar_SetFloat ( " sv_zmax " , 256.0f ) ;
2010-12-16 22:00:00 +01:00
if ( sv_zmax - > value > 32767.0f ) Cvar_SetFloat ( " sv_zmax " , 32767.0f ) ;
2010-12-14 22:00:00 +01:00
2009-11-10 22:00:00 +01:00
svgame . movevars . gravity = sv_gravity - > value ;
svgame . movevars . stopspeed = sv_stopspeed - > value ;
svgame . movevars . maxspeed = sv_maxspeed - > value ;
svgame . movevars . spectatormaxspeed = sv_spectatormaxspeed - > value ;
svgame . movevars . accelerate = sv_accelerate - > value ;
svgame . movevars . airaccelerate = sv_airaccelerate - > value ;
svgame . movevars . wateraccelerate = sv_wateraccelerate - > value ;
svgame . movevars . friction = sv_friction - > value ;
svgame . movevars . edgefriction = sv_edgefriction - > value ;
svgame . movevars . waterfriction = sv_waterfriction - > value ;
svgame . movevars . bounce = sv_wallbounce - > value ;
2011-01-10 22:00:00 +01:00
svgame . movevars . stepsize = sv_stepsize - > value ;
2009-11-10 22:00:00 +01:00
svgame . movevars . maxvelocity = sv_maxvelocity - > value ;
2010-08-15 22:00:00 +02:00
svgame . movevars . zmax = sv_zmax - > value ;
svgame . movevars . waveHeight = sv_wateramp - > value ;
2011-03-09 22:00:00 +01:00
Q_strncpy ( svgame . movevars . skyName , sv_skyname - > string , sizeof ( svgame . movevars . skyName ) ) ;
2010-08-15 22:00:00 +02:00
svgame . movevars . footsteps = sv_footsteps - > integer ;
2009-11-10 22:00:00 +01:00
svgame . movevars . rollangle = sv_rollangle - > value ;
svgame . movevars . rollspeed = sv_rollspeed - > value ;
2010-08-15 22:00:00 +02:00
svgame . movevars . skycolor_r = sv_skycolor_r - > value ;
svgame . movevars . skycolor_g = sv_skycolor_g - > value ;
svgame . movevars . skycolor_b = sv_skycolor_b - > value ;
svgame . movevars . skyvec_x = sv_skyvec_x - > value ;
svgame . movevars . skyvec_y = sv_skyvec_y - > value ;
svgame . movevars . skyvec_z = sv_skyvec_z - > value ;
2011-02-15 22:00:00 +01:00
svgame . movevars . studio_scale = sv_allow_studio_scaling - > integer ;
2011-04-08 22:00:00 +02:00
svgame . movevars . clienttrace = sv_clienttrace - > value ;
2009-11-10 22:00:00 +01:00
2010-10-09 22:00:00 +02:00
if ( MSG_WriteDeltaMovevars ( & sv . reliable_datagram , & svgame . oldmovevars , & svgame . movevars ) )
2011-03-10 22:00:00 +01:00
Q_memcpy ( & svgame . oldmovevars , & svgame . movevars , sizeof ( movevars_t ) ) ; // oldstate changed
2010-10-09 22:00:00 +02:00
2010-02-07 22:00:00 +01:00
physinfo - > modified = false ;
2009-11-10 22:00:00 +01:00
}
2010-03-30 22:00:00 +02:00
void pfnUpdateServerInfo ( const char * szKey , const char * szValue , const char * unused , void * unused2 )
{
2010-10-22 22:00:00 +02:00
convar_t * cv = Cvar_FindVar ( szKey ) ;
2010-03-30 22:00:00 +02:00
if ( ! cv | | ! cv - > modified ) return ; // this cvar not changed
2010-10-09 22:00:00 +02:00
BF_WriteByte ( & sv . reliable_datagram , svc_serverinfo ) ;
BF_WriteString ( & sv . reliable_datagram , szKey ) ;
BF_WriteString ( & sv . reliable_datagram , szValue ) ;
2010-03-30 22:00:00 +02:00
cv - > modified = false ; // reset state
}
void SV_UpdateServerInfo ( void )
{
if ( ! serverinfo - > modified ) return ;
Cvar_LookupVars ( CVAR_SERVERINFO , NULL , NULL , pfnUpdateServerInfo ) ;
serverinfo - > modified = false ;
}
2010-10-14 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = =
SV_CheckCmdTimes
= = = = = = = = = = = = = = = = =
*/
void SV_CheckCmdTimes ( void )
{
sv_client_t * cl ;
static double lastreset = 0 ;
double timewindow ;
int i ;
2011-04-05 22:00:00 +02:00
if ( ( host . realtime - lastreset ) < 1.0 )
2010-10-14 22:00:00 +02:00
return ;
lastreset = host . realtime ;
for ( i = 0 , cl = svs . clients ; i < sv_maxclients - > integer ; i + + , cl + + )
{
if ( cl - > state ! = cs_spawned )
continue ;
if ( cl - > last_cmdtime = = 0.0 )
{
cl - > last_cmdtime = host . realtime ;
}
timewindow = cl - > last_movetime + cl - > last_cmdtime - host . realtime ;
if ( timewindow > clockwindow - > value )
{
cl - > next_movetime = clockwindow - > value + host . realtime ;
cl - > last_movetime = host . realtime - cl - > last_cmdtime ;
}
else if ( timewindow < - clockwindow - > value )
{
cl - > last_movetime = host . realtime - cl - > last_cmdtime ;
}
}
}
2009-06-24 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = =
SV_ReadPackets
= = = = = = = = = = = = = = = = =
*/
void SV_ReadPackets ( void )
{
sv_client_t * cl ;
2010-08-04 22:00:00 +02:00
int i , qport , curSize ;
2007-06-21 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
while ( NET_GetPacket ( NS_SERVER , & net_from , net_message_buffer , & curSize ) )
2007-06-21 22:00:00 +02:00
{
2010-08-04 22:00:00 +02:00
BF_Init ( & net_message , " ClientPacket " , net_message_buffer , curSize ) ;
2009-06-24 22:00:00 +02:00
// check for connectionless packet (0xffffffff) first
2010-08-04 22:00:00 +02:00
if ( BF_GetMaxBytes ( & net_message ) > = 4 & & * ( int * ) net_message . pData = = - 1 )
2007-06-21 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
SV_ConnectionlessPacket ( net_from , & net_message ) ;
continue ;
2007-06-21 22:00:00 +02:00
}
2009-06-24 22:00:00 +02:00
// read the qport out of the message so we can fix up
// stupid address translating routers
2010-08-04 22:00:00 +02:00
BF_Clear ( & net_message ) ;
BF_ReadLong ( & net_message ) ; // sequence number
BF_ReadLong ( & net_message ) ; // sequence number
qport = ( int ) BF_ReadShort ( & net_message ) & 0xffff ;
2009-06-24 22:00:00 +02:00
// check for packets from connected clients
2009-09-25 22:00:00 +02:00
for ( i = 0 , cl = svs . clients ; i < sv_maxclients - > integer ; i + + , cl + + )
2009-06-24 22:00:00 +02:00
{
2011-04-05 22:00:00 +02:00
if ( cl - > state = = cs_free | | cl - > fakeclient )
continue ;
if ( ! NET_CompareBaseAdr ( net_from , cl - > netchan . remote_address ) )
continue ;
if ( cl - > netchan . qport ! = qport )
continue ;
2009-06-24 22:00:00 +02:00
if ( cl - > netchan . remote_address . port ! = net_from . port )
2007-06-21 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
MsgDev ( D_INFO , " SV_ReadPackets: fixing up a translated port \n " ) ;
cl - > netchan . remote_address . port = net_from . port ;
2007-06-21 22:00:00 +02:00
}
2010-04-03 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
if ( Netchan_Process ( & cl - > netchan , & net_message ) )
{
2010-04-03 22:00:00 +02:00
cl - > send_message = true ; // reply at end of frame
2009-06-24 22:00:00 +02:00
// this is a valid, sequenced packet, so process it
if ( cl - > state ! = cs_zombie )
{
2010-10-09 22:00:00 +02:00
cl - > lastmessage = host . realtime ; // don't timeout
2009-06-24 22:00:00 +02:00
SV_ExecuteClientMessage ( cl , & net_message ) ;
}
}
2010-10-07 22:00:00 +02:00
2011-04-05 22:00:00 +02:00
// fragmentation/reassembly sending takes priority over all game messages, want this in the future?
2010-10-07 22:00:00 +02:00
if ( Netchan_IncomingReady ( & cl - > netchan ) )
{
if ( Netchan_CopyNormalFragments ( & cl - > netchan , & net_message ) )
{
BF_Clear ( & net_message ) ;
SV_ExecuteClientMessage ( cl , & net_message ) ;
}
if ( Netchan_CopyFileFragments ( & cl - > netchan , & net_message ) )
{
2011-07-07 22:00:00 +02:00
SV_ProcessFile ( cl , cl - > netchan . incomingfilename ) ;
2010-10-07 22:00:00 +02:00
}
}
2009-06-24 22:00:00 +02:00
break ;
2007-06-21 22:00:00 +02:00
}
2010-06-20 22:00:00 +02:00
if ( i ! = sv_maxclients - > integer )
continue ;
2009-06-22 22:00:00 +02:00
}
}
2007-06-21 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = = =
SV_CheckTimeouts
If a packet has not been received from a client for timeout - > value
seconds , drop the conneciton . Server frames are used instead of
realtime to avoid dropping the local client while debugging .
2008-07-09 22:00:00 +02:00
When a client is normally dropped , the sv_client_t goes into a zombie state
2007-06-21 22:00:00 +02:00
for a few seconds to make sure any final reliable message gets resent
if necessary
= = = = = = = = = = = = = = = = = =
*/
2008-07-12 22:00:00 +02:00
void SV_CheckTimeouts ( void )
2007-06-21 22:00:00 +02:00
{
2008-07-09 22:00:00 +02:00
sv_client_t * cl ;
2010-10-09 22:00:00 +02:00
float droppoint ;
float zombiepoint ;
2009-11-10 22:00:00 +01:00
int i , numclients = 0 ;
2007-06-21 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
droppoint = host . realtime - timeout - > value ;
zombiepoint = host . realtime - zombietime - > value ;
2008-07-31 22:00:00 +02:00
2009-09-25 22:00:00 +02:00
for ( i = 0 , cl = svs . clients ; i < sv_maxclients - > integer ; i + + , cl + + )
2007-06-21 22:00:00 +02:00
{
2009-11-10 22:00:00 +01:00
if ( cl - > state > = cs_connected )
{
if ( cl - > edict & & ! ( cl - > edict - > v . flags & ( FL_SPECTATOR | FL_FAKECLIENT ) ) )
numclients + + ;
}
2009-06-24 22:00:00 +02:00
// fake clients do not timeout
2010-10-09 22:00:00 +02:00
if ( cl - > fakeclient ) cl - > lastmessage = host . realtime ;
2010-06-20 22:00:00 +02:00
2007-06-21 22:00:00 +02:00
// message times may be wrong across a changelevel
2010-10-09 22:00:00 +02:00
if ( cl - > lastmessage > host . realtime )
cl - > lastmessage = host . realtime ;
2010-06-20 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
if ( cl - > state = = cs_zombie & & cl - > lastmessage < zombiepoint )
2007-06-21 22:00:00 +02:00
{
2008-07-12 22:00:00 +02:00
cl - > state = cs_free ; // can now be reused
2007-06-21 22:00:00 +02:00
continue ;
}
2010-06-17 22:00:00 +02:00
if ( ( cl - > state = = cs_connected | | cl - > state = = cs_spawned ) & & cl - > lastmessage < droppoint )
2007-06-21 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
SV_BroadcastPrintf ( PRINT_HIGH , " %s timed out \n " , cl - > name ) ;
2008-07-12 22:00:00 +02:00
SV_DropClient ( cl ) ;
cl - > state = cs_free ; // don't bother with zombie state
2007-06-21 22:00:00 +02:00
}
}
2009-11-10 22:00:00 +01:00
if ( sv . paused & & ! numclients )
{
// nobody left, unpause the server
2011-04-05 22:00:00 +02:00
SV_TogglePause ( " Pause released since no players are left. " ) ;
2009-11-10 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
}
2009-01-14 22:00:00 +01:00
/*
= = = = = = = = = = = = = = = =
SV_PrepWorldFrame
This has to be done before the world logic , because
player processing happens outside RunWorldFrame
= = = = = = = = = = = = = = = =
*/
void SV_PrepWorldFrame ( void )
{
edict_t * ent ;
int i ;
2010-08-15 22:00:00 +02:00
for ( i = 1 ; i < svgame . numEntities ; i + + )
2009-01-14 22:00:00 +01:00
{
ent = EDICT_NUM ( i ) ;
if ( ent - > free ) continue ;
2010-04-03 22:00:00 +02:00
2010-03-15 22:00:00 +01:00
ent - > v . effects & = ~ EF_MUZZLEFLASH ;
2010-04-03 22:00:00 +02:00
// clear NOINTERP flag automatically only for alive creatures
2010-06-16 22:00:00 +02:00
if ( ent - > v . flags & ( FL_MONSTER | FL_CLIENT | FL_FAKECLIENT ) & & ent - > v . deadflag < DEAD_DEAD )
2010-04-03 22:00:00 +02:00
ent - > v . effects & = ~ EF_NOINTERP ;
2009-01-14 22:00:00 +01:00
}
}
2011-01-09 22:00:00 +01:00
2011-07-07 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = =
SV_ProcessFile
= = = = = = = = = = = = = = = = =
*/
void SV_ProcessFile ( sv_client_t * cl , char * filename )
{
// some other file...
MsgDev ( D_INFO , " Received file %s from %s \n " , filename , cl - > name ) ;
}
2011-01-09 22:00:00 +01:00
/*
= = = = = = = = = = = = = = = = =
SV_IsSimulating
= = = = = = = = = = = = = = = = =
*/
qboolean SV_IsSimulating ( void )
{
2011-04-05 22:00:00 +02:00
if ( sv . background & & SV_Active ( ) & & CL_Active ( ) )
2011-02-22 22:00:00 +01:00
{
if ( CL_IsInConsole ( ) )
return false ;
return true ; // force simulating for background map
}
2011-04-05 22:00:00 +02:00
2011-01-09 22:00:00 +01:00
if ( sv . hostflags & SVF_PLAYERSONLY )
return false ;
if ( ! SV_HasActivePlayers ( ) )
return false ;
if ( ! sv . paused & & CL_IsInGame ( ) )
return true ;
return false ;
}
2007-09-06 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = =
SV_RunGameFrame
= = = = = = = = = = = = = = = = =
*/
2009-02-02 22:00:00 +01:00
void SV_RunGameFrame ( void )
2007-09-06 22:00:00 +02:00
{
2011-01-09 22:00:00 +01:00
if ( ! SV_IsSimulating ( ) ) return ;
SV_Physics ( ) ;
sv . time + = host . frametime ;
2007-06-21 22:00:00 +02:00
}
/*
= = = = = = = = = = = = = = = = = =
2010-10-09 22:00:00 +02:00
Host_ServerFrame
2007-06-21 22:00:00 +02:00
= = = = = = = = = = = = = = = = = =
*/
2010-10-09 22:00:00 +02:00
void Host_ServerFrame ( void )
2007-06-21 22:00:00 +02:00
{
// if server is not active, do nothing
2008-07-03 22:00:00 +02:00
if ( ! svs . initialized ) return ;
2009-02-03 22:00:00 +01:00
2011-01-09 22:00:00 +01:00
svgame . globals - > frametime = host . frametime ;
2007-06-21 22:00:00 +02:00
// check timeouts
SV_CheckTimeouts ( ) ;
2007-09-06 22:00:00 +02:00
2010-10-14 22:00:00 +02:00
// check clients timewindow
SV_CheckCmdTimes ( ) ;
2009-06-22 22:00:00 +02:00
// read packets from clients
SV_ReadPackets ( ) ;
2007-06-21 22:00:00 +02:00
// update ping based on the last known frame from all clients
SV_CalcPings ( ) ;
2010-03-30 22:00:00 +02:00
// refresh serverinfo on the client side
SV_UpdateServerInfo ( ) ;
2009-11-10 22:00:00 +01:00
// refresh physic movevars on the client side
SV_UpdateMovevars ( ) ;
2010-07-26 22:00:00 +02:00
// let everything in the world think and move
SV_RunGameFrame ( ) ;
2011-01-09 22:00:00 +01:00
2007-06-21 22:00:00 +02:00
// send messages back to the clients that had packets read this frame
SV_SendClientMessages ( ) ;
2009-01-14 22:00:00 +01:00
// clear edict flags for next frame
SV_PrepWorldFrame ( ) ;
2010-04-12 22:00:00 +02:00
// send a heartbeat to the master if needed
Master_Heartbeat ( ) ;
2007-06-21 22:00:00 +02:00
}
//============================================================================
2011-07-07 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = = =
Master_Add
= = = = = = = = = = = = = = = = =
*/
void Master_Add ( void )
{
netadr_t adr ;
NET_Config ( true ) ; // allow remote
if ( ! NET_StringToAdr ( MASTERSERVER_ADR , & adr ) )
MsgDev ( D_INFO , " Can't resolve adr: %s \n " , MASTERSERVER_ADR ) ;
NET_SendPacket ( NS_SERVER , 2 , " \x4D \xFF " , adr ) ;
}
2007-06-21 22:00:00 +02:00
/*
= = = = = = = = = = = = = = = =
Master_Heartbeat
Send a message to the master every few minutes to
let it know we are alive , and log information
= = = = = = = = = = = = = = = =
*/
2008-12-20 22:00:00 +01:00
void Master_Heartbeat ( void )
2007-06-21 22:00:00 +02:00
{
2011-07-07 22:00:00 +02:00
if ( ! public_server - > integer | | sv_maxclients - > integer = = 1 )
return ; // only public servers send heartbeats
2007-06-21 22:00:00 +02:00
// check for time wraparound
2010-10-09 22:00:00 +02:00
if ( svs . last_heartbeat > host . realtime )
svs . last_heartbeat = host . realtime ;
2007-06-21 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
if ( ( host . realtime - svs . last_heartbeat ) < HEARTBEAT_SECONDS )
2009-02-03 22:00:00 +01:00
return ; // not time to send yet
2007-06-21 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
svs . last_heartbeat = host . realtime ;
2007-06-21 22:00:00 +02:00
2011-07-07 22:00:00 +02:00
Master_Add ( ) ;
2007-06-21 22:00:00 +02:00
}
/*
= = = = = = = = = = = = = = = = =
Master_Shutdown
Informs all masters that this server is going down
= = = = = = = = = = = = = = = = =
*/
2010-06-20 22:00:00 +02:00
void Master_Shutdown ( void )
2007-06-21 22:00:00 +02:00
{
}
//============================================================================
/*
= = = = = = = = = = = = = = =
SV_Init
2008-12-15 22:00:00 +01:00
Only called at startup , not for each game
2007-06-21 22:00:00 +02:00
= = = = = = = = = = = = = = =
*/
2008-08-04 22:00:00 +02:00
void SV_Init ( void )
2007-06-21 22:00:00 +02:00
{
2007-12-11 22:00:00 +01:00
SV_InitOperatorCommands ( ) ;
2007-06-21 22:00:00 +02:00
2010-03-24 22:00:00 +01:00
Cvar_Get ( " skill " , " 1 " , CVAR_LATCH , " game skill level " ) ;
2010-10-22 22:00:00 +02:00
Cvar_Get ( " deathmatch " , " 0 " , CVAR_LATCH | CVAR_SERVERNOTIFY , " displays deathmatch state " ) ;
Cvar_Get ( " teamplay " , " 0 " , CVAR_LATCH | CVAR_SERVERNOTIFY , " displays teamplay state " ) ;
Cvar_Get ( " coop " , " 0 " , CVAR_LATCH | CVAR_SERVERNOTIFY , " displays cooperative state " ) ;
2010-10-09 22:00:00 +02:00
Cvar_Get ( " protocol " , va ( " %i " , PROTOCOL_VERSION ) , CVAR_INIT , " displays server protocol version " ) ;
2010-10-22 22:00:00 +02:00
Cvar_Get ( " defaultmap " , " " , CVAR_SERVERNOTIFY , " holds the multiplayer mapname " ) ;
2010-06-11 22:00:00 +02:00
Cvar_Get ( " showtriggers " , " 0 " , CVAR_LATCH , " debug cvar shows triggers " ) ;
2010-07-22 22:00:00 +02:00
Cvar_Get ( " sv_aim " , " 0 " , CVAR_ARCHIVE , " enable auto-aiming " ) ;
2010-07-30 22:00:00 +02:00
Cvar_Get ( " mapcyclefile " , " mapcycle.txt " , 0 , " name of multiplayer map cycle configuration file " ) ;
2010-07-31 22:00:00 +02:00
Cvar_Get ( " servercfgfile " , " server.cfg " , 0 , " name of dedicated server configuration file " ) ;
Cvar_Get ( " lservercfgfile " , " listenserver.cfg " , 0 , " name of listen server configuration file " ) ;
2010-08-18 22:00:00 +02:00
Cvar_Get ( " motdfile " , " motd.txt " , 0 , " name of 'message of the day' file " ) ;
2010-07-31 22:00:00 +02:00
Cvar_Get ( " sv_language " , " 0 " , 0 , " game language (currently unused) " ) ;
2010-09-16 22:00:00 +02:00
Cvar_Get ( " suitvolume " , " 0.25 " , CVAR_ARCHIVE , " HEV suit volume " ) ;
2011-02-22 22:00:00 +01:00
Cvar_Get ( " sv_background " , " 0 " , CVAR_READ_ONLY , " indicate what background map is running " ) ;
2011-03-13 22:00:00 +01:00
Cvar_Get ( " gamedir " , GI - > gamefolder , CVAR_SERVERINFO | CVAR_SERVERNOTIFY | CVAR_INIT , " game folder " ) ;
2011-04-08 22:00:00 +02:00
Cvar_Get ( " sv_alltalk " , " 1 " , 0 , " allow to talking for all players (legacy, unused) " ) ;
2011-08-14 22:00:00 +02:00
Cvar_Get ( " sv_airmove " , " 1 " , CVAR_SERVERNOTIFY , " enable airmovement (legacy, unused) " ) ;
2011-04-08 22:00:00 +02:00
Cvar_Get ( " mp_autocrosshair " , " 0 " , 0 , " allow auto crosshair in multiplayer (legacy, unused) " ) ;
2011-03-13 22:00:00 +01:00
2010-06-16 22:00:00 +02:00
// half-life shared variables
2010-12-14 22:00:00 +01:00
sv_zmax = Cvar_Get ( " sv_zmax " , " 4096 " , CVAR_PHYSICINFO , " zfar server value " ) ;
2010-08-15 22:00:00 +02:00
sv_wateramp = Cvar_Get ( " sv_wateramp " , " 0 " , CVAR_PHYSICINFO , " global water wave height " ) ;
2011-03-03 22:00:00 +01:00
sv_skycolor_r = Cvar_Get ( " sv_skycolor_r " , " 255 " , CVAR_PHYSICINFO , " skycolor red (hl1 compatibility) " ) ;
sv_skycolor_g = Cvar_Get ( " sv_skycolor_g " , " 255 " , CVAR_PHYSICINFO , " skycolor green (hl1 compatibility) " ) ;
sv_skycolor_b = Cvar_Get ( " sv_skycolor_b " , " 255 " , CVAR_PHYSICINFO , " skycolor blue (hl1 compatibility) " ) ;
sv_skyvec_x = Cvar_Get ( " sv_skyvec_x " , " 0 " , CVAR_PHYSICINFO , " sky direction x (hl1 compatibility) " ) ;
2010-08-15 22:00:00 +02:00
sv_skyvec_y = Cvar_Get ( " sv_skyvec_y " , " 0 " , CVAR_PHYSICINFO , " sky direction y (hl1 compatibility) " ) ;
2011-03-03 22:00:00 +01:00
sv_skyvec_z = Cvar_Get ( " sv_skyvec_z " , " 0 " , CVAR_PHYSICINFO , " sky direction z (hl1 compatibility) " ) ;
2011-04-05 22:00:00 +02:00
sv_skyname = Cvar_Get ( " sv_skyname " , " desert " , CVAR_PHYSICINFO , " skybox name (can be dynamically changed in-game) " ) ;
2010-08-19 22:00:00 +02:00
sv_footsteps = Cvar_Get ( " mp_footsteps " , " 1 " , CVAR_PHYSICINFO , " can hear footsteps from other players " ) ;
2010-03-24 22:00:00 +01:00
2010-06-20 22:00:00 +02:00
rcon_password = Cvar_Get ( " rcon_password " , " " , 0 , " remote connect password " ) ;
2011-01-10 22:00:00 +01:00
sv_stepsize = Cvar_Get ( " sv_stepsize " , " 18 " , CVAR_ARCHIVE | CVAR_PHYSICINFO , " how high you can step up " ) ;
2010-03-24 22:00:00 +01:00
sv_newunit = Cvar_Get ( " sv_newunit " , " 0 " , 0 , " sets to 1 while new unit is loading " ) ;
2010-10-22 22:00:00 +02:00
hostname = Cvar_Get ( " hostname " , " unnamed " , CVAR_SERVERINFO | CVAR_SERVERNOTIFY | CVAR_ARCHIVE , " host name " ) ;
timeout = Cvar_Get ( " timeout " , " 125 " , CVAR_SERVERNOTIFY , " connection timeout " ) ;
zombietime = Cvar_Get ( " zombietime " , " 2 " , CVAR_SERVERNOTIFY , " timeout for clients-zombie (who died but not respawned) " ) ;
sv_pausable = Cvar_Get ( " pausable " , " 1 " , CVAR_SERVERNOTIFY , " allow players to pause or not " ) ;
2011-02-15 22:00:00 +01:00
sv_allow_studio_scaling = Cvar_Get ( " sv_allow_studio_scaling " , " 0 " , CVAR_ARCHIVE | CVAR_PHYSICINFO , " allow to apply scale for studio models " ) ;
2011-02-05 22:00:00 +01:00
sv_allow_studio_attachment_angles = Cvar_Get ( " sv_allow_studio_attachment_angles " , " 0 " , CVAR_ARCHIVE , " enable calc angles for attachment points (on studio models) " ) ;
2011-04-05 22:00:00 +02:00
sv_allow_rotate_pushables = Cvar_Get ( " sv_allow_rotate_pushables " , " 0 " , CVAR_ARCHIVE , " let the pushers rotate pushables with included origin-brush " ) ;
2011-04-08 22:00:00 +02:00
sv_clienttrace = Cvar_Get ( " sv_clienttrace " , " 0 " , CVAR_SERVERNOTIFY | CVAR_PHYSICINFO , " scaling factor for client hitboxes " ) ;
2010-02-07 22:00:00 +01:00
sv_wallbounce = Cvar_Get ( " sv_wallbounce " , " 1.0 " , CVAR_PHYSICINFO , " bounce factor for client with MOVETYPE_BOUNCE " ) ;
sv_spectatormaxspeed = Cvar_Get ( " sv_spectatormaxspeed " , " 500 " , CVAR_PHYSICINFO , " spectator maxspeed " ) ;
2011-01-10 22:00:00 +01:00
sv_waterfriction = Cvar_Get ( " sv_waterfriction " , " 1 " , CVAR_PHYSICINFO , " how fast you slow down in water " ) ;
2010-02-07 22:00:00 +01:00
sv_wateraccelerate = Cvar_Get ( " sv_wateraccelerate " , " 10 " , CVAR_PHYSICINFO , " rate at which a player accelerates to sv_maxspeed while in the water " ) ;
2011-03-22 22:00:00 +01:00
sv_rollangle = Cvar_Get ( " sv_rollangle " , " 0 " , CVAR_PHYSICINFO , " how much to tilt the view when strafing " ) ;
2010-02-07 22:00:00 +01:00
sv_rollspeed = Cvar_Get ( " sv_rollspeed " , " 200 " , CVAR_PHYSICINFO , " how much strafing is necessary to tilt the view " ) ;
2011-01-08 22:00:00 +01:00
sv_airaccelerate = Cvar_Get ( " sv_airaccelerate " , " 10 " , CVAR_PHYSICINFO , " player accellerate in air " ) ;
2010-03-24 22:00:00 +01:00
sv_maxvelocity = Cvar_Get ( " sv_maxvelocity " , " 2000 " , CVAR_PHYSICINFO , " max world velocity " ) ;
2010-02-07 22:00:00 +01:00
sv_gravity = Cvar_Get ( " sv_gravity " , " 800 " , CVAR_PHYSICINFO , " world gravity " ) ;
sv_maxspeed = Cvar_Get ( " sv_maxspeed " , " 320 " , CVAR_PHYSICINFO , " maximum speed a player can accelerate to when on ground " ) ;
sv_accelerate = Cvar_Get ( " sv_accelerate " , " 10 " , CVAR_PHYSICINFO , " rate at which a player accelerates to sv_maxspeed " ) ;
sv_friction = Cvar_Get ( " sv_friction " , " 4 " , CVAR_PHYSICINFO , " how fast you slow down " ) ;
2011-01-10 22:00:00 +01:00
sv_edgefriction = Cvar_Get ( " edgefriction " , " 2 " , CVAR_PHYSICINFO , " how much you slow down when nearing a ledge you might fall off " ) ;
2010-02-07 22:00:00 +01:00
sv_stopspeed = Cvar_Get ( " sv_stopspeed " , " 100 " , CVAR_PHYSICINFO , " how fast you come to a complete stop " ) ;
2011-01-03 22:00:00 +01:00
sv_maxclients = Cvar_Get ( " maxplayers " , " 1 " , CVAR_LATCH | CVAR_SERVERNOTIFY , " server clients limit " ) ;
2011-01-11 22:00:00 +01:00
sv_check_errors = Cvar_Get ( " sv_check_errors " , " 0 " , CVAR_ARCHIVE , " check edicts for errors " ) ;
2010-02-07 22:00:00 +01:00
physinfo = Cvar_Get ( " @physinfo " , " 0 " , CVAR_READ_ONLY , " " ) ; // use ->modified value only
2010-03-30 22:00:00 +02:00
serverinfo = Cvar_Get ( " @serverinfo " , " 0 " , CVAR_READ_ONLY , " " ) ; // use ->modified value only
2008-06-29 22:00:00 +02:00
public_server = Cvar_Get ( " public " , " 0 " , 0 , " change server type from private to public " ) ;
2010-12-13 22:00:00 +01:00
sv_lighting_modulate = Cvar_Get ( " r_lighting_modulate " , " 0.6 " , CVAR_ARCHIVE , " lightstyles modulate scale " ) ;
2008-07-11 22:00:00 +02:00
sv_reconnect_limit = Cvar_Get ( " sv_reconnect_limit " , " 3 " , CVAR_ARCHIVE , " max reconnect attempts " ) ;
2010-10-14 22:00:00 +02:00
sv_failuretime = Cvar_Get ( " sv_failuretime " , " 0.5 " , 0 , " after this long without a packet from client, don't send any more until client starts sending again " ) ;
sv_unlag = Cvar_Get ( " sv_unlag " , " 1 " , 0 , " allow lag compensation on server-side " ) ;
sv_maxunlag = Cvar_Get ( " sv_maxunlag " , " 0.5 " , 0 , " max latency which can be interpolated " ) ;
sv_unlagpush = Cvar_Get ( " sv_unlagpush " , " 0.0 " , 0 , " unlag push bias " ) ;
sv_unlagsamples = Cvar_Get ( " sv_unlagsamples " , " 1 " , 0 , " max samples to interpolate " ) ;
2010-10-28 22:00:00 +02:00
sv_allow_upload = Cvar_Get ( " sv_allow_upload " , " 1 " , 0 , " allow uploading custom resources from clients " ) ;
sv_allow_download = Cvar_Get ( " sv_allow_download " , " 1 " , 0 , " allow download missed resources to clients " ) ;
sv_send_logos = Cvar_Get ( " sv_send_logos " , " 1 " , 0 , " send custom player decals to other clients " ) ;
sv_send_resources = Cvar_Get ( " sv_send_resources " , " 1 " , 0 , " send generic resources that specified in 'mapname.res' " ) ;
2010-12-27 22:00:00 +01:00
sv_sendvelocity = Cvar_Get ( " sv_sendvelocity " , " 1 " , CVAR_ARCHIVE , " force to send velocity for event_t structure across network " ) ;
2010-10-28 22:00:00 +02:00
mp_consistency = Cvar_Get ( " mp_consistency " , " 1 " , CVAR_SERVERNOTIFY , " enbale consistency check in multiplayer " ) ;
2010-10-14 22:00:00 +02:00
clockwindow = Cvar_Get ( " clockwindow " , " 0.5 " , 0 , " timewindow to execute client moves " ) ;
2010-12-09 22:00:00 +01:00
sv_novis = Cvar_Get ( " sv_novis " , " 0 " , 0 , " force to ignore server visibility " ) ;
2011-04-15 22:00:00 +02:00
sv_fix_pushents = Cvar_Get ( " sv_fix_pushents " , " 1 " , CVAR_ARCHIVE , " prevent toss entities from falling through level " ) ;
2008-12-26 22:00:00 +01:00
2010-03-25 22:00:00 +01:00
SV_ClearSaveDir ( ) ; // delete all temporary *.hl files
2010-08-04 22:00:00 +02:00
BF_Init ( & net_message , " NetMessage " , net_message_buffer , sizeof ( net_message_buffer ) ) ;
2007-06-21 22:00:00 +02:00
}
/*
= = = = = = = = = = = = = = = = = =
SV_FinalMessage
Used by SV_Shutdown 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 .
= = = = = = = = = = = = = = = = = =
*/
2010-10-26 22:00:00 +02:00
void SV_FinalMessage ( char * message , qboolean reconnect )
2007-06-21 22:00:00 +02:00
{
2008-07-09 22:00:00 +02:00
sv_client_t * cl ;
2011-04-05 22:00:00 +02:00
byte msg_buf [ 1024 ] ;
2010-08-06 22:00:00 +02:00
sizebuf_t msg ;
2008-07-12 22:00:00 +02:00
int i ;
2007-06-21 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
BF_Init ( & msg , " FinalMessage " , msg_buf , sizeof ( msg_buf ) ) ;
BF_WriteByte ( & msg , svc_print ) ;
BF_WriteByte ( & msg , PRINT_HIGH ) ;
BF_WriteString ( & msg , message ) ;
2007-06-21 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
if ( reconnect )
2007-08-01 22:00:00 +02:00
{
2010-08-16 22:00:00 +02:00
BF_WriteByte ( & msg , svc_changing ) ;
2010-07-31 22:00:00 +02:00
if ( sv . loadgame | | sv_maxclients - > integer > 1 )
2010-08-16 22:00:00 +02:00
BF_WriteOneBit ( & msg , 1 ) ; // changelevel
else BF_WriteOneBit ( & msg , 0 ) ;
2007-08-01 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
else
2007-08-01 22:00:00 +02:00
{
2010-08-04 22:00:00 +02:00
BF_WriteByte ( & msg , svc_disconnect ) ;
2007-08-01 22:00:00 +02:00
}
2008-07-12 22:00:00 +02:00
2007-06-21 22:00:00 +02:00
// send it twice
// stagger the packets to crutch operating system limited buffers
2009-09-25 22:00:00 +02:00
for ( i = 0 , cl = svs . clients ; i < sv_maxclients - > integer ; i + + , cl + + )
2010-10-09 22:00:00 +02:00
if ( cl - > state > = cs_connected & & ! cl - > fakeclient )
2010-08-04 22:00:00 +02:00
Netchan_Transmit ( & cl - > netchan , BF_GetNumBytesWritten ( & msg ) , BF_GetData ( & msg ) ) ;
2007-06-21 22:00:00 +02:00
2009-09-25 22:00:00 +02:00
for ( i = 0 , cl = svs . clients ; i < sv_maxclients - > integer ; i + + , cl + + )
2010-10-09 22:00:00 +02:00
if ( cl - > state > = cs_connected & & ! cl - > fakeclient )
2010-08-04 22:00:00 +02:00
Netchan_Transmit ( & cl - > netchan , BF_GetNumBytesWritten ( & msg ) , BF_GetData ( & msg ) ) ;
2007-06-21 22:00:00 +02:00
}
/*
= = = = = = = = = = = = = = = =
SV_Shutdown
Called when each game quits ,
before Sys_Quit or Sys_Error
= = = = = = = = = = = = = = = =
*/
2010-10-26 22:00:00 +02:00
void SV_Shutdown ( qboolean reconnect )
2007-06-21 22:00:00 +02:00
{
2007-10-29 22:00:00 +01:00
// already freed
2008-08-04 22:00:00 +02:00
if ( host . state = = HOST_ERROR ) return ;
2009-09-25 22:00:00 +02:00
if ( ! SV_Active ( ) ) return ;
2007-10-29 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
MsgDev ( D_INFO , " SV_Shutdown: %s \n " , host . finalmsg ) ;
2008-12-15 22:00:00 +01:00
if ( svs . clients ) SV_FinalMessage ( host . finalmsg , reconnect ) ;
2007-06-21 22:00:00 +02:00
2007-11-21 22:00:00 +01:00
Master_Shutdown ( ) ;
2009-09-28 22:00:00 +02:00
if ( ! reconnect ) SV_UnloadProgs ( ) ;
else SV_DeactivateServer ( ) ;
2007-06-21 22:00:00 +02:00
// free current level
2011-03-10 22:00:00 +01:00
Q_memset ( & sv , 0 , sizeof ( sv ) ) ;
2008-08-04 22:00:00 +02:00
Host_SetServerState ( sv . state ) ;
2007-06-21 22:00:00 +02:00
// free server static data
2010-10-15 22:00:00 +02:00
if ( svs . clients )
{
Z_Free ( svs . clients ) ;
svs . clients = NULL ;
}
if ( svs . baselines )
{
Z_Free ( svs . baselines ) ;
svs . baselines = NULL ;
}
if ( svs . packet_entities )
{
Z_Free ( svs . packet_entities ) ;
svs . packet_entities = NULL ;
svs . num_client_entities = 0 ;
svs . next_client_entities = 0 ;
}
svs . initialized = false ;
2009-01-02 22:00:00 +01:00
}