/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // Copyright (C) 2001-2003 pat@aftermoon.net for modif flanked by // cl_main.c -- client main loop #include "client.h" #include "../ui/ui_local.h" #ifdef _WIN32 #include "../win32/winquake.h" #endif cvar_t *freelook; cvar_t *adr0; cvar_t *adr1; cvar_t *adr2; cvar_t *adr3; cvar_t *adr4; cvar_t *adr5; cvar_t *adr6; cvar_t *adr7; cvar_t *adr8; cvar_t *cl_stereo_separation; cvar_t *cl_stereo; cvar_t *rcon_client_password; cvar_t *rcon_address; cvar_t *cl_noskins; cvar_t *cl_autoskins; cvar_t *cl_footsteps; cvar_t *cl_timeout; cvar_t *cl_predict; //cvar_t *cl_minfps; cvar_t *cl_maxfps; cvar_t *cl_sleep; // whether to trick version 34 servers that this is a version 34 client cvar_t *cl_servertrick; cvar_t *cl_gun; cvar_t *cl_weapon_shells; cvar_t *cl_blood; // reduction factor for particle effects cvar_t *cl_particle_scale; // whether to adjust fov for wide aspect rattio cvar_t *cl_widescreen_fov; // whether to use texsurfs.txt footstep sounds cvar_t *cl_footstep_override; // Psychospaz's rail code cvar_t *cl_railred; cvar_t *cl_railgreen; cvar_t *cl_railblue; cvar_t *cl_railtype; cvar_t *cl_rail_length; cvar_t *cl_rail_space; cvar_t *r_decals; // decal quantity cvar_t *r_decal_life; // decal duration in seconds cvar_t *con_font_size; cvar_t *alt_text_color; // Psychospaz's chasecam cvar_t *cl_3dcam; cvar_t *cl_3dcam_angle; cvar_t *cl_3dcam_chase; cvar_t *cl_3dcam_dist; cvar_t *cl_3dcam_alpha; cvar_t *cl_3dcam_adjust; cvar_t *cl_3dcam_yaw; cvar_t *cl_enableconsole; // whether to try to play OGGs instead of CD tracks cvar_t *cl_ogg_music; cvar_t *cl_rogue_music; // whether to play Rogue tracks cvar_t *cl_xatrix_music; // whether to play Xatrix tracks cvar_t *cl_add_particles; cvar_t *cl_add_lights; cvar_t *cl_add_entities; cvar_t *cl_add_blend; cvar_t *cl_shownet; cvar_t *cl_showmiss; cvar_t *cl_showclamp; cvar_t *cl_paused; cvar_t *cl_timedemo; cvar_t *lookspring; cvar_t *lookstrafe; cvar_t *sensitivity; cvar_t *menu_sensitivity; cvar_t *menu_rotate; cvar_t *menu_alpha; //cvar_t *hud_scale; //cvar_t *hud_width; //cvar_t *hud_height; //cvar_t *hud_alpha; cvar_t *m_pitch; cvar_t *m_yaw; cvar_t *m_forward; cvar_t *m_side; cvar_t *cl_lightlevel; // // userinfo // cvar_t *info_password; cvar_t *info_spectator; cvar_t *name; cvar_t *skin; cvar_t *rate; cvar_t *fov; cvar_t *msg; cvar_t *hand; cvar_t *gender; cvar_t *gender_auto; cvar_t *cl_vwep; // for the server to tell which version the client is cvar_t *cl_engine; cvar_t *cl_engine_version; client_static_t cls; client_state_t cl; centity_t cl_entities[MAX_EDICTS]; entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES]; extern cvar_t *allow_download; extern cvar_t *allow_download_players; extern cvar_t *allow_download_models; extern cvar_t *allow_download_sounds; extern cvar_t *allow_download_maps; // whether to allow downloading 24-bit textures extern cvar_t *allow_download_enh_textures; extern cvar_t *s_volume; extern cvar_t *s_musicvolume; float ClampCvar( float min, float max, float value ) { if ( value < min ) return min; if ( value > max ) return max; return value; } //====================================================================== /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length ==================== */ void CL_WriteDemoMessage (void) { int len, swlen; // the first eight bytes are just packet sequencing stuff len = net_message.cursize-8; swlen = LittleLong(len); fwrite (&swlen, 4, 1, cls.demofile); fwrite (net_message.data+8, len, 1, cls.demofile); } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { int len; if (!cls.demorecording) { Com_Printf ("Not recording a demo.\n"); return; } // finish up len = -1; fwrite (&len, 4, 1, cls.demofile); fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Com_Printf ("Stopped demo.\n"); } /* ==================== CL_Record_f record Begins recording a demo from the current position ==================== */ void CL_Record_f (void) { char name[MAX_OSPATH]; char buf_data[MAX_MSGLEN]; sizebuf_t buf; int i; int len; entity_state_t *ent; entity_state_t nullstate; if (Cmd_Argc() != 2) { Com_Printf ("record \n"); return; } if (cls.demorecording) { Com_Printf ("Already recording.\n"); return; } if (cls.state != ca_active) { Com_Printf ("You must be in a level to record.\n"); return; } // // open the demo file // Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1)); Com_Printf ("recording to %s.\n", name); FS_CreatePath (name); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Com_Printf ("ERROR: couldn't open.\n"); return; } cls.demorecording = true; // don't start saving messages until a non-delta compressed message is received cls.demowaiting = true; // // write out messages to hold the startup information // SZ_Init (&buf, buf_data, sizeof(buf_data)); // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, 0x10000 + cl.servercount); MSG_WriteByte (&buf, 1); // demos are always attract loops MSG_WriteString (&buf, cl.gamedir); MSG_WriteShort (&buf, cl.playernum); MSG_WriteString (&buf, cl.configstrings[CS_NAME]); // configstrings for (i=0 ; i buf.maxsize) { // write it out len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte (&buf, svc_configstring); MSG_WriteShort (&buf, i); MSG_WriteString (&buf, cl.configstrings[i]); } } // baselines memset (&nullstate, 0, sizeof(nullstate)); for (i=0; imodelindex) continue; if (buf.cursize + 64 > buf.maxsize) { // write it out len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte (&buf, svc_spawnbaseline); MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true); } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, "precache\n"); // write it to the demo file len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); // the rest of the demo file will be individual frames } //====================================================================== /* =================== 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; cmd = Cmd_Argv(0); if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+') { Com_Printf ("Unknown command \"%s\"\n", cmd); return; } MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, cmd); if (Cmd_Argc() > 1) { SZ_Print (&cls.netchan.message, " "); SZ_Print (&cls.netchan.message, Cmd_Args()); } } void CL_Setenv_f( void ) { int argc = Cmd_Argc(); if ( argc > 2 ) { char buffer[1000]; int i; strcpy( buffer, Cmd_Argv(1) ); strcat( buffer, "=" ); for ( i = 2; i < argc; i++ ) { strcat( buffer, Cmd_Argv( i ) ); strcat( buffer, " " ); } putenv( buffer ); } else if ( argc == 2 ) { char *env = getenv( Cmd_Argv(1) ); if ( env ) { Com_Printf( "%s=%s\n", Cmd_Argv(1), env ); } else { Com_Printf( "%s undefined\n", Cmd_Argv(1), env ); } } } /* ================== CL_ForwardToServer_f ================== */ void CL_ForwardToServer_f (void) { if (cls.state != ca_connected && cls.state != ca_active) { Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } // don't forward the first argument if (Cmd_Argc() > 1) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, Cmd_Args()); } } /* ================== CL_Pause_f ================== */ void CL_Pause_f (void) { // never pause in multiplayer if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ()) { Cvar_SetValue ("paused", 0); return; } Cvar_SetValue ("paused", !cl_paused->value); } /* ================== CL_Quit_f ================== */ void CL_Quit_f (void) { CL_Disconnect (); Com_Quit (); } /* ================ CL_Drop Called after an ERR_DROP was thrown ================ */ void CL_Drop (void) { if (cls.state == ca_uninitialized) return; // if an error occurs during initial load // or during game start, drop loading plaque if ( (cls.disable_servercount != -1) || (cls.key_dest == key_game) ) SCR_EndLoadingPlaque (); // get rid of loading plaque if (cls.state == ca_disconnected) return; CL_Disconnect (); } /* ======================= CL_SendConnectPacket We have gotten a challenge from the server, so try and connect. ====================== */ void CL_SendConnectPacket (void) { netadr_t adr; int port; if (!NET_StringToAdr (cls.servername, &adr)) { Com_Printf ("Bad server address\n"); cls.connect_time = 0; return; } if (adr.port == 0) adr.port = BigShort (PORT_SERVER); port = Cvar_VariableValue ("qport"); userinfo_modified = false; // if in compatibility mode, lie to server about this // client's protocol, but exclude localhost for this. if (cl_servertrick->value && strcmp(cls.servername, "localhost")) Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", OLD_PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() ); else Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() ); } /* ================= CL_CheckForResend Resend a connect message if the last one has timed out ================= */ void CL_CheckForResend (void) { netadr_t adr; // if the local server is running and we aren't // then connect if (cls.state == ca_disconnected && Com_ServerState() ) { cls.state = ca_connecting; strncpy (cls.servername, "localhost", sizeof(cls.servername)-1); // we don't need a challenge on the localhost CL_SendConnectPacket (); return; // cls.connect_time = -99999; // CL_CheckForResend() will fire immediately } // resend if we haven't gotten a reply yet if (cls.state != ca_connecting) return; if (cls.realtime - cls.connect_time < 3000) return; if (!NET_StringToAdr (cls.servername, &adr)) { Com_Printf ("Bad server address\n"); cls.state = ca_disconnected; return; } if (adr.port == 0) adr.port = BigShort (PORT_SERVER); cls.connect_time = cls.realtime; // for retransmit requests Com_Printf ("Connecting to %s...\n", cls.servername); Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n"); } /* ================ CL_Connect_f ================ */ void CL_Connect_f (void) { char *server; if (Cmd_Argc() != 2) { Com_Printf ("usage: connect \n"); return; } if (Com_ServerState ()) { // if running a local server, kill it and reissue SV_Shutdown (va("Server quit\n", msg), false); } else { CL_Disconnect (); } server = Cmd_Argv (1); NET_Config (true); // allow remote CL_Disconnect (); cls.state = ca_connecting; strncpy (cls.servername, server, sizeof(cls.servername)-1); cls.connect_time = -99999; // CL_CheckForResend() will fire immediately } /* ===================== CL_Rcon_f Send the rest of the command line over as an unconnected command. ===================== */ void CL_Rcon_f (void) { char message[1024]; int i; netadr_t to; if (!rcon_client_password->string) { Com_Printf ("You must set 'rcon_password' before\n" "issuing an rcon command.\n"); return; } message[0] = (char)255; message[1] = (char)255; message[2] = (char)255; message[3] = (char)255; message[4] = 0; NET_Config (true); // allow remote strcat (message, "rcon "); strcat (message, rcon_client_password->string); strcat (message, " "); for (i=1 ; i= ca_connected) to = cls.netchan.remote_address; else { if (!strlen(rcon_address->string)) { Com_Printf ("You must either be connected,\n" "or set the 'rcon_address' cvar\n" "to issue rcon commands\n"); return; } NET_StringToAdr (rcon_address->string, &to); if (to.port == 0) to.port = BigShort (PORT_SERVER); } NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to); } /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { S_StopAllSounds (); CL_ClearEffects (); CL_ClearTEnts (); R_SetFogVars (false, 0, 0, 0, 0, 0, 0, 0); // clear fog effets // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); memset (&cl_entities, 0, sizeof(cl_entities)); SZ_Clear (&cls.netchan.message); } /* ===================== CL_Disconnect Goes from a connected state to full screen console state Sends a disconnect message to the server This is also called on Com_Error, so it shouldn't cause any errors ===================== */ extern char *currentweaponmodel; void CL_Disconnect (void) { byte final[32]; if (cls.state == ca_disconnected) return; if (cl_timedemo && cl_timedemo->value) { int time; time = Sys_Milliseconds () - cl.timedemo_start; if (time > 0) Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames, time/1000.0, cl.timedemo_frames*1000.0 / time); } VectorClear (cl.refdef.blend); //R_SetPalette(NULL); UI_ForceMenuOff (); cls.connect_time = 0; SCR_StopCinematic (); if (cls.demorecording) CL_Stop_f (); // send a disconnect message to the server final[0] = clc_stringcmd; strcpy ((char *)final+1, "disconnect"); Netchan_Transmit (&cls.netchan, strlen(final), final); Netchan_Transmit (&cls.netchan, strlen(final), final); Netchan_Transmit (&cls.netchan, strlen(final), final); CL_ClearState (); // stop download if (cls.download) { fclose(cls.download); cls.download = NULL; } cls.state = ca_disconnected; // reset current weapon model currentweaponmodel = NULL; } void CL_Disconnect_f (void) { Com_Error (ERR_DROP, "Disconnected from server"); } /* ==================== CL_Packet_f packet Contents allows \n escape character ==================== */ void CL_Packet_f (void) { char send[2048]; int i, l; char *in, *out; netadr_t adr; if (Cmd_Argc() != 3) { Com_Printf ("packet \n"); return; } NET_Config (true); // allow remote if (!NET_StringToAdr (Cmd_Argv(1), &adr)) { Com_Printf ("Bad address\n"); return; } if (!adr.port) adr.port = BigShort (PORT_SERVER); in = Cmd_Argv(2); out = send+4; send[0] = send[1] = send[2] = send[3] = (char)0xff; l = strlen (in); for (i=0 ; i= ca_connected) { CL_Disconnect(); cls.connect_time = cls.realtime - 1500; } else cls.connect_time = -99999; // fire immediately cls.state = ca_connecting; Com_Printf ("reconnecting...\n"); } } /* ================= CL_ParseStatusMessage Handle a reply from a ping ================= */ void CL_ParseStatusMessage (void) { char *s; s = MSG_ReadString(&net_message); Com_Printf ("%s\n", s); UI_AddToServerList (net_from, s); } /* ================= CL_PingServers_f ================= */ #if 1 // Added code for compute ping time of server broadcasted extern int global_udp_server_time; extern int global_ipx_server_time; extern int global_adr_server_time[16]; extern netadr_t global_adr_server_netadr[16]; void CL_PingServers_f (void) { int i; netadr_t adr; char name[32]; char *adrstring; cvar_t *noudp; cvar_t *noipx; NET_Config (true); // allow remote // send a broadcast packet Com_Printf ("pinging broadcast...\n"); // send a packet to each address book entry for (i=0 ; i<16 ; i++) { memset(&global_adr_server_netadr[i], 0, sizeof(global_adr_server_netadr[0])); global_adr_server_time[i] = Sys_Milliseconds() ; Com_sprintf (name, sizeof(name), "adr%i", i); adrstring = Cvar_VariableString (name); if (!adrstring || !adrstring[0]) continue; Com_Printf ("pinging %s...\n", adrstring); if (!NET_StringToAdr (adrstring, &adr)) { Com_Printf ("Bad address: %s\n", adrstring); continue; } if (!adr.port) adr.port = BigShort(PORT_SERVER); memcpy(&global_adr_server_netadr[i], &adr, sizeof(global_adr_server_netadr)); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noudp = Cvar_Get ("noudp", "0", CVAR_NOSET); if (!noudp->value) { global_udp_server_time = Sys_Milliseconds() ; adr.type = NA_BROADCAST; adr.port = BigShort(PORT_SERVER); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noipx = Cvar_Get ("noipx", "0", CVAR_NOSET); if (!noipx->value) { global_ipx_server_time = Sys_Milliseconds() ; adr.type = NA_BROADCAST_IPX; adr.port = BigShort(PORT_SERVER); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } } //> #else void CL_PingServers_f (void) { int i; netadr_t adr; char name[32]; char *adrstring; cvar_t *noudp; cvar_t *noipx; NET_Config (true); // allow remote // send a broadcast packet Com_Printf ("pinging broadcast...\n"); noudp = Cvar_Get ("noudp", "0", CVAR_NOSET); if (!noudp->value) { adr.type = NA_BROADCAST; adr.port = BigShort(PORT_SERVER); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noipx = Cvar_Get ("noipx", "0", CVAR_NOSET); if (!noipx->value) { adr.type = NA_BROADCAST_IPX; adr.port = BigShort(PORT_SERVER); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } // send a packet to each address book entry for (i=0; i<16; i++) { Com_sprintf (name, sizeof(name), "adr%i", i); adrstring = Cvar_VariableString (name); if (!adrstring || !adrstring[0]) continue; Com_Printf ("pinging %s...\n", adrstring); if (!NET_StringToAdr (adrstring, &adr)) { Com_Printf ("Bad address: %s\n", adrstring); continue; } if (!adr.port) adr.port = BigShort(PORT_SERVER); // if the server is using the old protocol, // lie to it about this client's protocol if (cl_servertrick->value) Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); else Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } } #endif /* ================= CL_Skins_f Load or download any custom player skins and models ================= */ void CL_Skins_f (void) { int i; for (i=0 ; ivalue) Cvar_SetValue ("cl_3dcam", 0 ); else Cvar_SetValue ("cl_3dcam", 1 ); } /* ================= CL_ConnectionlessPacket Responses to broadcasts, etc ================= */ void CL_ConnectionlessPacket (void) { char *s; char *c; MSG_BeginReading (&net_message); MSG_ReadLong (&net_message); // skip the -1 s = MSG_ReadStringLine (&net_message); Cmd_TokenizeString (s, false); c = Cmd_Argv(0); Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c); // server connection if (!strcmp(c, "client_connect")) { if (cls.state == ca_connected) { Com_Printf ("Dup connect received. Ignored.\n"); return; } Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort); MSG_WriteChar (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "new"); cls.state = ca_connected; return; } // server responding to a status broadcast if (!strcmp(c, "info")) { CL_ParseStatusMessage (); return; } // remote command from gui front end if (!strcmp(c, "cmd")) { if (!NET_IsLocalAddress(net_from)) { Com_Printf ("Command packet from remote host. Ignored.\n"); return; } Sys_AppActivate (); s = MSG_ReadString (&net_message); Cbuf_AddText (s); Cbuf_AddText ("\n"); return; } // print command from somewhere if (!strcmp(c, "print")) { s = MSG_ReadString (&net_message); Com_Printf ("%s", s); return; } // ping from somewhere if (!strcmp(c, "ping")) { Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack"); return; } // challenge from the server we are connecting to if (!strcmp(c, "challenge")) { cls.challenge = atoi(Cmd_Argv(1)); CL_SendConnectPacket (); return; } // echo request from server if (!strcmp(c, "echo")) { Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) ); return; } Com_Printf ("Unknown command.\n"); } /* ================= CL_DumpPackets A vain attempt to help bad TCP stacks that cause problems when they overflow ================= */ void CL_DumpPackets (void) { while (NET_GetPacket (NS_CLIENT, &net_from, &net_message)) { Com_Printf ("dumnping a packet\n"); } } /* ================= CL_ReadPackets ================= */ void CL_ReadPackets (void) { while (NET_GetPacket (NS_CLIENT, &net_from, &net_message)) { // Com_Printf ("packet\n"); // // remote command packet // if (*(int *)net_message.data == -1) { CL_ConnectionlessPacket (); continue; } if (cls.state == ca_disconnected || cls.state == ca_connecting) continue; // dump it if not connected if (net_message.cursize < 8) { Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from)); continue; } // // packet from server // if (!NET_CompareAdr (net_from, cls.netchan.remote_address)) { Com_DPrintf ("%s:sequenced packet without connection\n" ,NET_AdrToString(net_from)); continue; } if (!Netchan_Process(&cls.netchan, &net_message)) continue; // wasn't accepted for some reason CL_ParseServerMessage (); } // // check timeout // if (cls.state >= ca_connected && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000) { if (++cl.timeoutcount > 5) // timeoutcount saves debugger { Com_Printf ("\nServer connection timed out.\n"); CL_Disconnect (); return; } } else cl.timeoutcount = 0; } //============================================================================= /* ============== CL_FixUpGender_f ============== */ void CL_FixUpGender(void) { char *p; char sk[80]; if (gender_auto->value) { if (gender->modified) { // was set directly, don't override the user gender->modified = false; return; } strncpy(sk, skin->string, sizeof(sk) - 1); if ((p = strchr(sk, '/')) != NULL) *p = 0; if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0) Cvar_Set ("gender", "male"); else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0) Cvar_Set ("gender", "female"); else Cvar_Set ("gender", "none"); gender->modified = false; } } /* ============== CL_Userinfo_f ============== */ void CL_Userinfo_f (void) { Com_Printf ("User info settings:\n"); Info_Print (Cvar_Userinfo()); } /* ================= CL_Snd_Restart_f Restart the sound subsystem so it can pick up new parameters and flush all sounds ================= */ void CL_Snd_Restart_f (void) { S_Shutdown (); S_Init (); CL_RegisterSounds (); } extern int precache_check; // for autodownload of precache items extern int precache_spawncount; //extern int precache_tex; extern int precache_model_skin; extern byte *precache_model; // used for skin checking in alias models /* ================= 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(); } void cl_volume_lower_f (void) { int volume; if (s_volume->value >= 0.1) Cvar_SetValue ("s_volume", s_volume->value - 0.1); else Cvar_SetValue ("s_volume", 0); if (s_volume->value > 0) { volume = s_volume->value * 100; Com_Printf ("Sound Volume: %i%%\n", volume); } else Com_Printf ("Sound Volume: OFF\n"); } void cl_volume_raise_f (void) { int volume; if (s_volume->value <= 0.9) Cvar_SetValue ("s_volume", s_volume->value + 0.1); else Cvar_SetValue ("s_volume", 1.0); volume = s_volume->value * 100; Com_Printf ("Sound Volume: %i%%\n", volume); } void cl_musicvolume_lower_f (void) { int volume; if (s_musicvolume->value >= 0.1) Cvar_SetValue ("s_musicvolume", s_musicvolume->value - 0.1); else Cvar_SetValue ("s_musicvolume", 0); if (s_musicvolume->value > 0) { volume = s_musicvolume->value * 100; Com_Printf ("Music Volume: %i%%\n", volume); } else Com_Printf ("Music Volume: OFF\n"); } void cl_musicvolume_raise_f (void) { int volume; if (s_musicvolume->value <= 0.9) Cvar_SetValue ("s_musicvolume", s_musicvolume->value + 0.1); else Cvar_SetValue ("s_musicvolume", 1.0); volume = s_musicvolume->value * 100; Com_Printf ("Music Volume: %i%%\n", volume); } /* ================= 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_weapon_shells = Cvar_Get ("cl_weapon_shells", "1", CVAR_ARCHIVE); cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0); cl_blood = Cvar_Get ("cl_blood", "2", CVAR_ARCHIVE); // reduction factor for particle effects cl_particle_scale = Cvar_Get ("cl_particle_scale", "1", CVAR_ARCHIVE); // whether to adjust fov for wide aspect rattio cl_widescreen_fov = Cvar_Get ("cl_widescreen_fov", "1", CVAR_ARCHIVE); 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_sleep = Cvar_Get ("cl_sleep", "1", 0); // whether to trick version 34 servers that this is a version 34 client cl_servertrick = Cvar_Get ("cl_servertrick", "0", 0); // whether to use texsurfs.txt footstep sounds cl_footstep_override = Cvar_Get ("cl_footstep_override", "1", CVAR_ARCHIVE); // Psychospaz's changeable rail code cl_railred = Cvar_Get ("cl_railred", "20", CVAR_ARCHIVE); cl_railgreen = Cvar_Get ("cl_railgreen", "50", CVAR_ARCHIVE); cl_railblue = Cvar_Get ("cl_railblue", "175", CVAR_ARCHIVE); cl_railtype = Cvar_Get ("cl_railtype", "0", CVAR_ARCHIVE); cl_rail_length = Cvar_Get ("cl_rail_length", va("%i", DEFAULT_RAIL_LENGTH), CVAR_ARCHIVE); cl_rail_space = Cvar_Get ("cl_rail_space", va("%i", DEFAULT_RAIL_SPACE), CVAR_ARCHIVE); // decal control r_decals = Cvar_Get ("r_decals", "500", CVAR_ARCHIVE); r_decal_life = Cvar_Get ("r_decal_life", "1000", CVAR_ARCHIVE); con_font_size = Cvar_Get ("con_font_size", "16", CVAR_ARCHIVE); alt_text_color = Cvar_Get ("alt_text_color", "9", CVAR_ARCHIVE); cl_enableconsole = Cvar_Get ("cl_enableconsole", "0", CVAR_ARCHIVE); // Psychospaz's chasecam cl_3dcam = Cvar_Get ("cl_3dcam", "0", CVAR_ARCHIVE); cl_3dcam_angle = Cvar_Get ("cl_3dcam_angle", "10", CVAR_ARCHIVE); cl_3dcam_dist = Cvar_Get ("cl_3dcam_dist", "50", CVAR_ARCHIVE); cl_3dcam_alpha = Cvar_Get ("cl_3dcam_alpha", "0", CVAR_ARCHIVE); cl_3dcam_chase = Cvar_Get ("cl_3dcam_chase", "1", CVAR_ARCHIVE); cl_3dcam_adjust = Cvar_Get ("cl_3dcam_adjust", "1", CVAR_ARCHIVE); cl_3dcam_yaw = Cvar_Get ("cl_3dcam_yaw", "0", CVAR_ARCHIVE); // whether to try to play OGGs instead of CD tracks cl_ogg_music = Cvar_Get ("cl_ogg_music", "1", CVAR_ARCHIVE); cl_rogue_music = Cvar_Get ("cl_rogue_music", "0", CVAR_ARCHIVE); cl_xatrix_music = Cvar_Get ("cl_xatrix_music", "0", CVAR_ARCHIVE); cl_upspeed = Cvar_Get ("cl_upspeed", "150", 0); cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "150", 0); cl_sidespeed = Cvar_Get ("cl_sidespeed", "150", 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", "1", CVAR_ARCHIVE ); // Knightmare changed, was 0 lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE); lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE); sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); menu_sensitivity = Cvar_Get ("menu_sensitivity", "1", CVAR_ARCHIVE); menu_rotate = Cvar_Get ("menu_rotate", "0", CVAR_ARCHIVE); menu_alpha = Cvar_Get ("menu_alpha", "0.6", CVAR_ARCHIVE); // hud_scale = Cvar_Get ("hud_scale", "3", CVAR_ARCHIVE); // hud_width = Cvar_Get ("hud_width", "640", CVAR_ARCHIVE); // hud_height = Cvar_Get ("hud_height", "480", CVAR_ARCHIVE); // hud_alpha = Cvar_Get ("hud_alpha", "1", 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", CVAR_CHEAT); cl_timedemo = Cvar_Get ("timedemo", "0", CVAR_CHEAT); 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", "Citizen Abel", 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); // for the server to tell which version the client is cl_engine = Cvar_Get ("cl_engine", "KMQuake2", CVAR_USERINFO | CVAR_NOSET | CVAR_LATCH); cl_engine_version = Cvar_Get ("cl_engine_version", va("%f",VERSION), CVAR_USERINFO | CVAR_NOSET | CVAR_LATCH); // // 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); // added Psychospaz's command for toggling chasecam Cmd_AddCommand ("togglecam", CL_ToggleCam_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); Cmd_AddCommand ("volume_lower", cl_volume_lower_f); Cmd_AddCommand ("volume_raise", cl_volume_raise_f); Cmd_AddCommand ("musicvolume_lower", cl_musicvolume_lower_f); Cmd_AddCommand ("musicvolume_raise", cl_musicvolume_raise_f); } /* =============== 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; // Knightmare changed- use separate config for better cohabitation //Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir()); Com_sprintf (path, sizeof(path),"%s/kmq2config.cfg",FS_Gamedir()); f = fopen (path, "w"); if (!f) { // Knightmare changed- use separate config for better cohabitation //Com_Printf ("Couldn't write config.cfg.\n"); Com_Printf ("Couldn't write kmq2config.cfg.\n"); return; } fprintf (f, "// This file is generated by KMQuake2, do not modify.\n"); fprintf (f, "// Use autoexec.cfg for adding custom settings.\n"); Key_WriteBindings (f); fclose (f); Cvar_WriteVariables (path); } /* ================== CL_FixCvarCheats ================== */ void CL_FixCvarCheats (void) { if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1") || !cl.configstrings[CS_MAXCLIENTS][0] ) { // single player can cheat Cvar_FixCheatVars(true); return; } // don't allow cheats in multiplayer Cvar_FixCheatVars(false); } //============================================================================ /* ================== 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) { #ifdef _WIN32 // Pooy's CPU usage fix if (cl_sleep->value) { int temptime = 1000/cl_maxfps->value-extratime; if (temptime > 1) Sleep(1); } #endif // end CPU usage fix 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 // clamp this to acceptable values (don't allow infinite particles) if (cl_particle_scale->value < 1.0f) Cvar_SetValue( "cl_particle_scale", 1); // clamp this to acceptable minimum length if (cl_rail_length->value < MIN_RAIL_LENGTH) Cvar_SetValue( "cl_rail_length", MIN_RAIL_LENGTH); // clamp this to acceptable minimum duration if (r_decal_life->value < MIN_DECAL_LIFE) Cvar_SetValue( "r_decal_life", MIN_DECAL_LIFE); // 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); UI_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; int sec, base; // zaphster's delay variables if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; CL_WriteConfiguration (); CDAudio_Shutdown (); // added delay sec = base = Sys_Milliseconds(); while ((sec - base) < 200) sec = Sys_Milliseconds(); // end delay S_Shutdown(); // added delay sec = base = Sys_Milliseconds(); while ((sec - base) < 200) sec = Sys_Milliseconds(); // end delay IN_Shutdown (); VID_Shutdown(); }