Inside3D!
     

SV_Invisible_To_Client (Anti-Wallhack)
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials
View previous topic :: View next topic  
Author Message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sun Dec 07, 2008 2:53 pm    Post subject: SV_Invisible_To_Client (Anti-Wallhack) Reply with quote

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
View user's profile Send private message
Stealth Kill



Joined: 29 Dec 2006
Posts: 83

PostPosted: Sun Dec 07, 2008 6:28 pm    Post subject: Reply with quote

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
View user's profile Send private message
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Sun Dec 07, 2008 7:21 pm    Post subject: Reply with quote

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 Surprised.
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sun Dec 07, 2008 7:48 pm    Post subject: Reply with quote

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


Didn't catch that, heh. Fixed. Thanks!
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Mon Dec 08, 2008 12:00 am    Post subject: Re: SV_Invisible_To_Client (Anti-Wallhack) Reply with quote

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
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Mon Dec 08, 2008 1:26 am    Post subject: Reply with quote

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
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Mon Dec 08, 2008 8:56 pm    Post subject: Reply with quote

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
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Mon Dec 08, 2008 9:06 pm    Post subject: Reply with quote

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 Very Happy

I'm still learning.
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Mon Dec 08, 2008 9:15 pm    Post subject: Reply with quote

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

Wink
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Mon Dec 08, 2008 9:50 pm    Post subject: Reply with quote

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

Wink


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 Very Happy

/The dangers of self-deprecating humor
Back to top
View user's profile Send private message
LordHavoc



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

PostPosted: Sat Sep 26, 2009 12:20 am    Post subject: Reply with quote

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 Smile
Back to top
View user's profile Send private message Visit poster's website
Team Xlink



Joined: 25 Jun 2009
Posts: 320

PostPosted: Sat Sep 26, 2009 12:31 am    Post subject: Reply with quote

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
View user's profile Send private message
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Sat Sep 26, 2009 12:38 am    Post subject: Reply with quote

It shouldn't make a dent on a decent dedicated server.A
Back to top
View user's profile Send private message
LordHavoc



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

PostPosted: Sat Sep 26, 2009 12:40 am    Post subject: Reply with quote

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


You haven't run helm18.bsp (10000 knights) Smile

Also you probably haven't played dpmod with the cvar spawnmonsters set to 2000 (and multiple players) Smile

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
View user's profile Send private message Visit poster's website
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Sat Sep 26, 2009 4:22 am    Post subject: Reply with quote

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
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    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