added DP_SV_CUSTOMIZEENTITYFORCLIENT extension based on a patch from [515]
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 14 Jan 2006 17:40:50 +0000 (17:40 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 14 Jan 2006 17:40:50 +0000 (17:40 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5903 d7cf8633-e32d-0410-b094-e92efae38249

progs.h
protocol.c
protocol.h
sv_main.c
svvm_cmds.c

diff --git a/progs.h b/progs.h
index 7991ed2..9afca77 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -123,6 +123,7 @@ extern int eval_cursor_trace_ent;
 extern int eval_colormod;
 extern int eval_playermodel;
 extern int eval_playerskin;
+extern int eval_customizeentityforclient;
 
 extern mfunction_t *SV_PlayerPhysicsQC;
 extern mfunction_t *EndFrameQC;
index d3f14fe..034b11f 100644 (file)
@@ -1,7 +1,7 @@
 
 #include "quakedef.h"
 
-// this is 80 bytes
+// this is 88 bytes (must match entity_state_t in protocol.h)
 entity_state_t defaultstate =
 {
        // ! means this is not sent to client
@@ -10,6 +10,7 @@ entity_state_t defaultstate =
        {0,0,0},//float angles[3];
        0,//int number; // entity number this state is for
        0,//int effects;
+       0,//unsigned int customizeentityforclient; // !
        0,//unsigned short modelindex;
        0,//unsigned short frame;
        0,//unsigned short tagentity;
@@ -32,7 +33,7 @@ entity_state_t defaultstate =
        0,//unsigned char tagindex;
        {32, 32, 32},//unsigned char colormod[3];
        // padding to a multiple of 8 bytes (to align the double time)
-       {0,0}//unsigned char unused[2]; // !
+       {0,0,0,0,0,0}//unsigned char unused[6]; // !
 };
 
 // LordHavoc: I own protocol ranges 96, 97, 3500-3599
index 0b566fb..2855e0f 100644 (file)
@@ -318,7 +318,7 @@ void Protocol_Names(char *buffer, size_t buffersize);
 #define RENDER_TRANSPARENT 262144 // can't light during opaque stage
 #define RENDER_NOCULLFACE 524288 // render as double sided (disable GL_CULL_FACE)
 
-// this is 80 bytes
+// this is 88 bytes
 typedef struct entity_state_s
 {
        // ! means this is not sent to client
@@ -327,6 +327,7 @@ typedef struct entity_state_s
        float angles[3];
        int number; // entity number this state is for
        int effects;
+       unsigned int customizeentityforclient; // !
        unsigned short modelindex;
        unsigned short frame;
        unsigned short tagentity;
@@ -349,7 +350,7 @@ typedef struct entity_state_s
        unsigned char tagindex;
        unsigned char colormod[3];
        // padding to a multiple of 8 bytes (to align the double time)
-       unsigned char unused[2];
+       unsigned char unused[6];
 }
 entity_state_t;
 
index 60e222a..7296d9d 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -455,239 +455,252 @@ static int numsendentities;
 static entity_state_t sendentities[MAX_EDICTS];
 static entity_state_t *sendentitiesindex[MAX_EDICTS];
 
-void SV_PrepareEntitiesForSending(void)
+qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int e)
 {
-       int e, i;
-       float f;
+       int i;
        unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
+       unsigned int customizeentityforclient;
+       float f;
        vec3_t cullmins, cullmaxs;
        model_t *model;
-       prvm_edict_t *ent;
        prvm_eval_t *val;
-       entity_state_t cs;
-       // send all entities that touch the pvs
-       numsendentities = 0;
-       sendentitiesindex[0] = NULL;
-       for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
-       {
-               sendentitiesindex[e] = NULL;
-               // the 2 billion unit check is actually to detect NAN origins (we really don't want to send those)
-               if (ent->priv.server->free || VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
-                       continue;
 
-               // this check disabled because it is never true
-               //if (numsendentities >= MAX_EDICTS)
-               //      continue;
-
-               // EF_NODRAW prevents sending for any reason except for your own
-               // client, so we must keep all clients in this superset
-               effects = (unsigned)ent->fields.server->effects;
-               if (e > svs.maxclients && (effects & EF_NODRAW))
-                       continue;
-
-               // we can omit invisible entities with no effects that are not clients
-               // LordHavoc: this could kill tags attached to an invisible entity, I
-               // just hope we never have to support that case
-               i = (int)ent->fields.server->modelindex;
-               modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
-
-               flags = 0;
-               i = (int)(PRVM_GETEDICTFIELDVALUE(ent, eval_glow_size)->_float * 0.25f);
-               glowsize = (unsigned char)bound(0, i, 255);
-               if (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
-                       flags |= RENDER_GLOWTRAIL;
-
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[0]*256;
-               light[0] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[1]*256;
-               light[1] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[2]*256;
-               light[2] = (unsigned short)bound(0, f, 65535);
-               f = PRVM_GETEDICTFIELDVALUE(ent, eval_light_lev)->_float;
-               light[3] = (unsigned short)bound(0, f, 65535);
-               lightstyle = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_style)->_float;
-               lightpflags = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_pflags)->_float;
-
-               if (gamemode == GAME_TENEBRAE)
+       // EF_NODRAW prevents sending for any reason except for your own
+       // client, so we must keep all clients in this superset
+       effects = (unsigned)ent->fields.server->effects;
+
+       // we can omit invisible entities with no effects that are not clients
+       // LordHavoc: this could kill tags attached to an invisible entity, I
+       // just hope we never have to support that case
+       i = (int)ent->fields.server->modelindex;
+       modelindex = (i >= 1 && i < MAX_MODELS && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
+
+       flags = 0;
+       i = (int)(PRVM_GETEDICTFIELDVALUE(ent, eval_glow_size)->_float * 0.25f);
+       glowsize = (unsigned char)bound(0, i, 255);
+       if (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
+               flags |= RENDER_GLOWTRAIL;
+
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[0]*256;
+       light[0] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[1]*256;
+       light[1] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_color)->vector[2]*256;
+       light[2] = (unsigned short)bound(0, f, 65535);
+       f = PRVM_GETEDICTFIELDVALUE(ent, eval_light_lev)->_float;
+       light[3] = (unsigned short)bound(0, f, 65535);
+       lightstyle = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_style)->_float;
+       lightpflags = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_pflags)->_float;
+
+       if (gamemode == GAME_TENEBRAE)
+       {
+               // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
+               if (effects & 16)
                {
-                       // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
-                       if (effects & 16)
-                       {
-                               effects &= ~16;
-                               lightpflags |= PFLAGS_FULLDYNAMIC;
-                       }
-                       // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
-                       if (effects & 32)
-                       {
-                               effects &= ~32;
-                               light[0] = 0.2;
-                               light[1] = 1;
-                               light[2] = 0.2;
-                               light[3] = 200;
-                               lightpflags |= PFLAGS_FULLDYNAMIC;
-                       }
+                       effects &= ~16;
+                       lightpflags |= PFLAGS_FULLDYNAMIC;
                }
-
-               specialvisibilityradius = 0;
-               if (lightpflags & PFLAGS_FULLDYNAMIC)
-                       specialvisibilityradius = max(specialvisibilityradius, light[3]);
-               if (glowsize)
-                       specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
-               if (flags & RENDER_GLOWTRAIL)
-                       specialvisibilityradius = max(specialvisibilityradius, 100);
-               if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
+               // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
+               if (effects & 32)
                {
-                       if (effects & EF_BRIGHTFIELD)
-                               specialvisibilityradius = max(specialvisibilityradius, 80);
-                       if (effects & EF_MUZZLEFLASH)
-                               specialvisibilityradius = max(specialvisibilityradius, 100);
-                       if (effects & EF_BRIGHTLIGHT)
-                               specialvisibilityradius = max(specialvisibilityradius, 400);
-                       if (effects & EF_DIMLIGHT)
-                               specialvisibilityradius = max(specialvisibilityradius, 200);
-                       if (effects & EF_RED)
-                               specialvisibilityradius = max(specialvisibilityradius, 200);
-                       if (effects & EF_BLUE)
-                               specialvisibilityradius = max(specialvisibilityradius, 200);
-                       if (effects & EF_FLAME)
-                               specialvisibilityradius = max(specialvisibilityradius, 250);
-                       if (effects & EF_STARDUST)
-                               specialvisibilityradius = max(specialvisibilityradius, 100);
+                       effects &= ~32;
+                       light[0] = 0.2;
+                       light[1] = 1;
+                       light[2] = 0.2;
+                       light[3] = 200;
+                       lightpflags |= PFLAGS_FULLDYNAMIC;
                }
-               if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
-                       continue;
+       }
 
-               cs = defaultstate;
-               cs.active = true;
-               cs.number = e;
-               VectorCopy(ent->fields.server->origin, cs.origin);
-               VectorCopy(ent->fields.server->angles, cs.angles);
-               cs.flags = flags;
-               cs.effects = effects;
-               cs.colormap = (unsigned)ent->fields.server->colormap;
-               cs.modelindex = modelindex;
-               cs.skin = (unsigned)ent->fields.server->skin;
-               cs.frame = (unsigned)ent->fields.server->frame;
-               cs.viewmodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)->edict;
-               cs.exteriormodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)->edict;
-               cs.nodrawtoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)->edict;
-               cs.drawonlytoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)->edict;
-               cs.tagentity = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
-               cs.tagindex = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
-               cs.glowsize = glowsize;
-
-               // don't need to init cs.colormod because the defaultstate did that for us
-               //cs.colormod[0] = cs.colormod[1] = cs.colormod[2] = 32;
-               val = PRVM_GETEDICTFIELDVALUE(ent, eval_colormod);
-               if (val->vector[0] || val->vector[1] || val->vector[2])
-               {
-                       i = val->vector[0] * 32.0f;cs.colormod[0] = bound(0, i, 255);
-                       i = val->vector[1] * 32.0f;cs.colormod[1] = bound(0, i, 255);
-                       i = val->vector[2] * 32.0f;cs.colormod[2] = bound(0, i, 255);
-               }
+       specialvisibilityradius = 0;
+       if (lightpflags & PFLAGS_FULLDYNAMIC)
+               specialvisibilityradius = max(specialvisibilityradius, light[3]);
+       if (glowsize)
+               specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
+       if (flags & RENDER_GLOWTRAIL)
+               specialvisibilityradius = max(specialvisibilityradius, 100);
+       if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
+       {
+               if (effects & EF_BRIGHTFIELD)
+                       specialvisibilityradius = max(specialvisibilityradius, 80);
+               if (effects & EF_MUZZLEFLASH)
+                       specialvisibilityradius = max(specialvisibilityradius, 100);
+               if (effects & EF_BRIGHTLIGHT)
+                       specialvisibilityradius = max(specialvisibilityradius, 400);
+               if (effects & EF_DIMLIGHT)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_RED)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_BLUE)
+                       specialvisibilityradius = max(specialvisibilityradius, 200);
+               if (effects & EF_FLAME)
+                       specialvisibilityradius = max(specialvisibilityradius, 250);
+               if (effects & EF_STARDUST)
+                       specialvisibilityradius = max(specialvisibilityradius, 100);
+       }
+
+       // early culling checks
+       // (final culling is done by SV_MarkWriteEntityStateToClient)
+       customizeentityforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_customizeentityforclient)->function;
+       if (!customizeentityforclient)
+       {
+               if (e > svs.maxclients && (!modelindex && !specialvisibilityradius))
+                       return false;
+               // this 2 billion unit check is actually to detect NAN origins
+               // (we really don't want to send those)
+               if (VectorLength2(ent->fields.server->origin) > 2000000000.0*2000000000.0)
+                       return false;
+       }
 
