📄 cl_main.c
字号:
if (allow_download_players->value) {
while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
int i, n;
char model[MAX_QPATH], skin[MAX_QPATH], *p;
i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
if (!cl.configstrings[CS_PLAYERSKINS+i][0]) {
precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
continue;
}
if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
p++;
else
p = cl.configstrings[CS_PLAYERSKINS+i];
strcpy(model, p);
p = strchr(model, '/');
if (!p)
p = strchr(model, '\\');
if (p) {
*p++ = 0;
strcpy(skin, p);
} else
*skin = 0;
switch (n) {
case 0: // model
Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
if (!CL_CheckOrDownloadFile(fn)) {
precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
return; // started a download
}
n++;
/*FALL THROUGH*/
case 1: // weapon model
Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
if (!CL_CheckOrDownloadFile(fn)) {
precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
return; // started a download
}
n++;
/*FALL THROUGH*/
case 2: // weapon skin
Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
if (!CL_CheckOrDownloadFile(fn)) {
precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
return; // started a download
}
n++;
/*FALL THROUGH*/
case 3: // skin
Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
if (!CL_CheckOrDownloadFile(fn)) {
precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
return; // started a download
}
n++;
/*FALL THROUGH*/
case 4: // skin_i
Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
if (!CL_CheckOrDownloadFile(fn)) {
precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
return; // started a download
}
// move on to next model
precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
}
}
}
// precache phase completed
precache_check = ENV_CNT;
}
if (precache_check == ENV_CNT) {
precache_check = ENV_CNT + 1;
CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) {
Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
return;
}
}
if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
if (allow_download->value && allow_download_maps->value) {
while (precache_check < TEXTURE_CNT) {
int n = precache_check++ - ENV_CNT - 1;
if (n & 1)
Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx",
cl.configstrings[CS_SKY], env_suf[n/2]);
else
Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
cl.configstrings[CS_SKY], env_suf[n/2]);
if (!CL_CheckOrDownloadFile(fn))
return; // started a download
}
}
precache_check = TEXTURE_CNT;
}
if (precache_check == TEXTURE_CNT) {
precache_check = TEXTURE_CNT+1;
precache_tex = 0;
}
// confirm existance of textures, download any that don't exist
if (precache_check == TEXTURE_CNT+1) {
// from qcommon/cmodel.c
extern int numtexinfo;
extern mapsurface_t map_surfaces[];
if (allow_download->value && allow_download_maps->value) {
while (precache_tex < numtexinfo) {
char fn[MAX_OSPATH];
sprintf(fn, "textures/%s.wal", map_surfaces[precache_tex++].rname);
if (!CL_CheckOrDownloadFile(fn))
return; // started a download
}
}
precache_check = TEXTURE_CNT+999;
}
//ZOID
CL_RegisterSounds ();
CL_PrepRefresh ();
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
MSG_WriteString (&cls.netchan.message, va("begin %i\n", precache_spawncount) );
}
/*
=================
CL_Precache_f
The server will send this command right
before allowing the client into the server
=================
*/
void CL_Precache_f (void)
{
//Yet another hack to let old demos work
//the old precache sequence
if (Cmd_Argc() < 2) {
unsigned map_checksum; // for detecting cheater maps
CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
CL_RegisterSounds ();
CL_PrepRefresh ();
return;
}
precache_check = CS_MODELS;
precache_spawncount = atoi(Cmd_Argv(1));
precache_model = 0;
precache_model_skin = 0;
CL_RequestNextDownload();
}
/*
=================
CL_InitLocal
=================
*/
void CL_InitLocal (void)
{
cls.state = ca_disconnected;
cls.realtime = Sys_Milliseconds ();
CL_InitInput ();
adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
//
// register our variables
//
cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
cl_gun = Cvar_Get ("cl_gun", "1", 0);
cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
cl_predict = Cvar_Get ("cl_predict", "1", 0);
// cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
cl_maxfps = Cvar_Get ("cl_maxfps", "90", 0);
cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);
freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE );
lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
m_forward = Cvar_Get ("m_forward", "1", 0);
m_side = Cvar_Get ("m_side", "1", 0);
cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
cl_showclamp = Cvar_Get ("showclamp", "0", 0);
cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
cl_paused = Cvar_Get ("paused", "0", 0);
cl_timedemo = Cvar_Get ("timedemo", "0", 0);
rcon_client_password = Cvar_Get ("rcon_password", "", 0);
rcon_address = Cvar_Get ("rcon_address", "", 0);
cl_lightlevel = Cvar_Get ("r_lightlevel", "0", 0);
//
// userinfo
//
info_password = Cvar_Get ("password", "", CVAR_USERINFO);
info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME
msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
gender->modified = false; // clear this so we know when user sets it manually
cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
//
// register our commands
//
Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
Cmd_AddCommand ("pause", CL_Pause_f);
Cmd_AddCommand ("pingservers", CL_PingServers_f);
Cmd_AddCommand ("skins", CL_Skins_f);
Cmd_AddCommand ("userinfo", CL_Userinfo_f);
Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
Cmd_AddCommand ("changing", CL_Changing_f);
Cmd_AddCommand ("disconnect", CL_Disconnect_f);
Cmd_AddCommand ("record", CL_Record_f);
Cmd_AddCommand ("stop", CL_Stop_f);
Cmd_AddCommand ("quit", CL_Quit_f);
Cmd_AddCommand ("connect", CL_Connect_f);
Cmd_AddCommand ("reconnect", CL_Reconnect_f);
Cmd_AddCommand ("rcon", CL_Rcon_f);
// Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
Cmd_AddCommand ("setenv", CL_Setenv_f );
Cmd_AddCommand ("precache", CL_Precache_f);
Cmd_AddCommand ("download", CL_Download_f);
//
// forward to server commands
//
// the only thing this does is allow command completion
// to work -- all unknown commands are automatically
// forwarded to the server
Cmd_AddCommand ("wave", NULL);
Cmd_AddCommand ("inven", NULL);
Cmd_AddCommand ("kill", NULL);
Cmd_AddCommand ("use", NULL);
Cmd_AddCommand ("drop", NULL);
Cmd_AddCommand ("say", NULL);
Cmd_AddCommand ("say_team", NULL);
Cmd_AddCommand ("info", NULL);
Cmd_AddCommand ("prog", NULL);
Cmd_AddCommand ("give", NULL);
Cmd_AddCommand ("god", NULL);
Cmd_AddCommand ("notarget", NULL);
Cmd_AddCommand ("noclip", NULL);
Cmd_AddCommand ("invuse", NULL);
Cmd_AddCommand ("invprev", NULL);
Cmd_AddCommand ("invnext", NULL);
Cmd_AddCommand ("invdrop", NULL);
Cmd_AddCommand ("weapnext", NULL);
Cmd_AddCommand ("weapprev", NULL);
}
/*
===============
CL_WriteConfiguration
Writes key bindings and archived cvars to config.cfg
===============
*/
void CL_WriteConfiguration (void)
{
FILE *f;
char path[MAX_QPATH];
if (cls.state == ca_uninitialized)
return;
Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
f = fopen (path, "w");
if (!f)
{
Com_Printf ("Couldn't write config.cfg.\n");
return;
}
fprintf (f, "// generated by quake, do not modify\n");
Key_WriteBindings (f);
fclose (f);
Cvar_WriteVariables (path);
}
/*
==================
CL_FixCvarCheats
==================
*/
typedef struct
{
char *name;
char *value;
cvar_t *var;
} cheatvar_t;
cheatvar_t cheatvars[] = {
{"timescale", "1"},
{"timedemo", "0"},
{"r_drawworld", "1"},
{"cl_testlights", "0"},
{"r_fullbright", "0"},
{"r_drawflat", "0"},
{"paused", "0"},
{"fixedtime", "0"},
{"sw_draworder", "0"},
{"gl_lightmap", "0"},
{"gl_saturatelighting", "0"},
{NULL, NULL}
};
int numcheatvars;
void CL_FixCvarCheats (void)
{
int i;
cheatvar_t *var;
if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")
|| !cl.configstrings[CS_MAXCLIENTS][0] )
return; // single player can cheat
// find all the cvars if we haven't done it yet
if (!numcheatvars)
{
while (cheatvars[numcheatvars].name)
{
cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
cheatvars[numcheatvars].value, 0);
numcheatvars++;
}
}
// make sure they are all set to the proper values
for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
{
if ( strcmp (var->var->string, var->value) )
{
Cvar_Set (var->name, var->value);
}
}
}
//============================================================================
/*
==================
CL_SendCommand
==================
*/
void CL_SendCommand (void)
{
// get new key events
Sys_SendKeyEvents ();
// allow mice or other external controllers to add commands
IN_Commands ();
// process console commands
Cbuf_Execute ();
// fix any cheating cvars
CL_FixCvarCheats ();
// send intentions now
CL_SendCmd ();
// resend a connection request if necessary
CL_CheckForResend ();
}
/*
==================
CL_Frame
==================
*/
void CL_Frame (int msec)
{
static int extratime;
static int lasttimecalled;
if (dedicated->value)
return;
extratime += msec;
if (!cl_timedemo->value)
{
if (cls.state == ca_connected && extratime < 100)
return; // don't flood packets out while connecting
if (extratime < 1000/cl_maxfps->value)
return; // framerate is too high
}
// let the mouse activate or deactivate
IN_Frame ();
// decide the simulation time
cls.frametime = extratime/1000.0;
cl.time += extratime;
cls.realtime = curtime;
extratime = 0;
#if 0
if (cls.frametime > (1.0 / cl_minfps->value))
cls.frametime = (1.0 / cl_minfps->value);
#else
if (cls.frametime > (1.0 / 5))
cls.frametime = (1.0 / 5);
#endif
// if in the debugger last frame, don't timeout
if (msec > 5000)
cls.netchan.last_received = Sys_Milliseconds ();
// fetch results from server
CL_ReadPackets ();
// send a new command message to the server
CL_SendCommand ();
// predict all unacknowledged movements
CL_PredictMovement ();
// allow rendering DLL change
VID_CheckChanges ();
if (!cl.refresh_prepped && cls.state == ca_active)
CL_PrepRefresh ();
// update the screen
if (host_speeds->value)
time_before_ref = Sys_Milliseconds ();
SCR_UpdateScreen ();
if (host_speeds->value)
time_after_ref = Sys_Milliseconds ();
// update audio
S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
CDAudio_Update();
// advance local effects for next frame
CL_RunDLights ();
CL_RunLightStyles ();
SCR_RunCinematic ();
SCR_RunConsole ();
cls.framecount++;
if ( log_stats->value )
{
if ( cls.state == ca_active )
{
if ( !lasttimecalled )
{
lasttimecalled = Sys_Milliseconds();
if ( log_stats_file )
fprintf( log_stats_file, "0\n" );
}
else
{
int now = Sys_Milliseconds();
if ( log_stats_file )
fprintf( log_stats_file, "%d\n", now - lasttimecalled );
lasttimecalled = now;
}
}
}
}
//============================================================================
/*
====================
CL_Init
====================
*/
void CL_Init (void)
{
if (dedicated->value)
return; // nothing running on the client
// all archived variables will now be loaded
Con_Init ();
#if defined __linux__ || defined __sgi
S_Init ();
VID_Init ();
#else
VID_Init ();
S_Init (); // sound must be initialized after window is created
#endif
V_Init ();
net_message.data = net_message_buffer;
net_message.maxsize = sizeof(net_message_buffer);
M_Init ();
SCR_Init ();
cls.disable_screen = true; // don't draw yet
CDAudio_Init ();
CL_InitLocal ();
IN_Init ();
// Cbuf_AddText ("exec autoexec.cfg\n");
FS_ExecAutoexec ();
Cbuf_Execute ();
}
/*
===============
CL_Shutdown
FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
to run quit through here before the final handoff to the sys code.
===============
*/
void CL_Shutdown(void)
{
static qboolean isdown = false;
if (isdown)
{
printf ("recursive shutdown\n");
return;
}
isdown = true;
CL_WriteConfiguration ();
CDAudio_Shutdown ();
S_Shutdown();
IN_Shutdown ();
VID_Shutdown();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -