#include "client.h"
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_DownloadFileName(char *dest, int destlen, char *fn) {
if(strncmp(fn, "players", 7) == 0)
Com_sprintf(dest, destlen, "%s/%s", BASEDIRNAME, fn);
else
Com_sprintf(dest, destlen, "%s/%s", FS_Gamedir(), fn);
}
bool CL_CheckOrDownloadFile(char *filename) {
FILE *fp;
char name[MAX_OSPATH];
if(strstr(filename, "..")) {
Com_Printf("Refusing to download a path with ..\n");
return true;
}
if(FS_LoadFile(filename, NULL) != -1) { return true;
}
strcpy(cls.downloadname, filename);
COM_StripExtension(cls.downloadname, cls.downloadtempname);
strcat(cls.downloadtempname, ".tmp");
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
fp = fopen(name, "r+b");
if(fp) { int len;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
cls.download = fp;
Com_Printf("Resuming %s\n", cls.downloadname);
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
MSG_WriteString(&cls.netchan.message, va("download %s %i", cls.downloadname, len));
} else {
Com_Printf("Downloading %s\n", cls.downloadname);
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
MSG_WriteString(&cls.netchan.message, va("download %s", cls.downloadname));
}
cls.downloadnumber++;
return false;
}
void CL_Download_f(void) {
char filename[MAX_OSPATH];
if(Cmd_Argc() != 2) {
Com_Printf("Usage: download <filename>\n");
return;
}
Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
if(strstr(filename, "..")) {
Com_Printf("Refusing to download a path with ..\n");
return;
}
if(FS_LoadFile(filename, NULL) != -1) { Com_Printf("File already exists.\n");
return;
}
strcpy(cls.downloadname, filename);
Com_Printf("Downloading %s\n", cls.downloadname);
COM_StripExtension(cls.downloadname, cls.downloadtempname);
strcat(cls.downloadtempname, ".tmp");
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
MSG_WriteString(&cls.netchan.message, va("download %s", cls.downloadname));
cls.downloadnumber++;
}
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]);
Sys_SendKeyEvents(); }
S_EndRegistration();
}
void CL_ParseDownload(void) {
int size, percent;
char name[MAX_OSPATH];
int r;
size = MSG_ReadShort(&net_message);
percent = MSG_ReadByte(&net_message);
if(size == -1) {
Com_Printf("Server does not have this file.\n");
if(cls.download) {
fclose(cls.download);
cls.download = NULL;
}
CL_RequestNextDownload();
return;
}
if(!cls.download) {
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
FS_CreatePath(name);
cls.download = fopen(name, "wb");
if(!cls.download) {
net_message.readcount += size;
Com_Printf("Failed to open %s\n", cls.downloadtempname);
CL_RequestNextDownload();
return;
}
}
fwrite(net_message.data + net_message.readcount, 1, size, cls.download);
net_message.readcount += size;
if(percent != 100) {
#if 0#endif
cls.downloadpercent = percent;
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
SZ_Print(&cls.netchan.message, "nextdl");
} else {
char oldn[MAX_OSPATH];
char newn[MAX_OSPATH];
fclose(cls.download);
CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
r = rename(oldn, newn);
if(r)
Com_Printf("failed to rename.\n");
cls.download = NULL;
cls.downloadpercent = 0;
CL_RequestNextDownload();
}
}
void CL_ParseServerData(void) {
extern cvar_t *fs_gamedirvar;
char *str;
int i;
Com_DPrintf("Serverdata packet received.\n");
CL_ClearState();
cls.state = ca_connected;
i = MSG_ReadLong(&net_message);
cls.serverProtocol = i;
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);
str = MSG_ReadString(&net_message);
strncpy(cl.gamedir, str, sizeof(cl.gamedir) - 1);
if((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) ||
(!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
Cvar_Set("game", str);
cl.playernum = MSG_ReadShort(&net_message);
str = MSG_ReadString(&net_message);
if(cl.playernum == -1) { SCR_PlayCinematic(str);
} else {
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);
cl.refresh_prepped = false;
}
}
void CL_ParseBaseline(void) {
entity_state_t *es;
uint32_t 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];
strncpy(ci->cinfo, s, sizeof(ci->cinfo));
ci->cinfo[sizeof(ci->cinfo) - 1] = 0;
strncpy(ci->name, s, sizeof(ci->name));
ci->name[sizeof(ci->name) - 1] = 0;
t = strstr(s, "\\");
if(t) {
ci->name[t - s] = 0;
s = t + 1;
}
if(cl_noskins->value || *s == 0) {
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
Com_sprintf(skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
Com_sprintf(ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
ci->model = re.RegisterModel(CMODEL_A, model_filename);
memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
ci->weaponmodel[0] = re.RegisterModel(CMODEL_A, weapon_filename);
ci->skin = re.RegisterSkin(skin_filename);
ci->icon = re.RegisterPic(ci->iconname);
} else {
strcpy(model_name, s);
t = strstr(model_name, "/");
if(!t)
t = strstr(model_name, "\\");
if(!t)
t = model_name;
*t = 0;
strcpy(skin_name, s + strlen(model_name) + 1);
Com_sprintf(model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
ci->model = re.RegisterModel(CMODEL_A, model_filename);
if(!ci->model) {
strcpy(model_name, "male");
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
ci->model = re.RegisterModel(CMODEL_A, model_filename);
}
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin(skin_filename);
if(!ci->skin && Q_stricmp(model_name, "male")) {
strcpy(model_name, "male");
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
ci->model = re.RegisterModel(CMODEL_A, model_filename);
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin(skin_filename);
}
if(!ci->skin) {
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
ci->skin = re.RegisterSkin(skin_filename);
}
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] = re.RegisterModel(CMODEL_A, weapon_filename);
if(!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) {
Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
ci->weaponmodel[i] = re.RegisterModel(CMODEL_A, weapon_filename);
}
if(!cl_vwep->value)
break; }
Com_sprintf(ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
ci->icon = re.RegisterPic(ci->iconname);
}
if(!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0]) {
ci->skin = NULL;
ci->icon = NULL;
ci->model = NULL;
ci->weaponmodel[0] = NULL;
return;
}
}
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;
char *s;
i = MSG_ReadShort(&net_message);
if(i < 0 || i >= MAX_CONFIGSTRINGS)
Com_Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
s = MSG_ReadString(&net_message);
strcpy(cl.configstrings[i], s);
if(i >= CS_LIGHTS && i < CS_LIGHTS + MAX_LIGHTSTYLES)
CL_SetLightstyle(i - CS_LIGHTS);
else if(i == CS_CDTRACK) {
} else if(i >= CS_MODELS && i < CS_MODELS + MAX_MODELS) {
if(cl.refresh_prepped) {
int model = i - CS_MODELS;
if(model != 0 && model <= CMODEL_COUNT) {
char path[MAX_QPATH];
memcpy(path, cl.configstrings[i], MAX_QPATH);
*strchr(path, ';') = 0;
cl.model_draw[model] = re.RegisterModel(model - 1, path);
unsigned int checksum;
cl.cmodel_draw[model - 1][0] = cl.model_draw[model];
cl.cmodel_clip[model - 1][0] = CM_LoadMap(model - 1, path, true, &checksum);
for(int k = 1; k < CM_NumInlineModels(model - 1); k++) {
char inline_name[6];
sprintf(inline_name, sizeof(inline_name), "*%i", k);
cl.cmodel_draw[model - 1][k] = re.RegisterModel(model - 1, inline_name);
cl.cmodel_clip[model - 1][k] = CM_InlineModel(model - 1, inline_name);
}
} else {
cl.model_draw[model] = re.RegisterModel(CMODEL_A, cl.configstrings[i]);
}
}
} 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] = re.RegisterPic(cl.configstrings[i]);
} else if(i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS + MAX_CLIENTS) {
if(cl.refresh_prepped)
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.0;
else
volume = DEFAULT_SOUND_PACKET_VOLUME;
if(flags & SND_ATTENUATION)
attenuation = MSG_ReadByte(&net_message) / 64.0;
else
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
if(flags & SND_OFFSET)
ofs = MSG_ReadByte(&net_message) / 1000.0;
else
ofs = 0;
if(flags & SND_ENT) { 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) { MSG_ReadPos(&net_message, pos_v);
pos = pos_v;
} else 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(cl_shownet->value == 1)
Com_Printf("%i ", net_message.cursize);
else if(cl_shownet->value >= 2)
Com_Printf("------------------\n");
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]);
}
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) {
fclose(cls.download);
cls.download = NULL;
}
cls.state = ca_connecting;
cls.connect_time = -99999; 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(); 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_ParseMuzzleFlash();
break;
case svc_muzzleflash2:
CL_ParseMuzzleFlash2();
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);
strncpy(cl.layout, s, sizeof(cl.layout) - 1);
break;
case svc_playerinfo:
case svc_packetentities:
case svc_deltapacketentities:
Com_Error(ERR_DROP, "Out of place frame data");
break;
}
}
CL_AddNetgraph();
if(cls.demorecording && !cls.demowaiting)
CL_WriteDemoMessage();
}