This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/server/sv_client.c

2487 lines
64 KiB
C
Raw Normal View History

2011-05-09 22:00:00 +02:00
/*
sv_client.c - client interactions
Copyright (C) 2008 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.
*/
2008-05-20 22:00:00 +02:00
2008-06-09 22:00:00 +02:00
#include "common.h"
2008-08-05 22:00:00 +02:00
#include "const.h"
2008-05-20 22:00:00 +02:00
#include "server.h"
2010-08-04 22:00:00 +02:00
#include "net_encode.h"
2011-10-09 22:00:00 +02:00
#include "net_api.h"
2008-05-20 22:00:00 +02:00
2012-12-16 21:00:00 +01:00
const char *clc_strings[11] =
2010-10-14 22:00:00 +02:00
{
"clc_bad",
"clc_nop",
"clc_move",
"clc_stringcmd",
"clc_delta",
"clc_resourcelist",
2017-02-12 22:00:00 +01:00
"clc_unused6",
2010-10-14 22:00:00 +02:00
"clc_fileconsistency",
"clc_voicedata",
};
2008-07-12 22:00:00 +02:00
typedef struct ucmd_s
{
const char *name;
void (*func)( sv_client_t *cl );
} ucmd_t;
2010-06-23 22:00:00 +02:00
static int g_userid = 1;
2008-07-12 22:00:00 +02:00
/*
=================
SV_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 SV_GetChallenge( netadr_t from )
{
int i, oldest = 0;
2010-10-09 22:00:00 +02:00
double oldestTime;
2008-07-12 22:00:00 +02:00
oldestTime = 0x7fffffff;
2012-12-16 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
// see if we already have a challenge for this ip
2012-12-16 21:00:00 +01:00
for( i = 0; i < MAX_CHALLENGES; i++ )
2008-07-12 22:00:00 +02:00
{
if( !svs.challenges[i].connected && NET_CompareAdr( from, svs.challenges[i].adr ))
break;
2012-12-16 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
if( svs.challenges[i].time < oldestTime )
{
oldestTime = svs.challenges[i].time;
oldest = i;
}
}
if( i == MAX_CHALLENGES )
{
// this is the first time this client has asked for a challenge
2017-02-27 22:00:00 +01:00
svs.challenges[oldest].challenge = (COM_RandomLong( 0, 0xFFFF ) << 16) | COM_RandomLong( 0, 0xFFFF );
2008-07-12 22:00:00 +02:00
svs.challenges[oldest].adr = from;
2010-10-09 22:00:00 +02:00
svs.challenges[oldest].time = host.realtime;
2008-07-12 22:00:00 +02:00
svs.challenges[oldest].connected = false;
i = oldest;
}
// send it back
Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "challenge %i", svs.challenges[i].challenge );
}
2017-02-05 22:00:00 +01:00
int SV_GetFragmentSize( sv_client_t *cl )
{
int size = FRAGMENT_SV2CL_MAX_SIZE;
int cl_size;
if( cl->state == cs_spawned )
{
cl_size = Q_atoi( Info_ValueForKey( cl->userinfo, "cl_dlmax" ));
if( cl_size != 0 )
{
size = bound( FRAGMENT_SV2CL_MIN_SIZE, cl_size, FRAGMENT_SV2CL_MAX_SIZE );
}
else
{
size = FRAGMENT_SV2CL_MIN_SIZE;
}
}
return size;
}
2008-07-12 22:00:00 +02:00
/*
==================
SV_DirectConnect
A connection request that did not come from the master
==================
*/
void SV_DirectConnect( netadr_t from )
{
char userinfo[MAX_INFO_STRING];
2016-11-21 22:00:00 +01:00
char physinfo[MAX_PHYSINFO_STRING];
2008-07-12 22:00:00 +02:00
sv_client_t temp, *cl, *newcl;
2010-10-07 22:00:00 +02:00
int qport, version;
2016-11-21 22:00:00 +01:00
int i, edictnum;
2010-10-07 22:00:00 +02:00
int count = 0;
2008-07-12 22:00:00 +02:00
int challenge;
2012-03-24 21:00:00 +01:00
edict_t *ent;
2008-07-12 22:00:00 +02:00
2011-03-09 22:00:00 +01:00
version = Q_atoi( Cmd_Argv( 1 ));
2012-12-16 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
if( version != PROTOCOL_VERSION )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION );
MsgDev( D_ERROR, "SV_DirectConnect: rejected connect from version %i\n", version );
2013-02-19 21:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
2008-07-12 22:00:00 +02:00
return;
}
2011-03-09 22:00:00 +01:00
qport = Q_atoi( Cmd_Argv( 2 ));
challenge = Q_atoi( Cmd_Argv( 3 ));
2014-01-01 21:00:00 +01:00
Q_strncpy( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ));
2008-07-12 22:00:00 +02:00
// quick reject
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2008-07-12 22:00:00 +02:00
{
2015-12-04 22:00:00 +01:00
if( cl->state == cs_free || cl->state == cs_zombie )
2012-12-16 21:00:00 +01:00
continue;
2010-10-07 22:00:00 +02:00
if( NET_CompareBaseAdr( from, cl->netchan.remote_address ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port ))
2008-07-12 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
if( !NET_IsLocalAddress( from ) && ( host.realtime - cl->connection_started ) < sv_reconnect_limit->value )
2008-07-12 22:00:00 +02:00
{
MsgDev( D_INFO, "%s:reconnect rejected : too soon\n", NET_AdrToString( from ));
2013-02-19 21:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
2008-07-12 22:00:00 +02:00
return;
}
break;
}
}
// see if the challenge is valid (LAN clients don't need to challenge)
if( !NET_IsLocalAddress( from ))
{
for( i = 0; i < MAX_CHALLENGES; i++ )
{
if( NET_CompareAdr( from, svs.challenges[i].adr ))
{
if( challenge == svs.challenges[i].challenge )
break; // valid challenge
}
}
2010-07-28 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
if( i == MAX_CHALLENGES )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for address.\n" );
2013-02-19 21:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
2008-07-12 22:00:00 +02:00
return;
}
2010-07-28 22:00:00 +02:00
2011-03-07 22:00:00 +01:00
MsgDev( D_NOTE, "Client %i connecting with challenge %p\n", i, challenge );
2012-12-16 21:00:00 +01:00
svs.challenges[i].connected = true;
2008-07-12 22:00:00 +02:00
}
2011-03-07 22:00:00 +01:00
// force the IP key/value pair so the game can filter based on ip
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ), MAX_INFO_STRING );
2008-07-12 22:00:00 +02:00
newcl = &temp;
2016-11-17 22:00:00 +01:00
memset( newcl, 0, sizeof( sv_client_t ));
2008-07-12 22:00:00 +02:00
// if there is already a slot for this ip, reuse it
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2008-07-12 22:00:00 +02:00
{
2015-12-04 22:00:00 +01:00
if( cl->state == cs_free || cl->state == cs_zombie )
2012-12-16 21:00:00 +01:00
continue;
2010-10-07 22:00:00 +02:00
if( NET_CompareBaseAdr( from, cl->netchan.remote_address ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port ))
2008-07-12 22:00:00 +02:00
{
MsgDev( D_INFO, "%s:reconnect\n", NET_AdrToString( from ));
newcl = cl;
goto gotnewcl;
}
}
// find a client slot
newcl = NULL;
2012-12-16 21:00:00 +01:00
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2008-07-12 22:00:00 +02:00
{
if( cl->state == cs_free )
{
newcl = cl;
break;
}
}
2010-07-28 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
if( !newcl )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" );
2012-12-16 21:00:00 +01:00
MsgDev( D_INFO, "SV_DirectConnect: rejected a connection.\n" );
2013-02-19 21:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
2008-07-12 22:00:00 +02:00
return;
}
// build a new connection
// accept the new client
2011-02-20 22:00:00 +01:00
gotnewcl:
2008-07-12 22:00:00 +02:00
// this is the only place a sv_client_t is ever initialized
2012-12-16 21:00:00 +01:00
2017-02-12 22:00:00 +01:00
if( svs.maxclients == 1 ) // save physinfo for singleplayer
2011-03-09 22:00:00 +01:00
Q_strncpy( physinfo, newcl->physinfo, sizeof( physinfo ));
2010-07-28 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
*newcl = temp;
2010-07-28 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
if( svs.maxclients == 1 ) // restore physinfo for singleplayer
2011-03-09 22:00:00 +01:00
Q_strncpy( newcl->physinfo, physinfo, sizeof( physinfo ));
2010-07-28 22:00:00 +02:00
2011-04-08 22:00:00 +02:00
svs.currentPlayer = newcl;
2011-04-21 22:00:00 +02:00
svs.currentPlayerNum = (newcl - svs.clients);
edictnum = svs.currentPlayerNum + 1;
2008-07-12 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( edictnum );
2008-07-12 22:00:00 +02:00
newcl->edict = ent;
newcl->challenge = challenge; // save challenge for checksumming
2010-04-02 22:00:00 +02:00
newcl->frames = (client_frame_t *)Z_Malloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
2010-06-23 22:00:00 +02:00
newcl->userid = g_userid++; // create unique userid
2011-04-08 22:00:00 +02:00
newcl->authentication_method = 2;
2012-03-24 21:00:00 +01:00
2013-02-19 21:00:00 +01:00
// initailize netchan here because SV_DropClient will clear network buffer
2017-02-05 22:00:00 +01:00
Netchan_Setup( NS_SERVER, &newcl->netchan, from, qport, newcl, SV_GetFragmentSize );
2016-11-14 22:00:00 +01:00
MSG_Init( &newcl->datagram, "Datagram", newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf
2013-02-19 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
// get the game a chance to reject this connection or modify the userinfo
2010-08-25 22:00:00 +02:00
if( !( SV_ClientConnect( ent, userinfo )))
2008-07-12 22:00:00 +02:00
{
2011-04-05 22:00:00 +02:00
if( *Info_ValueForKey( userinfo, "rejmsg" ))
2008-07-12 22:00:00 +02:00
Netchan_OutOfBandPrint( NS_SERVER, from, "print\n%s\nConnection refused.\n", Info_ValueForKey( userinfo, "rejmsg" ));
else Netchan_OutOfBandPrint( NS_SERVER, from, "print\nConnection refused.\n" );
2012-12-16 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
MsgDev( D_ERROR, "SV_DirectConnect: game rejected a connection.\n");
2013-02-19 21:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
2011-03-28 22:00:00 +02:00
SV_DropClient( newcl );
2008-07-12 22:00:00 +02:00
return;
}
// send the connect packet to the client
Netchan_OutOfBandPrint( NS_SERVER, from, "client_connect" );
newcl->state = cs_connected;
2010-10-09 22:00:00 +02:00
newcl->lastmessage = host.realtime;
2017-02-05 22:00:00 +01:00
newcl->connection_started = host.realtime;
2016-11-21 22:00:00 +01:00
newcl->cl_updaterate = 0.05; // 20 fps as default
2010-10-14 22:00:00 +02:00
newcl->delta_sequence = -1;
2009-09-16 22:00:00 +02:00
2015-12-26 22:00:00 +01:00
// parse some info from the info strings (this can override cl_updaterate)
2016-11-23 22:00:00 +01:00
Q_strncpy( newcl->userinfo, userinfo, sizeof( newcl->userinfo ));
SV_UserinfoChanged( newcl, newcl->userinfo );
2015-12-26 22:00:00 +01:00
newcl->next_messagetime = host.realtime + newcl->cl_updaterate;
2017-02-05 22:00:00 +01:00
newcl->next_sendinfotime = 0.0;
2015-12-26 22:00:00 +01:00
2008-07-12 22:00:00 +02:00
// if this was the first client on the server, or the last client
// the server can hold, send a heartbeat to the master.
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2008-07-12 22:00:00 +02:00
if( cl->state >= cs_connected ) count++;
2012-12-16 21:00:00 +01:00
2017-02-12 22:00:00 +01:00
if( count == 1 || count == svs.maxclients )
2008-07-12 22:00:00 +02:00
svs.last_heartbeat = MAX_HEARTBEAT;
}
2012-06-25 22:00:00 +02:00
/*
==================
SV_DisconnectClient
Disconnect client callback
==================
*/
void SV_DisconnectClient( edict_t *pClient )
{
if( !pClient ) return;
2013-02-19 21:00:00 +01:00
svgame.dllFuncs.pfnClientDisconnect( pClient );
2012-06-25 22:00:00 +02:00
// don't send to other clients
pClient->v.modelindex = 0;
2016-11-21 22:00:00 +01:00
SV_FreePrivateData( pClient );
2015-12-07 22:00:00 +01:00
// invalidate serial number
pClient->serialnumber++;
2012-06-25 22:00:00 +02:00
}
2009-09-17 22:00:00 +02:00
/*
==================
SV_FakeConnect
A connection request that came from the game module
==================
*/
2009-11-23 22:00:00 +01:00
edict_t *SV_FakeConnect( const char *netname )
2009-09-17 22:00:00 +02:00
{
int i, edictnum;
char userinfo[MAX_INFO_STRING];
sv_client_t temp, *cl, *newcl;
edict_t *ent;
2009-11-23 22:00:00 +01:00
if( !netname ) netname = "";
userinfo[0] = '\0';
2011-04-05 22:00:00 +02:00
// setup fake client params
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( userinfo, "name", netname, MAX_INFO_STRING );
Info_SetValueForKey( userinfo, "model", "gordon", MAX_INFO_STRING );
Info_SetValueForKey( userinfo, "topcolor", "0", MAX_INFO_STRING );
Info_SetValueForKey( userinfo, "bottomcolor", "0", MAX_INFO_STRING );
2009-09-17 22:00:00 +02:00
// force the IP key/value pair so the game can filter based on ip
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( userinfo, "ip", "127.0.0.1", MAX_INFO_STRING );
2009-09-17 22:00:00 +02:00
// find a client slot
newcl = &temp;
2016-11-17 22:00:00 +01:00
memset( newcl, 0, sizeof( sv_client_t ));
2012-12-16 21:00:00 +01:00
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2009-09-17 22:00:00 +02:00
{
if( cl->state == cs_free )
{
newcl = cl;
break;
}
}
2010-10-23 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
if( i == svs.maxclients )
2009-09-17 22:00:00 +02:00
{
MsgDev( D_INFO, "SV_DirectConnect: rejected a connection.\n");
2009-11-23 22:00:00 +01:00
return NULL;
2009-09-17 22:00:00 +02:00
}
// build a new connection
// accept the new client
// this is the only place a sv_client_t is ever initialized
*newcl = temp;
2011-04-08 22:00:00 +02:00
svs.currentPlayer = newcl;
2011-04-21 22:00:00 +02:00
svs.currentPlayerNum = (newcl - svs.clients);
edictnum = svs.currentPlayerNum + 1;
2009-09-17 22:00:00 +02:00
2011-04-05 22:00:00 +02:00
if( newcl->frames )
Mem_Free( newcl->frames ); // fakeclients doesn't have frames
newcl->frames = NULL;
2010-10-10 22:00:00 +02:00
2009-09-17 22:00:00 +02:00
ent = EDICT_NUM( edictnum );
newcl->edict = ent;
newcl->challenge = -1; // fake challenge
2010-10-14 22:00:00 +02:00
newcl->delta_sequence = -1;
2010-10-19 22:00:00 +02:00
newcl->userid = g_userid++; // create unique userid
2016-11-21 22:00:00 +01:00
SetBits( newcl->flags, FCL_FAKECLIENT );
2009-09-17 22:00:00 +02:00
// get the game a chance to reject this connection or modify the userinfo
if( !SV_ClientConnect( ent, userinfo ))
{
MsgDev( D_ERROR, "SV_DirectConnect: game rejected a connection.\n" );
2009-11-23 22:00:00 +01:00
return NULL;
2009-09-17 22:00:00 +02:00
}
// parse some info from the info strings
2016-11-23 22:00:00 +01:00
Q_strncpy( newcl->userinfo, userinfo, sizeof( newcl->userinfo ));
2017-02-12 22:00:00 +01:00
Q_strncpy( newcl->name, netname, sizeof( newcl->name ));
2016-11-23 22:00:00 +01:00
SV_UserinfoChanged( newcl, newcl->userinfo );
SetBits( cl->flags, FCL_RESEND_USERINFO );
2017-02-05 22:00:00 +01:00
cl->next_sendinfotime = 0.0;
2009-09-17 22:00:00 +02:00
2011-03-07 22:00:00 +01:00
MsgDev( D_NOTE, "Bot %i connecting with challenge %p\n", i, -1 );
2011-03-01 22:00:00 +01:00
2017-02-12 22:00:00 +01:00
SetBits( ent->v.flags, FL_CLIENT|FL_FAKECLIENT ); // mark it as fakeclient
2009-09-17 22:00:00 +02:00
newcl->state = cs_spawned;
2010-10-09 22:00:00 +02:00
newcl->lastmessage = host.realtime; // don't timeout
2017-02-05 22:00:00 +01:00
newcl->connection_started = host.realtime;
2009-12-04 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
return ent;
2009-09-17 22:00:00 +02:00
}
2008-07-30 22:00:00 +02:00
/*
=====================
SV_ClientCconnect
QC code can rejected a connection for some reasons
e.g. ipban
=====================
*/
2010-10-26 22:00:00 +02:00
qboolean SV_ClientConnect( edict_t *ent, char *userinfo )
2008-07-30 22:00:00 +02:00
{
2010-10-26 22:00:00 +02:00
qboolean result = true;
2010-06-23 22:00:00 +02:00
char *pszName, *pszAddress;
2011-04-05 22:00:00 +02:00
char szRejectReason[MAX_INFO_STRING];
2008-07-30 22:00:00 +02:00
// make sure we start with known default
2009-09-25 22:00:00 +02:00
if( !sv.loadgame ) ent->v.flags = 0;
2010-06-23 22:00:00 +02:00
szRejectReason[0] = '\0';
pszName = Info_ValueForKey( userinfo, "name" );
pszAddress = Info_ValueForKey( userinfo, "ip" );
2008-07-30 22:00:00 +02:00
2009-11-10 22:00:00 +01:00
MsgDev( D_NOTE, "SV_ClientConnect()\n" );
2010-06-23 22:00:00 +02:00
result = svgame.dllFuncs.pfnClientConnect( ent, pszName, pszAddress, szRejectReason );
2017-02-12 22:00:00 +01:00
if( szRejectReason[0] ) Info_SetValueForKey( userinfo, "rejmsg", szRejectReason, MAX_INFO_STRING );
2008-07-30 22:00:00 +02:00
return result;
}
2008-07-12 22:00:00 +02: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( sv_client_t *drop )
{
int i;
2011-04-05 22:00:00 +02:00
if( drop->state == cs_zombie )
return; // already dropped
2008-07-12 22:00:00 +02:00
// add the disconnect
2016-11-21 22:00:00 +01:00
if( !FBitSet( drop->flags, FCL_FAKECLIENT ))
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &drop->netchan.message, svc_disconnect );
2008-07-12 22:00:00 +02:00
// let the game known about client state
2012-06-25 22:00:00 +02:00
SV_DisconnectClient( drop->edict );
2010-02-02 22:00:00 +01:00
2016-11-21 22:00:00 +01:00
ClearBits( drop->flags, FCL_FAKECLIENT );
ClearBits( drop->flags, FCL_HLTV_PROXY );
2008-07-12 22:00:00 +02:00
drop->state = cs_zombie; // become free in a few seconds
drop->name[0] = 0;
2011-04-05 22:00:00 +02:00
if( drop->frames )
2016-11-21 22:00:00 +01:00
Mem_Free( drop->frames ); // release delta
2011-04-05 22:00:00 +02:00
drop->frames = NULL;
2010-10-10 22:00:00 +02:00
2018-01-15 22:00:00 +01:00
if( NET_CompareBaseAdr( drop->netchan.remote_address, host.rd.address ))
2015-12-04 22:00:00 +01:00
SV_EndRedirect();
2011-04-05 22:00:00 +02:00
// throw away any residual garbage in the channel.
2010-10-07 22:00:00 +02:00
Netchan_Clear( &drop->netchan );
2015-12-04 22:00:00 +01:00
// clean client data on disconnect
2016-11-17 22:00:00 +01:00
memset( drop->userinfo, 0, MAX_INFO_STRING );
memset( drop->physinfo, 0, MAX_INFO_STRING );
2015-12-04 22:00:00 +01:00
drop->edict->v.frags = 0;
2010-10-09 22:00:00 +02:00
// send notification to all other clients
SV_FullClientUpdate( drop, &sv.reliable_datagram );
2010-02-07 22:00:00 +01:00
2008-07-12 22:00:00 +02:00
// if this was the last client on the server, send a heartbeat
// to the master so it is known the server is empty
// send a heartbeat now so the master will get up to date info
// if there is already a slot for this ip, reuse it
2017-02-12 22:00:00 +01:00
for( i = 0; i < svs.maxclients; i++ )
2008-07-12 22:00:00 +02:00
{
if( svs.clients[i].state >= cs_connected )
break;
}
2010-06-20 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
if( i == svs.maxclients )
2010-06-20 22:00:00 +02:00
svs.last_heartbeat = MAX_HEARTBEAT;
2008-07-12 22:00:00 +02:00
}
/*
==============================================================================
SVC COMMAND REDIRECT
==============================================================================
*/
void SV_BeginRedirect( netadr_t adr, int target, char *buffer, int buffersize, void (*flush))
{
if( !target || !buffer || !buffersize || !flush )
return;
host.rd.target = target;
host.rd.buffer = buffer;
host.rd.buffersize = buffersize;
host.rd.flush = flush;
host.rd.address = adr;
host.rd.buffer[0] = 0;
}
void SV_FlushRedirect( netadr_t adr, int dest, char *buf )
{
2016-11-21 22:00:00 +01:00
if( svs.currentPlayer && FBitSet( svs.currentPlayer->flags, FCL_FAKECLIENT ))
2009-06-24 22:00:00 +02:00
return;
2008-07-12 22:00:00 +02:00
switch( dest )
{
case RD_PACKET:
Netchan_OutOfBandPrint( NS_SERVER, adr, "print\n%s", buf );
break;
case RD_CLIENT:
2011-04-08 22:00:00 +02:00
if( !svs.currentPlayer ) return; // client not set
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &svs.currentPlayer->netchan.message, svc_print );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &svs.currentPlayer->netchan.message, PRINT_HIGH );
MSG_WriteString( &svs.currentPlayer->netchan.message, buf );
2008-07-12 22:00:00 +02:00
break;
case RD_NONE:
MsgDev( D_ERROR, "SV_FlushRedirect: %s: invalid destination\n", NET_AdrToString( adr ));
break;
}
}
void SV_EndRedirect( void )
{
2015-12-04 22:00:00 +01:00
if( host.rd.flush )
host.rd.flush( host.rd.address, host.rd.target, host.rd.buffer );
2008-07-12 22:00:00 +02:00
host.rd.target = 0;
host.rd.buffer = NULL;
host.rd.buffersize = 0;
host.rd.flush = NULL;
}
2010-10-19 22:00:00 +02:00
/*
===============
SV_GetClientIDString
Returns a pointer to a static char for most likely only printing.
===============
*/
const char *SV_GetClientIDString( sv_client_t *cl )
{
static char result[CS_SIZE];
result[0] = '\0';
if( !cl )
{
MsgDev( D_ERROR, "SV_GetClientIDString: invalid client\n" );
return result;
}
if( cl->authentication_method == 0 )
{
// probably some old compatibility code.
2011-03-09 22:00:00 +01:00
Q_snprintf( result, sizeof( result ), "%010lu", cl->WonID );
2010-10-19 22:00:00 +02:00
}
else if( cl->authentication_method == 2 )
{
if( NET_IsLocalAddress( cl->netchan.remote_address ))
{
2011-03-09 22:00:00 +01:00
Q_strncpy( result, "VALVE_ID_LOOPBACK", sizeof( result ));
2010-10-19 22:00:00 +02:00
}
else if( cl->WonID == 0 )
{
2011-03-09 22:00:00 +01:00
Q_strncpy( result, "VALVE_ID_PENDING", sizeof( result ));
2010-10-19 22:00:00 +02:00
}
else
{
2011-03-09 22:00:00 +01:00
Q_snprintf( result, sizeof( result ), "VALVE_%010lu", cl->WonID );
2010-10-19 22:00:00 +02:00
}
}
2011-03-09 22:00:00 +01:00
else Q_strncpy( result, "UNKNOWN", sizeof( result ));
2010-10-19 22:00:00 +02:00
return result;
}
2008-07-12 22:00:00 +02:00
/*
================
SV_Ack
================
*/
void SV_Ack( netadr_t from )
{
2009-11-02 22:00:00 +01:00
Msg( "ping %s\n", NET_AdrToString( from ));
2008-07-12 22:00:00 +02:00
}
/*
================
SV_Info
Responds with short info for broadcast scans
The second parameter should be the current protocol version number.
================
*/
void SV_Info( netadr_t from )
{
2010-01-30 22:00:00 +01:00
char string[MAX_INFO_STRING];
2008-07-12 22:00:00 +02:00
int i, count = 0;
int version;
// ignore in single player
2017-02-12 22:00:00 +01:00
if( svs.maxclients == 1 || !svs.initialized )
2010-01-30 22:00:00 +01:00
return;
2008-07-12 22:00:00 +02:00
2011-03-09 22:00:00 +01:00
version = Q_atoi( Cmd_Argv( 1 ));
2010-01-30 22:00:00 +01:00
string[0] = '\0';
2008-07-12 22:00:00 +02:00
if( version != PROTOCOL_VERSION )
2009-09-25 22:00:00 +02:00
{
2011-03-09 22:00:00 +01:00
Q_snprintf( string, sizeof( string ), "%s: wrong version\n", hostname->string );
2009-09-25 22:00:00 +02:00
}
2008-07-12 22:00:00 +02:00
else
{
2017-02-12 22:00:00 +01:00
for( i = 0; i < svs.maxclients; i++ )
2008-07-12 22:00:00 +02:00
if( svs.clients[i].state >= cs_connected )
count++;
2010-01-30 22:00:00 +01:00
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( string, "host", hostname->string, MAX_INFO_STRING );
Info_SetValueForKey( string, "map", sv.name, MAX_INFO_STRING );
Info_SetValueForKey( string, "dm", va( "%i", (int)svgame.globals->deathmatch ), MAX_INFO_STRING );
Info_SetValueForKey( string, "team", va( "%i", (int)svgame.globals->teamplay ), MAX_INFO_STRING );
Info_SetValueForKey( string, "coop", va( "%i", (int)svgame.globals->coop ), MAX_INFO_STRING );
Info_SetValueForKey( string, "numcl", va( "%i", count ), MAX_INFO_STRING );
Info_SetValueForKey( string, "maxcl", va( "%i", svs.maxclients ), MAX_INFO_STRING );
Info_SetValueForKey( string, "gamedir", GI->gamefolder, MAX_INFO_STRING );
2008-07-12 22:00:00 +02:00
}
2012-12-16 21:00:00 +01:00
2008-07-12 22:00:00 +02:00
Netchan_OutOfBandPrint( NS_SERVER, from, "info\n%s", string );
}
2011-09-28 22:00:00 +02:00
/*
================
SV_BuildNetAnswer
Responds with long info for local and broadcast requests
================
*/
void SV_BuildNetAnswer( netadr_t from )
{
2011-10-09 22:00:00 +02:00
char string[MAX_INFO_STRING], answer[512];
2011-09-28 22:00:00 +02:00
int version, context, type;
int i, count = 0;
// ignore in single player
2017-02-12 22:00:00 +01:00
if( svs.maxclients == 1 || !svs.initialized )
2011-09-28 22:00:00 +02:00
return;
version = Q_atoi( Cmd_Argv( 1 ));
context = Q_atoi( Cmd_Argv( 2 ));
type = Q_atoi( Cmd_Argv( 3 ));
2011-10-09 22:00:00 +02:00
if( version != PROTOCOL_VERSION )
2016-11-15 22:00:00 +01:00
{
// handle the unsupported protocol
string[0] = '\0';
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( string, "neterror", "protocol", MAX_INFO_STRING );
2011-10-09 22:00:00 +02:00
2016-11-15 22:00:00 +01:00
// send error unsupported protocol
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string );
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
return;
}
2016-11-21 22:00:00 +01:00
2011-10-09 22:00:00 +02:00
if( type == NETAPI_REQUEST_PING )
{
2016-11-15 22:00:00 +01:00
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, "" );
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
2011-10-09 22:00:00 +02:00
}
else if( type == NETAPI_REQUEST_RULES )
{
// send serverinfo
2017-02-12 22:00:00 +01:00
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, svs.serverinfo );
2016-11-15 22:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
2011-10-09 22:00:00 +02:00
}
else if( type == NETAPI_REQUEST_PLAYERS )
{
string[0] = '\0';
2017-02-12 22:00:00 +01:00
for( i = 0; i < svs.maxclients; i++ )
2011-10-09 22:00:00 +02:00
{
if( svs.clients[i].state >= cs_connected )
{
edict_t *ed = svs.clients[i].edict;
2017-02-05 22:00:00 +01:00
float time = host.realtime - svs.clients[i].connection_started;
2016-08-13 23:00:00 +02:00
Q_strncat( string, va( "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, (int)ed->v.frags, time ), sizeof( string ));
2011-10-09 22:00:00 +02:00
count++;
}
}
// send playernames
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string );
2016-11-15 22:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
2011-10-09 22:00:00 +02:00
}
else if( type == NETAPI_REQUEST_DETAILS )
{
2017-02-12 22:00:00 +01:00
for( i = 0; i < svs.maxclients; i++ )
2011-10-09 22:00:00 +02:00
if( svs.clients[i].state >= cs_connected )
count++;
string[0] = '\0';
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( string, "hostname", hostname->string, MAX_INFO_STRING );
Info_SetValueForKey( string, "gamedir", GI->gamefolder, MAX_INFO_STRING );
Info_SetValueForKey( string, "current", va( "%i", count ), MAX_INFO_STRING );
Info_SetValueForKey( string, "max", va( "%i", svs.maxclients ), MAX_INFO_STRING );
Info_SetValueForKey( string, "map", sv.name, MAX_INFO_STRING );
2011-10-09 22:00:00 +02:00
// send serverinfo
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string );
2016-11-15 22:00:00 +01:00
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
}
else
{
string[0] = '\0';
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( string, "neterror", "undefined", MAX_INFO_STRING );
2016-11-15 22:00:00 +01:00
// send error undefined request type
Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string );
Netchan_OutOfBandPrint( NS_SERVER, from, answer );
2011-10-09 22:00:00 +02:00
}
2011-09-28 22:00:00 +02:00
}
2008-07-12 22:00:00 +02:00
/*
================
SV_Ping
Just responds with an acknowledgement
================
*/
void SV_Ping( netadr_t from )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "ack" );
}
2012-12-16 21:00:00 +01:00
/*
================
Rcon_Validate
================
*/
2010-10-26 22:00:00 +02:00
qboolean Rcon_Validate( void )
2008-07-12 22:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
if( !Q_strlen( rcon_password.string ))
2008-07-12 22:00:00 +02:00
return false;
2017-02-12 22:00:00 +01:00
if( Q_strcmp( Cmd_Argv( 1 ), rcon_password.string ))
2008-07-12 22:00:00 +02:00
return false;
return true;
}
/*
===============
SV_RemoteCommand
A client issued an rcon command.
Shift down the remaining args
Redirect all printfs
===============
*/
2010-08-06 22:00:00 +02:00
void SV_RemoteCommand( netadr_t from, sizebuf_t *msg )
2008-07-12 22:00:00 +02:00
{
2011-04-05 22:00:00 +02:00
static char outputbuf[2048];
2016-11-21 22:00:00 +01:00
char remaining[1024];
2008-07-12 22:00:00 +02:00
int i;
2016-11-14 22:00:00 +01:00
MsgDev( D_INFO, "Rcon from %s:\n%s\n", NET_AdrToString( from ), MSG_GetData( msg ) + 4 );
2011-04-05 22:00:00 +02:00
SV_BeginRedirect( from, RD_PACKET, outputbuf, sizeof( outputbuf ) - 16, SV_FlushRedirect );
2008-07-12 22:00:00 +02:00
2011-04-08 22:00:00 +02:00
if( Rcon_Validate( ))
2008-07-12 22:00:00 +02:00
{
remaining[0] = 0;
for( i = 2; i < Cmd_Argc(); i++ )
{
2011-03-09 22:00:00 +01:00
Q_strcat( remaining, Cmd_Argv( i ));
Q_strcat( remaining, " " );
2008-07-12 22:00:00 +02:00
}
2016-11-17 22:00:00 +01:00
Cmd_ExecuteString( remaining );
2008-07-12 22:00:00 +02:00
}
2011-04-08 22:00:00 +02:00
else MsgDev( D_ERROR, "Bad rcon_password.\n" );
2008-07-12 22:00:00 +02:00
SV_EndRedirect();
}
2010-10-14 22:00:00 +02:00
/*
===================
SV_CalcPing
recalc ping on current client
===================
*/
int SV_CalcPing( sv_client_t *cl )
{
float ping = 0;
2017-02-05 22:00:00 +01:00
int i, count;
int idx, back;
2010-10-14 22:00:00 +02:00
client_frame_t *frame;
// bots don't have a real ping
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_FAKECLIENT ) || !cl->frames )
2010-10-14 22:00:00 +02:00
return 5;
2015-12-26 22:00:00 +01:00
if( SV_UPDATE_BACKUP <= 31 )
2015-12-07 22:00:00 +01:00
{
back = SV_UPDATE_BACKUP / 2;
2015-12-26 22:00:00 +01:00
if( back <= 0 ) return 0;
2015-12-07 22:00:00 +01:00
}
2015-12-26 22:00:00 +01:00
else back = 16;
2015-12-07 22:00:00 +01:00
2017-02-05 22:00:00 +01:00
count = 0;
2015-12-07 22:00:00 +01:00
for( i = 0; i < back; i++ )
2010-10-14 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
idx = cl->netchan.incoming_acknowledged + ~i;
frame = &cl->frames[idx & SV_UPDATE_MASK];
2010-10-14 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
if( frame->ping_time > 0.0f )
2010-10-14 22:00:00 +02:00
{
2015-12-26 22:00:00 +01:00
ping += frame->ping_time;
2010-10-14 22:00:00 +02:00
count++;
}
}
2017-02-05 22:00:00 +01:00
if( count > 0 )
return (( ping / count ) * 1000.0f );
return 0;
2010-10-14 22:00:00 +02:00
}
/*
===================
SV_EstablishTimeBase
Finangles latency and the like.
===================
*/
2015-12-26 22:00:00 +01:00
void SV_EstablishTimeBase( sv_client_t *cl, usercmd_t *cmds, int dropped, int numbackup, int numcmds )
2010-10-14 22:00:00 +02:00
{
2015-12-26 22:00:00 +01:00
double runcmd_time = 0.0;
2017-08-15 23:00:00 +02:00
int i, cmdnum = dropped;
2010-10-14 22:00:00 +02:00
2015-12-26 22:00:00 +01:00
if( dropped < 24 )
2010-10-14 22:00:00 +02:00
{
2017-08-15 23:00:00 +02:00
while( dropped > numbackup )
2010-10-14 22:00:00 +02:00
{
2017-08-15 23:00:00 +02:00
runcmd_time = (double)cl->lastcmd.msec / 1000.0;
dropped--;
2010-10-14 22:00:00 +02:00
}
2017-08-15 23:00:00 +02:00
while( dropped > 0 )
{
cmdnum = dropped + numcmds - 1;
runcmd_time += (double)cmds[cmdnum].msec / 1000.0;
dropped--;
}
2015-12-26 22:00:00 +01:00
}
2010-10-14 22:00:00 +02:00
2017-08-15 23:00:00 +02:00
for( i = numcmds - 1; i >= 0; i-- )
runcmd_time += cmds[i].msec / 1000.0;
2015-12-26 22:00:00 +01:00
2017-08-15 23:00:00 +02:00
cl->timebase = sv.time + sv.frametime - runcmd_time;
2015-12-26 22:00:00 +01:00
}
/*
===================
SV_CalcClientTime
compute latency for client
===================
*/
float SV_CalcClientTime( sv_client_t *cl )
{
float minping, maxping;
float ping = 0.0f;
int i, count = 0;
int backtrack;
2017-02-12 22:00:00 +01:00
backtrack = (int)sv_unlagsamples.value;
2015-12-26 22:00:00 +01:00
if( backtrack < 1 ) backtrack = 1;
if( backtrack >= (SV_UPDATE_BACKUP <= 16 ? SV_UPDATE_BACKUP : 16 ))
backtrack = ( SV_UPDATE_BACKUP <= 16 ? SV_UPDATE_BACKUP : 16 );
if( backtrack <= 0 )
return 0.0f;
for( i = 0; i < backtrack; i++ )
{
client_frame_t *frame = &cl->frames[SV_UPDATE_MASK & (cl->netchan.incoming_acknowledged - i)];
if( frame->ping_time <= 0.0f )
continue;
ping += frame->ping_time;
count++;
2010-10-14 22:00:00 +02:00
}
2015-12-26 22:00:00 +01:00
if( !count ) return 0.0f;
minping = 9999.0f;
maxping = -9999.0f;
ping /= count;
for( i = 0; i < ( SV_UPDATE_BACKUP <= 4 ? SV_UPDATE_BACKUP : 4 ); i++ )
2010-10-14 22:00:00 +02:00
{
2015-12-26 22:00:00 +01:00
client_frame_t *frame = &cl->frames[SV_UPDATE_MASK & (cl->netchan.incoming_acknowledged - i)];
if( frame->ping_time <= 0.0f )
continue;
if( frame->ping_time < minping )
minping = frame->ping_time;
if( frame->ping_time > maxping )
maxping = frame->ping_time;
2010-10-14 22:00:00 +02:00
}
2015-12-26 22:00:00 +01:00
if( maxping < minping || fabs( maxping - minping ) <= 0.2f )
return ping;
return 0.0f;
2010-10-14 22:00:00 +02:00
}
2009-12-04 22:00:00 +01:00
/*
===================
SV_FullClientUpdate
2010-08-04 22:00:00 +02:00
Writes all update values to a bitbuf
2009-12-04 22:00:00 +01:00
===================
*/
2010-08-06 22:00:00 +02:00
void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg )
2009-12-04 22:00:00 +01:00
{
char info[MAX_INFO_STRING];
2011-07-22 22:00:00 +02:00
int i;
2016-11-23 22:00:00 +01:00
// process userinfo before updating
SV_UserinfoChanged( cl, cl->userinfo );
2009-12-04 22:00:00 +01:00
i = cl - svs.clients;
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( msg, svc_updateuserinfo );
2016-11-14 22:00:00 +01:00
MSG_WriteUBitLong( msg, i, MAX_CLIENT_BITS );
2017-02-05 22:00:00 +01:00
MSG_WriteLong( msg, cl->userid );
2009-12-04 22:00:00 +01:00
if( cl->name[0] )
{
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( msg, 1 );
2009-12-04 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
Q_strncpy( info, cl->userinfo, sizeof( info ));
2009-12-04 22:00:00 +01:00
// remove server passwords, etc.
Info_RemovePrefixedKeys( info, '_' );
2016-11-14 22:00:00 +01:00
MSG_WriteString( msg, info );
2009-12-04 22:00:00 +01:00
}
2016-11-14 22:00:00 +01:00
else MSG_WriteOneBit( msg, 0 );
2009-12-04 22:00:00 +01:00
}
2012-12-16 21:00:00 +01:00
/*
===================
SV_RefreshUserinfo
===================
*/
2010-02-02 22:00:00 +01:00
void SV_RefreshUserinfo( void )
{
sv_client_t *cl;
2016-11-21 22:00:00 +01:00
int i;
2010-02-09 22:00:00 +01:00
2017-02-12 22:00:00 +01:00
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
2016-11-21 22:00:00 +01:00
{
if( cl->state >= cs_connected )
SetBits( cl->flags, FCL_RESEND_USERINFO );
}
2010-02-07 22:00:00 +01:00
}
/*
===================
2010-10-09 22:00:00 +02:00
SV_FullUpdateMovevars
2010-02-07 22:00:00 +01:00
this is send all movevars values when client connected
otherwise see code SV_UpdateMovevars()
===================
*/
2010-10-09 22:00:00 +02:00
void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg )
2010-02-07 22:00:00 +01:00
{
movevars_t nullmovevars;
2016-11-17 22:00:00 +01:00
memset( &nullmovevars, 0, sizeof( nullmovevars ));
2010-08-06 22:00:00 +02:00
MSG_WriteDeltaMovevars( msg, &nullmovevars, &svgame.movevars );
2010-02-02 22:00:00 +01:00
}
2010-02-07 22:00:00 +01:00
2010-10-14 22:00:00 +02:00
/*
===================
SV_ShouldUpdatePing
2017-02-05 22:00:00 +01:00
determine should we recalculate
ping times now
2010-10-14 22:00:00 +02:00
===================
*/
2010-10-26 22:00:00 +02:00
qboolean SV_ShouldUpdatePing( sv_client_t *cl )
2010-10-14 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
2010-10-14 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
if( host.realtime < cl->next_checkpingtime )
return false;
2010-10-14 22:00:00 +02:00
cl->next_checkpingtime = host.realtime + 2.0;
return true;
}
2012-12-16 21:00:00 +01:00
2017-02-05 22:00:00 +01:00
// they are viewing the scoreboard. Send them pings.
return FBitSet( cl->lastcmd.buttons, IN_SCORE ) ? true : false;
2010-10-14 22:00:00 +02:00
}
2012-12-16 21:00:00 +01:00
/*
===================
SV_IsPlayerIndex
===================
*/
2010-10-26 22:00:00 +02:00
qboolean SV_IsPlayerIndex( int idx )
2010-10-20 22:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
if( idx > 0 && idx <= svs.maxclients )
2010-10-20 22:00:00 +02:00
return true;
return false;
}
2010-10-14 22:00:00 +02:00
/*
===================
SV_GetPlayerStats
This function and its static vars track some of the networking
conditions. I haven't bothered to trace it beyond that, because
this fucntion sucks pretty badly.
===================
*/
void SV_GetPlayerStats( sv_client_t *cl, int *ping, int *packet_loss )
{
static int last_ping[MAX_CLIENTS];
static int last_loss[MAX_CLIENTS];
int i;
i = cl - svs.clients;
2017-02-05 22:00:00 +01:00
if( host.realtime >= cl->next_checkpingtime )
2010-10-14 22:00:00 +02:00
{
cl->next_checkpingtime = host.realtime + 2.0;
last_ping[i] = SV_CalcPing( cl );
last_loss[i] = cl->packet_loss;
}
if( ping ) *ping = last_ping[i];
if( packet_loss ) *packet_loss = last_loss[i];
}
2008-07-12 22:00:00 +02:00
/*
===========
PutClientInServer
Called when a player connects to a server or respawns in
a deathmatch.
============
*/
2016-11-21 22:00:00 +01:00
void SV_PutClientInServer( sv_client_t *cl )
2008-07-12 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
edict_t *ent = cl->edict;
2008-12-16 22:00:00 +01:00
2016-11-21 22:00:00 +01:00
// now client is spawned
cl->state = cs_spawned;
2008-07-12 22:00:00 +02:00
2010-03-24 22:00:00 +01:00
if( !sv.loadgame )
2008-07-12 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
if( Q_atoi( Info_ValueForKey( cl->userinfo, "hltv" )))
SetBits( cl->flags, FCL_HLTV_PROXY );
2012-03-24 21:00:00 +01:00
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
SetBits( ent->v.flags, FL_PROXY );
2011-04-08 22:00:00 +02:00
else ent->v.flags = 0;
2011-04-05 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
ent->v.netname = MAKE_STRING( cl->name );
2017-02-07 22:00:00 +01:00
ent->v.colormap = NUM_FOR_EDICT( ent ); // ???
2010-06-28 22:00:00 +02:00
2012-03-24 21:00:00 +01:00
// fisrt entering
svgame.globals->time = sv.time;
svgame.dllFuncs.pfnClientPutInServer( ent );
2011-02-22 22:00:00 +01:00
2012-03-24 21:00:00 +01:00
if( sv.background ) // don't attack player in background mode
2016-11-21 22:00:00 +01:00
SetBits( ent->v.flags, FL_GODMODE|FL_NOTARGET );
2010-10-28 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
cl->pViewEntity = NULL; // reset pViewEntity
2010-11-22 22:00:00 +01:00
if( svgame.globals->cdAudioTrack )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-21 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, va( "cd loop %3d\n", svgame.globals->cdAudioTrack ));
2010-11-22 22:00:00 +01:00
svgame.globals->cdAudioTrack = 0;
}
2008-07-12 22:00:00 +02:00
}
2009-01-25 22:00:00 +01:00
else
{
2010-04-12 22:00:00 +02:00
// NOTE: we needs to setup angles on restore here
if( ent->v.fixangle == 1 )
2009-12-01 22:00:00 +01:00
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_setangle );
2016-11-21 22:00:00 +01:00
MSG_WriteBitAngle( &cl->netchan.message, ent->v.angles[0], 16 );
MSG_WriteBitAngle( &cl->netchan.message, ent->v.angles[1], 16 );
MSG_WriteBitAngle( &cl->netchan.message, ent->v.angles[2], 16 );
2010-04-12 22:00:00 +02:00
ent->v.fixangle = 0;
2009-12-01 22:00:00 +01:00
}
2008-07-12 22:00:00 +02:00
2011-01-09 22:00:00 +01:00
// reset weaponanim
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_weaponanim );
2016-11-21 22:00:00 +01:00
MSG_WriteByte( &cl->netchan.message, 0 );
MSG_WriteByte( &cl->netchan.message, 0 );
2011-01-09 22:00:00 +01:00
2010-10-28 22:00:00 +02:00
// trigger_camera restored here
2010-10-31 22:00:00 +01:00
if( sv.viewentity > 0 && sv.viewentity < GI->max_edicts )
2016-11-21 22:00:00 +01:00
cl->pViewEntity = EDICT_NUM( sv.viewentity );
else cl->pViewEntity = NULL;
2010-10-28 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
2017-11-12 22:00:00 +01:00
// enable dev-mode to prevent crash cheat-protecting from Invasion mod
if( FBitSet( ent->v.flags, FL_GODMODE|FL_NOTARGET ) && !Q_stricmp( GI->gamefolder, "invasion" ))
SV_ExecuteClientCommand( cl, "test\n" );
2016-12-02 22:00:00 +01:00
// refresh the userinfo and movevars
// NOTE: because movevars can be changed during the connection process
SetBits( cl->flags, FCL_RESEND_USERINFO|FCL_RESEND_MOVEVARS );
2016-11-23 22:00:00 +01:00
2010-10-14 22:00:00 +02:00
// reset client times
2017-02-05 22:00:00 +01:00
cl->connecttime = 0.0;
cl->ignorecmdtime = 0.0;
cl->cmdtime = 0.0;
2010-10-14 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
if( !FBitSet( cl->flags, FCL_FAKECLIENT ))
2009-09-18 22:00:00 +02:00
{
2016-12-02 22:00:00 +01:00
sv_client_t *cur;
int i, viewEnt;
2010-10-28 22:00:00 +02:00
2017-03-02 22:00:00 +01:00
// NOTE: it's will be fragmented automatically in right ordering
2017-03-21 22:00:00 +01:00
MSG_WriteBits( &cl->netchan.message, MSG_GetData( &sv.signon ), MSG_GetNumBitsWritten( &sv.signon ));
2017-03-02 22:00:00 +01:00
2016-11-21 22:00:00 +01:00
if( cl->pViewEntity )
viewEnt = NUM_FOR_EDICT( cl->pViewEntity );
else viewEnt = NUM_FOR_EDICT( cl->edict );
2010-06-28 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_setview );
2016-11-21 22:00:00 +01:00
MSG_WriteWord( &cl->netchan.message, viewEnt );
2016-12-02 22:00:00 +01:00
2017-03-16 22:00:00 +01:00
// time to send signon buffer
2017-03-21 22:00:00 +01:00
// Netchan_CreateFragments( &cl->netchan, &sv.signon );
// Netchan_FragSend( &cl->netchan );
2017-03-16 22:00:00 +01:00
2016-12-02 22:00:00 +01:00
// collect the info about all the players and send to me
2017-02-12 22:00:00 +01:00
for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ )
2016-12-02 22:00:00 +01:00
{
2017-02-05 22:00:00 +01:00
if( !cur->edict || cur->state != cs_spawned )
continue; // not in game yet
2016-12-02 22:00:00 +01:00
SV_FullClientUpdate( cur, &cl->netchan.message );
}
2009-09-18 22:00:00 +02:00
}
2009-09-28 22:00:00 +02:00
// clear any temp states
2012-08-29 22:00:00 +02:00
sv.changelevel = false;
2009-09-28 22:00:00 +02:00
sv.loadgame = false;
2010-03-28 22:00:00 +02:00
sv.paused = false;
2009-09-28 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
if( svs.maxclients == 1 ) // singleplayer profiler
2010-06-17 22:00:00 +02:00
MsgDev( D_INFO, "level loaded at %.2f sec\n", Sys_DoubleTime() - svs.timestart );
2008-07-12 22:00:00 +02:00
}
2017-01-17 22:00:00 +01:00
/*
===========
SV_UpdateClientView
Resend the client viewentity (used for demos)
============
*/
void SV_UpdateClientView( sv_client_t *cl )
{
int viewEnt;
if( cl->pViewEntity )
viewEnt = NUM_FOR_EDICT( cl->pViewEntity );
else viewEnt = NUM_FOR_EDICT( cl->edict );
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_setview );
MSG_WriteWord( &cl->netchan.message, viewEnt );
2017-01-17 22:00:00 +01:00
}
2009-11-10 22:00:00 +01:00
/*
==================
SV_TogglePause
==================
*/
void SV_TogglePause( const char *msg )
{
2011-02-22 22:00:00 +01:00
if( sv.background ) return;
2009-11-10 22:00:00 +01:00
sv.paused ^= 1;
2016-11-21 22:00:00 +01:00
if( msg ) SV_BroadcastPrintf( NULL, PRINT_HIGH, "%s", msg );
2009-11-10 22:00:00 +01:00
// send notification to all clients
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &sv.reliable_datagram, svc_setpause );
2016-11-14 22:00:00 +01:00
MSG_WriteOneBit( &sv.reliable_datagram, sv.paused );
2009-11-10 22:00:00 +01:00
}
2008-07-12 22:00:00 +02:00
/*
============================================================
CLIENT COMMAND EXECUTION
============================================================
*/
/*
================
SV_New_f
Sends the first message from the server to a connected client.
This will be sent on the initial connection and upon each server load.
================
*/
void SV_New_f( sv_client_t *cl )
{
2017-03-08 22:00:00 +01:00
int i, playernum;
2008-07-12 22:00:00 +02:00
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'new' is not valid from the console\n" );
2008-07-12 22:00:00 +02:00
return;
}
playernum = cl - svs.clients;
2008-07-15 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
// send the serverdata
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_serverdata );
2016-11-14 22:00:00 +01:00
MSG_WriteLong( &cl->netchan.message, PROTOCOL_VERSION );
MSG_WriteLong( &cl->netchan.message, svs.spawncount );
MSG_WriteLong( &cl->netchan.message, sv.checksum );
MSG_WriteByte( &cl->netchan.message, playernum );
MSG_WriteByte( &cl->netchan.message, svgame.globals->maxClients );
MSG_WriteWord( &cl->netchan.message, svgame.globals->maxEntities );
2018-02-02 22:00:00 +01:00
MSG_WriteWord( &cl->netchan.message, MAX_MODELS );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, sv.name );
MSG_WriteString( &cl->netchan.message, STRING( EDICT_NUM( 0 )->v.message )); // Map Message
MSG_WriteOneBit( &cl->netchan.message, sv.background ); // tell client about background map
MSG_WriteString( &cl->netchan.message, GI->gamefolder );
MSG_WriteLong( &cl->netchan.message, host.features );
2008-07-12 22:00:00 +02:00
2017-03-08 22:00:00 +01:00
// send the player hulls
for( i = 0; i < MAX_MAP_HULLS * 3; i++ )
{
MSG_WriteChar( &cl->netchan.message, host.player_mins[i/3][i%3] );
MSG_WriteChar( &cl->netchan.message, host.player_maxs[i/3][i%3] );
}
2017-02-12 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
MSG_WriteString( &cl->netchan.message, va( "fullserverinfo \"%s\"\n", SV_Serverinfo( )));
2008-07-12 22:00:00 +02:00
// game server
if( sv.state == ss_active )
{
// set up the entity for the client
2016-11-21 22:00:00 +01:00
cl->edict = EDICT_NUM( playernum + 1 );
2008-07-12 22:00:00 +02:00
2011-09-03 22:00:00 +02:00
// NOTE: custom resources download is disabled until is done
2017-02-12 22:00:00 +01:00
if( /*svs.maxclients ==*/ 1 )
2011-07-07 22:00:00 +02:00
{
2016-11-17 22:00:00 +01:00
memset( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
2011-07-07 22:00:00 +02:00
// begin fetching modellist
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, va( "cmd modellist %i %i\n", svs.spawncount, 0 ));
2011-07-07 22:00:00 +02:00
}
else
{
// request resource list
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, va( "cmd getresourelist\n" ));
2011-07-07 22:00:00 +02:00
}
2008-07-12 22:00:00 +02:00
}
}
2011-07-07 22:00:00 +02:00
/*
==================
SV_ContinueLoading_f
==================
*/
void SV_ContinueLoading_f( sv_client_t *cl )
{
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'continueloading' is not valid from the console\n" );
2011-07-07 22:00:00 +02:00
return;
}
2016-11-17 22:00:00 +01:00
memset( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
2011-07-07 22:00:00 +02:00
// begin fetching modellist
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, va( "cmd modellist %i %i\n", svs.spawncount, 0 ));
2011-07-07 22:00:00 +02:00
}
/*
=======================
SV_SendResourceList
NOTE: Sending the list of cached resources.
g-cont. this is fucking big message!!! i've rewriting this code
=======================
*/
void SV_SendResourceList_f( sv_client_t *cl )
{
int index = 0;
int rescount = 0;
2012-12-16 21:00:00 +01:00
resourcelist_t reslist; // g-cont. what about stack???
2011-07-07 22:00:00 +02:00
size_t msg_size;
2016-11-17 22:00:00 +01:00
memset( &reslist, 0, sizeof( resourcelist_t ));
2011-07-07 22:00:00 +02:00
reslist.restype[rescount] = t_world; // terminator
Q_strcpy( reslist.resnames[rescount], "NULL" );
rescount++;
for( index = 1; index < MAX_MODELS && sv.model_precache[index][0]; index++ )
{
if( sv.model_precache[index][0] == '*' ) // internal bmodel
continue;
reslist.restype[rescount] = t_model;
Q_strcpy( reslist.resnames[rescount], sv.model_precache[index] );
rescount++;
}
for( index = 1; index < MAX_SOUNDS && sv.sound_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_sound;
Q_strcpy( reslist.resnames[rescount], sv.sound_precache[index] );
rescount++;
}
for( index = 1; index < MAX_EVENTS && sv.event_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_eventscript;
Q_strcpy( reslist.resnames[rescount], sv.event_precache[index] );
rescount++;
}
for( index = 1; index < MAX_CUSTOM && sv.files_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_generic;
Q_strcpy( reslist.resnames[rescount], sv.files_precache[index] );
rescount++;
}
2016-11-14 22:00:00 +01:00
msg_size = MSG_GetRealBytesWritten( &cl->netchan.message ); // start
2011-07-07 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_resourcelist );
2016-11-14 22:00:00 +01:00
MSG_WriteWord( &cl->netchan.message, rescount );
2011-07-07 22:00:00 +02:00
for( index = 1; index < rescount; index++ )
{
2016-11-14 22:00:00 +01:00
MSG_WriteWord( &cl->netchan.message, reslist.restype[index] );
MSG_WriteString( &cl->netchan.message, reslist.resnames[index] );
2011-07-07 22:00:00 +02:00
}
Msg( "Count res: %d\n", rescount );
2016-11-14 22:00:00 +01:00
Msg( "ResList size: %s\n", Q_memprint( MSG_GetRealBytesWritten( &cl->netchan.message ) - msg_size ));
2011-07-07 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
/*
==================
SV_WriteModels_f
==================
*/
void SV_WriteModels_f( sv_client_t *cl )
{
int start;
string cmd;
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'modellist' is not valid from the console\n" );
2010-10-26 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-10-26 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'modellist' from different level\n" );
2010-10-26 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2010-10-26 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < MAX_MODELS )
2010-10-26 22:00:00 +02:00
{
if( sv.model_precache[start][0] )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_modelindex );
2016-11-14 22:00:00 +01:00
MSG_WriteUBitLong( &cl->netchan.message, start, MAX_MODEL_BITS );
MSG_WriteString( &cl->netchan.message, sv.model_precache[start] );
2010-10-26 22:00:00 +02:00
}
start++;
}
2011-03-09 22:00:00 +01:00
if( start == MAX_MODELS ) Q_snprintf( cmd, MAX_STRING, "cmd soundlist %i %i\n", svs.spawncount, 0 );
else Q_snprintf( cmd, MAX_STRING, "cmd modellist %i %i\n", svs.spawncount, start );
2010-10-26 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2010-10-26 22:00:00 +02:00
}
/*
==================
SV_WriteSounds_f
==================
*/
void SV_WriteSounds_f( sv_client_t *cl )
{
int start;
string cmd;
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'soundlist' is not valid from the console\n" );
2010-10-26 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-10-26 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'soundlist' from different level\n" );
2010-10-26 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2010-10-26 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < MAX_SOUNDS )
2010-10-26 22:00:00 +02:00
{
if( sv.sound_precache[start][0] )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_soundindex );
2016-11-14 22:00:00 +01:00
MSG_WriteUBitLong( &cl->netchan.message, start, MAX_SOUND_BITS );
MSG_WriteString( &cl->netchan.message, sv.sound_precache[start] );
2010-10-26 22:00:00 +02:00
}
start++;
}
2011-03-09 22:00:00 +01:00
if( start == MAX_SOUNDS ) Q_snprintf( cmd, MAX_STRING, "cmd eventlist %i %i\n", svs.spawncount, 0 );
else Q_snprintf( cmd, MAX_STRING, "cmd soundlist %i %i\n", svs.spawncount, start );
2010-10-26 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2010-10-26 22:00:00 +02:00
}
2010-10-28 22:00:00 +02:00
/*
==================
SV_WriteEvents_f
==================
*/
void SV_WriteEvents_f( sv_client_t *cl )
{
int start;
string cmd;
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'eventlist' is not valid from the console\n" );
2010-10-28 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-10-28 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'eventlist' from different level\n" );
2010-10-28 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2010-10-28 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < MAX_EVENTS )
2010-10-28 22:00:00 +02:00
{
if( sv.event_precache[start][0] )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_eventindex );
2016-11-14 22:00:00 +01:00
MSG_WriteUBitLong( &cl->netchan.message, start, MAX_EVENT_BITS );
MSG_WriteString( &cl->netchan.message, sv.event_precache[start] );
2010-10-28 22:00:00 +02:00
}
start++;
}
2011-03-09 22:00:00 +01:00
if( start == MAX_EVENTS ) Q_snprintf( cmd, MAX_STRING, "cmd lightstyles %i %i\n", svs.spawncount, 0 );
else Q_snprintf( cmd, MAX_STRING, "cmd eventlist %i %i\n", svs.spawncount, start );
2010-10-28 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2010-10-28 22:00:00 +02:00
}
/*
==================
SV_WriteLightstyles_f
==================
*/
void SV_WriteLightstyles_f( sv_client_t *cl )
{
int start;
string cmd;
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'lightstyles' is not valid from the console\n" );
2010-10-28 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-10-28 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'lightstyles' from different level\n" );
2010-10-28 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2010-10-28 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < MAX_LIGHTSTYLES )
2010-10-28 22:00:00 +02:00
{
if( sv.lightstyles[start].pattern[0] )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_lightstyle );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &cl->netchan.message, start );
MSG_WriteString( &cl->netchan.message, sv.lightstyles[start].pattern );
MSG_WriteFloat( &cl->netchan.message, sv.lightstyles[start].time );
2010-10-28 22:00:00 +02:00
}
start++;
}
2011-03-09 22:00:00 +01:00
if( start == MAX_LIGHTSTYLES ) Q_snprintf( cmd, MAX_STRING, "cmd usermsgs %i %i\n", svs.spawncount, 0 );
else Q_snprintf( cmd, MAX_STRING, "cmd lightstyles %i %i\n", svs.spawncount, start );
2010-10-28 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2010-10-28 22:00:00 +02:00
}
2010-08-05 22:00:00 +02:00
/*
==================
2010-10-02 22:00:00 +02:00
SV_UserMessages_f
2010-08-05 22:00:00 +02:00
==================
*/
2010-10-02 22:00:00 +02:00
void SV_UserMessages_f( sv_client_t *cl )
2010-08-05 22:00:00 +02:00
{
2010-10-02 22:00:00 +02:00
int start;
sv_user_message_t *message;
2010-08-05 22:00:00 +02:00
string cmd;
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'usermsgs' is not valid from the console\n" );
2010-08-05 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-08-05 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'usermsgs' from different level\n" );
2010-08-05 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2010-08-05 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < MAX_USER_MESSAGES )
2010-08-05 22:00:00 +02:00
{
2010-10-02 22:00:00 +02:00
message = &svgame.msg[start];
if( message->name[0] )
2010-08-05 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_usermessage );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &cl->netchan.message, message->number );
MSG_WriteByte( &cl->netchan.message, (byte)message->size );
MSG_WriteString( &cl->netchan.message, message->name );
2010-08-05 22:00:00 +02:00
}
2010-10-02 22:00:00 +02:00
start++;
2010-08-05 22:00:00 +02:00
}
2011-03-09 22:00:00 +01:00
if( start == MAX_USER_MESSAGES ) Q_snprintf( cmd, MAX_STRING, "cmd deltainfo %i 0 0\n", svs.spawncount );
else Q_snprintf( cmd, MAX_STRING, "cmd usermsgs %i %i\n", svs.spawncount, start );
2010-08-05 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2010-08-05 22:00:00 +02:00
}
2010-06-22 22:00:00 +02:00
/*
==================
2010-10-02 22:00:00 +02:00
SV_DeltaInfo_f
2010-06-22 22:00:00 +02:00
==================
*/
2010-10-02 22:00:00 +02:00
void SV_DeltaInfo_f( sv_client_t *cl )
2010-06-22 22:00:00 +02:00
{
2010-10-02 22:00:00 +02:00
delta_info_t *dt;
2010-06-22 22:00:00 +02:00
string cmd;
2010-10-02 22:00:00 +02:00
int tableIndex;
int fieldIndex;
2010-06-22 22:00:00 +02:00
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'deltainfo' is not valid from the console\n" );
2010-06-22 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2010-06-22 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'deltainfo' from different level\n" );
2010-06-22 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
tableIndex = Q_atoi( Cmd_Argv( 2 ));
2011-03-28 22:00:00 +02:00
fieldIndex = Q_atoi( Cmd_Argv( 3 ));
2010-06-22 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && tableIndex < Delta_NumTables( ))
2010-06-22 22:00:00 +02:00
{
2010-10-02 22:00:00 +02:00
dt = Delta_FindStructByIndex( tableIndex );
for( ; fieldIndex < dt->numFields; fieldIndex++ )
2010-06-22 22:00:00 +02:00
{
2010-10-02 22:00:00 +02:00
Delta_WriteTableField( &cl->netchan.message, tableIndex, &dt->pFields[fieldIndex] );
// it's time to send another portion
2016-11-14 22:00:00 +01:00
if( MSG_GetNumBytesWritten( &cl->netchan.message ) >= ( NET_MAX_PAYLOAD / 2 ))
2010-10-02 22:00:00 +02:00
break;
}
if( fieldIndex == dt->numFields )
{
// go to the next table
fieldIndex = 0;
tableIndex++;
2010-06-22 22:00:00 +02:00
}
}
2011-04-17 22:00:00 +02:00
if( tableIndex == Delta_NumTables() )
{
2012-05-21 22:00:00 +02:00
// send movevars here because we need loading skybox early than HLFX 0.6 may override him
2011-04-17 22:00:00 +02:00
SV_FullUpdateMovevars( cl, &cl->netchan.message );
Q_snprintf( cmd, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, 0 );
}
2011-03-09 22:00:00 +01:00
else Q_snprintf( cmd, MAX_STRING, "cmd deltainfo %i %i %i\n", svs.spawncount, tableIndex, fieldIndex );
2010-06-22 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2008-07-12 22:00:00 +02:00
}
/*
==================
SV_Baselines_f
==================
*/
void SV_Baselines_f( sv_client_t *cl )
{
int start;
entity_state_t *base, nullstate;
2010-06-22 22:00:00 +02:00
string cmd;
2008-07-12 22:00:00 +02:00
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'baselines' is not valid from the console\n" );
2008-07-12 22:00:00 +02:00
return;
}
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2008-07-12 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'baselines' from different level\n" );
2008-07-12 22:00:00 +02:00
SV_New_f( cl );
return;
}
2011-03-09 22:00:00 +01:00
start = Q_atoi( Cmd_Argv( 2 ));
2008-07-12 22:00:00 +02:00
2016-11-17 22:00:00 +01:00
memset( &nullstate, 0, sizeof( nullstate ));
2008-07-12 22:00:00 +02:00
// write a packet full of data
2017-02-07 22:00:00 +01:00
while(( MSG_GetNumBytesLeft( &cl->netchan.message ) > MAX_UDP_PACKET ) && start < svgame.numEntities )
2008-07-12 22:00:00 +02:00
{
2008-07-17 22:00:00 +02:00
base = &svs.baselines[start];
2016-11-14 22:00:00 +01:00
if(( !start || base->number ) && ( base->modelindex || base->effects != EF_NODRAW ))
2008-07-12 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_spawnbaseline );
2010-10-20 22:00:00 +02:00
MSG_WriteDeltaEntity( &nullstate, base, &cl->netchan.message, true, SV_IsPlayerIndex( base->number ), sv.time );
2008-07-12 22:00:00 +02:00
}
start++;
}
2011-03-09 22:00:00 +01:00
if( start == svgame.numEntities ) Q_snprintf( cmd, MAX_STRING, "precache %i\n", svs.spawncount );
else Q_snprintf( cmd, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, start );
2008-07-12 22:00:00 +02:00
// send next command
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &cl->netchan.message, svc_stufftext );
2016-11-14 22:00:00 +01:00
MSG_WriteString( &cl->netchan.message, cmd );
2008-07-12 22:00:00 +02:00
}
/*
==================
SV_Begin_f
==================
*/
void SV_Begin_f( sv_client_t *cl )
{
2010-10-26 22:00:00 +02:00
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'begin' is not valid from the console\n" );
2010-10-26 22:00:00 +02:00
return;
}
2008-07-12 22:00:00 +02:00
// handle the case of a level changing while a client was connecting
2011-03-09 22:00:00 +01:00
if( Q_atoi( Cmd_Argv( 1 )) != svs.spawncount )
2008-07-12 22:00:00 +02:00
{
2016-11-21 22:00:00 +01:00
Msg( "'begin' from different level\n" );
2008-07-12 22:00:00 +02:00
SV_New_f( cl );
return;
}
2009-11-25 22:00:00 +01:00
2016-11-21 22:00:00 +01:00
SV_PutClientInServer( cl );
2009-11-10 22:00:00 +01:00
2017-02-07 22:00:00 +01:00
// if we are paused, tell the clients
2009-11-10 22:00:00 +01:00
if( sv.paused )
{
2017-02-05 22:00:00 +01:00
MSG_BeginServerCmd( &sv.reliable_datagram, svc_setpause );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &sv.reliable_datagram, sv.paused );
2009-11-10 22:00:00 +01:00
SV_ClientPrintf( cl, PRINT_HIGH, "Server is paused.\n" );
}
2008-07-12 22:00:00 +02:00
}
/*
=================
SV_Disconnect_f
The client is going to disconnect, so remove the connection immediately
=================
*/
void SV_Disconnect_f( sv_client_t *cl )
{
SV_DropClient( cl );
}
/*
==================
SV_ShowServerinfo_f
Dumps the serverinfo info string
==================
*/
void SV_ShowServerinfo_f( sv_client_t *cl )
{
2017-02-12 22:00:00 +01:00
Info_Print( svs.serverinfo );
2008-07-12 22:00:00 +02:00
}
2009-11-10 22:00:00 +01:00
/*
==================
SV_Pause_f
==================
*/
void SV_Pause_f( sv_client_t *cl )
{
string message;
2011-04-01 22:00:00 +02:00
if( UI_CreditsActive( )) return;
2017-02-12 22:00:00 +01:00
if( !sv_pausable->value )
2009-11-10 22:00:00 +01:00
{
SV_ClientPrintf( cl, PRINT_HIGH, "Pause not allowed.\n" );
return;
}
2016-11-21 22:00:00 +01:00
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
2009-11-10 22:00:00 +01:00
{
SV_ClientPrintf( cl, PRINT_HIGH, "Spectators can not pause.\n" );
return;
}
2011-10-14 22:00:00 +02:00
if( !sv.paused ) Q_snprintf( message, MAX_STRING, "^2%s^7 paused the game\n", cl->name );
else Q_snprintf( message, MAX_STRING, "^2%s^7 unpaused the game\n", cl->name );
2009-11-10 22:00:00 +01:00
SV_TogglePause( message );
}
2008-07-12 22:00:00 +02:00
/*
=================
SV_UserinfoChanged
Pull specific info from a newly changed userinfo string
into a more C freindly form.
=================
*/
2009-11-23 22:00:00 +01:00
void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo )
2008-07-12 22:00:00 +02:00
{
2011-03-30 22:00:00 +02:00
int i, dupc = 1;
edict_t *ent = cl->edict;
2016-11-21 22:00:00 +01:00
string name1, name2;
2011-03-30 22:00:00 +02:00
sv_client_t *current;
char *val;
2008-07-12 22:00:00 +02:00
2009-11-23 22:00:00 +01:00
if( !userinfo || !userinfo[0] ) return; // ignored
2011-03-30 22:00:00 +02:00
val = Info_ValueForKey( cl->userinfo, "name" );
2016-11-21 22:00:00 +01:00
Q_strncpy( name2, val, sizeof( name2 ));
COM_TrimSpace( name2, name1 );
2011-03-30 22:00:00 +02:00
2016-11-21 22:00:00 +01:00
if( !Q_stricmp( name1, "console" ))
2011-03-30 22:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( cl->userinfo, "name", "unnamed", MAX_INFO_STRING );
2011-03-30 22:00:00 +02:00
val = Info_ValueForKey( cl->userinfo, "name" );
}
2016-11-21 22:00:00 +01:00
else if( Q_strcmp( name1, val ))
2011-03-30 22:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( cl->userinfo, "name", name1, MAX_INFO_STRING );
2011-03-30 22:00:00 +02:00
val = Info_ValueForKey( cl->userinfo, "name" );
}
2016-11-21 22:00:00 +01:00
if( !Q_strlen( name1 ))
2016-08-13 23:00:00 +02:00
{
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( cl->userinfo, "name", "unnamed", MAX_INFO_STRING );
2016-08-13 23:00:00 +02:00
val = Info_ValueForKey( cl->userinfo, "name" );
2016-11-21 22:00:00 +01:00
Q_strncpy( name2, "unnamed", sizeof( name2 ));
Q_strncpy( name1, "unnamed", sizeof( name1 ));
2016-08-13 23:00:00 +02:00
}
2011-03-30 22:00:00 +02:00
// check to see if another user by the same name exists
while( 1 )
{
2017-02-12 22:00:00 +01:00
for( i = 0, current = svs.clients; i < svs.maxclients; i++, current++ )
2011-03-30 22:00:00 +02:00
{
if( current == cl || current->state != cs_spawned )
continue;
2011-07-22 22:00:00 +02:00
2011-03-30 22:00:00 +02:00
if( !Q_stricmp( current->name, val ))
break;
}
2017-02-12 22:00:00 +01:00
if( i != svs.maxclients )
2011-03-30 22:00:00 +02:00
{
// dup name
2016-11-21 22:00:00 +01:00
Q_snprintf( name2, sizeof( name2 ), "%s (%u)", name1, dupc++ );
2017-02-12 22:00:00 +01:00
Info_SetValueForKey( cl->userinfo, "name", name2, MAX_INFO_STRING );
2011-03-30 22:00:00 +02:00
val = Info_ValueForKey( cl->userinfo, "name" );
2016-11-21 22:00:00 +01:00
Q_strncpy( cl->name, name2, sizeof( cl->name ));
2011-03-30 22:00:00 +02:00
}
else
{
if( dupc == 1 ) // unchanged
2016-11-21 22:00:00 +01:00
Q_strncpy( cl->name, name1, sizeof( cl->name ));
2011-03-30 22:00:00 +02:00
break;
}
}
2008-07-12 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
// rate command
val = Info_ValueForKey( cl->userinfo, "rate" );
2011-03-09 22:00:00 +01:00
if( Q_strlen( val ))
cl->netchan.rate = bound( MIN_RATE, Q_atoi( val ), MAX_RATE );
2010-10-14 22:00:00 +02:00
else cl->netchan.rate = DEFAULT_RATE;
2010-07-02 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
// msg command
2017-02-12 22:00:00 +01:00
val = Info_ValueForKey( cl->userinfo, "msg" );
2011-03-23 22:00:00 +01:00
if( Q_strlen( val )) cl->messagelevel = Q_atoi( val );
2009-11-23 22:00:00 +01:00
2017-02-12 22:00:00 +01:00
if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_nopred" )))
2017-02-13 22:00:00 +01:00
ClearBits( cl->flags, FCL_PREDICT_MOVEMENT );
else SetBits( cl->flags, FCL_PREDICT_MOVEMENT );
2016-11-21 22:00:00 +01:00
if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_lc" )))
SetBits( cl->flags, FCL_LAG_COMPENSATION );
else ClearBits( cl->flags, FCL_LAG_COMPENSATION );
if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )))
SetBits( cl->flags, FCL_LOCAL_WEAPONS );
else ClearBits( cl->flags, FCL_LOCAL_WEAPONS );
2010-10-14 22:00:00 +02:00
2011-03-23 22:00:00 +01:00
val = Info_ValueForKey( cl->userinfo, "cl_updaterate" );
if( Q_strlen( val ))
2010-02-09 22:00:00 +01:00
{
2016-11-21 22:00:00 +01:00
if( Q_atoi( val ) != 0 )
{
int i = bound( 10, Q_atoi( val ), 300 );
cl->cl_updaterate = 1.0 / i;
}
else cl->cl_updaterate = 0.0;
2011-03-23 22:00:00 +01:00
}
2010-07-01 22:00:00 +02:00
2017-02-12 22:00:00 +01:00
if( svs.maxclients > 1 )
2011-03-23 22:00:00 +01:00
{
const char *model = Info_ValueForKey( cl->userinfo, "model" );
// apply custom playermodel
2014-10-31 22:00:00 +01:00
if( Q_strlen( model ) && Q_stricmp( model, "player" ))
2011-03-23 22:00:00 +01:00
{
const char *path = va( "models/player/%s/%s.mdl", model, model );
2013-02-28 21:00:00 +01:00
if( FS_FileExists( path, false ))
{
Mod_RegisterModel( path, SV_ModelIndex( path )); // register model
SV_SetModel( ent, path );
cl->modelindex = ent->v.modelindex;
}
else cl->modelindex = 0;
2010-02-09 22:00:00 +01:00
}
2010-07-02 22:00:00 +02:00
else cl->modelindex = 0;
2010-02-09 22:00:00 +01:00
}
2011-03-23 22:00:00 +01:00
else cl->modelindex = 0;
2010-02-09 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
// call prog code to allow overrides
svgame.dllFuncs.pfnClientUserInfoChanged( cl->edict, cl->userinfo );
2011-03-23 22:00:00 +01:00
2016-11-23 22:00:00 +01:00
val = Info_ValueForKey( userinfo, "name" );
Q_strncpy( cl->name, val, sizeof( cl->name ));
ent->v.netname = MAKE_STRING( cl->name );
2008-07-12 22:00:00 +02:00
}
/*
==================
SV_UpdateUserinfo_f
==================
*/
static void SV_UpdateUserinfo_f( sv_client_t *cl )
{
2016-11-23 22:00:00 +01:00
Q_strncpy( cl->userinfo, Cmd_Argv( 1 ), sizeof( cl->userinfo ));
if( cl->state >= cs_connected )
SetBits( cl->flags, FCL_RESEND_USERINFO ); // needs for update client info
2008-07-12 22:00:00 +02:00
}
2017-02-12 22:00:00 +01:00
/*
==================
SV_SetInfo_f
==================
*/
static void SV_SetInfo_f( sv_client_t *cl )
{
Info_SetValueForKey( cl->userinfo, Cmd_Argv( 1 ), Cmd_Argv( 2 ), MAX_INFO_STRING );
if( cl->state >= cs_connected )
SetBits( cl->flags, FCL_RESEND_USERINFO ); // needs for update client info
}
2010-10-20 22:00:00 +02:00
/*
==================
SV_Noclip_f
==================
*/
static void SV_Noclip_f( sv_client_t *cl )
{
edict_t *pEntity = cl->edict;
2011-02-22 22:00:00 +01:00
if( !Cvar_VariableInteger( "sv_cheats" ) || sv.background )
return;
2010-10-20 22:00:00 +02:00
if( pEntity->v.movetype != MOVETYPE_NOCLIP )
{
pEntity->v.movetype = MOVETYPE_NOCLIP;
SV_ClientPrintf( cl, PRINT_HIGH, "noclip ON\n" );
}
else
{
pEntity->v.movetype = MOVETYPE_WALK;
SV_ClientPrintf( cl, PRINT_HIGH, "noclip OFF\n" );
}
}
/*
==================
SV_Godmode_f
==================
*/
static void SV_Godmode_f( sv_client_t *cl )
{
edict_t *pEntity = cl->edict;
2011-02-22 22:00:00 +01:00
if( !Cvar_VariableInteger( "sv_cheats" ) || sv.background )
return;
2010-10-20 22:00:00 +02:00
pEntity->v.flags = pEntity->v.flags ^ FL_GODMODE;
2016-11-21 22:00:00 +01:00
if( !FBitSet( pEntity->v.flags, FL_GODMODE ))
2010-10-20 22:00:00 +02:00
SV_ClientPrintf( cl, PRINT_HIGH, "godmode OFF\n" );
else SV_ClientPrintf( cl, PRINT_HIGH, "godmode ON\n" );
}
/*
==================
SV_Notarget_f
==================
*/
static void SV_Notarget_f( sv_client_t *cl )
{
edict_t *pEntity = cl->edict;
2011-02-22 22:00:00 +01:00
if( !Cvar_VariableInteger( "sv_cheats" ) || sv.background )
return;
2010-10-20 22:00:00 +02:00
pEntity->v.flags = pEntity->v.flags ^ FL_NOTARGET;
2016-11-21 22:00:00 +01:00
if( !FBitSet( pEntity->v.flags, FL_NOTARGET ))
2010-10-20 22:00:00 +02:00
SV_ClientPrintf( cl, PRINT_HIGH, "notarget OFF\n" );
else SV_ClientPrintf( cl, PRINT_HIGH, "notarget ON\n" );
}
2012-05-20 22:00:00 +02:00
/*
==================
SV_SendRes_f
==================
*/
void SV_SendRes_f( sv_client_t *cl )
{
2013-01-11 21:00:00 +01:00
static byte buffer[65535];
2016-11-21 22:00:00 +01:00
sizebuf_t msg;
2012-05-20 22:00:00 +02:00
if( cl->state != cs_connected )
{
2016-11-21 22:00:00 +01:00
MsgDev( D_INFO, "'sendres' is not valid from the console\n" );
2012-05-20 22:00:00 +02:00
return;
}
2016-11-14 22:00:00 +01:00
MSG_Init( &msg, "SendResources", buffer, sizeof( buffer ));
2012-05-20 22:00:00 +02:00
SV_SendResources( &msg );
2017-02-05 22:00:00 +01:00
Netchan_CreateFragments( &cl->netchan, &msg );
2012-05-20 22:00:00 +02:00
Netchan_FragSend( &cl->netchan );
}
2008-07-12 22:00:00 +02:00
ucmd_t ucmds[] =
{
2009-11-02 22:00:00 +01:00
{ "new", SV_New_f },
2010-10-20 22:00:00 +02:00
{ "god", SV_Godmode_f },
2009-11-02 22:00:00 +01:00
{ "begin", SV_Begin_f },
2009-11-10 22:00:00 +01:00
{ "pause", SV_Pause_f },
2010-10-20 22:00:00 +02:00
{ "noclip", SV_Noclip_f },
2017-02-12 22:00:00 +01:00
{ "setinfo", SV_SetInfo_f },
2012-05-20 22:00:00 +02:00
{ "sendres", SV_SendRes_f },
2010-10-20 22:00:00 +02:00
{ "notarget", SV_Notarget_f },
2009-11-02 22:00:00 +01:00
{ "baselines", SV_Baselines_f },
2010-08-05 22:00:00 +02:00
{ "deltainfo", SV_DeltaInfo_f },
2009-11-02 22:00:00 +01:00
{ "info", SV_ShowServerinfo_f },
2010-10-26 22:00:00 +02:00
{ "modellist", SV_WriteModels_f },
{ "soundlist", SV_WriteSounds_f },
2010-10-28 22:00:00 +02:00
{ "eventlist", SV_WriteEvents_f },
2009-11-02 22:00:00 +01:00
{ "disconnect", SV_Disconnect_f },
2010-06-22 22:00:00 +02:00
{ "usermsgs", SV_UserMessages_f },
2009-11-02 22:00:00 +01:00
{ "userinfo", SV_UpdateUserinfo_f },
2010-10-28 22:00:00 +02:00
{ "lightstyles", SV_WriteLightstyles_f },
2011-07-07 22:00:00 +02:00
{ "getresourelist", SV_SendResourceList_f },
{ "continueloading", SV_ContinueLoading_f },
2009-11-02 22:00:00 +01:00
{ NULL, NULL }
2008-07-12 22:00:00 +02:00
};
/*
==================
SV_ExecuteUserCommand
==================
*/
void SV_ExecuteClientCommand( sv_client_t *cl, char *s )
{
ucmd_t *u;
2008-12-20 22:00:00 +01:00
2011-04-06 22:00:00 +02:00
svs.currentPlayer = cl;
2011-04-21 22:00:00 +02:00
svs.currentPlayerNum = (cl - svs.clients);
2008-07-12 22:00:00 +02:00
Cmd_TokenizeString( s );
2011-04-05 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
for( u = ucmds; u->name; u++ )
{
2011-03-09 22:00:00 +01:00
if( !Q_strcmp( Cmd_Argv( 0 ), u->name ))
2008-07-12 22:00:00 +02:00
{
2008-07-31 22:00:00 +02:00
MsgDev( D_NOTE, "ucmd->%s()\n", u->name );
2010-12-26 22:00:00 +01:00
if( u->func ) u->func( cl );
2008-07-12 22:00:00 +02:00
break;
}
}
2009-11-02 22:00:00 +01:00
2008-07-12 22:00:00 +02:00
if( !u->name && sv.state == ss_active )
{
// custom client commands
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnClientCommand( cl->edict );
2012-08-31 22:00:00 +02:00
if( !Q_strcmp( Cmd_Argv( 0 ), "fullupdate" ))
{
// resend the ambient sounds for demo recording
Host_RestartAmbientSounds();
2013-10-23 22:00:00 +02:00
// resend all the decals for demo recording
Host_RestartDecals();
// resend all the static ents for demo recording
SV_RestartStaticEnts();
2017-01-17 22:00:00 +01:00
// resend the viewentity
SV_UpdateClientView( cl );
2012-08-31 22:00:00 +02:00
}
2008-07-12 22:00:00 +02:00
}
}
2016-08-13 23:00:00 +02:00
/*
==================
SV_TSourceEngineQuery
==================
*/
void SV_TSourceEngineQuery( netadr_t from )
{
// A2S_INFO
char answer[1024] = "";
2016-11-21 22:00:00 +01:00
int count = 0, bots = 0;
int index;
2016-08-13 23:00:00 +02:00
sizebuf_t buf;
if( svs.clients )
{
2017-02-12 22:00:00 +01:00
for( index = 0; index < svs.maxclients; index++ )
2016-08-13 23:00:00 +02:00
{
if( svs.clients[index].state >= cs_connected )
{
2016-11-21 22:00:00 +01:00
if( FBitSet( svs.clients[index].flags, FCL_FAKECLIENT ))
2016-08-13 23:00:00 +02:00
bots++;
else count++;
}
}
}
2016-11-14 22:00:00 +01:00
MSG_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer ));
2016-08-13 23:00:00 +02:00
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, 'm' );
2017-01-14 22:00:00 +01:00
MSG_WriteString( &buf, NET_AdrToString( net_local ));
2016-11-14 22:00:00 +01:00
MSG_WriteString( &buf, hostname->string );
MSG_WriteString( &buf, sv.name );
MSG_WriteString( &buf, GI->gamefolder );
MSG_WriteString( &buf, GI->title );
MSG_WriteByte( &buf, count );
2017-02-12 22:00:00 +01:00
MSG_WriteByte( &buf, svs.maxclients );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, PROTOCOL_VERSION );
2017-01-14 22:00:00 +01:00
MSG_WriteByte( &buf, host.type == HOST_DEDICATED ? 'D' : 'L' );
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, 'W' );
2016-08-13 23:00:00 +02:00
if( Q_stricmp( GI->gamedir, "valve" ))
{
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, 1 ); // mod
MSG_WriteString( &buf, GI->game_url );
MSG_WriteString( &buf, GI->update_url );
MSG_WriteByte( &buf, 0 );
MSG_WriteLong( &buf, (long)GI->version );
MSG_WriteLong( &buf, GI->size );
2016-08-13 23:00:00 +02:00
if( GI->gamemode == 2 )
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, 1 ); // multiplayer_only
else MSG_WriteByte( &buf, 0 );
2016-08-13 23:00:00 +02:00
if( Q_strstr( GI->game_dll, "hl." ))
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, 0 ); // Half-Life DLL
else MSG_WriteByte( &buf, 1 ); // Own DLL
2016-08-13 23:00:00 +02:00
}
2016-11-14 22:00:00 +01:00
else MSG_WriteByte( &buf, 0 ); // Half-Life
2016-08-13 23:00:00 +02:00
2016-11-14 22:00:00 +01:00
MSG_WriteByte( &buf, GI->secure ); // unsecure
MSG_WriteByte( &buf, bots );
2016-08-13 23:00:00 +02:00
2016-11-14 22:00:00 +01:00
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from );
2016-08-13 23:00:00 +02:00
}
2008-07-12 22:00:00 +02: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.
=================
*/
2010-08-06 22:00:00 +02:00
void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
2008-07-12 22:00:00 +02:00
{
2010-08-15 22:00:00 +02:00
char *args;
2017-01-14 22:00:00 +01:00
char *pcmd, buf[MAX_SYSPATH];
2010-08-15 22:00:00 +02:00
int len = sizeof( buf );
2008-07-12 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
MSG_Clear( msg );
MSG_ReadLong( msg );// skip the -1 marker
2008-07-12 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
args = MSG_ReadStringLine( msg );
2010-08-15 22:00:00 +02:00
Cmd_TokenizeString( args );
2008-07-12 22:00:00 +02:00
2017-01-14 22:00:00 +01:00
pcmd = Cmd_Argv( 0 );
MsgDev( D_NOTE, "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), pcmd );
if( !Q_strcmp( pcmd, "ping" )) SV_Ping( from );
else if( !Q_strcmp( pcmd, "ack" )) SV_Ack( from );
else if( !Q_strcmp( pcmd, "info" )) SV_Info( from );
else if( !Q_strcmp( pcmd, "getchallenge" )) SV_GetChallenge( from );
else if( !Q_strcmp( pcmd, "connect" )) SV_DirectConnect( from );
else if( !Q_strcmp( pcmd, "rcon" )) SV_RemoteCommand( from, msg );
else if( !Q_strcmp( pcmd, "netinfo" )) SV_BuildNetAnswer( from );
else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg );
else if( !Q_strcmp( pcmd, "T" "Source" )) SV_TSourceEngineQuery( from );
else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING
2010-10-07 22:00:00 +02:00
else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
2010-08-15 22:00:00 +02:00
{
// user out of band message (must be handled in CL_ConnectionlessPacket)
2010-10-07 22:00:00 +02:00
if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf );
2010-08-15 22:00:00 +02:00
}
else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
2008-07-12 22:00:00 +02:00
}
/*
==================
2011-02-20 22:00:00 +01:00
SV_ParseClientMove
2008-07-12 22:00:00 +02:00
The message usually contains all the movement commands
that were in the last three packets, so that the information
in dropped packets can be recovered.
On very fast clients, there may be multiple usercmd packed into
each of the backup packets.
==================
*/
2010-10-14 22:00:00 +02:00
static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg )
{
client_frame_t *frame;
int key, size, checksum1, checksum2;
2017-02-05 22:00:00 +01:00
int i, numbackup, totalcmds, numcmds;
2016-11-21 22:00:00 +01:00
usercmd_t nullcmd, *to, *from;
2017-02-05 22:00:00 +01:00
usercmd_t cmds[CMD_BACKUP];
float packet_loss;
2010-10-14 22:00:00 +02:00
edict_t *player;
player = cl->edict;
frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK];
2016-11-17 22:00:00 +01:00
memset( &nullcmd, 0, sizeof( usercmd_t ));
memset( cmds, 0, sizeof( cmds ));
2010-10-14 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
key = MSG_GetRealBytesRead( msg );
checksum1 = MSG_ReadByte( msg );
2017-02-05 22:00:00 +01:00
packet_loss = MSG_ReadByte( msg );
2010-10-14 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
numbackup = MSG_ReadByte( msg );
2017-02-05 22:00:00 +01:00
numcmds = MSG_ReadByte( msg );
2010-10-14 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
totalcmds = numcmds + numbackup;
2017-08-15 23:00:00 +02:00
net_drop -= (numcmds - 1);
2010-10-14 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
if( totalcmds < 0 || totalcmds >= CMD_MASK )
2010-10-14 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
MsgDev( D_ERROR, "SV_ParseClientMove: %s sending too many commands %i\n", cl->name, totalcmds );
2010-10-14 22:00:00 +02:00
SV_DropClient( cl );
return;
}
2016-11-21 22:00:00 +01:00
from = &nullcmd; // first cmd are starting from null-compressed usercmd_t
2010-10-14 22:00:00 +02:00
2017-02-05 22:00:00 +01:00
for( i = totalcmds - 1; i >= 0; i-- )
2010-10-14 22:00:00 +02:00
{
to = &cmds[i];
MSG_ReadDeltaUsercmd( msg, from, to );
from = to; // get new baseline
}
if( cl->state != cs_spawned )
return;
// if the checksum fails, ignore the rest of the packet
2016-11-14 22:00:00 +01:00
size = MSG_GetRealBytesRead( msg ) - key - 1;
2011-02-28 22:00:00 +01:00
checksum2 = CRC32_BlockSequence( msg->pData + key + 1, size, cl->netchan.incoming_sequence );
2010-10-14 22:00:00 +02:00
if( checksum2 != checksum1 )
{
MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 );
return;
}
2017-02-05 22:00:00 +01:00
cl->packet_loss = packet_loss;
2010-10-14 22:00:00 +02:00
// check for pause or frozen
2017-07-12 23:00:00 +02:00
if( sv.paused || sv.loadgame || sv.background || !CL_IsInGame() || SV_PlayerIsFrozen( player ))
2010-10-14 22:00:00 +02:00
{
2017-02-05 22:00:00 +01:00
for( i = 0; i < numcmds; i++ )
2010-10-14 22:00:00 +02:00
{
cmds[i].msec = 0;
cmds[i].forwardmove = 0;
cmds[i].sidemove = 0;
cmds[i].upmove = 0;
cmds[i].buttons = 0;
2017-07-12 23:00:00 +02:00
if( SV_PlayerIsFrozen( player ) || sv.background )
2010-10-14 22:00:00 +02:00
cmds[i].impulse = 0;
VectorCopy( cmds[i].viewangles, player->v.v_angle );
}
net_drop = 0;
}
else
{
2012-06-25 22:00:00 +02:00
if( !player->v.fixangle )
VectorCopy( cmds[0].viewangles, player->v.v_angle );
2010-10-14 22:00:00 +02:00
}
2017-02-05 22:00:00 +01:00
SV_EstablishTimeBase( cl, cmds, net_drop, numbackup, numcmds );
2010-10-14 22:00:00 +02:00
if( net_drop < 24 )
{
while( net_drop > numbackup )
{
SV_RunCmd( cl, &cl->lastcmd, 0 );
net_drop--;
}
while( net_drop > 0 )
{
2017-02-05 22:00:00 +01:00
i = numcmds + net_drop - 1;
2010-10-14 22:00:00 +02:00
SV_RunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
net_drop--;
}
}
2017-02-05 22:00:00 +01:00
for( i = numcmds - 1; i >= 0; i-- )
2010-10-14 22:00:00 +02:00
{
SV_RunCmd( cl, &cmds[i], cl->netchan.incoming_sequence - i );
}
2017-08-15 23:00:00 +02:00
cl->lastcmd = cmds[0];
2010-10-14 22:00:00 +02:00
// adjust latency time by 1/2 last client frame since
// the message probably arrived 1/2 through client's frame loop
2017-08-15 23:00:00 +02:00
frame->ping_time -= ( cl->lastcmd.msec * 0.5f ) / 1000.0f;
2017-02-05 22:00:00 +01:00
frame->ping_time = Q_max( 0.0f, frame->ping_time );
2010-10-14 22:00:00 +02:00
2017-07-12 23:00:00 +02:00
if( Mod_GetType( player->v.modelindex ) == mod_studio )
{
if( player->v.animtime > svgame.globals->time + sv.frametime )
player->v.animtime = svgame.globals->time + sv.frametime;
}
2010-10-14 22:00:00 +02:00
}
2011-07-07 22:00:00 +02:00
/*
===================
SV_ParseResourceList
Parse resource list
===================
*/
void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg )
{
2017-02-05 22:00:00 +01:00
Netchan_CreateFileFragments( &cl->netchan, MSG_ReadString( msg ));
2011-07-07 22:00:00 +02:00
Netchan_FragSend( &cl->netchan );
}
2018-02-12 22:00:00 +01:00
/*
===================
SV_ParseCvarValue
Parse a requested value from client cvar
===================
*/
void SV_ParseCvarValue( sv_client_t *cl, sizebuf_t *msg )
{
const char *value = MSG_ReadString( msg );
if( svgame.dllFuncs2.pfnCvarValue != NULL )
svgame.dllFuncs2.pfnCvarValue( cl->edict, value );
MsgDev( D_REPORT, "Cvar query response: name:%s, value:%s\n", cl->name, value );
}
/*
===================
SV_ParseCvarValue2
Parse a requested value from client cvar
===================
*/
void SV_ParseCvarValue2( sv_client_t *cl, sizebuf_t *msg )
{
string name, value;
int requestID = MSG_ReadLong( msg );
Q_strcpy( name, MSG_ReadString( msg ));
Q_strcpy( value, MSG_ReadString( msg ));
if( svgame.dllFuncs2.pfnCvarValue2 != NULL )
svgame.dllFuncs2.pfnCvarValue2( cl->edict, requestID, name, value );
MsgDev( D_REPORT, "Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, name, value );
}
2008-07-12 22:00:00 +02:00
/*
===================
SV_ExecuteClientMessage
Parse a client packet
===================
*/
2010-08-06 22:00:00 +02:00
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
2008-07-12 22:00:00 +02:00
{
2010-10-14 22:00:00 +02:00
int c, stringCmdCount = 0;
2010-10-26 22:00:00 +02:00
qboolean move_issued = false;
2010-10-14 22:00:00 +02:00
client_frame_t *frame;
char *s;
// calc ping time
frame = &cl->frames[cl->netchan.incoming_acknowledged & SV_UPDATE_MASK];
2016-11-21 22:00:00 +01:00
// ping time doesn't factor in message interval, either
2015-12-26 22:00:00 +01:00
frame->ping_time = host.realtime - frame->senttime - cl->cl_updaterate;
2010-10-14 22:00:00 +02:00
// on first frame ( no senttime ) don't skew ping
2017-02-05 22:00:00 +01:00
if( frame->senttime == 0.0f ) frame->ping_time = 0.0f;
2010-10-14 22:00:00 +02:00
// don't skew ping based on signon stuff either
2017-02-05 22:00:00 +01:00
if(( host.realtime - cl->connection_started ) < 2.0f && ( frame->ping_time > 0.0 ))
2015-12-26 22:00:00 +01:00
frame->ping_time = 0.0f;
2010-10-14 22:00:00 +02:00
2015-12-26 22:00:00 +01:00
cl->latency = SV_CalcClientTime( cl );
2010-10-14 22:00:00 +02:00
cl->delta_sequence = -1; // no delta unless requested
2011-10-09 22:00:00 +02:00
// set the current client
svs.currentPlayer = cl;
svs.currentPlayerNum = (cl - svs.clients);
2010-10-14 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
// read optional clientCommand strings
while( cl->state != cs_zombie )
{
2016-11-14 22:00:00 +01:00
if( MSG_CheckOverflow( msg ))
2009-06-22 22:00:00 +02:00
{
2009-11-02 22:00:00 +01:00
MsgDev( D_ERROR, "SV_ReadClientMessage: clc_bad\n" );
2009-06-24 22:00:00 +02:00
SV_DropClient( cl );
return;
2010-08-04 22:00:00 +02:00
}
// end of message
2016-11-14 22:00:00 +01:00
if( MSG_GetNumBitsLeft( msg ) < 8 )
2010-08-04 22:00:00 +02:00
break;
2017-02-05 22:00:00 +01:00
c = MSG_ReadClientCmd( msg );
2008-07-12 22:00:00 +02:00
switch( c )
{
case clc_nop:
break;
2010-10-14 22:00:00 +02:00
case clc_delta:
2016-11-14 22:00:00 +01:00
cl->delta_sequence = MSG_ReadByte( msg );
2010-10-14 22:00:00 +02:00
break;
2008-07-12 22:00:00 +02:00
case clc_move:
if( move_issued ) return; // someone is trying to cheat...
move_issued = true;
2010-10-14 22:00:00 +02:00
SV_ParseClientMove( cl, msg );
2008-07-12 22:00:00 +02:00
break;
case clc_stringcmd:
2016-11-14 22:00:00 +01:00
s = MSG_ReadString( msg );
2008-07-12 22:00:00 +02:00
// malicious users may try using too many string commands
if( ++stringCmdCount < 8 ) SV_ExecuteClientCommand( cl, s );
if( cl->state == cs_zombie ) return; // disconnect command
break;
2011-07-07 22:00:00 +02:00
case clc_resourcelist:
SV_ParseResourceList( cl, msg );
break;
2018-02-12 22:00:00 +01:00
case clc_requestcvarvalue:
SV_ParseCvarValue( cl, msg );
break;
case clc_requestcvarvalue2:
SV_ParseCvarValue2( cl, msg );
break;
2008-07-12 22:00:00 +02:00
default:
2009-11-02 22:00:00 +01:00
MsgDev( D_ERROR, "SV_ReadClientMessage: clc_bad\n" );
2008-07-12 22:00:00 +02:00
SV_DropClient( cl );
return;
}
}
}