-               cs.modelindex = modelindex;
 
-               cs.alpha = 255;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
-               if (f)
-               {
-                       i = (int)f;
-                       cs.alpha = (unsigned char)bound(0, i, 255);
-               }
-               // halflife
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
-               if (f)
-               {
-                       i = (int)f;
-                       cs.alpha = (unsigned char)bound(0, i, 255);
-               }
+       *cs = defaultstate;
+       cs->active = true;
+       cs->number = e;
+       VectorCopy(ent->fields.server->origin, cs->origin);
+       VectorCopy(ent->fields.server->angles, cs->angles);
+       cs->flags = flags;
+       cs->effects = effects;
+       cs->colormap = (unsigned)ent->fields.server->colormap;
+       cs->modelindex = modelindex;
+       cs->skin = (unsigned)ent->fields.server->skin;
+       cs->frame = (unsigned)ent->fields.server->frame;
+       cs->viewmodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)->edict;
+       cs->exteriormodelforclient = PRVM_GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)->edict;
+       cs->nodrawtoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)->edict;
+       cs->drawonlytoclient = PRVM_GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)->edict;
+       cs->customizeentityforclient = customizeentityforclient;
+       cs->tagentity = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
+       cs->tagindex = (unsigned char)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
+       cs->glowsize = glowsize;
+
+       // don't need to init cs->colormod because the defaultstate did that for us
+       //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32;
+       val = PRVM_GETEDICTFIELDVALUE(ent, eval_colormod);
+       if (val->vector[0] || val->vector[1] || val->vector[2])
+       {
+               i = val->vector[0] * 32.0f;cs->colormod[0] = bound(0, i, 255);
+               i = val->vector[1] * 32.0f;cs->colormod[1] = bound(0, i, 255);
+               i = val->vector[2] * 32.0f;cs->colormod[2] = bound(0, i, 255);
+       }
 
