mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-22 20:11:44 +00:00
fc770f310d
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1456 fc73d0e0-1445-4013-8a0c-d673dee63da5
1151 lines
28 KiB
C
1151 lines
28 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 included (GNU.txt) 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.
|
|
*/
|
|
|
|
|
|
#include "qtv.h"
|
|
|
|
#define ParseError(m) (m)->cursize = (m)->cursize+1 //
|
|
|
|
void InitNetMsg(netmsg_t *b, char *buffer, int bufferlength)
|
|
{
|
|
b->data = buffer;
|
|
b->maxsize = bufferlength;
|
|
b->readpos = 0;
|
|
b->cursize = 0;
|
|
}
|
|
|
|
//probably not the place for these any more..
|
|
unsigned char ReadByte(netmsg_t *b)
|
|
{
|
|
if (b->readpos >= b->cursize)
|
|
{
|
|
b->readpos = b->cursize+1;
|
|
return 0;
|
|
}
|
|
return b->data[b->readpos++];
|
|
}
|
|
unsigned short ReadShort(netmsg_t *b)
|
|
{
|
|
int b1, b2;
|
|
b1 = ReadByte(b);
|
|
b2 = ReadByte(b);
|
|
|
|
return b1 | (b2<<8);
|
|
}
|
|
unsigned int ReadLong(netmsg_t *b)
|
|
{
|
|
int s1, s2;
|
|
s1 = ReadShort(b);
|
|
s2 = ReadShort(b);
|
|
|
|
return s1 | (s2<<16);
|
|
}
|
|
float ReadFloat(netmsg_t *b)
|
|
{
|
|
union {
|
|
unsigned int i;
|
|
float f;
|
|
} u;
|
|
|
|
u.i = ReadLong(b);
|
|
return u.f;
|
|
}
|
|
void ReadString(netmsg_t *b, char *string, int maxlen)
|
|
{
|
|
maxlen--; //for null terminator
|
|
while(maxlen)
|
|
{
|
|
*string = ReadByte(b);
|
|
if (!*string)
|
|
return;
|
|
string++;
|
|
maxlen--;
|
|
}
|
|
*string++ = '\0'; //add the null
|
|
}
|
|
|
|
void WriteByte(netmsg_t *b, unsigned char c)
|
|
{
|
|
if (b->cursize>=b->maxsize)
|
|
return;
|
|
b->data[b->cursize++] = c;
|
|
}
|
|
void WriteShort(netmsg_t *b, unsigned short l)
|
|
{
|
|
WriteByte(b, (l&0x00ff)>>0);
|
|
WriteByte(b, (l&0xff00)>>8);
|
|
}
|
|
void WriteLong(netmsg_t *b, unsigned int l)
|
|
{
|
|
WriteByte(b, (l&0x000000ff)>>0);
|
|
WriteByte(b, (l&0x0000ff00)>>8);
|
|
WriteByte(b, (l&0x00ff0000)>>16);
|
|
WriteByte(b, (l&0xff000000)>>24);
|
|
}
|
|
void WriteFloat(netmsg_t *b, float f)
|
|
{
|
|
union {
|
|
unsigned int i;
|
|
float f;
|
|
} u;
|
|
|
|
u.f = f;
|
|
WriteLong(b, u.i);
|
|
}
|
|
void WriteString2(netmsg_t *b, const char *str)
|
|
{ //no null terminator, convienience function.
|
|
while(*str)
|
|
WriteByte(b, *str++);
|
|
}
|
|
void WriteString(netmsg_t *b, const char *str)
|
|
{
|
|
while(*str)
|
|
WriteByte(b, *str++);
|
|
WriteByte(b, 0);
|
|
}
|
|
void WriteData(netmsg_t *b, const char *data, int length)
|
|
{
|
|
int i;
|
|
unsigned char *buf;
|
|
|
|
if (b->cursize + length > b->maxsize) //urm, that's just too big. :(
|
|
return;
|
|
buf = b->data+b->cursize;
|
|
for (i = 0; i < length; i++)
|
|
*buf++ = *data++;
|
|
b->cursize+=length;
|
|
}
|
|
|
|
#define DF_ORIGIN 1
|
|
#define DF_ANGLES (1<<3)
|
|
#define DF_EFFECTS (1<<6)
|
|
#define DF_SKINNUM (1<<7)
|
|
#define DF_DEAD (1<<8)
|
|
#define DF_GIB (1<<9)
|
|
#define DF_WEAPONFRAME (1<<10)
|
|
#define DF_MODEL (1<<11)
|
|
|
|
void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable)
|
|
{
|
|
if (reliable)
|
|
{
|
|
//try and put it in the normal reliable
|
|
if (!v->backbuffered && v->netchan.message.cursize+length < v->netchan.message.maxsize)
|
|
WriteData(&v->netchan.message, buffer, length);
|
|
else if (v->backbuffered>0 && v->backbuf[v->backbuffered-1].cursize+length < v->backbuf[v->backbuffered-1].maxsize) //try and put it in the current backbuffer
|
|
WriteData(&v->backbuf[v->backbuffered-1], buffer, length);
|
|
else if (v->backbuffered == MAX_BACK_BUFFERS)
|
|
v->drop = true; //we would need too many backbuffers.
|
|
else
|
|
{
|
|
//create a new backbuffer
|
|
if (!v->backbuf[v->backbuffered].data)
|
|
{
|
|
InitNetMsg(&v->backbuf[v->backbuffered], (unsigned char *)malloc(MAX_BACKBUF_SIZE), MAX_BACKBUF_SIZE);
|
|
}
|
|
v->backbuf[v->backbuffered].cursize = 0; //make sure it's empty
|
|
WriteData(&v->backbuf[v->backbuffered], buffer, length);
|
|
v->backbuffered++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Multicast(sv_t *tv, char *buffer, int length, int to, unsigned int playermask)
|
|
{
|
|
viewer_t *v;
|
|
switch(to)
|
|
{
|
|
case dem_multiple:
|
|
case dem_single:
|
|
case dem_stats:
|
|
//check and send to them only if they're tracking this player(s).
|
|
for (v = tv->cluster->viewers; v; v = v->next)
|
|
{
|
|
if (v->server == tv)
|
|
if (v->trackplayer>=0)
|
|
if ((1<<v->trackplayer)&playermask)
|
|
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
|
|
}
|
|
break;
|
|
default:
|
|
//send to all
|
|
for (v = tv->cluster->viewers; v; v = v->next)
|
|
{
|
|
if (v->server == tv)
|
|
SendBufferToViewer(v, buffer, length, true); //FIXME: change the reliable depending on message type
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
void Broadcast(cluster_t *cluster, char *buffer, int length)
|
|
{
|
|
viewer_t *v;
|
|
for (v = cluster->viewers; v; v = v->next)
|
|
{
|
|
SendBufferToViewer(v, buffer, length, true);
|
|
}
|
|
}
|
|
|
|
static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playermask)
|
|
{
|
|
int protocol;
|
|
viewer_t *v;
|
|
|
|
protocol = ReadLong(m);
|
|
if (protocol != PROTOCOL_VERSION)
|
|
{
|
|
ParseError(m);
|
|
return;
|
|
}
|
|
|
|
tv->parsingconnectiondata = true;
|
|
|
|
ReadLong(m); //we don't care about server's servercount, it's all reliable data anyway.
|
|
|
|
ReadString(m, tv->gamedir, sizeof(tv->gamedir));
|
|
|
|
/*tv->servertime =*/ ReadFloat(m);
|
|
ReadString(m, tv->mapname, sizeof(tv->mapname));
|
|
|
|
Sys_Printf(tv->cluster, "Gamedir: %s\n", tv->gamedir);
|
|
Sys_Printf(tv->cluster, "---------------------\n");
|
|
Sys_Printf(tv->cluster, "%s\n", tv->mapname);
|
|
Sys_Printf(tv->cluster, "---------------------\n");
|
|
|
|
// get the movevars
|
|
tv->movevars.gravity = ReadFloat(m);
|
|
tv->movevars.stopspeed = ReadFloat(m);
|
|
tv->movevars.maxspeed = ReadFloat(m);
|
|
tv->movevars.spectatormaxspeed = ReadFloat(m);
|
|
tv->movevars.accelerate = ReadFloat(m);
|
|
tv->movevars.airaccelerate = ReadFloat(m);
|
|
tv->movevars.wateraccelerate = ReadFloat(m);
|
|
tv->movevars.friction = ReadFloat(m);
|
|
tv->movevars.waterfriction = ReadFloat(m);
|
|
tv->movevars.entgrav = ReadFloat(m);
|
|
|
|
for (v = tv->cluster->viewers; v; v = v->next)
|
|
{
|
|
if (v->server == tv)
|
|
v->thinksitsconnected = false;
|
|
}
|
|
|
|
tv->maxents = 0; //clear these
|
|
memset(tv->modellist, 0, sizeof(tv->modellist));
|
|
memset(tv->soundlist, 0, sizeof(tv->soundlist));
|
|
memset(tv->lightstyle, 0, sizeof(tv->lightstyle));
|
|
tv->staticsound_count = 0;
|
|
memset(tv->staticsound, 0, sizeof(tv->staticsound));
|
|
|
|
memset(tv->players, 0, sizeof(tv->players));
|
|
}
|
|
|
|
static void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
tv->cdtrack = ReadByte(m);
|
|
|
|
if (!tv->parsingconnectiondata)
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
static void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
viewer_t *v;
|
|
char text[1024];
|
|
char value[256];
|
|
qboolean fromproxy;
|
|
|
|
ReadString(m, text, sizeof(text));
|
|
|
|
if (!strcmp(text, "skins\n"))
|
|
{
|
|
const char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\n','\0'};
|
|
tv->parsingconnectiondata = false;
|
|
|
|
for (v = tv->cluster->viewers; v; v = v->next)
|
|
{
|
|
if (v->server == tv)
|
|
{
|
|
v->servercount++;
|
|
SendBufferToViewer(v, newcmd, sizeof(newcmd), true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (!strncmp(text, "fullserverinfo ", 15))
|
|
{
|
|
text[strlen(text)-1] = '\0';
|
|
text[strlen(text)-1] = '\0';
|
|
|
|
//copy over the server's serverinfo
|
|
strncpy(tv->serverinfo, text+16, sizeof(tv->serverinfo)-1);
|
|
|
|
Info_ValueForKey(tv->serverinfo, "*qtv", value, sizeof(value));
|
|
if (*value)
|
|
fromproxy = true;
|
|
else
|
|
fromproxy = false;
|
|
|
|
//add on our extra infos
|
|
Info_SetValueForStarKey(tv->serverinfo, "*qtv", VERSION, sizeof(tv->serverinfo));
|
|
Info_SetValueForStarKey(tv->serverinfo, "*z_ext", Z_EXT_STRING, sizeof(tv->serverinfo));
|
|
|
|
//change the hostname (the qtv's hostname with the server's hostname in brackets)
|
|
Info_ValueForKey(tv->serverinfo, "hostname", value, sizeof(value));
|
|
if (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')') //already has brackets
|
|
{ //the fromproxy check is because it's fairly common to find a qw server with brackets after it's name.
|
|
char *s;
|
|
s = strchr(value, '('); //so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets
|
|
snprintf(text, sizeof(text), "%s %s", tv->cluster->hostname, s);
|
|
}
|
|
else
|
|
{
|
|
if (tv->file)
|
|
snprintf(text, sizeof(text), "%s (recorded from: %s)", tv->cluster->hostname, value);
|
|
else
|
|
snprintf(text, sizeof(text), "%s (live: %s)", tv->cluster->hostname, value);
|
|
}
|
|
Info_SetValueForStarKey(tv->serverinfo, "hostname", text, sizeof(tv->serverinfo));
|
|
|
|
return;
|
|
}
|
|
else if (!strncmp(text, "cmd ", 4))
|
|
return; //commands the game server asked for are pointless.
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseSetInfo(sv_t *tv, netmsg_t *m)
|
|
{
|
|
int pnum;
|
|
char key[64];
|
|
char value[256];
|
|
pnum = ReadByte(m);
|
|
ReadString(m, key, sizeof(key));
|
|
ReadString(m, value, sizeof(value));
|
|
|
|
if (pnum < MAX_CLIENTS)
|
|
Info_SetValueForStarKey(tv->players[pnum].userinfo, key, value, sizeof(tv->players[pnum].userinfo));
|
|
|
|
if (!tv->parsingconnectiondata)
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1);
|
|
}
|
|
|
|
static void ParseServerinfo(sv_t *tv, netmsg_t *m)
|
|
{
|
|
char key[64];
|
|
char value[256];
|
|
ReadString(m, key, sizeof(key));
|
|
ReadString(m, value, sizeof(value));
|
|
|
|
if (strcmp(key, "hostname")) //don't allow the hostname to change, but allow the server to change other serverinfos.
|
|
Info_SetValueForStarKey(tv->serverinfo, key, value, sizeof(tv->serverinfo));
|
|
|
|
if (!tv->parsingconnectiondata)
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1);
|
|
}
|
|
|
|
static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned char *t;
|
|
char text[1024];
|
|
int level;
|
|
|
|
level = ReadByte(m);
|
|
ReadString(m, text, sizeof(text));
|
|
|
|
if (to == dem_all || to == dem_read)
|
|
{
|
|
if (level > 1)
|
|
{
|
|
for (t = (unsigned char*)text; *t; t++)
|
|
{
|
|
if (*t >= 146 && *t < 156)
|
|
*t = *t - 146 + '0';
|
|
if (*t == 143)
|
|
*t = '.';
|
|
if (*t == 157 || *t == 158 || *t == 159)
|
|
*t = '-';
|
|
if (*t >= 128)
|
|
*t -= 128;
|
|
if (*t == 16)
|
|
*t = '[';
|
|
if (*t == 17)
|
|
*t = ']';
|
|
if (*t == 29)
|
|
*t = '-';
|
|
if (*t == 30)
|
|
*t = '-';
|
|
if (*t == 31)
|
|
*t = '-';
|
|
if (*t == '\a') //doh. :D
|
|
*t = ' ';
|
|
}
|
|
Sys_Printf(tv->cluster, "%s", text);
|
|
}
|
|
}
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
static void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
char text[1024];
|
|
ReadString(m, text, sizeof(text));
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
static int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask)
|
|
{
|
|
int first;
|
|
|
|
first = ReadByte(m)+1;
|
|
for (; first < MAX_LIST; first++)
|
|
{
|
|
ReadString(m, list[first].name, sizeof(list[first].name));
|
|
// printf("read %i: %s\n", first, list[first].name);
|
|
if (!*list[first].name)
|
|
break;
|
|
// printf("%i: %s\n", first, list[first].name);
|
|
}
|
|
|
|
return ReadByte(m);
|
|
}
|
|
|
|
static void ParseEntityState(entity_state_t *es, netmsg_t *m) //for baselines/static entities
|
|
{
|
|
int i;
|
|
|
|
es->modelindex = ReadByte(m);
|
|
es->frame = ReadByte(m);
|
|
es->colormap = ReadByte(m);
|
|
es->skinnum = ReadByte(m);
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
es->origin[i] = ReadShort(m);
|
|
es->angles[i] = ReadByte(m);
|
|
}
|
|
}
|
|
static void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned int entnum;
|
|
entnum = ReadShort(m);
|
|
if (entnum >= MAX_ENTITIES)
|
|
{
|
|
ParseError(m);
|
|
return;
|
|
}
|
|
ParseEntityState(&tv->entity[entnum].baseline, m);
|
|
}
|
|
|
|
static void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
if (tv->staticsound_count == MAX_STATICSOUNDS)
|
|
{
|
|
tv->staticsound_count--; // don't be fatal.
|
|
Sys_Printf(tv->cluster, "Too many static sounds\n");
|
|
}
|
|
|
|
tv->staticsound[tv->staticsound_count].origin[0] = ReadShort(m);
|
|
tv->staticsound[tv->staticsound_count].origin[1] = ReadShort(m);
|
|
tv->staticsound[tv->staticsound_count].origin[2] = ReadShort(m);
|
|
tv->staticsound[tv->staticsound_count].soundindex = ReadByte(m);
|
|
tv->staticsound[tv->staticsound_count].volume = ReadByte(m);
|
|
tv->staticsound[tv->staticsound_count].attenuation = ReadByte(m);
|
|
|
|
tv->staticsound_count++;
|
|
if (!tv->parsingconnectiondata)
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
ReadShort(m);
|
|
ReadShort(m);
|
|
ReadShort(m);
|
|
ReadByte(m);
|
|
ReadByte(m);
|
|
ReadByte(m);
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
void ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
if (tv->spawnstatic_count == MAX_STATICENTITIES)
|
|
{
|
|
tv->spawnstatic_count--; // don't be fatal.
|
|
Sys_Printf(tv->cluster, "Too many static entities\n");
|
|
}
|
|
|
|
ParseEntityState(&tv->spawnstatic[tv->spawnstatic_count], m);
|
|
|
|
tv->spawnstatic_count++;
|
|
|
|
if (!tv->parsingconnectiondata)
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
|
|
{
|
|
int flags;
|
|
int num;
|
|
int i;
|
|
|
|
if (clearoldplayers)
|
|
{
|
|
for (i = 0; i < MAX_CLIENTS; i++)
|
|
{ //hide players
|
|
//they'll be sent after this packet.
|
|
tv->players[i].active = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
num = ReadByte(m);
|
|
if (num >= MAX_CLIENTS)
|
|
{
|
|
num = 0; // don't be fatal.
|
|
Sys_Printf(tv->cluster, "Too many svc_playerinfos, wrapping\n");
|
|
}
|
|
|
|
tv->players[num].old = tv->players[num].current;
|
|
|
|
flags = ReadShort(m);
|
|
tv->players[num].gibbed = !!(flags & DF_GIB);
|
|
tv->players[num].dead = !!(flags & DF_DEAD);
|
|
tv->players[num].current.frame = ReadByte(m);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (flags & (DF_ORIGIN << i))
|
|
tv->players[num].current.origin[i] = ReadShort (m);
|
|
}
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (flags & (DF_ANGLES << i))
|
|
{
|
|
tv->players[num].current.angles[i] = ReadShort(m);
|
|
}
|
|
}
|
|
|
|
if (flags & DF_MODEL)
|
|
tv->players[num].current.modelindex = ReadByte (m);
|
|
|
|
if (flags & DF_SKINNUM)
|
|
tv->players[num].current.skinnum = ReadByte (m);
|
|
|
|
if (flags & DF_EFFECTS)
|
|
tv->players[num].current.effects = ReadByte (m);
|
|
|
|
if (flags & DF_WEAPONFRAME)
|
|
tv->players[num].current.weaponframe = ReadByte (m);
|
|
|
|
tv->players[num].active = true;
|
|
|
|
|
|
tv->players[num].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->players[num].leafs,
|
|
tv->players[num].current.origin[0]/8.0f,
|
|
tv->players[num].current.origin[1]/8.0f,
|
|
tv->players[num].current.origin[2]/8.0f, 32);
|
|
}
|
|
|
|
static void FlushPacketEntities(sv_t *tv)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_ENTITIES; i++)
|
|
tv->entity[i].current.modelindex = 0;
|
|
}
|
|
|
|
static void ParsePacketEntities(sv_t *tv, netmsg_t *m)
|
|
{
|
|
int entnum;
|
|
int flags;
|
|
qboolean forcerelink;
|
|
|
|
viewer_t *v;
|
|
|
|
tv->physicstime = tv->parsetime;
|
|
|
|
if (tv->cluster->chokeonnotupdated)
|
|
for (v = tv->cluster->viewers; v; v = v->next)
|
|
{
|
|
if (v->server == tv)
|
|
v->chokeme = false;
|
|
}
|
|
|
|
//luckilly, only updated entities are here, so that keeps cpu time down a bit.
|
|
for (;;)
|
|
{
|
|
flags = ReadShort(m);
|
|
if (!flags)
|
|
break;
|
|
|
|
entnum = flags & 511;
|
|
if (tv->maxents < entnum)
|
|
tv->maxents = entnum;
|
|
flags &= ~511;
|
|
memcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t)); //ow.
|
|
if (flags & U_REMOVE)
|
|
{
|
|
tv->entity[entnum].current.modelindex = 0;
|
|
continue;
|
|
}
|
|
if (!tv->entity[entnum].current.modelindex) //lerp from baseline
|
|
{
|
|
memcpy(&tv->entity[entnum].current, &tv->entity[entnum].baseline, sizeof(entity_state_t));
|
|
forcerelink = true;
|
|
}
|
|
else
|
|
forcerelink = false;
|
|
|
|
if (flags & U_MOREBITS)
|
|
flags |= ReadByte(m);
|
|
if (flags & U_MODEL)
|
|
tv->entity[entnum].current.modelindex = ReadByte(m);
|
|
if (flags & U_FRAME)
|
|
tv->entity[entnum].current.frame = ReadByte(m);
|
|
if (flags & U_COLORMAP)
|
|
tv->entity[entnum].current.colormap = ReadByte(m);
|
|
if (flags & U_SKIN)
|
|
tv->entity[entnum].current.skinnum = ReadByte(m);
|
|
if (flags & U_EFFECTS)
|
|
tv->entity[entnum].current.effects = ReadByte(m);
|
|
|
|
if (flags & U_ORIGIN1)
|
|
tv->entity[entnum].current.origin[0] = ReadShort(m);
|
|
if (flags & U_ANGLE1)
|
|
tv->entity[entnum].current.angles[0] = ReadByte(m);
|
|
if (flags & U_ORIGIN2)
|
|
tv->entity[entnum].current.origin[1] = ReadShort(m);
|
|
if (flags & U_ANGLE2)
|
|
tv->entity[entnum].current.angles[1] = ReadByte(m);
|
|
if (flags & U_ORIGIN3)
|
|
tv->entity[entnum].current.origin[2] = ReadShort(m);
|
|
if (flags & U_ANGLE3)
|
|
tv->entity[entnum].current.angles[2] = ReadByte(m);
|
|
|
|
tv->entity[entnum].updatetime = tv->curtime;
|
|
if (!tv->entity[entnum].old.modelindex) //no old state
|
|
memcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t)); //copy the new to the old, so we don't end up with interpolation glitches
|
|
|
|
|
|
if ((flags & (U_ORIGIN1 | U_ORIGIN2 | U_ORIGIN3)) || forcerelink)
|
|
tv->entity[entnum].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->entity[entnum].leafs,
|
|
tv->entity[entnum].current.origin[0]/8.0f,
|
|
tv->entity[entnum].current.origin[1]/8.0f,
|
|
tv->entity[entnum].current.origin[2]/8.0f, 32);
|
|
}
|
|
}
|
|
|
|
static void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
int pnum;
|
|
int ping;
|
|
pnum = ReadByte(m);
|
|
ping = ReadShort(m);
|
|
|
|
if (pnum < MAX_CLIENTS)
|
|
tv->players[pnum].ping = ping;
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updateping: invalid player number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
int pnum;
|
|
int frags;
|
|
pnum = ReadByte(m);
|
|
frags = ReadShort(m);
|
|
|
|
if (pnum < MAX_CLIENTS)
|
|
tv->players[pnum].frags = frags;
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updatefrags: invalid player number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned int pnum;
|
|
int value;
|
|
int statnum;
|
|
|
|
statnum = ReadByte(m);
|
|
value = ReadByte(m);
|
|
|
|
if (statnum < MAX_STATS)
|
|
{
|
|
for (pnum = 0; pnum < MAX_CLIENTS; pnum++)
|
|
{
|
|
if (mask & (1<<pnum))
|
|
tv->players[pnum].stats[statnum] = value;
|
|
}
|
|
}
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updatestat: invalid stat number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
static void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned int pnum;
|
|
int value;
|
|
int statnum;
|
|
|
|
statnum = ReadByte(m);
|
|
value = ReadLong(m);
|
|
|
|
if (statnum < MAX_STATS)
|
|
{
|
|
for (pnum = 0; pnum < MAX_CLIENTS; pnum++)
|
|
{
|
|
if (mask & (1<<pnum))
|
|
tv->players[pnum].stats[statnum] = value;
|
|
}
|
|
}
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updatestatlong: invalid stat number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m)
|
|
{
|
|
int pnum;
|
|
pnum = ReadByte(m);
|
|
ReadLong(m);
|
|
if (pnum < MAX_CLIENTS)
|
|
ReadString(m, tv->players[pnum].userinfo, sizeof(tv->players[pnum].userinfo));
|
|
else
|
|
{
|
|
Sys_Printf(tv->cluster, "svc_updateuserinfo: invalid player number\n");
|
|
while (ReadByte(m)) //suck out the message.
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned int pnum;
|
|
int value;
|
|
|
|
pnum = ReadByte(m)%MAX_CLIENTS;
|
|
value = ReadByte(m);
|
|
|
|
if (pnum < MAX_CLIENTS)
|
|
tv->players[pnum].packetloss = value;
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updatepl: invalid player number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
unsigned int pnum;
|
|
float value;
|
|
|
|
pnum = ReadByte(m)%MAX_CLIENTS;
|
|
value = ReadFloat(m);
|
|
|
|
if (pnum < MAX_CLIENTS)
|
|
tv->players[pnum].entertime = value;
|
|
else
|
|
Sys_Printf(tv->cluster, "svc_updateentertime: invalid player number\n");
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
#define SND_VOLUME (1<<15) // a qbyte
|
|
#define SND_ATTENUATION (1<<14) // a qbyte
|
|
|
|
#define DEFAULT_SOUND_PACKET_VOLUME 255
|
|
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
|
|
int i;
|
|
unsigned short chan;
|
|
unsigned char vol;
|
|
unsigned char atten;
|
|
unsigned char sound_num;
|
|
short org[3];
|
|
|
|
chan = ReadShort(m);
|
|
|
|
if (chan & SND_VOLUME)
|
|
vol = ReadByte (m);
|
|
else
|
|
vol = DEFAULT_SOUND_PACKET_VOLUME;
|
|
|
|
if (chan & SND_ATTENUATION)
|
|
atten = ReadByte (m) / 64.0;
|
|
else
|
|
atten = DEFAULT_SOUND_PACKET_ATTENUATION;
|
|
|
|
sound_num = ReadByte (m);
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = ReadShort (m);
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
static void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
ReadByte (m);
|
|
ReadByte (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
enum {
|
|
TE_SPIKE = 0,
|
|
TE_SUPERSPIKE = 1,
|
|
TE_GUNSHOT = 2,
|
|
TE_EXPLOSION = 3,
|
|
TE_TAREXPLOSION = 4,
|
|
TE_LIGHTNING1 = 5,
|
|
TE_LIGHTNING2 = 6,
|
|
TE_WIZSPIKE = 7,
|
|
TE_KNIGHTSPIKE = 8,
|
|
TE_LIGHTNING3 = 9,
|
|
TE_LAVASPLASH = 10,
|
|
TE_TELEPORT = 11,
|
|
|
|
TE_BLOOD = 12,
|
|
TE_LIGHTNINGBLOOD = 13,
|
|
};
|
|
static void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
|
{
|
|
int i;
|
|
i = ReadByte (m);
|
|
switch(i)
|
|
{
|
|
case TE_SPIKE:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_SUPERSPIKE:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_GUNSHOT:
|
|
ReadByte (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_EXPLOSION:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_TAREXPLOSION:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_LIGHTNING1:
|
|
case TE_LIGHTNING2:
|
|
case TE_LIGHTNING3:
|
|
ReadShort (m);
|
|
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_WIZSPIKE:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_KNIGHTSPIKE:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_LAVASPLASH:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_TELEPORT:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_BLOOD:
|
|
ReadByte (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
case TE_LIGHTNINGBLOOD:
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
ReadShort (m);
|
|
break;
|
|
default:
|
|
Sys_Printf(tv->cluster, "temp entity %i not recognised\n", i);
|
|
return;
|
|
}
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, to, mask);
|
|
}
|
|
|
|
void ParseLightstyle(sv_t *tv, netmsg_t *m)
|
|
{
|
|
int style;
|
|
style = ReadByte(m);
|
|
if (style < MAX_LIGHTSTYLES)
|
|
ReadString(m, tv->lightstyle[style].name, sizeof(tv->lightstyle[style].name));
|
|
else
|
|
{
|
|
Sys_Printf(tv->cluster, "svc_lightstyle: invalid lightstyle index (%i)\n", style);
|
|
while (ReadByte(m)) //suck out the message.
|
|
{
|
|
}
|
|
}
|
|
|
|
Multicast(tv, m->data+m->startpos, m->readpos - m->startpos, dem_read, (unsigned)-1);
|
|
}
|
|
|
|
void ParseMessage(sv_t *tv, char *buffer, int length, int to, int mask)
|
|
{
|
|
netmsg_t buf;
|
|
qboolean clearoldplayers = true;
|
|
buf.cursize = length;
|
|
buf.maxsize = length;
|
|
buf.readpos = 0;
|
|
buf.data = buffer;
|
|
buf.startpos = 0;
|
|
while(buf.readpos < buf.cursize)
|
|
{
|
|
if (buf.readpos > buf.cursize)
|
|
{
|
|
Sys_Printf(tv->cluster, "Read past end of parse buffer\n");
|
|
return;
|
|
}
|
|
// printf("%i\n", buf.buffer[0]);
|
|
buf.startpos = buf.readpos;
|
|
switch (ReadByte(&buf))
|
|
{
|
|
case svc_bad:
|
|
ParseError(&buf);
|
|
Sys_Printf(tv->cluster, "ParseMessage: svc_bad\n");
|
|
return;
|
|
case svc_nop: //quakeworld isn't meant to send these.
|
|
Sys_Printf(tv->cluster, "nop\n");
|
|
break;
|
|
|
|
case svc_disconnect:
|
|
//mvdsv safely terminates it's mvds with an svc_disconnect.
|
|
//the client is meant to read that and disconnect without reading the intentionally corrupt packet following it.
|
|
//however, our demo playback is chained and looping and buffered.
|
|
//so we've already found the end of the source file and restarted parsing.
|
|
//so there's very little we can do except crash ourselves on the EndOfDemo text following the svc_disconnect
|
|
//that's a bad plan, so just stop reading this packet.
|
|
return;
|
|
|
|
case svc_updatestat:
|
|
ParseUpdateStat(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_version 4 // [long] server version
|
|
//#define svc_setview 5 // [short] entity number
|
|
case svc_sound:
|
|
ParseSound(tv, &buf, to, mask);
|
|
break;
|
|
//#define svc_time 7 // [float] server time
|
|
case svc_print:
|
|
ParsePrint(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_stufftext:
|
|
ParseStufftext(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_setangle:
|
|
ReadByte(&buf);
|
|
ReadByte(&buf);
|
|
ReadByte(&buf);
|
|
ReadByte(&buf);
|
|
break;
|
|
|
|
case svc_serverdata:
|
|
ParseServerData(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_lightstyle:
|
|
ParseLightstyle(tv, &buf);
|
|
break;
|
|
|
|
//#define svc_updatename 13 // [qbyte] [string]
|
|
case svc_updatefrags:
|
|
ParseUpdateFrags(tv, &buf, to, mask);
|
|
break;
|
|
//#define svc_clientdata 15 // <shortbits + data>
|
|
//#define svc_stopsound 16 // <see code>
|
|
//#define svc_updatecolors 17 // [qbyte] [qbyte] [qbyte]
|
|
//#define svc_particle 18 // [vec3] <variable>
|
|
case svc_damage:
|
|
ParseDamage(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_spawnstatic:
|
|
ParseSpawnStatic(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_spawnstatic2 21
|
|
case svc_spawnbaseline:
|
|
ParseBaseline(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_temp_entity:
|
|
ParseTempEntity(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_setpause 24 // [qbyte] on / off
|
|
//#define svc_signonnum 25 // [qbyte] used for the signon sequence
|
|
|
|
case svc_centerprint:
|
|
ParseCenterprint(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_killedmonster 27
|
|
//#define svc_foundsecret 28
|
|
|
|
case svc_spawnstaticsound:
|
|
ParseStaticSound(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_intermission:
|
|
ParseIntermission(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_finale 31 // [string] text
|
|
|
|
case svc_cdtrack:
|
|
ParseCDTrack(tv, &buf, to, mask);
|
|
break;
|
|
//#define svc_sellscreen 33
|
|
|
|
//#define svc_cutscene 34 //hmm... nq only... added after qw tree splitt?
|
|
|
|
case svc_smallkick:
|
|
case svc_bigkick:
|
|
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask);
|
|
break;
|
|
|
|
case svc_updateping:
|
|
ParseUpdatePing(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_updateentertime:
|
|
ParseUpdateEnterTime(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_updatestatlong:
|
|
ParseUpdateStatLong(tv, &buf, to, mask);
|
|
break;
|
|
|
|
case svc_muzzleflash:
|
|
ReadShort(&buf);
|
|
Multicast(tv, buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask);
|
|
break;
|
|
|
|
case svc_updateuserinfo:
|
|
ParseUpdateUserinfo(tv, &buf);
|
|
break;
|
|
|
|
//#define svc_download 41 // [short] size [size bytes]
|
|
case svc_playerinfo:
|
|
ParsePlayerInfo(tv, &buf, clearoldplayers);
|
|
clearoldplayers = false;
|
|
break;
|
|
//#define svc_nails 43 // [qbyte] num [48 bits] xyzpy 12 12 12 4 8
|
|
case svc_chokecount:
|
|
ReadByte(&buf);
|
|
break;
|
|
case svc_modellist:
|
|
if (!ParseList(tv, &buf, tv->modellist, to, mask))
|
|
{
|
|
int i;
|
|
if (tv->bsp)
|
|
BSP_Free(tv->bsp);
|
|
|
|
if (tv->cluster->nobsp)
|
|
tv->bsp = NULL;
|
|
else
|
|
tv->bsp = BSP_LoadModel(tv->cluster, tv->gamedir, tv->modellist[1].name);
|
|
|
|
tv->numinlines = 0;
|
|
for (i = 2; i < 256; i++)
|
|
{
|
|
if (*tv->modellist[i].name != '*')
|
|
break;
|
|
tv->numinlines = i;
|
|
}
|
|
}
|
|
break;
|
|
case svc_soundlist:
|
|
ParseList(tv, &buf, tv->soundlist, to, mask);
|
|
break;
|
|
case svc_packetentities:
|
|
FlushPacketEntities(tv);
|
|
ParsePacketEntities(tv, &buf);
|
|
break;
|
|
case svc_deltapacketentities:
|
|
ReadByte(&buf);
|
|
ParsePacketEntities(tv, &buf);
|
|
break;
|
|
//#define svc_maxspeed 49 // maxspeed change, for prediction
|
|
//#define svc_entgravity 50 // gravity change, for prediction
|
|
case svc_setinfo:
|
|
ParseSetInfo(tv, &buf);
|
|
break;
|
|
case svc_serverinfo:
|
|
ParseServerinfo(tv, &buf);
|
|
break;
|
|
case svc_updatepl:
|
|
ParsePacketloss(tv, &buf, to, mask);
|
|
break;
|
|
|
|
//#define svc_nails2 54 //qwe - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8
|
|
|
|
default:
|
|
buf.readpos = buf.startpos;
|
|
Sys_Printf(tv->cluster, "Can't handle svc %i\n", (unsigned int)ReadByte(&buf));
|
|
return;
|
|
}
|
|
}
|
|
}
|