9ae7e2621d
Lots of changes. CSQC should be functional, but is still tied to debug builds. It WILL have some bugs still, hopefully I'll be able to clean them up better if people test it a bit. Precompiled headers are working properly now. Compile times are now much quicker in msvc. This takes most of the files this commit. Restructured how client commands work. They're buffered outside the network message, some multithreaded code is in. It needs a bit of testing before it's active. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@885 fc73d0e0-1445-4013-8a0c-d673dee63da5
3021 lines
63 KiB
C
3021 lines
63 KiB
C
/*
|
|
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
|
|
|
|
#if defined(_WIN32) && !defined(MINGW) && defined(RGLQUAKE)
|
|
#define WINAVI
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
//imap exports
|
|
extern void IMAP_CreateConnection(char *servername, char *username, char *password);
|
|
extern cvar_t imap_checkfrequency;
|
|
extern void IMAP_Think (void);
|
|
|
|
//pop3
|
|
extern void POP3_CreateConnection(char *servername, char *username, char *password);
|
|
extern cvar_t pop3_checkfrequency;
|
|
extern void POP3_Think (void);
|
|
|
|
|
|
// 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", ""};
|
|
|
|
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", NULL, CVAR_ARCHIVE};
|
|
cvar_t cl_hudswap = {"cl_hudswap", "0", NULL, CVAR_ARCHIVE};
|
|
cvar_t cl_maxfps = {"cl_maxfps", "-1", NULL, CVAR_ARCHIVE};
|
|
cvar_t cl_nopext = {"cl_nopext", "0", NULL, CVAR_ARCHIVE};
|
|
|
|
cvar_t cfg_save_name = {"cfg_save_name", "fte", NULL, CVAR_ARCHIVE};
|
|
|
|
cvar_t cl_splitscreen = {"cl_splitscreen", "0"};
|
|
|
|
cvar_t lookspring = {"lookspring","0", NULL, CVAR_ARCHIVE};
|
|
cvar_t lookstrafe = {"lookstrafe","0", NULL, CVAR_ARCHIVE};
|
|
cvar_t sensitivity = {"sensitivity","3", NULL, CVAR_ARCHIVE};
|
|
|
|
cvar_t cl_staticsounds = {"cl_staticsounds", "1"};
|
|
|
|
cvar_t m_pitch = {"m_pitch","0.022", NULL, CVAR_ARCHIVE};
|
|
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 cl_demospeed = {"cl_demospeed", "0"};
|
|
|
|
cvar_t localid = {"localid", ""};
|
|
|
|
static qboolean allowremotecmd = true;
|
|
|
|
//
|
|
// info mirrors
|
|
//
|
|
cvar_t password = {"password", "", NULL, CVAR_USERINFO}; //this is parhaps slightly dodgy...
|
|
cvar_t spectator = {"spectator", "", NULL, CVAR_USERINFO};
|
|
cvar_t name = {"name", "unnamed", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t team = {"team", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t skin = {"skin", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t model = {"model", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t topcolor = {"topcolor", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t bottomcolor = {"bottomcolor", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t rate = {"rate", "2500", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t drate = {"drate", "100000", NULL, CVAR_ARCHIVE | CVAR_USERINFO}; // :)
|
|
cvar_t noaim = {"noaim", "", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t msg = {"msg", "1", NULL, CVAR_ARCHIVE | CVAR_USERINFO};
|
|
cvar_t cl_nofake = {"cl_nofake", "2"};
|
|
cvar_t cl_chatsound = {"cl_chatsound", "1"};
|
|
|
|
cvar_t r_rocketlight = {"r_rocketlight", "1"};
|
|
cvar_t r_lightflicker = {"r_lightflicker", "1"};
|
|
cvar_t cl_r2g = {"cl_r2g", "0"};
|
|
cvar_t r_powerupglow = {"r_powerupglow", "1"};
|
|
cvar_t cl_gibfilter = {"cl_gibfilter", "0"};
|
|
cvar_t cl_deadbodyfilter = {"cl_deadbodyfilter", "0"};
|
|
|
|
cvar_t cl_muzzleflash = {"cl_muzzleflash", "1"};
|
|
|
|
cvar_t cl_item_bobbing = {"cl_model_bobbing", "0"};
|
|
|
|
cvar_t requiredownloads = {"requiredownloads","1", NULL, CVAR_ARCHIVE};
|
|
cvar_t cl_standardchat = {"cl_standardchat", "0"};
|
|
|
|
cvar_t host_mapname = {"host_mapname", ""};
|
|
|
|
extern cvar_t cl_hightrack;
|
|
|
|
char cl_screengroup[] = "Screen options";
|
|
char cl_controlgroup[] = "client operation options";
|
|
char cl_inputgroup[] = "client input controls";
|
|
char cl_predictiongroup[] = "Client side prediction";
|
|
|
|
|
|
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];
|
|
//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
|
|
int connect_type = 0;
|
|
|
|
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;
|
|
|
|
qbyte *host_basepal;
|
|
qbyte *host_colormap;
|
|
|
|
cvar_t host_speeds = {"host_speeds","0"}; // set for running times
|
|
#ifdef CRAZYDEBUGGING
|
|
cvar_t developer = {"developer","1"};
|
|
#else
|
|
cvar_t developer = {"developer","0"};
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifndef CLIENTONLY
|
|
if (!isDedicated)
|
|
#endif
|
|
{
|
|
M_Menu_Quit_f ();
|
|
return;
|
|
}
|
|
CL_Disconnect ();
|
|
Sys_Quit ();
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
CL_Version_f
|
|
======================
|
|
*/
|
|
void CL_Version_f (void)
|
|
{
|
|
#ifdef VERSION3PART
|
|
#ifdef DISTRIBUTION
|
|
Con_TPrintf (TLC_VERSIONST3, DISTRIBUTION, VERSION, build_number());
|
|
#else
|
|
Con_TPrintf (TCL_VERSION3, VERSION, build_number());
|
|
#endif
|
|
#else
|
|
#ifdef DISTRIBUTION
|
|
Con_TPrintf (TLC_VERSIONST2, DISTRIBUTION, VERSION, build_number());
|
|
#else
|
|
Con_TPrintf (TLC_VERSION2, VERSION, build_number());
|
|
#endif
|
|
#endif
|
|
Con_TPrintf (TL_EXEDATETIME, __DATE__, __TIME__);
|
|
}
|
|
|
|
void CL_ConnectToDarkPlaces(char *challenge, netadr_t adr)
|
|
{
|
|
char data[2048];
|
|
cls.fteprotocolextensions = 0;
|
|
|
|
cls.resendinfo = false;
|
|
|
|
connect_time = realtime; // for retransmit requests
|
|
|
|
sprintf(data, "%c%c%c%cconnect\\protocol\\darkplaces 3\\challenge\\%s", 255, 255, 255, 255, challenge);
|
|
|
|
NET_SendPacket (NS_CLIENT, strlen(data), data, adr);
|
|
|
|
cl.splitclients = 0;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
CL_SendConnectPacket
|
|
|
|
called by CL_Connect_f and CL_CheckResend
|
|
======================
|
|
*/
|
|
void CL_SendConnectPacket (
|
|
#ifdef PROTOCOL_VERSION_FTE
|
|
int ftepext,
|
|
#endif
|
|
int compressioncrc
|
|
/*, ...*/) //dmw new parms
|
|
{
|
|
netadr_t adr;
|
|
char data[2048];
|
|
char playerinfo2[MAX_INFO_STRING];
|
|
double t1, t2;
|
|
#ifdef PROTOCOL_VERSION_FTE
|
|
int fteprotextsupported=0;
|
|
#endif
|
|
int clients;
|
|
int c;
|
|
|
|
// 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;
|
|
|
|
if (cl_nopext.value) //imagine it's an unenhanced server
|
|
{
|
|
ftepext = 0;
|
|
compressioncrc = 0;
|
|
}
|
|
|
|
#ifdef PROTOCOL_VERSION_FTE
|
|
#ifdef PEXT_SCALE //dmw - protocol extensions
|
|
fteprotextsupported |= PEXT_SCALE;
|
|
#endif
|
|
#ifdef PEXT_LIGHTSTYLECOL
|
|
fteprotextsupported |= PEXT_LIGHTSTYLECOL;
|
|
#endif
|
|
#ifdef PEXT_TRANS
|
|
fteprotextsupported |= PEXT_TRANS;
|
|
#endif
|
|
#ifdef PEXT_VIEW2
|
|
fteprotextsupported |= PEXT_VIEW2;
|
|
#endif
|
|
#ifdef PEXT_BULLETENS
|
|
fteprotextsupported |= PEXT_BULLETENS;
|
|
#endif
|
|
#ifdef PEXT_ZLIBDL
|
|
fteprotextsupported |= PEXT_ZLIBDL;
|
|
#endif
|
|
#ifdef PEXT_LIGHTUPDATES
|
|
fteprotextsupported |= PEXT_LIGHTUPDATES;
|
|
#endif
|
|
#ifdef PEXT_FATNESS
|
|
fteprotextsupported |= PEXT_FATNESS;
|
|
#endif
|
|
#ifdef PEXT_HLBSP
|
|
fteprotextsupported |= PEXT_HLBSP;
|
|
#endif
|
|
|
|
#ifdef PEXT_Q2BSP
|
|
fteprotextsupported |= PEXT_Q2BSP;
|
|
#endif
|
|
#ifdef PEXT_Q3BSP
|
|
fteprotextsupported |= PEXT_Q3BSP;
|
|
#endif
|
|
|
|
#ifdef PEXT_TE_BULLET
|
|
fteprotextsupported |= PEXT_TE_BULLET;
|
|
#endif
|
|
#ifdef PEXT_HULLSIZE
|
|
fteprotextsupported |= PEXT_HULLSIZE;
|
|
#endif
|
|
#ifdef PEXT_SETVIEW
|
|
fteprotextsupported |= PEXT_SETVIEW;
|
|
#endif
|
|
#ifdef PEXT_MODELDBL
|
|
fteprotextsupported |= PEXT_MODELDBL;
|
|
#endif
|
|
#ifdef PEXT_VWEAP
|
|
fteprotextsupported |= PEXT_VWEAP;
|
|
#endif
|
|
#ifdef PEXT_FLOATCOORDS
|
|
fteprotextsupported |= PEXT_FLOATCOORDS;
|
|
#endif
|
|
fteprotextsupported |= PEXT_SPAWNSTATIC2;
|
|
#ifdef PEXT_SEEF1
|
|
fteprotextsupported |= PEXT_SEEF1;
|
|
#endif
|
|
fteprotextsupported |= PEXT_SPLITSCREEN;
|
|
fteprotextsupported |= PEXT_HEXEN2;
|
|
fteprotextsupported |= PEXT_CUSTOMTEMPEFFECTS;
|
|
fteprotextsupported |= PEXT_256PACKETENTITIES;
|
|
// fteprotextsupported |= PEXT_64PLAYERS;
|
|
fteprotextsupported |= PEXT_SHOWPIC;
|
|
fteprotextsupported |= PEXT_SETATTACHMENT;
|
|
#ifdef PEXT_CHUNKEDDOWNLOADS
|
|
fteprotextsupported |= PEXT_CHUNKEDDOWNLOADS;
|
|
#endif
|
|
#ifdef PEXT_CSQC
|
|
fteprotextsupported |= PEXT_CSQC;
|
|
#endif
|
|
|
|
fteprotextsupported &= ftepext;
|
|
|
|
cls.fteprotocolextensions = fteprotextsupported;
|
|
#endif
|
|
|
|
t1 = Sys_DoubleTime ();
|
|
|
|
if (
|
|
#ifdef NQPROT
|
|
!cls.netcon &&
|
|
#endif
|
|
!NET_StringToAdr (cls.servername, &adr))
|
|
{
|
|
Con_TPrintf (TLC_BADSERVERADDRESS);
|
|
connect_time = -1;
|
|
return;
|
|
}
|
|
|
|
if (!NET_IsClientLegal(&adr))
|
|
{
|
|
Con_TPrintf (TLC_ILLEGALSERVERADDRESS);
|
|
connect_time = -1;
|
|
return;
|
|
}
|
|
|
|
if (adr.port == 0)
|
|
adr.port = BigShort (27500);
|
|
t2 = Sys_DoubleTime ();
|
|
|
|
cls.resendinfo = false;
|
|
|
|
connect_time = realtime+t2-t1; // for retransmit requests
|
|
|
|
cls.qport = Cvar_VariableValue("qport");
|
|
|
|
// Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING);
|
|
|
|
Q_strncpyz(playerinfo2, cls.userinfo, sizeof(playerinfo2)-1);
|
|
Info_SetValueForStarKey (playerinfo2, "name", "Second player", MAX_INFO_STRING);
|
|
|
|
clients = 1;
|
|
if (cl_splitscreen.value)
|
|
{
|
|
if (adr.type == NA_LOOPBACK)
|
|
clients = cl_splitscreen.value+1;
|
|
else
|
|
Con_Printf("Split screens are still under development\n");
|
|
}
|
|
|
|
if (clients < 1)
|
|
clients = 1;
|
|
if (clients > MAX_SPLITS)
|
|
clients = MAX_SPLITS;
|
|
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server) //sorry - too lazy.
|
|
clients = 1;
|
|
#endif
|
|
|
|
#ifdef Q3CLIENT
|
|
if (cls.q2server==2)
|
|
{ //q3 requires some very strange things.
|
|
CLQ3_SendConnectPacket(adr);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
sprintf(data, "%c%c%c%cconnect", 255, 255, 255, 255);
|
|
|
|
if (clients>1) //splitscreen 'connect' command specifies the number of userinfos sent.
|
|
strcat(data, va("%i", clients));
|
|
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
strcat(data, va(" %i", PROTOCOL_VERSION_Q2));
|
|
else
|
|
#endif
|
|
strcat(data, va(" %i", PROTOCOL_VERSION));
|
|
|
|
|
|
strcat(data, va(" %i %i", cls.qport, cls.challenge));
|
|
|
|
//userinfo 0 + zquake extension info.
|
|
strcat(data, va(" \"%s\\*z_ext\\%i\"", cls.userinfo, SUPPORTED_Z_EXTENSIONS));
|
|
for (c = 1; c < clients; c++)
|
|
{
|
|
Info_SetValueForStarKey (playerinfo2, "name", va("%s%i", name.string, c+1), MAX_INFO_STRING);
|
|
strcat(data, va(" \"%s\"", playerinfo2, SUPPORTED_Z_EXTENSIONS));
|
|
}
|
|
|
|
strcat(data, "\n");
|
|
|
|
#ifdef PROTOCOL_VERSION_FTE
|
|
if (ftepext)
|
|
strcat(data, va("0x%x 0x%x\n", PROTOCOL_VERSION_FTE, fteprotextsupported));
|
|
#endif
|
|
|
|
#ifdef HUFFNETWORK
|
|
if (compressioncrc && Huff_CompressionCRC(compressioncrc))
|
|
{
|
|
strcat(data, va("0x%x 0x%x\n", (('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24)), compressioncrc));
|
|
cls.netchan.compress = true;
|
|
}
|
|
else
|
|
#endif
|
|
cls.netchan.compress = false;
|
|
#ifdef NQPROT
|
|
if (cls.netcon)
|
|
{
|
|
sizebuf_t msg;
|
|
msg.allowoverflow = false;
|
|
msg.cursize = strlen(data);
|
|
msg.data = data;
|
|
msg.maxsize = sizeof(data);
|
|
msg.overflowed = false;
|
|
NET_SendMessage(cls.netcon, &msg);
|
|
}
|
|
else
|
|
#endif
|
|
NET_SendPacket (NS_CLIENT, strlen(data), data, adr);
|
|
|
|
cl.splitclients = 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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;
|
|
|
|
#ifndef CLIENTONLY
|
|
if (!cls.state && sv.state)
|
|
{
|
|
Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername));
|
|
cls.state = ca_disconnected;
|
|
#ifdef Q2CLIENT
|
|
if (!svprogfuncs)
|
|
cls.q2server = true;
|
|
else
|
|
cls.q2server = false;
|
|
#endif
|
|
|
|
CL_SendConnectPacket (svs.fteprotocolextensions, false);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (connect_time == -1)
|
|
return;
|
|
if (cls.state != ca_disconnected)
|
|
return;
|
|
#ifdef NQPROT
|
|
if (connect_type)
|
|
{
|
|
if (!connect_time || !(realtime - connect_time < 5.0))
|
|
{
|
|
connect_time = realtime;
|
|
NQ_BeginConnect(cls.servername);
|
|
NQ_ContinueConnect(cls.servername);
|
|
}
|
|
else
|
|
NQ_ContinueConnect(cls.servername);
|
|
return;
|
|
}
|
|
#endif
|
|
if (connect_time && realtime - connect_time < 5.0)
|
|
return;
|
|
|
|
t1 = Sys_DoubleTime ();
|
|
if (!NET_StringToAdr (cls.servername, &adr))
|
|
{
|
|
Con_TPrintf (TLC_BADSERVERADDRESS);
|
|
connect_time = -1;
|
|
return;
|
|
}
|
|
if (!NET_IsClientLegal(&adr))
|
|
{
|
|
Con_TPrintf (TLC_ILLEGALSERVERADDRESS);
|
|
connect_time = -1;
|
|
return;
|
|
}
|
|
|
|
if (adr.port == 0)
|
|
adr.port = BigShort (27500);
|
|
t2 = Sys_DoubleTime ();
|
|
|
|
connect_time = realtime+t2-t1; // for retransmit requests
|
|
|
|
Con_TPrintf (TLC_CONNECTINGTO, cls.servername);
|
|
sprintf (data, "%c%c%c%cgetchallenge\n", 255, 255, 255, 255);
|
|
NET_SendPacket (NS_CLIENT, strlen(data), data, adr);
|
|
}
|
|
|
|
void CL_BeginServerConnect(void)
|
|
{
|
|
#ifdef NQPROT
|
|
if (cls.netcon)
|
|
{
|
|
NET_Close(cls.netcon);
|
|
cls.netcon = cls.netchan.qsocket = NULL;
|
|
}
|
|
#endif
|
|
connect_time = 0;
|
|
connect_type=0;
|
|
CL_CheckForResend();
|
|
}
|
|
#ifdef NQPROT
|
|
void CLNQ_BeginServerConnect(void)
|
|
{
|
|
if (cls.netcon)
|
|
{
|
|
NET_Close(cls.netcon);
|
|
cls.netcon = NULL;
|
|
}
|
|
connect_time = 0;
|
|
connect_type=1;
|
|
CL_CheckForResend();
|
|
}
|
|
#endif
|
|
void CL_BeginServerReconnect(void)
|
|
{
|
|
#ifndef CLIENTONLY
|
|
if (isDedicated)
|
|
#endif
|
|
{
|
|
Con_TPrintf (TLC_DEDICATEDCANNOTCONNECT);
|
|
return;
|
|
}
|
|
connect_time = 0;
|
|
CL_CheckForResend();
|
|
}
|
|
/*
|
|
================
|
|
CL_Connect_f
|
|
|
|
================
|
|
*/
|
|
void CL_Connect_f (void)
|
|
{
|
|
char *server;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_CONNECT);
|
|
return;
|
|
}
|
|
|
|
server = Cmd_Argv (1);
|
|
|
|
CL_Disconnect_f ();
|
|
|
|
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
|
|
CL_BeginServerConnect();
|
|
}
|
|
|
|
void CL_Join_f (void)
|
|
{
|
|
char *server;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
if (cls.state)
|
|
{ //Hmm. This server sucks.
|
|
if (cls.z_ext & Z_EXT_JOIN_OBSERVE)
|
|
Cmd_ForwardToServer();
|
|
else
|
|
Cbuf_AddText("\nspectator 0;reconnect\n", RESTRICT_LOCAL);
|
|
return;
|
|
}
|
|
Con_Printf ("join requires a connection or servername/ip\n");
|
|
return;
|
|
}
|
|
|
|
server = Cmd_Argv (1);
|
|
|
|
CL_Disconnect_f ();
|
|
|
|
Cvar_Set(&spectator, "0");
|
|
|
|
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
|
|
CL_BeginServerConnect();
|
|
}
|
|
|
|
void CL_Observe_f (void)
|
|
{
|
|
char *server;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
if (cls.state)
|
|
{ //Hmm. This server sucks.
|
|
if (cls.z_ext & Z_EXT_JOIN_OBSERVE)
|
|
Cmd_ForwardToServer();
|
|
else
|
|
Cbuf_AddText("\nspectator 1;reconnect\n", RESTRICT_LOCAL);
|
|
return;
|
|
}
|
|
Con_Printf ("observe requires a connection or servername/ip\n");
|
|
return;
|
|
}
|
|
|
|
server = Cmd_Argv (1);
|
|
|
|
CL_Disconnect_f ();
|
|
|
|
Cvar_Set(&spectator, "1");
|
|
|
|
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
|
|
CL_BeginServerConnect();
|
|
}
|
|
|
|
#ifdef NQPROT
|
|
void CLNQ_Connect_f (void)
|
|
{
|
|
char *server;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_CONNECT);
|
|
return;
|
|
}
|
|
|
|
server = Cmd_Argv (1);
|
|
|
|
CL_Disconnect_f ();
|
|
|
|
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
|
|
CLNQ_BeginServerConnect();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=====================
|
|
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) //FIXME: this is strange...
|
|
{
|
|
Con_TPrintf (TLC_NORCONPASSWORD);
|
|
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_TPrintf (TLC_NORCONDEST);
|
|
|
|
return;
|
|
}
|
|
NET_StringToAdr (rcon_address.string, &to);
|
|
}
|
|
|
|
NET_SendPacket (NS_CLIENT, strlen(message)+1, message
|
|
, to);
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_ClearState
|
|
|
|
=====================
|
|
*/
|
|
void CL_ClearState (void)
|
|
{
|
|
int i;
|
|
#ifndef CLIENTONLY
|
|
#define serverrunning (sv.state != ss_dead)
|
|
#define tolocalserver NET_IsLoopBackAddress(cls.netchan.remote_address)
|
|
#else
|
|
#define serverrunning false
|
|
#define tolocalserver false
|
|
#define SV_UnspawnServer()
|
|
#endif
|
|
|
|
CL_AllowIndependantSendCmd(false); //model stuff could be a problem.
|
|
|
|
S_StopAllSounds (true);
|
|
|
|
Cvar_ApplyLatches(CVAR_SERVEROVERRIDE);
|
|
|
|
Con_DPrintf ("Clearing memory\n");
|
|
#ifdef PEXT_BULLETENS
|
|
WipeBulletenTextures ();
|
|
#endif
|
|
if (!serverrunning || !tolocalserver)
|
|
{
|
|
if (serverrunning)
|
|
SV_UnspawnServer();
|
|
D_FlushCaches ();
|
|
Mod_ClearAll ();
|
|
|
|
if (host_hunklevel) // FIXME: check this...
|
|
Hunk_FreeToLowMark (host_hunklevel);
|
|
|
|
Cvar_ApplyLatches(CVAR_LATCH);
|
|
}
|
|
|
|
CL_ClearTEnts ();
|
|
CL_ClearCustomTEnts();
|
|
SCR_ShowPic_Clear();
|
|
|
|
if (cl.playernum[0] == -1)
|
|
{ //left over from q2 connect.
|
|
Media_PlayFilm("");
|
|
}
|
|
|
|
// 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));
|
|
|
|
memset (cl_baselines, 0, sizeof(cl_baselines));
|
|
|
|
//
|
|
// 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;
|
|
|
|
for (i = 0; i < MAX_SPLITS; i++)
|
|
cl.viewheight[i] = DEFAULT_VIEWHEIGHT;
|
|
cl.minpitch = -70;
|
|
cl.maxpitch = 80;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
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)
|
|
{
|
|
qbyte final[10];
|
|
|
|
connect_time = -1;
|
|
|
|
Cvar_ApplyLatches(CVAR_SERVEROVERRIDE);
|
|
|
|
if (VID_SetWindowCaption)
|
|
VID_SetWindowCaption("FTE QuakeWorld: disconnected");
|
|
|
|
// stop sounds (especially looping!)
|
|
S_StopAllSounds (true);
|
|
#ifdef VM_CG
|
|
CG_Stop();
|
|
#endif
|
|
#ifdef CSQC_DAT
|
|
CSQC_Shutdown();
|
|
#endif
|
|
// if running a local server, shut it down
|
|
if (cls.demoplayback != DPB_NONE)
|
|
CL_StopPlayback ();
|
|
else if (cls.state != ca_disconnected)
|
|
{
|
|
if (cls.demorecording)
|
|
CL_Stop_f ();
|
|
|
|
#ifdef NQPROT
|
|
if (cls.netcon)
|
|
{
|
|
sizebuf_t msg;
|
|
final[0] = clc_disconnect;
|
|
msg.data = final;
|
|
msg.cursize = 1;
|
|
msg.maxsize = 10;
|
|
NET_SendMessage(cls.netcon, &msg);
|
|
NET_SendMessage(cls.netcon, &msg);
|
|
NET_SendMessage(cls.netcon, &msg);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
{
|
|
final[0] = clcq2_stringcmd;
|
|
strcpy (final+1, "disconnect");
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
final[0] = clc_stringcmd;
|
|
strcpy (final+1, "drop");
|
|
}
|
|
Netchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);
|
|
Netchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);
|
|
Netchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);
|
|
}
|
|
|
|
cls.state = ca_disconnected;
|
|
|
|
cls.demoplayback = DPB_NONE;
|
|
cls.demorecording = cls.timedemo = false;
|
|
|
|
#ifndef CLIENTONLY
|
|
//running a server, and it's our own
|
|
if (serverrunning && !tolocalserver)SV_UnspawnServer();
|
|
#endif
|
|
}
|
|
Cam_Reset();
|
|
|
|
if (cl.worldmodel)
|
|
{
|
|
#if defined(RUNTIMELIGHTING) && defined(RGLQUAKE)
|
|
extern model_t *lightmodel;
|
|
lightmodel = NULL;
|
|
#endif
|
|
|
|
cl.worldmodel->needload=true;
|
|
cl.worldmodel=NULL;
|
|
}
|
|
|
|
if (cls.downloadmethod <= DL_QWPENDING)
|
|
cls.downloadmethod = DL_NONE;
|
|
if (cls.downloadqw)
|
|
{
|
|
fclose(cls.downloadqw);
|
|
cls.downloadqw = NULL;
|
|
}
|
|
if (!cls.downloadmethod)
|
|
*cls.downloadname = '\0';
|
|
{
|
|
downloadlist_t *next;
|
|
while(cl.downloadlist)
|
|
{
|
|
next = cl.downloadlist->next;
|
|
Z_Free(cl.downloadlist);
|
|
cl.downloadlist = next;
|
|
}
|
|
while(cl.faileddownloads)
|
|
{
|
|
next = cl.faileddownloads->next;
|
|
Z_Free(cl.faileddownloads);
|
|
cl.faileddownloads = next;
|
|
}
|
|
}
|
|
|
|
COM_FlushTempoaryPacks();
|
|
|
|
#ifdef NQPROT
|
|
cls.signon=0;
|
|
NET_Close(cls.netcon);
|
|
cls.netcon = NULL;
|
|
#endif
|
|
CL_StopUpload();
|
|
|
|
#ifndef CLIENTONLY
|
|
if (!isDedicated)
|
|
#endif
|
|
SCR_EndLoadingPlaque();
|
|
|
|
#ifdef Q2CLIENT
|
|
cls.q2server = 0;
|
|
#endif
|
|
}
|
|
|
|
#undef serverrunning
|
|
#undef tolocalserver
|
|
|
|
void CL_Disconnect_f (void)
|
|
{
|
|
CL_Disconnect ();
|
|
|
|
#ifndef CLIENTONLY
|
|
if (sv.state)
|
|
SV_UnspawnServer();
|
|
#endif
|
|
|
|
Alias_WipeStuffedAliaes();
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_User_f
|
|
|
|
user <name or userid>
|
|
|
|
Dump userdata / masterdata for a user
|
|
====================
|
|
*/
|
|
void CL_User_f (void)
|
|
{
|
|
int uid;
|
|
int i;
|
|
|
|
#ifndef CLIENTONLY
|
|
if (isDedicated)
|
|
{
|
|
SV_User_f();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_USER);
|
|
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_TPrintf (TLC_USER_NOUSER);
|
|
}
|
|
|
|
/*
|
|
====================
|
|
CL_Users_f
|
|
|
|
Dump userids for all current players
|
|
====================
|
|
*/
|
|
void CL_Users_f (void)
|
|
{
|
|
int i;
|
|
int c;
|
|
|
|
c = 0;
|
|
Con_TPrintf (TLC_USERBANNER);
|
|
Con_TPrintf (TLC_USERBANNER2);
|
|
for (i=0 ; i<MAX_CLIENTS ; i++)
|
|
{
|
|
if (cl.players[i].name[0])
|
|
{
|
|
Con_TPrintf (TLC_USERLINE, cl.players[i].userid, cl.players[i].frags, cl.players[i].name);
|
|
c++;
|
|
}
|
|
}
|
|
|
|
Con_TPrintf (TLC_USERTOTAL, c);
|
|
}
|
|
|
|
void CL_Color_f (void)
|
|
{
|
|
// just for quake compatability...
|
|
int top, bottom;
|
|
char num[16];
|
|
|
|
qboolean server_owns_colour;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_TPrintf (TLC_COLOURCURRENT,
|
|
Info_ValueForKey (cls.userinfo, "topcolor"),
|
|
Info_ValueForKey (cls.userinfo, "bottomcolor") );
|
|
Con_TPrintf (TLC_SYNTAX_COLOUR);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_FromServer())
|
|
server_owns_colour = true;
|
|
else
|
|
server_owns_colour = false;
|
|
|
|
|
|
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);
|
|
if (top == 0)
|
|
*num = '\0';
|
|
if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client
|
|
Cbuf_AddText(va("cmd %i setinfo topcolor %i\n", Cmd_ExecLevel-RESTRICT_SERVER-1, top), Cmd_ExecLevel);
|
|
// else if (server_owns_colour)
|
|
// Cvar_LockFromServer(&topcolor, num);
|
|
else
|
|
Cvar_Set (&topcolor, num);
|
|
sprintf (num, "%i", bottom);
|
|
if (bottom == 0)
|
|
*num = '\0';
|
|
if (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client
|
|
Cbuf_AddText(va("cmd %i setinfo bottomcolor %i\n", Cmd_ExecLevel-RESTRICT_SERVER-1, bottom), Cmd_ExecLevel);
|
|
else if (server_owns_colour)
|
|
Cvar_LockFromServer(&bottomcolor, num);
|
|
else
|
|
Cvar_Set (&bottomcolor, num);
|
|
#ifdef NQPROT
|
|
if (cls.netcon)
|
|
Cmd_ForwardToServer();
|
|
#endif
|
|
}
|
|
|
|
|
|
void CL_CheckServerInfo(void)
|
|
{
|
|
char *s;
|
|
unsigned int allowed;
|
|
|
|
cl.teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay"));
|
|
cl.deathmatch = atoi(Info_ValueForKey(cl.serverinfo, "deathmatch"));
|
|
|
|
cls.allow_cheats = false;
|
|
cls.allow_semicheats=true;
|
|
cls.allow_rearview=false;
|
|
cls.allow_watervis=false;
|
|
cls.allow_skyboxes=false;
|
|
cls.allow_mirrors=false;
|
|
cls.allow_shaders=false;
|
|
cls.allow_luma=true;
|
|
cls.allow_bump=false;
|
|
#ifdef FISH
|
|
cls.allow_fish=false;
|
|
#endif
|
|
// cls.allow_fbskins = 0;
|
|
// cls.allow_overbrightlight;
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "rearview")))
|
|
cls.allow_rearview=true;
|
|
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "watervis")))
|
|
cls.allow_watervis=true;
|
|
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_skybox")) || atoi(Info_ValueForKey(cl.serverinfo, "allow_skyboxes")))
|
|
cls.allow_skyboxes=true;
|
|
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "mirrors")))
|
|
cls.allow_mirrors=true;
|
|
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_shaders")))
|
|
cls.allow_shaders=true;
|
|
|
|
if (cls.demoplayback || !atoi(Info_ValueForKey(cl.serverinfo, "allow_luma")))
|
|
cls.allow_luma=false;
|
|
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_lmgamma")))
|
|
cls.allow_lightmapgamma=true;
|
|
|
|
s = Info_ValueForKey(cl.serverinfo, "allow_bump");
|
|
if (cls.demoplayback || atoi(s) || !*s) //admin doesn't care.
|
|
cls.allow_bump=true;
|
|
#ifdef FISH
|
|
if (cls.demoplayback || atoi(Info_ValueForKey(cl.serverinfo, "allow_fish")))
|
|
cls.allow_fish=true;
|
|
#endif
|
|
|
|
s = Info_ValueForKey(cl.serverinfo, "fbskins");
|
|
if (cls.demoplayback || *s)
|
|
cls.allow_fbskins = atof(s);
|
|
else
|
|
cls.allow_fbskins = 1;
|
|
|
|
s = Info_ValueForKey(cl.serverinfo, "*cheats");
|
|
if (cls.demoplayback || !stricmp(s, "on"))
|
|
cls.allow_cheats = true;
|
|
|
|
s = Info_ValueForKey(cl.serverinfo, "strict");
|
|
if (!cls.demoplayback && *s && strcmp(s, "0"))
|
|
{
|
|
cls.allow_semicheats = false;
|
|
cls.allow_cheats = false;
|
|
}
|
|
|
|
|
|
cls.maxfps = atof(Info_ValueForKey(cl.serverinfo, "maxfps"));
|
|
|
|
if (!atoi(Info_ValueForKey(cl.serverinfo, "deathmatch")))
|
|
cls.gamemode = GAME_COOP;
|
|
else
|
|
cls.gamemode = GAME_DEATHMATCH;
|
|
|
|
cls.z_ext = atoi(Info_ValueForKey(cl.serverinfo, "*z_ext"));
|
|
|
|
// movement vars for prediction
|
|
cl.bunnyspeedcap = Q_atof(Info_ValueForKey(cl.serverinfo, "pm_bunnyspeedcap"));
|
|
movevars.slidefix = (Q_atof(Info_ValueForKey(cl.serverinfo, "pm_slidefix")) != 0);
|
|
movevars.airstep = (Q_atof(Info_ValueForKey(cl.serverinfo, "pm_airstep")) != 0);
|
|
movevars.walljump = (Q_atof(Info_ValueForKey(cl.serverinfo, "pm_walljump")));
|
|
movevars.ktjump = Q_atof(Info_ValueForKey(cl.serverinfo, "pm_ktjump"));
|
|
|
|
// Initialize cl.maxpitch & cl.minpitch
|
|
s = (cls.z_ext & Z_EXT_PITCHLIMITS) ? Info_ValueForKey (cl.serverinfo, "maxpitch") : "";
|
|
cl.maxpitch = *s ? Q_atof(s) : 80.0f;
|
|
s = (cls.z_ext & Z_EXT_PITCHLIMITS) ? Info_ValueForKey (cl.serverinfo, "minpitch") : "";
|
|
cl.minpitch = *s ? Q_atof(s) : -70.0f;
|
|
|
|
allowed = atoi(Info_ValueForKey(cl.serverinfo, "allow"));
|
|
if (allowed & 1)
|
|
cls.allow_watervis = true;
|
|
if (allowed & 2)
|
|
cls.allow_rearview = true;
|
|
if (allowed & 4)
|
|
cls.allow_skyboxes = true;
|
|
if (allowed & 8)
|
|
cls.allow_mirrors = true;
|
|
if (allowed & 16)
|
|
cls.allow_shaders = true;
|
|
if (allowed & 32)
|
|
cls.allow_luma = true;
|
|
if (allowed & 64)
|
|
cls.allow_bump = true;
|
|
#ifdef FISH
|
|
if (allowed & 128)
|
|
cls.allow_fish = true;
|
|
#endif
|
|
if (allowed & 256)
|
|
cls.allow_lightmapgamma = true;
|
|
if (allowed & 512)
|
|
cls.allow_cheats = true;
|
|
|
|
if (cls.allow_semicheats)
|
|
cls.allow_anyparticles = true;
|
|
else
|
|
cls.allow_anyparticles = false;
|
|
|
|
Cvar_ForceCheatVars(cls.allow_semicheats, cls.allow_cheats);
|
|
|
|
}
|
|
/*
|
|
==================
|
|
CL_FullServerinfo_f
|
|
|
|
Sent by server when serverinfo changes
|
|
==================
|
|
*/
|
|
void CL_FullServerinfo_f (void)
|
|
{
|
|
char *p;
|
|
float v;
|
|
|
|
if (!Cmd_FromServer())
|
|
{
|
|
Con_Printf("Hey! fullserverinfo is meant to come from the server!\n");
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_FULLSERVERINFO);
|
|
return;
|
|
}
|
|
|
|
strcpy (cl.serverinfo, Cmd_Argv(1));
|
|
|
|
if ((p = Info_ValueForKey(cl.serverinfo, "*version")) && *p) {
|
|
v = Q_atof(p);
|
|
if (v) {
|
|
if (!server_version)
|
|
Con_TPrintf (TLC_SERVER_VERSION, v);
|
|
server_version = v;
|
|
}
|
|
}
|
|
CL_CheckServerInfo();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_FullInfo_f
|
|
|
|
Allow clients to change userinfo
|
|
==================
|
|
*/
|
|
void CL_FullInfo_f (void)
|
|
{
|
|
char key[512];
|
|
char value[512];
|
|
char *o;
|
|
char *s;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_FULLINFO);
|
|
return;
|
|
}
|
|
|
|
s = Cmd_Argv(1);
|
|
if (*s == '\\')
|
|
s++;
|
|
while (*s)
|
|
{
|
|
o = key;
|
|
while (*s && *s != '\\')
|
|
*o++ = *s++;
|
|
*o = 0;
|
|
|
|
if (!*s)
|
|
{
|
|
Con_TPrintf (TL_KEYHASNOVALUE);
|
|
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)
|
|
{
|
|
cvar_t *var;
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Info_Print (cls.userinfo);
|
|
return;
|
|
}
|
|
if (Cmd_Argc() != 3)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_SETINFO);
|
|
return;
|
|
}
|
|
if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name))
|
|
return;
|
|
|
|
var = Cvar_FindVar(Cmd_Argv(1));
|
|
if (var && (var->flags & CVAR_USERINFO))
|
|
{ //get the cvar code to set it. the server might have locked it.
|
|
Cvar_Set(var, Cmd_Argv(2));
|
|
return;
|
|
}
|
|
|
|
Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING);
|
|
if (cls.state >= ca_connected)
|
|
{
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
cls.resendinfo = true;
|
|
else
|
|
#endif
|
|
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_TPrintf (TLC_PACKET_SYNTAX);
|
|
return;
|
|
}
|
|
|
|
if (!NET_StringToAdr (Cmd_Argv(1), &adr))
|
|
{
|
|
Con_TPrintf (TLC_BADADDRESS);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_FromServer()) //some mvd servers stuffcmd a packet command which lets them know which ip the client is from.
|
|
{ //unfortunatly, 50% of servers are badly configured.
|
|
if (adr.type == NA_IP)
|
|
if (adr.ip[0] == 127)
|
|
if (adr.ip[1] == 0)
|
|
if (adr.ip[2] == 0)
|
|
if (adr.ip[3] == 1)
|
|
{
|
|
adr.ip[0] = cls.netchan.remote_address.ip[0];
|
|
adr.ip[1] = cls.netchan.remote_address.ip[1];
|
|
adr.ip[2] = cls.netchan.remote_address.ip[2];
|
|
adr.ip[3] = cls.netchan.remote_address.ip[3];
|
|
adr.port = cls.netchan.remote_address.port;
|
|
Con_Printf ("^b^1Server is broken. Ignoring 'realip' packet request\n");
|
|
|
|
}
|
|
|
|
Con_DPrintf ("Sending realip packet\n");
|
|
}
|
|
cls.lastarbiatarypackettime = Sys_DoubleTime(); //prevent the packet command from causing a reconnect on badly configured mvdsv servers.
|
|
|
|
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 (NS_CLIENT, 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, RESTRICT_LOCAL);
|
|
cls.demonum++;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
DEMO LOOP CONTROL
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_Startdemos_f
|
|
==================
|
|
*/
|
|
void CL_Startdemos_f (void)
|
|
{
|
|
int i, c;
|
|
|
|
c = Cmd_Argc() - 1;
|
|
if (c > MAX_DEMOS)
|
|
{
|
|
Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
|
|
c = MAX_DEMOS;
|
|
}
|
|
Con_Printf ("%i demo(s) in loop\n", c);
|
|
|
|
for (i=1 ; i<c+1 ; i++)
|
|
Q_strncpyz (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0]));
|
|
|
|
if (
|
|
#ifndef CLIENTONLY
|
|
!sv.state &&
|
|
#endif
|
|
cls.demonum != -1 && cls.demoplayback==DPB_NONE && !media_filmtype && COM_CheckParm("-demos"))
|
|
{
|
|
cls.demonum = 0;
|
|
CL_NextDemo ();
|
|
}
|
|
else
|
|
cls.demonum = -1;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_Demos_f
|
|
|
|
Return to looping demos
|
|
==================
|
|
*/
|
|
void CL_Demos_f (void)
|
|
{
|
|
if (cls.demonum == -1)
|
|
cls.demonum = 1;
|
|
CL_Disconnect_f ();
|
|
CL_NextDemo ();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_Stopdemo_f
|
|
|
|
stop demo
|
|
==================
|
|
*/
|
|
void CL_Stopdemo_f (void)
|
|
{
|
|
if (cls.demoplayback == DPB_NONE)
|
|
return;
|
|
CL_StopPlayback ();
|
|
CL_Disconnect ();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
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.downloadqw) // don't change when downloading
|
|
return;
|
|
|
|
SCR_BeginLoadingPlaque();
|
|
|
|
S_StopAllSounds (true);
|
|
cl.intermission = 0;
|
|
cls.state = ca_connected; // not active anymore, but not disconnected
|
|
Con_TPrintf (TLC_CHANGINGMAP);
|
|
|
|
#ifdef NQPROT
|
|
cls.signon=0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CL_Reconnect_f
|
|
|
|
The server is changing levels
|
|
=================
|
|
*/
|
|
void CL_Reconnect_f (void)
|
|
{
|
|
if (cls.downloadqw) // don't change when downloading
|
|
return;
|
|
#ifdef NQPROT
|
|
if (cls.netcon)
|
|
{
|
|
CL_Changing_f();
|
|
return;
|
|
}
|
|
#endif
|
|
S_StopAllSounds (true);
|
|
|
|
if (cls.state == ca_connected)
|
|
{
|
|
Con_TPrintf (TLC_RECONNECTING);
|
|
CL_SendClientCommand("new");
|
|
return;
|
|
}
|
|
|
|
if (!*cls.servername)
|
|
{
|
|
Con_TPrintf (TLC_RECONNECT_NOSERVER);
|
|
return;
|
|
}
|
|
|
|
CL_Disconnect();
|
|
CL_BeginServerReconnect();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 == DPB_NONE)
|
|
Con_TPrintf (TL_ST_COLON, NET_AdrToString (net_from));
|
|
// Con_DPrintf ("%s", net_message.data + 4);
|
|
|
|
if (c == S2C_CHALLENGE)
|
|
{
|
|
unsigned long pext = 0, huffcrc=0;
|
|
Con_TPrintf (TLC_S2C_CHALLENGE);
|
|
|
|
s = MSG_ReadString ();
|
|
COM_Parse(s);
|
|
if (!strcmp(com_token, "hallengeResponse"))
|
|
{
|
|
#ifdef Q3CLIENT
|
|
cls.q2server = 2;
|
|
cls.challenge = atoi(s+17);
|
|
CL_SendConnectPacket (0, 0/*, ...*/);
|
|
return;
|
|
#else
|
|
Con_Printf("\nUnable to connect to Quake3\n");
|
|
return;
|
|
#endif
|
|
}
|
|
else if (!strcmp(com_token, "hallenge"))
|
|
{
|
|
char *s2;
|
|
for (s2 = s+9; *s2; s2++)
|
|
{
|
|
if ((*s2 < '0' || *s2 > '9') && *s2 != '-')
|
|
break;
|
|
}
|
|
if (*s2)
|
|
{//and if it's not, we're unlikly to be compatable with whatever it is that's talking at us.
|
|
#ifdef NQPROT
|
|
CL_ConnectToDarkPlaces(s+9, net_from);
|
|
#else
|
|
Con_Printf("\nUnable connect to DarkPlaces\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
cls.q2server = true;
|
|
#else
|
|
Con_Printf("\nUnable to connect to Quake2\n");
|
|
#endif
|
|
s+=9;
|
|
}
|
|
#ifdef Q3CLIENT
|
|
else if (!strcmp(com_token, "onnectResponse"))
|
|
{
|
|
goto client_connect;
|
|
}
|
|
#endif
|
|
#ifdef Q2CLIENT
|
|
else if (!strcmp(com_token, "lient_connect"))
|
|
{
|
|
goto client_connect;
|
|
}
|
|
else
|
|
cls.q2server = false;
|
|
#endif
|
|
cls.challenge = atoi(s);
|
|
|
|
for(;;)
|
|
{
|
|
c = MSG_ReadLong ();
|
|
if (msg_badread)
|
|
break;
|
|
if (c == PROTOCOL_VERSION_FTE)
|
|
pext = MSG_ReadLong ();
|
|
#ifdef HUFFNETWORK
|
|
else if (c == (('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24)))
|
|
huffcrc = MSG_ReadLong ();
|
|
#endif
|
|
//else if (c == PROTOCOL_VERSION_...)
|
|
else
|
|
MSG_ReadLong ();
|
|
}
|
|
CL_SendConnectPacket (pext, huffcrc/*, ...*/);
|
|
return;
|
|
}
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
{
|
|
char *nl;
|
|
msg_readcount--;
|
|
c = msg_readcount;
|
|
s = MSG_ReadString ();
|
|
nl = strchr(s, '\n');
|
|
if (nl)
|
|
{
|
|
msg_readcount = c + nl-s + 1;
|
|
*nl = '\0';
|
|
}
|
|
|
|
if (!strcmp(s, "print"))
|
|
{
|
|
Con_TPrintf (TLC_A2C_PRINT);
|
|
|
|
s = MSG_ReadString ();
|
|
Con_Print (s);
|
|
return;
|
|
}
|
|
else if (!strcmp(s, "client_connect"))
|
|
{
|
|
goto client_connect;
|
|
}
|
|
else if (!strcmp(s, "disconnect"))
|
|
{
|
|
if (NET_CompareAdr(net_from, cls.netchan.remote_address))
|
|
{
|
|
Con_Printf ("disconnect\n");
|
|
CL_Disconnect_f();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Ignoring random disconnect command\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_TPrintf (TLC_Q2CONLESSPACKET_UNKNOWN, s);
|
|
msg_readcount = c;
|
|
c = MSG_ReadByte();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef NQPROT
|
|
if (c == 'a')
|
|
{
|
|
s = MSG_ReadString ();
|
|
COM_Parse(s);
|
|
if (!strcmp(com_token, "ccept"))
|
|
{
|
|
cls.netcon = Datagram_ConnectToDarkPlacesServer(&net_from);
|
|
|
|
SockadrToNetadr(&cls.netcon->addr, &net_from);
|
|
Netchan_Setup(cls.netcon->socket, &cls.netchan, net_from, cls.qport);
|
|
cls.netchan.qsocket = cls.netcon;
|
|
Con_DPrintf ("CL_EstablishConnection: connected to %s\n", cls.servername);
|
|
|
|
cls.netchan.qsocket = cls.netcon;
|
|
|
|
cls.netchan.isnqprotocol = true;
|
|
|
|
cls.demonum = -1; // not in the demo loop now
|
|
cls.state = ca_connected;
|
|
|
|
|
|
SCR_BeginLoadingPlaque();
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (c == 'd') //note - this conflicts with qw masters, our browser uses a different socket.
|
|
{
|
|
Con_Printf("Disconnect\n");
|
|
CL_Disconnect_f();
|
|
return;
|
|
}
|
|
|
|
if (c == S2C_CONNECTION)
|
|
{
|
|
int compress;
|
|
#ifdef Q2CLIENT
|
|
client_connect: //fixme: make function
|
|
#endif
|
|
Con_TPrintf (TLC_GOTCONNECTION);
|
|
if (cls.state >= ca_connected)
|
|
{
|
|
if (cls.demoplayback == DPB_NONE)
|
|
Con_TPrintf (TLC_DUPCONNECTION);
|
|
return;
|
|
}
|
|
compress = cls.netchan.compress;
|
|
Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.qport);
|
|
cls.netchan.compress = compress;
|
|
#ifdef NQPROT
|
|
cls.netchan.qsocket = cls.netcon;
|
|
#endif
|
|
CL_SendClientCommand("new");
|
|
cls.state = ca_connected;
|
|
Con_TPrintf (TLC_CONNECTED);
|
|
allowremotecmd = false; // localid required now for remote cmds
|
|
return;
|
|
}
|
|
// remote command from gui front end
|
|
if (c == A2C_CLIENT_COMMAND) //man I hate this.
|
|
{
|
|
char cmdtext[2048];
|
|
|
|
Con_TPrintf (TLC_CONLESS_CONCMD);
|
|
if (net_from.type != net_local_ipadr.type
|
|
|| ((*(unsigned *)net_from.ip != *(unsigned *)net_local_ipadr.ip) && (*(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK))))
|
|
{
|
|
Con_TPrintf (TLC_CMDFROMREMOTE);
|
|
return;
|
|
}
|
|
#ifdef _WIN32
|
|
ShowWindow (mainwindow, SW_RESTORE);
|
|
SetForegroundWindow (mainwindow);
|
|
#endif
|
|
s = MSG_ReadString ();
|
|
|
|
Q_strncpyz(cmdtext, s, sizeof(cmdtext));
|
|
|
|
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_TPrintf (TL_LINEBREAK_EQUALS);
|
|
Con_TPrintf (TLC_LOCALID_NOTSET);
|
|
Con_TPrintf (TL_LINEBREAK_EQUALS);
|
|
return;
|
|
}
|
|
Con_TPrintf (TL_LINEBREAK_EQUALS);
|
|
Con_TPrintf (TLC_LOCALID_BAD,
|
|
s, localid.string);
|
|
Con_TPrintf (TL_LINEBREAK_EQUALS);
|
|
Cvar_Set(&localid, "");
|
|
return;
|
|
}
|
|
|
|
Cbuf_AddText (cmdtext, RESTRICT_SERVER);
|
|
allowremotecmd = false;
|
|
return;
|
|
}
|
|
// print command from somewhere
|
|
if (c == 'p')
|
|
{
|
|
if (!strncmp(net_message.data+4, "print\n", 6))
|
|
{
|
|
Con_TPrintf (TLC_A2C_PRINT);
|
|
Con_Print (net_message.data+10);
|
|
return;
|
|
}
|
|
}
|
|
if (c == A2C_PRINT)
|
|
{
|
|
Con_TPrintf (TLC_A2C_PRINT);
|
|
|
|
s = MSG_ReadString ();
|
|
Con_Print (s);
|
|
return;
|
|
}
|
|
|
|
// ping from somewhere
|
|
if (c == A2A_PING)
|
|
{
|
|
char data[6];
|
|
|
|
Con_TPrintf (TLC_A2A_PING);
|
|
|
|
data[0] = 0xff;
|
|
data[1] = 0xff;
|
|
data[2] = 0xff;
|
|
data[3] = 0xff;
|
|
data[4] = A2A_ACK;
|
|
data[5] = 0;
|
|
|
|
NET_SendPacket (NS_CLIENT, 6, &data, net_from);
|
|
return;
|
|
}
|
|
|
|
//happens in demos
|
|
if (c == svc_disconnect && cls.demoplayback != DPB_NONE)
|
|
{
|
|
Host_EndGame ("End of Demo");
|
|
return;
|
|
}
|
|
|
|
Con_TPrintf (TLC_CONLESSPACKET_UNKNOWN, c);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CL_ReadPackets
|
|
=================
|
|
*/
|
|
void CL_ReadPackets (void)
|
|
{
|
|
// while (NET_GetPacket ())
|
|
while (CL_GetMessage())
|
|
{
|
|
#ifdef NQPROT
|
|
if (cls.demoplayback == DPB_NETQUAKE)
|
|
{
|
|
cls.netchan.last_received = realtime;
|
|
CLNQ_ParseServerMessage ();
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifdef Q2CLIENT
|
|
if (cls.demoplayback == DPB_QUAKE2)
|
|
{
|
|
MSG_BeginReading ();
|
|
cls.netchan.last_received = realtime;
|
|
CLQ2_ParseServerMessage ();
|
|
continue;
|
|
}
|
|
#endif
|
|
//
|
|
// remote command packet
|
|
//
|
|
if (*(int *)net_message.data == -1)
|
|
{
|
|
CL_ConnectionlessPacket ();
|
|
continue;
|
|
}
|
|
|
|
if (net_message.cursize < 8 && cls.demoplayback != DPB_MVD) //MVDs don't have the whole sequence header thing going on
|
|
{
|
|
Con_TPrintf (TL_RUNTPACKET,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 (cls.state == ca_disconnected)
|
|
continue; //ignore it. We arn't connected.
|
|
|
|
#ifdef Q3CLIENT
|
|
if (cls.q2server == 2)
|
|
{
|
|
CLQ3_ParseServerMessage();
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (cls.demoplayback == DPB_MVD)
|
|
MSG_BeginReading();
|
|
else if (!Netchan_Process(&cls.netchan))
|
|
continue; // wasn't accepted for some reason
|
|
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
CLQ2_ParseServerMessage ();
|
|
else
|
|
#endif
|
|
CL_ParseServerMessage ();
|
|
|
|
// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind())
|
|
// return;
|
|
}
|
|
#ifdef NQPROT
|
|
while(CLNQ_GetMessage()>0)
|
|
{
|
|
//allow qw protocol over nq transports
|
|
if (cls.netcon->qwprotocol)
|
|
{
|
|
if (*(int *)net_message.data == -1)
|
|
{
|
|
CL_ConnectionlessPacket ();
|
|
continue;
|
|
}
|
|
|
|
if (net_message.cursize < 8)
|
|
{
|
|
Con_TPrintf (TL_RUNTPACKET,NET_AdrToString(net_from));
|
|
continue;
|
|
}
|
|
|
|
if (cls.state == ca_disconnected)
|
|
continue; //ignore it. We arn't connected.
|
|
|
|
memcpy(&cls.netchan.remote_address, &net_from, sizeof(net_from));
|
|
if (!Netchan_Process(&cls.netchan))
|
|
continue; // wasn't accepted for some reason
|
|
// MSG_BeginReading();
|
|
// MSG_ReadLong();
|
|
// MSG_ReadLong();
|
|
|
|
#ifdef Q2CLIENT
|
|
if (cls.q2server)
|
|
CLQ2_ParseServerMessage ();
|
|
else
|
|
#endif
|
|
CL_ParseServerMessage ();
|
|
}
|
|
else
|
|
{
|
|
cls.netchan.last_received = realtime;
|
|
CLNQ_ParseServerMessage ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// check timeout
|
|
//
|
|
if (cls.state >= ca_connected
|
|
&& realtime - cls.netchan.last_received > cl_timeout.value)
|
|
{
|
|
Con_TPrintf (TLC_SERVERTIMEOUT);
|
|
CL_Disconnect ();
|
|
return;
|
|
}
|
|
|
|
if (cls.demoplayback == DPB_MVD)
|
|
MVD_Interpolate();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=====================
|
|
CL_Download_f
|
|
=====================
|
|
*/
|
|
void CL_Download_f (void)
|
|
{
|
|
// char *p, *q;
|
|
char *url;
|
|
|
|
url = Cmd_Argv(1);
|
|
|
|
#ifdef WEBCLIENT
|
|
if (!strnicmp(url, "http://", 7) || !strnicmp(url, "ftp://", 6))
|
|
{
|
|
if (Cmd_FromServer())
|
|
return;
|
|
HTTP_CL_Get(url, Cmd_Argv(2));//"test.txt");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (!strnicmp(url, "qw://", 5) || !strnicmp(url, "q2://", 5))
|
|
{
|
|
url += 5;
|
|
}
|
|
|
|
if (cls.state == ca_disconnected || cls.demoplayback)
|
|
{
|
|
Con_TPrintf (TLC_CONNECTFIRST);
|
|
return;
|
|
}
|
|
|
|
if (cls.netchan.remote_address.type == NA_LOOPBACK)
|
|
{
|
|
Con_TPrintf (TLC_CONNECTFIRST);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (TLC_SYNTAX_DOWNLOAD);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_FromServer()) //mark server specified downloads.
|
|
{
|
|
if (!strncmp(url, "game", 4) || !strcmp(url, "progs.dat") || !strcmp(url, "menu.dat") || !strcmp(url, "csqc.dat") || !strcmp(url, "qwprogs.dat") || strstr(url, "..") || strstr(url, ".dll") || strstr(url, ".so"))
|
|
{ //yes, I know the user can use a different progs from the one that is specified. If you leave it blank there will be no problem. (server isn't allowed to stuff progs cvar)
|
|
Con_Printf("Ignoring stuffed download of \"%s\" due to possible security risk\n", url);
|
|
return;
|
|
}
|
|
|
|
cls.downloadtype = dl_singlestuffed;
|
|
|
|
CL_CheckOrDownloadFile(url, false);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
cls.downloadtype = dl_single;
|
|
}
|
|
|
|
CL_EnqueDownload(url, true, false);
|
|
|
|
/*
|
|
strcpy(cls.downloadname, url);
|
|
|
|
_snprintf (cls.downloadname, sizeof(cls.downloadname), "%s/%s", com_gamedir, url);
|
|
|
|
p = cls.downloadname;
|
|
for (;;)
|
|
{
|
|
if ((q = strchr(p, '/')) != NULL)
|
|
{
|
|
*q = 0;
|
|
Sys_mkdir(cls.downloadname);
|
|
*q = '/';
|
|
p = q + 1;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
COM_StripExtension(cls.downloadname, cls.downloadtempname);
|
|
COM_DefaultExtension(cls.downloadtempname, ".tmp");
|
|
if (cls.down
|
|
// cls.downloadqw = fopen (cls.downloadname, "wb");
|
|
cls.downloadmethod = DL_QWPENDING;
|
|
|
|
|
|
CL_SendClientCommand("download %s\n",url);*/
|
|
}
|
|
|
|
#ifdef _WINDOWS
|
|
#include <windows.h>
|
|
/*
|
|
=================
|
|
CL_Minimize_f
|
|
=================
|
|
*/
|
|
void CL_Windows_f (void)
|
|
{
|
|
if (!mainwindow)
|
|
{
|
|
Con_Printf("Cannot comply\n");
|
|
return;
|
|
}
|
|
// if (modestate == MS_WINDOWED)
|
|
// ShowWindow(mainwindow, SW_MINIMIZE);
|
|
// else
|
|
SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29));
|
|
}
|
|
#endif
|
|
|
|
#ifndef CLIENTONLY
|
|
void CL_ServerInfo_f(void)
|
|
{
|
|
if (!sv.state && cls.state)
|
|
Cmd_ForwardToServer ();
|
|
else
|
|
{
|
|
SV_Serverinfo_f(); //allow it to be set... (whoops)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WEBCLIENT
|
|
void CL_FTP_f(void)
|
|
{
|
|
FTP_Client_Command(Cmd_Args());
|
|
}
|
|
#endif
|
|
#ifdef IRCCLIENT
|
|
void CL_IRC_f(void)
|
|
{
|
|
IRC_Command(Cmd_Args());
|
|
}
|
|
#endif
|
|
#ifdef EMAILCLIENT
|
|
void CL_IMAPPoll_f(void)
|
|
{
|
|
IMAP_CreateConnection(Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3));
|
|
}
|
|
|
|
void CL_POP3Poll_f(void)
|
|
{
|
|
POP3_CreateConnection(Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3));
|
|
}
|
|
#endif
|
|
|
|
void SCR_ShowPic_Script_f(void);
|
|
/*
|
|
=================
|
|
CL_Init
|
|
=================
|
|
*/
|
|
void CL_Init (void)
|
|
{
|
|
extern void CL_Say_f (void);
|
|
extern void CL_SayMe_f (void);
|
|
extern void CL_SayTeam_f (void);
|
|
extern cvar_t baseskin;
|
|
extern cvar_t noskins;
|
|
char st[80];
|
|
|
|
cls.state = ca_disconnected;
|
|
|
|
#ifdef VERSION3PART
|
|
sprintf (st, "%s %4.3f-%04d", DISTRIBUTION, VERSION, build_number());
|
|
#else
|
|
sprintf (st, "%s %4.2f-%04d", DISTRIBUTION, VERSION, build_number());
|
|
#endif
|
|
sprintf (st, "%s", DISTRIBUTION);
|
|
Info_SetValueForStarKey (cls.userinfo, "*ver", st, MAX_INFO_STRING);
|
|
|
|
InitValidation();
|
|
#ifdef CLPROGS
|
|
CLPR_Init();
|
|
#endif
|
|
|
|
CL_InitInput ();
|
|
CL_InitTEnts ();
|
|
CL_InitPrediction ();
|
|
CL_InitCam ();
|
|
PM_Init ();
|
|
TP_Init();
|
|
|
|
//
|
|
// register our commands
|
|
//
|
|
CLSCR_Init();
|
|
|
|
Cvar_Register (&host_speeds, cl_controlgroup);
|
|
Cvar_Register (&developer, cl_controlgroup);
|
|
|
|
Cvar_Register (&cfg_save_name, cl_controlgroup);
|
|
|
|
Cvar_Register (&cl_demospeed, "Demo playback");
|
|
Cvar_Register (&cl_warncmd, "Warnings");
|
|
Cvar_Register (&cl_upspeed, cl_inputgroup);
|
|
Cvar_Register (&cl_forwardspeed, cl_inputgroup);
|
|
Cvar_Register (&cl_backspeed, cl_inputgroup);
|
|
Cvar_Register (&cl_sidespeed, cl_inputgroup);
|
|
Cvar_Register (&cl_movespeedkey, cl_inputgroup);
|
|
Cvar_Register (&cl_yawspeed, cl_inputgroup);
|
|
Cvar_Register (&cl_pitchspeed, cl_inputgroup);
|
|
Cvar_Register (&cl_anglespeedkey, cl_inputgroup);
|
|
Cvar_Register (&cl_shownet, cl_screengroup);
|
|
Cvar_Register (&cl_sbar, cl_screengroup);
|
|
Cvar_Register (&cl_hudswap, cl_screengroup);
|
|
Cvar_Register (&cl_maxfps, cl_screengroup);
|
|
Cvar_Register (&cl_timeout, cl_controlgroup);
|
|
Cvar_Register (&lookspring, cl_inputgroup);
|
|
Cvar_Register (&lookstrafe, cl_inputgroup);
|
|
Cvar_Register (&sensitivity, cl_inputgroup);
|
|
|
|
Cvar_Register (&m_pitch, cl_inputgroup);
|
|
Cvar_Register (&m_yaw, cl_inputgroup);
|
|
Cvar_Register (&m_forward, cl_inputgroup);
|
|
Cvar_Register (&m_side, cl_inputgroup);
|
|
|
|
Cvar_Register (&rcon_password, cl_controlgroup);
|
|
Cvar_Register (&rcon_address, cl_controlgroup);
|
|
|
|
Cvar_Register (&entlatency, cl_predictiongroup);
|
|
Cvar_Register (&cl_predict_players2, cl_predictiongroup);
|
|
Cvar_Register (&cl_predict_players, cl_predictiongroup);
|
|
Cvar_Register (&cl_solid_players, cl_predictiongroup);
|
|
|
|
Cvar_Register (&localid, cl_controlgroup);
|
|
|
|
Cvar_Register (&cl_muzzleflash, cl_controlgroup);
|
|
|
|
Cvar_Register (&baseskin, "Teamplay");
|
|
Cvar_Register (&noskins, "Teamplay");
|
|
|
|
Cvar_Register (&cl_item_bobbing, "Item effects");
|
|
|
|
Cvar_Register (&cl_staticsounds, "Item effects");
|
|
|
|
Cvar_Register (&r_rocketlight, "Item effects");
|
|
Cvar_Register (&r_lightflicker, "Item effects");
|
|
Cvar_Register (&cl_r2g, "Item effects");
|
|
Cvar_Register (&r_powerupglow, "Item effects");
|
|
|
|
Cvar_Register (&cl_gibfilter, "Item effects");
|
|
Cvar_Register (&cl_deadbodyfilter, "Item effects");
|
|
|
|
//
|
|
// info mirrors
|
|
//
|
|
Cvar_Register (&name, cl_controlgroup);
|
|
Cvar_Register (&password, cl_controlgroup);
|
|
Cvar_Register (&spectator, cl_controlgroup);
|
|
Cvar_Register (&skin, cl_controlgroup);
|
|
Cvar_Register (&model, cl_controlgroup);
|
|
Cvar_Register (&team, cl_controlgroup);
|
|
Cvar_Register (&topcolor, cl_controlgroup);
|
|
Cvar_Register (&bottomcolor, cl_controlgroup);
|
|
Cvar_Register (&rate, cl_controlgroup);
|
|
Cvar_Register (&drate, cl_controlgroup);
|
|
Cvar_Register (&msg, cl_controlgroup);
|
|
Cvar_Register (&noaim, cl_controlgroup);
|
|
|
|
Cvar_Register (&cl_nofake, cl_controlgroup);
|
|
Cvar_Register (&cl_chatsound, cl_controlgroup);
|
|
|
|
Cvar_Register (&requiredownloads, cl_controlgroup);
|
|
Cvar_Register (&cl_standardchat, cl_controlgroup);
|
|
Cvar_Register (&cl_nopext, cl_controlgroup);
|
|
Cvar_Register (&cl_splitscreen, cl_controlgroup);
|
|
|
|
Cvar_Register (&host_mapname, "Scripting");
|
|
|
|
#ifdef IRCCLIENT
|
|
Cmd_AddCommand ("irc", CL_IRC_f);
|
|
#endif
|
|
#ifdef WEBCLIENT
|
|
Cmd_AddCommand ("ftp", CL_FTP_f);
|
|
#endif
|
|
|
|
#ifdef EMAILCLIENT
|
|
Cvar_Register (&imap_checkfrequency, "Email notifications");
|
|
Cmd_AddCommand ("imapaccount", CL_IMAPPoll_f);
|
|
|
|
Cvar_Register (&pop3_checkfrequency, "Email notifications");
|
|
Cmd_AddCommand ("pop3account", CL_POP3Poll_f);
|
|
#endif
|
|
|
|
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 ("showpic", SCR_ShowPic_Script_f);
|
|
|
|
Cmd_AddCommand ("startdemos", CL_Startdemos_f);
|
|
Cmd_AddCommand ("demos", CL_Demos_f);
|
|
Cmd_AddCommand ("stopdemo", CL_Stopdemo_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);
|
|
#ifdef NQPROT
|
|
Cmd_AddCommand ("nqconnect", CLNQ_Connect_f);
|
|
#endif
|
|
Cmd_AddCommand ("reconnect", CL_Reconnect_f);
|
|
Cmd_AddCommand ("join", CL_Join_f);
|
|
Cmd_AddCommand ("observe", CL_Observe_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 ("god", NULL); //cheats
|
|
Cmd_AddCommand ("give", NULL);
|
|
Cmd_AddCommand ("noclip", NULL);
|
|
Cmd_AddCommand ("fly", NULL);
|
|
Cmd_AddCommand ("setpos", NULL);
|
|
|
|
Cmd_AddCommand ("topten", NULL);
|
|
|
|
Cmd_AddCommand ("kill", NULL);
|
|
Cmd_AddCommand ("pause", NULL);
|
|
Cmd_AddCommand ("say", CL_Say_f);
|
|
Cmd_AddCommand ("me", CL_SayMe_f);
|
|
Cmd_AddCommand ("sayone", CL_Say_f);
|
|
Cmd_AddCommand ("say_team", CL_SayTeam_f);
|
|
#ifdef CLIENTONLY
|
|
Cmd_AddCommand ("serverinfo", NULL);
|
|
#else
|
|
Cmd_AddCommand ("serverinfo", CL_ServerInfo_f);
|
|
#endif
|
|
|
|
//
|
|
// 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 VARGS Host_EndGame (char *message, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
SCR_EndLoadingPlaque();
|
|
|
|
va_start (argptr,message);
|
|
_vsnprintf (string,sizeof(string)-1, message,argptr);
|
|
va_end (argptr);
|
|
Con_TPrintf (TL_NL);
|
|
Con_TPrintf (TL_LINEBREAK_EQUALS);
|
|
Con_TPrintf (TLC_CLIENTCON_ERROR_ENDGAME,string);
|
|
Con_TPrintf (TL_LINEBREAK_EQUALS);
|
|
Con_TPrintf (TL_NL);
|
|
|
|
SCR_EndLoadingPlaque();
|
|
|
|
CL_Disconnect ();
|
|
|
|
Cvar_Set(&cl_shownet, "0");
|
|
|
|
longjmp (host_abort, 1);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Host_Error
|
|
|
|
This shuts down the client and exits qwcl
|
|
================
|
|
*/
|
|
void VARGS 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);
|
|
_vsnprintf (string,sizeof(string)-1, error,argptr);
|
|
va_end (argptr);
|
|
Con_TPrintf (TLC_HOSTFATALERROR,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 && cfg_save_name.string && *cfg_save_name.string)
|
|
{
|
|
if (strchr(cfg_save_name.string, '.'))
|
|
{
|
|
Con_TPrintf (TLC_CONFIGCFG_WRITEFAILED);
|
|
return;
|
|
}
|
|
|
|
f = fopen (va("%s/%s.cfg",com_gamedir, cfg_save_name.string), "w");
|
|
if (!f)
|
|
{
|
|
Con_TPrintf (TLC_CONFIGCFG_WRITEFAILED);
|
|
return;
|
|
}
|
|
|
|
Key_WriteBindings (f);
|
|
Cvar_WriteVariables (f, false);
|
|
|
|
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
|
|
==================
|
|
*/
|
|
#if defined(WINAVI) && !defined(NOMEDIA)
|
|
extern float recordavi_frametime;
|
|
extern qboolean recordingdemo;
|
|
#endif
|
|
|
|
extern cvar_t cl_netfps;
|
|
int nopacketcount;
|
|
void SNDDMA_SetUnderWater(qboolean underwater);
|
|
qboolean CL_FilterTime (double time, float wantfps);
|
|
void Host_Frame (float time)
|
|
{
|
|
static double time1 = 0;
|
|
static double time2 = 0;
|
|
static double time3 = 0;
|
|
int pass1, pass2, pass3;
|
|
// float fps;
|
|
|
|
RSpeedLocals();
|
|
|
|
if (setjmp (host_abort) )
|
|
return; // something bad happened, or the server disconnected
|
|
|
|
#if defined(WINAVI) && !defined(NOMEDIA)
|
|
if (cls.demoplayback && recordingdemo && recordavi_frametime>0.01)
|
|
time = recordavi_frametime;
|
|
#endif
|
|
|
|
if (cls.demoplayback && cl_demospeed.value>0)
|
|
time *= cl_demospeed.value;
|
|
|
|
#ifndef CLIENTONLY
|
|
RSpeedRemark();
|
|
SV_Frame(time);
|
|
RSpeedEnd(RSPEED_SERVER);
|
|
#endif
|
|
|
|
#ifdef WEBCLIENT
|
|
FTP_ClientThink();
|
|
HTTP_CL_Think();
|
|
#endif
|
|
#ifdef IRCCLIENT
|
|
IRC_Frame();
|
|
#endif
|
|
#ifdef EMAILCLIENT
|
|
IMAP_Think();
|
|
POP3_Think();
|
|
#endif
|
|
|
|
#ifdef PLUGINS
|
|
Plug_Tick();
|
|
#endif
|
|
|
|
// decide the simulation time
|
|
realtime += time;
|
|
if (oldrealtime > realtime)
|
|
oldrealtime = 0;
|
|
|
|
if (cl.paused)
|
|
cl.gametimemark += time;
|
|
|
|
|
|
#if defined(NQPROT) || defined(Q2CLIENT)
|
|
#if defined(NQPROT) && defined(Q2CLIENT)
|
|
if (cls.q2server || cls.demoplayback == DPB_NETQUAKE)
|
|
#elif defined(NQPROT)
|
|
if (cls.demoplayback == DPB_NETQUAKE)
|
|
#elif defined(Q2CLIENT)
|
|
if (cls.q2server)
|
|
#endif
|
|
cl.time += time;
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef VOICECHAT
|
|
CLVC_Poll();
|
|
#endif
|
|
|
|
/*
|
|
if (cl_maxfps.value)
|
|
fps = cl_maxfps.value;//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
|
|
|
|
*/
|
|
Mod_Think(); //think even on idle (which means small walls and a fast cpu can get more surfaces done.
|
|
if (cl_maxfps.value>0 && cl_netfps.value>0)
|
|
{ //limit the fps freely, and expect the netfps to cope.
|
|
if ((realtime - oldrealtime) < 1/cl_maxfps.value)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (!CL_FilterTime((realtime - oldrealtime)*1000, cl_maxfps.value>0?cl_maxfps.value:cl_netfps.value))
|
|
return;
|
|
}
|
|
|
|
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 ();
|
|
|
|
#ifndef CLIENTONLY
|
|
if (isDedicated) //someone changed it.
|
|
return;
|
|
#endif
|
|
|
|
cls.framecount++;
|
|
|
|
#ifdef NQPROT
|
|
NET_Poll();
|
|
#endif
|
|
|
|
|
|
RSpeedRemark();
|
|
|
|
CL_AllowIndependantSendCmd(false);
|
|
|
|
if (cls.downloadtype == dl_none && !*cls.downloadname && cl.downloadlist)
|
|
CL_RequestNextDownload();
|
|
|
|
// fetch results from server
|
|
CL_ReadPackets ();
|
|
|
|
CL_AllowIndependantSendCmd(true);
|
|
|
|
// send intentions now
|
|
// resend a connection request if necessary
|
|
if (cls.state == ca_disconnected)
|
|
{
|
|
IN_Move(NULL, 0);
|
|
CL_CheckForResend ();
|
|
}
|
|
else
|
|
{
|
|
CL_SendCmd ();
|
|
|
|
if (cls.state == ca_onserver && cl.validsequence && cl.worldmodel)
|
|
{ // first update is the final signon stage
|
|
cls.state = ca_active;
|
|
if (VID_SetWindowCaption)
|
|
VID_SetWindowCaption(va("FTE QuakeWorld: %s", cls.servername));
|
|
|
|
SCR_EndLoadingPlaque();
|
|
}
|
|
}
|
|
|
|
RSpeedEnd(RSPEED_CLIENT);
|
|
|
|
// update video
|
|
if (host_speeds.value)
|
|
time1 = Sys_DoubleTime ();
|
|
|
|
TP_CheckVars();
|
|
if (SCR_UpdateScreen)
|
|
{
|
|
extern mleaf_t *r_viewleaf;
|
|
SCR_UpdateScreen ();
|
|
if (cls.state >= ca_active && r_viewleaf)
|
|
SNDDMA_SetUnderWater(r_viewleaf->contents <= Q1CONTENTS_WATER);
|
|
else
|
|
SNDDMA_SetUnderWater(false);
|
|
}
|
|
|
|
if (host_speeds.value)
|
|
time2 = Sys_DoubleTime ();
|
|
|
|
// update audio
|
|
if (cls.state == ca_active)
|
|
{
|
|
S_Update (r_origin, vpn, vright, vup);
|
|
}
|
|
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_TPrintf (TLC_HOSTSPEEDSOUTPUT,
|
|
pass1+pass2+pass3, pass1, pass2, pass3);
|
|
}
|
|
|
|
host_framecount++;
|
|
fps_count++;
|
|
|
|
IN_Commands ();
|
|
|
|
// process console commands
|
|
Cbuf_Execute ();
|
|
|
|
#ifdef NQPROT
|
|
NET_Poll();
|
|
#endif
|
|
|
|
if (cls.downloadtype == dl_none && !*cls.downloadname && cl.downloadlist)
|
|
{
|
|
CL_RequestNextDownload();
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
int i;
|
|
int qrc, hrc, def;
|
|
|
|
extern cvar_t vid_renderer;
|
|
COM_InitArgv (parms->argc, parms->argv);
|
|
|
|
if (setjmp (host_abort) )
|
|
Sys_Error("Host_Init: An error occured. Try the -condebug commandline parameter\n");
|
|
|
|
|
|
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);
|
|
|
|
COM_ParsePlusSets();
|
|
|
|
Cbuf_Init ();
|
|
Cmd_Init ();
|
|
|
|
V_Init ();
|
|
|
|
COM_Init ();
|
|
#ifdef Q2BSPS
|
|
CM_Init();
|
|
#endif
|
|
|
|
Host_FixupModelNames();
|
|
|
|
|
|
NET_Init ();
|
|
NET_InitClient ();
|
|
|
|
Netchan_Init ();
|
|
|
|
Renderer_Init();
|
|
|
|
// W_LoadWadFile ("gfx.wad");
|
|
Key_Init ();
|
|
Con_Init ();
|
|
M_Init ();
|
|
|
|
#ifdef __linux__
|
|
IN_Init ();
|
|
CDAudio_Init ();
|
|
// VID_Init (host_basepal);
|
|
// Draw_Init ();
|
|
// SCR_Init ();
|
|
// R_Init ();
|
|
|
|
S_Init ();
|
|
|
|
cls.state = ca_disconnected;
|
|
Sbar_Init ();
|
|
CL_Init ();
|
|
#else
|
|
S_Init ();
|
|
|
|
cls.state = ca_disconnected;
|
|
CDAudio_Init ();
|
|
Sbar_Init ();
|
|
CL_Init ();
|
|
IN_Init ();
|
|
#endif
|
|
TranslateInit();
|
|
#ifndef CLIENTONLY
|
|
SV_Init(parms);
|
|
#endif
|
|
#ifdef TEXTEDITOR
|
|
Editor_Init();
|
|
#endif
|
|
|
|
#ifdef PLUGINS
|
|
Plug_Init();
|
|
#endif
|
|
|
|
// Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
|
|
Con_TPrintf (TL_HEAPSIZE, parms->memsize/ (1024*1024.0));
|
|
|
|
Cbuf_AddText ("+mlook\n", RESTRICT_LOCAL); //fixme: this is bulky, only exec one of these.
|
|
|
|
//who should we imitate?
|
|
qrc = COM_FDepthFile("quake.rc", true); //q1
|
|
hrc = COM_FDepthFile("hexen.rc", true); //h2
|
|
def = COM_FDepthFile("default.cfg", true); //q2/q3
|
|
|
|
if (qrc >= def && qrc >= hrc && qrc!=0x7fffffff)
|
|
Cbuf_AddText ("exec quake.rc\n", RESTRICT_LOCAL);
|
|
else if (hrc >= def && hrc!=0x7fffffff)
|
|
Cbuf_AddText ("exec hexen.rc\n", RESTRICT_LOCAL);
|
|
else
|
|
{ //they didn't give us an rc file!
|
|
Cbuf_AddText ("exec default.cfg\n", RESTRICT_LOCAL);
|
|
Cbuf_AddText ("exec config.cfg\n", RESTRICT_LOCAL);
|
|
Cbuf_AddText ("exec autoexec.cfg\n", RESTRICT_LOCAL);
|
|
}
|
|
Cbuf_AddText ("exec fte.cfg\n", RESTRICT_LOCAL);
|
|
Cbuf_AddText ("cl_warncmd 1\n", RESTRICT_LOCAL); //and then it's allowed to start moaning.
|
|
|
|
Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
|
|
host_hunklevel = Hunk_LowMark ();
|
|
|
|
R_SetRenderer(0);//set the renderer stuff to 'none'...
|
|
|
|
host_initialized = true;
|
|
|
|
Cmd_StuffCmds();
|
|
|
|
Cbuf_Execute (); //if the server initialisation causes a problem, give it a place to abort to
|
|
|
|
//assuming they didn't use any waits in thier config (fools)
|
|
//the configs should be fully loaded.
|
|
//so convert the backwards compable commandline parameters in cvar sets.
|
|
|
|
if (COM_CheckParm ("-window") || COM_CheckParm ("-startwindowed"))
|
|
Cvar_Set(Cvar_FindVar("vid_fullscreen"), "0");
|
|
if (COM_CheckParm ("-fullscreen"))
|
|
Cvar_Set(Cvar_FindVar("vid_fullscreen"), "1");
|
|
|
|
if ((i = COM_CheckParm ("-width"))) //width on it's own also sets height
|
|
{
|
|
Cvar_Set(Cvar_FindVar("vid_width"), com_argv[i+1]);
|
|
Cvar_SetValue(Cvar_FindVar("vid_height"), (atoi(com_argv[i+1])/3)*4);
|
|
}
|
|
if ((i = COM_CheckParm ("-height")))
|
|
Cvar_Set(Cvar_FindVar("vid_height"), com_argv[i+1]);
|
|
|
|
if ((i = COM_CheckParm ("-conwidth"))) //width on it's own also sets height
|
|
{
|
|
Cvar_Set(Cvar_FindVar("vid_conwidth"), com_argv[i+1]);
|
|
Cvar_SetValue(Cvar_FindVar("vid_conheight"), (atoi(com_argv[i+1])/3)*4);
|
|
}
|
|
if ((i = COM_CheckParm ("-conheight")))
|
|
Cvar_Set(Cvar_FindVar("vid_conheight"), com_argv[i+1]);
|
|
|
|
if ((i = COM_CheckParm ("-bpp")))
|
|
Cvar_Set(Cvar_FindVar("vid_bpp"), com_argv[i+1]);
|
|
|
|
if (!qrenderer && *vid_renderer.string)
|
|
{
|
|
Cmd_ExecuteString("vid_restart\n", RESTRICT_LOCAL);
|
|
}
|
|
|
|
if (!qrenderer)
|
|
{
|
|
Cmd_ExecuteString("vid_restart\n", RESTRICT_LOCAL);
|
|
}
|
|
|
|
UI_Init();
|
|
|
|
#ifndef NOMEDIA
|
|
if (!cls.demofile && !cls.state && !media_filmtype)
|
|
{
|
|
if (COM_FDepthFile("video/idlogo.roq", true) > COM_FDepthFile("video/idlog.cin", true))
|
|
Media_PlayFilm("video/idlog.cin");
|
|
else
|
|
Media_PlayFilm("video/idlogo.roq");
|
|
}
|
|
#endif
|
|
|
|
Con_TPrintf (TL_NL);
|
|
#ifdef VERSION3PART
|
|
Con_TPrintf (TCL_VERSION3, VERSION, build_number());
|
|
#else
|
|
Con_TPrintf (TLC_VERSION2, VERSION, build_number());
|
|
#endif
|
|
Con_TPrintf (TL_NL);
|
|
|
|
Con_TPrintf (TLC_QUAKEWORLD_INITED);
|
|
|
|
Con_DPrintf("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."
|
|
"\n"
|
|
"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. "
|
|
"\n"
|
|
"See the GNU General Public License for more details.\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)
|
|
{
|
|
Sys_Printf ("recursive shutdown\n");
|
|
return;
|
|
}
|
|
isdown = true;
|
|
|
|
UI_Stop();
|
|
|
|
Host_WriteConfiguration ();
|
|
|
|
CDAudio_Shutdown ();
|
|
S_Shutdown();
|
|
IN_Shutdown ();
|
|
if (VID_DeInit)
|
|
VID_DeInit();
|
|
#ifndef CLIENTONLY
|
|
SV_Shutdown();
|
|
#else
|
|
NET_Shutdown ();
|
|
#endif
|
|
|
|
Cvar_Shutdown();
|
|
Validation_FlushFileList();
|
|
}
|
|
|
|
#ifdef CLIENTONLY
|
|
void SV_EndRedirect (void)
|
|
{
|
|
}
|
|
#endif
|