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

1973 lines
50 KiB
C
Raw Normal View History

2008-05-20 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2008 <20>
// sv_client.c - client interactions
//=======================================================================
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"
2009-11-10 22:00:00 +01:00
#include "pm_defs.h"
2008-05-20 22:00:00 +02:00
2009-09-13 22:00:00 +02:00
#define MAX_FORWARD 6
2008-07-12 22:00:00 +02:00
typedef struct ucmd_s
{
const char *name;
void (*func)( sv_client_t *cl );
} ucmd_t;
2008-07-30 22:00:00 +02:00
static vec3_t wishdir, forward, right, up;
static float wishspeed;
static bool onground;
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;
2009-09-20 22:00:00 +02:00
int oldestTime;
2008-07-12 22:00:00 +02:00
oldestTime = 0x7fffffff;
// see if we already have a challenge for this ip
for (i = 0; i < MAX_CHALLENGES; i++ )
{
if( !svs.challenges[i].connected && NET_CompareAdr( from, svs.challenges[i].adr ))
break;
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
2009-09-17 22:00:00 +02:00
svs.challenges[oldest].challenge = ((rand()<<16) ^ rand()) ^ svs.realtime;
2008-07-12 22:00:00 +02:00
svs.challenges[oldest].adr = from;
2009-09-14 22:00:00 +02:00
svs.challenges[oldest].time = svs.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 );
}
/*
==================
SV_DirectConnect
A connection request that did not come from the master
==================
*/
void SV_DirectConnect( netadr_t from )
{
char userinfo[MAX_INFO_STRING];
sv_client_t temp, *cl, *newcl;
edict_t *ent;
int i, edictnum;
int version;
int qport, count = 0;
int challenge;
2009-09-25 22:00:00 +02:00
version = com.atoi( Cmd_Argv( 1 ));
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 );
return;
}
qport = com.atoi(Cmd_Argv(2));
challenge = com.atoi(Cmd_Argv(3));
com.strncpy( userinfo, Cmd_Argv( 4 ), sizeof(userinfo) - 1);
userinfo[sizeof(userinfo) - 1] = 0;
// quick reject
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
2008-07-12 22:00:00 +02:00
{
if( cl->state == cs_free ) continue;
2009-09-16 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
{
2009-09-17 22:00:00 +02:00
if(!NET_IsLocalAddress( from ) && (svs.realtime - cl->lastconnect) < sv_reconnect_limit->value * 1000 )
2008-07-12 22:00:00 +02:00
{
MsgDev( D_INFO, "%s:reconnect rejected : too soon\n", NET_AdrToString( from ));
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
}
}
if( i == MAX_CHALLENGES )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for address.\n" );
return;
}
// force the IP key/value pair so the game can filter based on ip
Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ));
svs.challenges[i].connected = true;
MsgDev( D_INFO, "Client %i connecting with challenge %p\n", i, challenge );
}
else
{
// force the "ip" info key to "localhost"
2009-09-25 22:00:00 +02:00
Info_SetValueForKey( userinfo, "ip", "127.0.0.1" );
2009-09-10 22:00:00 +02:00
Info_SetValueForKey( userinfo, "name", SI->username ); // can be overwrited later
2008-07-12 22:00:00 +02:00
}
newcl = &temp;
2009-09-25 22:00:00 +02:00
Mem_Set( 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
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
2008-07-12 22:00:00 +02:00
{
if( cl->state == cs_free ) continue;
2009-09-25 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;
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++)
2008-07-12 22:00:00 +02:00
{
if( cl->state == cs_free )
{
newcl = cl;
break;
}
}
if( !newcl )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" );
MsgDev( D_INFO, "SV_DirectConnect: rejected a connection.\n");
return;
}
gotnewcl:
// build a new connection
// accept the new client
// this is the only place a sv_client_t is ever initialized
*newcl = temp;
sv_client = newcl;
edictnum = (newcl - svs.clients) + 1;
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( edictnum );
2008-12-26 22:00:00 +01:00
ent->pvServerData->client = newcl;
2008-07-12 22:00:00 +02:00
newcl->edict = ent;
newcl->challenge = challenge; // save challenge for checksumming
// get the game a chance to reject this connection or modify the userinfo
if(!(SV_ClientConnect( ent, userinfo )))
{
if(*Info_ValueForKey( userinfo, "rejmsg" ))
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" );
MsgDev( D_ERROR, "SV_DirectConnect: game rejected a connection.\n");
return;
}
// parse some info from the info strings
com.strncpy( newcl->userinfo, userinfo, sizeof(newcl->userinfo) - 1);
SV_UserinfoChanged( newcl );
// send the connect packet to the client
Netchan_OutOfBandPrint( NS_SERVER, from, "client_connect" );
Netchan_Setup( NS_SERVER, &newcl->netchan, from, qport );
2009-09-19 22:00:00 +02:00
MSG_Init( &newcl->reliable, newcl->reliable_buf, sizeof( newcl->reliable_buf )); // reliable buf
MSG_Init( &newcl->datagram, newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf
2008-07-12 22:00:00 +02:00
newcl->state = cs_connected;
2009-09-14 22:00:00 +02:00
newcl->lastmessage = svs.realtime;
newcl->lastconnect = svs.realtime;
2009-09-16 22:00:00 +02: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.
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
2008-07-12 22:00:00 +02:00
if( cl->state >= cs_connected ) count++;
2009-09-25 22:00:00 +02:00
if( count == 1 || count == sv_maxclients->integer )
2008-07-12 22:00:00 +02:00
svs.last_heartbeat = MAX_HEARTBEAT;
}
2009-09-17 22:00:00 +02:00
/*
==================
SV_FakeConnect
A connection request that came from the game module
UNDONE: not called anymore
==================
*/
void SV_FakeConnect( char *cl_userinfo )
{
int i, edictnum;
char userinfo[MAX_INFO_STRING];
sv_client_t temp, *cl, *newcl;
edict_t *ent;
if( !cl_userinfo ) cl_userinfo = "";
com.strncpy( userinfo, cl_userinfo, sizeof( userinfo ));
// force the IP key/value pair so the game can filter based on ip
Info_SetValueForKey( userinfo, "ip", "127.0.0.1" );
// find a client slot
newcl = &temp;
Mem_Set( newcl, 0, sizeof( sv_client_t ));
2009-09-25 22:00:00 +02:00
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
2009-09-17 22:00:00 +02:00
{
if( cl->state == cs_free )
{
newcl = cl;
break;
}
}
if( !newcl )
{
MsgDev( D_INFO, "SV_DirectConnect: rejected a connection.\n");
return;
}
// build a new connection
// accept the new client
// this is the only place a sv_client_t is ever initialized
*newcl = temp;
sv_client = newcl;
edictnum = (newcl - svs.clients) + 1;
ent = EDICT_NUM( edictnum );
ent->pvServerData->client = newcl;
newcl->edict = ent;
newcl->challenge = -1; // fake challenge
ent->v.flags |= FL_FAKECLIENT; // mark it as fakeclient
// 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" );
return;
}
// parse some info from the info strings
com.strncpy( newcl->userinfo, userinfo, sizeof( newcl->userinfo ) - 1);
SV_UserinfoChanged( newcl );
newcl->state = cs_spawned;
newcl->lastmessage = svs.realtime; // don't timeout
newcl->lastconnect = svs.realtime;
}
2008-07-30 22:00:00 +02:00
/*
=====================
SV_ClientCconnect
QC code can rejected a connection for some reasons
e.g. ipban
=====================
*/
bool SV_ClientConnect( edict_t *ent, char *userinfo )
{
bool result = true;
// make sure we start with known default
2009-09-25 22:00:00 +02:00
if( !sv.loadgame ) ent->v.flags = 0;
2008-07-30 22:00:00 +02:00
2009-11-10 22:00:00 +01:00
MsgDev( D_NOTE, "SV_ClientConnect()\n" );
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
result = svgame.dllFuncs.pfnClientConnect( ent, userinfo );
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;
if( drop->state == cs_zombie ) return; // already dropped
// add the disconnect
2009-06-24 22:00:00 +02:00
if(!( drop->edict && (drop->edict->v.flags & FL_FAKECLIENT )))
MSG_WriteByte( &drop->netchan.message, svc_disconnect );
2008-07-12 22:00:00 +02:00
// let the game known about client state
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnClientDisconnect( drop->edict );
2008-12-15 22:00:00 +01:00
2008-07-12 22:00:00 +02:00
SV_FreeEdict( drop->edict );
if( drop->download ) drop->download = NULL;
drop->state = cs_zombie; // become free in a few seconds
drop->name[0] = 0;
// 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
2009-09-25 22:00:00 +02:00
for( i = 0; i < sv_maxclients->integer; i++ )
2008-07-12 22:00:00 +02:00
{
if( svs.clients[i].state >= cs_connected )
break;
}
2009-09-25 22:00:00 +02:00
if( i == sv_maxclients->integer ) 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 )
{
2009-06-24 22:00:00 +02:00
if( sv_client->edict && (sv_client->edict->v.flags & FL_FAKECLIENT))
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:
if( !sv_client ) return; // client not set
MSG_WriteByte( &sv_client->netchan.message, svc_print );
MSG_WriteString( &sv_client->netchan.message, buf );
break;
case RD_NONE:
MsgDev( D_ERROR, "SV_FlushRedirect: %s: invalid destination\n", NET_AdrToString( adr ));
break;
}
}
void SV_EndRedirect( void )
{
host.rd.flush( host.rd.address, host.rd.target, host.rd.buffer );
host.rd.target = 0;
host.rd.buffer = NULL;
host.rd.buffersize = 0;
host.rd.flush = NULL;
}
/*
===============
SV_StatusString
Builds the string that is sent as heartbeats and status replies
===============
*/
char *SV_StatusString( void )
{
char player[1024];
static char status[MAX_MSGLEN - 16];
int i;
sv_client_t *cl;
int statusLength;
int playerLength;
com.strcpy( status, Cvar_Serverinfo());
com.strcat( status, "\n" );
2008-07-31 22:00:00 +02:00
statusLength = com.strlen(status);
2008-07-12 22:00:00 +02:00
2009-09-25 22:00:00 +02:00
for( i = 0; i < sv_maxclients->integer; i++ )
2008-07-12 22:00:00 +02:00
{
cl = &svs.clients[i];
if( cl->state == cs_connected || cl->state == cs_spawned )
{
2008-12-15 22:00:00 +01:00
com.sprintf( player, "%i %i \"%s\"\n", (int)cl->edict->v.frags, cl->ping, cl->name );
2008-07-12 22:00:00 +02:00
playerLength = com.strlen(player);
if( statusLength + playerLength >= sizeof(status))
break; // can't hold any more
com.strcpy( status + statusLength, player );
statusLength += playerLength;
}
}
return status;
}
/*
================
SV_Status
Responds with all the info that qplug or qspy can see
================
*/
void SV_Status( netadr_t from )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "print\n%s", SV_StatusString());
}
/*
================
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 )
{
char string[64];
int i, count = 0;
int version;
// ignore in single player
2009-09-25 22:00:00 +02:00
if( sv_maxclients->integer == 1 ) return;
2008-07-12 22:00:00 +02:00
version = com.atoi(Cmd_Argv( 1 ));
if( version != PROTOCOL_VERSION )
2009-09-25 22:00:00 +02:00
{
com.sprintf( string, "%s: wrong version\n", hostname->string, sizeof( string ));
}
2008-07-12 22:00:00 +02:00
else
{
2009-09-25 22:00:00 +02:00
for( i = 0; i < sv_maxclients->integer; i++ )
2008-07-12 22:00:00 +02:00
if( svs.clients[i].state >= cs_connected )
count++;
2009-09-25 22:00:00 +02:00
com.sprintf( string, "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, sv_maxclients->integer );
2008-07-12 22:00:00 +02:00
}
Netchan_OutOfBandPrint( NS_SERVER, from, "info\n%s", string );
}
/*
================
SV_Ping
Just responds with an acknowledgement
================
*/
void SV_Ping( netadr_t from )
{
Netchan_OutOfBandPrint( NS_SERVER, from, "ack" );
}
bool Rcon_Validate( void )
{
2009-11-02 22:00:00 +01:00
if( !com.strlen( rcon_password->string ))
2008-07-12 22:00:00 +02:00
return false;
2009-11-02 22:00:00 +01:00
if( !com.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
===============
*/
void SV_RemoteCommand( netadr_t from, sizebuf_t *msg )
{
char remaining[1024];
static char outputbuf[MAX_MSGLEN - 16];
int i;
if(!Rcon_Validate()) MsgDev(D_INFO, "Bad rcon from %s:\n%s\n", NET_AdrToString( from ), msg->data + 4 );
else MsgDev( D_INFO, "Rcon from %s:\n%s\n", NET_AdrToString( from ), msg->data + 4 );
SV_BeginRedirect( from, RD_PACKET, outputbuf, MAX_MSGLEN - 16, SV_FlushRedirect );
2009-11-02 22:00:00 +01:00
if( !Rcon_Validate( ))
{
MsgDev( D_WARN, "Bad rcon_password.\n" );
}
2008-07-12 22:00:00 +02:00
else
{
remaining[0] = 0;
for( i = 2; i < Cmd_Argc(); i++ )
{
com.strcat( remaining, Cmd_Argv( i ));
com.strcat( remaining, " " );
}
Cmd_ExecuteString( remaining );
}
SV_EndRedirect();
}
/*
===========
PutClientInServer
Called when a player connects to a server or respawns in
a deathmatch.
============
*/
void SV_PutClientInServer( edict_t *ent )
{
2009-01-25 22:00:00 +01:00
int index;
2008-07-12 22:00:00 +02:00
sv_client_t *client;
2008-12-16 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
index = NUM_FOR_EDICT( ent ) - 1;
2008-12-26 22:00:00 +01:00
client = ent->pvServerData->client;
2008-07-12 22:00:00 +02:00
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
2008-12-26 22:00:00 +01:00
ent->pvServerData->s.ed_type = ED_CLIENT; // init edict type
2009-01-25 22:00:00 +01:00
ent->free = false;
2008-07-12 22:00:00 +02:00
2009-09-28 22:00:00 +02:00
if( !sv.changelevel && !sv.loadgame )
2008-07-12 22:00:00 +02:00
{
// fisrt entering
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnClientPutInServer( ent );
2009-11-16 22:00:00 +01:00
ent->v.view_ofs[2] = GI->viewheight[0];
2009-01-06 22:00:00 +01:00
ent->v.viewangles[ROLL] = 0; // cut off any camera rolling
2008-12-15 22:00:00 +01:00
ent->v.origin[2] += 1; // make sure off ground
2008-07-12 22:00:00 +02:00
}
2009-01-25 22:00:00 +01:00
else
{
}
2008-07-12 22:00:00 +02:00
2009-09-18 22:00:00 +02:00
if( !( ent->v.flags & FL_FAKECLIENT ))
{
MSG_WriteByte( &client->netchan.message, svc_setview );
MSG_WriteWord( &client->netchan.message, NUM_FOR_EDICT( client->edict ));
MSG_Send( MSG_ONE_R, NULL, client->edict );
}
2009-11-02 22:00:00 +01:00
Mem_EmptyPool( svgame.temppool ); // all tempstrings can be frees now
2009-08-02 22:00:00 +02:00
2009-09-28 22:00:00 +02:00
// clear any temp states
sv.changelevel = false;
sv.loadgame = false;
2009-09-17 22:00:00 +02:00
MsgDev( D_INFO, "level loaded at %g sec\n", (Sys_Milliseconds() - svs.timestart) * 0.001f );
2008-07-12 22:00:00 +02:00
}
2009-11-10 22:00:00 +01:00
/*
==================
SV_TogglePause
==================
*/
void SV_TogglePause( const char *msg )
{
sv.paused ^= 1;
if( msg ) SV_BroadcastPrintf( PRINT_HIGH, "%s", msg );
// send notification to all clients
MSG_Begin( svc_setpause );
MSG_WriteByte( &sv.multicast, sv.paused );
MSG_Send( MSG_ALL_R, vec3_origin, NULL );
}
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 )
{
int playernum;
edict_t *ent;
if( cl->state != cs_connected )
{
MsgDev( D_INFO, "new is not valid from the console\n" );
return;
}
playernum = cl - svs.clients;
2008-07-15 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
// send the serverdata
MSG_WriteByte( &cl->netchan.message, svc_serverdata );
2009-09-16 22:00:00 +02:00
MSG_WriteLong( &cl->netchan.message, PROTOCOL_VERSION);
2008-07-12 22:00:00 +02:00
MSG_WriteLong( &cl->netchan.message, svs.spawncount );
MSG_WriteShort( &cl->netchan.message, playernum );
MSG_WriteString( &cl->netchan.message, sv.configstrings[CS_NAME] );
2009-09-28 22:00:00 +02:00
MSG_WriteString( &cl->netchan.message, STRING( EDICT_NUM( 0 )->v.message )); // Map Message
2008-07-12 22:00:00 +02:00
// game server
if( sv.state == ss_active )
{
// set up the entity for the client
2008-12-15 22:00:00 +01:00
ent = EDICT_NUM( playernum + 1 );
ent->serialnumber = playernum + 1;
2008-07-12 22:00:00 +02:00
cl->edict = ent;
2009-06-24 22:00:00 +02:00
Mem_Set( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
2008-07-12 22:00:00 +02:00
// begin fetching configstrings
MSG_WriteByte( &cl->netchan.message, svc_stufftext );
2009-09-28 22:00:00 +02:00
MSG_WriteString( &cl->netchan.message, va( "cmd configstrings %i %i\n", svs.spawncount, 0 ));
2008-07-12 22:00:00 +02:00
}
}
/*
==================
SV_Configstrings_f
==================
*/
void SV_Configstrings_f( sv_client_t *cl )
{
int start;
string cs;
if( cl->state != cs_connected )
{
MsgDev( D_INFO, "configstrings is not valid from the console\n" );
return;
}
// handle the case of a level changing while a client was connecting
2008-12-21 22:00:00 +01:00
if( com.atoi( Cmd_Argv( 1 )) != svs.spawncount )
2008-07-12 22:00:00 +02:00
{
MsgDev( D_INFO, "configstrings from different level\n" );
SV_New_f( cl );
return;
}
2008-12-21 22:00:00 +01:00
start = com.atoi( Cmd_Argv( 2 ));
2008-07-12 22:00:00 +02:00
// write a packet full of data
2008-12-21 22:00:00 +01:00
while( cl->netchan.message.cursize < (MAX_MSGLEN / 2) && start < MAX_CONFIGSTRINGS )
2008-07-12 22:00:00 +02:00
{
if( sv.configstrings[start][0])
{
MSG_WriteByte( &cl->netchan.message, svc_configstring );
MSG_WriteShort( &cl->netchan.message, start );
MSG_WriteString( &cl->netchan.message, sv.configstrings[start] );
}
start++;
}
2008-12-21 22:00:00 +01:00
if( start == MAX_CONFIGSTRINGS ) com.snprintf( cs, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, 0 );
2008-07-12 22:00:00 +02:00
else com.snprintf( cs, MAX_STRING, "cmd configstrings %i %i\n", svs.spawncount, start );
// send next command
MSG_WriteByte( &cl->netchan.message, svc_stufftext );
MSG_WriteString( &cl->netchan.message, cs );
}
/*
==================
SV_Baselines_f
==================
*/
void SV_Baselines_f( sv_client_t *cl )
{
int start;
entity_state_t *base, nullstate;
string baseline;
if( cl->state != cs_connected )
{
MsgDev( D_INFO, "baselines is not valid from the console\n" );
return;
}
// handle the case of a level changing while a client was connecting
2009-06-24 22:00:00 +02:00
if( com.atoi( Cmd_Argv( 1 )) != svs.spawncount )
2008-07-12 22:00:00 +02:00
{
MsgDev( D_INFO, "baselines from different level\n" );
SV_New_f( cl );
return;
}
2009-10-13 22:00:00 +02:00
start = com.atoi( Cmd_Argv( 2 ));
2008-07-12 22:00:00 +02:00
2009-10-13 22:00:00 +02:00
Mem_Set( &nullstate, 0, sizeof( nullstate ));
2008-07-12 22:00:00 +02:00
// write a packet full of data
2009-09-24 22:00:00 +02:00
while( cl->netchan.message.cursize < MAX_MSGLEN / 2 && start < GI->max_edicts )
2008-07-12 22:00:00 +02:00
{
2008-07-17 22:00:00 +02:00
base = &svs.baselines[start];
2009-01-05 22:00:00 +01:00
if( base->modelindex || base->soundindex || base->effects )
2008-07-12 22:00:00 +02:00
{
MSG_WriteByte( &cl->netchan.message, svc_spawnbaseline );
2008-07-15 22:00:00 +02:00
MSG_WriteDeltaEntity( &nullstate, base, &cl->netchan.message, true, true );
2008-07-12 22:00:00 +02:00
}
start++;
}
2009-09-24 22:00:00 +02:00
if( start == GI->max_edicts ) com.snprintf( baseline, MAX_STRING, "precache %i\n", svs.spawncount );
2009-10-13 22:00:00 +02:00
else com.snprintf( baseline, MAX_STRING, "cmd baselines %i %i\n", svs.spawncount, start );
2008-07-12 22:00:00 +02:00
// send next command
MSG_WriteByte( &cl->netchan.message, svc_stufftext );
MSG_WriteString( &cl->netchan.message, baseline );
}
/*
==================
SV_Begin_f
==================
*/
void SV_Begin_f( sv_client_t *cl )
{
// handle the case of a level changing while a client was connecting
2009-06-24 22:00:00 +02:00
if( com.atoi( Cmd_Argv( 1 )) != svs.spawncount )
2008-07-12 22:00:00 +02:00
{
Msg( "begin from different level\n" );
SV_New_f( cl );
return;
}
cl->state = cs_spawned;
2008-07-30 22:00:00 +02:00
SV_PutClientInServer( cl->edict );
2009-11-10 22:00:00 +01:00
// if we are paused, tell the client
if( sv.paused )
{
MSG_Begin( svc_setpause );
MSG_WriteByte( &sv.multicast, sv.paused );
MSG_Send( MSG_ONE_R, vec3_origin, cl->edict );
SV_ClientPrintf( cl, PRINT_HIGH, "Server is paused.\n" );
}
2008-07-12 22:00:00 +02:00
}
/*
==================
SV_NextDownload_f
==================
*/
void SV_NextDownload_f( sv_client_t *cl )
{
int percent;
int r, size;
if( !cl->download ) return;
r = cl->downloadsize - cl->downloadcount;
if( r > 1024 ) r = 1024;
MSG_WriteByte( &cl->netchan.message, svc_download );
MSG_WriteShort( &cl->netchan.message, r );
cl->downloadcount += r;
size = cl->downloadsize;
if( !size ) size = 1;
percent = cl->downloadcount * 100 / size;
MSG_WriteByte( &cl->netchan.message, percent );
MSG_WriteData( &cl->netchan.message, cl->download + cl->downloadcount - r, r );
if( cl->downloadcount == cl->downloadsize ) cl->download = NULL;
}
/*
==================
SV_BeginDownload_f
==================
*/
void SV_BeginDownload_f( sv_client_t *cl )
{
char *name;
int offset = 0;
name = Cmd_Argv( 1 );
if(Cmd_Argc() > 2 ) offset = com.atoi(Cmd_Argv(2)); // continue download from
cl->download = FS_LoadFile( name, &cl->downloadsize );
cl->downloadcount = offset;
if( offset > cl->downloadsize ) cl->downloadcount = cl->downloadsize;
if( !allow_download->integer || !cl->download )
{
MsgDev( D_ERROR, "SV_BeginDownload_f: couldn't download %s to %s\n", name, cl->name );
2009-08-02 22:00:00 +02:00
if( cl->download ) Mem_Free( cl->download );
2008-07-12 22:00:00 +02:00
MSG_WriteByte( &cl->netchan.message, svc_download );
MSG_WriteShort( &cl->netchan.message, -1 );
MSG_WriteByte( &cl->netchan.message, 0 );
2009-08-02 22:00:00 +02:00
cl->download = NULL;
2008-07-12 22:00:00 +02:00
return;
}
SV_NextDownload_f( cl );
MsgDev( D_INFO, "Downloading %s to %s\n", name, cl->name );
}
/*
=================
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 )
{
2009-06-24 22:00:00 +02:00
Info_Print( Cvar_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;
if( !sv_pausable->integer )
{
SV_ClientPrintf( cl, PRINT_HIGH, "Pause not allowed.\n" );
return;
}
if( cl->edict->v.flags & FL_SPECTATOR )
{
SV_ClientPrintf( cl, PRINT_HIGH, "Spectators can not pause.\n" );
return;
}
if( !sv.paused ) com.snprintf( message, MAX_STRING, "%s paused the game\n", cl->name );
else com.snprintf( message, MAX_STRING, "%s unpaused the game\n", cl->name );
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.
=================
*/
void SV_UserinfoChanged( sv_client_t *cl )
{
char *val;
int i;
// name for C code (make colored string)
com.snprintf( cl->name, sizeof(cl->name), "^2%s", Info_ValueForKey( cl->userinfo, "name"));
2009-06-24 22:00:00 +02:00
// rate command
val = Info_ValueForKey( cl->userinfo, "rate" );
if( com.strlen( val ))
2008-07-12 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
i = com.atoi( val );
2009-09-16 22:00:00 +02:00
cl->rate = i;
cl->rate = bound ( 100, cl->rate, 15000 );
2008-07-12 22:00:00 +02:00
}
2009-09-16 22:00:00 +02:00
else cl->rate = 5000;
2009-09-15 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
// msg command
val = Info_ValueForKey( cl->userinfo, "msg" );
if( com.strlen( val ))
2008-07-12 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
cl->messagelevel = com.atoi( val );
2008-07-12 22:00:00 +02:00
}
}
/*
==================
SV_UpdateUserinfo_f
==================
*/
static void SV_UpdateUserinfo_f( sv_client_t *cl )
{
com.strncpy( cl->userinfo, Cmd_Argv(1), sizeof(cl->userinfo));
SV_UserinfoChanged( cl );
// call prog code to allow overrides
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
svgame.globals->frametime = sv.frametime * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnClientUserInfoChanged( cl->edict, cl->userinfo );
2008-07-12 22:00:00 +02:00
}
ucmd_t ucmds[] =
{
2009-11-02 22:00:00 +01:00
{ "new", SV_New_f },
{ "begin", SV_Begin_f },
2009-11-10 22:00:00 +01:00
{ "pause", SV_Pause_f },
2009-11-02 22:00:00 +01:00
{ "baselines", SV_Baselines_f },
{ "info", SV_ShowServerinfo_f },
{ "nextdl", SV_NextDownload_f },
{ "disconnect", SV_Disconnect_f },
{ "download", SV_BeginDownload_f },
{ "userinfo", SV_UpdateUserinfo_f },
{ "configstrings", SV_Configstrings_f },
{ 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
2008-07-12 22:00:00 +02:00
Cmd_TokenizeString( s );
for( u = ucmds; u->name; u++ )
{
2009-11-02 22:00:00 +01:00
if( !com.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 );
2008-07-12 22:00:00 +02:00
u->func( cl );
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
2009-09-17 22:00:00 +02:00
svgame.globals->time = sv.time * 0.001f;
svgame.globals->frametime = sv.frametime * 0.001f;
2008-12-26 22:00:00 +01:00
svgame.dllFuncs.pfnClientCommand( cl->edict );
2008-07-12 22:00:00 +02:00
}
}
2008-07-16 22:00:00 +02:00
/*
=================
MSG_Begin
Misc helper function
=================
*/
void _MSG_Begin( int dest, const char *filename, int fileline )
{
2008-10-19 22:00:00 +02:00
_MSG_WriteBits( &sv.multicast, dest, "MSG_Begin", NET_BYTE, filename, fileline );
2008-07-16 22:00:00 +02:00
}
/*
=================
SV_Send
Sends the contents of sv.multicast to a subset of the clients,
then clears sv.multicast.
MULTICAST_ONE send to one client (ent can't be NULL)
MULTICAST_ALL same as broadcast (origin can be NULL)
MULTICAST_PVS send to clients potentially visible from org
MULTICAST_PHS send to clients potentially hearable from org
=================
*/
2009-01-23 22:00:00 +01:00
void _MSG_Send( msgtype_t msg_type, vec3_t origin, const edict_t *ent, const char *filename, int fileline )
2008-07-16 22:00:00 +02:00
{
byte *mask = NULL;
int leafnum = 0, cluster = 0;
int area1 = 0, area2 = 0;
2009-09-25 22:00:00 +02:00
int j, numclients = sv_maxclients->integer;
2008-07-16 22:00:00 +02:00
sv_client_t *cl, *current = svs.clients;
bool reliable = false;
switch( msg_type )
{
case MSG_ALL_R:
reliable = true;
// intentional fallthrough
case MSG_ALL:
// nothing to sort
break;
case MSG_PHS_R:
reliable = true;
// intentional fallthrough
case MSG_PHS:
if( origin == NULL ) return;
2009-11-03 22:00:00 +01:00
leafnum = CM_PointLeafnum( origin );
cluster = CM_LeafCluster( leafnum );
mask = CM_ClusterPHS( cluster );
area1 = CM_LeafArea( leafnum );
2008-07-16 22:00:00 +02:00
break;
case MSG_PVS_R:
reliable = true;
// intentional fallthrough
case MSG_PVS:
if( origin == NULL ) return;
2009-11-03 22:00:00 +01:00
leafnum = CM_PointLeafnum( origin );
cluster = CM_LeafCluster( leafnum );
mask = CM_ClusterPVS( cluster );
area1 = CM_LeafArea( leafnum );
2008-07-16 22:00:00 +02:00
break;
case MSG_ONE_R:
reliable = true;
// intentional fallthrough
case MSG_ONE:
if( ent == NULL ) return;
2008-12-15 22:00:00 +01:00
j = NUM_FOR_EDICT( ent );
2008-07-16 22:00:00 +02:00
if( j < 1 || j > numclients ) return;
current = svs.clients + (j - 1);
numclients = 1; // send to one
break;
default:
2009-11-02 22:00:00 +01:00
MsgDev( D_ERROR, "MSG_Send: bad dest: %i (called at %s:%i)\n", msg_type, filename, fileline );
2008-07-16 22:00:00 +02:00
return;
}
// send the data to all relevent clients (or once only)
for( j = 0, cl = current; j < numclients; j++, cl++ )
{
if( cl->state == cs_free || cl->state == cs_zombie )
continue;
if( cl->state != cs_spawned && !reliable )
continue;
2009-11-02 22:00:00 +01:00
if( cl->edict && ( cl->edict->v.flags & FL_FAKECLIENT ))
2009-06-24 22:00:00 +02:00
continue;
2008-07-16 22:00:00 +02:00
if( mask )
{
2009-11-03 22:00:00 +01:00
area2 = CM_LeafArea( leafnum );
cluster = CM_LeafCluster( leafnum );
leafnum = CM_PointLeafnum( cl->edict->v.origin );
if(!CM_AreasConnected( area1, area2 )) continue;
2009-11-02 22:00:00 +01:00
if( mask && (!(mask[cluster>>3] & (1<<(cluster & 7)))))
2008-07-16 22:00:00 +02:00
continue;
}
2009-09-19 22:00:00 +02:00
if( reliable ) MSG_WriteData( &cl->reliable, sv.multicast.data, sv.multicast.cursize );
2008-07-16 22:00:00 +02:00
else MSG_WriteData( &cl->datagram, sv.multicast.data, sv.multicast.cursize );
}
MSG_Clear( &sv.multicast );
}
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.
=================
*/
void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
{
char *s;
char *c;
MSG_BeginReading( msg );
MSG_ReadLong( msg );// skip the -1 marker
s = MSG_ReadStringLine( msg );
Cmd_TokenizeString( s );
c = Cmd_Argv( 0 );
2009-09-28 22:00:00 +02:00
MsgDev( D_INFO, "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );
2008-07-12 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
if( !com.strcmp( c, "ping" )) SV_Ping( from );
else if( !com.strcmp( c, "ack" )) SV_Ack( from );
2009-11-10 22:00:00 +01:00
else if( !com.strcmp( c, "status" )) SV_Status( from );
else if( !com.strcmp( c, "info" )) SV_Info( from );
else if( !com.strcmp( c, "getchallenge" )) SV_GetChallenge( from );
else if( !com.strcmp( c, "connect" )) SV_DirectConnect( from );
2009-06-24 22:00:00 +02:00
else if( !com.strcmp( c, "rcon" )) SV_RemoteCommand( from, msg );
2008-07-12 22:00:00 +02:00
else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), s );
}
2009-11-10 22:00:00 +01:00
/*
============================================================
CLIENT MOVEMENT CODE
============================================================
*/
// builtins
/*
=================
PM_ClientPrintf
Sends text across to be displayed
=================
*/
void PM_ClientPrintf( int index, char *fmt, ... )
{
va_list argptr;
char string[MAX_SYSPATH];
sv_client_t *cl;
2009-11-15 22:00:00 +01:00
if( index < 0 || index >= sv_maxclients->integer )
2009-11-10 22:00:00 +01:00
return;
cl = svs.clients + index;
if( cl->edict && (cl->edict->v.flags & FL_FAKECLIENT ))
return;
va_start( argptr, fmt );
com.vsprintf( string, fmt, argptr );
va_end( argptr );
MSG_WriteByte( &cl->netchan.message, svc_print );
MSG_WriteString( &cl->netchan.message, string );
}
/*
===============
PM_PlayerTrace
===============
*/
2009-11-15 22:00:00 +01:00
static TraceResult PM_PlayerTrace( const vec3_t start, const vec3_t end, int trace_type )
2009-11-10 22:00:00 +01:00
{
2009-11-15 22:00:00 +01:00
float *mins;
float *maxs;
2009-11-10 22:00:00 +01:00
trace_t result;
TraceResult out;
2009-11-15 22:00:00 +01:00
if( VectorIsNAN( start ) || VectorIsNAN( end ))
Host_Error( "TraceTexture: NAN errors detected ('%f %f %f', '%f %f %f'\n", start[0], start[1], start[2], end[0], end[1], end[2] );
2009-11-10 22:00:00 +01:00
2009-11-15 22:00:00 +01:00
svgame.pmove->usehull = bound( 0, svgame.pmove->usehull, 3 );
mins = svgame.pmove->player_mins[svgame.pmove->usehull];
maxs = svgame.pmove->player_maxs[svgame.pmove->usehull];
result = SV_Move( start, mins, maxs, end, trace_type, svgame.pmove->player );
2009-11-10 22:00:00 +01:00
Mem_Copy( &out, &result, sizeof( TraceResult ));
return out;
}
/*
===============
PM_TraceTexture
===============
*/
const char *PM_TraceTexture( edict_t *pTextureEntity, const float *v1, const float *v2 )
{
2009-11-15 22:00:00 +01:00
if( VectorIsNAN( v1 ) || VectorIsNAN( v2 ))
Host_Error( "TraceTexture: NAN errors detected ('%f %f %f', '%f %f %f'\n", v1[0], v1[1], v1[2], v2[0], v2[1], v2[2] );
2009-11-13 22:00:00 +01:00
if( !pTextureEntity || pTextureEntity->free ) return NULL;
2009-11-15 22:00:00 +01:00
return SV_ClipMoveToEntity( pTextureEntity, v1, vec3_origin, vec3_origin, v2, MASK_SOLID ).pTexName;
2009-11-10 22:00:00 +01:00
}
/*
===============
PM_TraceModel
===============
*/
TraceResult PM_TraceModel( edict_t *pEnt, const vec3_t start, const vec3_t end )
{
2009-11-15 22:00:00 +01:00
float *mins;
float *maxs;
2009-11-10 22:00:00 +01:00
trace_t result;
TraceResult out;
2009-11-15 22:00:00 +01:00
uint umask;
if( VectorIsNAN( start ) || VectorIsNAN( end ))
Host_Error( "TraceTexture: NAN errors detected ('%f %f %f', '%f %f %f'\n", start[0], start[1], start[2], end[0], end[1], end[2] );
2009-11-10 22:00:00 +01:00
2009-11-15 22:00:00 +01:00
umask = World_MaskForEdict( svgame.pmove->player );
svgame.pmove->usehull = bound( 0, svgame.pmove->usehull, 3 );
mins = svgame.pmove->player_mins[svgame.pmove->usehull];
maxs = svgame.pmove->player_maxs[svgame.pmove->usehull];
result = SV_ClipMoveToEntity( pEnt, start, mins, maxs, end, umask );
2009-11-10 22:00:00 +01:00
Mem_Copy( &out, &result, sizeof( TraceResult ));
return out;
}
/*
===============
PM_GetEntityByIndex
safe version of SV_EDICT_NUM
===============
*/
edict_t *PM_GetEntityByIndex( int index )
{
if( index < 0 || index > svgame.globals->numEntities )
{
2009-11-15 22:00:00 +01:00
if( index == VIEWENT_INDEX ) return svgame.pmove->player->v.aiment; // current weapon
2009-11-10 22:00:00 +01:00
if( index == NULLENT_INDEX ) return NULL;
MsgDev( D_ERROR, "PM_GetEntityByIndex: invalid entindex %i\n", index );
return NULL;
}
if( EDICT_NUM( index )->free )
return NULL;
return EDICT_NUM( index );
}
void PM_PlaySound( int chan, const char *sample, float vol, float attn, int pitch )
{
if( !svgame.pmove->runfuncs ) return; // ignored
2009-11-15 22:00:00 +01:00
SV_StartSound( svgame.pmove->player, chan, sample, vol, attn, 0, pitch );
2009-11-10 22:00:00 +01:00
}
2009-11-15 22:00:00 +01:00
edict_t *PM_TestPlayerPosition( const vec3_t origin, TraceResult *trace )
2009-11-10 22:00:00 +01:00
{
2009-11-15 22:00:00 +01:00
return SV_TestPlayerPosition( origin, svgame.pmove->player, trace );
2009-11-10 22:00:00 +01:00
}
/*
===============
SV_InitClientMove
===============
*/
void SV_InitClientMove( void )
{
int i;
svgame.pmove->movevars = &svgame.movevars;
// init hulls
for( i = 0; i < PM_MAXHULLS; i++ )
{
VectorCopy( GI->client_mins[i], svgame.pmove->player_mins[i] );
VectorCopy( GI->client_maxs[i], svgame.pmove->player_maxs[i] );
2009-11-16 22:00:00 +01:00
svgame.pmove->player_view[i] = GI->viewheight[i];
2009-11-10 22:00:00 +01:00
}
// common utilities
svgame.pmove->PM_Info_ValueForKey = Info_ValueForKey;
2009-11-15 22:00:00 +01:00
svgame.pmove->PM_TestPlayerPosition = PM_TestPlayerPosition;
2009-11-10 22:00:00 +01:00
svgame.pmove->ClientPrintf = PM_ClientPrintf;
svgame.pmove->AlertMessage = pfnAlertMessage;
svgame.pmove->PM_GetString = SV_GetString;
svgame.pmove->PM_PointContents = SV_PointContents;
svgame.pmove->PM_PlayerTrace = PM_PlayerTrace;
svgame.pmove->PM_TraceTexture = PM_TraceTexture;
svgame.pmove->PM_GetEntityByIndex = PM_GetEntityByIndex;
svgame.pmove->AngleVectors = AngleVectors;
svgame.pmove->RandomLong = pfnRandomLong;
svgame.pmove->RandomFloat = pfnRandomFloat;
svgame.pmove->PM_GetModelType = CM_GetModelType;
svgame.pmove->PM_GetModelBounds = Mod_GetBounds;
svgame.pmove->PM_ModExtradata = Mod_Extradata;
svgame.pmove->PM_TraceModel = PM_TraceModel;
svgame.pmove->COM_LoadFile = pfnLoadFile;
svgame.pmove->COM_ParseToken = pfnParseToken;
svgame.pmove->COM_FreeFile = pfnFreeFile;
svgame.pmove->memfgets = pfnMemFgets;
svgame.pmove->PM_PlaySound = PM_PlaySound;
// initalize pmove
svgame.dllFuncs.pfnPM_Init( svgame.pmove );
}
2009-11-15 22:00:00 +01:00
void SV_PreRunCmd( sv_client_t *cl, usercmd_t *ucmd )
2009-11-10 22:00:00 +01:00
{
svgame.pmove->runfuncs = true;
}
/*
===========
SV_RunCmd
===========
*/
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd )
{
2009-11-15 22:00:00 +01:00
edict_t *clent;
int i, oldmsec;
static usercmd_t cmd;
vec3_t oldvel;
2009-11-10 22:00:00 +01:00
cl->commandMsec -= ucmd->msec;
if( cl->commandMsec < 0 && sv_enforcetime->integer )
{
MsgDev( D_INFO, "SV_ClientThink: commandMsec underflow from %s\n", cl->name );
return;
}
clent = cl->edict;
2009-11-15 22:00:00 +01:00
cmd = *ucmd;
2009-11-10 22:00:00 +01:00
if( !clent || clent->free ) return;
// chop up very long commands
2009-11-15 22:00:00 +01:00
if( cmd.msec > 50 )
2009-11-10 22:00:00 +01:00
{
oldmsec = ucmd->msec;
2009-11-15 22:00:00 +01:00
cmd.msec = oldmsec / 2;
SV_RunCmd( cl, &cmd );
cmd.msec = oldmsec / 2;
cmd.impulse = 0;
SV_RunCmd( cl, &cmd );
2009-11-10 22:00:00 +01:00
return;
}
2009-11-15 22:00:00 +01:00
VectorCopy( clent->v.viewangles, svgame.pmove->oldangles ); // save oldangles
2009-11-10 22:00:00 +01:00
if( !clent->v.fixangle )
2009-11-15 22:00:00 +01:00
VectorCopy( ucmd->viewangles, clent->v.viewangles );
2009-11-10 22:00:00 +01:00
// copy player buttons
clent->v.button = ucmd->buttons;
2009-11-15 22:00:00 +01:00
if( ucmd->impulse ) clent->v.impulse = ucmd->impulse;
2009-11-10 22:00:00 +01:00
// angles
// show 1/3 the pitch angle and all the roll angle
if( clent->v.health > 0.0f )
{
if( !clent->v.fixangle )
{
clent->v.angles[PITCH] = -clent->v.viewangles[PITCH] / 3;
clent->v.angles[YAW] = clent->v.viewangles[YAW];
}
}
if(!( clent->v.flags & FL_SPECTATOR ))
{
2009-11-15 22:00:00 +01:00
svgame.globals->time = sv.time * 0.001f;
2009-11-10 22:00:00 +01:00
svgame.globals->frametime = 0.0f;
svgame.dllFuncs.pfnPlayerPreThink( clent );
svgame.globals->frametime = ucmd->msec * 0.001f;
SV_RunThink( clent );
}
// setup playermove state
svgame.pmove->multiplayer = (sv_maxclients->integer > 1) ? true : false;
svgame.pmove->realtime = svs.realtime * 0.001f;
svgame.pmove->frametime = ucmd->msec * 0.001f;
com.strncpy( svgame.pmove->physinfo, cl->userinfo, MAX_INFO_STRING );
svgame.pmove->serverflags = svgame.globals->serverflags;
svgame.pmove->clientmaxspeed = clent->v.maxspeed;
svgame.pmove->maxspeed = svgame.movevars.maxspeed;
svgame.pmove->cmd = *ucmd; // setup current cmds
2009-11-15 22:00:00 +01:00
svgame.pmove->player = clent; // ptr to client state
2009-11-10 22:00:00 +01:00
svgame.pmove->numtouch = 0; // reset touchents
svgame.pmove->dead = (clent->v.health <= 0.0f) ? true : false;
svgame.pmove->flWaterJumpTime = clent->v.teleport_time;
svgame.pmove->onground = clent->v.groundentity;
svgame.pmove->usehull = clent->v.bInDuck ? 1 : 0; // reset hull
for( i = 0; i < 3; i++ )
svgame.pmove->origin[i] = clent->v.origin[i] + (clent->v.mins[i] - svgame.pmove->player_mins[svgame.pmove->usehull][i]);
// motor!
svgame.dllFuncs.pfnPM_Move( svgame.pmove, true );
// copy results back to client
clent->v.teleport_time = svgame.pmove->flWaterJumpTime;
clent->v.groundentity = svgame.pmove->onground;
2009-11-15 22:00:00 +01:00
VectorCopy( svgame.pmove->angles, clent->v.viewangles );
2009-11-10 22:00:00 +01:00
for( i = 0; i < 3; i++ )
clent->v.origin[i] = svgame.pmove->origin[i] - (clent->v.mins[i] - svgame.pmove->player_mins[svgame.pmove->usehull][i]);
2009-11-15 22:00:00 +01:00
VectorCopy( clent->v.velocity, oldvel ); // save velocity
2009-11-10 22:00:00 +01:00
if(!( clent->v.flags & FL_SPECTATOR ))
{
// link into place and touch triggers
SV_LinkEdict( clent, true );
// touch other objects
for( i = 0; i < svgame.pmove->numtouch; i++ )
{
if( i == MAX_PHYSENTS ) break;
2009-11-15 22:00:00 +01:00
if( svgame.pmove->touchents[i] == clent ) continue;
VectorCopy( svgame.pmove->touchvels[i], clent->v.velocity );
2009-11-10 22:00:00 +01:00
svgame.dllFuncs.pfnTouch( svgame.pmove->touchents[i], clent );
}
}
2009-11-15 22:00:00 +01:00
VectorCopy( oldvel, clent->v.velocity ); // save velocity
svgame.pmove->numtouch = 0;
2009-11-10 22:00:00 +01:00
}
/*
===========
SV_PostRunCmd
Done after running a player command.
===========
*/
void SV_PostRunCmd( sv_client_t *cl )
{
edict_t *clent;
clent = cl->edict;
if( !clent || clent->free ) return;
2009-11-15 22:00:00 +01:00
svgame.pmove->runfuncs = false; // all next calls ignore footstep sounds
2009-11-10 22:00:00 +01:00
// run post-think
if(!( clent->v.flags & FL_SPECTATOR ))
{
svgame.globals->time = sv.time * 0.001f;
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnPlayerPostThink( clent );
}
else
{
svgame.globals->time = sv.time * 0.001f;
svgame.globals->frametime = 0;
svgame.dllFuncs.pfnSpectatorThink( clent );
}
// restore frametime
svgame.globals->frametime = sv.frametime * 0.001f;
}
2009-09-13 22:00:00 +02:00
/*
===============
SV_SetIdealPitch
===============
*/
void SV_SetIdealPitch( sv_client_t *cl )
{
2009-10-28 22:00:00 +01:00
float angleval, sinval, cosval;
2009-11-02 22:00:00 +01:00
trace_t tr;
2009-10-28 22:00:00 +01:00
vec3_t top, bottom;
float z[MAX_FORWARD];
int i, j;
int step, dir, steps;
edict_t *ent = cl->edict;
2009-09-13 22:00:00 +02:00
if( !( ent->v.flags & FL_ONGROUND ))
return;
angleval = ent->v.angles[YAW] * M_PI * 2 / 360;
com.sincos( angleval, &sinval, &cosval );
for( i = 0; i < MAX_FORWARD; i++ )
{
top[0] = ent->v.origin[0] + cosval * (i + 3) * 12;
top[1] = ent->v.origin[1] + sinval * (i + 3) * 12;
top[2] = ent->v.origin[2] + ent->v.view_ofs[2];
bottom[0] = top[0];
bottom[1] = top[1];
bottom[2] = top[2] - 160;
2009-11-02 22:00:00 +01:00
tr = SV_Move( top, vec3_origin, vec3_origin, bottom, MOVE_NOMONSTERS, ent );
2009-10-28 22:00:00 +01:00
if( tr.fAllSolid )
2009-09-13 22:00:00 +02:00
return; // looking at a wall, leave ideal the way is was
2009-10-28 22:00:00 +01:00
if( tr.flFraction == 1.0f )
2009-09-13 22:00:00 +02:00
return; // near a dropoff
2009-10-28 22:00:00 +01:00
z[i] = top[2] + tr.flFraction * (bottom[2] - top[2]);
2009-09-13 22:00:00 +02:00
}
dir = 0;
steps = 0;
for( j = 1; j < i; j++ )
{
step = z[j] - z[j-1];
if( step > -ON_EPSILON && step < ON_EPSILON )
continue;
if( dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ))
return; // mixed changes
steps++;
dir = step;
}
if( !dir )
{
ent->v.ideal_pitch = 0;
return;
}
if( steps < 2 ) return;
ent->v.ideal_pitch = -dir * sv_idealpitchscale->value;
}
2009-11-02 22:00:00 +01:00
/*
==================
SV_UserFriction
==================
*/
void SV_UserFriction( sv_client_t *cl )
{
float speed, newspeed;
float *origin, *vel;
float control, friction;
vec3_t start, stop;
trace_t trace;
vel = cl->edict->v.velocity;
origin = cl->edict->v.origin;
speed = com.sqrt( vel[0] * vel[0] + vel[1] * vel[1] );
if( !speed ) return;
// if the leading edge is over a dropoff, increase friction
start[0] = stop[0] = origin[0] + vel[0] / speed * 16;
start[1] = stop[1] = origin[1] + vel[1] / speed * 16;
start[2] = origin[2] + cl->edict->v.mins[2];
stop[2] = start[2] - 34;
trace = SV_Move( start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, cl->edict );
if( trace.flFraction == 1.0 )
friction = sv_friction->value * sv_edgefriction->value;
else friction = sv_friction->value;
// apply friction
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
newspeed = speed - svgame.globals->frametime * control * friction;
if( newspeed < 0 ) newspeed = 0;
else newspeed /= speed;
VectorScale( vel, newspeed, vel );
}
2008-07-30 22:00:00 +02:00
/*
==============
SV_Accelerate
==============
*/
void SV_Accelerate( sv_client_t *cl )
{
int i;
float addspeed;
float accelspeed, currentspeed;
2008-12-15 22:00:00 +01:00
currentspeed = DotProduct( cl->edict->v.velocity, wishdir );
2008-07-30 22:00:00 +02:00
addspeed = wishspeed - currentspeed;
if( addspeed <= 0 ) return;
2009-09-17 22:00:00 +02:00
accelspeed = sv_accelerate->value * svgame.globals->frametime * wishspeed;
2008-07-30 22:00:00 +02:00
if( accelspeed > addspeed ) accelspeed = addspeed;
2009-11-02 22:00:00 +01:00
for( i = 0; i < 3; i++ )
cl->edict->v.velocity[i] += accelspeed * wishdir[i];
2008-07-30 22:00:00 +02:00
}
2009-11-02 22:00:00 +01:00
/*
==============
SV_AirAccelerate
==============
*/
2008-07-30 22:00:00 +02:00
void SV_AirAccelerate( sv_client_t *cl, vec3_t wishveloc )
{
int i;
2009-11-02 22:00:00 +01:00
float addspeed, accel;
2008-07-30 22:00:00 +02:00
float wishspd, accelspeed, currentspeed;
2009-11-02 22:00:00 +01:00
accel = sv_airaccelerate->value ? sv_airaccelerate->value : 1.0f;
2008-07-30 22:00:00 +02:00
wishspd = VectorNormalizeLength( wishveloc );
if( wishspd > 30 ) wishspd = 30;
2008-12-15 22:00:00 +01:00
currentspeed = DotProduct( cl->edict->v.velocity, wishveloc );
2008-07-30 22:00:00 +02:00
addspeed = wishspd - currentspeed;
if( addspeed <= 0 ) return;
2009-11-02 22:00:00 +01:00
accelspeed = accel * wishspeed * svgame.globals->frametime;
2008-07-30 22:00:00 +02:00
if( accelspeed > addspeed ) accelspeed = addspeed;
2009-11-02 22:00:00 +01:00
for( i = 0; i < 3; i++ )
cl->edict->v.velocity[i] += accelspeed * wishveloc[i];
}
void SV_DropPunchAngle( sv_client_t *cl )
{
float len;
len = VectorNormalizeLength( cl->edict->v.punchangle );
len -= 10 * svgame.globals->frametime;
if( len < 0 ) len = 0;
VectorScale( cl->edict->v.punchangle, len, cl->edict->v.punchangle );
2008-07-30 22:00:00 +02:00
}
/*
===================
SV_WaterMove
===================
*/
void SV_WaterMove( sv_client_t *cl, usercmd_t *cmd )
{
int i;
vec3_t wishvel;
float speed, newspeed;
float wishspeed, addspeed;
2009-11-02 22:00:00 +01:00
float accelspeed;
2008-07-30 22:00:00 +02:00
// user intentions
2009-01-06 22:00:00 +01:00
AngleVectors( cl->edict->v.viewangles, forward, right, up );
2008-07-30 22:00:00 +02:00
2009-11-02 22:00:00 +01:00
for( i = 0; i < 3; i++ )
wishvel[i] = forward[i] * cmd->forwardmove + right[i] * cmd->sidemove;
2008-07-30 22:00:00 +02:00
2009-11-02 22:00:00 +01:00
if( !cmd->forwardmove && !cmd->sidemove && !cmd->upmove )
2008-07-30 22:00:00 +02:00
wishvel[2] -= 60; // drift towards bottom
else wishvel[2] += cmd->upmove;
wishspeed = VectorLength( wishvel );
if( wishspeed > sv_maxspeed->value )
{
2009-11-02 22:00:00 +01:00
VectorScale( wishvel, (sv_maxspeed->value / wishspeed), wishvel );
2008-07-30 22:00:00 +02:00
wishspeed = sv_maxspeed->value;
}
wishspeed *= 0.7;
// water friction
2008-12-15 22:00:00 +01:00
speed = VectorLength( cl->edict->v.velocity );
2008-07-30 22:00:00 +02:00
if( speed )
{
2009-09-17 22:00:00 +02:00
newspeed = speed - svgame.globals->frametime * speed * sv_friction->value;
2008-07-30 22:00:00 +02:00
if( newspeed < 0 ) newspeed = 0;
2009-11-02 22:00:00 +01:00
VectorScale( cl->edict->v.velocity, (newspeed / speed), cl->edict->v.velocity );
2008-07-30 22:00:00 +02:00
}
else newspeed = 0;
// water acceleration
if( !wishspeed ) return;
addspeed = wishspeed - newspeed;
if( addspeed <= 0 ) return;
VectorNormalize( wishvel );
2009-09-17 22:00:00 +02:00
accelspeed = sv_accelerate->value * wishspeed * svgame.globals->frametime;
2008-07-30 22:00:00 +02:00
if( accelspeed > addspeed ) accelspeed = addspeed;
2009-11-02 22:00:00 +01:00
for( i = 0; i < 3; i++ )
cl->edict->v.velocity[i] += accelspeed * wishvel[i];
2008-07-30 22:00:00 +02:00
}
void SV_WaterJump( sv_client_t *cl )
{
2009-09-17 22:00:00 +02:00
if( svgame.globals->time > cl->edict->v.teleport_time || !cl->edict->v.waterlevel )
2008-07-30 22:00:00 +02:00
{
2008-12-21 22:00:00 +01:00
cl->edict->v.flags &= ~FL_WATERJUMP;
2008-12-15 22:00:00 +01:00
cl->edict->v.teleport_time = 0;
2008-07-30 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
cl->edict->v.velocity[0] = cl->edict->v.movedir[0];
cl->edict->v.velocity[1] = cl->edict->v.movedir[1];
2008-07-30 22:00:00 +02:00
}
/*
===================
SV_AirMove
===================
*/
void SV_AirMove( sv_client_t *cl, usercmd_t *cmd )
{
int i;
vec3_t wishvel;
2009-11-02 22:00:00 +01:00
float fmove, smove;
2008-07-30 22:00:00 +02:00
2009-11-02 22:00:00 +01:00
wishvel[2] = 0;
wishvel[0] = -cl->edict->v.angles[0];
2008-12-15 22:00:00 +01:00
wishvel[1] = cl->edict->v.angles[1];
2008-07-30 22:00:00 +02:00
AngleVectors( wishvel, forward, right, up );
fmove = cmd->forwardmove;
smove = cmd->sidemove;
2009-02-03 22:00:00 +01:00
// HACKHACK to not let you back into teleporter
2009-09-17 22:00:00 +02:00
if( svgame.globals->time < cl->edict->v.teleport_time && fmove < 0 )
2008-07-30 22:00:00 +02:00
fmove = 0;
2009-11-02 22:00:00 +01:00
for( i = 0; i < 3; i++ )
wishvel[i] = forward[i] * fmove + right[i] * smove;
2008-07-30 22:00:00 +02:00
2009-11-02 22:00:00 +01:00
if( cl->edict->v.movetype != MOVETYPE_WALK )
2008-07-30 22:00:00 +02:00
wishvel[2] += cmd->upmove;
2009-11-02 22:00:00 +01:00
else wishvel[2] = 0;
2009-08-17 22:00:00 +02:00
wishspeed = VectorNormalizeLength2( wishvel, wishdir );
2008-07-30 22:00:00 +02:00
if( wishspeed > sv_maxspeed->value )
{
2009-11-02 22:00:00 +01:00
VectorScale( wishvel, (sv_maxspeed->value / wishspeed), wishvel );
2008-07-30 22:00:00 +02:00
wishspeed = sv_maxspeed->value;
}
2008-12-15 22:00:00 +01:00
if( cl->edict->v.movetype == MOVETYPE_NOCLIP )
2008-07-30 22:00:00 +02:00
{
// noclip
2008-12-15 22:00:00 +01:00
VectorCopy( wishvel, cl->edict->v.velocity );
2008-07-30 22:00:00 +02:00
}
else if( onground )
{
SV_UserFriction( cl );
SV_Accelerate( cl );
}
else
{
// not on ground, so little effect on velocity
SV_AirAccelerate( cl, wishvel );
}
}
2009-11-02 22:00:00 +01:00
/*
===============
SV_CalcRoll
Used by view and sv_user
===============
*/
float SV_CalcRoll( vec3_t angles, vec3_t velocity )
{
vec3_t right;
float sign, side, value;
AngleVectors( angles, NULL, right, NULL );
side = DotProduct( velocity, right );
sign = side < 0 ? -1 : 1;
side = fabs( side );
value = sv_rollangle->value;
if( side < sv_rollspeed->value )
side = side * value / sv_rollspeed->value;
else side = value;
return side*sign;
}
2008-07-30 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
/*
==================
SV_ClientThink
Also called by bot code
==================
*/
void SV_ClientThink( sv_client_t *cl, usercmd_t *cmd )
{
2009-01-06 22:00:00 +01:00
vec3_t viewangles;
2009-06-24 22:00:00 +02:00
2009-11-10 22:00:00 +01:00
if( sv.paused ) return; // paused
2009-11-02 22:00:00 +01:00
2009-06-24 22:00:00 +02:00
cl->commandMsec -= cmd->msec;
if( cl->commandMsec < 0 && sv_enforcetime->integer )
{
2009-11-02 22:00:00 +01:00
MsgDev( D_INFO, "SV_ClientThink: commandMsec underflow from %s\n", cl->name );
2009-06-24 22:00:00 +02:00
return;
}
2008-07-30 22:00:00 +02:00
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity( cl->edict );
2008-12-15 22:00:00 +01:00
if( cl->edict->v.movetype == MOVETYPE_NONE )
2008-07-30 22:00:00 +02:00
return;
2008-12-15 22:00:00 +01:00
onground = (cl->edict->v.flags & FL_ONGROUND);
2008-07-30 22:00:00 +02:00
SV_DropPunchAngle( cl );
// if dead, behave differently
2008-12-15 22:00:00 +01:00
if( cl->edict->v.health <= 0 )
2008-07-16 22:00:00 +02:00
return;
2008-07-30 22:00:00 +02:00
// show 1/3 the pitch angle and all the roll angle
2009-01-06 22:00:00 +01:00
VectorAdd( cl->edict->v.viewangles, cl->edict->v.punchangle, viewangles );
2009-09-13 22:00:00 +02:00
cl->edict->v.viewangles[ROLL] = SV_CalcRoll( viewangles, cl->edict->v.velocity ) * 4;
2009-11-02 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
if( !cl->edict->v.fixangle )
2008-07-30 22:00:00 +02:00
{
2009-11-02 22:00:00 +01:00
cl->edict->v.angles[PITCH] = -viewangles[PITCH] / 3;
2009-01-06 22:00:00 +01:00
cl->edict->v.angles[YAW] = viewangles[YAW];
2008-07-30 22:00:00 +02:00
}
2009-11-02 22:00:00 +01:00
// waterjump
2008-12-21 22:00:00 +01:00
if( cl->edict->v.flags & FL_WATERJUMP )
2008-07-30 22:00:00 +02:00
{
SV_WaterJump( cl );
SV_CheckVelocity( cl->edict );
return;
}
// walk
2009-11-02 22:00:00 +01:00
if(( cl->edict->v.waterlevel >= 2 ) && ( cl->edict->v.movetype != MOVETYPE_NOCLIP ))
2008-07-30 22:00:00 +02:00
{
2009-11-02 22:00:00 +01:00
SV_WaterMove( cl, cmd );
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( cl->edict );
return;
}
2009-09-13 22:00:00 +02:00
2009-11-02 22:00:00 +01:00
SV_AirMove( cl, cmd );
2008-07-30 22:00:00 +02:00
SV_CheckVelocity( cl->edict );
2008-07-12 22:00:00 +02:00
}
/*
==================
2009-06-22 22:00:00 +02:00
SV_ReadClientMove
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.
==================
*/
2009-09-16 22:00:00 +02:00
static void SV_ReadClientMove( sv_client_t *cl, sizebuf_t *msg )
2008-07-12 22:00:00 +02:00
{
2009-11-15 22:00:00 +01:00
int key, lastframe, net_drop;
int checksum1, checksum2, latency;
usercmd_t oldest, oldcmd, newcmd, nulcmd;
2009-09-20 22:00:00 +02:00
key = msg->readcount;
checksum1 = MSG_ReadByte( msg );
2009-09-16 22:00:00 +02:00
lastframe = MSG_ReadLong( msg );
2009-09-20 22:00:00 +02:00
2009-09-16 22:00:00 +02:00
if( lastframe != cl->lastframe )
2008-07-12 22:00:00 +02:00
{
2009-09-16 22:00:00 +02:00
cl->lastframe = lastframe;
if( cl->lastframe > 0 )
{
latency = svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
cl->frame_latency[cl->lastframe & (LATENCY_COUNTS-1)] = latency;
}
2008-07-12 22:00:00 +02:00
}
2009-11-15 22:00:00 +01:00
Mem_Set( &nulcmd, 0, sizeof( nulcmd ));
MSG_ReadDeltaUsercmd( msg, &nulcmd, &oldest );
MSG_ReadDeltaUsercmd( msg, &oldest, &oldcmd );
MSG_ReadDeltaUsercmd( msg, &oldcmd, &newcmd );
2008-07-12 22:00:00 +02:00
if( cl->state != cs_spawned )
{
2009-09-16 22:00:00 +02:00
cl->lastframe = -1;
2008-07-12 22:00:00 +02:00
return;
}
// if the checksum fails, ignore the rest of the packet
2009-09-20 22:00:00 +02:00
checksum2 = CRC_Sequence( msg->data + key + 1, msg->readcount - key - 1, cl->netchan.incoming_sequence );
if( checksum2 != checksum1 )
2008-07-12 22:00:00 +02:00
{
2009-09-20 22:00:00 +02:00
MsgDev( D_ERROR, "SV_UserMove: failed command checksum for %s (%d != %d)\n", cl->name, checksum2, checksum1 );
2009-06-24 22:00:00 +02:00
return;
2008-07-15 22:00:00 +02:00
}
2009-06-22 22:00:00 +02:00
2009-11-15 22:00:00 +01:00
if( !sv.paused )
2008-07-12 22:00:00 +02:00
{
2009-11-15 22:00:00 +01:00
SV_PreRunCmd( cl, &newcmd ); // get random_seed from newcmd
2009-09-15 22:00:00 +02:00
2009-11-15 22:00:00 +01:00
net_drop = cl->netchan.dropped;
if( net_drop < 20 )
{
while( net_drop > 2 )
{
SV_RunCmd( cl, &cl->lastcmd );
net_drop--;
}
if( net_drop > 1 ) SV_RunCmd( cl, &oldest );
if( net_drop > 0 ) SV_RunCmd( cl, &oldcmd );
}
SV_RunCmd( cl, &newcmd );
SV_PostRunCmd( cl );
2008-07-12 22:00:00 +02:00
}
2009-11-10 22:00:00 +01:00
2009-11-15 22:00:00 +01:00
cl->lastcmd = newcmd;
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
2008-07-12 22:00:00 +02:00
}
/*
===================
SV_ExecuteClientMessage
Parse a client packet
===================
*/
void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
{
int c, stringCmdCount = 0;
bool move_issued = false;
char *s;
2009-07-04 22:00:00 +02:00
// make sure the reply sequence number matches the incoming sequence number
if( cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence )
cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;
2008-07-12 22:00:00 +02:00
// read optional clientCommand strings
while( cl->state != cs_zombie )
{
c = MSG_ReadByte( msg );
2009-06-24 22:00:00 +02:00
if( c == -1 ) break;
if( msg->error )
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;
}
2008-07-12 22:00:00 +02:00
switch( c )
{
case clc_nop:
break;
case clc_userinfo:
SV_UpdateUserinfo_f( cl );
break;
case clc_move:
if( move_issued ) return; // someone is trying to cheat...
move_issued = true;
2009-09-16 22:00:00 +02:00
SV_ReadClientMove( cl, msg );
2008-07-12 22:00:00 +02:00
break;
case clc_stringcmd:
s = MSG_ReadString( msg );
// 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;
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;
}
}
}