| /*
|
| 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); |
| } |