mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
7240d2dc80
The main goal was to get visframe out of mnode_t to make it thread-safe (each thread can have its own visframe array), but moving the plane info into mnode_t made for better data access patters when traversing the bsp tree as the plane is right there with the child indices. Nicely, the size of mnode_t is the same as before (64 bytes due to alignment), with 4 bytes wasted. Performance-wise, there seems to be very little difference. Maybe slightly slower. The unfortunate thing about the change is the plane distance is negated, possibly leading to some confusion, particularly since the box and sphere culling functions were affected. However, this is so point-plane distance calculations can be done with a single 4d dot product.
1191 lines
27 KiB
C
1191 lines
27 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"
|
|
|
|
#define QTV_LEAFS 32
|
|
typedef struct qtv_leaf_bucket_s {
|
|
struct qtv_leaf_bucket_s *next;
|
|
qtv_leaf_t qtv_leafs[QTV_LEAFS];
|
|
} qtv_leaf_bucket_t;
|
|
|
|
static qtv_leaf_bucket_t *qtv_leaf_buckets;
|
|
static qtv_leaf_bucket_t **qtv_leaf_bucket_tail = &qtv_leaf_buckets;
|
|
static qtv_leaf_t *free_qtv_leaf_list;
|
|
|
|
static qtv_leaf_t *
|
|
alloc_qtv_leaf (void)
|
|
{
|
|
qtv_leaf_bucket_t *bucket;
|
|
qtv_leaf_t *leaf;
|
|
int i;
|
|
|
|
if ((leaf = free_qtv_leaf_list)) {
|
|
free_qtv_leaf_list = leaf->next;
|
|
leaf->next = 0;
|
|
return leaf;
|
|
}
|
|
|
|
bucket = malloc (sizeof (qtv_leaf_bucket_t));
|
|
bucket->next = 0;
|
|
*qtv_leaf_bucket_tail = bucket;
|
|
qtv_leaf_bucket_tail = &bucket->next;
|
|
|
|
for (leaf = bucket->qtv_leafs, i = 0; i < QTV_LEAFS - 1; i++, leaf++)
|
|
leaf->next = leaf + 1;
|
|
leaf->next = 0;
|
|
free_qtv_leaf_list = bucket->qtv_leafs;
|
|
|
|
return alloc_qtv_leaf ();
|
|
}
|
|
|
|
static void
|
|
free_qtv_leafs (qtv_leaf_t **leafs)
|
|
{
|
|
qtv_leaf_t **l;
|
|
|
|
for (l = leafs; *l; l = &(*l)->next)
|
|
;
|
|
*l = free_qtv_leaf_list;
|
|
free_qtv_leaf_list = *leafs;
|
|
*leafs = 0;
|
|
}
|
|
|
|
static void
|
|
sv_unlink_entity (server_t *sv, qtv_entity_t *ent)
|
|
{
|
|
free_qtv_leafs (&ent->leafs);
|
|
}
|
|
|
|
static void
|
|
sv_find_touched_leafs (server_t *sv, qtv_entity_t *ent, int node_id)
|
|
{
|
|
// add an efrag if the node is a leaf
|
|
if (node_id < 0) {
|
|
mleaf_t *leaf = sv->worldmodel->brush.leafs + ~node_id;
|
|
if (leaf->contents == CONTENTS_SOLID) {
|
|
return;
|
|
}
|
|
qtv_leaf_t *ent_leaf = alloc_qtv_leaf ();
|
|
ent_leaf->num = ~node_id - 1;
|
|
ent_leaf->next = ent->leafs;
|
|
ent->leafs = ent_leaf;
|
|
return;
|
|
}
|
|
|
|
vec3_t emins, emaxs;
|
|
VectorAdd (ent->e.origin, ent->model->mins, emins);
|
|
VectorAdd (ent->e.origin, ent->model->maxs, emaxs);
|
|
|
|
mnode_t *node = sv->worldmodel->brush.nodes + node_id;
|
|
plane_t *splitplane = (plane_t *) &node->plane;
|
|
int sides = BOX_ON_PLANE_SIDE (emins, emaxs, splitplane);
|
|
if (sides & 1) {
|
|
sv_find_touched_leafs (sv, ent, node->children[0]);
|
|
}
|
|
if (sides & 2) {
|
|
sv_find_touched_leafs (sv, ent, node->children[1]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
sv_link_entity (server_t *sv, qtv_entity_t *ent)
|
|
{
|
|
sv_unlink_entity (sv, ent);
|
|
if (ent->model_index != ent->e.modelindex) {
|
|
ent->model_index = ent->e.modelindex;
|
|
ent->model = Mod_ForName (sv->modellist[ent->model_index - 1], false);
|
|
}
|
|
if (ent->model) {
|
|
sv_find_touched_leafs (sv, ent, 0);
|
|
}
|
|
}
|
|
|
|
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 (0, "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 (0, "soundlist %d %d", sv->spawncount, n));
|
|
} else {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va (0, "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 + 1;
|
|
}
|
|
n = MSG_ReadByte (msg);
|
|
if (n) {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va (0, "modellist %d %d", sv->spawncount, n));
|
|
} else {
|
|
MSG_WriteByte (&sv->netchan.message, qtv_stringcmd);
|
|
MSG_WriteString (&sv->netchan.message,
|
|
va (0, "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 (0, "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].e = newp->entities[newindex];
|
|
sv_link_entity (sv, &sv->entities[num]);
|
|
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].e = newp->entities[newindex];
|
|
sv_link_entity (sv, &sv->entities[num]);
|
|
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].e = newp->entities[newindex];
|
|
sv_link_entity (sv, &sv->entities[newnum]);
|
|
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].e = newp->entities[newindex];
|
|
sv_link_entity (sv, &sv->entities[newnum]);
|
|
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->es.flags = MSG_ReadShort (msg);
|
|
MSG_ReadCoordV (msg, (vec_t*)&to->es.origin);//FIXME
|
|
to->es.frame = (to->es.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->es.velocity[i] = (short) MSG_ReadShort (msg);
|
|
}
|
|
if (flags & PF_MODEL)
|
|
to->es.modelindex = MSG_ReadByte (msg);
|
|
if (flags & PF_SKINNUM)
|
|
to->es.skinnum = MSG_ReadByte (msg);
|
|
if (flags & PF_EFFECTS)
|
|
to->es.effects = (to->es.effects & 0xff00) | MSG_ReadByte (msg);
|
|
if (flags & PF_WEAPONFRAME)
|
|
to->es.weaponframe = MSG_ReadByte (msg);
|
|
if (flags & PF_QF) {
|
|
int bits;
|
|
|
|
bits = MSG_ReadByte (msg);
|
|
if (bits & PF_ALPHA)
|
|
to->es.alpha = MSG_ReadByte (msg);
|
|
if (bits & PF_SCALE)
|
|
to->es.scale = MSG_ReadByte (msg);
|
|
if (bits & PF_EFFECTS2)
|
|
to->es.effects = (to->es.effects & 0x00ff)
|
|
| (MSG_ReadByte (msg) << 8);
|
|
if (bits & PF_GLOWSIZE)
|
|
to->es.glow_size = MSG_ReadByte (msg);
|
|
if (bits & PF_GLOWCOLOR)
|
|
to->es.glow_color = MSG_ReadByte (msg);
|
|
if (bits & PF_COLORMOD)
|
|
to->es.colormod = MSG_ReadByte (msg);
|
|
if (bits & PF_FRAME2)
|
|
to->es.frame = (to->es.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.es.alpha) {
|
|
null_player_state.es.alpha = 255;
|
|
null_player_state.es.scale = 16;
|
|
null_player_state.es.glow_size = 0;
|
|
null_player_state.es.glow_color = 254;
|
|
null_player_state.es.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, (vec_t*)&ent->origin, ent->angles); //FIXME
|
|
ent->origin[3] = 1;
|
|
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);
|
|
sv->worldmodel = Mod_ForName (sv->modellist[0], false);
|
|
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);
|
|
}
|
|
}
|
|
}
|