yquake2remaster/src/client/cl_parse.c

1401 lines
28 KiB
C

/*
* Copyright (C) 1997-2001 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.
*
* =======================================================================
*
* This file implements the entity and network protocol parsing
*
* =======================================================================
*/
#include "header/client.h"
#include "input/header/input.h"
void CL_DownloadFileName(char *dest, int destlen, char *fn);
void CL_ParseDownload(void);
int bitcounts[32]; /* just for protocol profiling */
char *svc_strings[256] = {
"svc_bad",
"svc_muzzleflash",
"svc_muzzlflash2",
"svc_temp_entity",
"svc_layout",
"svc_inventory",
"svc_nop",
"svc_disconnect",
"svc_reconnect",
"svc_sound",
"svc_print",
"svc_stufftext",
"svc_serverdata",
"svc_configstring",
"svc_spawnbaseline",
"svc_centerprint",
"svc_download",
"svc_playerinfo",
"svc_packetentities",
"svc_deltapacketentities",
"svc_frame"
};
void
CL_RegisterSounds(void)
{
int i;
S_BeginRegistration();
CL_RegisterTEntSounds();
for (i = 1; i < MAX_SOUNDS; i++)
{
if (!cl.configstrings[CS_SOUNDS + i][0])
{
break;
}
cl.sound_precache[i] = S_RegisterSound(cl.configstrings[CS_SOUNDS + i]);
IN_Update();
}
S_EndRegistration();
}
/*
* Returns the entity number and the header bits
*/
int
CL_ParseEntityBits(unsigned *bits)
{
unsigned b, total;
int i;
int number;
total = MSG_ReadByte(&net_message);
if (total & U_MOREBITS1)
{
b = MSG_ReadByte(&net_message);
total |= b << 8;
}
if (total & U_MOREBITS2)
{
b = MSG_ReadByte(&net_message);
total |= b << 16;
}
if (total & U_MOREBITS3)
{
b = MSG_ReadByte(&net_message);
total |= b << 24;
}
/* count the bits for net profiling */
for (i = 0; i < 32; i++)
{
if (total & (1u << i))
{
bitcounts[i]++;
}
}
if (total & U_NUMBER16)
{
number = MSG_ReadShort(&net_message);
}
else
{
number = MSG_ReadByte(&net_message);
}
*bits = total;
return number;
}
/*
* Can go from either a baseline or a previous packet_entity
*/
void
CL_ParseDelta(entity_state_t *from, entity_state_t *to, int number, int bits)
{
/* set everything to the state we are delta'ing from */
*to = *from;
VectorCopy(from->origin, to->old_origin);
to->number = number;
if (bits & U_MODEL)
{
to->modelindex = MSG_ReadByte(&net_message);
}
if (bits & U_MODEL2)
{
to->modelindex2 = MSG_ReadByte(&net_message);
}
if (bits & U_MODEL3)
{
to->modelindex3 = MSG_ReadByte(&net_message);
}
if (bits & U_MODEL4)
{
to->modelindex4 = MSG_ReadByte(&net_message);
}
if (bits & U_FRAME8)
{
to->frame = MSG_ReadByte(&net_message);
}
if (bits & U_FRAME16)
{
to->frame = MSG_ReadShort(&net_message);
}
/* used for laser colors */
if ((bits & U_SKIN8) && (bits & U_SKIN16))
{
to->skinnum = MSG_ReadLong(&net_message);
}
else if (bits & U_SKIN8)
{
to->skinnum = MSG_ReadByte(&net_message);
}
else if (bits & U_SKIN16)
{
to->skinnum = MSG_ReadShort(&net_message);
}
if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
{
to->effects = MSG_ReadLong(&net_message);
}
else if (bits & U_EFFECTS8)
{
to->effects = MSG_ReadByte(&net_message);
}
else if (bits & U_EFFECTS16)
{
to->effects = MSG_ReadShort(&net_message);
}
if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
{
to->renderfx = MSG_ReadLong(&net_message);
}
else if (bits & U_RENDERFX8)
{
to->renderfx = MSG_ReadByte(&net_message);
}
else if (bits & U_RENDERFX16)
{
to->renderfx = MSG_ReadShort(&net_message);
}
if (bits & U_ORIGIN1)
{
to->origin[0] = MSG_ReadCoord(&net_message);
}
if (bits & U_ORIGIN2)
{
to->origin[1] = MSG_ReadCoord(&net_message);
}
if (bits & U_ORIGIN3)
{
to->origin[2] = MSG_ReadCoord(&net_message);
}
if (bits & U_ANGLE1)
{
to->angles[0] = MSG_ReadAngle(&net_message);
}
if (bits & U_ANGLE2)
{
to->angles[1] = MSG_ReadAngle(&net_message);
}
if (bits & U_ANGLE3)
{
to->angles[2] = MSG_ReadAngle(&net_message);
}
if (bits & U_OLDORIGIN)
{
MSG_ReadPos(&net_message, to->old_origin);
}
if (bits & U_SOUND)
{
to->sound = MSG_ReadByte(&net_message);
}
if (bits & U_EVENT)
{
to->event = MSG_ReadByte(&net_message);
}
else
{
to->event = 0;
}
if (bits & U_SOLID)
{
to->solid = MSG_ReadShort(&net_message);
}
}
/*
* Parses deltas from the given base and adds the resulting entity to
* the current frame
*/
void
CL_DeltaEntity(frame_t *frame, int newnum, entity_state_t *old, int bits)
{
centity_t *ent;
entity_state_t *state;
ent = &cl_entities[newnum];
state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES - 1)];
cl.parse_entities++;
frame->num_entities++;
CL_ParseDelta(old, state, newnum, bits);
/* some data changes will force no lerping */
if ((state->modelindex != ent->current.modelindex) ||
(state->modelindex2 != ent->current.modelindex2) ||
(state->modelindex3 != ent->current.modelindex3) ||
(state->modelindex4 != ent->current.modelindex4) ||
(state->event == EV_PLAYER_TELEPORT) ||
(state->event == EV_OTHER_TELEPORT) ||
(abs((int)(state->origin[0] - ent->current.origin[0])) > 512) ||
(abs((int)(state->origin[1] - ent->current.origin[1])) > 512) ||
(abs((int)(state->origin[2] - ent->current.origin[2])) > 512)
)
{
ent->serverframe = -99;
}
/* wasn't in last update, so initialize some things */
if (ent->serverframe != cl.frame.serverframe - 1)
{
ent->trailcount = 1024; /* for diminishing rocket / grenade trails */
/* duplicate the current state so
lerping doesn't hurt anything */
ent->prev = *state;
if (state->event == EV_OTHER_TELEPORT)
{
VectorCopy(state->origin, ent->prev.origin);
VectorCopy(state->origin, ent->lerp_origin);
}
else
{
VectorCopy(state->old_origin, ent->prev.origin);
VectorCopy(state->old_origin, ent->lerp_origin);
}
}
else
{
/* shuffle the last state to previous */
ent->prev = ent->current;
}
ent->serverframe = cl.frame.serverframe;
ent->current = *state;
}
/*
* An svc_packetentities has just been
* parsed, deal with the rest of the
* data stream.
*/
void
CL_ParsePacketEntities(frame_t *oldframe, frame_t *newframe)
{
unsigned int newnum;
unsigned bits;
entity_state_t
*oldstate = NULL;
int oldindex, oldnum;
newframe->parse_entities = cl.parse_entities;
newframe->num_entities = 0;
/* delta from the entities present in oldframe */
oldindex = 0;
if (!oldframe)
{
oldnum = 99999;
}
else
{
if (oldindex >= oldframe->num_entities)
{
oldnum = 99999;
}
else
{
oldstate = &cl_parse_entities[(oldframe->parse_entities +
oldindex) & (MAX_PARSE_ENTITIES - 1)];
oldnum = oldstate->number;
}
}
while (1)
{
newnum = CL_ParseEntityBits(&bits);
if (newnum >= MAX_EDICTS)
{
Com_Error(ERR_DROP, "CL_ParsePacketEntities: bad number:%i", newnum);
}
if (net_message.readcount > net_message.cursize)
{
Com_Error(ERR_DROP, "CL_ParsePacketEntities: end of message");
}
if (!newnum)
{
break;
}
while (oldnum < newnum)
{
/* one or more entities from the old packet are unchanged */
if (cl_shownet->value == 3)
{
Com_Printf(" unchanged: %i\n", oldnum);
}
CL_DeltaEntity(newframe, oldnum, oldstate, 0);
oldindex++;
if (oldindex >= oldframe->num_entities)
{
oldnum = 99999;
}
else
{
oldstate = &cl_parse_entities[(oldframe->parse_entities +
oldindex) & (MAX_PARSE_ENTITIES - 1)];
oldnum = oldstate->number;
}
}
if (bits & U_REMOVE)
{
/* the entity present in oldframe is not in the current frame */
if (cl_shownet->value == 3)
{
Com_Printf(" remove: %i\n", newnum);
}
if (oldnum != newnum)
{
Com_Printf("U_REMOVE: oldnum != newnum\n");
}
oldindex++;
if (oldindex >= oldframe->num_entities)
{
oldnum = 99999;
}
else
{
oldstate = &cl_parse_entities[(oldframe->parse_entities +
oldindex) & (MAX_PARSE_ENTITIES - 1)];
oldnum = oldstate->number;
}
continue;
}
if (oldnum == newnum)
{
/* delta from previous state */
if (cl_shownet->value == 3)
{
Com_Printf(" delta: %i\n", newnum);
}
CL_DeltaEntity(newframe, newnum, oldstate, bits);
oldindex++;
if (oldindex >= oldframe->num_entities)
{
oldnum = 99999;
}
else
{
oldstate = &cl_parse_entities[(oldframe->parse_entities +
oldindex) & (MAX_PARSE_ENTITIES - 1)];
oldnum = oldstate->number;
}
continue;
}
if (oldnum > newnum)
{
/* delta from baseline */
if (cl_shownet->value == 3)
{
Com_Printf(" baseline: %i\n", newnum);
}
CL_DeltaEntity(newframe, newnum,
&cl_entities[newnum].baseline,
bits);
continue;
}
}
/* any remaining entities in the old frame are copied over */
while (oldnum != 99999)
{
/* one or more entities from the old packet are unchanged */
if (cl_shownet->value == 3)
{
Com_Printf(" unchanged: %i\n", oldnum);
}
CL_DeltaEntity(newframe, oldnum, oldstate, 0);
oldindex++;
if (oldindex >= oldframe->num_entities)
{
oldnum = 99999;
}
else
{
oldstate = &cl_parse_entities[(oldframe->parse_entities +
oldindex) & (MAX_PARSE_ENTITIES - 1)];
oldnum = oldstate->number;
}
}
}
void
CL_ParsePlayerstate(frame_t *oldframe, frame_t *newframe)
{
int flags;
player_state_t *state;
int i;
int statbits;
state = &newframe->playerstate;
/* clear to old value before delta parsing */
if (oldframe)
{
*state = oldframe->playerstate;
}
else
{
memset(state, 0, sizeof(*state));
}
flags = MSG_ReadShort(&net_message);
/* parse the pmove_state_t */
if (flags & PS_M_TYPE)
{
state->pmove.pm_type = MSG_ReadByte(&net_message);
}
if (flags & PS_M_ORIGIN)
{
state->pmove.origin[0] = MSG_ReadShort(&net_message);
state->pmove.origin[1] = MSG_ReadShort(&net_message);
state->pmove.origin[2] = MSG_ReadShort(&net_message);
}
if (flags & PS_M_VELOCITY)
{
state->pmove.velocity[0] = MSG_ReadShort(&net_message);
state->pmove.velocity[1] = MSG_ReadShort(&net_message);
state->pmove.velocity[2] = MSG_ReadShort(&net_message);
}
if (flags & PS_M_TIME)
{
state->pmove.pm_time = MSG_ReadByte(&net_message);
}
if (flags & PS_M_FLAGS)
{
state->pmove.pm_flags = MSG_ReadByte(&net_message);
}
if (flags & PS_M_GRAVITY)
{
state->pmove.gravity = MSG_ReadShort(&net_message);
}
if (flags & PS_M_DELTA_ANGLES)
{
state->pmove.delta_angles[0] = MSG_ReadShort(&net_message);
state->pmove.delta_angles[1] = MSG_ReadShort(&net_message);
state->pmove.delta_angles[2] = MSG_ReadShort(&net_message);
}
if (cl.attractloop)
{
state->pmove.pm_type = PM_FREEZE; /* demo playback */
}
/* parse the rest of the player_state_t */
if (flags & PS_VIEWOFFSET)
{
state->viewoffset[0] = MSG_ReadChar(&net_message) * 0.25f;
state->viewoffset[1] = MSG_ReadChar(&net_message) * 0.25f;
state->viewoffset[2] = MSG_ReadChar(&net_message) * 0.25f;
}
if (flags & PS_VIEWANGLES)
{
state->viewangles[0] = MSG_ReadAngle16(&net_message);
state->viewangles[1] = MSG_ReadAngle16(&net_message);
state->viewangles[2] = MSG_ReadAngle16(&net_message);
}
if (flags & PS_KICKANGLES)
{
state->kick_angles[0] = MSG_ReadChar(&net_message) * 0.25f;
state->kick_angles[1] = MSG_ReadChar(&net_message) * 0.25f;
state->kick_angles[2] = MSG_ReadChar(&net_message) * 0.25f;
}
if (flags & PS_WEAPONINDEX)
{
state->gunindex = MSG_ReadByte(&net_message);
}
if (flags & PS_WEAPONFRAME)
{
state->gunframe = MSG_ReadByte(&net_message);
state->gunoffset[0] = MSG_ReadChar(&net_message) * 0.25f;
state->gunoffset[1] = MSG_ReadChar(&net_message) * 0.25f;
state->gunoffset[2] = MSG_ReadChar(&net_message) * 0.25f;
state->gunangles[0] = MSG_ReadChar(&net_message) * 0.25f;
state->gunangles[1] = MSG_ReadChar(&net_message) * 0.25f;
state->gunangles[2] = MSG_ReadChar(&net_message) * 0.25f;
}
if (flags & PS_BLEND)
{
state->blend[0] = MSG_ReadByte(&net_message) / 255.0f;
state->blend[1] = MSG_ReadByte(&net_message) / 255.0f;
state->blend[2] = MSG_ReadByte(&net_message) / 255.0f;
state->blend[3] = MSG_ReadByte(&net_message) / 255.0f;
}
if (flags & PS_FOV)
{
state->fov = (float)MSG_ReadByte(&net_message);
}
if (flags & PS_RDFLAGS)
{
state->rdflags = MSG_ReadByte(&net_message);
}
/* parse stats */
statbits = MSG_ReadLong(&net_message);
for (i = 0; i < MAX_STATS; i++)
{
if (statbits & (1u << i))
{
state->stats[i] = MSG_ReadShort(&net_message);
}
}
}
void
CL_FireEntityEvents(frame_t *frame)
{
entity_state_t *s1;
int pnum, num;
for (pnum = 0; pnum < frame->num_entities; pnum++)
{
num = (frame->parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1);
s1 = &cl_parse_entities[num];
if (s1->event)
{
CL_EntityEvent(s1);
}
if (s1->effects & EF_TELEPORTER)
{
CL_TeleporterParticles(s1);
}
}
}
void
CL_ParseFrame(void)
{
int cmd;
int len;
frame_t *old;
memset(&cl.frame, 0, sizeof(cl.frame));
cl.frame.serverframe = MSG_ReadLong(&net_message);
cl.frame.deltaframe = MSG_ReadLong(&net_message);
cl.frame.servertime = cl.frame.serverframe * 100;
/* BIG HACK to let old demos continue to work */
if (cls.serverProtocol != 26)
{
cl.surpressCount = MSG_ReadByte(&net_message);
}
if (cl_shownet->value == 3)
{
Com_Printf(" frame:%i delta:%i\n", cl.frame.serverframe,
cl.frame.deltaframe);
}
/* If the frame is delta compressed from data that we
no longer have available, we must suck up the rest of
the frame, but not use it, then ask for a non-compressed
message */
if (cl.frame.deltaframe <= 0)
{
cl.frame.valid = true; /* uncompressed frame */
old = NULL;
cls.demowaiting = false; /* we can start recording now */
}
else
{
old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK];
if (!old->valid)
{
/* should never happen */
Com_Printf("Delta from invalid frame (not supposed to happen!).\n");
}
if (old->serverframe != cl.frame.deltaframe)
{
/* The frame that the server did the delta from
is too old, so we can't reconstruct it properly. */
Com_Printf("Delta frame too old.\n");
}
else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES -
128)
{
Com_Printf("Delta parse_entities too old.\n");
}
else
{
cl.frame.valid = true; /* valid delta parse */
}
}
/* clamp time */
if (cl.time > cl.frame.servertime)
{
cl.time = cl.frame.servertime;
}
else if (cl.time < cl.frame.servertime - 100)
{
cl.time = cl.frame.servertime - 100;
}
/* read areabits */
len = MSG_ReadByte(&net_message);
MSG_ReadData(&net_message, &cl.frame.areabits, len);
/* read playerinfo */
cmd = MSG_ReadByte(&net_message);
SHOWNET(svc_strings[cmd]);
if (cmd != svc_playerinfo)
{
Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not playerinfo", cmd);
}
CL_ParsePlayerstate(old, &cl.frame);
/* read packet entities */
cmd = MSG_ReadByte(&net_message);
SHOWNET(svc_strings[cmd]);
if (cmd != svc_packetentities)
{
Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not packetentities", cmd);
}
CL_ParsePacketEntities(old, &cl.frame);
/* save the frame off in the backup array for later delta comparisons */
cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame;
if (cl.frame.valid)
{
/* getting a valid frame message ends the connection process */
if (cls.state != ca_active)
{
cls.state = ca_active;
cl.force_refdef = true;
cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125f;
cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125f;
cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125f;
VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles);
if ((cls.disable_servercount != cl.servercount) && cl.refresh_prepped)
{
SCR_EndLoadingPlaque(); /* get rid of loading plaque */
}
cl.sound_prepped = true;
if (paused_at_load)
{
if (cl_loadpaused->value == 1)
{
Cvar_Set("paused", "0");
}
paused_at_load = false;
}
}
/* fire entity events */
CL_FireEntityEvents(&cl.frame);
if (!(!cl_predict->value ||
(cl.frame.playerstate.pmove.pm_flags &
PMF_NO_PREDICTION)))
{
CL_CheckPredictionError();
}
}
}
void
CL_ParseServerData(void)
{
extern cvar_t *fs_gamedirvar;
char *str;
int i;
/* Clear all key states */
In_FlushQueue();
Com_DPrintf("Serverdata packet received.\n");
/* wipe the client_state_t struct */
CL_ClearState();
cls.state = ca_connected;
/* parse protocol version number */
i = MSG_ReadLong(&net_message);
cls.serverProtocol = i;
/* another demo hack */
if (Com_ServerState() && (PROTOCOL_VERSION == 34))
{
}
else if (i != PROTOCOL_VERSION)
{
Com_Error(ERR_DROP, "Server returned version %i, not %i",
i, PROTOCOL_VERSION);
}
cl.servercount = MSG_ReadLong(&net_message);
cl.attractloop = MSG_ReadByte(&net_message);
/* game directory */
str = MSG_ReadString(&net_message);
Q_strlcpy(cl.gamedir, str, sizeof(cl.gamedir));
/* set gamedir */
if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string ||
strcmp(fs_gamedirvar->string, str))) ||
(!*str && (fs_gamedirvar->string && !*fs_gamedirvar->string)))
{
Cvar_Set("game", str);
}
/* parse player entity number */
cl.playernum = MSG_ReadShort(&net_message);
/* get the full level name */
str = MSG_ReadString(&net_message);
if (cl.playernum == -1)
{
/* playing a cinematic or showing a pic, not a level */
SCR_PlayCinematic(str);
}
else
{
/* seperate the printfs so the server
* message can have a color */
Com_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");
Com_Printf("%c%s\n", 2, str);
/* need to prep refresh at next oportunity */
cl.refresh_prepped = false;
}
}
void
CL_ParseBaseline(void)
{
entity_state_t *es;
unsigned bits;
int newnum;
entity_state_t nullstate;
memset(&nullstate, 0, sizeof(nullstate));
newnum = CL_ParseEntityBits(&bits);
es = &cl_entities[newnum].baseline;
CL_ParseDelta(&nullstate, es, newnum, bits);
}
void
CL_LoadClientinfo(clientinfo_t *ci, char *s)
{
int i;
char *t;
char model_name[MAX_QPATH];
char skin_name[MAX_QPATH];
char model_filename[MAX_QPATH];
char skin_filename[MAX_QPATH];
char weapon_filename[MAX_QPATH];
Q_strlcpy(ci->cinfo, s, sizeof(ci->cinfo));
s = ci->cinfo;
/* isolate the player's name */
Q_strlcpy(ci->name, s, sizeof(ci->name));
t = strstr(s, "\\");
if (t)
{
ci->name[t - s] = 0;
s = t + 1;
}
if (cl_noskins->value || (*s == 0))
{
strcpy(model_filename, "players/male/tris.md2");
strcpy(weapon_filename, "players/male/weapon.md2");
strcpy(skin_filename, "players/male/grunt.pcx");
strcpy(ci->iconname, "/players/male/grunt_i.pcx");
ci->model = R_RegisterModel(model_filename);
memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
ci->weaponmodel[0] = R_RegisterModel(weapon_filename);
ci->skin = R_RegisterSkin(skin_filename);
ci->icon = Draw_FindPic(ci->iconname);
}
else
{
/* isolate the model name */
strcpy(model_name, s);
t = strstr(model_name, "/");
if (!t)
{
t = strstr(model_name, "\\");
}
if (!t)
{
t = model_name;
}
*t = 0;
/* isolate the skin name */
strcpy(skin_name, s + strlen(model_name) + 1);
/* model file */
Com_sprintf(model_filename, sizeof(model_filename),
"players/%s/tris.md2", model_name);
ci->model = R_RegisterModel(model_filename);
if (!ci->model)
{
strcpy(model_name, "male");
Com_sprintf(model_filename, sizeof(model_filename),
"players/male/tris.md2");
ci->model = R_RegisterModel(model_filename);
}
/* skin file */
Com_sprintf(skin_filename, sizeof(skin_filename),
"players/%s/%s.pcx", model_name, skin_name);
ci->skin = R_RegisterSkin(skin_filename);
/* if we don't have the skin and the model wasn't male,
* see if the male has it (this is for CTF's skins) */
if (!ci->skin && Q_stricmp(model_name, "male"))
{
/* change model to male */
strcpy(model_name, "male");
Com_sprintf(model_filename, sizeof(model_filename),
"players/male/tris.md2");
ci->model = R_RegisterModel(model_filename);
/* see if the skin exists for the male model */
Com_sprintf(skin_filename, sizeof(skin_filename),
"players/%s/%s.pcx", model_name, skin_name);
ci->skin = R_RegisterSkin(skin_filename);
}
/* if we still don't have a skin, it means that the male model didn't have
* it, so default to grunt */
if (!ci->skin)
{
/* see if the skin exists for the male model */
Com_sprintf(skin_filename, sizeof(skin_filename),
"players/%s/grunt.pcx", model_name);
ci->skin = R_RegisterSkin(skin_filename);
}
/* weapon file */
for (i = 0; i < num_cl_weaponmodels; i++)
{
Com_sprintf(weapon_filename, sizeof(weapon_filename),
"players/%s/%s", model_name, cl_weaponmodels[i]);
ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
if (!ci->weaponmodel[i] && (strcmp(model_name, "cyborg") == 0))
{
/* try male */
Com_sprintf(weapon_filename, sizeof(weapon_filename),
"players/male/%s", cl_weaponmodels[i]);
ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
}
if (!cl_vwep->value)
{
break; /* only one when vwep is off */
}
}
/* icon file */
Com_sprintf(ci->iconname, sizeof(ci->iconname),
"/players/%s/%s_i.pcx", model_name, skin_name);
ci->icon = Draw_FindPic(ci->iconname);
}
/* must have loaded all data types to be valid */
if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
{
ci->skin = NULL;
ci->icon = NULL;
ci->model = NULL;
ci->weaponmodel[0] = NULL;
return;
}
}
/*
* Load the skin, icon, and model for a client
*/
void
CL_ParseClientinfo(int player)
{
char *s;
clientinfo_t *ci;
s = cl.configstrings[player + CS_PLAYERSKINS];
ci = &cl.clientinfo[player];
CL_LoadClientinfo(ci, s);
}
void
CL_ParseConfigString(void)
{
int i, length;
char *s;
char olds[MAX_QPATH];
i = MSG_ReadShort(&net_message);
if ((i < 0) || (i >= MAX_CONFIGSTRINGS))
{
Com_Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
}
s = MSG_ReadString(&net_message);
Q_strlcpy(olds, cl.configstrings[i], sizeof(olds));
length = strlen(s);
if (length > sizeof(cl.configstrings) - sizeof(cl.configstrings[0])*i - 1)
{
Com_Error(ERR_DROP, "CL_ParseConfigString: oversize configstring");
}
strcpy(cl.configstrings[i], s);
/* do something apropriate */
if ((i >= CS_LIGHTS) && (i < CS_LIGHTS + MAX_LIGHTSTYLES))
{
CL_SetLightstyle(i - CS_LIGHTS);
}
else if (i == CS_CDTRACK)
{
if (cl.refresh_prepped)
{
int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10);
OGG_PlayTrack(track, true, true);
}
}
else if ((i >= CS_MODELS) && (i < CS_MODELS + MAX_MODELS))
{
if (cl.refresh_prepped)
{
cl.model_draw[i - CS_MODELS] = R_RegisterModel(cl.configstrings[i]);
if (cl.configstrings[i][0] == '*')
{
cl.model_clip[i - CS_MODELS] = CM_InlineModel(cl.configstrings[i]);
}
else
{
cl.model_clip[i - CS_MODELS] = NULL;
}
}
}
else if ((i >= CS_SOUNDS) && (i < CS_SOUNDS + MAX_MODELS))
{
if (cl.refresh_prepped)
{
cl.sound_precache[i - CS_SOUNDS] =
S_RegisterSound(cl.configstrings[i]);
}
}
else if ((i >= CS_IMAGES) && (i < CS_IMAGES + MAX_MODELS))
{
if (cl.refresh_prepped)
{
cl.image_precache[i - CS_IMAGES] = Draw_FindPic(cl.configstrings[i]);
}
}
else if ((i >= CS_PLAYERSKINS) && (i < CS_PLAYERSKINS + MAX_CLIENTS))
{
if (cl.refresh_prepped && strcmp(olds, s))
{
CL_ParseClientinfo(i - CS_PLAYERSKINS);
}
}
}
void
CL_ParseStartSoundPacket(void)
{
vec3_t pos_v;
float *pos;
int channel, ent;
int sound_num;
float volume;
float attenuation;
int flags;
float ofs;
flags = MSG_ReadByte(&net_message);
sound_num = MSG_ReadByte(&net_message);
if (flags & SND_VOLUME)
{
volume = MSG_ReadByte(&net_message) / 255.0f;
}
else
{
volume = DEFAULT_SOUND_PACKET_VOLUME;
}
if (flags & SND_ATTENUATION)
{
attenuation = MSG_ReadByte(&net_message) / 64.0f;
}
else
{
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
}
if (flags & SND_OFFSET)
{
ofs = MSG_ReadByte(&net_message) / 1000.0f;
}
else
{
ofs = 0;
}
if (flags & SND_ENT)
{
/* entity reletive */
channel = MSG_ReadShort(&net_message);
ent = channel >> 3;
if (ent > MAX_EDICTS)
{
Com_Error(ERR_DROP, "CL_ParseStartSoundPacket: ent = %i", ent);
}
channel &= 7;
}
else
{
ent = 0;
channel = 0;
}
if (flags & SND_POS)
{
/* positioned in space */
MSG_ReadPos(&net_message, pos_v);
pos = pos_v;
}
else
{
/* use entity number */
pos = NULL;
}
if (!cl.sound_precache[sound_num])
{
return;
}
S_StartSound(pos, ent, channel, cl.sound_precache[sound_num],
volume, attenuation, ofs);
}
void
SHOWNET(char *s)
{
if (cl_shownet->value >= 2)
{
Com_Printf("%3i:%s\n", net_message.readcount - 1, s);
}
}
void
CL_ParseServerMessage(void)
{
int cmd;
char *s;
int i;
/* if recording demos, copy the message out */
if (cl_shownet->value == 1)
{
Com_Printf("%i ", net_message.cursize);
}
else if (cl_shownet->value >= 2)
{
Com_Printf("------------------\n");
}
/* parse the message */
while (1)
{
if (net_message.readcount > net_message.cursize)
{
Com_Error(ERR_DROP, "CL_ParseServerMessage: Bad server message");
break;
}
cmd = MSG_ReadByte(&net_message);
if (cmd == -1)
{
SHOWNET("END OF MESSAGE");
break;
}
if (cl_shownet->value >= 2)
{
if (!svc_strings[cmd])
{
Com_Printf("%3i:BAD CMD %i\n", net_message.readcount - 1, cmd);
}
else
{
SHOWNET(svc_strings[cmd]);
}
}
/* other commands */
switch (cmd)
{
default:
Com_Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n");
break;
case svc_nop:
break;
case svc_disconnect:
Com_Error(ERR_DISCONNECT, "Server disconnected\n");
break;
case svc_reconnect:
Com_Printf("Server disconnected, reconnecting\n");
if (cls.download)
{
/* close download */
fclose(cls.download);
cls.download = NULL;
}
cls.state = ca_connecting;
cls.connect_time = -99999; /* CL_CheckForResend() will fire immediately */
break;
case svc_print:
i = MSG_ReadByte(&net_message);
if (i == PRINT_CHAT)
{
S_StartLocalSound("misc/talk.wav");
con.ormask = 128;
}
Com_Printf("%s", MSG_ReadString(&net_message));
con.ormask = 0;
break;
case svc_centerprint:
SCR_CenterPrint(MSG_ReadString(&net_message));
break;
case svc_stufftext:
s = MSG_ReadString(&net_message);
Com_DPrintf("stufftext: %s\n", s);
Cbuf_AddText(s);
break;
case svc_serverdata:
Cbuf_Execute(); /* make sure any stuffed commands are done */
CL_ParseServerData();
break;
case svc_configstring:
CL_ParseConfigString();
break;
case svc_sound:
CL_ParseStartSoundPacket();
break;
case svc_spawnbaseline:
CL_ParseBaseline();
break;
case svc_temp_entity:
CL_ParseTEnt();
break;
case svc_muzzleflash:
CL_AddMuzzleFlash();
break;
case svc_muzzleflash2:
CL_AddMuzzleFlash2();
break;
case svc_download:
CL_ParseDownload();
break;
case svc_frame:
CL_ParseFrame();
break;
case svc_inventory:
CL_ParseInventory();
break;
case svc_layout:
s = MSG_ReadString(&net_message);
Q_strlcpy(cl.layout, s, sizeof(cl.layout));
break;
case svc_playerinfo:
case svc_packetentities:
case svc_deltapacketentities:
Com_Error(ERR_DROP, "Out of place frame data");
break;
}
}
CL_AddNetgraph();
/* we don't know if it is ok to save a demo message
until after we have parsed the frame */
if (cls.demorecording && !cls.demowaiting)
{
CL_WriteDemoMessage();
}
}