-               cs.scale = 16;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
-               if (f)
-               {
-                       i = (int)f;
-                       cs.scale = (unsigned char)bound(0, i, 255);
-               }
+       cs->modelindex = modelindex;
 
-               cs.glowcolor = 254;
-               f = (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
-               if (f)
-                       cs.glowcolor = (int)f;
+       cs->alpha = 255;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
+       // halflife
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
+       if (f)
+       {
+               i = (int)f;
+               cs->alpha = (unsigned char)bound(0, i, 255);
+       }
 
-               if (PRVM_GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
-                       cs.effects |= EF_FULLBRIGHT;
+       cs->scale = 16;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
+       if (f)
+       {
+               i = (int)f;
+               cs->scale = (unsigned char)bound(0, i, 255);
+       }
 
-               if (ent->fields.server->movetype == MOVETYPE_STEP)
-                       cs.flags |= RENDER_STEP;
-               if ((cs.effects & EF_LOWPRECISION) && cs.origin[0] >= -32768 && cs.origin[1] >= -32768 && cs.origin[2] >= -32768 && cs.origin[0] <= 32767 && cs.origin[1] <= 32767 && cs.origin[2] <= 32767)
-                       cs.flags |= RENDER_LOWPRECISION;
-               if (ent->fields.server->colormap >= 1024)
-                       cs.flags |= RENDER_COLORMAPPED;
-               if (cs.viewmodelforclient)
-                       cs.flags |= RENDER_VIEWMODEL; // show relative to the view
-
-               cs.light[0] = light[0];
-               cs.light[1] = light[1];
-               cs.light[2] = light[2];
-               cs.light[3] = light[3];
-               cs.lightstyle = lightstyle;
-               cs.lightpflags = lightpflags;
-
-               cs.specialvisibilityradius = specialvisibilityradius;
-
-               // calculate the visible box of this entity (don't use the physics box
-               // as that is often smaller than a model, and would not count
-               // specialvisibilityradius)
-               if ((model = sv.models[modelindex]))
+       cs->glowcolor = 254;
+       f = (PRVM_GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
+       if (f)
+               cs->glowcolor = (int)f;
+
+       if (PRVM_GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
+               cs->effects |= EF_FULLBRIGHT;
+
+       if (ent->fields.server->movetype == MOVETYPE_STEP)
+               cs->flags |= RENDER_STEP;
+       if ((cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767)
+               cs->flags |= RENDER_LOWPRECISION;
+       if (ent->fields.server->colormap >= 1024)
+               cs->flags |= RENDER_COLORMAPPED;
+       if (cs->viewmodelforclient)
+               cs->flags |= RENDER_VIEWMODEL; // show relative to the view
+
+       cs->light[0] = light[0];
+       cs->light[1] = light[1];
+       cs->light[2] = light[2];
+       cs->light[3] = light[3];
+       cs->lightstyle = lightstyle;
+       cs->lightpflags = lightpflags;
+
+       cs->specialvisibilityradius = specialvisibilityradius;
+
+       // calculate the visible box of this entity (don't use the physics box
+       // as that is often smaller than a model, and would not count
+       // specialvisibilityradius)
+       if ((model = sv.models[modelindex]))
+       {
+               float scale = cs->scale * (1.0f / 16.0f);
+               if (cs->angles[0] || cs->angles[2]) // pitch and roll
                {
-                       float scale = cs.scale * (1.0f / 16.0f);
-                       if (cs.angles[0] || cs.angles[2]) // pitch and roll
-                       {
-                               VectorMA(cs.origin, scale, model->rotatedmins, cullmins);
-                               VectorMA(cs.origin, scale, model->rotatedmaxs, cullmaxs);
-                       }
-                       else if (cs.angles[1])
-                       {
-                               VectorMA(cs.origin, scale, model->yawmins, cullmins);
-                               VectorMA(cs.origin, scale, model->yawmaxs, cullmaxs);
-                       }
-                       else
-                       {
-                               VectorMA(cs.origin, scale, model->normalmins, cullmins);
-                               VectorMA(cs.origin, scale, model->normalmaxs, cullmaxs);
-                       }
+                       VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
+                       VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
                }
-               else
+               else if (cs->angles[1])
                {
-                       VectorCopy(cs.origin, cullmins);
-                       VectorCopy(cs.origin, cullmaxs);
+                       VectorMA(cs->origin, scale, model->yawmins, cullmins);
+                       VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
                }
-               if (specialvisibilityradius)
+               else
                {
-                       cullmins[0] = min(cullmins[0], cs.origin[0] - specialvisibilityradius);
-                       cullmins[1] = min(cullmins[1], cs.origin[1] - specialvisibilityradius);
-                       cullmins[2] = min(cullmins[2], cs.origin[2] - specialvisibilityradius);
-                       cullmaxs[0] = max(cullmaxs[0], cs.origin[0] + specialvisibilityradius);
-                       cullmaxs[1] = max(cullmaxs[1], cs.origin[1] + specialvisibilityradius);
-                       cullmaxs[2] = max(cullmaxs[2], cs.origin[2] + specialvisibilityradius);
+                       VectorMA(cs->origin, scale, model->normalmins, cullmins);
+                       VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs);
                }
-               if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
+       }
+       else
+       {
+               VectorCopy(cs->origin, cullmins);
+               VectorCopy(cs->origin, cullmaxs);
+       }
+       if (specialvisibilityradius)
+       {
+               cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius);
+               cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius);
+               cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius);
+               cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius);
+               cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius);
+               cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius);
+       }
+       if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
+       {
+               VectorCopy(cullmins, ent->priv.server->cullmins);
+               VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
+               ent->priv.server->pvs_numclusters = -1;
+               if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
                {
-                       VectorCopy(cullmins, ent->priv.server->cullmins);
-                       VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
-                       ent->priv.server->pvs_numclusters = -1;
-                       if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
-                       {
-                               i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
-                               if (i <= MAX_ENTITYCLUSTERS)
-                                       ent->priv.server->pvs_numclusters = i;
-                       }
+                       i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
+                       if (i <= MAX_ENTITYCLUSTERS)
+                               ent->priv.server->pvs_numclusters = i;
                }
+       }
 
