📄 sv_main.c
字号:
Redirect all printfs
===============
*/
void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
qboolean valid;
unsigned int time;
char remaining[1024];
// TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
// (OOB messages are the bottleneck here)
#define SV_OUTPUTBUF_LENGTH (1024 - 16)
char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
static unsigned int lasttime = 0;
char *cmd_aux;
// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
time = Com_Milliseconds();
if (time<(lasttime+500)) {
return;
}
lasttime = time;
if ( !strlen( sv_rconPassword->string ) ||
strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
valid = qfalse;
Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
} else {
valid = qtrue;
Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
}
// start redirecting all print outputs to the packet
svs.redirectAddress = from;
Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
if ( !strlen( sv_rconPassword->string ) ) {
Com_Printf ("No rconpassword set on the server.\n");
} else if ( !valid ) {
Com_Printf ("Bad rconpassword.\n");
} else {
remaining[0] = 0;
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
// get the command directly, "rcon <pass> <command>" to avoid quoting issues
// extract the command by walking
// since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing
cmd_aux = Cmd_Cmd();
cmd_aux+=4;
while(cmd_aux[0]==' ')
cmd_aux++;
while(cmd_aux[0] && cmd_aux[0]!=' ') // password
cmd_aux++;
while(cmd_aux[0]==' ')
cmd_aux++;
Q_strcat( remaining, sizeof(remaining), cmd_aux);
Cmd_ExecuteString (remaining);
}
Com_EndRedirect ();
}
/*
=================
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.
=================
*/
void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
char *s;
char *c;
MSG_BeginReadingOOB( msg );
MSG_ReadLong( msg ); // skip the -1 marker
if (!Q_strncmp("connect", &msg->data[4], 7)) {
Huff_Decompress(msg, 12);
}
s = MSG_ReadStringLine( msg );
Cmd_TokenizeString( s );
c = Cmd_Argv(0);
Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
if (!Q_stricmp(c, "getstatus")) {
SVC_Status( from );
} else if (!Q_stricmp(c, "getinfo")) {
SVC_Info( from );
} else if (!Q_stricmp(c, "getchallenge")) {
SV_GetChallenge( from );
} else if (!Q_stricmp(c, "connect")) {
SV_DirectConnect( from );
} else if (!Q_stricmp(c, "ipAuthorize")) {
SV_AuthorizeIpPacket( from );
} else if (!Q_stricmp(c, "rcon")) {
SVC_RemoteCommand( from, msg );
} else if (!Q_stricmp(c, "disconnect")) {
// if a client starts up a local server, we may see some spurious
// server disconnect messages when their new server sees our final
// sequenced messages to the old client
} else {
Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
, NET_AdrToString (from), s);
}
}
//============================================================================
/*
=================
SV_ReadPackets
=================
*/
void SV_PacketEvent( netadr_t from, msg_t *msg ) {
int i;
client_t *cl;
int qport;
// check for connectionless packet (0xffffffff) first
if ( msg->cursize >= 4 && *(int *)msg->data == -1) {
SV_ConnectionlessPacket( from, msg );
return;
}
// read the qport out of the message so we can fix up
// stupid address translating routers
MSG_BeginReadingOOB( msg );
MSG_ReadLong( msg ); // sequence number
qport = MSG_ReadShort( msg ) & 0xffff;
// find which client the message is from
for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
if (cl->state == CS_FREE) {
continue;
}
if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
continue;
}
// it is possible to have multiple clients from a single IP
// address, so they are differentiated by the qport variable
if (cl->netchan.qport != qport) {
continue;
}
// the IP port can't be used to differentiate them, because
// some address translating routers periodically change UDP
// port assignments
if (cl->netchan.remoteAddress.port != from.port) {
Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
cl->netchan.remoteAddress.port = from.port;
}
// make sure it is a valid, in sequence packet
if (SV_Netchan_Process(cl, msg)) {
// zombie clients still need to do the Netchan_Process
// to make sure they don't need to retransmit the final
// reliable message, but they don't do any other processing
if (cl->state != CS_ZOMBIE) {
cl->lastPacketTime = svs.time; // don't timeout
SV_ExecuteClientMessage( cl, msg );
}
}
return;
}
// if we received a sequenced packet from an address we don't recognize,
// send an out of band disconnect packet to it
NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
}
/*
===================
SV_CalcPings
Updates the cl->ping variables
===================
*/
void SV_CalcPings( void ) {
int i, j;
client_t *cl;
int total, count;
int delta;
playerState_t *ps;
for (i=0 ; i < sv_maxclients->integer ; i++) {
cl = &svs.clients[i];
if ( cl->state != CS_ACTIVE ) {
cl->ping = 999;
continue;
}
if ( !cl->gentity ) {
cl->ping = 999;
continue;
}
if ( cl->gentity->r.svFlags & SVF_BOT ) {
cl->ping = 0;
continue;
}
total = 0;
count = 0;
for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
if ( cl->frames[j].messageAcked <= 0 ) {
continue;
}
delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
count++;
total += delta;
}
if (!count) {
cl->ping = 999;
} else {
cl->ping = total/count;
if ( cl->ping > 999 ) {
cl->ping = 999;
}
}
// let the game dll know about the ping
ps = SV_GameClientNum( i );
ps->ping = cl->ping;
}
}
/*
==================
SV_CheckTimeouts
If a packet has not been received from a client for timeout->integer
seconds, drop the conneciton. Server time is used instead of
realtime to avoid dropping the local client while debugging.
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;
int droppoint;
int zombiepoint;
droppoint = svs.time - 1000 * sv_timeout->integer;
zombiepoint = svs.time - 1000 * sv_zombietime->integer;
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
// message times may be wrong across a changelevel
if (cl->lastPacketTime > svs.time) {
cl->lastPacketTime = svs.time;
}
if (cl->state == CS_ZOMBIE
&& cl->lastPacketTime < zombiepoint) {
// using the client id cause the cl->name is empty at this point
Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
cl->state = CS_FREE; // can now be reused
continue;
}
if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) {
// wait several frames so a debugger session doesn't
// cause a timeout
if ( ++cl->timeoutCount > 5 ) {
SV_DropClient (cl, "timed out");
cl->state = CS_FREE; // don't bother with zombie state
}
} else {
cl->timeoutCount = 0;
}
}
}
/*
==================
SV_CheckPaused
==================
*/
qboolean SV_CheckPaused( void ) {
int count;
client_t *cl;
int i;
if ( !cl_paused->integer ) {
return qfalse;
}
// only pause if there is just a single client connected
count = 0;
for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
count++;
}
}
if ( count > 1 ) {
// don't pause
if (sv_paused->integer)
Cvar_Set("sv_paused", "0");
return qfalse;
}
if (!sv_paused->integer)
Cvar_Set("sv_paused", "1");
return qtrue;
}
/*
==================
SV_Frame
Player movement occurs as a result of packet events, which
happen before SV_Frame is called
==================
*/
void SV_Frame( int msec ) {
int frameMsec;
int startTime;
// the menu kills the server with this cvar
if ( sv_killserver->integer ) {
SV_Shutdown ("Server was killed.\n");
Cvar_Set( "sv_killserver", "0" );
return;
}
if ( !com_sv_running->integer ) {
return;
}
// allow pause if only the local client is connected
if ( SV_CheckPaused() ) {
return;
}
// if it isn't time for the next frame, do nothing
if ( sv_fps->integer < 1 ) {
Cvar_Set( "sv_fps", "10" );
}
frameMsec = 1000 / sv_fps->integer ;
sv.timeResidual += msec;
if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual );
if ( com_dedicated->integer && sv.timeResidual < frameMsec ) {
// NET_Sleep will give the OS time slices until either get a packet
// or time enough for a server frame has gone by
NET_Sleep(frameMsec - sv.timeResidual);
return;
}
// if time is about to hit the 32nd bit, kick all clients
// and clear sv.time, rather
// than checking for negative time wraparound everywhere.
// 2giga-milliseconds = 23 days, so it won't be too often
if ( svs.time > 0x70000000 ) {
SV_Shutdown( "Restarting server due to time wrapping" );
Cbuf_AddText( "vstr nextmap\n" );
return;
}
// this can happen considerably earlier when lots of clients play and the map doesn't change
if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
Cbuf_AddText( "vstr nextmap\n" );
return;
}
if( sv.restartTime && svs.time >= sv.restartTime ) {
sv.restartTime = 0;
Cbuf_AddText( "map_restart 0\n" );
return;
}
// update infostrings if anything has been changed
if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
cvar_modifiedFlags &= ~CVAR_SERVERINFO;
}
if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
}
if ( com_speeds->integer ) {
startTime = Sys_Milliseconds ();
} else {
startTime = 0; // quite a compiler warning
}
// update ping based on the all received frames
SV_CalcPings();
if (com_dedicated->integer) SV_BotFrame( svs.time );
// run the game simulation in chunks
while ( sv.timeResidual >= frameMsec ) {
sv.timeResidual -= frameMsec;
svs.time += frameMsec;
// let everything in the world think and move
VM_Call( gvm, GAME_RUN_FRAME, svs.time );
}
if ( com_speeds->integer ) {
time_game = Sys_Milliseconds () - startTime;
}
// check timeouts
SV_CheckTimeouts();
// send messages back to the clients
SV_SendClientMessages();
// send a heartbeat to the master if needed
SV_MasterHeartbeat();
}
//============================================================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -