ngunix/engine/cl_parse.c

1692 lines
41 KiB
C

/*
Copyright (C) 2015 Marco "eukara" Hladik
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_parse.c -- parse a message received from the server
#include "globaldef.h"
extern int lightingavailable = 0; // leilei - point lighting, determines if our data is made available
extern int lightingcantbeavailable = 0;
void LoadPointLighting (char *entstring);
char *svc_strings[] =
{
"svc_bad",
"svc_nop",
"svc_disconnect",
"svc_updatestat",
"svc_version", // [long] server version
"svc_setview", // [short] entity number
"svc_sound", // <see code>
"svc_time", // [float] server time
"svc_print", // [string] null terminated string
"svc_stufftext", // [string] stuffed into client's console buffer
// the string should be \n terminated
"svc_setangle", // [vec3] set the view angle to this absolute value
"svc_serverinfo", // [long] version
// [string] signon string
// [string]..[0]model cache [string]...[0]sounds cache
// [string]..[0]item cache
"svc_lightstyle", // [byte] [string]
"svc_updatename", // [byte] [string]
"svc_updatefrags", // [byte] [short]
"svc_clientdata", // <shortbits + data>
"svc_stopsound", // <see code>
"svc_updatecolors", // [byte] [byte]
"svc_particle", // [vec3] <variable>
"svc_damage", // [byte] impact [byte] blood [vec3] from
"svc_spawnstatic",
"OBSOLETE svc_spawnbinary",
"svc_spawnbaseline",
"svc_temp_entity", // <variable>
"svc_setpause",
"svc_signonnum",
"svc_centerprint",
"svc_killedmonster",
"svc_foundsecret",
"svc_spawnstaticsound",
"svc_intermission",
"svc_finale", // [string] music [string] text
"svc_cdtrack", // [byte] track [byte] looptrack
"svc_sellscreen",
"svc_cutscene", // 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes
// added commata
// leilei
"svc 35", "svc 36", "svc 37", "svc 38", "svc 39",
"svc 40", "svc 41", "svc 42", "svc 43", "svc 44", "svc 45", "svc 46", "svc 47", "svc 48", "svc 49",
"svc 50", "svc 51", "svc 52", "svc 53", "svc 54", "svc 55", "svc 56", "svc 57", "svc 58", "svc 59",
"svc 60", "svc 61", "svc 62", "svc 63", "svc 64", "svc 65", "svc 66", "svc 67", "svc 68", "svc 69",
"svc 70", "svc 71", "svc 72", "svc 73", "svc 74", "svc 75", "svc 76", "svc 77", "svc 78", "svc 79",
"svc 80", "svc_sound3", "svc 82", "svc 83", "svc 84", "svc 85", "svc 86", "svc 87", "svc 88", "svc 89",
"svc 90", "svc 91", "svc 92", "svc 93", "svc 94", "svc 95", "svc 96", "svc 97", "svc 98", "svc 99",
"svc 100", "svc 101", "svc 102", "svc 103", "svc 104", "svc 105", "svc 106", "svc 107", "svc 108", "svc 109",
"svc 110", "svc 111", "svc 112", "svc 113", "svc 114", "svc 115", "svc 116", "svc 117", "svc 118", "svc 119",
"svc 120", "svc 121", "svc 122", "svc 123", "svc 124", "svc 125",
"svc_limit", // 2001-09-20 Configurable limits by Maddes
// <variable>
"svc_extra_version", // 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes
// [float] SSVC [float] CSVC [float] CCLC
};
//=============================================================================
/*
===============
CL_EntityNum
This error checks and tracks the total number of entities
===============
*/
entity_t *CL_EntityNum (int num)
{
// 2001-09-20 Configurable entity limits by Maddes start
if (!cl_entities)
{
Cvar_Set(cl_entities_min, cl_entities_min->string); // do rangecheck
if (cl.max_edicts < cl_entities_min->value)
{
cl.max_edicts = cl_entities_min->value;
}
Con_DPrintf("[ENGINE] Allocating memory for %i entities.\n", cl.max_edicts);
cl_entities = Hunk_AllocName (cl.max_edicts*sizeof(entity_t), "cl_edicts");
memset (cl_entities, 0, cl.max_edicts*sizeof(entity_t));
cl_entities[0].model = cl.worldmodel;
}
// 2001-09-20 Configurable entity limits by Maddes end
if (num >= cl.num_entities)
{
// 2001-09-20 Configurable entity limits by Maddes start
// if (num >= MAX_EDICTS)
if (num >= cl.max_edicts)
// 2001-09-20 Configurable entity limits by Maddes end
Host_Error ("%i is an invalid entity number",num);
while (cl.num_entities<=num)
{
cl_entities[cl.num_entities].colormap = vid.colormap;
cl.num_entities++;
}
}
return &cl_entities[num];
}
/*
==================
CL_ParseStartSoundPacket
==================
*/
void CL_ParseStartSoundPacket(void)
{
vec3_t pos;
int channel, ent;
int sound_num;
int volume;
int field_mask;
float attenuation;
int i;
field_mask = MSG_ReadByte();
if (field_mask & SND_VOLUME)
volume = MSG_ReadByte ();
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if (field_mask & SND_ATTENUATION)
attenuation = MSG_ReadByte () / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
channel = MSG_ReadShort ();
sound_num = MSG_ReadByte ();
ent = channel >> 3;
channel &= 7;
// 2001-09-20 Configurable entity limits by Maddes start
// if (ent > MAX_EDICTS)
if (ent > cl.max_edicts)
// 2001-09-20 Configurable entity limits by Maddes end
Host_Error ("Parse Sound Packet ent = %i > %i", ent, cl.max_edicts);
for (i=0 ; i<3 ; i++)
pos[i] = MSG_ReadCoord ();
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
}
/*
==================
CL_ParseStartSoundPacket2
==================
*/
void CL_ParseStartSoundPacket2(void)
{
vec3_t pos;
int channel, ent;
int sound_num;
int volume;
int field_mask;
float attenuation;
int pitch;
int i;
field_mask = MSG_ReadByte();
if (field_mask & SND_VOLUME)
volume = MSG_ReadByte ();
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if (field_mask & SND_ATTENUATION)
attenuation = MSG_ReadByte () / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
channel = MSG_ReadShort ();
sound_num = MSG_ReadByte ();
ent = channel >> 3;
channel &= 7;
if (field_mask & SND_PITCH)
pitch = MSG_ReadByte ();
else
pitch = 100;
if (pitch < 1)
pitch = 1;
if (pitch > 255)
pitch = 255;
if (ent > cl.max_edicts)
Host_Error ("Parse Sound Packet ent = %i > %i", ent, cl.max_edicts);
for (i=0 ; i<3 ; i++)
pos[i] = MSG_ReadCoord ();
S_StartSound2 (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation, pitch);
}
/*
==================
CL_KeepaliveMessage
When the client is taking a long time to load stuff, send keepalive messages
so the server doesn't disconnect.
==================
*/
void CL_KeepaliveMessage (void)
{
float time;
static float lastmsg;
int ret;
sizebuf_t old;
byte olddata[8192];
if (sv.active)
return; // no need if server is local
if (cls.demoplayback)
return;
// read messages from server, should just be nops
old = net_message;
memcpy (olddata, net_message.data, net_message.cursize);
do
{
ret = CL_GetMessage ();
switch (ret)
{
default:
Host_Error ("CL_GetMessage failed");
case 0:
break; // nothing waiting
case 1:
Host_Error ("CL_KeepaliveMessage received");
break;
case 2:
if (MSG_ReadByte() != svc_nop)
Host_Error ("Keepalive datagram wasn't a nop");
break;
}
} while (ret);
net_message = old;
memcpy (net_message.data, olddata, net_message.cursize);
// check time
time = Sys_FloatTime ();
if (time - lastmsg < 5)
return;
lastmsg = time;
// write out a nop
Con_Printf ("[CLIENT] .... keepalive ....\n");
MSG_WriteByte (&cls.message, clc_nop);
NET_SendMessage (cls.netcon, &cls.message);
SZ_Clear (&cls.message);
}
// Tomaz - Qc Parsing & HL Maps Begin
void CL_ParseEntityLump(char *entdata)
{
char *data;
char key[128], value[4096];
char wadname[128];
int i, j, k;
// skyname[0] = 0;
data = entdata;
if (!data)
return;
data = COM_Parse(data);
if (!data)
return; // valid exit
if (com_token[0] != '{')
return; // error
while (1)
{
data = COM_Parse(data);
if (!data)
return; // error
if (com_token[0] == '}')
return; // since we're just parsing the first ent (worldspawn), exit
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
return; // error
strcpy(value, com_token);
// if (!strcmp("sky", key))
// R_SetSkyBox(value); // TODO FIXME LATER ADDME skyboxes from tochris
// else if (!strcmp("skyname", key))
// R_SetSkyBox(value);
if (!strcmp("wad", key)) // for HalfLife maps
{
j = 0;
for (i = 0;i < 4096;i++)
if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
break;
if (value[i])
{
for (;i < 4096;i++)
{
// ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
j = i+1;
else if (value[i] == ';' || value[i] == 0)
{
k = value[i];
value[i] = 0;
strcpy(wadname, "textures/");
strcat(wadname, &value[j]);
W_LoadTextureWadFile (wadname, false);
j = i+1;
if (!k)
break;
}
}
}
}
}
}
// Tomaz - Qc Parsing & HL Maps End
/*
==================
CL_ParseServerInfo
==================
*/
void CL_ParseServerInfo (void)
{
char *str;
int i;
int nummodels, numsounds;
char model_precache[MAX_MODELS][MAX_QPATH];
char sound_precache[MAX_SOUNDS][MAX_QPATH];
Con_DPrintf ("[CLIENT] Serverinfo packet received\n");
//
// wipe the client_state_t struct
//
CL_ClearState ();
#ifdef DPPROTOCOLS
i = MSG_ReadLong ();
if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION && i != 250)
{
Con_Printf ("[CLIENT] Server returned version %i, not %i or %i", i, DPPROTOCOL_VERSION, PROTOCOL_VERSION);
return;
}
// Nehahrademcompatibility = false;
// if (i == 250)
// Nehahrademcompatibility = true;
// if (cls.demoplayback && demo_nehahra.value)
// Nehahrademcompatibility = true;
dpprotocol = i == DPPROTOCOL_VERSION;
#else
// parse protocol version number
i = MSG_ReadLong ();
if (i != PROTOCOL_VERSION)
{
Con_Printf ("[CLIENT] Server returned version %i, not %i\n", i, PROTOCOL_VERSION); // 2000-01-08 Missing linefeeds fix by Maddes
return;
}
#endif
// parse maxclients
cl.maxclients = MSG_ReadByte ();
if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
{
Con_Printf("[CLIENT] Bad maxclients (%u) from server\n", cl.maxclients);
return;
}
cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores");
// parse gametype
cl.gametype = MSG_ReadByte ();
// parse signon message
str = MSG_ReadString ();
strncpy (cl.levelname, str, sizeof(cl.levelname)-1);
// seperate the printfs so the server message can have a color
Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
Con_Printf ("%c%s\n", 2, str);
//
// first we go through and touch all of the precache data that still
// happens to be in the cache, so precaching something else doesn't
// needlessly purge it
//
// precache models
memset (cl.model_precache, 0, sizeof(cl.model_precache));
for (nummodels=1 ; ; nummodels++)
{
str = MSG_ReadString ();
if (!str[0])
break;
if (nummodels==MAX_MODELS)
{
Con_Printf ("[CLIENT] WARNING: Too many model precaches\n");
return;
}
strcpy (model_precache[nummodels], str);
Mod_TouchModel (str);
}
// precache sounds
memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
for (numsounds=1 ; ; numsounds++)
{
str = MSG_ReadString ();
if (!str[0])
break;
if (numsounds==MAX_SOUNDS)
{
Con_Printf ("[CLIENT] WARNING: Too many sound precaches\n");
return;
}
strcpy (sound_precache[numsounds], str);
S_TouchSound (str);
}
//
// now we try to load everything else until a cache allocation fails
//
for (i=1 ; i<nummodels ; i++)
{
cl.model_precache[i] = Mod_ForName (model_precache[i], false);
if (cl.model_precache[i] == NULL)
{
Con_Printf("[CLIENT] Model %s not found\n", model_precache[i]);
return;
}
CL_KeepaliveMessage ();
}
S_BeginPrecaching ();
for (i=1 ; i<numsounds ; i++)
{
cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]);
CL_KeepaliveMessage ();
}
S_EndPrecaching ();
// local state
// 2001-09-20 Configurable entity limits by Maddes start
// cl_entities[0].model =
cl.worldmodel = cl.model_precache[1];
// 2001-09-20 Configurable entity limits by Maddes end
R_NewMap ();
Hunk_Check (); // make sure nothing is hurt
noclip_anglehack = false; // noclip is turned off at start
R_LoadRTLights(); // Gather coronas from .rtlights files!
// LoadPointLighting(sv.worldmodel->entities); // doesn't seem to work when called here
}
/*
==================
CL_ParseUpdate
Parse an entity update message from the server
If an entities model or origin changes from frame to frame, it must be
relinked. Other attributes can change without relinking.
==================
*/
//#ifndef SONOFABITS
int bitcounts[16];
//#endif
void CL_ParseUpdate (int bits)
{
int i;
model_t *model;
int modnum;
qboolean forcelink;
entity_t *ent;
int num;
#ifdef GLQUAKE // 2000-07-16 General variable definition for GLQuake-only variable fix
int skin;
#endif // 2000-07-16 General variable definition for GLQuake-only variable fix
if (cls.signon == SIGNONS - 1)
{ // first update is the final signon stage
cls.signon = SIGNONS;
CL_SignonReply ();
}
if (bits & U_MOREBITS)
{
i = MSG_ReadByte ();
bits |= (i<<8);
}
#ifdef SONOFABITS
// Tomaz - QC Control Begin
if (bits & U_EXTEND1)
{
bits |= MSG_ReadByte() << 16;
if (bits & U_EXTEND2)
bits |= MSG_ReadByte() << 24;
}
#endif
// Tomaz - QC Control End
if (bits & U_LONGENTITY)
num = MSG_ReadShort ();
else
num = MSG_ReadByte ();
ent = CL_EntityNum (num);
//#ifndef SONOFABITS
for (i=0 ; i<16 ; i++)
if (bits&(1<<i))
bitcounts[i]++;
//#endif
if (ent->msgtime != cl.mtime[1])
forcelink = true; // no previous frame to lerp from
else
forcelink = false;
ent->msgtime = cl.mtime[0];
if (bits & U_MODEL)
{
modnum = MSG_ReadByte ();
if (modnum >= MAX_MODELS)
Host_Error ("CL_ParseModel: bad modnum");
}
else
modnum = ent->baseline.modelindex;
model = cl.model_precache[modnum];
if (model != ent->model)
{
#ifdef MHINTERPOL
// if the model has changed we must also reset the interpolation data
// pose1 and pose2 are critical as they might be pointing to invalid frames in the new model!!!
ent->frame_start_time = 0;
//ent->frame_interval = 0;
ent->lastpose = ent->currpose = 0;
//ent->translate_start_time = 0;
//ent->origin1[0] = ent->origin1[1] = ent->origin1[2] = 0;
//ent->origin2[0] = ent->origin2[1] = ent->origin2[2] = 0;
//ent->rotate_start_time = 0;
//ent->angles1[0] = ent->angles1[1] = ent->angles1[2] = 0;
//ent->angles2[0] = ent->angles2[1] = ent->angles2[2] = 0;
#endif
ent->model = model;
// automatic animation (torches, etc) can be either all together
// or randomized
if (model)
{
if (model->synctype == ST_RAND)
ent->syncbase = (float)(rand()&0x7fff) / 0x7fff;
else
ent->syncbase = 0.0;
}
else
forcelink = true; // hack to make null model players work
#ifdef GLQUAKE
if (num > 0 && num <= cl.maxclients)
R_TranslatePlayerSkin (num - 1);
#endif
}
if (bits & U_FRAME)
ent->frame = MSG_ReadByte ();
else
ent->frame = ent->baseline.frame;
if (bits & U_COLORMAP)
i = MSG_ReadByte();
else
i = ent->baseline.colormap;
if (!i)
ent->colormap = vid.colormap;
else
{
if (i > cl.maxclients)
// 2001-12-16 Various crashes changed to host errors by Maddes start
// Sys_Error ("i >= cl.maxclients");
{
Host_Error ("CL_ParseUpdate %i > %i", i, cl.maxclients);
return;
}
// 2001-12-16 Various crashes changed to host errors by Maddes end
ent->colormap = cl.scores[i-1].translations;
}
#ifdef GLQUAKE
if (bits & U_SKIN)
skin = MSG_ReadByte();
else
skin = ent->baseline.skin;
if (skin != ent->skinnum) {
ent->skinnum = skin;
if (num > 0 && num <= cl.maxclients)
R_TranslatePlayerSkin (num - 1);
}
#else
if (bits & U_SKIN)
ent->skinnum = MSG_ReadByte();
else
ent->skinnum = ent->baseline.skin;
#endif
if (bits & U_EFFECTS)
ent->effects = (ent->effects & 0xFF00) | MSG_ReadByte();
// if (bits & U_EFFECTS)
// ent->effects = MSG_ReadByte();
else
ent->effects = ent->baseline.effects;
// shift the known values for interpolation
VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
if (bits & U_ORIGIN1)
ent->msg_origins[0][0] = MSG_ReadCoord ();
else
ent->msg_origins[0][0] = ent->baseline.origin[0];
if (bits & U_ANGLE1)
ent->msg_angles[0][0] = MSG_ReadAngle();
else
ent->msg_angles[0][0] = ent->baseline.angles[0];
if (bits & U_ORIGIN2)
ent->msg_origins[0][1] = MSG_ReadCoord ();
else
ent->msg_origins[0][1] = ent->baseline.origin[1];
if (bits & U_ANGLE2)
ent->msg_angles[0][1] = MSG_ReadAngle();
else
ent->msg_angles[0][1] = ent->baseline.angles[1];
if (bits & U_ORIGIN3)
ent->msg_origins[0][2] = MSG_ReadCoord ();
else
ent->msg_origins[0][2] = ent->baseline.origin[2];
if (bits & U_ANGLE3)
ent->msg_angles[0][2] = MSG_ReadAngle();
else
ent->msg_angles[0][2] = ent->baseline.angles[2];
#ifdef SCALEE
ent->scale2 = 1.0f;
#endif
#ifdef ALPHASCALE
if(dpprotocol){
if (bits & U_ALPHA)
ent->alpha = MSG_ReadFloat ();
// else
// ent->alpha = 1.0f;
#ifdef SCALEE
if (bits & U_SCALE)
ent->scale2 = MSG_ReadFloat();
else
ent->scale2 = 1.0f;
#endif
if (bits & U_GLOWCOLOR)
ent->glowcolor = MSG_ReadFloat ();
else
ent->glowcolor = 0;
if (bits & U_GLOWSIZE)
ent->glowsize = MSG_ReadFloat ();
else
ent->glowsize = 0;
}
#endif
if ( bits & U_NOLERP )
ent->forcelink = true;
#ifdef VMTOC
if ( bits & U_VIEWMODEL )
ent->viewmodel = true;
#endif
if ( forcelink )
{ // didn't have an update last message
VectorCopy (ent->msg_origins[0], ent->msg_origins[1]);
VectorCopy (ent->msg_origins[0], ent->origin);
VectorCopy (ent->msg_angles[0], ent->msg_angles[1]);
VectorCopy (ent->msg_angles[0], ent->angles);
ent->forcelink = true;
}
}
/*
==================
CL_ParseBaseline
==================
*/
void CL_ParseBaseline (entity_t *ent)
{
int i;
ent->baseline.modelindex = MSG_ReadByte ();
ent->baseline.frame = MSG_ReadByte ();
ent->baseline.colormap = MSG_ReadByte();
ent->baseline.skin = MSG_ReadByte();
for (i=0 ; i<3 ; i++)
{
ent->baseline.origin[i] = MSG_ReadCoord ();
ent->baseline.angles[i] = MSG_ReadAngle ();
}
}
/*
==================
CL_ParseClientdata
Server information pertaining to this client only
==================
*/
extern int skipbob;
void CL_ParseClientdata (int bits)
{
int i, j;
if (bits & SU_VIEWHEIGHT)
cl.viewheight = MSG_ReadChar ();
else
cl.viewheight = DEFAULT_VIEWHEIGHT;
if (bits & SU_IDEALPITCH)
cl.idealpitch = MSG_ReadChar ();
else
cl.idealpitch = 0;
VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
// leilei - unrolled
/*
for (i=0 ; i<3 ; i++)
{
if (bits & (SU_PUNCH1<<i) )
cl.punchangle[i] = MSG_ReadChar();
else
cl.punchangle[i] = 0;
if (bits & (SU_VELOCITY1<<i) )
cl.mvelocity[0][i] = MSG_ReadChar()*16;
else
cl.mvelocity[0][i] = 0;
}
*/
{
if (bits & (SU_PUNCH1<<0) )
cl.punchangle[0] = MSG_ReadChar();
else
cl.punchangle[0] = 0;
if (bits & (SU_VELOCITY1<<0) )
cl.mvelocity[0][0] = MSG_ReadChar()*16;
else
cl.mvelocity[0][0] = 0;
if (bits & (SU_PUNCH1<<1) )
cl.punchangle[1] = MSG_ReadChar();
else
cl.punchangle[1] = 0;
if (bits & (SU_VELOCITY1<<1) )
cl.mvelocity[0][1] = MSG_ReadChar()*16;
else
cl.mvelocity[0][1] = 0;
if (bits & (SU_PUNCH1<<2) )
cl.punchangle[2] = MSG_ReadChar();
else
cl.punchangle[2] = 0;
if (bits & (SU_VELOCITY1<<2) )
cl.mvelocity[0][2] = MSG_ReadChar()*16;
else
cl.mvelocity[0][2] = 0;
}
// [always sent] if (bits & SU_ITEMS)
i = MSG_ReadLong ();
if (cl.items != i)
{ // set flash times
Sbar_Changed ();
for (j=0 ; j<32 ; j++)
if ( (i & (1<<j)) && !(cl.items & (1<<j)))
cl.item_gettime[j] = cl.time;
cl.items = i;
}
cl.onground = (bits & SU_ONGROUND) != 0;
cl.inwater = (bits & SU_INWATER) != 0;
if (bits & SU_WEAPONFRAME)
cl.stats[STAT_WEAPONFRAME] = MSG_ReadByte ();
else
cl.stats[STAT_WEAPONFRAME] = 0;
if (bits & SU_ARMOR)
i = MSG_ReadByte ();
else
i = 0;
if (cl.stats[STAT_ARMOR] != i)
{
cl.stats[STAT_ARMOR] = i;
Sbar_Changed ();
}
if (bits & SU_WEAPON)
i = MSG_ReadByte ();
else
i = 0;
if (cl.stats[STAT_WEAPON] != i)
{
cl.stats[STAT_WEAPON] = i;
Sbar_Changed ();
}
i = MSG_ReadShort ();
if (cl.stats[STAT_HEALTH] != i)
{
cl.stats[STAT_HEALTH] = i;
Sbar_Changed ();
}
i = MSG_ReadByte ();
if (cl.stats[STAT_AMMO] != i)
{
cl.stats[STAT_AMMO] = i;
Sbar_Changed ();
}
for (i=0 ; i<4 ; i++)
{
j = MSG_ReadByte ();
if (cl.stats[STAT_SHELLS+i] != j)
{
cl.stats[STAT_SHELLS+i] = j;
Sbar_Changed ();
}
}
i = MSG_ReadByte ();
if (standard_quake)
{
if (cl.stats[STAT_ACTIVEWEAPON] != i)
{
cl.stats[STAT_ACTIVEWEAPON] = i;
Sbar_Changed ();
}
}
else
{
if (cl.stats[STAT_ACTIVEWEAPON] != (1<<i))
{
cl.stats[STAT_ACTIVEWEAPON] = (1<<i);
Sbar_Changed ();
}
}
}
/*
=====================
CL_NewTranslation
=====================
*/
extern byte *gelmap [256];
void CL_NewTranslation (int slot)
{
int i, j;
int top, bottom;
byte *dest, *source;
if (slot > cl.maxclients)
// 2001-12-16 Various crashes changed to host errors by Maddes start
// Sys_Error ("CL_NewTranslation: slot > cl.maxclients");
{
Host_Error ("CL_NewTranslation %i > %i", slot, cl.maxclients);
return;
}
// 2001-12-16 Various crashes changed to host errors by Maddes end
dest = cl.scores[slot].translations;
source = vid.colormap;
memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations));
top = cl.scores[slot].colors & 0xf0;
bottom = (cl.scores[slot].colors &15)<<4;
#ifdef GLQUAKE
R_TranslatePlayerSkin (slot);
#endif
for (i=0 ; i<VID_GRADES ; i++, dest += 256, source+=256)
{
if (top < 128) // the artists made some backwards ranges. sigh.
memcpy (dest + TOP_RANGE, source + top, 16);
else
for (j=0 ; j<16 ; j++)
dest[TOP_RANGE+j] = source[top+15-j];
if (bottom < 128)
memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
else
for (j=0 ; j<16 ; j++)
dest[BOTTOM_RANGE+j] = source[bottom+15-j];
}
}
/*
=====================
CL_ParseStatic
=====================
*/
void CL_ParseStatic (void)
{
entity_t *ent;
int i;
// 2001-09-20 Configurable entity limits by Maddes start
if (!cl_static_entities)
{
Cvar_Set(cl_entities_min_static, cl_entities_min_static->string); // do rangecheck
if (cl.max_static_edicts < cl_entities_min_static->value)
{
cl.max_static_edicts = cl_entities_min_static->value;
}
Con_DPrintf("[CLIENT] Allocating memory for %i static ents\n", cl.max_static_edicts);
cl_static_entities = Hunk_AllocName (cl.max_static_edicts*sizeof(entity_t), "cl_ed_static");
memset (cl_static_entities, 0, cl.max_static_edicts*sizeof(entity_t));
}
// 2001-09-20 Configurable entity limits by Maddes end
i = cl.num_statics;
// 2001-09-20 Configurable entity limits by Maddes start
// if (i >= MAX_STATIC_ENTITIES)
// Host_Error ("Too many static entities");
if (i >= cl.max_static_edicts)
Host_Error ("Too many static ents, max is %i", cl.max_static_edicts);
// 2001-09-20 Configurable entity limits by Maddes end
ent = &cl_static_entities[i];
cl.num_statics++;
CL_ParseBaseline (ent);
// copy it to the current state
ent->model = cl.model_precache[ent->baseline.modelindex];
ent->frame = ent->baseline.frame;
ent->colormap = vid.colormap;
ent->skinnum = ent->baseline.skin;
ent->effects = ent->baseline.effects;
//ent->model->dontshadow = 1; // leilei - shadowhack - prevent torches from shadowing
// FIXME: Crashes After The Fall!
#ifdef ALPHASCALE
// Why did Makaqu need this?
// Not like anyone will ever alpha their static entities anyway
// This stuff breaks demo playback for some reason
// But right now this is not important, we do not need it
if(dpprotocol)
{
int bits;
bits = MSG_ReadLong();
if (bits & U_ALPHA)
ent->alpha = MSG_ReadFloat ();
// else
// ent->alpha = 0;
#ifdef SCALEE
if (bits & U_SCALE)
ent->scale2 = MSG_ReadFloat ();
#endif
if (bits & U_GLOWSIZE)
ent->glowsize = MSG_ReadFloat ();
else
ent->glowsize = 0;
if (bits & U_GLOWCOLOR)
ent->glowcolor = MSG_ReadFloat ();
else
ent->glowcolor = 0;
ent->effects = MSG_ReadShort();
// ent->glowsize = 0;
// ent->glowcolor = 0;
// ent->alpha = 1.0f;
#ifdef SCALEE
ent->scale2 = 1.0f;
#endif
}
#endif
VectorCopy (ent->baseline.origin, ent->origin);
VectorCopy (ent->baseline.angles, ent->angles);
R_AddEfrags (ent);
}
/*
===================
CL_ParseStaticSound
===================
*/
void CL_ParseStaticSound (void)
{
vec3_t org;
int sound_num, vol, atten;
int i;
org[0] = MSG_ReadCoord ();
org[1] = MSG_ReadCoord ();
org[2] = MSG_ReadCoord ();
sound_num = MSG_ReadByte ();
vol = MSG_ReadByte ();
atten = MSG_ReadByte ();
S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
}
// 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes start
/*
=================
CL_ParseVersion
=================
*/
void CL_ParseVersion (void)
{
int type;
float f;
type = MSG_ReadByte ();
switch (type)
{
case VERSION_NVS:
// read SSVC version
f = MSG_ReadFloat ();
if (!sv.active) // not necessary on listen server
{
nvs_current_ssvc->rangecheck = NULL; //deactivate, as server can run with a much higher version
Cvar_SetValue (nvs_current_ssvc, f);
nvs_current_ssvc->rangecheck = Cvar_RangecheckFloat; //reactivate
}
// read CSVC version
f = MSG_ReadFloat ();
Cvar_SetValue (nvs_current_csvc, f);
// read CCLC version
f = MSG_ReadFloat ();
Cvar_SetValue (nvs_current_cclc, f);
break;
default:
Host_Error ("CL_ParseVersion: bad type %i", type);
break;
}
}
// 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes end
// 2001-09-20 Configurable limits by Maddes start
/*
=================
CL_ParseLimit
=================
*/
void CL_ParseLimit (void)
{
int type;
type = MSG_ReadByte ();
switch (type)
{
// 2001-09-20 Configurable entity limits by Maddes start
case LIM_ENTITIES:
cl.max_edicts = MSG_ReadShort ();
cl.max_static_edicts = MSG_ReadShort ();
cl.max_temp_edicts = MSG_ReadShort ();
Con_DPrintf("[INFO] MAX %i normal, %i static and %i temp edicts\n", cl.max_edicts, cl.max_static_edicts, cl.max_temp_edicts);
// check values: only the normal entities are send with their index number, the others are send without indexes
if (cl.max_edicts > MAX_EDICTS)
{
Host_Error ("CL_ParseLimit: %i ents, max is %i", cl.max_edicts, MAX_EDICTS);
}
break;
// 2001-09-20 Configurable entity limits by Maddes end
default:
Host_Error ("CL_ParseLimit: bad type %i", type);
break;
}
}
// 2001-09-20 Configurable limits by Maddes end
#define SHOWNET(x) if(cl_shownet->value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
void Host_Autosavegame_f(void);
extern cvar_t *autosaver;
/*
=====================
CL_ParseServerMessage
=====================
*/
void CL_ParseServerMessage (void)
{
int cmd;
int i;
//
// if recording demos, copy the message out
//
if (cl_shownet->value == 1)
Con_Printf ("%i ",net_message.cursize);
else if (cl_shownet->value == 2)
Con_Printf ("------------------\n");
cl.onground = false; // unless the server says otherwise
//
// parse the message
//
MSG_BeginReading ();
while (1)
{
if (msg_badread)
Host_Error ("CL_ParseServerMessage: Bad server message");
cmd = MSG_ReadByte ();
if (cmd == -1)
{
SHOWNET("END OF MESSAGE");
return; // end of message
}
// if the high bit of the command byte is set, it is a fast update
if (cmd & 128)
{
SHOWNET("fast update");
CL_ParseUpdate (cmd&127);
continue;
}
SHOWNET(svc_strings[cmd]);
// other commands
switch (cmd)
{
default:
Host_Error ("CL_ParseServerMessage: Illegible server message");
break;
case svc_nop:
// Con_Printf ("svc_nop\n");
break;
case svc_time:
cl.mtime[1] = cl.mtime[0];
cl.mtime[0] = MSG_ReadFloat ();
break;
case svc_clientdata:
i = MSG_ReadShort ();
CL_ParseClientdata (i);
break;
case svc_version:
i = MSG_ReadLong ();
#ifdef DPPROTOCOLS
if (i != PROTOCOL_VERSION && i != DPPROTOCOL_VERSION && i != 250)
Host_Error ("CL_ParseServerMessage: Server is protocol %i, not %i or %i", i, DPPROTOCOL_VERSION, PROTOCOL_VERSION);
// Nehahrademcompatibility = false;
// if (i == 250)
// Nehahrademcompatibility = true;
// if (cls.demoplayback && demo_nehahra.value)
// Nehahrademcompatibility = true;
dpprotocol = i == DPPROTOCOL_VERSION;
#else
if (i != PROTOCOL_VERSION)
Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i", i, PROTOCOL_VERSION);
#endif
break;
case svc_disconnect:
Host_EndGame ("[CLIENT] Server disconnected\n");
case svc_print:
Con_Printf ("%s", MSG_ReadString ());
break;
case svc_centerprint:
SCR_CenterPrint (MSG_ReadString ());
break;
case svc_stufftext:
Cbuf_AddText (MSG_ReadString ());
break;
case svc_damage:
V_ParseDamage ();
break;
case svc_serverinfo:
CL_ParseServerInfo ();
vid.recalc_refdef = true; // leave intermission full screen
break;
case svc_setangle:
// 2000-05-02 NVS SVC_setangle by Maddes start
if ((nvs_current_csvc->value >= 0.50) && (cls.signon > 1))
{
for (i=0 ; i<3 ; i++)
cl.viewangles[i] = MSG_ReadFloat ();
}
else
{
// 2000-05-02 NVS SVC_setangle by Maddes end
for (i=0 ; i<3 ; i++)
cl.viewangles[i] = MSG_ReadAngle ();
} // 2000-05-02 NVS SVC_setangle by Maddes
break;
case svc_setview:
cl.viewentity = MSG_ReadShort ();
break;
case svc_lightstyle:
i = MSG_ReadByte ();
if (i >= MAX_LIGHTSTYLES)
// 2001-12-16 Various crashes changed to host errors by Maddes start
// Sys_Error ("svc_lightstyle >= MAX_LIGHTSTYLES");
{
Host_Error ("svc_lightstyle >= MAX_LIGHTSTYLES: %i >= %i", i, MAX_LIGHTSTYLES);
break;
}
// 2001-12-16 Various crashes changed to host errors by Maddes end
strcpy (cl_lightstyle[i].map, MSG_ReadString());
cl_lightstyle[i].length = strlen(cl_lightstyle[i].map);
break;
case svc_sound:
CL_ParseStartSoundPacket();
break;
case svc_stopsound:
i = MSG_ReadShort();
S_StopSound(i>>3, i&7);
break;
case svc_updatename:
Sbar_Changed ();
i = MSG_ReadByte ();
if (i >= cl.maxclients)
Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD");
strcpy (cl.scores[i].name, MSG_ReadString ());
break;
case svc_updatefrags:
Sbar_Changed ();
i = MSG_ReadByte ();
if (i >= cl.maxclients)
Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
cl.scores[i].frags = MSG_ReadShort ();
break;
case svc_updatecolors:
Sbar_Changed ();
i = MSG_ReadByte ();
if (i >= cl.maxclients)
Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD");
cl.scores[i].colors = MSG_ReadByte ();
CL_NewTranslation (i);
break;
case svc_particle:
R_ParseParticleEffect ();
break;
case svc_spawnbaseline:
i = MSG_ReadShort ();
// must use CL_EntityNum() to force cl.num_entities up
CL_ParseBaseline (CL_EntityNum(i));
break;
case svc_spawnstatic:
CL_ParseStatic ();
break;
case svc_temp_entity:
CL_ParseTEnt ();
break;
case svc_setpause:
{
cl.paused = MSG_ReadByte ();
if (cl.paused)
{
Music_Pause();
#ifdef _WIN32
VID_HandlePause (true);
#endif
}
else
{
Music_Resume();
#ifdef _WIN32
VID_HandlePause (false);
#endif
}
}
break;
case svc_signonnum:
i = MSG_ReadByte ();
if (i <= cls.signon)
Host_Error ("Received signon %i when at %i", i, cls.signon);
cls.signon = i;
CL_SignonReply ();
break;
case svc_killedmonster:
cl.stats[STAT_MONSTERS]++;
if (autosaver->value){
if (cl.stats[STAT_MONSTERS] > (cl.stats[STAT_TOTALMONSTERS] * 0.5) && !autosaved_monsthalfkilled){
autosaved_monsthalfkilled = 155;
Host_Autosavegame_f();
}
}
break;
case svc_foundsecret:
cl.stats[STAT_SECRETS]++;
break;
case svc_updatestat:
i = MSG_ReadByte ();
if (i < 0 || i >= MAX_CL_STATS)
// 2001-12-16 Various crashes changed to host errors by Maddes start
// Sys_Error ("svc_updatestat: %i is invalid", i);
{
Host_Error ("svc_updatestat: %i is invalid (0-%i)", i, MAX_CL_STATS);
break;
}
// 2001-12-16 Various crashes changed to host errors by Maddes end
cl.stats[i] = MSG_ReadLong ();;
break;
case svc_spawnstaticsound:
CL_ParseStaticSound ();
break;
case svc_cdtrack:
cl.cdtrack = MSG_ReadByte ();
cl.looptrack = MSG_ReadByte ();
if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
Music_Play ((byte)cls.forcetrack, true);
else
Music_Play ((byte)cl.cdtrack, true);
break;
case svc_intermission:
cl.intermission = 1;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
break;
case svc_finale:
cl.intermission = 2;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
SCR_CenterPrint (MSG_ReadString ());
break;
case svc_cutscene:
cl.intermission = 3;
cl.completed_time = cl.time;
vid.recalc_refdef = true; // go to full screen
SCR_CenterPrint (MSG_ReadString ());
break;
case svc_sound3:
CL_ParseStartSoundPacket2();
break;
case svc_sellscreen:
Cmd_ExecuteString ("help", src_command);
break;
// 2001-09-20 Configurable limits by Maddes start
case svc_limit:
CL_ParseLimit();
break;
// 2001-09-20 Configurable limits by Maddes end
// 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes start
case svc_extra_version:
CL_ParseVersion();
break;
// 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes end
}
}
}
/*
=============
A crapload of FTEQW lies below
for one purpose - getting lights to
reflect
=============
*/
typedef char pbool;
typedef struct mentity_s {
vec3_t origin;
float light;
float angle;
float cone;
int style;
vec3_t colour;
char classname[64];
char target[64];
char targetname[64];
struct mentity_s *targetent;
struct entity_s lighty; // leilei - lightstyles hack
} mentity_t;
static mentity_t entities[8192];
static int num_entities;
static void ParseEpair (mentity_t *mapent, char *key, char *value)
{
double vec[3];
if (!strcmp(key, "classname"))
strcpy(mapent->classname, value);
else if (!strcmp(key, "target"))
strcpy(mapent->target, value);
else if (!strcmp(key, "targetname"))
strcpy(mapent->targetname, value);
else if (!strcmp(key, "light") || !strcmp(key, "_light") || !strcmp(key, "light_lev"))
mapent->light = atoi(value);
else if (!strcmp(key, "style") || !strcmp(key, "_style"))
mapent->style = atoi(value);
else if (!strcmp(key, "angle") || !strcmp(key, "_angle"))
mapent->angle = atof(value);
else if (!strcmp(key, "cone") || !strcmp(key, "_cone"))
mapent->cone = atof(value);
else if (!strcmp(key, "origin"))
{
sscanf (value, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]);
mapent->origin[0]=vec[0];
mapent->origin[1]=vec[1];
mapent->origin[2]=vec[2];
}
else if (!strcmp(key, "colour") || !strcmp(key, "color") || !strcmp(key, "_colour") || !strcmp(key, "_color"))
{
sscanf (value, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]);
mapent->colour[0]=vec[0];
mapent->colour[1]=vec[1];
mapent->colour[2]=vec[2];
}
}
extern vec3_t lightcolor;
extern cvar_t *temp2;
// A botched up FTEQW version just to load realtime point lights for r_shading 3
void LoadPointLighting (char *entstring)
{
mentity_t *mapent;
char key[1024];
int i;
int switchedstyle=32;
int num_entities = 0;
int num_lights = 0;
while(1)
{
entstring = COM_Parse(entstring);
if (!entstring || !*com_token)
break;
if (strcmp(com_token, "{"))
{ //someone messed up. Stop parsing.
Con_Printf("token wasn't an open brace\n");
break;
}
mapent = &entities[num_entities];
memset(mapent, 0, sizeof(*mapent));
mapent->colour[0] = 1;
mapent->colour[1] = 1;
mapent->colour[2] = 1;
while(1)
{
entstring = COM_Parse(entstring);
if (!strcmp(com_token, "}"))
break;
strcpy(key, com_token);
entstring = COM_Parse(entstring);
ParseEpair(mapent, key, com_token);
}
if (!mapent->light && !strncmp (mapent->classname, "light", 5))
mapent->light = 300;
if (*mapent->targetname && !mapent->style && !strcmp(mapent->classname, "light"))
{
for (i = 0; i <= num_entities; i++)
{
if (entities[i].style >= 32 && !strcmp(entities[i].targetname, mapent->targetname))
{
mapent->style = entities[i].style;
break;
}
}
if (i == num_entities)
mapent->style = switchedstyle++;
}
if (mapent->light > 1){
// leilei - try to sample color from the spot if we're in colored lighting mode
// for getting lights from lit colored maps that assume colors, like mhcolour
if (coloredlights){
R_LightPoint(mapent->origin);
mapent->colour[0] = lightcolor[0] / 252;
mapent->colour[1] = lightcolor[1] / 252;
mapent->colour[2] = lightcolor[2] / 252;
// VectorScale(lightcolor, 0.003921568627451, mapent->colour);
// VectorScale(lightcolor, 0.014, mapent->colour);
// mapent->colour[0] = 0;
// mapent->colour[1] = 0;
// mapent->colour[2] = 5;
//Con_Printf("%f %f %f to %f %f %f\n", lightcolor[0], lightcolor[1], lightcolor[2], mapent->colour[0], mapent->colour[1], mapent->colour[2]);
}
// Con_Printf("yep %f %f %f %f\n", mapent->origin[0], mapent->origin[1], mapent->origin[2], mapent->light);
R_LightsHere(mapent->origin, (int)mapent->light, mapent->colour, num_entities, mapent);
// leilei - flare up torches, flames and other carp
{
vec3_t offsetup;
VectorCopy(mapent->origin, offsetup);
if (!strncmp(mapent->classname, "light_torch", 11))
{
offsetup[2] += 6;
R_FlareTest(offsetup,12,32,16,6,0, NULL);
}
if (!strncmp(mapent->classname, "light_flame_large_yellow", 24))
{
offsetup[2] += 3;
R_FlareTest(offsetup,12,72,36,6,0, NULL);
offsetup[2] += 3;
R_FlareTest(offsetup,12,72,36,6,0, NULL);
offsetup[2] += 3;
R_FlareTest(offsetup,12,32,16,6,0, NULL);
offsetup[2] += 3;
R_FlareTest(offsetup,12,32,16,6,0, NULL);
offsetup[2] += 3;
R_FlareTest(offsetup,12,32,16,6,0, NULL);
offsetup[2] += 3;
R_FlareTest(offsetup,12,32,16,6,0, NULL);
}
if (!strncmp(mapent->classname, "light_flame_small_yellow", 24))
{
offsetup[2] += 1;
R_FlareTest(offsetup,12,155,112,22,0, NULL);
}
if (!strncmp(mapent->classname, "light_flame_small_white", 23))
{
offsetup[2] += 1;
R_FlareTest(offsetup,12,166,133,100,0, NULL);
}
if (!strncmp(mapent->classname, "light_globe", 11))
{
R_FlareTest(offsetup,12,166,133,46,0, NULL);
}
}
num_lights++;
}
num_entities++;
}
for (mapent = entities; mapent < &entities[num_entities]; mapent++)
{
if (*mapent->target)
{
for (i = 0; i < num_entities; i++)
{
if (mapent == &entities[i])
continue;
if (!strcmp(mapent->target, entities[i].targetname))
{
mapent->targetent = &entities[i];
break;
}
}
}
}
if (num_lights > 58){
lightingavailable = 1;
lightingcantbeavailable = 0;
}
else
{
lightingavailable = 0;
lightingcantbeavailable = 1;
}
}