/* 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 */ #ifdef HAVE_CONFIG_H # include "config.h" #endif static __attribute__ ((unused)) const char rcsid[] = "$Id$"; #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include "QF/cvar.h" #include "QF/msg.h" #include "QF/sys.h" #include "qw/msg_ucmd.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; static 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. */ static 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; int nailcount; static 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; } static void SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder) { byte *buf; // [48 bits] xyzpy 12 12 12 4 8 int n, p, x, y, z, yaw; int bpn = recorder ? 7 : 6; // bytes per nail edict_t *ent; if (!numnails) return; buf = SZ_GetSpace (msg, numnails * bpn + 2); *buf++ = recorder ? svc_nails2 : svc_nails; *buf++ = numnails; for (n = 0; n < numnails; n++) { ent = nails[n]; if (recorder) { if (!SVfloat (ent, colormap)) { if (!((++nailcount) & 255)) nailcount++; SVfloat (ent, colormap) = nailcount&255; } *buf++ = (byte)SVfloat (ent, colormap); } x = ((int) (SVvector (ent, origin)[0] + 4096 + 1) >> 1) & 0xfff; y = ((int) (SVvector (ent, origin)[1] + 4096 + 1) >> 1) & 0xfff; z = ((int) (SVvector (ent, origin)[2] + 4096 + 1) >> 1) & 0xfff; p = (int) (SVvector (ent, angles)[0] * (16.0 / 360.0)) & 0x00f; yaw = (int) (SVvector (ent, angles)[1] * (256.0 / 360.0)) & 0x0ff; *buf++ = x; *buf++ = (x >> 8) | (y << 4); *buf++ = (y >> 4); *buf++ = z; *buf++ = (z >> 8) | (p << 4); *buf++ = yaw; } } /* SV_WriteDelta Writes part of a packetentities message. Can delta from either a baseline or a previous packet_entity */ static void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, int stdver) { int bits, 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 & 0xff) != (from->effects & 0xff)) 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->effects & 0xff00) != (from->effects & 0xff00)) bits |= U_EFFECTS2; 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 (to->frame > 255) bits |= U_EFFECTS2; } if (bits >= 16777216) bits |= U_EXTEND2; if (bits >= 65536) bits |= U_EXTEND1; // Ender (QSG - End) if (bits & 511) bits |= U_MOREBITS; if (to->flags & U_SOLID) bits |= U_SOLID; // write the message if (!to->number) Sys_Error ("Unset entity number"); if (to->number >= 512) Sys_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); // LordHavoc: cleaned up Endy's tabs // Ender (QSG - Begin) if (bits & U_EXTEND1) MSG_WriteByte (msg, bits >> 16); if (bits & U_EXTEND2) MSG_WriteByte (msg, bits >> 24); // Ender (QSG - End) 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]); // LordHavoc: cleaned up Endy's tabs, rearranged bytes, and implemented // missing effects // Ender (QSG - Begin) if (bits & U_ALPHA) MSG_WriteByte (msg, to->alpha); if (bits & U_SCALE) MSG_WriteByte (msg, to->scale); if (bits & U_EFFECTS2) MSG_WriteByte (msg, (to->effects >> 8)); if (bits & U_GLOWSIZE) MSG_WriteByte (msg, to->glow_size); if (bits & U_GLOWCOLOR) MSG_WriteByte (msg, to->glow_color); if (bits & U_COLORMOD) MSG_WriteByte (msg, to->colormod); if (bits & U_FRAME2) MSG_WriteByte (msg, (to->frame >> 8)); // Ender (QSG - End) } /* SV_EmitPacketEntities Writes a delta update of a packet_entities_t to the message. */ static void SV_EmitPacketEntities (delta_t *delta, packet_entities_t *to, sizebuf_t *msg, int stdver) { int newindex, oldindex, newnum, oldnum, oldmax; edict_t *ent; client_frame_t *fromframe; packet_entities_t *from; // this is the frame that we are going to delta update from if (delta->delta_sequence != -1) { fromframe = &delta->frames[delta->delta_sequence & UPDATE_MASK]; from = &fromframe->entities; oldmax = from->num_entities; MSG_WriteByte (msg, svc_deltapacketentities); MSG_WriteByte (msg, delta->delta_sequence); } else { oldmax = 0; // no delta update from = NULL; MSG_WriteByte (msg, svc_packetentities); } newindex = 0; oldindex = 0; // SV_Printf ("---%i to %i ----\n", delta->delta_sequence & UPDATE_MASK, // delta->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 // SV_Printf ("delta %i\n", newnum); SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false, stdver); oldindex++; newindex++; continue; } if (newnum < oldnum) { // this is a new entity, send it from the baseline if (newnum == 9999) { Sys_Printf ("LOL, %d, %d, %d, %d %d %d\n", newnum, oldnum, to->num_entities, oldmax, delta->in_frame, delta->delta_sequence & UPDATE_MASK); if (!delta->client) Sys_Printf ("demo\n"); } ent = EDICT_NUM (&sv_pr_state, newnum); // SV_Printf ("baseline %i\n", newnum); SV_WriteDelta (ent->data, &to->entities[newindex], msg, true, stdver); newindex++; continue; } if (newnum > oldnum) { // the old entity isn't present in // the new message // SV_Printf ("remove %i\n", oldnum); MSG_WriteShort (msg, oldnum | U_REMOVE); oldindex++; continue; } } MSG_WriteShort (msg, 0); // end of packetentities } static void write_demoplayer (delta_t *delta, plent_state_t *from, plent_state_t *to, sizebuf_t *msg, int mask, int full) { int flags; int j; flags = to->flags >> 1; // convert PF_(GIB|DEAD) to DF_(GIB|DEAD) flags &= DF_GIB | DF_DEAD; // PF_MSEC and PF_COMMAND aren't wanted if (full) { flags |= DF_ORIGIN | (DF_ORIGIN << 1) | (DF_ORIGIN << 2) | DF_ANGLES | (DF_ANGLES << 1) | (DF_ANGLES << 2) | DF_EFFECTS | DF_SKINNUM | DF_WEAPONFRAME | DF_MODEL; } else { for (j = 0; j < 3; j++) if (from->origin[j] != to->origin[j]) flags |= DF_ORIGIN << j; for (j = 0; j < 3; j++) if (from->cmd.angles[j] != to->cmd.angles[j]) flags |= DF_ANGLES << j; if (from->modelindex != to->modelindex) flags |= DF_MODEL; if ((from->effects & 0xff) != (to->effects & 0xff)) flags |= DF_EFFECTS; if (from->skinnum != to->skinnum) flags |= DF_SKINNUM; if (from->weaponframe != to->weaponframe) flags |= DF_WEAPONFRAME; } MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, to->number); MSG_WriteShort (msg, flags); MSG_WriteByte (msg, to->frame); for (j = 0; j < 3; j++) if (flags & (DF_ORIGIN << j)) MSG_WriteCoord (msg, to->origin[j]); for (j = 0; j < 3; j++) if (flags & (DF_ANGLES << j)) MSG_WriteAngle16 (msg, to->cmd.angles[j]); if (flags & DF_MODEL) MSG_WriteByte (msg, to->modelindex); if (flags & DF_SKINNUM) MSG_WriteByte (msg, to->skinnum); if (flags & DF_EFFECTS) MSG_WriteByte (msg, to->effects); if (flags & DF_WEAPONFRAME) MSG_WriteByte (msg, to->weaponframe); } static void write_player (delta_t *delta, plent_state_t *from, plent_state_t *to, sizebuf_t *msg, int mask, int full) { int flags = to->flags; int qf_bits = 0; int i; int ds = delta->delta_sequence & 0x7f; if (full) { flags |= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3 | PF_MODEL | PF_SKINNUM | PF_EFFECTS | PF_WEAPONFRAME; if (full > 1) { qf_bits = PF_ALPHA | PF_SCALE | PF_EFFECTS2 | PF_GLOWSIZE | PF_GLOWCOLOR | PF_COLORMOD | PF_FRAME2; flags |= PF_QF; } ds = -1; } else { for (i = 0; i < 3; i++) if (from->velocity[i] != to->velocity[i]) flags |= PF_VELOCITY1 << i; if (from->modelindex != to->modelindex) flags |= PF_MODEL; if (from->skinnum != to->skinnum) flags |= PF_SKINNUM; if ((from->effects & 0xff) != (to->effects &0xff)) flags |= PF_EFFECTS; if (from->weaponframe != to->weaponframe) flags |= PF_WEAPONFRAME; if (from->alpha != to->alpha) qf_bits |= PF_ALPHA; if (from->scale != to->scale) qf_bits |= PF_SCALE; if ((from->effects & 0xff00) != (to->effects & 0xff00)) qf_bits |= PF_EFFECTS2; if (from->glow_size != to->glow_size) qf_bits |= PF_GLOWSIZE; if (from->glow_color != to->glow_color) qf_bits |= PF_GLOWCOLOR; if (from->colormod != to->colormod) qf_bits |= PF_COLORMOD; if ((from->frame & 0xff00) != (to->frame & 0xff00)) qf_bits |= PF_FRAME2; if (qf_bits) flags |= PF_QF; } flags &= mask; MSG_WriteByte (msg, svc_playerinfo); if (delta->type == dt_tp_qtv) MSG_WriteByte (msg, ds); MSG_WriteByte (msg, to->number); MSG_WriteShort (msg, flags); MSG_WriteCoordV (msg, to->origin); MSG_WriteByte (msg, to->frame); if (flags & PF_MSEC) MSG_WriteByte (msg, to->msec); if (flags & PF_COMMAND) MSG_WriteDeltaUsercmd (msg, &from->cmd, &to->cmd); for (i = 0; i < 3; i++) if (flags & (PF_VELOCITY1 << i)) MSG_WriteShort (msg, to->velocity[i]); if (flags & PF_MODEL) MSG_WriteByte (msg, to->modelindex); if (flags & PF_SKINNUM) MSG_WriteByte (msg, to->skinnum); if (flags & PF_EFFECTS) MSG_WriteByte (msg, to->effects); if (flags & PF_WEAPONFRAME) MSG_WriteByte (msg, to->weaponframe); if (flags & PF_QF) { MSG_WriteByte (msg, qf_bits); if (qf_bits & PF_ALPHA) MSG_WriteByte (msg, to->alpha); if (qf_bits & PF_SCALE) MSG_WriteByte (msg, to->scale); if (qf_bits & PF_EFFECTS2) MSG_WriteByte (msg, to->effects >> 8); if (qf_bits & PF_GLOWSIZE) MSG_WriteByte (msg, to->glow_size); if (qf_bits & PF_GLOWCOLOR) MSG_WriteByte (msg, to->glow_color); if (qf_bits & PF_COLORMOD) MSG_WriteByte (msg, to->colormod); if (qf_bits & PF_FRAME2) MSG_WriteByte (msg, to->frame); } } static void SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg) { int i, j, k; client_t *cl; edict_t *clent = 0; int spec_track = 0; int stdver = 2, full = stdver; edict_t *ent; packet_players_t *pack; client_frame_t *frame = &delta->frames[delta->in_frame]; packet_players_t *from_pack = 0; plent_state_t dummy_player_state, *state = &dummy_player_state; static plent_state_t null_player_state; void (*write) (delta_t *, plent_state_t *, plent_state_t *, sizebuf_t *, int, int); 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; } null_player_state.modelindex = 0; if (delta->client) { clent = delta->client->edict; spec_track = delta->client->spec_track; stdver = delta->client->stdver; null_player_state.modelindex = sv_playermodel; full = 0; // normal qw clients don't get real deltas on players } write = write_player; if (!clent && delta->type == dt_tp_demo) write = write_demoplayer; if (delta->delta_sequence != -1) { client_frame_t *fromframe; fromframe = &delta->frames[delta->delta_sequence & UPDATE_MASK]; from_pack = &fromframe->players; full = 0; } pack = &frame->players; pack->num_players = 0; for (j = k = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) { int mask = ~PF_WEAPONFRAME; if (cl->state != cs_spawned && cl->state != cs_server) continue; ent = cl->edict; // ZOID visibility tracking // self or the the ent being tracked as a spectator always gets sent if (ent != clent && !(spec_track && spec_track - 1 == j)) { if (cl->spectator) continue; if (pvs) { // 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 (pack->players) state = &pack->players[pack->num_players]; pack->num_players++; state->number = j; state->flags = 0; VectorCopy (SVvector (ent, origin), state->origin); state->msec = min (255, 1000 * (sv.time - cl->localtime)); state->cmd = cl->lastcmd; if (SVfloat (ent, health) <= 0) { // don't show the corpse // looking around... state->cmd.angles[0] = 0; state->cmd.angles[1] = SVvector (ent, angles)[1]; state->cmd.angles[0] = 0; } VectorCopy (SVvector (ent, velocity), state->velocity); state->modelindex = SVfloat (ent, modelindex); state->frame = SVfloat (ent, frame); state->skinnum = SVfloat (ent, skin); state->effects = SVfloat (ent, effects); state->weaponframe = SVfloat (ent, weaponframe); if (SVfloat (ent, health) <= 0) state->flags |= PF_DEAD; if (SVvector (ent, mins)[2] != -24) state->flags |= PF_GIB; state->flags |= PF_MSEC | PF_COMMAND; 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)) { float alpha = SVfloat (ent, alpha); state->alpha = bound (0, alpha, 1) * 255.0; } if (sv_fields.scale != -1 && SVfloat (ent, scale)) { float scale = SVfloat (ent, scale); state->scale = bound (0, scale, 15.9375) * 16; } if (sv_fields.glow_size != -1 && SVfloat (ent, glow_size)) { int glow_size = SVfloat (ent, glow_size); state->glow_size = bound (-1024, glow_size, 1016) >> 3; } if (sv_fields.glow_color != -1 && SVfloat (ent, glow_color)) state->glow_color = SVfloat (ent, glow_color); if (sv_fields.colormod != -1 && !VectorIsZero (SVvector (ent, colormod))) { float *colormod= SVvector (ent, colormod); state->colormod = ((int) (bound (0, colormod[0], 1) * 7) << 5) | ((int) (bound (0, colormod[1], 1) * 7) << 2) | (int) (bound (0, colormod[2], 1) * 3); } if (cl->spectator) { // only send origin and velocity of spectators mask &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; } else if (ent == clent) { // don't send a lot of data on personal entity mask &= ~(PF_MSEC | PF_COMMAND); mask |= PF_WEAPONFRAME; } if (spec_track && spec_track - 1 == j) mask |= PF_WEAPONFRAME; if (from_pack && from_pack->players) { while (k < from_pack->num_players && from_pack->players[k].number < state->number) k++; if (k < from_pack->num_players && from_pack->players[k].number == state->number) { write (delta, &from_pack->players[k], state, msg, mask, full); continue; } full = 1; } write (delta, &null_player_state, state, msg, mask, full); } } static inline byte * calc_pvs (delta_t *delta) { byte *pvs = 0; vec3_t org; // find the client's PVS if (delta->pvs == dt_pvs_normal) { edict_t *clent = delta->client->edict; VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org); pvs = SV_FatPVS (org); } else if (delta->pvs == dt_pvs_fat) { // when recording a demo, only send entities that can be seen. Can help // shrink the mvd at expense of effectively lossy compressio (ents that // can't be seen by a player either won't get updated or will disappear // for people watching the mvd from viewpoints with no players around // (not sure yet:). client_t *cl; int i; for (i=0, cl = svs.clients; istate != cs_spawned && cl->state != cs_server) continue; if (cl->spectator) continue; VectorAdd (SVvector (cl->edict, origin), SVvector (cl->edict, view_ofs), org); if (pvs == NULL) { pvs = SV_FatPVS (org); } else { SV_AddToFatPVS (org, sv.worldmodel->nodes); } } } return pvs; } /* 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 (delta_t *delta, sizebuf_t *msg) { byte *pvs = 0; int e, i, num_edicts; int max_packet_entities = MAX_DEMO_PACKET_ENTITIES; int stdver = 1; client_frame_t *frame; edict_t *ent; entity_state_t *state; packet_entities_t *pack; if (delta->client) { max_packet_entities = MAX_PACKET_ENTITIES; stdver = delta->client->stdver; } pvs = calc_pvs (delta); // send over the players in the PVS SV_WritePlayersToClient (delta, pvs, msg); // this is the frame we are creating frame = &delta->frames[delta->in_frame]; // put other visible entities into either a packet_entities or a nails // message pack = &frame->entities; pack->num_entities = 0; numnails = 0; num_edicts = min (sv.num_edicts, 512); //FIXME stupid protocol limit for (e = MAX_CLIENTS + 1, ent = EDICT_NUM (&sv_pr_state, e); e < num_edicts; e++, ent = NEXT_EDICT (&sv_pr_state, ent)) { if (ent->free) continue; // ignore ents without visible models if (!SVfloat (ent, modelindex) || !*PR_GetString (&sv_pr_state, SVstring (ent, model))) continue; if (pvs) { // 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 (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 && SVfloat (ent, glow_color)) state->glow_color = (int) SVfloat (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) } // encode the packet entities as a delta from the // last packetentities acknowledged by the client SV_EmitPacketEntities (delta, pack, msg, stdver); // now add the specialized nail update SV_EmitNailUpdate (msg, delta->client != 0); }