/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code 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. Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 *adr9; cvar_t *adr10; cvar_t *adr11; 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; // unused cvar_t *cl_footsteps; cvar_t *cl_timeout; cvar_t *cl_predict; //cvar_t *cl_minfps; cvar_t *cl_maxfps; #ifdef CLIENT_SPLIT_NETFRAME cvar_t *cl_async; cvar_t *net_maxfps; cvar_t *r_maxfps; cvar_t *r_maxfps_autoset; #endif 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; // reduction factor for particle effects cvar_t *cl_particle_scale; // whether to adjust fov for wide aspect rattio cvar_t *cl_widescreen_fov; // Psychospaz's chasecam cvar_t *cg_thirdperson; cvar_t *cg_thirdperson_angle; cvar_t *cg_thirdperson_chase; cvar_t *cg_thirdperson_dist; cvar_t *cg_thirdperson_offset; cvar_t *cg_thirdperson_alpha; cvar_t *cg_thirdperson_adjust; cvar_t *cg_thirdperson_indemo; cvar_t *cg_thirdperson_overhead; cvar_t *cg_thirdperson_overhead_dist; cvar_t *cl_blood; cvar_t *cl_old_explosions; // Option for old explosions cvar_t *cl_plasma_explo_sound; // Option for unique plasma explosion sound cvar_t *cl_item_bobbing; // Option for bobbing items // 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; // whether to use texsurfs.txt footstep sounds cvar_t *cl_footstep_override; 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; // 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 *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; #ifdef USE_CURL // HTTP downloading from R1Q2 cvar_t *cl_http_downloads; cvar_t *cl_http_filelists; cvar_t *cl_http_proxy; cvar_t *cl_http_max_connections; cvar_t *cl_http_fallback; #endif // USE_CURL #ifdef LOC_SUPPORT // Xile/NiceAss LOC cvar_t *cl_drawlocs; cvar_t *loc_here; cvar_t *loc_there; #endif // LOC_SUPPORT // Chat Ignore from R1Q2/Q2Pro chatIgnore_t cl_chatNickIgnores; chatIgnore_t cl_chatTextIgnores; // end R1Q2/Q2Pro Chat Ignore client_static_t cls; client_state_t cl; centity_t cl_entities[MAX_EDICTS]; entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES]; qboolean local_initialized = false; //====================================================================== /* ==================== 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_Savegamedir(), Cmd_Argv(1)); // was FS_Gamedir() 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 } //====================================================================== // Chat Ignore from R1Q2/Q2Pro //====================================================================== /* =================== CL_FindChatIgnore =================== */ chatIgnore_t *CL_FindChatIgnore (chatIgnore_t *ignoreList, const char *match) { chatIgnore_t *cur=NULL; if (!ignoreList || !ignoreList->next) // no list to search return NULL; if ( !match || (strlen(match) < 1) ) // no search string return NULL; for (cur = ignoreList->next; cur != NULL; cur = cur->next) { if ( !cur->text || (strlen(cur->text) < 1) ) continue; if (!strcmp(cur->text, match)) return cur; } return NULL; } /* =================== CL_AddChatIgnore =================== */ qboolean CL_AddChatIgnore (chatIgnore_t *ignoreList, const char *add) { chatIgnore_t *next=NULL, *newEntry=NULL; size_t textLen; if (!ignoreList) // nothing to remove return false; if ( !add || (strlen(add) < 1) ) // no string to add return false; // Don't add the same ignore twice if ( CL_FindChatIgnore (ignoreList, add) ) { Com_Printf ("%s is already in ignore list.\n", add); return false; } next = ignoreList->next; // should be NULL for first entry textLen = strlen(Cmd_Argv(1))+1; newEntry = Z_Malloc (sizeof(chatIgnore_t)); newEntry->numHits = 0; newEntry->text = Z_Malloc (textLen); Q_strncpyz (newEntry->text, textLen, Cmd_Argv(1)); newEntry->next = next; ignoreList->next = newEntry; return true; } /* =================== CL_RemoveChatIgnore =================== */ qboolean CL_RemoveChatIgnore (chatIgnore_t *ignoreList, const char *match) { chatIgnore_t *cur=NULL, *last=NULL, *next=NULL; if (!ignoreList || !ignoreList->next) // nothing to remove return false; if ( !match || (strlen(match) < 1) ) // no search string return false; for (last = ignoreList, cur = ignoreList->next; cur != NULL; last = cur, cur = cur->next) { if ( !cur->text || (strlen(cur->text) < 1) ) continue; if ( !strcmp(match, cur->text) ) { next = cur->next; last->next = next; Z_Free (cur->text); cur->text = NULL; Z_Free (cur); return true; } } Com_Printf ("Can't find ignore filter \"%s\"\n", match); return false; } /* =================== CL_RemoveAllChatIgnores =================== */ void CL_RemoveAllChatIgnores (chatIgnore_t *ignoreList) { chatIgnore_t *cur=NULL, *next=NULL; int count = 0; if (!ignoreList || !ignoreList->next) // nothing to remove return; cur = ignoreList->next; next = cur->next; do { if (cur->text != NULL) { Z_Free (cur->text); cur->text = NULL; } next = cur->next; Z_Free (cur); cur = next; count++; } while (cur != NULL); ignoreList->next = NULL; Com_Printf ("Removed %i ignore filter(s).\n", count); } /* =================== CL_ListChatIgnores =================== */ void CL_ListChatIgnores (chatIgnore_t *ignoreList) { chatIgnore_t *cur=NULL; if (!ignoreList || !ignoreList->next) // no list to output return; Com_Printf ("Current ignore filters:\n"); for (cur = ignoreList->next; cur != NULL; cur = cur->next) { if ( !cur->text || (strlen(cur->text) < 1) ) continue; Com_Printf ("\"%s\" (%i hits)\n", cur->text, cur->numHits); } } /* =================== CL_IgnoreChatNick_f =================== */ void CL_IgnoreChatNick_f (void) { qboolean added; if (Cmd_Argc() < 2) { Com_Printf ("Usage: ignorenick \n"); CL_ListChatIgnores (&cl_chatNickIgnores); // output list if no param return; } added = CL_AddChatIgnore (&cl_chatNickIgnores, Cmd_Argv(1)); if (added) Com_Printf ("%s added to nick ignore list.\n", Cmd_Argv(1)); } /* =================== CL_UnIgnoreChatNick_f =================== */ void CL_UnIgnoreChatNick_f (void) { qboolean removed; if (Cmd_Argc() < 2) { Com_Printf ("Usage: unignorenick \n"); CL_ListChatIgnores (&cl_chatNickIgnores); // output list if no param return; } if ( (Cmd_Argc() == 2) && !strcmp(Cmd_Argv(1), "all") ) { CL_RemoveAllChatIgnores (&cl_chatNickIgnores); return; } removed = CL_RemoveChatIgnore (&cl_chatNickIgnores, Cmd_Argv(1)); if (removed) Com_Printf ("%s removed from nick ignore list.\n", Cmd_Argv(1)); } /* =================== CL_IgnoreChatText_f =================== */ void CL_IgnoreChatText_f (void) { qboolean added; if (Cmd_Argc() < 2) { Com_Printf ("Usage: ignoretext \n"); CL_ListChatIgnores (&cl_chatTextIgnores); // output list if no param return; } added = CL_AddChatIgnore (&cl_chatTextIgnores, Cmd_Argv(1)); if (added) Com_Printf ("%s added to text ignore list.\n", Cmd_Argv(1)); } /* =================== CL_UnIgnoreChatText_f =================== */ void CL_UnIgnoreChatText_f (void) { qboolean removed; if (Cmd_Argc() < 2) { Com_Printf ("Usage: unignoretext \n"); CL_ListChatIgnores (&cl_chatTextIgnores); // output list if no param return; } if ( (Cmd_Argc() == 2) && !strcmp(Cmd_Argv(1), "all") ) { CL_RemoveAllChatIgnores (&cl_chatTextIgnores); return; } removed = CL_RemoveChatIgnore (&cl_chatTextIgnores, Cmd_Argv(1)); if (removed) Com_Printf ("%s removed from text ignore list.\n", Cmd_Argv(1)); } /* =================== CL_ChatMatchIgnoreNick =================== */ qboolean CL_ChatMatchIgnoreNick (const char *buf, size_t bufSize, const char *nick) { size_t nickLen = strlen(nick); char *string = (char *)buf, *p = NULL; int idx = 0; qboolean clanTag; // Com_Printf ("CL_ChatMatchIgnoreNick: Searching for nick %s in chat message %s\n", nick, buf); do { clanTag = false; idx++; // catch nick with ": " following if ( !strncmp(string, nick, nickLen) && !strncmp(string + nickLen, ": ", 2) ) return true; if (*string == '(') // catch nick in parenthesis { if (!strncmp(string + 1, nick, nickLen) && !strncmp(string + 1 + nickLen, "): ", 3) ) return true; } // skip over clan tag in [] if (*string == '[') { p = strstr(string + 1, "] "); if (p) { // Com_Printf ("CL_ChatMatchIgnoreNick: skipping over clan tag\n"); clanTag = true; string = p + 2; } } } while ( clanTag && (idx < 2) && (string < (buf + bufSize)) ); return false; } /* =================== CL_CheckforChatIgnore =================== */ qboolean CL_CheckForChatIgnore (const char *string) { char chatBuf[MSG_STRING_SIZE]; chatIgnore_t *compare=NULL; if (!cl_chatNickIgnores.next && !cl_chatTextIgnores.next) // nothing in lists to compare return false; Q_strncpyz (chatBuf, sizeof(chatBuf), unformattedString(string)); // Com_Printf ("CL_CheckForChatIgnore: scanning chat message \"%s\" for ignore nicks and text\n", chatBuf); if (cl_chatNickIgnores.next != NULL) { for (compare = cl_chatNickIgnores.next; compare != NULL; compare = compare->next) { if ( (compare->text != NULL) && (strlen(compare->text) > 0) ) { if ( CL_ChatMatchIgnoreNick(chatBuf, sizeof(chatBuf), compare->text) ) { // Com_Printf ("CL_CheckForChatIgnore: filtered nick %s in chat message\n", compare->text); compare->numHits++; return true; } } } } if (cl_chatTextIgnores.next != NULL) { for (compare = cl_chatTextIgnores.next; compare != NULL; compare = compare->next) { if ( (compare->text != NULL) && (strlen(compare->text) > 0) ) { if ( Q_StrScanToken (chatBuf, compare->text, false) ) { // Com_Printf ("CL_CheckForChatIgnore: filtered text %s in chat message\n", compare->text); compare->numHits++; return true; } } } } return false; } //====================================================================== // end R1Q2/Q2Pro Chat Ignore //====================================================================== /* =================== 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()); } cls.forcePacket = true; } void CL_Setenv_f( void ) { int argc = Cmd_Argc(); if ( argc > 2 ) { char buffer[1000]; int i; // strncpy(buffer, Cmd_Argv(1)); // strncat(buffer, "="); Q_strncpyz (buffer, sizeof(buffer), Cmd_Argv(1)); Q_strncatz (buffer, sizeof(buffer), "="); for ( i = 2; i < argc; i++ ) { // strncat(buffer, Cmd_Argv( i )); // strncat(buffer, " "); Q_strncatz (buffer, sizeof(buffer), Cmd_Argv( i )); Q_strncatz (buffer, sizeof(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()); cls.forcePacket = true; } } /* ================== 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); Cvar_SetValue ("paused", !cl_paused->integer); } /* ================== 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, sendProtocolVersion; 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. sendProtocolVersion = ((cl_servertrick->integer != 0) && (strcmp(cls.servername, "localhost") != 0)) ? OLD_PROTOCOL_VERSION : PROTOCOL_VERSION; Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", sendProtocolVersion, port, cls.challenge, Cvar_Userinfo() ); } /* ================== CL_ForcePacket ================== */ void CL_ForcePacket (void) { cls.forcePacket = true; } /* ================= 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, *p; netadr_t serverAdr; 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); SV_Shutdown ("Server quit.\n", false); } else { CL_Disconnect (); } server = Cmd_Argv (1); // start quake2:// support if (!strncmp (server, "quake2://", 9)) server += 9; p = strchr (server, '/'); // remove trailing slash if (p) p[0] = '\0'; // end quake2:// support NET_Config (true); // allow remote // validate server address if (!NET_StringToAdr (server, &serverAdr)) { Com_Printf ("Bad server address: %s\n", server); return; } 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 // strncat (message, "rcon "); // strncat (message, rcon_client_password->string); // strncat (message, " "); Q_strncatz (message, sizeof(message), "rcon "); Q_strncatz (message, sizeof(message), rcon_client_password->string); Q_strncatz (message, sizeof(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, (int)strlen(message)+1, message, to); } /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { S_StopAllSounds (); CL_ClearEffects (); CL_ClearTEnts (); V_ClearFogInfo (); R_ClearState (); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); memset (&cl_entities, 0, sizeof(cl_entities)); cl.maxclients = MAX_CLIENTS; // from R1Q2 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 *ui_currentweaponmodel; void CL_Disconnect (void) { byte final[32]; if (cls.state == ca_disconnected) return; // if (cl_timedemo && cl_timedemo->value) if (cl_timedemo && cl_timedemo->integer) { 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; // strncpy ((char *)final+1, "disconnect"); Q_strncpyz ((char *)final+1, sizeof(final)-1, "disconnect"); Netchan_Transmit (&cls.netchan, (int)strlen(final), final); Netchan_Transmit (&cls.netchan, (int)strlen(final), final); Netchan_Transmit (&cls.netchan, (int)strlen(final), final); CL_ClearState (); // stop download if (cls.download) { fclose(cls.download); cls.download = NULL; } #ifdef USE_CURL // HTTP downloading from R1Q2 CL_CancelHTTPDownloads (true); cls.downloadReferer[0] = 0; cls.downloadname[0] = 0; cls.downloadposition = 0; #endif // USE_CURL cls.state = ca_disconnected; // reset current weapon model ui_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 = (int)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); // Catch wrong version reply from server if (s && strstr(s, "wrong version")) { // The "wrong version" reply has been removed from most Q2 servers // due to exploitability. So we can just ignore it if we get one, // as we're sending both stock Q2 and KMQ2 protocol versions at once. return; } 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)); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noudp = Cvar_Get ("noudp", "0", CVAR_NOSET); if (!noudp->integer) { global_udp_server_time = Sys_Milliseconds() ; adr.type = NA_BROADCAST; adr.port = BigShort(PORT_SERVER); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noipx = Cvar_Get ("noipx", "0", CVAR_NOSET); if (!noipx->integer) { global_ipx_server_time = Sys_Milliseconds() ; adr.type = NA_BROADCAST_IPX; adr.port = BigShort(PORT_SERVER); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); 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->integer) { adr.type = NA_BROADCAST; adr.port = BigShort(PORT_SERVER); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noipx = Cvar_Get ("noipx", "0", CVAR_NOSET); if (!noipx->integer) { adr.type = NA_BROADCAST_IPX; adr.port = BigShort(PORT_SERVER); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); 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); // Send both protocol versions, we'll get a reply from one or the other Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", OLD_PROTOCOL_VERSION)); 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 ; i= 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_auto->integer) { 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 extern int precache_pak; // Knightmare added /* ================= CL_ResetPrecacheCheck ================= */ void CL_ResetPrecacheCheck (void) { // precache_start_time = Sys_Milliseconds(); precache_check = CS_MODELS; // precache_spawncount = atoi(Cmd_Argv(1)); precache_model = 0; precache_model_skin = 0; precache_pak = 0; // Knightmare added } /* ================= 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; precache_pak = 0; // Knightmare added #ifdef USE_CURL // HTTP downloading from R1Q2 CL_HTTP_ResetMapAbort (); // Knightmare- reset the map abort flag #endif // USE_CURL CL_RequestNextDownload(); } #ifdef LOC_SUPPORT // Xile/NiceAss LOC /* ================= CL_AddLoc_f ================= */ void CL_AddLoc_f (void) { if (Cmd_Argc() != 2) { Com_Printf("Usage: loc_add