aboutsummaryrefslogtreecommitdiffstats
path: root/QW/server/sv_ents.c
diff options
context:
space:
mode:
authorTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 14:32:09 -0600
committerTravis Bradshaw <travis.bradshaw@idsoftware.com>2012-01-31 14:32:09 -0600
commit0023db327bc1db00068284b70e1db45857aeee35 (patch)
tree01dd8366d20bd674741cfe8ceb069d82a2e33973 /QW/server/sv_ents.c
downloadquake-pum-0023db327bc1db00068284b70e1db45857aeee35.tar.gz
The Quake sources as originally release under the GPL license on December 21, 1999
Diffstat (limited to 'QW/server/sv_ents.c')
-rw-r--r--QW/server/sv_ents.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/QW/server/sv_ents.c b/QW/server/sv_ents.c
new file mode 100644
index 0000000..055a9a9
--- /dev/null
+++ b/QW/server/sv_ents.c
@@ -0,0 +1,517 @@
+/*
+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 the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "qwsvdef.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.
+
+=============================================================================
+*/
+
+int fatbytes;
+byte fatpvs[MAX_MAP_LEAFS/8];
+
+void SV_AddToFatPVS (vec3_t org, mnode_t *node)
+{
+ int i;
+ byte *pvs;
+ mplane_t *plane;
+ float d;
+
+ 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;
+ Q_memset (fatpvs, 0, fatbytes);
+ SV_AddToFatPVS (org, sv.worldmodel->nodes);
+ return fatpvs;
+}
+
+//=============================================================================
+
+// because there can be a lot of nails, there is a special
+// network protocol for them
+#define MAX_NAILS 32
+edict_t *nails[MAX_NAILS];
+int numnails;
+
+extern int sv_nailmodel, sv_supernailmodel, sv_playermodel;
+
+qboolean SV_AddNailUpdate (edict_t *ent)
+{
+ if (ent->v.modelindex != sv_nailmodel
+ && ent->v.modelindex != sv_supernailmodel)
+ return false;
+ if (numnails == MAX_NAILS)
+ return true;
+ nails[numnails] = ent;
+ numnails++;
+ return true;
+}
+
+void SV_EmitNailUpdate (sizebuf_t *msg)
+{
+ byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8
+ int n, i;
+ edict_t *ent;
+ int x, y, z, p, yaw;
+
+ if (!numnails)
+ return;
+
+ MSG_WriteByte (msg, svc_nails);
+ MSG_WriteByte (msg, numnails);
+
+ for (n=0 ; n<numnails ; n++)
+ {
+ ent = nails[n];
+ x = (int)(ent->v.origin[0]+4096)>>1;
+ y = (int)(ent->v.origin[1]+4096)>>1;
+ z = (int)(ent->v.origin[2]+4096)>>1;
+ p = (int)(16*ent->v.angles[0]/360)&15;
+ yaw = (int)(256*ent->v.angles[1]/360)&255;
+
+ bits[0] = x;
+ bits[1] = (x>>8) | (y<<4);
+ bits[2] = (y>>4);
+ bits[3] = z;
+ bits[4] = (z>>8) | (p<<4);
+ bits[5] = yaw;
+
+ for (i=0 ; i<6 ; i++)
+ MSG_WriteByte (msg, bits[i]);
+ }
+}
+
+//=============================================================================
+
+
+/*
+==================
+SV_WriteDelta
+
+Writes part of a packetentities message.
+Can delta from either a baseline or a previous packet_entity
+==================
+*/
+void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force)
+{
+ int bits;
+ int i;
+ float miss;
+
+// send an update
+ 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;
+
+ if (bits & 511)
+ bits |= U_MOREBITS;
+
+ if (to->flags & U_SOLID)
+ bits |= U_SOLID;
+
+ //
+ // write the message
+ //
+ if (!to->number)
+ SV_Error ("Unset entity number");
+ if (to->number >= 512)
+ SV_Error ("Entity number >= 512");
+
+ if (!bits && !force)
+ return; // nothing to send!
+ i = to->number | (bits&~511);
+ if (i & U_REMOVE)
+ Sys_Error ("U_REMOVE");
+ MSG_WriteShort (msg, i);
+
+ if (bits & U_MOREBITS)
+ MSG_WriteByte (msg, bits&255);
+ if (bits & U_MODEL)
+ MSG_WriteByte (msg, to->modelindex);
+ if (bits & U_FRAME)
+ MSG_WriteByte (msg, to->frame);
+ if (bits & U_COLORMAP)
+ MSG_WriteByte (msg, to->colormap);
+ if (bits & U_SKIN)
+ MSG_WriteByte (msg, to->skinnum);
+ if (bits & U_EFFECTS)
+ MSG_WriteByte (msg, to->effects);
+ if (bits & U_ORIGIN1)
+ MSG_WriteCoord (msg, to->origin[0]);
+ if (bits & U_ANGLE1)
+ MSG_WriteAngle(msg, to->angles[0]);
+ if (bits & U_ORIGIN2)
+ MSG_WriteCoord (msg, to->origin[1]);
+ if (bits & U_ANGLE2)
+ MSG_WriteAngle(msg, to->angles[1]);
+ if (bits & U_ORIGIN3)
+ MSG_WriteCoord (msg, to->origin[2]);
+ if (bits & U_ANGLE3)
+ MSG_WriteAngle(msg, to->angles[2]);
+}
+
+/*
+=============
+SV_EmitPacketEntities
+
+Writes a delta update of a packet_entities_t to the message.
+
+=============
+*/
+void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg)
+{
+ edict_t *ent;
+ client_frame_t *fromframe;
+ packet_entities_t *from;
+ int oldindex, newindex;
+ int oldnum, newnum;
+ int oldmax;
+
+ // this is the frame that we are going to delta update from
+ if (client->delta_sequence != -1)
+ {
+ fromframe = &client->frames[client->delta_sequence & UPDATE_MASK];
+ from = &fromframe->entities;
+ oldmax = from->num_entities;
+
+ MSG_WriteByte (msg, svc_deltapacketentities);
+ MSG_WriteByte (msg, client->delta_sequence);
+ }
+ else
+ {
+ oldmax = 0; // no delta update
+ from = NULL;
+
+ MSG_WriteByte (msg, svc_packetentities);
+ }
+
+ newindex = 0;
+ oldindex = 0;
+//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK
+// , client->netchan.outgoing_sequence & UPDATE_MASK);
+ while (newindex < to->num_entities || oldindex < oldmax)
+ {
+ newnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number;
+ oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number;
+
+ if (newnum == oldnum)
+ { // delta update from old position
+//Con_Printf ("delta %i\n", newnum);
+ SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false);
+ oldindex++;
+ newindex++;
+ continue;
+ }
+
+ if (newnum < oldnum)
+ { // this is a new entity, send it from the baseline
+ ent = EDICT_NUM(newnum);
+//Con_Printf ("baseline %i\n", newnum);
+ SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true);
+ newindex++;
+ continue;
+ }
+
+ if (newnum > oldnum)
+ { // the old entity isn't present in the new message
+//Con_Printf ("remove %i\n", oldnum);
+ MSG_WriteShort (msg, oldnum | U_REMOVE);
+ oldindex++;
+ continue;
+ }
+ }
+
+ MSG_WriteShort (msg, 0); // end of packetentities
+}
+
+/*
+=============
+SV_WritePlayersToClient
+
+=============
+*/
+void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg)
+{
+ int i, j;
+ client_t *cl;
+ edict_t *ent;
+ int msec;
+ usercmd_t cmd;
+ int pflags;
+
+ 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
+ }
+
+ pflags = PF_MSEC | PF_COMMAND;
+
+ if (ent->v.modelindex != sv_playermodel)
+ pflags |= PF_MODEL;
+ for (i=0 ; i<3 ; i++)
+ if (ent->v.velocity[i])
+ pflags |= PF_VELOCITY1<<i;
+ if (ent->v.effects)
+ pflags |= PF_EFFECTS;
+ if (ent->v.skin)
+ pflags |= PF_SKINNUM;
+ if (ent->v.health <= 0)
+ pflags |= PF_DEAD;
+ if (ent->v.mins[2] != -24)
+ pflags |= PF_GIB;
+
+ if (cl->spectator)
+ { // only sent origin and velocity to spectators
+ pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3;
+ }
+ else if (ent == clent)
+ { // don't send a lot of data on personal entity
+ pflags &= ~(PF_MSEC|PF_COMMAND);
+ if (ent->v.weaponframe)
+ pflags |= PF_WEAPONFRAME;
+ }
+
+ if (client->spec_track && client->spec_track - 1 == j &&
+ ent->v.weaponframe)
+ pflags |= PF_WEAPONFRAME;
+
+ MSG_WriteByte (msg, svc_playerinfo);
+ MSG_WriteByte (msg, j);
+ MSG_WriteShort (msg, pflags);
+
+ for (i=0 ; i<3 ; i++)
+ MSG_WriteCoord (msg, ent->v.origin[i]);
+
+ MSG_WriteByte (msg, ent->v.frame);
+
+ if (pflags & PF_MSEC)
+ {
+ msec = 1000*(sv.time - cl->localtime);
+ if (msec > 255)
+ msec = 255;
+ MSG_WriteByte (msg, msec);
+ }
+
+ if (pflags & PF_COMMAND)
+ {
+ cmd = cl->lastcmd;
+
+ if (ent->v.health <= 0)
+ { // don't show the corpse looking around...
+ cmd.angles[0] = 0;
+ cmd.angles[1] = ent->v.angles[1];
+ cmd.angles[0] = 0;
+ }
+
+ cmd.buttons = 0; // never send buttons
+ cmd.impulse = 0; // never send impulses
+
+ MSG_WriteDeltaUsercmd (msg, &nullcmd, &cmd);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ if (pflags & (PF_VELOCITY1<<i) )
+ MSG_WriteShort (msg, ent->v.velocity[i]);
+
+ if (pflags & PF_MODEL)
+ MSG_WriteByte (msg, ent->v.modelindex);
+
+ if (pflags & PF_SKINNUM)
+ MSG_WriteByte (msg, ent->v.skin);
+
+ if (pflags & PF_EFFECTS)
+ MSG_WriteByte (msg, ent->v.effects);
+
+ if (pflags & PF_WEAPONFRAME)
+ MSG_WriteByte (msg, ent->v.weaponframe);
+ }
+}
+
+
+/*
+=============
+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)
+{
+ int e, i;
+ byte *pvs;
+ vec3_t org;
+ edict_t *ent;
+ packet_entities_t *pack;
+ edict_t *clent;
+ client_frame_t *frame;
+ entity_state_t *state;
+
+ // 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 (clent->v.origin, clent->v.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 (e=MAX_CLIENTS+1, ent=EDICT_NUM(e) ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
+ {
+ // ignore ents without visible models
+ if (!ent->v.modelindex || !*PR_GetString(ent->v.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
+
+ state = &pack->entities[pack->num_entities];
+ pack->num_entities++;
+
+ state->number = e;
+ state->flags = 0;
+ VectorCopy (ent->v.origin, state->origin);
+ VectorCopy (ent->v.angles, state->angles);
+ state->modelindex = ent->v.modelindex;
+ state->frame = ent->v.frame;
+ state->colormap = ent->v.colormap;
+ state->skinnum = ent->v.skin;
+ state->effects = ent->v.effects;
+ }
+
+ // encode the packet entities as a delta from the
+ // last packetentities acknowledged by the client
+
+ SV_EmitPacketEntities (client, pack, msg);
+
+ // now add the specialized nail update
+ SV_EmitNailUpdate (msg);
+}