quakeforge/qw/source/sv_ents.c
Adam Olsen 4e45592439 - remove an extra NET_Init prototype
- add MAX_NET_EDICTS and MAX_NET_EDICTS_MASK defines, which are 512
  and 511 respectively
- change baselines to access the array directly, rather than through
  the entity's "data" field
- cleanup SV_ReliableSVC_Emit
- add entity remapping.  the entity number used internally in the
  server no longer matches the number sent to the client, and it
  releases the mapping after 10 seconds of inuse, so there's no
  "512 entity limit" anymore.  Still the MAX_EDICTS limit though,
  which is currently 768, but it can probably be defined to something
  much higher without any trouble.
2001-12-21 07:39:49 +00:00

613 lines
15 KiB
C

/*
sv_ents.c
(description)
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 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
*/
static const char rcsid[] =
"$Id$";
#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 "QF/msg.h"
#include "QF/net_svc_qw.h"
#include "QF/sys.h"
#include "compat.h"
#include "server.h"
#include "sv_progs.h"
/*
The PVS must include a small area around the client to allow head
bobbing or other small motion on the client side. Otherwise, a bob
might cause an entity that should be visible to not show up, especially
when the bob crosses a waterline.
*/
byte fatpvs[MAX_MAP_LEAFS / 8];
int fatbytes;
void
SV_AddToFatPVS (vec3_t org, mnode_t *node)
{
byte *pvs;
int i;
float d;
mplane_t *plane;
while (1) {
// if this is a leaf, accumulate the pvs bits
if (node->contents < 0) {
if (node->contents != CONTENTS_SOLID) {
pvs = Mod_LeafPVS ((mleaf_t *) node, sv.worldmodel);
for (i = 0; i < fatbytes; i++)
fatpvs[i] |= pvs[i];
}
return;
}
plane = node->plane;
d = DotProduct (org, plane->normal) - plane->dist;
if (d > 8)
node = node->children[0];
else if (d < -8)
node = node->children[1];
else { // go down both
SV_AddToFatPVS (org, node->children[0]);
node = node->children[1];
}
}
}
/*
SV_FatPVS
Calculates a PVS that is the inclusive or of all leafs within 8 pixels
of the given point.
*/
byte *
SV_FatPVS (vec3_t org)
{
fatbytes = (sv.worldmodel->numleafs + 31) >> 3;
memset (fatpvs, 0, fatbytes);
SV_AddToFatPVS (org, sv.worldmodel->nodes);
return fatpvs;
}
// nails are plentiful, so there is a special network protocol for them
#define MAX_NAILS 32
edict_t *nails[MAX_NAILS];
int numnails;
qboolean
SV_AddNailUpdate (edict_t *ent)
{
if (SVfloat (ent, modelindex) != sv_nailmodel
&& SVfloat (ent, modelindex) != sv_supernailmodel) return false;
if (numnails == MAX_NAILS)
return true;
nails[numnails] = ent;
numnails++;
return true;
}
void
SV_EmitNailUpdate (sizebuf_t *msg)
{
int i;
net_svc_nails_t block;
if (!numnails)
return;
block.numnails = numnails;
for (i = 0; i < numnails; i++) {
VectorCopy (SVvector (nails[i], origin), block.nails[i].origin);
VectorCopy (SVvector (nails[i], angles), block.nails[i].angles);
}
NET_SVC_Emit (svc_nails, &block, msg);
}
unsigned int
SV_EntityState_Diff (entity_state_t *from, entity_state_t *to)
{
int i;
float miss;
unsigned int bits = 0;
for (i = 0; i < 3; i++) {
miss = to->origin[i] - from->origin[i];
if (miss < -0.1 || miss > 0.1)
bits |= U_ORIGIN1 << i;
}
if (to->angles[0] != from->angles[0])
bits |= U_ANGLE1;
if (to->angles[1] != from->angles[1])
bits |= U_ANGLE2;
if (to->angles[2] != from->angles[2])
bits |= U_ANGLE3;
if (to->colormap != from->colormap)
bits |= U_COLORMAP;
if (to->skinnum != from->skinnum)
bits |= U_SKIN;
if (to->frame != from->frame)
bits |= U_FRAME;
if (to->effects != from->effects)
bits |= U_EFFECTS;
if (to->modelindex != from->modelindex)
bits |= U_MODEL;
// LordHavoc: cleaned up Endy's coding style, and added missing effects
// Ender (QSG - Begin)
// if (stdver > 1) {
if (to->alpha != from->alpha)
bits |= U_ALPHA;
if (to->scale != from->scale)
bits |= U_SCALE;
if (to->glow_size != from->glow_size)
bits |= U_GLOWSIZE;
if (to->glow_color != from->glow_color)
bits |= U_GLOWCOLOR;
if (to->colormod != from->colormod)
bits |= U_COLORMOD;
// }
// if (bits >= 16777216)
if (bits & U_GROUP_EXTEND2)
bits |= U_EXTEND2;
// if (bits >= 65536)
if (bits & U_GROUP_EXTEND1)
bits |= U_EXTEND1;
// Ender (QSG - End)
// if (bits & 511)
if (bits & U_GROUP_MOREBITS)
bits |= U_MOREBITS;
if (to->flags & U_SOLID)
bits |= U_SOLID;
return bits;
}
/*
SV_EmitPacketEntities
Writes an update of a packet_entities_t to the message.
*/
void
SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg)
{
int index;
entity_state_t *baseline;
net_svc_packetentities_t block;
block.numwords = block.numdeltas = to->num_entities;
for (index = 0; index < to->num_entities; index++) {
baseline = &baselines[to->entities[index].number];
block.deltas[index] = to->entities[index];
block.deltas[index].flags =
SV_EntityState_Diff (baseline, &to->entities[index]);
// check if it's a client that doesn't support QSG2
if (client->stdver <= 1)
block.deltas[index].flags &= U_VERSION_ID;
block.words[index] = to->entities[index].number |
(block.deltas[index].flags & ~MAX_NET_EDICTS_MASK);
}
block.words[index] = 0;
NET_SVC_Emit (svc_packetentities, &block, msg);
}
/*
SV_EmitDeltaPacketEntities
Writes a delta update of a packet_entities_t to the message.
*/
void
SV_EmitDeltaPacketEntities (client_t *client, packet_entities_t *to,
sizebuf_t *msg)
{
int newindex, oldindex, newnum, oldnum;
int word, delta;
entity_state_t *baseline;
packet_entities_t *from;
net_svc_deltapacketentities_t block;
// this is the frame that we are going to delta update from
from = &client->frames[client->delta_sequence & UPDATE_MASK].entities;
block.from = client->delta_sequence;
// SV_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK,
// client->netchan.outgoing_sequence & UPDATE_MASK);
for (newindex = 0, oldindex = 0, word = 0, delta = 0;
newindex < to->num_entities || oldindex < from->num_entities;) {
newnum = newindex >= to->num_entities ?
9999 : to->entities[newindex].number;
oldnum = oldindex >= from->num_entities ?
9999 : from->entities[oldindex].number;
if (newnum == oldnum) { // delta update from old position
// SV_Printf ("delta %i\n", newnum);
block.deltas[delta] = to->entities[newindex];
block.deltas[delta].flags =
SV_EntityState_Diff (&from->entities[oldindex],
&to->entities[newindex]);
// check if it's a client that doesn't support QSG2
if (client->stdver <= 1)
block.deltas[delta].flags &= U_VERSION_ID;
if (!block.deltas[delta].flags) { // no updates to send
oldindex++;
newindex++;
continue;
}
block.words[word] = newnum |
(block.deltas[delta].flags & ~MAX_NET_EDICTS_MASK);
oldindex++;
newindex++;
word++;
delta++;
} else if (newnum < oldnum) { // this is a new entity, send
// it from the baseline
baseline = &baselines[newnum];
// SV_Printf ("baseline %i\n", newnum);
block.deltas[delta] = to->entities[newindex];
block.deltas[delta].flags =
SV_EntityState_Diff (baseline, &to->entities[newindex]);
// check if it's a client that doesn't support QSG2
if (client->stdver <= 1)
block.deltas[delta].flags &= U_VERSION_ID;
block.words[word] = newnum |
(block.deltas[delta].flags & ~MAX_NET_EDICTS_MASK);
newindex++;
word++;
delta++;
} else if (newnum > oldnum) { // the old entity isn't
// present in the new message
// SV_Printf ("remove %i\n", oldnum);
block.words[word] = oldnum | U_REMOVE;
oldindex++;
word++;
}
}
block.words[word] = 0;
NET_SVC_Emit (svc_deltapacketentities, &block, msg);
}
void
SV_WritePlayersToClient (client_t *client, edict_t *clent, byte * pvs,
sizebuf_t *msg)
{
int i, j;
client_t *cl;
edict_t *ent;
net_svc_playerinfo_t block;
for (j = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) {
if (cl->state != cs_spawned)
continue;
ent = cl->edict;
// ZOID visibility tracking
if (ent != clent &&
!(client->spec_track && client->spec_track - 1 == j)) {
if (cl->spectator)
continue;
// ignore if not touching a PV leaf
for (i = 0; i < ent->num_leafs; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
break;
if (i == ent->num_leafs)
continue; // not visible
}
block.flags = PF_MSEC | PF_COMMAND;
if (SVfloat (ent, modelindex) != sv_playermodel)
block.flags |= PF_MODEL;
for (i = 0; i < 3; i++)
if (SVvector (ent, velocity)[i])
block.flags |= PF_VELOCITY1 << i;
if (SVfloat (ent, effects))
block.flags |= PF_EFFECTS;
if (SVfloat (ent, skin))
block.flags |= PF_SKINNUM;
if (SVfloat (ent, health) <= 0)
block.flags |= PF_DEAD;
if (SVvector (ent, mins)[2] != -24)
block.flags |= PF_GIB;
if (cl->spectator) { // only sent origin and velocity to
// spectators
block.flags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3;
} else if (ent == clent) { // don't send a lot of data on
// personal entity
block.flags &= ~(PF_MSEC | PF_COMMAND);
if (SVfloat (ent, weaponframe))
block.flags |= PF_WEAPONFRAME;
}
if (client->spec_track && client->spec_track - 1 == j &&
SVfloat (ent, weaponframe))
block.flags |= PF_WEAPONFRAME;
block.playernum = j;
VectorCopy (SVvector (ent, origin), block.origin);
block.frame = SVfloat (ent, frame);
block.msec = 1000 * (sv.time - cl->localtime);
if (block.msec > 255)
block.msec = 255;
block.usercmd = cl->lastcmd;
if (SVfloat (ent, health) <= 0) { // don't show the corpse
// looking around...
block.usercmd.angles[0] = 0;
block.usercmd.angles[1] = SVvector (ent, angles)[1];
block.usercmd.angles[0] = 0;
}
block.usercmd.buttons = 0; // never send buttons
block.usercmd.impulse = 0; // never send impulses
VectorCopy (SVvector (ent, velocity), block.velocity);
block.modelindex = SVfloat (ent, modelindex);
block.skinnum = SVfloat (ent, skin);
block.effects = SVfloat (ent, effects);
block.weaponframe = SVfloat (ent, weaponframe);
NET_SVC_Emit (svc_playerinfo, &block, msg);
}
}
/*
SV_WriteEntitiesToClient
Encodes the current state of the world as
a svc_packetentities messages and possibly
a svc_nails message and
svc_playerinfo messages
*/
void
SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg)
{
byte *pvs;
int e, i;
vec3_t org;
client_frame_t *frame;
edict_t *clent, *ent;
entity_state_t *state;
packet_entities_t *pack;
int searchent, netnum;
// this is the frame we are creating
frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK];
// find the client's PVS
clent = client->edict;
VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org);
pvs = SV_FatPVS (org);
// send over the players in the PVS
SV_WritePlayersToClient (client, clent, pvs, msg);
// put other visible entities into either a packet_entities or a nails
// message
pack = &frame->entities;
pack->num_entities = 0;
numnails = 0;
for (searchent = netnum = MAX_CLIENTS + 1;
netnum < MAX_NET_EDICTS; netnum++) {
e = SV_EntMap_LookupByNetwork (&client->entmap, netnum);
if (e == ENTMAP_INVALID) {
// try to find an entity to map here
for (; searchent < sv.num_edicts; searchent++) {
ent = EDICT_NUM (&sv_pr_state, searchent);
if (ent->free)
continue;
// ignore ents without visible models
if (!SVfloat (ent, modelindex)
|| !*PR_GetString (&sv_pr_state, SVstring (ent, model)))
continue;
// ignore if not touching a PV leaf
for (i = 0; i < ent->num_leafs; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
break;
if (i == ent->num_leafs)
continue; // not visible
if (SV_AddNailUpdate (ent))
continue; // added to the special update list
if (SV_EntMap_LookupByInternal (&client->entmap,
searchent) != ENTMAP_INVALID)
continue;
break;
}
if (searchent == sv.num_edicts)
continue; // no entity to map
SV_EntMap_Add (&client->entmap, searchent, netnum);
e = searchent;
}
ent = EDICT_NUM (&sv_pr_state, e);
if (ent->free)
continue;
// ignore ents without visible models
if (!SVfloat (ent, modelindex)
|| !*PR_GetString (&sv_pr_state, SVstring (ent, model)))
continue;
// ignore if not touching a PV leaf
for (i = 0; i < ent->num_leafs; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
break;
if (i == ent->num_leafs)
continue; // not visible
if (SV_AddNailUpdate (ent))
continue; // added to the special update list
// add to the packetentities
if (pack->num_entities == MAX_PACKET_ENTITIES)
continue; // all full
SV_EntMap_Touch (&client->entmap, netnum);
state = &pack->entities[pack->num_entities];
pack->num_entities++;
state->number = netnum;
state->flags = 0;
VectorCopy (SVvector (ent, origin), state->origin);
VectorCopy (SVvector (ent, angles), state->angles);
state->modelindex = SVfloat (ent, modelindex);
state->frame = SVfloat (ent, frame);
state->colormap = SVfloat (ent, colormap);
state->skinnum = SVfloat (ent, skin);
state->effects = SVfloat (ent, effects);
// LordHavoc: cleaned up Endy's coding style, shortened the code,
// and implemented missing effects
// Ender: EXTEND (QSG - Begin)
{
state->alpha = 255;
state->scale = 16;
state->glow_size = 0;
state->glow_color = 254;
state->colormod = 255;
if (sv_fields.alpha != -1 && SVfloat (ent, alpha))
state->alpha = bound (0, SVfloat (ent, alpha), 1) * 255.0;
if (sv_fields.scale != -1 && SVfloat (ent, scale))
state->scale = bound (0, SVfloat (ent, scale), 15.9375) * 16.0;
if (sv_fields.glow_size != -1 && SVfloat (ent, glow_size))
state->glow_size = bound (-1024, (int) SVfloat
(ent, glow_size), 1016) >> 3;
if (sv_fields.glow_color != -1 && SVvector (ent, glow_color))
state->glow_color = (int) SVvector (ent, glow_color);
if (sv_fields.colormod != -1
&& SVvector (ent, colormod)[0]
&& SVvector (ent, colormod)[1]
&& SVvector (ent, colormod)[2])
state->colormod =
((int) (bound (0, SVvector (ent, colormod)[0], 1) * 7.0)
<< 5) |
((int) (bound (0, SVvector (ent, colormod)[1], 1) * 7.0)
<< 2) |
(int) (bound (0, SVvector (ent, colormod)[2], 1) * 3.0);
}
// Ender: EXTEND (QSG - End)
}
// handle any missing nails
for (; searchent < sv.num_edicts; searchent++) {
ent = EDICT_NUM (&sv_pr_state, searchent);
if (ent->free)
continue;
// ignore ents without visible models
if (!SVfloat (ent, modelindex)
|| !*PR_GetString (&sv_pr_state, SVstring (ent, model)))
continue;
// ignore if not touching a PV leaf
for (i = 0; i < ent->num_leafs; i++)
if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
break;
if (i == ent->num_leafs)
continue; // not visible
if (SV_EntMap_LookupByInternal (&client->entmap,
searchent) != ENTMAP_INVALID)
continue;
SV_AddNailUpdate (ent); // added to the special update list
}
// encode the packet entities as a delta from the
// last packetentities acknowledged by the client
if (client->delta_sequence != -1)
SV_EmitDeltaPacketEntities (client, pack, msg);
else
SV_EmitPacketEntities (client, pack, msg);
// now add the specialized nail update
SV_EmitNailUpdate (msg);
}