diff options
Diffstat (limited to 'QW/client/cl_main.c')
| -rw-r--r-- | QW/client/cl_main.c | 1535 |
1 files changed, 1535 insertions, 0 deletions
diff --git a/QW/client/cl_main.c b/QW/client/cl_main.c new file mode 100644 index 0000000..dfe7653 --- /dev/null +++ b/QW/client/cl_main.c @@ -0,0 +1,1535 @@ +/* +Copyright (C) 1996-1997 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. + +*/ +// cl_main.c -- client main loop + +#include "quakedef.h" +#include "winquake.h" +#ifdef _WIN32 +#include "winsock.h" +#else +#include <netinet/in.h> +#endif + + +// we need to declare some mouse variables here, because the menu system +// references them even when on a unix system. + +qboolean noclip_anglehack; // remnant from old quake + + +cvar_t rcon_password = {"rcon_password", "", false}; + +cvar_t rcon_address = {"rcon_address", ""}; + +cvar_t cl_timeout = {"cl_timeout", "60"}; + +cvar_t cl_shownet = {"cl_shownet","0"}; // can be 0, 1, or 2 + +cvar_t cl_sbar = {"cl_sbar", "0", true}; +cvar_t cl_hudswap = {"cl_hudswap", "0", true}; +cvar_t cl_maxfps = {"cl_maxfps", "0", true}; + +cvar_t lookspring = {"lookspring","0", true}; +cvar_t lookstrafe = {"lookstrafe","0", true}; +cvar_t sensitivity = {"sensitivity","3", true}; + +cvar_t m_pitch = {"m_pitch","0.022", true}; +cvar_t m_yaw = {"m_yaw","0.022"}; +cvar_t m_forward = {"m_forward","1"}; +cvar_t m_side = {"m_side","0.8"}; + +cvar_t entlatency = {"entlatency", "20"}; +cvar_t cl_predict_players = {"cl_predict_players", "1"}; +cvar_t cl_predict_players2 = {"cl_predict_players2", "1"}; +cvar_t cl_solid_players = {"cl_solid_players", "1"}; + +cvar_t localid = {"localid", ""}; + +static qboolean allowremotecmd = true; + +// +// info mirrors +// +cvar_t password = {"password", "", false, true}; +cvar_t spectator = {"spectator", "", false, true}; +cvar_t name = {"name","unnamed", true, true}; +cvar_t team = {"team","", true, true}; +cvar_t skin = {"skin","", true, true}; +cvar_t topcolor = {"topcolor","0", true, true}; +cvar_t bottomcolor = {"bottomcolor","0", true, true}; +cvar_t rate = {"rate","2500", true, true}; +cvar_t noaim = {"noaim","0", true, true}; +cvar_t msg = {"msg","1", true, true}; + +extern cvar_t cl_hightrack; + + +client_static_t cls; +client_state_t cl; + +entity_state_t cl_baselines[MAX_EDICTS]; +efrag_t cl_efrags[MAX_EFRAGS]; +entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +dlight_t cl_dlights[MAX_DLIGHTS]; + +// refresh list +// this is double buffered so the last frame +// can be scanned for oldorigins of trailing objects +int cl_numvisedicts, cl_oldnumvisedicts; +entity_t *cl_visedicts, *cl_oldvisedicts; +entity_t cl_visedicts_list[2][MAX_VISEDICTS]; + +double connect_time = -1; // for connection retransmits + +quakeparms_t host_parms; + +qboolean host_initialized; // true if into command execution +qboolean nomaster; + +double host_frametime; +double realtime; // without any filtering or bounding +double oldrealtime; // last frame run +int host_framecount; + +int host_hunklevel; + +byte *host_basepal; +byte *host_colormap; + +netadr_t master_adr; // address of the master server + +cvar_t host_speeds = {"host_speeds","0"}; // set for running times +cvar_t show_fps = {"show_fps","0"}; // set for running times +cvar_t developer = {"developer","0"}; + +int fps_count; + +jmp_buf host_abort; + +void Master_Connect_f (void); + +float server_version = 0; // version of server we connected to + +char emodel_name[] = + { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; +char pmodel_name[] = + { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 }; +char prespawn_name[] = + { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; +char modellist_name[] = + { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; +char soundlist_name[] = + { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff, + ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 }; + +/* +================== +CL_Quit_f +================== +*/ +void CL_Quit_f (void) +{ + if (1 /* key_dest != key_console */ /* && cls.state != ca_dedicated */) + { + M_Menu_Quit_f (); + return; + } + CL_Disconnect (); + Sys_Quit (); +} + +/* +======================= +CL_Version_f +====================== +*/ +void CL_Version_f (void) +{ + Con_Printf ("Version %4.2f\n", VERSION); + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); +} + + +/* +======================= +CL_SendConnectPacket + +called by CL_Connect_f and CL_CheckResend +====================== +*/ +void CL_SendConnectPacket (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; +// JACK: Fixed bug where DNS lookups would cause two connects real fast +// Now, adds lookup time to the connect time. +// Should I add it to realtime instead?!?! + + if (cls.state != ca_disconnected) + return; + + t1 = Sys_DoubleTime (); + + if (!NET_StringToAdr (cls.servername, &adr)) + { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + + if (!NET_IsClientLegal(&adr)) + { + Con_Printf ("Illegal server address\n"); + connect_time = -1; + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + t2 = Sys_DoubleTime (); + + connect_time = realtime+t2-t1; // for retransmit requests + + cls.qport = Cvar_VariableValue("qport"); + + Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING); + +// Con_Printf ("Connecting to %s...\n", cls.servername); + sprintf (data, "%c%c%c%cconnect %i %i %i \"%s\"\n", + 255, 255, 255, 255, PROTOCOL_VERSION, cls.qport, cls.challenge, cls.userinfo); + NET_SendPacket (strlen(data), data, adr); +} + +/* +================= +CL_CheckForResend + +Resend a connect message if the last one has timed out + +================= +*/ +void CL_CheckForResend (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; + + if (connect_time == -1) + return; + if (cls.state != ca_disconnected) + return; + if (connect_time && realtime - connect_time < 5.0) + return; + + t1 = Sys_DoubleTime (); + if (!NET_StringToAdr (cls.servername, &adr)) + { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + if (!NET_IsClientLegal(&adr)) + { + Con_Printf ("Illegal server address\n"); + connect_time = -1; + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + t2 = Sys_DoubleTime (); + + connect_time = realtime+t2-t1; // for retransmit requests + + Con_Printf ("Connecting to %s...\n", cls.servername); + sprintf (data, "%c%c%c%cgetchallenge\n", 255, 255, 255, 255); + NET_SendPacket (strlen(data), data, adr); +} + +void CL_BeginServerConnect(void) +{ + connect_time = 0; + CL_CheckForResend(); +} + +/* +================ +CL_Connect_f + +================ +*/ +void CL_Connect_f (void) +{ + char *server; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: connect <server>\n"); + return; + } + + server = Cmd_Argv (1); + + CL_Disconnect (); + + strncpy (cls.servername, server, sizeof(cls.servername)-1); + CL_BeginServerConnect(); +} + + +/* +===================== +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_password.string) + { + Con_Printf ("You must set 'rcon_password' before\n" + "issuing an rcon command.\n"); + return; + } + + message[0] = 255; + message[1] = 255; + message[2] = 255; + message[3] = 255; + message[4] = 0; + + strcat (message, "rcon "); + + strcat (message, rcon_password.string); + strcat (message, " "); + + for (i=1 ; i<Cmd_Argc() ; i++) + { + strcat (message, Cmd_Argv(i)); + strcat (message, " "); + } + + if (cls.state >= ca_connected) + to = cls.netchan.remote_address; + else + { + if (!strlen(rcon_address.string)) + { + Con_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); + } + + NET_SendPacket (strlen(message)+1, message + , to); +} + + +/* +===================== +CL_ClearState + +===================== +*/ +void CL_ClearState (void) +{ + int i; + + S_StopAllSounds (true); + + Con_DPrintf ("Clearing memory\n"); + D_FlushCaches (); + Mod_ClearAll (); + if (host_hunklevel) // FIXME: check this... + Hunk_FreeToLowMark (host_hunklevel); + + CL_ClearTEnts (); + +// wipe the entire cl structure + memset (&cl, 0, sizeof(cl)); + + SZ_Clear (&cls.netchan.message); + +// clear other arrays + memset (cl_efrags, 0, sizeof(cl_efrags)); + memset (cl_dlights, 0, sizeof(cl_dlights)); + memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); + +// +// allocate the efrags and chain together into a free list +// + cl.free_efrags = cl_efrags; + for (i=0 ; i<MAX_EFRAGS-1 ; i++) + cl.free_efrags[i].entnext = &cl.free_efrags[i+1]; + cl.free_efrags[i].entnext = NULL; +} + +/* +===================== +CL_Disconnect + +Sends a disconnect message to the server +This is also called on Host_Error, so it shouldn't cause any errors +===================== +*/ +void CL_Disconnect (void) +{ + byte final[10]; + + connect_time = -1; + +#ifdef _WIN32 + SetWindowText (mainwindow, "QuakeWorld: disconnected"); +#endif + +// stop sounds (especially looping!) + S_StopAllSounds (true); + +// if running a local server, shut it down + if (cls.demoplayback) + CL_StopPlayback (); + else if (cls.state != ca_disconnected) + { + if (cls.demorecording) + CL_Stop_f (); + + final[0] = clc_stringcmd; + strcpy (final+1, "drop"); + Netchan_Transmit (&cls.netchan, 6, final); + Netchan_Transmit (&cls.netchan, 6, final); + Netchan_Transmit (&cls.netchan, 6, final); + + cls.state = ca_disconnected; + + cls.demoplayback = cls.demorecording = cls.timedemo = false; + } + Cam_Reset(); + + if (cls.download) { + fclose(cls.download); + cls.download = NULL; + } + + CL_StopUpload(); + +} + +void CL_Disconnect_f (void) +{ + CL_Disconnect (); +} + +/* +==================== +CL_User_f + +user <name or userid> + +Dump userdata / masterdata for a user +==================== +*/ +void CL_User_f (void) +{ + int uid; + int i; + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: user <username / userid>\n"); + return; + } + + uid = atoi(Cmd_Argv(1)); + + for (i=0 ; i<MAX_CLIENTS ; i++) + { + if (!cl.players[i].name[0]) + continue; + if (cl.players[i].userid == uid + || !strcmp(cl.players[i].name, Cmd_Argv(1)) ) + { + Info_Print (cl.players[i].userinfo); + return; + } + } + Con_Printf ("User not in server.\n"); +} + +/* +==================== +CL_Users_f + +Dump userids for all current players +==================== +*/ +void CL_Users_f (void) +{ + int i; + int c; + + c = 0; + Con_Printf ("userid frags name\n"); + Con_Printf ("------ ----- ----\n"); + for (i=0 ; i<MAX_CLIENTS ; i++) + { + if (cl.players[i].name[0]) + { + Con_Printf ("%6i %4i %s\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name); + c++; + } + } + + Con_Printf ("%i total users\n", c); +} + +void CL_Color_f (void) +{ + // just for quake compatability... + int top, bottom; + char num[16]; + + if (Cmd_Argc() == 1) + { + Con_Printf ("\"color\" is \"%s %s\"\n", + Info_ValueForKey (cls.userinfo, "topcolor"), + Info_ValueForKey (cls.userinfo, "bottomcolor") ); + Con_Printf ("color <0-13> [0-13]\n"); + return; + } + + if (Cmd_Argc() == 2) + top = bottom = atoi(Cmd_Argv(1)); + else + { + top = atoi(Cmd_Argv(1)); + bottom = atoi(Cmd_Argv(2)); + } + + top &= 15; + if (top > 13) + top = 13; + bottom &= 15; + if (bottom > 13) + bottom = 13; + + sprintf (num, "%i", top); + Cvar_Set ("topcolor", num); + sprintf (num, "%i", bottom); + Cvar_Set ("bottomcolor", num); +} + +/* +================== +CL_FullServerinfo_f + +Sent by server when serverinfo changes +================== +*/ +void CL_FullServerinfo_f (void) +{ + char *p; + float v; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: fullserverinfo <complete info string>\n"); + return; + } + + strcpy (cl.serverinfo, Cmd_Argv(1)); + + if ((p = Info_ValueForKey(cl.serverinfo, "*vesion")) && *p) { + v = Q_atof(p); + if (v) { + if (!server_version) + Con_Printf("Version %1.2f Server\n", v); + server_version = v; + } + } +} + +/* +================== +CL_FullInfo_f + +Allow clients to change userinfo +================== +Casey was here :) +*/ +void CL_FullInfo_f (void) +{ + char key[512]; + char value[512]; + char *o; + char *s; + + if (Cmd_Argc() != 2) + { + Con_Printf ("fullinfo <complete info string>\n"); + return; + } + + s = Cmd_Argv(1); + if (*s == '\\') + s++; + while (*s) + { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (!*s) + { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + + if (!stricmp(key, pmodel_name) || !stricmp(key, emodel_name)) + continue; + + Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING); + } +} + +/* +================== +CL_SetInfo_f + +Allow clients to change userinfo +================== +*/ +void CL_SetInfo_f (void) +{ + if (Cmd_Argc() == 1) + { + Info_Print (cls.userinfo); + return; + } + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: setinfo [ <key> <value> ]\n"); + return; + } + if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name)) + return; + + Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); + if (cls.state >= ca_connected) + Cmd_ForwardToServer (); +} + +/* +==================== +CL_Packet_f + +packet <destination> <contents> + +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) + { + Con_Printf ("packet <destination> <contents>\n"); + return; + } + + if (!NET_StringToAdr (Cmd_Argv(1), &adr)) + { + Con_Printf ("Bad address\n"); + return; + } + + in = Cmd_Argv(2); + out = send+4; + send[0] = send[1] = send[2] = send[3] = 0xff; + + l = strlen (in); + for (i=0 ; i<l ; i++) + { + if (in[i] == '\\' && in[i+1] == 'n') + { + *out++ = '\n'; + i++; + } + else + *out++ = in[i]; + } + *out = 0; + + NET_SendPacket (out-send, send, adr); +} + + +/* +===================== +CL_NextDemo + +Called to play the next demo in the demo loop +===================== +*/ +void CL_NextDemo (void) +{ + char str[1024]; + + if (cls.demonum == -1) + return; // don't play demos + + if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) + { + cls.demonum = 0; + if (!cls.demos[cls.demonum][0]) + { +// Con_Printf ("No demos listed with startdemos\n"); + cls.demonum = -1; + return; + } + } + + sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]); + Cbuf_InsertText (str); + cls.demonum++; +} + + +/* +================= +CL_Changing_f + +Just sent as a hint to the client that they should +drop to full console +================= +*/ +void CL_Changing_f (void) +{ + if (cls.download) // don't change when downloading + return; + + S_StopAllSounds (true); + cl.intermission = 0; + cls.state = ca_connected; // not active anymore, but not disconnected + Con_Printf ("\nChanging map...\n"); +} + + +/* +================= +CL_Reconnect_f + +The server is changing levels +================= +*/ +void CL_Reconnect_f (void) +{ + if (cls.download) // don't change when downloading + return; + + S_StopAllSounds (true); + + if (cls.state == ca_connected) { + Con_Printf ("reconnecting...\n"); + MSG_WriteChar (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "new"); + return; + } + + if (!*cls.servername) { + Con_Printf("No server to reconnect to...\n"); + return; + } + + CL_Disconnect(); + CL_BeginServerConnect(); +} + +/* +================= +CL_ConnectionlessPacket + +Responses to broadcasts, etc +================= +*/ +void CL_ConnectionlessPacket (void) +{ + char *s; + int c; + + MSG_BeginReading (); + MSG_ReadLong (); // skip the -1 + + c = MSG_ReadByte (); + if (!cls.demoplayback) + Con_Printf ("%s: ", NET_AdrToString (net_from)); +// Con_DPrintf ("%s", net_message.data + 5); + if (c == S2C_CONNECTION) + { + Con_Printf ("connection\n"); + if (cls.state >= ca_connected) + { + if (!cls.demoplayback) + Con_Printf ("Dup connect received. Ignored.\n"); + return; + } + Netchan_Setup (&cls.netchan, net_from, cls.qport); + MSG_WriteChar (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "new"); + cls.state = ca_connected; + Con_Printf ("Connected.\n"); + allowremotecmd = false; // localid required now for remote cmds + return; + } + // remote command from gui front end + if (c == A2C_CLIENT_COMMAND) + { + char cmdtext[2048]; + + Con_Printf ("client command\n"); + + if ((*(unsigned *)net_from.ip != *(unsigned *)net_local_adr.ip + && *(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK)) ) + { + Con_Printf ("Command packet from remote host. Ignored.\n"); + return; + } +#ifdef _WIN32 + ShowWindow (mainwindow, SW_RESTORE); + SetForegroundWindow (mainwindow); +#endif + s = MSG_ReadString (); + + strncpy(cmdtext, s, sizeof(cmdtext) - 1); + cmdtext[sizeof(cmdtext) - 1] = 0; + + s = MSG_ReadString (); + + while (*s && isspace(*s)) + s++; + while (*s && isspace(s[strlen(s) - 1])) + s[strlen(s) - 1] = 0; + + if (!allowremotecmd && (!*localid.string || strcmp(localid.string, s))) { + if (!*localid.string) { + Con_Printf("===========================\n"); + Con_Printf("Command packet received from local host, but no " + "localid has been set. You may need to upgrade your server " + "browser.\n"); + Con_Printf("===========================\n"); + return; + } + Con_Printf("===========================\n"); + Con_Printf("Invalid localid on command packet received from local host. " + "\n|%s| != |%s|\n" + "You may need to reload your server browser and QuakeWorld.\n", + s, localid.string); + Con_Printf("===========================\n"); + Cvar_Set("localid", ""); + return; + } + + Cbuf_AddText (cmdtext); + allowremotecmd = false; + return; + } + // print command from somewhere + if (c == A2C_PRINT) + { + Con_Printf ("print\n"); + + s = MSG_ReadString (); + Con_Print (s); + return; + } + + // ping from somewhere + if (c == A2A_PING) + { + char data[6]; + + Con_Printf ("ping\n"); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + data[4] = A2A_ACK; + data[5] = 0; + + NET_SendPacket (6, &data, net_from); + return; + } + + if (c == S2C_CHALLENGE) { + Con_Printf ("challenge\n"); + + s = MSG_ReadString (); + cls.challenge = atoi(s); + CL_SendConnectPacket (); + return; + } + +#if 0 + if (c == svc_disconnect) { + Con_Printf ("disconnect\n"); + + Host_EndGame ("Server disconnected"); + return; + } +#endif + + Con_Printf ("unknown: %c\n", c); +} + + +/* +================= +CL_ReadPackets +================= +*/ +void CL_ReadPackets (void) +{ +// while (NET_GetPacket ()) + while (CL_GetMessage()) + { + // + // remote command packet + // + if (*(int *)net_message.data == -1) + { + CL_ConnectionlessPacket (); + continue; + } + + if (net_message.cursize < 8) + { + Con_Printf ("%s: Runt packet\n",NET_AdrToString(net_from)); + continue; + } + + // + // packet from server + // + if (!cls.demoplayback && + !NET_CompareAdr (net_from, cls.netchan.remote_address)) + { + Con_DPrintf ("%s:sequenced packet without connection\n" + ,NET_AdrToString(net_from)); + continue; + } + if (!Netchan_Process(&cls.netchan)) + continue; // wasn't accepted for some reason + CL_ParseServerMessage (); + +// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind()) +// return; + } + + // + // check timeout + // + if (cls.state >= ca_connected + && realtime - cls.netchan.last_received > cl_timeout.value) + { + Con_Printf ("\nServer connection timed out.\n"); + CL_Disconnect (); + return; + } + +} + +//============================================================================= + +/* +===================== +CL_Download_f +===================== +*/ +void CL_Download_f (void) +{ + char *p, *q; + + if (cls.state == ca_disconnected) + { + Con_Printf ("Must be connected.\n"); + return; + } + + if (Cmd_Argc() != 2) + { + Con_Printf ("Usage: download <datafile>\n"); + return; + } + + sprintf (cls.downloadname, "%s/%s", com_gamedir, Cmd_Argv(1)); + + p = cls.downloadname; + for (;;) { + if ((q = strchr(p, '/')) != NULL) { + *q = 0; + Sys_mkdir(cls.downloadname); + *q = '/'; + p = q + 1; + } else + break; + } + + strcpy(cls.downloadtempname, cls.downloadname); + cls.download = fopen (cls.downloadname, "wb"); + cls.downloadtype = dl_single; + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1))); +} + +#ifdef _WINDOWS +#include <windows.h> +/* +================= +CL_Minimize_f +================= +*/ +void CL_Windows_f (void) { +// if (modestate == MS_WINDOWED) +// ShowWindow(mainwindow, SW_MINIMIZE); +// else + SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29)); +} +#endif + +/* +================= +CL_Init +================= +*/ +void CL_Init (void) +{ + extern cvar_t baseskin; + extern cvar_t noskins; + char st[80]; + + cls.state = ca_disconnected; + + Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING); + sprintf (st, "%4.2f-%04d", VERSION, build_number()); + Info_SetValueForStarKey (cls.userinfo, "*ver", st, MAX_INFO_STRING); + + CL_InitInput (); + CL_InitTEnts (); + CL_InitPrediction (); + CL_InitCam (); + Pmove_Init (); + +// +// register our commands +// + Cvar_RegisterVariable (&show_fps); + Cvar_RegisterVariable (&host_speeds); + Cvar_RegisterVariable (&developer); + + Cvar_RegisterVariable (&cl_warncmd); + Cvar_RegisterVariable (&cl_upspeed); + Cvar_RegisterVariable (&cl_forwardspeed); + Cvar_RegisterVariable (&cl_backspeed); + Cvar_RegisterVariable (&cl_sidespeed); + Cvar_RegisterVariable (&cl_movespeedkey); + Cvar_RegisterVariable (&cl_yawspeed); + Cvar_RegisterVariable (&cl_pitchspeed); + Cvar_RegisterVariable (&cl_anglespeedkey); + Cvar_RegisterVariable (&cl_shownet); + Cvar_RegisterVariable (&cl_sbar); + Cvar_RegisterVariable (&cl_hudswap); + Cvar_RegisterVariable (&cl_maxfps); + Cvar_RegisterVariable (&cl_timeout); + Cvar_RegisterVariable (&lookspring); + Cvar_RegisterVariable (&lookstrafe); + Cvar_RegisterVariable (&sensitivity); + + Cvar_RegisterVariable (&m_pitch); + Cvar_RegisterVariable (&m_yaw); + Cvar_RegisterVariable (&m_forward); + Cvar_RegisterVariable (&m_side); + + Cvar_RegisterVariable (&rcon_password); + Cvar_RegisterVariable (&rcon_address); + + Cvar_RegisterVariable (&entlatency); + Cvar_RegisterVariable (&cl_predict_players2); + Cvar_RegisterVariable (&cl_predict_players); + Cvar_RegisterVariable (&cl_solid_players); + + Cvar_RegisterVariable (&localid); + + Cvar_RegisterVariable (&baseskin); + Cvar_RegisterVariable (&noskins); + + // + // info mirrors + // + Cvar_RegisterVariable (&name); + Cvar_RegisterVariable (&password); + Cvar_RegisterVariable (&spectator); + Cvar_RegisterVariable (&skin); + Cvar_RegisterVariable (&team); + Cvar_RegisterVariable (&topcolor); + Cvar_RegisterVariable (&bottomcolor); + Cvar_RegisterVariable (&rate); + Cvar_RegisterVariable (&msg); + Cvar_RegisterVariable (&noaim); + + + Cmd_AddCommand ("version", CL_Version_f); + + Cmd_AddCommand ("changing", CL_Changing_f); + Cmd_AddCommand ("disconnect", CL_Disconnect_f); + Cmd_AddCommand ("record", CL_Record_f); + Cmd_AddCommand ("rerecord", CL_ReRecord_f); + Cmd_AddCommand ("stop", CL_Stop_f); + Cmd_AddCommand ("playdemo", CL_PlayDemo_f); + Cmd_AddCommand ("timedemo", CL_TimeDemo_f); + + Cmd_AddCommand ("skins", Skin_Skins_f); + Cmd_AddCommand ("allskins", Skin_AllSkins_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); + Cmd_AddCommand ("user", CL_User_f); + Cmd_AddCommand ("users", CL_Users_f); + + Cmd_AddCommand ("setinfo", CL_SetInfo_f); + Cmd_AddCommand ("fullinfo", CL_FullInfo_f); + Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f); + + Cmd_AddCommand ("color", CL_Color_f); + Cmd_AddCommand ("download", CL_Download_f); + + Cmd_AddCommand ("nextul", CL_NextUpload); + Cmd_AddCommand ("stopul", CL_StopUpload); + +// +// forward to server commands +// + Cmd_AddCommand ("kill", NULL); + Cmd_AddCommand ("pause", NULL); + Cmd_AddCommand ("say", NULL); + Cmd_AddCommand ("say_team", NULL); + Cmd_AddCommand ("serverinfo", NULL); + +// +// Windows commands +// +#ifdef _WINDOWS + Cmd_AddCommand ("windows", CL_Windows_f); +#endif +} + + +/* +================ +Host_EndGame + +Call this to drop to a console without exiting the qwcl +================ +*/ +void Host_EndGame (char *message, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,message); + vsprintf (string,message,argptr); + va_end (argptr); + Con_Printf ("\n===========================\n"); + Con_Printf ("Host_EndGame: %s\n",string); + Con_Printf ("===========================\n\n"); + + CL_Disconnect (); + + longjmp (host_abort, 1); +} + +/* +================ +Host_Error + +This shuts down the client and exits qwcl +================ +*/ +void Host_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("Host_Error: recursively entered"); + inerror = true; + + va_start (argptr,error); + vsprintf (string,error,argptr); + va_end (argptr); + Con_Printf ("Host_Error: %s\n",string); + + CL_Disconnect (); + cls.demonum = -1; + + inerror = false; + +// FIXME + Sys_Error ("Host_Error: %s\n",string); +} + + +/* +=============== +Host_WriteConfiguration + +Writes key bindings and archived cvars to config.cfg +=============== +*/ +void Host_WriteConfiguration (void) +{ + FILE *f; + + if (host_initialized) + { + f = fopen (va("%s/config.cfg",com_gamedir), "w"); + if (!f) + { + Con_Printf ("Couldn't write config.cfg.\n"); + return; + } + + Key_WriteBindings (f); + Cvar_WriteVariables (f); + + fclose (f); + } +} + + +//============================================================================ + +#if 0 +/* +================== +Host_SimulationTime + +This determines if enough time has passed to run a simulation frame +================== +*/ +qboolean Host_SimulationTime(float time) +{ + float fps; + + if (oldrealtime > realtime) + oldrealtime = 0; + + if (cl_maxfps.value) + fps = max(30.0, min(cl_maxfps.value, 72.0)); + else + fps = max(30.0, min(rate.value/80.0, 72.0)); + + if (!cls.timedemo && (realtime + time) - oldrealtime < 1.0/fps) + return false; // framerate is too high + return true; +} +#endif + + +/* +================== +Host_Frame + +Runs all active servers +================== +*/ +int nopacketcount; +void Host_Frame (float time) +{ + static double time1 = 0; + static double time2 = 0; + static double time3 = 0; + int pass1, pass2, pass3; + float fps; + if (setjmp (host_abort) ) + return; // something bad happened, or the server disconnected + + // decide the simulation time + realtime += time; + if (oldrealtime > realtime) + oldrealtime = 0; + + if (cl_maxfps.value) + fps = max(30.0, min(cl_maxfps.value, 72.0)); + else + fps = max(30.0, min(rate.value/80.0, 72.0)); + + if (!cls.timedemo && realtime - oldrealtime < 1.0/fps) + return; // framerate is too high + + host_frametime = realtime - oldrealtime; + oldrealtime = realtime; + if (host_frametime > 0.2) + host_frametime = 0.2; + + // get new key events + Sys_SendKeyEvents (); + + // allow mice or other external controllers to add commands + IN_Commands (); + + // process console commands + Cbuf_Execute (); + + // fetch results from server + CL_ReadPackets (); + + // send intentions now + // resend a connection request if necessary + if (cls.state == ca_disconnected) { + CL_CheckForResend (); + } else + CL_SendCmd (); + + // Set up prediction for other players + CL_SetUpPlayerPrediction(false); + + // do client side motion prediction + CL_PredictMove (); + + // Set up prediction for other players + CL_SetUpPlayerPrediction(true); + + // build a refresh entity list + CL_EmitEntities (); + + // update video + if (host_speeds.value) + time1 = Sys_DoubleTime (); + + SCR_UpdateScreen (); + + if (host_speeds.value) + time2 = Sys_DoubleTime (); + + // update audio + if (cls.state == ca_active) + { + S_Update (r_origin, vpn, vright, vup); + CL_DecayLights (); + } + else + S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); + + CDAudio_Update(); + + if (host_speeds.value) + { + pass1 = (time1 - time3)*1000; + time3 = Sys_DoubleTime (); + pass2 = (time2 - time1)*1000; + pass3 = (time3 - time2)*1000; + Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", + pass1+pass2+pass3, pass1, pass2, pass3); + } + + host_framecount++; + fps_count++; +} + +static void simple_crypt(char *buf, int len) +{ + while (len--) + *buf++ ^= 0xff; +} + +void Host_FixupModelNames(void) +{ + simple_crypt(emodel_name, sizeof(emodel_name) - 1); + simple_crypt(pmodel_name, sizeof(pmodel_name) - 1); + simple_crypt(prespawn_name, sizeof(prespawn_name) - 1); + simple_crypt(modellist_name, sizeof(modellist_name) - 1); + simple_crypt(soundlist_name, sizeof(soundlist_name) - 1); +} + +//============================================================================ + +/* +==================== +Host_Init +==================== +*/ +void Host_Init (quakeparms_t *parms) +{ + COM_InitArgv (parms->argc, parms->argv); + COM_AddParm ("-game"); + COM_AddParm ("qw"); + + Sys_mkdir("qw"); + + if (COM_CheckParm ("-minmemory")) + parms->memsize = MINIMUM_MEMORY; + + host_parms = *parms; + + if (parms->memsize < MINIMUM_MEMORY) + Sys_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); + + Memory_Init (parms->membase, parms->memsize); + Cbuf_Init (); + Cmd_Init (); + V_Init (); + + COM_Init (); + + Host_FixupModelNames(); + + NET_Init (PORT_CLIENT); + Netchan_Init (); + + W_LoadWadFile ("gfx.wad"); + Key_Init (); + Con_Init (); + M_Init (); + Mod_Init (); + +// Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megs RAM used.\n",parms->memsize/ (1024*1024.0)); + + R_InitTextures (); + + host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); + if (!host_basepal) + Sys_Error ("Couldn't load gfx/palette.lmp"); + host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); + if (!host_colormap) + Sys_Error ("Couldn't load gfx/colormap.lmp"); +#ifdef __linux__ + IN_Init (); + CDAudio_Init (); + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); + +// S_Init (); // S_Init is now done as part of VID. Sigh. + + cls.state = ca_disconnected; + Sbar_Init (); + CL_Init (); +#else + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); +// S_Init (); // S_Init is now done as part of VID. Sigh. +#ifdef GLQUAKE + S_Init(); +#endif + + cls.state = ca_disconnected; + CDAudio_Init (); + Sbar_Init (); + CL_Init (); + IN_Init (); +#endif + + Cbuf_InsertText ("exec quake.rc\n"); + Cbuf_AddText ("echo Type connect <internet address> or use GameSpy to connect to a game.\n"); + Cbuf_AddText ("cl_warncmd 1\n"); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + host_initialized = true; + + Con_Printf ("\nClient Version %4.2f (Build %04d)\n\n", VERSION, build_number()); + + Con_Printf ("€������ QuakeWorld Initialized ������‚\n"); +} + + +/* +=============== +Host_Shutdown + +FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better +to run quit through here before the final handoff to the sys code. +=============== +*/ +void Host_Shutdown(void) +{ + static qboolean isdown = false; + + if (isdown) + { + printf ("recursive shutdown\n"); + return; + } + isdown = true; + + Host_WriteConfiguration (); + + CDAudio_Shutdown (); + NET_Shutdown (); + S_Shutdown(); + IN_Shutdown (); + if (host_basepal) + VID_Shutdown(); +} + |
