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/client/cl_main.c

1436 lines
36 KiB
C
Raw Normal View History

2009-11-23 22:00:00 +01:00
//=======================================================================
// Copyright XashXT Group 2009 <20>
// cl_main.c - client main loop
//=======================================================================
2007-06-21 22:00:00 +02:00
2008-06-09 22:00:00 +02:00
#include "common.h"
2007-06-21 22:00:00 +02:00
#include "client.h"
2008-07-23 22:00:00 +02:00
#include "byteorder.h"
2010-08-04 22:00:00 +02:00
#include "protocol.h"
2010-08-07 22:00:00 +02:00
#include "net_encode.h"
2007-06-21 22:00:00 +02:00
2010-08-12 22:00:00 +02:00
#define CONNECTION_PROBLEM_TIME 15.0 * 1000 // 15 seconds
2007-06-21 22:00:00 +02:00
cvar_t *rcon_client_password;
cvar_t *rcon_address;
2010-06-20 22:00:00 +02:00
cvar_t *cl_smooth;
2007-06-21 22:00:00 +02:00
cvar_t *cl_timeout;
cvar_t *cl_predict;
2008-01-20 22:00:00 +01:00
cvar_t *cl_showfps;
2009-12-05 22:00:00 +01:00
cvar_t *cl_nodelta;
2009-12-03 22:00:00 +01:00
cvar_t *cl_crosshair;
2010-08-06 22:00:00 +02:00
cvar_t *cl_idealpitchscale;
2010-08-19 22:00:00 +02:00
cvar_t *cl_solid_players;
2007-06-21 22:00:00 +02:00
cvar_t *cl_shownet;
cvar_t *cl_showmiss;
2009-11-23 22:00:00 +01:00
cvar_t *userinfo;
2007-06-21 22:00:00 +02:00
//
// userinfo
//
cvar_t *name;
2009-10-16 22:00:00 +02:00
cvar_t *model;
cvar_t *topcolor;
cvar_t *bottomcolor;
2007-06-21 22:00:00 +02:00
cvar_t *rate;
2010-03-14 22:00:00 +01:00
cvar_t *cl_lw;
2007-06-21 22:00:00 +02:00
2007-11-04 22:00:00 +01:00
client_t cl;
2009-11-23 22:00:00 +01:00
client_static_t cls;
2008-12-26 22:00:00 +01:00
clgame_static_t clgame;
2007-06-21 22:00:00 +02:00
//======================================================================
2010-03-25 22:00:00 +01:00
bool CL_Active( void )
{
return ( cls.state == ca_active );
}
2007-06-21 22:00:00 +02:00
//======================================================================
2010-03-25 22:00:00 +01:00
bool CL_IsInGame( void )
2009-10-02 22:00:00 +02:00
{
2009-12-05 22:00:00 +01:00
if( host.type == HOST_DEDICATED ) return true; // always active for dedicated servers
if( CL_GetMaxClients() > 1 ) return true; // always active for multiplayer
return ( cls.key_dest == key_game ); // active if not menu or console
2009-10-02 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
2010-07-01 22:00:00 +02:00
bool CL_IsInMenu( void )
{
return ( cls.key_dest == key_menu );
}
2010-06-20 22:00:00 +02:00
bool CL_IsPlaybackDemo( void )
{
return cls.demoplayback;
}
2009-11-03 22:00:00 +01:00
void CL_ForceVid( void )
{
cl.video_prepped = false;
2009-11-16 22:00:00 +01:00
host.state = HOST_RESTART;
2009-11-03 22:00:00 +01:00
}
void CL_ForceSnd( void )
{
cl.audio_prepped = false;
}
2008-07-15 22:00:00 +02:00
/*
=======================================================================
CLIENT RELIABLE COMMAND COMMUNICATION
=======================================================================
*/
2007-06-21 22:00:00 +02:00
/*
===================
Cmd_ForwardToServer
adds the current command line as a clc_stringcmd to the client message.
things like godmode, noclip, etc, are commands directed to the server,
so when they are typed in at the console, they will need to be forwarded.
===================
*/
/*
==================
CL_ForwardToServer_f
==================
*/
2008-12-20 22:00:00 +01:00
void CL_ForwardToServer_f( void )
2007-06-21 22:00:00 +02:00
{
2008-12-20 22:00:00 +01:00
char *cmd;
2009-11-10 22:00:00 +01:00
if( cls.demoplayback )
{
2010-01-08 22:00:00 +01:00
if( !com.stricmp( Cmd_Argv( 1 ), "pause" ))
2009-11-10 22:00:00 +01:00
cl.refdef.paused ^= 1;
return;
}
if( cls.state != ca_connected && cls.state != ca_active )
2008-12-20 22:00:00 +01:00
return; // not connected
cmd = Cmd_Argv( 0 );
if( *cmd == '-' || *cmd == '+' )
2007-06-21 22:00:00 +02:00
{
2008-12-20 22:00:00 +01:00
Msg( "Unknown command \"%s\"\n", cmd );
2007-06-21 22:00:00 +02:00
return;
2008-12-20 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
// don't forward the first argument
2008-07-12 22:00:00 +02:00
if( Cmd_Argc() > 1 )
2007-06-21 22:00:00 +02:00
{
2010-08-04 22:00:00 +02:00
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, Cmd_Args( ));
2007-06-21 22:00:00 +02:00
}
}
2010-08-04 22:00:00 +02:00
2010-02-02 22:00:00 +01:00
/*
===================
Cmd_ForwardToServer
adds the current command line as a clc_stringcmd to the client message.
things like godmode, noclip, etc, are commands directed to the server,
so when they are typed in at the console, they will need to be forwarded.
===================
*/
void Cmd_ForwardToServer( void )
{
char *cmd;
2010-02-07 22:00:00 +01:00
if( cls.demoplayback )
{
if( !com.stricmp( Cmd_Argv( 1 ), "pause" ))
cl.refdef.paused ^= 1;
return;
}
2010-02-02 22:00:00 +01:00
cmd = Cmd_Argv( 0 );
if( cls.state <= ca_connected || *cmd == '-' || *cmd == '+' )
{
Msg( "Unknown command \"%s\"\n", cmd );
return;
}
2010-08-04 22:00:00 +02:00
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
2010-02-02 22:00:00 +01:00
if( Cmd_Argc() > 1 )
2010-08-05 22:00:00 +02:00
BF_WriteString( &cls.netchan.message, va( "%s %s", cmd, Cmd_Args( )));
else BF_WriteString( &cls.netchan.message, cmd );
2010-02-02 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
2010-08-12 22:00:00 +02:00
/*
=======================================================================
CLIENT MOVEMENT COMMUNICATION
=======================================================================
*/
/*
=================
CL_CreateCmd
=================
*/
usercmd_t CL_CreateCmd( void )
{
usercmd_t cmd;
static double extramsec = 0;
client_data_t cdata;
int ms;
// send milliseconds of time to apply the move
extramsec += cls.frametime * 1000;
ms = extramsec;
extramsec -= ms; // fractional part is left for next frame
if( ms > 250 ) ms = 100; // time was unreasonable
Mem_Set( &cmd, 0, sizeof( cmd ));
// allways dump the first ten messages,
// because it may contain leftover inputs
// from the last level
if( ++cl.movemessages <= 10 )
return cmd;
VectorCopy( cl.frame.cd.origin, cdata.origin );
VectorCopy( cl.refdef.cl_viewangles, cdata.viewangles );
cdata.iWeaponBits = cl.frame.cd.weapons;
cdata.fov = cl.frame.cd.fov;
clgame.dllFuncs.pfnUpdateClientData( &cdata, cl_time( ));
2010-08-20 22:00:00 +02:00
clgame.dllFuncs.pfnCreateMove( &cmd, host.inputmsec, cls.frametime, ( cls.state == ca_active && !cl.refdef.paused ));
2010-08-12 22:00:00 +02:00
// random seed for predictable random values
cl.random_seed = Com_RandomLong( 0, 0x7fffffff ); // full range
// never let client.dll calc frametime for player
// because is potential backdoor for cheating
cmd.msec = ms;
return cmd;
}
/*
===================
CL_WritePacket
Create and send the command packet to the server
Including both the reliable commands and the usercmds
During normal gameplay, a client packet will contain something like:
1 clc_move
<usercmd[-2]>
<usercmd[-1]>
<usercmd[-0]>
===================
*/
void CL_WritePacket( void )
{
sizebuf_t buf;
bool noDelta = false;
byte data[MAX_MSGLEN];
usercmd_t *cmd, *oldcmd;
usercmd_t nullcmd;
int key, size;
// don't send anything if playing back a demo
if( cls.demoplayback || cls.state == ca_cinematic )
return;
if( cls.state == ca_disconnected || cls.state == ca_connecting )
return;
if( cls.state == ca_connected )
{
// just update reliable
if( BF_GetNumBytesWritten( &cls.netchan.message ) || cls.realtime - cls.netchan.last_sent > 1000 )
Netchan_Transmit( &cls.netchan, 0, NULL );
return;
}
if( cl_nodelta->integer || !cl.frame.valid || cls.demowaiting )
noDelta = true;
if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CMD_BACKUP )
{
if(( cls.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME )
{
MsgDev( D_WARN, "^1 Connection Problem^7\n" );
noDelta = true; // request a fullupdate
}
}
// send a userinfo update if needed
if( userinfo->modified )
{
userinfo->modified = false;
BF_WriteByte( &cls.netchan.message, clc_userinfo );
BF_WriteString( &cls.netchan.message, Cvar_Userinfo( ));
}
BF_Init( &buf, "ClientData", data, sizeof( data ));
// write new random_seed
BF_WriteByte( &buf, clc_random_seed );
BF_WriteUBitLong( &buf, cl.random_seed, 32 ); // full range
// begin a client move command
BF_WriteByte( &buf, clc_move );
// save the position for a checksum byte
key = BF_GetNumBytesWritten( &buf );
BF_WriteByte( &buf, 0 );
// let the server know what the last frame we
// got was, so the next message can be delta compressed
if( noDelta ) BF_WriteLong( &buf, -1 ); // no compression
else BF_WriteLong( &buf, cl.frame.serverframe );
// send this and the previous cmds in the message, so
// if the last packet was dropped, it can be recovered
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 2) & CMD_MASK];
Mem_Set( &nullcmd, 0, sizeof( nullcmd ));
MSG_WriteDeltaUsercmd( &buf, &nullcmd, cmd );
oldcmd = cmd;
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 1) & CMD_MASK];
MSG_WriteDeltaUsercmd( &buf, oldcmd, cmd );
oldcmd = cmd;
cmd = &cl.cmds[(cls.netchan.outgoing_sequence - 0) & CMD_MASK];
MSG_WriteDeltaUsercmd( &buf, oldcmd, cmd );
// calculate a checksum over the move commands
size = BF_GetNumBytesWritten( &buf ) - key - 1;
buf.pData[key] = CRC_Sequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence );
// deliver the message
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
}
/*
=================
CL_SendCmd
Called every frame to builds and sends a command packet to the server.
=================
*/
void CL_SendCmd( void )
{
// we create commands even if a demo is playing,
cl.refdef.cmd = &cl.cmds[cls.netchan.outgoing_sequence & CMD_MASK];
*cl.refdef.cmd = CL_CreateCmd();
// clc_move, userinfo etc
CL_WritePacket();
}
2007-06-21 22:00:00 +02:00
/*
==================
CL_Quit_f
==================
*/
2010-07-23 22:00:00 +02:00
void CL_Quit_f( void )
2007-06-21 22:00:00 +02:00
{
2008-06-06 22:00:00 +02:00
CL_Disconnect();
Sys_Quit();
2007-06-21 22:00:00 +02:00
}
/*
================
CL_Drop
2007-09-10 22:00:00 +02:00
Called after an Host_Error was thrown
2007-06-21 22:00:00 +02:00
================
*/
2009-09-25 22:00:00 +02:00
void CL_Drop( void )
2007-06-21 22:00:00 +02:00
{
2009-09-25 22:00:00 +02:00
if( cls.state == ca_uninitialized )
return;
2007-11-13 22:00:00 +01:00
CL_Disconnect();
2007-06-21 22:00:00 +02:00
}
/*
=======================
CL_SendConnectPacket
We have gotten a challenge from the server, so try and
connect.
======================
*/
2010-07-23 22:00:00 +02:00
void CL_SendConnectPacket( void )
2007-06-21 22:00:00 +02:00
{
netadr_t adr;
2010-07-23 22:00:00 +02:00
int port;
2007-06-21 22:00:00 +02:00
2009-09-17 22:00:00 +02:00
if( !NET_StringToAdr( cls.servername, &adr ))
2007-06-21 22:00:00 +02:00
{
2008-05-20 22:00:00 +02:00
MsgDev( D_INFO, "CL_SendConnectPacket: bad server address\n");
2007-06-21 22:00:00 +02:00
cls.connect_time = 0;
return;
}
2010-07-23 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
if( adr.port == 0 ) adr.port = BigShort( PORT_SERVER );
port = Cvar_VariableValue( "net_qport" );
2007-06-21 22:00:00 +02:00
2009-11-23 22:00:00 +01:00
userinfo->modified = false;
Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo( ));
2007-06-21 22:00:00 +02:00
}
/*
=================
CL_CheckForResend
Resend a connect message if the last one has timed out
=================
*/
2009-09-25 22:00:00 +02:00
void CL_CheckForResend( void )
2007-06-21 22:00:00 +02:00
{
netadr_t adr;
2009-09-25 22:00:00 +02:00
// if the local server is running and we aren't then connect
2010-03-28 22:00:00 +02:00
if( cls.state == ca_disconnected && SV_Active( ))
2007-06-21 22:00:00 +02:00
{
cls.state = ca_connecting;
2009-09-25 22:00:00 +02:00
com.strncpy( cls.servername, "localhost", sizeof( cls.servername ));
2007-06-21 22:00:00 +02:00
// we don't need a challenge on the localhost
2009-09-25 22:00:00 +02:00
CL_SendConnectPacket();
2007-06-21 22:00:00 +02:00
return;
}
// resend if we haven't gotten a reply yet
2009-02-03 22:00:00 +01:00
if( cls.demoplayback || cls.state != ca_connecting )
2008-05-20 22:00:00 +02:00
return;
2010-03-28 22:00:00 +02:00
2010-07-23 22:00:00 +02:00
if(( cls.realtime - cls.connect_time ) < 3000 )
2010-03-28 22:00:00 +02:00
return;
2007-06-21 22:00:00 +02:00
2009-02-03 22:00:00 +01:00
if( !NET_StringToAdr( cls.servername, &adr ))
2007-06-21 22:00:00 +02:00
{
2010-03-28 22:00:00 +02:00
MsgDev( D_ERROR, "CL_CheckForResend: bad server address\n" );
2007-06-21 22:00:00 +02:00
cls.state = ca_disconnected;
return;
}
2010-03-28 22:00:00 +02:00
2009-02-03 22:00:00 +01:00
if( adr.port == 0 ) adr.port = BigShort( PORT_SERVER );
2010-07-23 22:00:00 +02:00
cls.connect_time = cls.realtime; // for retransmit requests
2010-03-28 22:00:00 +02:00
MsgDev( D_NOTE, "Connecting to %s...\n", cls.servername );
2009-02-03 22:00:00 +01:00
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
2007-06-21 22:00:00 +02:00
}
/*
================
CL_Connect_f
================
*/
2009-11-23 22:00:00 +01:00
void CL_Connect_f( void )
2007-06-21 22:00:00 +02:00
{
char *server;
2009-11-23 22:00:00 +01:00
if( Cmd_Argc() != 2 )
2007-06-21 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
Msg( "Usage: connect <server>\n" );
2007-06-21 22:00:00 +02:00
return;
}
2009-11-23 22:00:00 +01:00
if( Host_ServerState())
2007-11-21 22:00:00 +01:00
{
// if running a local server, kill it and reissue
2007-11-30 22:00:00 +01:00
com.strncpy( host.finalmsg, "Server quit\n", MAX_STRING );
2007-11-21 22:00:00 +01:00
SV_Shutdown( false );
2007-06-21 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
server = Cmd_Argv( 1 );
2010-01-30 22:00:00 +01:00
NET_Config( true ); // allow remote
2010-01-28 22:00:00 +01:00
Msg( "server %s\n", server );
2007-11-21 22:00:00 +01:00
CL_Disconnect();
2007-06-21 22:00:00 +02:00
cls.state = ca_connecting;
2009-11-23 22:00:00 +01:00
com.strncpy( cls.servername, server, sizeof( cls.servername ));
2008-06-30 22:00:00 +02:00
cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately
2007-06-21 22:00:00 +02:00
}
/*
=====================
CL_Rcon_f
2009-11-23 22:00:00 +01:00
Send the rest of the command line over as
an unconnected command.
2007-06-21 22:00:00 +02:00
=====================
*/
2009-11-23 22:00:00 +01:00
void CL_Rcon_f( void )
2007-06-21 22:00:00 +02:00
{
char message[1024];
netadr_t to;
2009-11-23 22:00:00 +01:00
int i;
2007-06-21 22:00:00 +02:00
2009-11-23 22:00:00 +01:00
if( !rcon_client_password->string )
2007-06-21 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
Msg( "You must set 'rcon_password' before\n" "issuing an rcon command.\n" );
2007-06-21 22:00:00 +02:00
return;
}
message[0] = (char)255;
message[1] = (char)255;
message[2] = (char)255;
message[3] = (char)255;
message[4] = 0;
2010-01-30 22:00:00 +01:00
NET_Config( true ); // allow remote
2009-11-23 22:00:00 +01:00
com.strcat( message, "rcon " );
com.strcat( message, rcon_client_password->string );
com.strcat( message, " " );
2007-06-21 22:00:00 +02:00
2009-11-23 22:00:00 +01:00
for( i = 1; i < Cmd_Argc(); i++ )
2007-06-21 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
com.strcat( message, Cmd_Argv( i ));
com.strcat( message, " " );
2007-06-21 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
if( cls.state >= ca_connected )
{
2007-06-21 22:00:00 +02:00
to = cls.netchan.remote_address;
2009-11-23 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
else
{
2009-11-23 22:00:00 +01:00
if( !com.strlen( rcon_address->string ))
2007-06-21 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
Msg( "You must either be connected,\n" "or set the 'rcon_address' cvar\n" "to issue rcon commands\n" );
2007-06-21 22:00:00 +02:00
return;
}
2009-11-23 22:00:00 +01:00
NET_StringToAdr( rcon_address->string, &to );
if( to.port == 0 ) to.port = BigShort( PORT_SERVER );
2007-06-21 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
NET_SendPacket( NS_CLIENT, com.strlen( message ) + 1, message, to );
2007-06-21 22:00:00 +02:00
}
/*
=====================
CL_ClearState
=====================
*/
2009-01-25 22:00:00 +01:00
void CL_ClearState( void )
2007-06-21 22:00:00 +02:00
{
S_StopAllSounds ();
CL_ClearEffects ();
2010-04-02 22:00:00 +02:00
CL_FreeEdicts ();
2007-06-21 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
if( clgame.hInstance ) clgame.dllFuncs.pfnReset();
2008-12-26 22:00:00 +01:00
2008-06-22 22:00:00 +02:00
// wipe the entire cl structure
2008-11-15 22:00:00 +01:00
Mem_Set( &cl, 0, sizeof( cl ));
2010-08-04 22:00:00 +02:00
BF_Clear( &cls.netchan.message );
2010-08-18 22:00:00 +02:00
cl.refdef.movevars = &clgame.movevars;
2008-08-03 22:00:00 +02:00
Cvar_SetValue( "scr_download", 0.0f );
Cvar_SetValue( "scr_loading", 0.0f );
2007-06-21 22:00:00 +02:00
}
2010-08-04 22:00:00 +02:00
/*
=====================
CL_SendDisconnectMessage
Sends a disconnect message to the server
=====================
*/
void CL_SendDisconnectMessage( void )
{
2010-08-06 22:00:00 +02:00
sizebuf_t buf;
2010-08-04 22:00:00 +02:00
byte data[32];
BF_Init( &buf, "LastMessage", data, sizeof( data ));
BF_WriteByte( &buf, clc_stringcmd );
BF_WriteString( &buf, "disconnect" );
// make sure message will be delivered
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
}
2007-06-21 22:00:00 +02:00
/*
=====================
CL_Disconnect
Goes from a connected state to full screen console state
Sends a disconnect message to the server
2007-09-10 22:00:00 +02:00
This is also called on Host_Error, so it shouldn't cause any errors
2007-06-21 22:00:00 +02:00
=====================
*/
2008-06-28 22:00:00 +02:00
void CL_Disconnect( void )
2007-06-21 22:00:00 +02:00
{
2008-08-29 22:00:00 +02:00
if( cls.state == ca_disconnected )
2007-06-21 22:00:00 +02:00
return;
cls.connect_time = 0;
2008-05-20 22:00:00 +02:00
CL_Stop_f();
2007-06-21 22:00:00 +02:00
// send a disconnect message to the server
2010-08-04 22:00:00 +02:00
CL_SendDisconnectMessage();
2007-06-21 22:00:00 +02:00
CL_ClearState ();
// stop download
2009-09-25 22:00:00 +02:00
if( cls.download )
2007-06-21 22:00:00 +02:00
{
2009-09-25 22:00:00 +02:00
FS_Close( cls.download );
2007-06-21 22:00:00 +02:00
cls.download = NULL;
}
cls.state = ca_disconnected;
2010-02-02 22:00:00 +01:00
// back to menu if developer mode set to "player" or "mapper"
if( host.developer > 2 ) return;
2010-07-18 22:00:00 +02:00
UI_SetActiveMenu( true );
2007-06-21 22:00:00 +02:00
}
2009-06-22 22:00:00 +02:00
void CL_Disconnect_f( void )
2007-06-21 22:00:00 +02:00
{
2009-06-22 22:00:00 +02:00
Host_Error( "Disconnected from server\n" );
2007-06-21 22:00:00 +02:00
}
2010-02-10 22:00:00 +01:00
void CL_Crashed_f( void )
{
// already freed
2010-03-25 22:00:00 +01:00
if( host.state == HOST_CRASHED ) return;
2010-02-10 22:00:00 +01:00
if( host.type != HOST_NORMAL ) return;
if( !cls.initialized ) return;
2010-03-25 22:00:00 +01:00
host.state = HOST_CRASHED;
2010-02-10 22:00:00 +01:00
CL_Stop_f(); // stop any demos
// send a disconnect message to the server
2010-08-04 22:00:00 +02:00
CL_SendDisconnectMessage();
2010-02-10 22:00:00 +01:00
// stop any downloads
if( cls.download ) FS_Close( cls.download );
2010-07-30 22:00:00 +02:00
Host_WriteOpenGLConfig();
2010-02-10 22:00:00 +01:00
Host_WriteConfig(); // write config
2010-07-30 22:00:00 +02:00
2010-02-10 22:00:00 +01:00
if( re ) re->RestoreGamma();
}
2010-01-30 22:00:00 +01:00
/*
=================
CL_LocalServers_f
=================
*/
void CL_LocalServers_f( void )
{
netadr_t adr;
MsgDev( D_INFO, "Scanning for servers on the local network area...\n" );
NET_Config( true ); // allow remote
// send a broadcast packet
adr.type = NA_BROADCAST;
adr.port = BigShort( PORT_SERVER );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION );
}
2007-06-21 22:00:00 +02:00
/*
====================
CL_Packet_f
packet <destination> <contents>
Contents allows \n escape character
====================
*/
2009-11-23 22:00:00 +01:00
void CL_Packet_f( void )
2007-06-21 22:00:00 +02:00
{
char send[2048];
char *in, *out;
2010-07-23 22:00:00 +02:00
int i, l;
2007-06-21 22:00:00 +02:00
netadr_t adr;
if (Cmd_Argc() != 3)
{
2007-07-28 22:00:00 +02:00
Msg ("packet <destination> <contents>\n");
2007-06-21 22:00:00 +02:00
return;
}
2010-01-30 22:00:00 +01:00
NET_Config( true ); // allow remote
if( !NET_StringToAdr( Cmd_Argv( 1 ), &adr ))
2007-06-21 22:00:00 +02:00
{
2010-01-30 22:00:00 +01:00
Msg( "Bad address\n" );
2007-06-21 22:00:00 +02:00
return;
}
2010-01-30 22:00:00 +01:00
if( !adr.port ) adr.port = BigShort( PORT_SERVER );
in = Cmd_Argv( 2 );
out = send + 4;
2007-06-21 22:00:00 +02:00
send[0] = send[1] = send[2] = send[3] = (char)0xff;
2008-07-31 22:00:00 +02:00
l = com.strlen (in);
2007-06-21 22:00:00 +02:00
for (i=0 ; i<l ; i++)
{
if (in[i] == '\\' && in[i+1] == 'n')
{
*out++ = '\n';
i++;
}
else
*out++ = in[i];
}
*out = 0;
2010-07-23 22:00:00 +02:00
NET_SendPacket( NS_CLIENT, out - send, send, adr );
2007-06-21 22:00:00 +02:00
}
/*
=================
CL_Reconnect_f
The server is changing levels
=================
*/
2009-09-25 22:00:00 +02:00
void CL_Reconnect_f( void )
2007-06-21 22:00:00 +02:00
{
2009-09-16 22:00:00 +02:00
// if we are downloading, we don't change! This so we don't suddenly stop downloading a map
2010-08-07 22:00:00 +02:00
if( cls.download || cls.state == ca_disconnected )
return;
2007-06-21 22:00:00 +02:00
S_StopAllSounds ();
2009-10-18 22:00:00 +02:00
2010-07-17 22:00:00 +02:00
cls.changelevel = true;
2010-04-12 22:00:00 +02:00
Cmd_ExecuteString( "hud_changelevel\n" );
2009-09-28 22:00:00 +02:00
2009-10-13 22:00:00 +02:00
if( cls.demoplayback ) return;
2008-07-24 22:00:00 +02:00
if( cls.state == ca_connected )
2007-09-07 22:00:00 +02:00
{
2009-10-13 22:00:00 +02:00
cls.demonum = -1; // not in the demo loop now
2007-06-21 22:00:00 +02:00
cls.state = ca_connected;
2010-08-04 22:00:00 +02:00
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, "new" );
2007-06-21 22:00:00 +02:00
return;
}
2010-08-07 22:00:00 +02:00
if( cls.servername[0] )
2007-09-07 22:00:00 +02:00
{
2009-02-03 22:00:00 +01:00
if( cls.state >= ca_connected )
2007-09-07 22:00:00 +02:00
{
2007-06-21 22:00:00 +02:00
CL_Disconnect();
2010-07-23 22:00:00 +02:00
cls.connect_time = cls.realtime - 1500;
2007-09-07 22:00:00 +02:00
}
2008-06-30 22:00:00 +02:00
else cls.connect_time = MAX_HEARTBEAT; // fire immediately
2007-06-21 22:00:00 +02:00
2009-10-13 22:00:00 +02:00
cls.demonum = -1; // not in the demo loop now
2007-06-21 22:00:00 +02:00
cls.state = ca_connecting;
2009-09-14 22:00:00 +02:00
Msg( "reconnecting...\n" );
2007-06-21 22:00:00 +02:00
}
}
2008-06-28 22:00:00 +02:00
/*
=================
CL_ParseStatusMessage
2010-01-30 22:00:00 +01:00
Handle a reply from a info
2008-06-28 22:00:00 +02:00
=================
*/
2010-08-06 22:00:00 +02:00
void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
2008-06-28 22:00:00 +02:00
{
char *s;
2010-08-04 22:00:00 +02:00
s = BF_ReadString( msg );
2010-01-30 22:00:00 +01:00
UI_AddServerToList( from, s );
2008-06-28 22:00:00 +02:00
}
2008-08-04 22:00:00 +02:00
//===================================================================
/*
======================
CL_PrepSound
Call before entering a new level, or after changing dlls
======================
*/
void CL_PrepSound( void )
{
int i, sndcount;
2009-10-02 22:00:00 +02:00
MsgDev( D_LOAD, "CL_PrepSound: %s\n", cl.configstrings[CS_NAME] );
2009-01-14 22:00:00 +01:00
for( i = 0, sndcount = 0; i < MAX_SOUNDS && cl.configstrings[CS_SOUNDS+i+1][0]; i++ )
2008-08-04 22:00:00 +02:00
sndcount++; // total num sounds
S_BeginRegistration();
2009-01-14 22:00:00 +01:00
for( i = 0; i < MAX_SOUNDS && cl.configstrings[CS_SOUNDS+1+i][0]; i++ )
2008-08-04 22:00:00 +02:00
{
2009-01-14 22:00:00 +01:00
cl.sound_precache[i+1] = S_RegisterSound( cl.configstrings[CS_SOUNDS+1+i] );
Cvar_SetValue( "scr_loading", scr_loading->value + 5.0f / sndcount );
2010-07-22 22:00:00 +02:00
if( cl_allow_levelshots->integer || host.developer > 3 ) SCR_UpdateScreen();
2008-08-04 22:00:00 +02:00
}
2008-12-03 22:00:00 +01:00
2008-08-04 22:00:00 +02:00
S_EndRegistration();
2008-12-03 22:00:00 +01:00
CL_RunBackgroundTrack();
2008-08-04 22:00:00 +02:00
cl.audio_prepped = true;
}
/*
=================
CL_PrepVideo
Call before entering a new level, or after changing dlls
=================
*/
void CL_PrepVideo( void )
{
2009-11-03 22:00:00 +01:00
string name, mapname;
int i, mdlcount;
int map_checksum; // dummy
2008-08-04 22:00:00 +02:00
2008-11-09 22:00:00 +01:00
if( !cl.configstrings[CS_MODELS+1][0] )
2008-08-04 22:00:00 +02:00
return; // no map loaded
2009-01-25 22:00:00 +01:00
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
2009-10-02 22:00:00 +02:00
MsgDev( D_LOAD, "CL_PrepVideo: %s\n", cl.configstrings[CS_NAME] );
2009-12-08 22:00:00 +01:00
2008-08-04 22:00:00 +02:00
// let the render dll load the map
2009-11-03 22:00:00 +01:00
com.strncpy( mapname, cl.configstrings[CS_MODELS+1], MAX_STRING );
CM_BeginRegistration( mapname, true, &map_checksum );
2010-05-27 22:00:00 +02:00
re->BeginRegistration( mapname );
2008-11-18 22:00:00 +01:00
SCR_RegisterShaders(); // update with new sequence
2008-08-04 22:00:00 +02:00
SCR_UpdateScreen();
2009-01-14 22:00:00 +01:00
for( i = 0, mdlcount = 0; i < MAX_MODELS && cl.configstrings[CS_MODELS+1+i][0]; i++ )
2008-08-04 22:00:00 +02:00
mdlcount++; // total num models
2009-01-14 22:00:00 +01:00
for( i = 0; i < MAX_MODELS && cl.configstrings[CS_MODELS+1+i][0]; i++ )
2008-08-04 22:00:00 +02:00
{
com.strncpy( name, cl.configstrings[CS_MODELS+1+i], MAX_STRING );
re->RegisterModel( name, i+1 );
2009-10-29 22:00:00 +01:00
CM_RegisterModel( name, i+1 );
Cvar_SetValue( "scr_loading", scr_loading->value + 45.0f / mdlcount );
2010-07-22 22:00:00 +02:00
if( cl_allow_levelshots->integer || host.developer > 3 ) SCR_UpdateScreen();
2008-08-04 22:00:00 +02:00
}
2010-06-29 22:00:00 +02:00
for( i = 0; i < MAX_DECALNAMES && cl.configstrings[CS_DECALS+1+i][0]; i++ )
2008-12-15 22:00:00 +01:00
{
2009-01-16 22:00:00 +01:00
com.strncpy( name, cl.configstrings[CS_DECALS+1+i], MAX_STRING );
2010-05-24 22:00:00 +02:00
cl.decal_shaders[i+1] = re->RegisterShader( name, SHADER_DECAL );
2008-12-15 22:00:00 +01:00
}
2008-11-18 22:00:00 +01:00
// setup sky and free unneeded stuff
2010-08-18 22:00:00 +02:00
re->EndRegistration( cl.refdef.movevars->skyName );
2009-11-03 22:00:00 +01:00
CM_EndRegistration (); // free unused models
2009-01-16 22:00:00 +01:00
Cvar_SetValue( "scr_loading", 100.0f ); // all done
2010-07-01 22:00:00 +02:00
if( host.decalList )
{
// need to reapply all decals after restarting
for( i = 0; i < host.numdecals; i++ )
{
decallist_t *entry = &host.decalList[i];
2010-08-07 22:00:00 +02:00
cl_entity_t *pEdict = CL_GetEntityByIndex( entry->entityIndex );
2010-07-01 22:00:00 +02:00
shader_t decalIndex = pfnDecalIndexFromName( entry->name );
int modelIndex = 0;
2010-08-07 22:00:00 +02:00
if( pEdict ) modelIndex = pEdict->curstate.modelindex;
2010-07-01 22:00:00 +02:00
CL_DecalShoot( decalIndex, entry->entityIndex, modelIndex, entry->position, entry->flags );
}
Z_Free( host.decalList );
}
host.decalList = NULL;
host.numdecals = 0;
2008-11-18 22:00:00 +01:00
2010-07-23 22:00:00 +02:00
if( host.developer <= 2 )
Con_ClearNotify(); // clear any lines of console text
2010-07-13 22:00:00 +02:00
SCR_UpdateScreen ();
CL_RunLightStyles ();
2008-08-04 22:00:00 +02:00
cl.video_prepped = true;
2009-09-28 22:00:00 +02:00
cl.force_refdef = true;
2008-08-04 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
/*
=================
CL_ConnectionlessPacket
Responses to broadcasts, etc
=================
*/
2010-08-06 22:00:00 +02:00
void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
2007-06-21 22:00:00 +02:00
{
2008-06-22 22:00:00 +02:00
char *s, *c;
2007-06-21 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
BF_Clear( msg );
BF_ReadLong( msg ); // skip the -1
2007-06-21 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
s = BF_ReadStringLine( msg );
2007-06-21 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
Cmd_TokenizeString( s );
2010-01-31 22:00:00 +01:00
c = Cmd_Argv( 0 );
2007-06-21 22:00:00 +02:00
2010-01-31 22:00:00 +01:00
MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c );
2007-06-21 22:00:00 +02:00
// server connection
2009-06-22 22:00:00 +02:00
if( !com.strcmp( c, "client_connect" ))
2007-06-21 22:00:00 +02:00
{
2009-06-22 22:00:00 +02:00
if( cls.state == ca_connected )
2007-06-21 22:00:00 +02:00
{
2009-09-11 22:00:00 +02:00
MsgDev( D_INFO, "dup connect received. ignored\n");
2007-06-21 22:00:00 +02:00
return;
}
2010-08-04 22:00:00 +02:00
2008-07-12 22:00:00 +02:00
Netchan_Setup( NS_CLIENT, &cls.netchan, from, Cvar_VariableValue( "net_qport" ));
2010-08-04 22:00:00 +02:00
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, "new" );
2007-06-21 22:00:00 +02:00
cls.state = ca_connected;
2010-07-18 22:00:00 +02:00
UI_SetActiveMenu( false );
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "info" ))
2007-06-21 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// server responding to a status broadcast
2008-07-12 22:00:00 +02:00
CL_ParseStatusMessage( from, msg );
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "cmd" ))
2007-06-21 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// remote command from gui front end
2010-08-04 22:00:00 +02:00
if( !NET_IsLocalAddress( from ))
2007-06-21 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
Msg( "Command packet from remote host. Ignored.\n" );
2007-06-21 22:00:00 +02:00
return;
}
2010-01-31 22:00:00 +01:00
2009-06-24 22:00:00 +02:00
ShowWindow( host.hWnd, SW_RESTORE );
2007-11-17 22:00:00 +01:00
SetForegroundWindow ( host.hWnd );
2010-08-04 22:00:00 +02:00
s = BF_ReadString( msg );
2010-01-30 22:00:00 +01:00
Cbuf_AddText( s );
Cbuf_AddText( "\n" );
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "print" ))
2007-06-21 22:00:00 +02:00
{
2008-06-22 22:00:00 +02:00
// print command from somewhere
2010-08-04 22:00:00 +02:00
s = BF_ReadString( msg );
2010-01-30 22:00:00 +01:00
Msg( s );
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "ping" ))
2007-06-21 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// ping from somewhere
2008-07-12 22:00:00 +02:00
Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" );
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "challenge" ))
2007-06-21 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// challenge from the server we are connecting to
cls.challenge = com.atoi( Cmd_Argv( 1 ));
2008-07-12 22:00:00 +02:00
CL_SendConnectPacket();
2007-06-21 22:00:00 +02:00
return;
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "echo" ))
2007-06-21 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// echo request from server
2009-11-23 22:00:00 +01:00
Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ));
2008-07-12 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else if( !com.strcmp( c, "disconnect" ))
2008-07-12 22:00:00 +02:00
{
2010-01-31 22:00:00 +01:00
// a disconnect message from the server, which will happen if the server
// dropped the connection but it is still getting packets from us
2008-07-12 22:00:00 +02:00
CL_Disconnect();
2007-06-21 22:00:00 +02:00
}
2010-01-31 22:00:00 +01:00
else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), s );
2008-07-11 22:00:00 +02:00
}
/*
=================
2009-06-24 22:00:00 +02:00
CL_ReadNetMessage
2008-07-11 22:00:00 +02:00
=================
*/
2009-06-24 22:00:00 +02:00
void CL_ReadNetMessage( void )
2008-07-11 22:00:00 +02:00
{
2010-08-04 22:00:00 +02:00
int curSize;
while( NET_GetPacket( NS_CLIENT, &net_from, net_message_buffer, &curSize ))
2008-07-11 22:00:00 +02:00
{
2010-06-22 22:00:00 +02:00
if( host.type == HOST_DEDICATED || cls.demoplayback )
return;
2010-08-04 22:00:00 +02:00
BF_Init( &net_message, "ServerData", net_message_buffer, curSize );
// check for connectionless packet (0xffffffff) first
if( BF_GetMaxBytes( &net_message ) >= 4 && *(int *)net_message.pData == -1 )
2009-06-24 22:00:00 +02:00
{
CL_ConnectionlessPacket( net_from, &net_message );
return;
}
2008-07-12 22:00:00 +02:00
2009-06-24 22:00:00 +02:00
// can't be a valid sequenced packet
if( cls.state < ca_connected ) return;
2008-07-11 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
if( BF_GetMaxBytes( &net_message ) < 8 )
2009-06-24 22:00:00 +02:00
{
MsgDev( D_WARN, "%s: runt packet\n", NET_AdrToString( net_from ));
return;
}
// packet from server
2010-06-22 22:00:00 +02:00
if( !NET_CompareAdr( net_from, cls.netchan.remote_address ))
2009-06-24 22:00:00 +02:00
{
MsgDev( D_WARN, "CL_ReadPackets: %s:sequenced packet without connection\n", NET_AdrToString( net_from ));
return;
}
2008-07-11 22:00:00 +02:00
2010-06-22 22:00:00 +02:00
if( Netchan_Process( &cls.netchan, &net_message ))
{
// the header is different lengths for reliable and unreliable messages
2010-08-04 22:00:00 +02:00
int headerBytes = BF_GetNumBytesRead( &net_message );
2008-07-11 22:00:00 +02:00
2010-06-22 22:00:00 +02:00
CL_ParseServerMessage( &net_message );
// we don't know if it is ok to save a demo message until
// after we have parsed the frame
if( cls.demorecording && !cls.demowaiting )
CL_WriteDemoMessage( &net_message, headerBytes );
}
2009-06-22 22:00:00 +02:00
}
}
2008-07-12 22:00:00 +02:00
void CL_ReadPackets( void )
2008-07-11 22:00:00 +02:00
{
2010-06-22 22:00:00 +02:00
if( cls.demoplayback )
CL_ReadDemoMessage();
else CL_ReadNetMessage();
2008-07-11 22:00:00 +02:00
2010-08-19 22:00:00 +02:00
// build list of all solid entities per frame (exclude clients)
CL_SetSolidEntities ();
2009-06-24 22:00:00 +02:00
// singleplayer never has connection timeout
if( NET_IsLocalAddress( cls.netchan.remote_address ))
2008-07-09 22:00:00 +02:00
return;
2008-07-11 22:00:00 +02:00
// check timeout
if( cls.state >= ca_connected && !cls.demoplayback )
{
2010-07-23 22:00:00 +02:00
if( cls.realtime - cls.netchan.last_received > ( cl_timeout->value * 1000 ))
2008-07-11 22:00:00 +02:00
{
2009-06-24 22:00:00 +02:00
if( ++cl.timeoutcount > 5 ) // timeoutcount saves debugger
2008-07-11 22:00:00 +02:00
{
2009-09-17 22:00:00 +02:00
Msg( "\nServer connection timed out.\n" );
2008-07-11 22:00:00 +02:00
CL_Disconnect();
return;
}
}
2007-06-21 22:00:00 +02:00
}
2008-07-11 22:00:00 +02:00
else cl.timeoutcount = 0;
2007-06-21 22:00:00 +02:00
}
2008-07-11 22:00:00 +02:00
2007-06-21 22:00:00 +02:00
//=============================================================================
/*
==============
CL_Userinfo_f
==============
*/
2009-11-23 22:00:00 +01:00
void CL_Userinfo_f( void )
2007-06-21 22:00:00 +02:00
{
2009-07-04 22:00:00 +02:00
Msg( "User info settings:\n" );
2009-11-23 22:00:00 +01:00
Info_Print( Cvar_Userinfo( ));
}
/*
==============
CL_Physinfo_f
==============
*/
void CL_Physinfo_f( void )
{
Msg( "Phys info settings:\n" );
2010-08-05 22:00:00 +02:00
Info_Print( cl.frame.cd.physinfo );
2007-06-21 22:00:00 +02:00
}
int precache_check; // for autodownload of precache items
int precache_spawncount;
2008-08-02 22:00:00 +02:00
// ENV_CNT is map load, ENV_CNT+1 is first cubemap
#define ENV_CNT MAX_CONFIGSTRINGS
2009-11-23 22:00:00 +01:00
#define TEXTURE_CNT (ENV_CNT+1)
2007-06-21 22:00:00 +02:00
2008-08-02 22:00:00 +02:00
void CL_RequestNextDownload( void )
2007-06-21 22:00:00 +02:00
{
2010-08-04 22:00:00 +02:00
string fn;
uint map_checksum; // for detecting cheater maps
2007-06-21 22:00:00 +02:00
2008-08-02 22:00:00 +02:00
if( cls.state != ca_connected )
2007-06-21 22:00:00 +02:00
return;
2008-08-02 22:00:00 +02:00
if( !allow_download->value && precache_check < ENV_CNT )
2007-06-21 22:00:00 +02:00
precache_check = ENV_CNT;
2008-08-02 22:00:00 +02:00
if( precache_check == CS_MODELS )
2007-09-14 22:00:00 +02:00
{
// confirm map
2007-06-21 22:00:00 +02:00
precache_check = CS_MODELS+2; // 0 isn't used
2008-08-02 22:00:00 +02:00
if(!CL_CheckOrDownloadFile( cl.configstrings[CS_MODELS+1] ))
return; // started a download map
2007-06-21 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
if( precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS )
2007-09-14 22:00:00 +02:00
{
2008-08-02 22:00:00 +02:00
while( precache_check < CS_MODELS+MAX_MODELS && cl.configstrings[precache_check][0])
2007-09-14 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
if( cl.configstrings[precache_check][0] == '*' || cl.configstrings[precache_check][0] == '#' )
{
precache_check++; // ignore bsp models or built-in models
continue;
}
2008-08-02 22:00:00 +02:00
com.sprintf( fn, "%s", cl.configstrings[precache_check++]);
if(!CL_CheckOrDownloadFile( fn )) return; // started a download
2007-06-21 22:00:00 +02:00
}
precache_check = CS_SOUNDS;
}
2009-11-23 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
if( precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS )
2009-11-23 22:00:00 +01:00
{
2008-08-02 22:00:00 +02:00
if( precache_check == CS_SOUNDS ) precache_check++; // zero is blank
while( precache_check < CS_SOUNDS+MAX_SOUNDS && cl.configstrings[precache_check][0])
2007-08-11 22:00:00 +02:00
{
2008-08-02 22:00:00 +02:00
// sound pathes from model events
2009-11-23 22:00:00 +01:00
if( cl.configstrings[precache_check][0] == '*' || cl.configstrings[precache_check][0] == '#' )
2007-08-11 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
precache_check++;
continue;
2007-06-21 22:00:00 +02:00
}
2008-08-02 22:00:00 +02:00
com.sprintf( fn, "sound/%s", cl.configstrings[precache_check++]);
if(!CL_CheckOrDownloadFile( fn )) return; // started a download
2007-06-21 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
precache_check = CS_GENERICS;
}
if( precache_check >= CS_GENERICS && precache_check < CS_GENERICS+MAX_GENERICS )
{
if( precache_check == CS_GENERICS ) precache_check++; // zero is blank
while( precache_check < CS_GENERICS+MAX_GENERICS && cl.configstrings[precache_check][0] )
{
// generic recources - pakfiles, wadfiles etc
if( cl.configstrings[precache_check][0] == '#' )
{
precache_check++;
continue;
}
com.sprintf( fn, "%s", cl.configstrings[precache_check++]);
if(!CL_CheckOrDownloadFile( fn )) return; // started a download
}
2007-06-21 22:00:00 +02:00
precache_check = ENV_CNT;
}
2009-11-23 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
if( precache_check == ENV_CNT )
2007-08-11 22:00:00 +02:00
{
2007-06-21 22:00:00 +02:00
precache_check = ENV_CNT + 1;
2009-11-03 22:00:00 +01:00
CM_BeginRegistration( cl.configstrings[CS_MODELS+1], true, &map_checksum );
2010-02-07 22:00:00 +01:00
2010-08-12 22:00:00 +02:00
if( map_checksum != com.atoi( cl.configstrings[CS_MAPCHECKSUM] ))
2007-08-11 22:00:00 +02:00
{
2009-10-28 22:00:00 +01:00
Host_Error( "Local map version differs from server: %i != '%s'\n", map_checksum, cl.configstrings[CS_MAPCHECKSUM] );
2007-06-21 22:00:00 +02:00
return;
}
}
2009-11-23 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
if( precache_check > ENV_CNT && precache_check < TEXTURE_CNT )
2007-08-11 22:00:00 +02:00
{
2008-07-30 22:00:00 +02:00
if( allow_download->value )
2007-08-11 22:00:00 +02:00
{
2010-08-18 22:00:00 +02:00
com.sprintf( fn, "env/%s.dds", cl.refdef.movevars->skyName ); // cubemap pack
2009-11-23 22:00:00 +01:00
if( !CL_CheckOrDownloadFile( fn )) return; // started a download
2007-06-21 22:00:00 +02:00
}
precache_check = TEXTURE_CNT;
}
2009-11-23 22:00:00 +01:00
// skip textures: use generic for downloading packs
2008-08-02 22:00:00 +02:00
if( precache_check == TEXTURE_CNT )
precache_check = TEXTURE_CNT + 999;
2007-06-21 22:00:00 +02:00
2008-08-03 22:00:00 +02:00
CL_PrepSound();
CL_PrepVideo();
2008-06-12 22:00:00 +02:00
if( cls.demoplayback ) return; // not really connected
2010-08-04 22:00:00 +02:00
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, va( "begin %i\n", precache_spawncount ));
2007-06-21 22:00:00 +02:00
}
/*
=================
CL_Precache_f
The server will send this command right
before allowing the client into the server
=================
*/
2008-08-02 22:00:00 +02:00
void CL_Precache_f( void )
2007-06-21 22:00:00 +02:00
{
precache_check = CS_MODELS;
2009-10-13 22:00:00 +02:00
precache_spawncount = com.atoi( Cmd_Argv( 1 ));
2007-06-21 22:00:00 +02:00
CL_RequestNextDownload();
}
2010-06-22 22:00:00 +02:00
/*
=================
CL_DumpCfgStrings_f
Dump all the configstring that used on the client-side
=================
*/
void CL_DumpCfgStrings_f( void )
{
int i, numStrings = 0;
if( cls.state != ca_active )
{
Msg( "No server running\n" );
return;
}
for( i = 0; i < MAX_CONFIGSTRINGS; i++ )
{
if( !cl.configstrings[i][0] ) continue;
Msg( "%s [%i]\n", cl.configstrings[i], i );
numStrings++;
}
2010-08-04 22:00:00 +02:00
2010-06-22 22:00:00 +02:00
Msg( "%i total strings used\n", numStrings );
}
2010-02-02 22:00:00 +01:00
/*
=================
CL_Escape_f
Escape to menu from game
=================
*/
void CL_Escape_f( void )
{
if( cls.key_dest == key_menu )
return;
if( cls.state == ca_cinematic )
SCR_StopCinematic();
2010-07-18 22:00:00 +02:00
else UI_SetActiveMenu( true );
2010-02-02 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
/*
=================
CL_InitLocal
=================
*/
2009-09-10 22:00:00 +02:00
void CL_InitLocal( void )
2007-06-21 22:00:00 +02:00
{
cls.state = ca_disconnected;
2009-01-14 22:00:00 +01:00
2007-11-05 22:00:00 +01:00
// register our variables
2009-09-13 22:00:00 +02:00
cl_predict = Cvar_Get( "cl_predict", "1", CVAR_ARCHIVE, "disables client movement prediction" );
2010-08-20 22:00:00 +02:00
cl_crosshair = Cvar_Get( "crosshair", "1", CVAR_ARCHIVE, "show weapon chrosshair" );
2009-12-05 22:00:00 +01:00
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0, "disable delta-compression for usercommnds" );
2010-08-06 22:00:00 +02:00
cl_idealpitchscale = Cvar_Get( "cl_idealpitchscale", "0.8", 0, "how much to look up/down slopes and stairs when not using freelook" );
2010-08-19 22:00:00 +02:00
cl_solid_players = Cvar_Get( "cl_solid_players", "1", 0, "Make all players not solid (can't traceline them)" );
2010-08-06 22:00:00 +02:00
2009-09-13 22:00:00 +02:00
cl_shownet = Cvar_Get( "cl_shownet", "0", 0, "client show network packets" );
2009-12-05 22:00:00 +01:00
cl_showmiss = Cvar_Get( "cl_showmiss", "0", CVAR_ARCHIVE, "client show prediction errors" );
2009-09-13 22:00:00 +02:00
cl_timeout = Cvar_Get( "cl_timeout", "120", 0, "connect timeout (in-seconds)" );
2007-06-21 22:00:00 +02:00
2009-09-13 22:00:00 +02:00
rcon_client_password = Cvar_Get( "rcon_password", "", 0, "remote control client password" );
rcon_address = Cvar_Get( "rcon_address", "", 0, "remote control address" );
2007-06-21 22:00:00 +02:00
// userinfo
2010-08-20 22:00:00 +02:00
Cvar_Get( "password", "", CVAR_USERINFO, "player password" );
2010-08-15 22:00:00 +02:00
name = Cvar_Get( "name", SI->username, CVAR_USERINFO|CVAR_ARCHIVE|CVAR_PRINTABLEONLY, "player name" );
model = Cvar_Get( "model", "player", CVAR_USERINFO|CVAR_ARCHIVE, "player model ('player' it's a single player model)" );
topcolor = Cvar_Get( "topcolor", "0", CVAR_USERINFO|CVAR_ARCHIVE, "player top color" );
2010-08-20 22:00:00 +02:00
bottomcolor = Cvar_Get( "bottomcolor", "0", CVAR_USERINFO|CVAR_ARCHIVE, "player bottom color" );
rate = Cvar_Get( "rate", "25000", CVAR_USERINFO|CVAR_ARCHIVE, "player network rate" );
2009-11-23 22:00:00 +01:00
userinfo = Cvar_Get( "@userinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
2009-09-13 22:00:00 +02:00
cl_showfps = Cvar_Get( "cl_showfps", "1", CVAR_ARCHIVE, "show client fps" );
2010-03-14 22:00:00 +01:00
cl_lw = Cvar_Get( "cl_lw", "1", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" );
2010-06-20 22:00:00 +02:00
cl_smooth = Cvar_Get ("cl_smooth", "1", 0, "smooth up stair climbing and interpolate position in multiplayer" );
2010-08-15 22:00:00 +02:00
Cvar_Get( "hud_scale", "0", CVAR_ARCHIVE|CVAR_LATCH, "scale hud at current resolution" );
2007-06-21 22:00:00 +02:00
// register our commands
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("cmd", CL_ForwardToServer_f, "send a console commandline to the server" );
2009-11-10 22:00:00 +01:00
Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" );
2010-01-30 22:00:00 +01:00
Cmd_AddCommand ("localservers", CL_LocalServers_f, "collect info about local servers" );
2007-06-21 22:00:00 +02:00
2010-02-10 22:00:00 +01:00
Cmd_AddCommand ("@crashed", CL_Crashed_f, "" ); // internal system command
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("userinfo", CL_Userinfo_f, "print current client userinfo" );
2009-11-23 22:00:00 +01:00
Cmd_AddCommand ("physinfo", CL_Physinfo_f, "print current client physinfo" );
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("disconnect", CL_Disconnect_f, "disconnect from server" );
Cmd_AddCommand ("record", CL_Record_f, "record a demo" );
2008-05-20 22:00:00 +02:00
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "playing a demo" );
2010-04-03 22:00:00 +02:00
Cmd_AddCommand ("killdemo", CL_DeleteDemo_f, "delete a specified demo file and demoshot" );
2009-10-13 22:00:00 +02:00
Cmd_AddCommand ("startdemos", CL_StartDemos_f, "start playing back the selected demos sequentially" );
Cmd_AddCommand ("demos", CL_Demos_f, "restart looping demos defined by the last startdemos command" );
2008-08-02 22:00:00 +02:00
Cmd_AddCommand ("movie", CL_PlayVideo_f, "playing a movie" );
2008-05-20 22:00:00 +02:00
Cmd_AddCommand ("stop", CL_Stop_f, "stop playing or recording a demo" );
2010-01-30 22:00:00 +01:00
Cmd_AddCommand ("info", NULL, "collect info about local servers with specified protocol" );
2010-02-02 22:00:00 +01:00
Cmd_AddCommand ("escape", CL_Escape_f, "escape from game to menu" );
2010-06-22 22:00:00 +02:00
Cmd_AddCommand ("cfgstringslist", CL_DumpCfgStrings_f, "dump all configstrings that used on the client" );
2010-01-30 22:00:00 +01:00
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("quit", CL_Quit_f, "quit from game" );
Cmd_AddCommand ("exit", CL_Quit_f, "quit from game" );
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("screenshot", CL_ScreenShot_f, "takes a screenshot of the next rendered frame" );
2008-11-09 22:00:00 +01:00
Cmd_AddCommand ("envshot", CL_EnvShot_f, "takes a six-sides cubemap shot with specified name" );
Cmd_AddCommand ("skyshot", CL_SkyShot_f, "takes a six-sides envmap (skybox) shot with specified name" );
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("levelshot", CL_LevelShot_f, "same as \"screenshot\", used for create plaque images" );
2009-09-10 22:00:00 +02:00
Cmd_AddCommand ("saveshot", CL_SaveShot_f, "used for create save previews with LoadGame menu" );
2010-01-07 22:00:00 +01:00
Cmd_AddCommand ("demoshot", CL_DemoShot_f, "used for create demo previews with PlayDemo menu" );
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("connect", CL_Connect_f, "connect to a server by hostname" );
Cmd_AddCommand ("reconnect", CL_Reconnect_f, "reconnect to current level" );
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("rcon", CL_Rcon_f, "sends a command to the server console (rcon_password and rcon_address required)" );
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
// this is dangerous to leave in
// Cmd_AddCommand ("packet", CL_Packet_f, "send a packet with custom contents" );
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
Cmd_AddCommand ("precache", CL_Precache_f, "precache specified resource (by index)" );
Cmd_AddCommand ("download", CL_Download_f, "download specified resource (by name)" );
2007-06-21 22:00:00 +02:00
}
2008-07-11 22:00:00 +02:00
//============================================================================
2008-07-10 22:00:00 +02:00
/*
==================
CL_SendCommand
==================
*/
2009-02-03 22:00:00 +01:00
void CL_SendCommand( void )
2008-07-10 22:00:00 +02:00
{
// send intentions now
2008-07-11 22:00:00 +02:00
CL_SendCmd ();
2008-07-10 22:00:00 +02:00
// resend a connection request if necessary
2008-07-11 22:00:00 +02:00
CL_CheckForResend ();
2009-09-14 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
/*
==================
2010-06-20 22:00:00 +02:00
Host_ClientFrame
2007-06-21 22:00:00 +02:00
==================
*/
2010-07-23 22:00:00 +02:00
void CL_Frame( int time )
2007-06-21 22:00:00 +02:00
{
2010-07-24 22:00:00 +02:00
static int extratime;
2010-06-20 22:00:00 +02:00
// if client is not active, do nothing
if( !cls.initialized ) return;
2008-07-06 22:00:00 +02:00
2010-07-24 22:00:00 +02:00
extratime += time;
2010-07-23 22:00:00 +02:00
cls.realtime += time;
2010-07-24 22:00:00 +02:00
if( extratime < ( 1000 / host_maxfps->value ))
return; // framerate is too high
// decide the simulation time
cl.time += extratime; // can be merged by cl.frame.servertime
cls.frametime = extratime * 0.001f;
extratime = 0;
2010-07-23 22:00:00 +02:00
if( cls.frametime > 0.2f ) cls.frametime = 0.2f;
2008-07-11 22:00:00 +02:00
2010-07-18 22:00:00 +02:00
// menu time (not paused, not clamped)
2010-07-23 22:00:00 +02:00
gameui.globals->time = cls.realtime * 0.001f;
gameui.globals->frametime = cls.frametime;
2010-07-18 22:00:00 +02:00
gameui.globals->demoplayback = cls.demoplayback;
gameui.globals->demorecording = cls.demorecording;
2010-07-02 22:00:00 +02:00
// if in the debugger last frame, don't timeout
2010-07-24 22:00:00 +02:00
if( time > 5000 ) cls.netchan.last_received = Sys_Milliseconds();
2010-07-02 22:00:00 +02:00
2007-06-21 22:00:00 +02:00
// fetch results from server
2007-11-17 22:00:00 +01:00
CL_ReadPackets();
2007-06-21 22:00:00 +02:00
2008-07-11 22:00:00 +02:00
// send a new command message to the server
2008-07-10 22:00:00 +02:00
CL_SendCommand();
2007-06-21 22:00:00 +02:00
// predict all unacknowledged movements
2009-10-02 22:00:00 +02:00
CL_PredictMovement();
Host_CheckChanges();
2007-06-21 22:00:00 +02:00
2010-03-25 22:00:00 +01:00
// allow sound and video DLL change
2008-08-03 22:00:00 +02:00
if( cls.state == ca_active )
{
if( !cl.video_prepped ) CL_PrepVideo();
if( !cl.audio_prepped ) CL_PrepSound();
}
2007-06-21 22:00:00 +02:00
// update the screen
2010-04-12 22:00:00 +02:00
SCR_UpdateScreen ();
2007-06-21 22:00:00 +02:00
// update audio
2010-06-28 22:00:00 +02:00
S_RenderFrame( &cl.refdef );
2007-06-21 22:00:00 +02:00
// advance local effects for next frame
CL_RunLightStyles ();
2007-11-05 22:00:00 +01:00
2007-11-06 22:00:00 +01:00
SCR_RunCinematic();
Con_RunConsole();
2007-06-21 22:00:00 +02:00
cls.framecount++;
}
//============================================================================
/*
====================
CL_Init
====================
*/
2008-06-12 22:00:00 +02:00
void CL_Init( void )
2007-06-21 22:00:00 +02:00
{
2008-07-06 22:00:00 +02:00
if( host.type == HOST_DEDICATED )
return; // nothing running on the client
2007-06-21 22:00:00 +02:00
2007-11-17 22:00:00 +01:00
Con_Init();
2010-07-18 22:00:00 +02:00
CL_InitLocal();
2009-06-22 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
if( !CL_LoadProgs( "client.dll" ))
2010-02-07 22:00:00 +01:00
Host_Error( "CL_InitGame: can't initialize client.dll\n" );
2010-07-18 22:00:00 +02:00
Host_CheckChanges ();
2008-08-04 22:00:00 +02:00
cls.initialized = true;
2007-06-21 22:00:00 +02:00
}
/*
===============
CL_Shutdown
2010-07-30 22:00:00 +02:00
FIXME: this is a callback from Sys_Quit and Host_Error. It would be better
2007-06-21 22:00:00 +02:00
to run quit through here before the final handoff to the sys code.
===============
*/
2008-07-17 22:00:00 +02:00
void CL_Shutdown( void )
2007-06-21 22:00:00 +02:00
{
2007-10-29 22:00:00 +01:00
// already freed
2008-08-04 22:00:00 +02:00
if( host.state == HOST_ERROR ) return;
if( !cls.initialized ) return;
2007-06-21 22:00:00 +02:00
2010-07-30 22:00:00 +02:00
Host_WriteOpenGLConfig ();
Host_WriteConfig ();
CL_UnloadProgs ();
SCR_Shutdown ();
2008-08-04 22:00:00 +02:00
cls.initialized = false;
}