Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

SV_Invisible_To_Client (Anti-Wallhack) Goto page 1, 2 Next

| Inside3d Forums Forum Index -> Programming Tutorials

View previous topic :: View next topic
Author | Message
--- | ---
Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Sun Dec 07, 2008 2:53 pm Post subject: SV_Invisible_To_Client (Anti-Wallhack) |

Quake had always had a weakness in that it was vulnerable to "stick models", textures being removed in maps, external cheater opengl32.dll libraries that allow walls to be toggled don't draw walls (etc.) 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.

Quote:
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.

Adding the Code:
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

Quote:
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:

Quote:
char localmodels[MAX_MODELS][5]; // inline model names for precache cvar_t sv_cullentities = {"sv_cullentities","1", false, true}; //============================================================================

b. We need to register the cvar. Go 5 or 6 lines downad to SV_Init.c and add the yellow text.

Quote:
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);

c. Now we need to add our new function, find this code:

Code:
/* ============= SV_WriteEntitiesToClient ============= */

And above it paste our new function:

Code:
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;
}

4. Finally we need to make sure this screening process gets called:
Go to SV_WriteEntitiesToClient and find this code and add the yellow text:

Quote:
if (!ent->v.modelindex || !pr_strings[ent->v.model]) continue; 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_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

Last edited by Baker on Mon Dec 08, 2008 12:09 am; edited 3 times in total

Back to top |

Stealth Kill Joined: 29 Dec 2006 Posts: 83 |

Posted: Sun Dec 07, 2008 6:28 pm Post subject: |

Can you add a entity view distance? For example you can see only entites max 1000 units away from your player model i need something like that for singleplayer. and i think step b) should be
Cvar_RegisterVariable (&sv_cullentities);

Back to top |

Downsider Joined: 16 Sep 2008 Posts: 478 |

Posted: Sun Dec 07, 2008 7:21 pm Post subject: |

Stealth Kill wrote:
Can you add a entity view distance? For example you can see only entites max 1000 units away from your player model i need something like that for singleplayer. and i think step b) should be
Cvar_RegisterVariable (&sv_cullentities);

Just modify this so if the distance between the player and the entity is >= 1000, it returns the same as if they were behind an object. Easy .

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Sun Dec 07, 2008 7:48 pm Post subject: |

Quote:
and i think step b) should be Cvar_RegisterVariable (&sv_cullentities);

Didn't catch that, heh. Fixed. Thanks!

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Dec 08, 2008 12:00 am Post subject: Re: SV_Invisible_To_Client (Anti-Wallhack) |

Hmmm. This looks like it needs improvement really:

Quote:
if ((SV_InvisibleToClient(clent,ent)) && (sv_cullentities.value)) continue;

It should go like this:

Quote:
if (sv_cullentities.value && SV_InvisibleToClient(clent, ent)) continue;

(Due to C's short-circuit evaluation, if sv_cullentities has a non-zero value the second part, SV_InvisibleToClient, doesn't even get called and it really shouldn't get called unless it has a non-zero value. And I don't see anything in SV_InvisibleToClient that returns early is sv_cull_entities is 0.)

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Dec 08, 2008 1:26 am Post subject: |

Stealth Kill wrote:
Can you add a entity view distance?

I haven't played around with calculating distances yet, but it probably goes something like this:

Quote:
If ( fabs ( Length(viewer->v.origin, seen->v.origin)) > 1000.0 ) continue;

And would go before this:

Quote:
if (sv_cullentities.value && SV_InvisibleToClient(clent, ent)) continue;

I haven't mess around with that kind of stuff and am not looking at the source a the moment, so I don't know if that code works "as-is" or needs slight touching.

Back to top |

r00k Joined: 13 Nov 2004 Posts: 483 |

Posted: Mon Dec 08, 2008 8:56 pm Post subject: |

You meant to say?

Code:
VectorSubtract(clent->v.origin, ent->v.origin, edist); if (VectorLength(edist) > 1024) continue; if (sv_cullentities.value) {
if (SV_InvisibleToClient(clent,ent))
continue;
}

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Dec 08, 2008 9:06 pm Post subject: |

r00k wrote:
You meant to say?

Code:
VectorSubtract(clent->v.origin, ent->v.origin, edist); if (VectorLength(edist) > 1024) continue; if (sv_cullentities.value) {
if (SV_InvisibleToClient(clent,ent))
continue;
}

^^^ is better than me
I'm still learning.

Back to top |

r00k Joined: 13 Nov 2004 Posts: 483 |

Posted: Mon Dec 08, 2008 9:15 pm Post subject: |

WEll, I meant the distance check is in SV_WriteEntitiesToClient not in SV_InvisibleToClient.

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Dec 08, 2008 9:50 pm Post subject: |

r00k wrote:
WEll, I meant the distance check is in SV_WriteEntitiesToClient not in SV_InvisibleToClient.

Hehe, I should have only quoted the relevant part. If I didn't know recognize the equivalence of the latter part, I'd be in a whole world of trouble
/The dangers of self-deprecating humor

Back to top |

LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Sat Sep 26, 2009 12:20 am Post subject: |

You forgot to define offsetrandom. Also using 64 traces is exceedingly slow - DarkPlaces uses only a few and simply renews a timer in the edict_t whenever one hits the entity, as long as the timer is in the future it shows the entity, the timer has to be set low (like 0.2) to prevent wallhacks from being useful, but it's much lower cpu usage.
I should also mention that I got the idea from Unreal1, a developer note mentioned the technique, and that the lag/flicker problems of it are lost in the noise of multiplayer gameplay, it looks like some kind of network lag rather than a major annoyance.
UnrealEngine3 however just uses the single trace to the center, and modders have complained about how much trouble this causes, it seems UE3 does one thing worse than UE1

Back to top |

Team Xlink Joined: 25 Jun 2009 Posts: 320 |

Posted: Sat Sep 26, 2009 12:31 am Post subject: |

Code:
#define offsetrandom(MIN,MAX) (((double)(rand() + 0.5) / ((double)RAND_MAX + 1)) * ((MAX)-(MIN)) + (MIN))

The equivalent to DarkPlaces lhrandom courtesy of LordHavoc.
So about the 64 traces being slow.
I would set it to lets say 32?
_________________

Anonymous wrote:
if it works, it works. if it doesn't, HAHAHA!

Back to top |

Downsider Joined: 16 Sep 2008 Posts: 478 |

Posted: Sat Sep 26, 2009 12:38 am Post subject: |

It shouldn't make a dent on a decent dedicated server.A

Back to top |

LordHavoc Joined: 05 Nov 2004 Posts: 243 Location: western Oregon, USA |

Posted: Sat Sep 26, 2009 12:40 am Post subject: |

Downsider wrote:
It shouldn't make a dent on a decent dedicated server.A

You haven't run helm18.bsp (10000 knights)
Also you probably haven't played dpmod with the cvar spawnmonsters set to 2000 (and multiple players)
Note the cost goes up with player count exponentially (each player is producing more entities to cull, and each player runs culling code on all of them).

Back to top |

Downsider Joined: 16 Sep 2008 Posts: 478 |

Posted: Sat Sep 26, 2009 4:22 am Post subject: |

lol well I was talking more in context to what XLink needs for his project, but no, I did not think of that xD

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Programming Tutorials | All times are GMT Goto page 1, 2 Next
Page 1 of 2

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group