mirror of
https://git.code.sf.net/p/quake/quakeforge-old
synced 2024-11-29 07:02:08 +00:00
47bed98da1
How view height changes in QW are passed from server to client HAS CHANGED and you will need to update both client and server for things not to act weird. You may also need to distclean before rebuilding..
1794 lines
36 KiB
C
1794 lines
36 KiB
C
/*
|
|
cl_main.c
|
|
|
|
client main loop
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
Copyright (C) 1999,2000 Nelson Rush.
|
|
Copyright (C) 1999,2000 contributors of the QuakeForge project
|
|
Please see the file "AUTHORS" for a list of contributors
|
|
|
|
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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
/* Will have problems w/ isspace() when compiling w/ gcc on SGI without this */
|
|
#ifdef sgi
|
|
#define _LINT
|
|
#define UNDEFINE_LINT
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifdef UNDEFINE_LINT
|
|
#undef _LINT
|
|
#endif
|
|
|
|
#include <quakedef.h>
|
|
#include <winquake.h>
|
|
#ifdef QUAKEWORLD
|
|
#include <pmove_simple.h>
|
|
#endif
|
|
#include <qtypes.h>
|
|
#include <qstructs.h>
|
|
#include <client.h>
|
|
#include <menu.h>
|
|
#include <sound.h>
|
|
#include <common.h>
|
|
#include <net.h>
|
|
#include <console.h>
|
|
#include <screen.h>
|
|
#include <mathlib.h>
|
|
#include <keys.h>
|
|
#include <sound.h>
|
|
#include <cmd.h>
|
|
#include <protocol.h>
|
|
#include <cvar.h>
|
|
#include <screen.h>
|
|
#include <zone.h>
|
|
#include <client.h>
|
|
#ifdef UQUAKE
|
|
#include <server.h>
|
|
#endif
|
|
#include <view.h>
|
|
#include <sbar.h>
|
|
#include <cdaudio.h>
|
|
#include <input.h>
|
|
|
|
#ifdef __sun
|
|
/* Sun's model_t in sys/model.h conflicts w/ Quake's model_t */
|
|
#define model_t sunmodel_t
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#ifdef __sun
|
|
#undef model_t
|
|
#endif
|
|
|
|
#ifndef INADDR_LOOPBACK
|
|
# define INADDR_LOOPBACK (u_int32_t)0x7f000001
|
|
#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;
|
|
cvar_t *rcon_address;
|
|
cvar_t *cl_timeout;
|
|
|
|
// these two are not intended to be set directly
|
|
cvar_t *cl_name;
|
|
cvar_t *cl_color;
|
|
|
|
cvar_t *cl_shownet;
|
|
cvar_t *cl_nolerp;
|
|
|
|
cvar_t *cl_sbar;
|
|
cvar_t *cl_hudswap;
|
|
cvar_t *cl_maxfps;
|
|
|
|
cvar_t *lookspring;
|
|
cvar_t *lookstrafe;
|
|
cvar_t *sensitivity;
|
|
|
|
cvar_t *m_pitch;
|
|
cvar_t *m_yaw;
|
|
cvar_t *m_forward;
|
|
cvar_t *m_side;
|
|
|
|
cvar_t *entlatency;
|
|
cvar_t *cl_predict_players;
|
|
cvar_t *cl_predict_players2;
|
|
cvar_t *cl_solid_players;
|
|
cvar_t *cl_verstring;
|
|
|
|
cvar_t *cl_talksound;
|
|
|
|
extern cvar_t *sys_nostdout;
|
|
|
|
cvar_t *localid;
|
|
|
|
#ifdef QUAKEWORLD
|
|
static qboolean allowremotecmd = true;
|
|
#endif
|
|
|
|
//
|
|
// info mirrors
|
|
//
|
|
cvar_t *password;
|
|
cvar_t *spectator;
|
|
cvar_t *name;
|
|
cvar_t *team;
|
|
cvar_t *skin;
|
|
cvar_t *topcolor;
|
|
cvar_t *bottomcolor;
|
|
cvar_t *rate;
|
|
cvar_t *noaim;
|
|
cvar_t *msg;
|
|
|
|
extern cvar_t *cl_hightrack;
|
|
|
|
client_static_t cls;
|
|
client_state_t cl;
|
|
entity_state_t cl_baselines[MAX_EDICTS];
|
|
// FIXME: put these on hunk?
|
|
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;
|
|
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;
|
|
cvar_t *show_fps;
|
|
cvar_t *developer;
|
|
|
|
void Master_Connect_f (void);
|
|
|
|
char *server_version = NULL; // version of server we connected to
|
|
|
|
char emodel_name[] = "emodel";
|
|
char pmodel_name[] = "pmodel";
|
|
char prespawn_name[] = "prespawn %i 0 %i";
|
|
char modellist_name[] = "modellist %i %i";
|
|
char soundlist_name[] = "soundlist %i %i";
|
|
|
|
|
|
#ifdef QUAKEWORLD
|
|
void CL_BeginServerConnect(void)
|
|
{
|
|
connect_time = 0;
|
|
CL_CheckForResend();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
CL_Changing_f
|
|
|
|
Just sent as a hint to the client that they should
|
|
drop to full console
|
|
=================
|
|
*/
|
|
void CL_Changing_f (void)
|
|
{
|
|
#ifdef QUAKEWORLD
|
|
if (cls.download) // don't change when downloading
|
|
return;
|
|
#endif
|
|
|
|
S_StopAllSounds (true);
|
|
cl.intermission = 0;
|
|
cls.state = ca_connected; // not active anymore, but not disconnected
|
|
Con_Printf ("\nChanging map...\n");
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
=================
|
|
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);
|
|
snprintf(data, sizeof(data), "%c%c%c%cgetchallenge\n", 255, 255, 255, 255);
|
|
NET_SendPacket (strlen(data), data, adr);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=====================
|
|
CL_ClearState
|
|
|
|
=====================
|
|
*/
|
|
void CL_ClearState (void)
|
|
{
|
|
int i;
|
|
|
|
S_StopAllSounds (true);
|
|
|
|
#ifdef QUAKEWORLD
|
|
Host_ClearMemory ();
|
|
#elif UQUAKE
|
|
if (!sv.active)
|
|
Host_ClearMemory ();
|
|
#endif
|
|
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));
|
|
#ifdef UQUAKE
|
|
memset (cl_entities, 0, sizeof(cl_entities));
|
|
memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
|
|
#endif
|
|
memset (cl_dlights, 0, sizeof(cl_dlights));
|
|
memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
|
|
CL_ClearTEnts ();
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
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;
|
|
|
|
snprintf(num, sizeof(num), "%i", top);
|
|
Cvar_Set (topcolor, num);
|
|
snprintf(num, sizeof(num), "%i", bottom);
|
|
Cvar_Set (bottomcolor, num);
|
|
}
|
|
|
|
/*
|
|
================
|
|
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_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((int)*s))
|
|
s++;
|
|
while (*s && isspace((int)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 QuakeForge.\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 1 // Tonik: in qwcl 2.33+, Zoid commented this out. Why?
|
|
if (c == svc_disconnect) {
|
|
//Tonik Con_Printf ("disconnect\n");
|
|
|
|
Host_EndGame ("Server disconnected");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
Con_Printf ("unknown: %c\n", c);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
=====================
|
|
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)
|
|
{
|
|
#ifdef QUAKEWORLD
|
|
byte final[10];
|
|
#endif
|
|
|
|
connect_time = -1;
|
|
|
|
#if defined(_WIN32) && defined (QUAKEWORLD)
|
|
SetWindowText (mainwindow, "QuakeWorld: disconnected");
|
|
#endif
|
|
|
|
// stop sounds (especially looping!)
|
|
S_StopAllSounds (true);
|
|
|
|
// bring the console down and fade the colors back to normal
|
|
// SCR_BringDownConsole ();
|
|
|
|
// if running a local server, shut it down
|
|
if (cls.demoplayback)
|
|
CL_StopPlayback ();
|
|
else if (cls.state != ca_disconnected)
|
|
// else if (cls.state >= ca_connected)
|
|
{
|
|
if (cls.demorecording)
|
|
CL_Stop_f ();
|
|
|
|
#ifdef QUAKEWORLD
|
|
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);
|
|
#else
|
|
Con_DPrintf ("Sending clc_disconnect\n");
|
|
SZ_Clear (&cls.netchan.message);
|
|
MSG_WriteByte (&cls.netchan.message, clc_disconnect);
|
|
NET_SendUnreliableMessage (cls.netcon, &cls.netchan.message);
|
|
SZ_Clear (&cls.netchan.message);
|
|
NET_Close (cls.netcon);
|
|
#endif
|
|
|
|
cls.state = ca_disconnected;
|
|
|
|
cls.demoplayback = cls.demorecording = cls.timedemo = false;
|
|
#ifdef UQUAKE
|
|
if (sv.active)
|
|
SV_Shutdown(false);
|
|
#endif
|
|
}
|
|
#ifdef QUAKEWORLD
|
|
Cam_Reset();
|
|
|
|
if (cls.download) {
|
|
Qclose(cls.download);
|
|
cls.download = NULL;
|
|
}
|
|
|
|
CL_StopUpload();
|
|
#endif
|
|
#ifdef UQUAKE
|
|
cls.demoplayback = cls.timedemo = false;
|
|
cls.signon = 0;
|
|
cl.paused = false; // Tonik
|
|
#endif
|
|
}
|
|
|
|
void CL_Disconnect_f (void)
|
|
{
|
|
CL_Disconnect ();
|
|
#ifdef UQUAKE
|
|
if (sv.active)
|
|
SV_Shutdown (false);
|
|
#endif
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
=====================
|
|
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;
|
|
}
|
|
|
|
snprintf(cls.downloadname, sizeof(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 = Qopen (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)));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=====================
|
|
CL_EstablishConnection
|
|
|
|
Host should be either "local" or a net address to be passed on
|
|
=====================
|
|
*/
|
|
void CL_EstablishConnection (char *host)
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
return;
|
|
|
|
if (cls.demoplayback)
|
|
return;
|
|
|
|
CL_Disconnect ();
|
|
|
|
#ifdef UQUAKE
|
|
cls.netcon = NET_Connect (host);
|
|
if (!cls.netcon)
|
|
Host_Error ("CL_Connect: connect failed\n");
|
|
#endif
|
|
Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
|
|
|
|
cls.demonum = -1; // not in the demo loop now
|
|
cls.state = ca_connected;
|
|
#ifdef UQUAKE
|
|
cls.signon = 0; // need all the signon messages before playing
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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;
|
|
|
|
#ifdef QUAKEWORLD
|
|
Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
==================
|
|
CL_FullServerinfo_f
|
|
|
|
Sent by server when serverinfo changes
|
|
==================
|
|
*/
|
|
void CL_FullServerinfo_f (void)
|
|
{
|
|
char *p;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_Printf ("usage: fullserverinfo <complete info string>\n");
|
|
return;
|
|
}
|
|
|
|
Con_DPrintf("Cmd_Argv(1): '%s'\n", Cmd_Argv(1));
|
|
strcpy (cl.serverinfo, Cmd_Argv(1));
|
|
Con_DPrintf("cl.serverinfo: '%s'\n", cl.serverinfo);
|
|
|
|
if ((p = Info_ValueForKey(cl.serverinfo, "*qf_version")) && *p) {
|
|
if (!server_version)
|
|
Con_Printf("QuakeForge Version %s Server\n", p);
|
|
server_version = strdup(p);
|
|
} else if ((p = Info_ValueForKey(cl.serverinfo, "*version")) && *p) {
|
|
if (!server_version)
|
|
Con_Printf("Version %s Server\n", p);
|
|
server_version = strdup(p);
|
|
}
|
|
|
|
if ((p = Info_ValueForKey(cl.serverinfo, "*qsg_standard")) && *p)
|
|
{
|
|
if ((cl.stdver = Q_atoi (p)))
|
|
Con_Printf("QSG standards version %i\n", cl.stdver);
|
|
else
|
|
Con_Printf("Invalid standards version: %s", p);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef UQUAKE
|
|
/*
|
|
===============
|
|
CL_LerpPoint
|
|
|
|
Determines the fraction between the last two messages that the objects
|
|
should be put at.
|
|
===============
|
|
*/
|
|
float CL_LerpPoint (void)
|
|
{
|
|
float f, frac;
|
|
|
|
f = cl.mtime[0] - cl.mtime[1];
|
|
|
|
if (!f || cl_nolerp->value || cls.timedemo || sv.active)
|
|
{
|
|
cl.time = cl.mtime[0];
|
|
return 1;
|
|
}
|
|
if (f > 0.1)
|
|
{ // dropped packet, or start of demo
|
|
cl.mtime[1] = cl.mtime[0] - 0.1;
|
|
f = 0.1;
|
|
}
|
|
frac = (cl.time - cl.mtime[1]) / f;
|
|
//Con_Printf ("frac: %f\n",frac);
|
|
if (frac < 0)
|
|
{
|
|
if (frac < -0.01)
|
|
{
|
|
SetPal(1);
|
|
cl.time = cl.mtime[1];
|
|
// Con_Printf ("low frac\n");
|
|
}
|
|
frac = 0;
|
|
}
|
|
else if (frac > 1)
|
|
{
|
|
if (frac > 1.01)
|
|
{
|
|
SetPal(2);
|
|
cl.time = cl.mtime[0];
|
|
// Con_Printf ("high frac\n");
|
|
}
|
|
frac = 1;
|
|
}
|
|
else
|
|
SetPal(0);
|
|
|
|
return frac;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=====================
|
|
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
|
|
|
|
#ifdef UQUAKE
|
|
SCR_BeginLoadingPlaque ();
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
snprintf(str, sizeof(str),"playdemo %s\n", cls.demos[cls.demonum]);
|
|
Cbuf_InsertText (str);
|
|
cls.demonum++;
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
====================
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
#ifdef UQUAKE
|
|
/*
|
|
==============
|
|
CL_PrintEntities_f
|
|
==============
|
|
*/
|
|
void CL_PrintEntities_f (void)
|
|
{
|
|
entity_t *ent;
|
|
int i;
|
|
|
|
for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
|
|
{
|
|
Con_Printf ("%3i:",i);
|
|
if (!ent->model)
|
|
{
|
|
Con_Printf ("EMPTY\n");
|
|
continue;
|
|
}
|
|
Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
|
|
,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
==================
|
|
CL_Quit_f
|
|
==================
|
|
*/
|
|
void CL_Quit_f (void)
|
|
{
|
|
#ifdef QUAKEWORLD
|
|
if (1 /* key_dest != key_console */ /* && cls.state != ca_dedicated */)
|
|
{
|
|
M_Menu_Quit_f ();
|
|
return;
|
|
}
|
|
#endif
|
|
CL_Disconnect ();
|
|
Sys_Quit ();
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
=====================
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
#ifdef UQUAKE
|
|
/*
|
|
===============
|
|
CL_ReadFromServer
|
|
|
|
Read all incoming data from the server
|
|
===============
|
|
*/
|
|
int CL_ReadFromServer (void)
|
|
{
|
|
int ret;
|
|
|
|
cl.oldtime = cl.time;
|
|
cl.time += host_frametime;
|
|
|
|
do
|
|
{
|
|
ret = CL_GetMessage ();
|
|
if (ret == -1)
|
|
Host_Error ("CL_ReadFromServer: lost server connection");
|
|
if (!ret)
|
|
break;
|
|
|
|
cl.last_received_message = realtime;
|
|
CL_ParseServerMessage ();
|
|
} while (ret && cls.state >= ca_connected);
|
|
|
|
if (cl_shownet->value)
|
|
Con_Printf ("\n");
|
|
|
|
CL_RelinkEntities ();
|
|
CL_UpdateTEnts ();
|
|
|
|
//
|
|
// bring the links up to date
|
|
//
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
=================
|
|
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;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
CL_Reconnect_f
|
|
|
|
The server is changing levels
|
|
=================
|
|
*/
|
|
void CL_Reconnect_f (void)
|
|
{
|
|
#ifdef QUAKEWORLD
|
|
if (cls.download) // don't change when downloading
|
|
return;
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef QUAKEWORLD
|
|
if (!*cls.servername) {
|
|
Con_Printf("No server to reconnect to...\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
CL_Disconnect();
|
|
#ifdef QUAKEWORLD
|
|
CL_BeginServerConnect();
|
|
#endif
|
|
}
|
|
|
|
#ifdef UQUAKE
|
|
/*
|
|
===============
|
|
CL_RelinkEntities
|
|
===============
|
|
*/
|
|
void CL_RelinkEntities (void)
|
|
{
|
|
entity_t *ent;
|
|
int i, j;
|
|
float frac, f, d;
|
|
vec3_t delta;
|
|
float bobjrotate;
|
|
vec3_t oldorg;
|
|
dlight_t *dl;
|
|
|
|
// determine partial update time
|
|
frac = CL_LerpPoint ();
|
|
|
|
cl_numvisedicts = 0;
|
|
|
|
//
|
|
// interpolate player info
|
|
//
|
|
for (i=0 ; i<3 ; i++)
|
|
cl.velocity[i] = cl.mvelocity[1][i] +
|
|
frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
|
|
|
|
if (cls.demoplayback)
|
|
{
|
|
// interpolate the angles
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
|
|
if (d > 180)
|
|
d -= 360;
|
|
else if (d < -180)
|
|
d += 360;
|
|
cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
|
|
}
|
|
}
|
|
|
|
bobjrotate = anglemod(100*cl.time);
|
|
|
|
// start on the entity after the world
|
|
for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
|
|
{
|
|
if (!ent->model)
|
|
{ // empty slot
|
|
if (ent->forcelink)
|
|
R_RemoveEfrags (ent); // just became empty
|
|
continue;
|
|
}
|
|
|
|
// if the object wasn't included in the last packet, remove it
|
|
if (ent->msgtime != cl.mtime[0])
|
|
{
|
|
ent->model = NULL;
|
|
continue;
|
|
}
|
|
|
|
VectorCopy (ent->origin, oldorg);
|
|
|
|
if (ent->forcelink)
|
|
{ // the entity was not updated in the last message
|
|
// so move to the final spot
|
|
VectorCopy (ent->msg_origins[0], ent->origin);
|
|
VectorCopy (ent->msg_angles[0], ent->angles);
|
|
}
|
|
else
|
|
{ // if the delta is large, assume a teleport and don't lerp
|
|
f = frac;
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
|
|
if (delta[j] > 100 || delta[j] < -100)
|
|
f = 1; // assume a teleportation, not a motion
|
|
}
|
|
|
|
// interpolate the origin and angles
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
|
|
|
|
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
|
|
if (d > 180)
|
|
d -= 360;
|
|
else if (d < -180)
|
|
d += 360;
|
|
ent->angles[j] = ent->msg_angles[1][j] + f*d;
|
|
}
|
|
|
|
}
|
|
|
|
// rotate binary objects locally
|
|
if (ent->model->flags & EF_ROTATE)
|
|
ent->angles[1] = bobjrotate;
|
|
|
|
if (ent->effects & EF_BRIGHTFIELD)
|
|
R_EntityParticles (ent);
|
|
#ifdef QUAKE2
|
|
if (ent->effects & EF_DARKFIELD)
|
|
R_DarkFieldParticles (ent);
|
|
#endif
|
|
if (ent->effects & EF_MUZZLEFLASH)
|
|
{
|
|
vec3_t fv, rv, uv;
|
|
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->origin[2] += 16;
|
|
AngleVectors (ent->angles, fv, rv, uv);
|
|
|
|
VectorMA (dl->origin, 18, fv, dl->origin);
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->minlight = 32;
|
|
dl->die = cl.time + 0.1;
|
|
}
|
|
if (ent->effects & EF_BRIGHTLIGHT)
|
|
{
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->origin[2] += 16;
|
|
dl->radius = 400 + (rand()&31);
|
|
dl->die = cl.time + 0.001;
|
|
}
|
|
if (ent->effects & EF_DIMLIGHT)
|
|
{
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->die = cl.time + 0.001;
|
|
}
|
|
#ifdef QUAKE2
|
|
if (ent->effects & EF_DARKLIGHT)
|
|
{
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->radius = 200.0 + (rand()&31);
|
|
dl->die = cl.time + 0.001;
|
|
dl->dark = true;
|
|
}
|
|
if (ent->effects & EF_LIGHT)
|
|
{
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->radius = 200;
|
|
dl->die = cl.time + 0.001;
|
|
}
|
|
#endif
|
|
|
|
if (ent->model->flags & EF_GIB)
|
|
R_RocketTrail (oldorg, ent->origin, 2);
|
|
else if (ent->model->flags & EF_ZOMGIB)
|
|
R_RocketTrail (oldorg, ent->origin, 4);
|
|
else if (ent->model->flags & EF_TRACER)
|
|
R_RocketTrail (oldorg, ent->origin, 3);
|
|
else if (ent->model->flags & EF_TRACER2)
|
|
R_RocketTrail (oldorg, ent->origin, 5);
|
|
else if (ent->model->flags & EF_ROCKET)
|
|
{
|
|
R_RocketTrail (oldorg, ent->origin, 0);
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (ent->origin, dl->origin);
|
|
dl->radius = 200;
|
|
dl->die = cl.time + 0.01;
|
|
}
|
|
else if (ent->model->flags & EF_GRENADE)
|
|
R_RocketTrail (oldorg, ent->origin, 1);
|
|
else if (ent->model->flags & EF_TRACER3)
|
|
R_RocketTrail (oldorg, ent->origin, 6);
|
|
|
|
ent->forcelink = false;
|
|
|
|
if (i == cl.playernum + 1 && !cl_chasecam->value)
|
|
continue;
|
|
|
|
#ifdef QUAKE2
|
|
if ( ent->effects & EF_NODRAW )
|
|
continue;
|
|
#endif
|
|
if (cl_numvisedicts < MAX_VISEDICTS)
|
|
{
|
|
cl_visedicts[cl_numvisedicts] = *ent;
|
|
cl_numvisedicts++;
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
=======================
|
|
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");
|
|
|
|
// Arrgh, this was not in the old binary only release, and eats up
|
|
// far too much of the 196 chars in the userinfo space, leaving nothing
|
|
// for player use, thus, its commented out for the moment..
|
|
//
|
|
//Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING);
|
|
|
|
// Con_Printf ("Connecting to %s...\n", cls.servername);
|
|
snprintf(data, sizeof(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);
|
|
}
|
|
#endif
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
==================
|
|
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 ();
|
|
}
|
|
#endif
|
|
|
|
#ifdef UQUAKE
|
|
/*
|
|
=====================
|
|
CL_SignonReply
|
|
|
|
An svc_signonnum has been received, perform a client side setup
|
|
=====================
|
|
*/
|
|
void CL_SignonReply (void)
|
|
{
|
|
char str[8192];
|
|
|
|
Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
|
|
|
|
switch (cls.signon)
|
|
{
|
|
case 1:
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
MSG_WriteString (&cls.netchan.message, "prespawn");
|
|
cls.state = ca_onserver;
|
|
break;
|
|
|
|
case 2:
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
MSG_WriteString (&cls.netchan.message, va("name \"%s\"\n", cl_name->string));
|
|
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
MSG_WriteString (&cls.netchan.message, va("color %i %i\n", ((int)cl_color->value)>>4, ((int)cl_color->value)&15));
|
|
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
snprintf(str, sizeof(str), "spawn %s", cls.spawnparms);
|
|
MSG_WriteString (&cls.netchan.message, str);
|
|
break;
|
|
|
|
case 3:
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
MSG_WriteString (&cls.netchan.message, "begin");
|
|
Cache_Report (); // print remaining memory
|
|
break;
|
|
|
|
case 4:
|
|
SCR_EndLoadingPlaque (); // allow normal screen updates
|
|
cls.state = ca_active;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef QUAKEWORLD
|
|
/*
|
|
====================
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=======================
|
|
CL_Version_f
|
|
======================
|
|
*/
|
|
void CL_Version_f (void)
|
|
{
|
|
Con_Printf ("Version %s\n", QF_VERSION);
|
|
Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
#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
|
|
|
|
/*
|
|
===============
|
|
SetPal
|
|
|
|
Debugging tool, just flashes the screen
|
|
===============
|
|
*/
|
|
void SetPal (int i)
|
|
{
|
|
#if 0
|
|
static int old;
|
|
byte pal[768];
|
|
int c;
|
|
|
|
if (i == old)
|
|
return;
|
|
old = i;
|
|
|
|
if (i==0)
|
|
VID_SetPalette (host_basepal);
|
|
else if (i==1)
|
|
{
|
|
for (c=0 ; c<768 ; c+=3)
|
|
{
|
|
pal[c] = 0;
|
|
pal[c+1] = 255;
|
|
pal[c+2] = 0;
|
|
}
|
|
VID_SetPalette (pal);
|
|
}
|
|
else
|
|
{
|
|
for (c=0 ; c<768 ; c+=3)
|
|
{
|
|
pal[c] = 0;
|
|
pal[c+1] = 0;
|
|
pal[c+2] = 255;
|
|
}
|
|
VID_SetPalette (pal);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_Init
|
|
=================
|
|
*/
|
|
void CL_Init (void)
|
|
{
|
|
#ifdef UQUAKE
|
|
SZ_Alloc (&cls.netchan.message, 1024);
|
|
#endif
|
|
|
|
cls.state = ca_disconnected;
|
|
|
|
#ifdef QUAKEWORLD
|
|
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);
|
|
Info_SetValueForStarKey(cls.userinfo, "*qf_ver", QF_VERSION, MAX_INFO_STRING);
|
|
Info_SetValueForStarKey(cls.userinfo, "*ver", VERSION, MAX_INFO_STRING);
|
|
#endif
|
|
|
|
CL_InitInput ();
|
|
CL_InitTEnts ();
|
|
#ifdef QUAKEWORLD
|
|
CL_InitPrediction ();
|
|
CL_InitCam ();
|
|
Pmove_Init ();
|
|
#endif
|
|
|
|
//
|
|
// register our commands
|
|
//
|
|
Cmd_AddCommand ("maplist", COM_Maplist_f);
|
|
Cmd_AddCommand ("changing", CL_Changing_f);
|
|
#ifdef UQUAKE
|
|
Cmd_AddCommand ("entities", CL_PrintEntities_f);
|
|
#endif
|
|
Cmd_AddCommand ("disconnect", CL_Disconnect_f);
|
|
Cmd_AddCommand ("record", CL_Record_f);
|
|
#ifdef QUAKEWORLD
|
|
Cmd_AddCommand ("version", CL_Version_f);
|
|
Cmd_AddCommand ("rerecord", CL_ReRecord_f);
|
|
#endif
|
|
Cmd_AddCommand ("stop", CL_Stop_f);
|
|
Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
|
|
Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
|
|
|
|
#ifdef QUAKEWORLD
|
|
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);
|
|
Cmd_AddCommand ("kill", NULL);
|
|
Cmd_AddCommand ("pause", NULL);
|
|
Cmd_AddCommand ("say", NULL);
|
|
Cmd_AddCommand ("say_team", NULL);
|
|
#endif
|
|
//
|
|
// forward to server commands
|
|
//
|
|
Cmd_AddCommand ("serverinfo", NULL);
|
|
|
|
//
|
|
// Windows commands
|
|
//
|
|
#ifdef _WIN32
|
|
Cmd_AddCommand ("windows", CL_Windows_f);
|
|
#endif
|
|
}
|
|
|
|
void CL_InitCvars()
|
|
{
|
|
#ifdef QUAKEWORLD
|
|
extern cvar_t *baseskin;
|
|
extern cvar_t *noskins;
|
|
#endif
|
|
|
|
//
|
|
// register our cvars
|
|
//
|
|
host_speeds = Cvar_Get ("host_speeds","0",0,"None");
|
|
show_fps = Cvar_Get ("show_fps","0",0,"None");
|
|
developer = Cvar_Get ("developer","0",0,"None");
|
|
sys_nostdout = Cvar_Get ("sys_nostdout","0",0,"None");
|
|
|
|
cl_warncmd = Cvar_Get ("cl_warncmd","0",0,"None");
|
|
cl_name = Cvar_Get ("_cl_name","player",CVAR_ARCHIVE,"None");
|
|
cl_color = Cvar_Get ("_cl_color","0",CVAR_ARCHIVE,"None");
|
|
cl_upspeed = Cvar_Get ("cl_upspeed","200",0,"None");
|
|
cl_forwardspeed = Cvar_Get ("cl_forwardspeed","200",CVAR_ARCHIVE,
|
|
"None");
|
|
cl_backspeed = Cvar_Get ("cl_backspeed","200",CVAR_ARCHIVE,"None");
|
|
cl_sidespeed = Cvar_Get ("cl_sidespeed","350",0,"None");
|
|
cl_movespeedkey = Cvar_Get ("cl_movespeedkey","2.0",0,"None");
|
|
cl_yawspeed = Cvar_Get ("cl_yawspeed","140",0,"None");
|
|
cl_pitchspeed = Cvar_Get ("cl_pitchspeed","150",0,"None");
|
|
cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey","1.5",0,"None");
|
|
cl_shownet = Cvar_Get ("cl_shownet","0",0,"None");
|
|
cl_nolerp = Cvar_Get ("cl_nolerp","0",0,"None");
|
|
cl_sbar = Cvar_Get ("cl_sbar","1",CVAR_ARCHIVE,"None");
|
|
cl_hudswap = Cvar_Get ("cl_hudswap","1",CVAR_ARCHIVE,"None");
|
|
cl_maxfps = Cvar_Get ("cl_maxfps","0",CVAR_ARCHIVE,"None");
|
|
cl_timeout = Cvar_Get ("cl_timeout","60",0,"None");
|
|
cl_verstring = Cvar_Get ("cl_verstring",
|
|
#ifdef QUAKEWORLD
|
|
"QuakeForge (QW client) " QF_VERSION,
|
|
#else
|
|
"QuakeForge (UQuake) " QF_VERSION,
|
|
#endif
|
|
CVAR_NONE, "Console version string");
|
|
lookspring = Cvar_Get ("lookspring","0",CVAR_ARCHIVE,"None");
|
|
lookstrafe = Cvar_Get ("lookstrafe","0",CVAR_ARCHIVE,"None");
|
|
sensitivity = Cvar_Get ("sensitivity","3",CVAR_ARCHIVE,"None");
|
|
|
|
m_pitch = Cvar_Get ("m_pitch","0.022",CVAR_ARCHIVE,"None");
|
|
m_yaw = Cvar_Get ("m_yaw","0.022",CVAR_ARCHIVE,"None");
|
|
m_forward = Cvar_Get ("m_forward","1",CVAR_ARCHIVE,"None");
|
|
m_side = Cvar_Get ("m_side","0.8",CVAR_ARCHIVE,"None");
|
|
|
|
rcon_password = Cvar_Get ("rcon_password","",0,"None");
|
|
rcon_address = Cvar_Get ("rcon_address","",0,"None");
|
|
|
|
entlatency = Cvar_Get ("entlatency","20",0,"None");
|
|
cl_predict_players2 = Cvar_Get ("cl_predict_players2","1",0,"None");
|
|
cl_predict_players = Cvar_Get ("cl_predict_players","1",0,"None");
|
|
cl_solid_players = Cvar_Get ("cl_solid_players","1",0,"None");
|
|
|
|
cl_talksound = Cvar_Get ("cl_talksound", "misc/talk.wav", CVAR_NONE,
|
|
"The sound used for talk messages");
|
|
|
|
localid = Cvar_Get ("localid","",0,"None");
|
|
|
|
#ifdef QUAKEWORLD
|
|
baseskin = Cvar_Get ("baseskin","base",0,"None");
|
|
noskins = Cvar_Get ("noskins","0",0,"None");
|
|
|
|
//
|
|
// info mirrors
|
|
//
|
|
name = Cvar_Get ("name","unnamed",
|
|
CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,"None");
|
|
#endif
|
|
password = Cvar_Get ("password","",CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
spectator = Cvar_Get ("spectator","",CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
skin = Cvar_Get ("skin","",CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
team = Cvar_Get ("team","",CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
topcolor = Cvar_Get ("topcolor","0",
|
|
CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
bottomcolor = Cvar_Get ("bottomcolor","0",
|
|
CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
rate = Cvar_Get ("rate","2500",
|
|
CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
msg = Cvar_Get ("msg","1",CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
noaim = Cvar_Get ("noaim","0",
|
|
CVAR_ARCHIVE|CVAR_USERINFO|CVAR_SERVERINFO,
|
|
"None");
|
|
}
|