mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
6d5ffa9f8e
There's still some cleanup to do, but everything seems to be working nicely: `make -j` works, `make distcheck` passes. There is probably plenty of bitrot in the package directories (RPM, debian), though. The vc project files have been removed since those versions are way out of date and quakeforge is pretty much dependent on gcc now anyway. Most of the old Makefile.am files are now Makemodule.am. This should allow for new Makefile.am files that allow local building (to be added on an as-needed bases). The current remaining Makefile.am files are for standalone sub-projects.a The installable bins are currently built in the top-level build directory. This may change if the clutter gets to be too much. While this does make a noticeable difference in build times, the main reason for the switch was to take care of the growing dependency issues: now it's possible to build tools for code generation (eg, using qfcc and ruamoko programs for code-gen).
1087 lines
25 KiB
C
1087 lines
25 KiB
C
/*
|
|
sv_parse.c
|
|
|
|
quakeworld protocol parsing
|
|
|
|
Copyright (C) 2005 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie
|
|
Date: 2005/05/06
|
|
|
|
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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "QF/cmd.h"
|
|
#include "QF/console.h"
|
|
#include "QF/dstring.h"
|
|
#include "QF/hash.h"
|
|
#include "QF/idparse.h"
|
|
#include "QF/info.h"
|
|
#include "QF/msg.h"
|
|
#include "QF/qendian.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/va.h"
|
|
|
|
#include "qw/bothdefs.h"
|
|
#include "qw/msg_ucmd.h"
|
|
#include "qw/protocol.h"
|
|
|
|
#include "qtv/include/client.h"
|
|
#include "qtv/include/connection.h"
|
|
#include "qtv/include/qtv.h"
|
|
#include "qtv/include/server.h"
|
|
|
|
static void
|
|
sv_serverdata (server_t *sv, qmsg_t *msg)
|
|
{
|
|
const char *str;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_SOUNDS; i++) {
|
|
if (sv->soundlist[i]) {
|
|
free (sv->soundlist[i]);
|
|
sv->soundlist[i] = 0;
|
|
}
|
|
}
|
|
for (i = 0; i < MAX_MODELS; i++) {
|
|
if (sv->modellist[i]) {
|
|
free (sv->modellist[i]);
|
|
sv->modellist[i] = 0;
|
|
}
|
|
}
|
|
|
|
sv->ver = MSG_ReadLong (msg);
|
|
sv->spawncount = MSG_ReadLong (msg);
|
|
sv->gamedir = strdup (MSG_ReadString (msg));
|
|
|
|
sv->message = strdup (MSG_ReadString (msg));
|
|
sv->movevars.gravity = MSG_ReadFloat (msg);
|
|
sv->movevars.stopspeed = MSG_ReadFloat (msg);
|
|
sv->movevars.maxspeed = MSG_ReadFloat (msg);
|
|
sv->movevars.spectatormaxspeed = MSG_ReadFloat (msg);
|
|
sv->movevars.accelerate = MSG_ReadFloat (msg);
|
|
sv->movevars.airaccelerate = MSG_ReadFloat (msg);
|
|
sv->movevars.wateraccelerate = MSG_ReadFloat (msg);
|
|
sv->movevars.friction = MSG_ReadFloat (msg);
|
|
sv->movevars.waterfriction = MSG_ReadFloat (msg);
|
|
sv->movevars.entgravity = MSG_ReadFloat (msg);
|
|
|
|
sv->cdtrack = MSG_ReadByte (msg);
|
|
sv->sounds = MSG_ReadByte (msg);
|
|
|
|
COM_TokenizeString (MSG_ReadString (msg), qtv_args);
|
|
cmd_args = qtv_args;
|
|
Info_Destroy (sv->info);
|
|
sv->info = Info_ParseString (Cmd_Argv (1), MAX_SERVERINFO_STRING, 0);
|
|
|
|
str = Info_ValueForKey (sv->info, "hostname");
|
|
if (strcmp (str, "unnamed"))
|
|
qtv_printf ("%s: %s\n", sv->name, str);
|
|
str = Info_ValueForKey (sv->info, "*version");
|
|
qtv_printf ("%s: QW %s\n", sv->name, str);
|
|
str = Info_ValueForKey (sv->info, "*qf_version");
|
|
if (str[0])
|
|
qtv_printf ("%s: QuakeForge %s\n", sv->name, str);
|
|
qtv_printf ("%s: gamedir: %s\n", sv->name, sv->gamedir);
|
|
str = Info_ValueForKey (sv->info, "map");
|
|
qtv_printf ("%s: (%s) %s\n", sv->name, str, sv->message);
|
|
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va ("soundlist %i %i", sv->spawncount, 0));
|
|
sv->next_run = realtime;
|
|
}
|
|
|
|
static void
|
|
sv_soundlist (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int numsounds = MSG_ReadByte (msg);
|
|
int n;
|
|
const char *str;
|
|
|
|
for (;;) {
|
|
str = MSG_ReadString (msg);
|
|
if (!str[0])
|
|
break;
|
|
//qtv_printf ("%s\n", str);
|
|
numsounds++;
|
|
if (numsounds == MAX_SOUNDS) {
|
|
while (str[0])
|
|
str = MSG_ReadString (msg);
|
|
MSG_ReadByte (msg);
|
|
return;
|
|
}
|
|
sv->soundlist[numsounds - 1] = strdup (str);
|
|
}
|
|
n = MSG_ReadByte (msg);
|
|
if (n) {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va ("soundlist %d %d", sv->spawncount, n));
|
|
} else {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va ("modellist %d %d", sv->spawncount, 0));
|
|
}
|
|
sv->next_run = realtime;
|
|
}
|
|
|
|
static void
|
|
sv_modellist (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int nummodels = MSG_ReadByte (msg);
|
|
int n;
|
|
const char *str;
|
|
|
|
for (;;) {
|
|
str = MSG_ReadString (msg);
|
|
if (!str[0])
|
|
break;
|
|
//qtv_printf ("%s\n", str);
|
|
n = nummodels++;
|
|
if (nummodels == MAX_MODELS) {
|
|
while (str[0])
|
|
str = MSG_ReadString (msg);
|
|
MSG_ReadByte (msg);
|
|
return;
|
|
}
|
|
sv->modellist[n] = strdup (str);
|
|
if (!strcmp (sv->modellist[n], "progs/player.mdl"))
|
|
sv->playermodel = n;
|
|
}
|
|
n = MSG_ReadByte (msg);
|
|
if (n) {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va ("modellist %d %d", sv->spawncount, n));
|
|
} else {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va ("prespawn %d 0 0", sv->spawncount));
|
|
sv->signon = 1;
|
|
}
|
|
sv->next_run = realtime;
|
|
}
|
|
|
|
static void
|
|
sv_cmd_f (server_t *sv)
|
|
{
|
|
if (Cmd_Argc () > 1) {
|
|
if (!strcmp (Cmd_Argv(1), "spawn"))
|
|
sv->signon = 0;
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
SZ_Print (&sv->netchan.message, Cmd_Args (1));
|
|
}
|
|
sv->next_run = realtime;
|
|
}
|
|
|
|
static void
|
|
sv_skins_f (server_t *sv)
|
|
{
|
|
int i;
|
|
// we don't actually bother checking skins here, but this is a good way
|
|
// to get everything ready at the last miniute before we start getting
|
|
// actual in-game update messages
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message, va ("begin %d", sv->spawncount));
|
|
sv->next_run = realtime;
|
|
sv->connected = 2;
|
|
sv->delta = -1;
|
|
|
|
for (i = 0; i < UPDATE_BACKUP; i++) {
|
|
sv->frames[i].entities.entities = sv->entity_states[i];
|
|
sv->frames[i].players.players = sv->player_states[i];
|
|
}
|
|
Server_BroadcastCommand (sv, "reconnect\n");
|
|
}
|
|
|
|
static void
|
|
sv_changing_f (server_t *sv)
|
|
{
|
|
client_t *cl;
|
|
|
|
sv->connected = 1;
|
|
sv->num_signon_buffers = 0;
|
|
qtv_printf ("Changing map...\n");
|
|
MSG_WriteByte (&sv->netchan.message, qtv_nop);
|
|
sv->next_run = realtime;
|
|
Server_BroadcastCommand (sv, "changing\n");
|
|
for (cl = sv->clients; cl; cl = cl->next) {
|
|
cl->connected = 0;
|
|
Client_SendMessages (cl);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_reconnect_f (server_t *sv)
|
|
{
|
|
sizebuf_t *msg = &sv->netchan.message;
|
|
|
|
qtv_printf ("Reconnecting...\n");
|
|
MSG_WriteByte (msg, qtv_stringcmd);
|
|
MSG_WriteString (msg, "new");
|
|
sv->next_run = realtime;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
void (*func) (server_t *sv);
|
|
} svcmd_t;
|
|
|
|
svcmd_t svcmds[] = {
|
|
{"cmd", sv_cmd_f},
|
|
{"skins", sv_skins_f},
|
|
{"changing", sv_changing_f},
|
|
{"reconnect", sv_reconnect_f},
|
|
|
|
{0, 0},
|
|
};
|
|
|
|
void
|
|
sv_stringcmd (server_t *sv, qmsg_t *msg)
|
|
{
|
|
svcmd_t *c;
|
|
const char *name;
|
|
|
|
COM_TokenizeString (MSG_ReadString (msg), qtv_args);
|
|
cmd_args = qtv_args;
|
|
name = Cmd_Argv (0);
|
|
|
|
for (c = svcmds; c->name; c++)
|
|
if (strcmp (c->name, name) == 0)
|
|
break;
|
|
if (!c->name) {
|
|
qtv_printf ("Bad QTV command: %s\n", name);
|
|
return;
|
|
}
|
|
c->func (sv);
|
|
}
|
|
|
|
static void
|
|
sv_parse_delta (qmsg_t *msg, int bits, entity_state_t *ent)
|
|
{
|
|
ent->number = bits & 511;
|
|
bits &= ~511;
|
|
|
|
if (bits & U_MOREBITS)
|
|
bits |= MSG_ReadByte (msg);
|
|
if (bits & U_EXTEND1) {
|
|
bits |= MSG_ReadByte (msg) << 16;
|
|
if (bits & U_EXTEND2)
|
|
bits |= MSG_ReadByte (msg) << 24;
|
|
}
|
|
if (bits & U_MODEL)
|
|
ent->modelindex = MSG_ReadByte (msg);
|
|
if (bits & U_FRAME)
|
|
ent->frame = (ent->frame & 0xff00) | MSG_ReadByte (msg);
|
|
if (bits & U_COLORMAP)
|
|
ent->colormap = MSG_ReadByte (msg);
|
|
if (bits & U_SKIN)
|
|
ent->skinnum = MSG_ReadByte (msg);
|
|
if (bits & U_EFFECTS)
|
|
ent->effects = (ent->effects & 0xff00) | MSG_ReadByte (msg);
|
|
if (bits & U_ORIGIN1)
|
|
ent->origin[0] = MSG_ReadCoord (msg);
|
|
if (bits & U_ANGLE1)
|
|
ent->angles[0] = MSG_ReadAngle (msg);
|
|
if (bits & U_ORIGIN2)
|
|
ent->origin[1] = MSG_ReadCoord (msg);
|
|
if (bits & U_ANGLE2)
|
|
ent->angles[1] = MSG_ReadAngle (msg);
|
|
if (bits & U_ORIGIN3)
|
|
ent->origin[2] = MSG_ReadCoord (msg);
|
|
if (bits & U_ANGLE3)
|
|
ent->angles[2] = MSG_ReadAngle (msg);
|
|
if (bits & U_SOLID) {
|
|
// FIXME
|
|
}
|
|
if (!(bits & U_EXTEND1))
|
|
return;
|
|
if (bits & U_ALPHA)
|
|
ent->alpha = MSG_ReadByte (msg);
|
|
if (bits & U_SCALE)
|
|
ent->scale = MSG_ReadByte (msg);
|
|
if (bits & U_EFFECTS2)
|
|
ent->effects = (ent->effects & 0x00ff) | (MSG_ReadByte (msg) << 8);
|
|
if (bits & U_GLOWSIZE)
|
|
ent->glow_size = MSG_ReadByte (msg);
|
|
if (bits & U_GLOWCOLOR)
|
|
ent->glow_color = MSG_ReadByte (msg);
|
|
if (bits & U_COLORMOD)
|
|
ent->colormod = MSG_ReadByte (msg);
|
|
if (!(bits & U_EXTEND1))
|
|
return;
|
|
if (bits & U_FRAME2)
|
|
ent->frame = (ent->frame & 0x00ff) | (MSG_ReadByte (msg) << 8);
|
|
}
|
|
|
|
static void
|
|
flush_entity_packet (server_t *sv, qmsg_t *msg)
|
|
{
|
|
entity_state_t dummy;
|
|
int word;
|
|
|
|
memset (&dummy, 0, sizeof (dummy));
|
|
sv->frames[sv->netchan.incoming_sequence & UPDATE_MASK].invalid = true;
|
|
while (1) {
|
|
word = MSG_ReadShort (msg);
|
|
if (msg->badread) {
|
|
qtv_printf ("msg_badread in packetentities\n");
|
|
return;
|
|
}
|
|
if (!word)
|
|
break;
|
|
sv_parse_delta (msg, word, &dummy);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_packetentities (server_t *sv, qmsg_t *msg, int delta)
|
|
{
|
|
unsigned short word;
|
|
int newnum, oldnum, from, num;
|
|
int newindex, oldindex;
|
|
int newpacket, oldpacket;
|
|
int full;
|
|
packet_entities_t *oldp, *newp, dummy;
|
|
|
|
if (sv->connected < 2) {
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
|
|
newpacket = sv->netchan.incoming_sequence & UPDATE_MASK;
|
|
newp = &sv->frames[newpacket].entities;
|
|
sv->frames[newpacket].invalid = false;
|
|
|
|
if (delta) {
|
|
from = MSG_ReadByte (msg);
|
|
oldpacket = sv->frames[newpacket].delta_sequence;
|
|
if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK))
|
|
qtv_printf ("WARNING: from mismatch\n");
|
|
} else {
|
|
oldpacket = -1;
|
|
}
|
|
full = 0;
|
|
if (oldpacket != -1) {
|
|
if (sv->netchan.outgoing_sequence - oldpacket > UPDATE_BACKUP) {
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
oldp = &sv->frames[oldpacket & UPDATE_MASK].entities;
|
|
sv->validsequence = sv->netchan.incoming_sequence;
|
|
} else {
|
|
oldp = &dummy;
|
|
dummy.num_entities = 0;
|
|
sv->validsequence = sv->netchan.incoming_sequence;
|
|
full = 1;
|
|
memset (sv->ent_valid, 0, sizeof (sv->ent_valid));
|
|
}
|
|
//qtv_printf ("newp = %-5d oldp = %d\n", newpacket, oldpacket & UPDATE_MASK);
|
|
sv->delta = sv->netchan.incoming_sequence;
|
|
newindex = oldindex = 0;
|
|
newp->num_entities = 0;
|
|
while (1) {
|
|
word = MSG_ReadShort (msg);
|
|
if (msg->badread) { // something didn't parse right...
|
|
qtv_printf ("msg_badread in packetentities\n");
|
|
return;
|
|
}
|
|
//qtv_printf ("word = %04x new = %-3d old = %-3d\n", word, newindex, oldindex);
|
|
if (!word) {
|
|
// copy rest of ents from old packet
|
|
while (oldindex < oldp->num_entities) {
|
|
if (newindex >= MAX_DEMO_PACKET_ENTITIES) {
|
|
qtv_printf ("A too many packet entities\n");
|
|
Sys_Quit ();
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
newp->entities[newindex] = oldp->entities[oldindex];
|
|
num = newp->entities[newindex].number;
|
|
sv->entities[num] = newp->entities[newindex];
|
|
sv->ent_valid[num] = 1;
|
|
newindex++;
|
|
oldindex++;
|
|
}
|
|
break;
|
|
}
|
|
newnum = word & 511;
|
|
oldnum = 9999;
|
|
if (oldindex < oldp->num_entities)
|
|
oldnum = oldp->entities[oldindex].number;
|
|
//qtv_printf (" %-3d %-4d %3d\n", newnum, oldnum, oldp->num_entities);
|
|
|
|
while (newnum > oldnum) {
|
|
if (full) {
|
|
qtv_printf ("WARNING: oldcopy on full update\n");
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
if (newindex >= MAX_DEMO_PACKET_ENTITIES) {
|
|
qtv_printf ("B too many packet entities\n");
|
|
Sys_Quit ();
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
newp->entities[newindex] = oldp->entities[oldindex];
|
|
num = newp->entities[newindex].number;
|
|
sv->entities[num] = newp->entities[newindex];
|
|
sv->ent_valid[num] = 1;
|
|
newindex++;
|
|
oldindex++;
|
|
oldnum = 9999;
|
|
if (oldindex < oldp->num_entities)
|
|
oldnum = oldp->entities[oldindex].number;
|
|
//qtv_printf (" %-3d %-4d %3d\n", newnum, oldnum, oldp->num_entities);
|
|
}
|
|
|
|
if (newnum < oldnum) {
|
|
if (word & U_REMOVE) {
|
|
if (full) {
|
|
qtv_printf ("WARNING: U_REMOVE on full update\n");
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
if (newindex >= MAX_DEMO_PACKET_ENTITIES) {
|
|
qtv_printf ("C too many packet entities\n");
|
|
Sys_Quit ();
|
|
flush_entity_packet (sv, msg);
|
|
return;
|
|
}
|
|
newp->entities[newindex] = sv->baselines[newnum];
|
|
sv_parse_delta (msg, word, &newp->entities[newindex]);
|
|
sv->entities[newnum] = newp->entities[newindex];
|
|
newindex++;
|
|
continue;
|
|
}
|
|
if (newnum == oldnum) {
|
|
if (full) {
|
|
sv->validsequence = 0;
|
|
qtv_printf ("WARNING: delta on full update\n");
|
|
}
|
|
if (word & U_REMOVE) {
|
|
sv->ent_valid[newnum] = 0;
|
|
oldindex++;
|
|
continue;
|
|
}
|
|
newp->entities[newindex] = oldp->entities[oldindex];
|
|
sv_parse_delta (msg, word, &newp->entities[newindex]);
|
|
sv->entities[newnum] = newp->entities[newindex];
|
|
sv->ent_valid[newnum] = 1;
|
|
newindex++;
|
|
oldindex++;
|
|
}
|
|
}
|
|
newp->num_entities = newindex;
|
|
}
|
|
|
|
static void
|
|
parse_player_delta (qmsg_t *msg, plent_state_t *from, plent_state_t *to)
|
|
{
|
|
int i;
|
|
int flags;
|
|
|
|
flags = to->flags = MSG_ReadShort (msg);
|
|
MSG_ReadCoordV (msg, to->origin);
|
|
to->frame = (to->frame & 0xff00) | MSG_ReadByte (msg);
|
|
if (flags & PF_MSEC)
|
|
to->msec = MSG_ReadByte (msg);
|
|
// qtv_printf ("%02x\n", msg->message->data[msg->readcount]);
|
|
if (flags & PF_COMMAND)
|
|
MSG_ReadDeltaUsercmd (msg, &from->cmd, &to->cmd);
|
|
for (i = 0; i < 3; i++) {
|
|
if (flags & (PF_VELOCITY1 << i))
|
|
to->velocity[i] = (short) MSG_ReadShort (msg);
|
|
}
|
|
if (flags & PF_MODEL)
|
|
to->modelindex = MSG_ReadByte (msg);
|
|
if (flags & PF_SKINNUM)
|
|
to->skinnum = MSG_ReadByte (msg);
|
|
if (flags & PF_EFFECTS)
|
|
to->effects = (to->effects & 0xff00) | MSG_ReadByte (msg);;
|
|
if (flags & PF_WEAPONFRAME)
|
|
to->weaponframe = MSG_ReadByte (msg);
|
|
if (flags & PF_QF) {
|
|
int bits;
|
|
|
|
bits = MSG_ReadByte (msg);
|
|
if (bits & PF_ALPHA)
|
|
to->alpha = MSG_ReadByte (msg);
|
|
if (bits & PF_SCALE)
|
|
to->scale = MSG_ReadByte (msg);
|
|
if (bits & PF_EFFECTS2)
|
|
to->effects = (to->effects & 0x00ff)
|
|
| (MSG_ReadByte (msg) << 8);
|
|
if (bits & PF_GLOWSIZE)
|
|
to->glow_size = MSG_ReadByte (msg);
|
|
if (bits & PF_GLOWCOLOR)
|
|
to->glow_color = MSG_ReadByte (msg);
|
|
if (bits & PF_COLORMOD)
|
|
to->colormod = MSG_ReadByte (msg);
|
|
if (bits & PF_FRAME2)
|
|
to->frame = (to->frame & 0xff)
|
|
| (MSG_ReadByte (msg) << 8);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_playerinfo (server_t *sv, qmsg_t *msg)
|
|
{
|
|
plent_state_t dummy; // for bad player indices
|
|
plent_state_t *ent;
|
|
plent_state_t *from, *to;
|
|
int num;
|
|
int fromind, toind;
|
|
static plent_state_t null_player_state;
|
|
|
|
if (!null_player_state.alpha) {
|
|
null_player_state.alpha = 255;
|
|
null_player_state.scale = 16;
|
|
null_player_state.glow_size = 0;
|
|
null_player_state.glow_color = 254;
|
|
null_player_state.colormod = 255;
|
|
}
|
|
fromind = MSG_ReadByte (msg);
|
|
toind = sv->netchan.incoming_sequence & UPDATE_MASK;
|
|
num = MSG_ReadByte (msg);
|
|
if (num > MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", num);
|
|
ent = from = to = &dummy;
|
|
} else {
|
|
ent = &sv->players[num].ent;
|
|
from = &null_player_state;
|
|
if (fromind != 255)
|
|
from = &sv->player_states[fromind & UPDATE_MASK][num];
|
|
to = &sv->player_states[toind][num];
|
|
*to = *from;
|
|
}
|
|
parse_player_delta (msg, from, to);
|
|
// qtv_printf ("%3d %g %g %g %d\n", fromind, to->cmd.angles[0], to->cmd.angles[1], to->cmd.angles[2], to->modelindex);
|
|
*ent = *to;
|
|
}
|
|
|
|
static void
|
|
sv_serverinfo (server_t *sv, qmsg_t *msg)
|
|
{
|
|
dstring_t *key = dstring_newstr ();
|
|
dstring_t *value = dstring_newstr ();
|
|
|
|
dstring_copystr (key, MSG_ReadString (msg));
|
|
dstring_copystr (value, MSG_ReadString (msg));
|
|
|
|
Info_SetValueForKey (sv->info, key->str, value->str, 0);
|
|
|
|
dstring_delete (key);
|
|
dstring_delete (value);
|
|
}
|
|
|
|
static void
|
|
sv_setinfo (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int slot;
|
|
dstring_t *key = dstring_newstr ();
|
|
dstring_t *value = dstring_newstr ();
|
|
player_t *pl;
|
|
|
|
slot = MSG_ReadByte (msg);
|
|
dstring_copystr (key, MSG_ReadString (msg));
|
|
dstring_copystr (value, MSG_ReadString (msg));
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
} else {
|
|
pl = sv->players + slot;
|
|
if (!pl->info)
|
|
pl->info = Info_ParseString ("", MAX_INFO_STRING,
|
|
0);
|
|
Info_SetValueForKey (pl->info, key->str, value->str,
|
|
0);
|
|
}
|
|
dstring_delete (key);
|
|
dstring_delete (value);
|
|
}
|
|
|
|
static void
|
|
sv_updateuserinfo (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int slot, uid;
|
|
const char *info;
|
|
player_t *pl;
|
|
|
|
slot = MSG_ReadByte (msg);
|
|
uid = MSG_ReadLong (msg);
|
|
info = MSG_ReadString (msg);
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
return;
|
|
}
|
|
pl = sv->players + slot;
|
|
if (pl->info)
|
|
Info_Destroy (pl->info);
|
|
if (info) {
|
|
pl->info = Info_ParseString (info, MAX_INFO_STRING, 0);
|
|
pl->uid = uid;
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_updatestat (server_t *sv, qmsg_t *msg, int islong)
|
|
{
|
|
int stat, val;
|
|
player_t *pl;
|
|
|
|
stat = MSG_ReadByte (msg);
|
|
if (!islong)
|
|
val = MSG_ReadByte (msg);
|
|
else
|
|
val = MSG_ReadLong (msg);
|
|
for (pl = sv->players; pl; pl = pl->next)
|
|
pl->stats[stat] = val;
|
|
}
|
|
|
|
static void
|
|
sv_update_net (server_t *sv, qmsg_t *msg, int ping)
|
|
{
|
|
int slot, val;
|
|
player_t *pl;
|
|
|
|
slot = MSG_ReadByte (msg);
|
|
if (ping)
|
|
val = MSG_ReadShort (msg);
|
|
else
|
|
val = MSG_ReadByte (msg);
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
return;
|
|
}
|
|
pl = sv->players + slot;
|
|
if (ping)
|
|
pl->ping = val;
|
|
else
|
|
pl->pl = val;
|
|
}
|
|
|
|
static void
|
|
sv_sound (server_t *sv, qmsg_t *msg, int stop)
|
|
{
|
|
int c;
|
|
vec3_t v;
|
|
|
|
if (stop) {
|
|
MSG_ReadShort (msg);
|
|
} else {
|
|
c = MSG_ReadShort (msg);
|
|
if (c & SND_VOLUME)
|
|
MSG_ReadByte (msg);
|
|
if (c & SND_ATTENUATION)
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadCoordV (msg, v);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_setangle (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int slot;
|
|
player_t *pl;
|
|
vec3_t ang;
|
|
|
|
slot = MSG_ReadByte (msg);
|
|
MSG_ReadAngleV (msg, ang);
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
return;
|
|
}
|
|
pl = sv->players + slot;
|
|
VectorCopy (ang, pl->ent.cmd.angles);
|
|
}
|
|
|
|
static void
|
|
sv_updatefrags (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int slot, frags;
|
|
player_t *pl;
|
|
|
|
slot = MSG_ReadByte (msg);
|
|
frags = MSG_ReadShort (msg);
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
return;
|
|
}
|
|
pl = sv->players + slot;
|
|
pl->frags = frags;
|
|
}
|
|
|
|
static void
|
|
parse_baseline (qmsg_t *msg, entity_state_t *ent)
|
|
{
|
|
ent->modelindex = MSG_ReadByte (msg);
|
|
ent->frame = MSG_ReadByte (msg);
|
|
ent->colormap = MSG_ReadByte (msg);
|
|
ent->skinnum = MSG_ReadByte (msg);
|
|
MSG_ReadCoordAngleV (msg, ent->origin, ent->angles);
|
|
ent->colormod = 255;
|
|
ent->alpha = 255;
|
|
ent->scale = 16;
|
|
ent->glow_size = 254;
|
|
ent->glow_color = 254;
|
|
}
|
|
|
|
static void
|
|
sv_spawnbaseline (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int i;
|
|
|
|
i = MSG_ReadShort (msg) % MAX_SV_ENTITIES;
|
|
sv->baselines[i].number = i;
|
|
parse_baseline (msg, &sv->baselines[i]);
|
|
}
|
|
|
|
static void
|
|
sv_spawnstatic (server_t *sv, qmsg_t *msg)
|
|
{
|
|
entity_state_t ent;
|
|
parse_baseline (msg, &ent);
|
|
}
|
|
|
|
static void
|
|
parse_beam (qmsg_t *msg)
|
|
{
|
|
vec3_t start, end;
|
|
|
|
MSG_ReadShort (msg);
|
|
MSG_ReadCoordV (msg, start);
|
|
MSG_ReadCoordV (msg, end);
|
|
}
|
|
|
|
static void
|
|
sv_temp_entity (server_t *sv, qmsg_t *msg)
|
|
{
|
|
vec3_t pos;
|
|
int type;
|
|
|
|
type = MSG_ReadByte (msg);
|
|
switch (type) {
|
|
case TE_WIZSPIKE:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_KNIGHTSPIKE:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_SPIKE:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_SUPERSPIKE:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_EXPLOSION:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_TAREXPLOSION:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_LIGHTNING1:
|
|
case TE_LIGHTNING2:
|
|
case TE_LIGHTNING3:
|
|
case TE_BEAM:
|
|
parse_beam (msg);
|
|
break;
|
|
case TE_LAVASPLASH:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_TELEPORT:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_EXPLOSION2:
|
|
MSG_ReadCoordV (msg, pos);
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadByte (msg);
|
|
break;
|
|
case TE_GUNSHOT:
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_BLOOD:
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
case TE_LIGHTNINGBLOOD:
|
|
MSG_ReadCoordV (msg, pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_nails (server_t *sv, qmsg_t *msg, int nails2)
|
|
{
|
|
int c, i;
|
|
byte bits[6];
|
|
|
|
c = MSG_ReadByte (msg);
|
|
for (i = 0; i < c; i++) {
|
|
if (nails2)
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadBytes (msg, bits, 6);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_print (server_t *sv, qmsg_t *msg)
|
|
{
|
|
MSG_ReadByte (msg);
|
|
qtv_printf ("%s", MSG_ReadString (msg));
|
|
}
|
|
|
|
static void
|
|
sv_lightstyle (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int ind = MSG_ReadByte (msg);
|
|
const char *style = MSG_ReadString (msg);
|
|
|
|
if (ind > MAX_LIGHTSTYLES)
|
|
return;
|
|
if (sv->lightstyles[ind])
|
|
free (sv->lightstyles[ind]);
|
|
sv->lightstyles[ind] = strdup (style);
|
|
}
|
|
|
|
static void
|
|
sv_updateentertime (server_t *sv, qmsg_t *msg)
|
|
{
|
|
int slot = MSG_ReadByte (msg);
|
|
int time = MSG_ReadFloat (msg);
|
|
player_t *pl;
|
|
|
|
if (slot >= MAX_SV_PLAYERS) {
|
|
qtv_printf ("bogus player: %d\n", slot);
|
|
return;
|
|
}
|
|
pl = sv->players + slot;
|
|
pl->time = time;
|
|
}
|
|
|
|
void
|
|
sv_parse (server_t *sv, qmsg_t *msg, int reliable)
|
|
{
|
|
int svc;
|
|
vec3_t v;
|
|
player_t *pl;
|
|
|
|
while (1) {
|
|
int bc_len = msg->readcount;
|
|
byte *bc_data = msg->message->data + bc_len;
|
|
int send = 1;
|
|
|
|
svc = MSG_ReadByte (msg);
|
|
if (svc == -1)
|
|
break;
|
|
//qtv_printf ("sv_parse: svc: %d\n", svc);
|
|
switch (svc) {
|
|
default:
|
|
qtv_printf ("sv_parse: unknown svc: %d\n", svc);
|
|
Sys_Quit ();
|
|
return;
|
|
case svc_nop:
|
|
send = 0;
|
|
break;
|
|
//case svc_setview:
|
|
// break;
|
|
case svc_sound:
|
|
sv_sound (sv, msg, 0);
|
|
break;
|
|
case svc_print:
|
|
sv_print (sv, msg);
|
|
break;
|
|
case svc_setangle:
|
|
sv_setangle (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_updatefrags:
|
|
sv_updatefrags (sv, msg);
|
|
break;
|
|
case svc_stopsound:
|
|
sv_sound (sv, msg, 1);
|
|
break;
|
|
case svc_damage:
|
|
//XXX
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadCoordV (msg, v);
|
|
send = 0;
|
|
break;
|
|
case svc_temp_entity:
|
|
sv_temp_entity (sv, msg);
|
|
//XXX
|
|
break;
|
|
case svc_setpause:
|
|
//XXX
|
|
MSG_ReadByte (msg);
|
|
break;
|
|
case svc_centerprint:
|
|
//XXX
|
|
MSG_ReadString (msg);
|
|
break;
|
|
case svc_killedmonster:
|
|
for (pl = sv->players; pl; pl = pl->next)
|
|
pl->stats[STAT_MONSTERS]++;
|
|
break;
|
|
case svc_foundsecret:
|
|
for (pl = sv->players; pl; pl = pl->next)
|
|
pl->stats[STAT_SECRETS]++;
|
|
break;
|
|
case svc_intermission:
|
|
//XXX
|
|
MSG_ReadCoordV (msg, v);
|
|
MSG_ReadAngleV (msg, v);
|
|
break;
|
|
case svc_finale:
|
|
//XXX
|
|
MSG_ReadString (msg);
|
|
break;
|
|
case svc_cdtrack:
|
|
//XXX
|
|
MSG_ReadByte (msg);
|
|
break;
|
|
case svc_sellscreen:
|
|
//ignore
|
|
break;
|
|
case svc_smallkick:
|
|
//XXX
|
|
break;
|
|
case svc_bigkick:
|
|
//XXX
|
|
break;
|
|
case svc_updateentertime:
|
|
sv_updateentertime (sv, msg);
|
|
break;
|
|
case svc_updatestat:
|
|
case svc_updatestatlong:
|
|
sv_updatestat (sv, msg, svc == svc_updatestatlong);
|
|
break;
|
|
case svc_muzzleflash:
|
|
//XXX
|
|
MSG_ReadShort (msg);
|
|
break;
|
|
case svc_updateuserinfo:
|
|
sv_updateuserinfo (sv, msg);
|
|
break;
|
|
case svc_playerinfo:
|
|
sv_playerinfo (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_nails:
|
|
case svc_nails2:
|
|
sv_nails (sv, msg, svc == svc_nails2);
|
|
send = 0;
|
|
break;
|
|
case svc_packetentities:
|
|
sv_packetentities (sv, msg, 0);
|
|
send = 0;
|
|
break;
|
|
case svc_deltapacketentities:
|
|
sv_packetentities (sv, msg, 1);
|
|
send = 0;
|
|
break;
|
|
case svc_maxspeed:
|
|
//XXX
|
|
MSG_ReadFloat (msg);
|
|
break;
|
|
case svc_entgravity:
|
|
//XXX
|
|
MSG_ReadFloat (msg);
|
|
break;
|
|
case svc_setinfo:
|
|
sv_setinfo (sv, msg);
|
|
break;
|
|
case svc_serverinfo:
|
|
sv_serverinfo (sv, msg);
|
|
break;
|
|
case svc_updatepl:
|
|
sv_update_net (sv, msg, 0);
|
|
break;
|
|
case svc_updateping:
|
|
sv_update_net (sv, msg, 1);
|
|
break;
|
|
case svc_chokecount:
|
|
//XXX
|
|
MSG_ReadByte (msg);
|
|
send = 0;
|
|
break;
|
|
case svc_serverdata:
|
|
sv_serverdata (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_stufftext:
|
|
sv_stringcmd (sv, msg);
|
|
send = 0;
|
|
break;
|
|
|
|
case svc_soundlist:
|
|
sv_soundlist (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_modellist:
|
|
sv_modellist (sv, msg);
|
|
send = 0;
|
|
break;
|
|
|
|
case svc_spawnstaticsound:
|
|
//XXX
|
|
MSG_ReadCoordV (msg, v);
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadByte (msg);
|
|
MSG_ReadByte (msg);
|
|
send = 0;
|
|
break;
|
|
|
|
case svc_spawnbaseline:
|
|
sv_spawnbaseline (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_spawnstatic:
|
|
sv_spawnstatic (sv, msg);
|
|
send = 0;
|
|
break;
|
|
case svc_lightstyle:
|
|
sv_lightstyle (sv, msg);
|
|
break;
|
|
}
|
|
if (send) {
|
|
bc_len = msg->readcount - bc_len;
|
|
Server_Broadcast (sv, reliable, 0, bc_data, bc_len);
|
|
}
|
|
}
|
|
}
|