-               sendentitiesindex[e] = sendentities + numsendentities;
-               sendentities[numsendentities++] = cs;
+       return true;
+}
+
+void SV_PrepareEntitiesForSending(void)
+{
+       int e;
+       prvm_edict_t *ent;
+       // send all entities that touch the pvs
+       numsendentities = 0;
+       sendentitiesindex[0] = NULL;
+       memset(sendentitiesindex, 0, prog->num_edicts * sizeof(entity_state_t *));
+       for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent))
+       {
+               if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sendentities + numsendentities, e))
+               {
+                       sendentitiesindex[e] = sendentities + numsendentities;
+                       numsendentities++;
+               }
        }
 }
 
@@ -715,6 +728,15 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
        sententitiesconsideration[s->number] = sententitiesmark;
        sv_writeentitiestoclient_totalentities++;
 
+       if (s->customizeentityforclient)
+       {
+               prog->globals.server->self = s->number;
+               prog->globals.server->other = sv_writeentitiestoclient_clentnum;
+               PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function");
+               if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number))
+                       return;
+       }
+
        // never reject player
        if (s->number != sv_writeentitiestoclient_clentnum)
        {
@@ -2162,6 +2184,7 @@ int eval_cursor_trace_ent;
 int eval_colormod;
 int eval_playermodel;
 int eval_playerskin;
+int eval_customizeentityforclient;
 
 mfunction_t *SV_PlayerPhysicsQC;
 mfunction_t *EndFrameQC;
@@ -2229,6 +2252,7 @@ void SV_VM_FindEdictFieldOffsets(void)
        eval_colormod = PRVM_ED_FindFieldOffset("colormod");
        eval_playermodel = PRVM_ED_FindFieldOffset("playermodel");
        eval_playerskin = PRVM_ED_FindFieldOffset("playerskin");
+       eval_customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
 
        // LordHavoc: allowing QuakeC to override the player movement code
        SV_PlayerPhysicsQC = PRVM_ED_FindFunction ("SV_PlayerPhysics");
@@ -2300,7 +2324,8 @@ prvm_required_field_t reqfields[] =
        {ev_vector, "movement"},
        {ev_vector, "punchvector"},
        {ev_string, "playermodel"},
-       {ev_string, "playerskin"}
+       {ev_string, "playerskin"},
+       {ev_function, "customizeentityforclient"},
 };
 
 void SV_VM_Setup(void)
index 020a40a..b54c265 100644 (file)
@@ -83,6 +83,7 @@ char *vm_sv_extensions =
 "DP_SV_BOTCLIENT "
 "DP_SV_CLIENTCOLORS "
 "DP_SV_CLIENTNAME "
+"DP_SV_CUSTOMIZEENTITYFORCLIENT "
 "DP_SV_DRAWONLYTOCLIENT "
 "DP_SV_DROPCLIENT "
 "DP_SV_EFFECT "