DarkPlaces add a sv_cullentities_trace feature to screen out entities that can't be see so Nexuiz would run faster and this had the side effect of being a strong cheat-protection measure.
Last year, this functionality was added to FTEQW and later in the year I ported the FTEQW version to ProQuake server.
Later Rook, would test and perfect a streamlined version that condensed it down to a single function.
Adding the Code:Summary: Anti-Wallhack
Using SV_Invisible_To_Client, you can screen out entities that a client shouldn't be able to see and have the server simply not send them.
Then you don't have to worry much about cheaters with wallhacks or altered player models: there is no model to draw because the client is never sent data for what they cannot see.
Other Benefits
This reduces the amount of data sent to the client by the server.
In single player, this visibility screening can speed up rendering and increase frames per second.
The downside is that doing this check for 300-600 entities for every frame is a little CPU intensive. To deal with this, there are 2 options:
1. sv_cullentities 1 - only checks the player entities. So it is only checking at most 16 entities per frame.
2. sv_cullentities 2 - check everything
Culling Weakness
If I recall correctly, this function has a very minor weakness in that if ONLY the middle of a model is visible and not extremities, the function falsely returns that the entity can't be seen.
Not typically a problem except for lifts, platforms and other entities that are large. As a reult, it the function skips those entities.
The code was written by Rook.
It is very easily to implement. It is a single function with 1 supporting cvar we need to register and 1 instance where our new function gets called.
And it's all in sv_main.c!
1. sv_main.c
a. Let's create the sv_cullentities cvar; we will be default it to 1 (players only screen).
At the top of sv_main.c, add the yellow text:
b. We need to register the cvar. Go 5 or 6 lines downad to SV_Init.c and add the yellow text.char localmodels[MAX_MODELS][5]; // inline model names for precache
cvar_t sv_cullentities = {"sv_cullentities","1", false, true};
//============================================================================
c. Now we need to add our new function, find this code:Cvar_RegisterVariable (&sv_cull_entities);
Cvar_RegisterVariable (&sv_maxvelocity)
Cvar_RegisterVariable (&sv_gravity);
Cvar_RegisterVariable (&sv_friction);
Cvar_RegisterVariable (&sv_edgefriction);
Cvar_RegisterVariable (&sv_stopspeed);
And above it paste our new function:Code: Select all
/* ============= SV_WriteEntitiesToClient ============= */
4. Finally we need to make sure this screening process gets called:Code: Select all
extern trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end); qboolean SV_InvisibleToClient(edict_t *viewer, edict_t *seen) { int i; trace_t tr; vec3_t start; vec3_t end; if (seen->v.movetype == MOVETYPE_PUSH )//dont cull doors and plats :( { return false; } if (sv_cullentities.value == 1) //1 only check player models, 2 = check all ents if (strcmp(pr_strings + seen->v.classname, "player")) return false; memset (&tr, 0, sizeof(tr)); tr.fraction = 1; start[0] = viewer->v.origin[0]; start[1] = viewer->v.origin[1]; start[2] = viewer->v.origin[2] + viewer->v.view_ofs[2]; //aim straight at the center of "seen" from our eyes end[0] = 0.5 * (seen->v.mins[0] + seen->v.maxs[0]); end[1] = 0.5 * (seen->v.mins[1] + seen->v.maxs[1]); end[2] = 0.5 * (seen->v.mins[2] + seen->v.maxs[2]); tr = SV_ClipMoveToEntity (sv.edicts, start, vec3_origin, vec3_origin, end); if (tr.fraction == 1)// line hit the ent return false; //last attempt to eliminate any flaws... if ((!strcmp(pr_strings + seen->v.classname, "player")) || (sv_cullentities.value > 1)) { for (i = 0; i < 64; i++) { end[0] = seen->v.origin[0] + offsetrandom(seen->v.mins[0], seen->v.maxs[0]); end[1] = seen->v.origin[1] + offsetrandom(seen->v.mins[1], seen->v.maxs[1]); end[2] = seen->v.origin[2] + offsetrandom(seen->v.mins[2], seen->v.maxs[2]); tr = SV_ClipMoveToEntity (sv.edicts, start, vec3_origin, vec3_origin, end); if (tr.fraction == 1)// line hit the ent { Con_DPrintf (va("found ent in %i hits\n", i)); return false; } } } return true; }
Go to SV_WriteEntitiesToClient and find this code and add the yellow text:
if (!ent->v.modelindex || !pr_strings[ent->v.model])
continue;
for (i=0 ; i < ent->num_leafs ; i++)
if (pvs[ent->leafnums >> 3] & (1 << (ent->leafnums&7) ))
break;
if (i == ent->num_leafs)
continue; // not visible
if (sv_cullentities.value && SV_InvisibleToClient(clent,ent))
continue; // Baker: bail out and skip to next entity because we are not going to send data on this one