Anti-Cheat Lag.
#1

I wrote myself a simple server and money sided anti-cheat into my gamemode which seems to work fine for the most part. However their are bits of lag where client and server dont seem to quite match up. I added debug statements into the code to watch the steps of the Anti-Cheat and consistently alert me of a second or so when the client and server are not synced up. This is normally not a problem but sometimes, especially when a player spawns, if the client freezes up a bit, the sync is not complete enough for them to either lose the money/weapons or for them to be banned due to hacks. Anoter instance is I have a part of code that will consistently give player money and if that happens too much, they seem to end up getting banned from cash hacks.

My question is, is there anyway to avoid this apparant lag or a better way to do the code more efficiently. I hope I have explained myself clearly :P

pawn Код:
GivePlayerCash(playerid, money)
{
    dailyEarning[playerid] += money;
    playerCash[playerid] += money;
    GivePlayerMoney(playerid, money);
}

TakePlayerCash(playerid, money)
{
    GivePlayerMoney(playerid, 0 - money);
    playerCash[playerid] -= money;
}

ResetPlayerCash(playerid)
{
    new money = GetPlayerCash(playerid);
    dailyEarning[playerid] -= money;
    playerCash[playerid] -= money;
    ResetPlayerMoney(playerid);
}

ResetPWeapons(playerid)
{
    ResetPlayerWeapons(playerid);
    for (new i = 0; i < 13; i++) {
        playerWeapon[playerid][i] = 0;
    }
}

GivePWeapon(playerid, weapon, ammo)
{
    switch (weapon)
    {
        case 0,1: playerWeapon[playerid][0] = weapon;
        case 2,3,4,5,6,7,8,9: playerWeapon[playerid][1] = weapon;
        case 10,11,12,13,14,15: playerWeapon[playerid][10] = weapon;
        case 16,17,18: playerWeapon[playerid][8] = weapon;
        case 22,23,24: playerWeapon[playerid][2] = weapon;
        case 25,26,27: playerWeapon[playerid][3] = weapon;
        case 28,29,32: playerWeapon[playerid][4] = weapon;
        case 30,31: playerWeapon[playerid][5] = weapon;
        case 33,34: playerWeapon[playerid][6] = weapon;
        case 35,36,37,38: playerWeapon[playerid][7] = weapon;
        case 39: playerWeapon[playerid][8] = weapon;
        case 40: playerWeapon[playerid][12] = weapon;
        case 41,42,43: playerWeapon[playerid][9] = weapon;
        case 44,45,46: playerWeapon[playerid][11] = weapon;
    }
    GivePlayerWeapon(playerid, weapon, ammo);
}
The function beneath is within a one second timer

pawn Код:
if (GetPlayerMoney(i) > playerCash[i])
                {
                    cashHackWarn[i]++;
                    format(string, sizeof(string), "Cash hack warn! %d %d", GetPlayerMoney(i), playerCash[i]);
                    SendClientMessage(i, COLOUR_ERROR, string);
                    //if (cashHackWarn[i] == 2) BanPlayer(i, "AUTO", "Anti-Cheat", "Cheat ID 0 was detected!");
                    if (cashHackWarn[i] >= 4) {
                        KickPlayer(i, "AUTO", "Cheat ID 0 Detected!");
                    }
                }
                else if (GetPlayerMoney(i) < playerCash[i])
                {
                    cashHackWarn[i]++;
                    format(string, sizeof(string), "Money shortage warn! %d %d", GetPlayerMoney(i), playerCash[i]);
                    SendClientMessage(i, COLOUR_ERROR, string);
                    if (cashHackWarn[i] >= 3) playerCash[i] = GetPlayerMoney(i);
                }
                else
                {
                    if (cashHackWarn[i] > 0) SendClientMessage(i, COLOUR_ERROR, "Cash false alarm!");
                    cashHackWarn[i] = 0; // False alarm! Reset cash warning!
                }
            }

            // Weapon anti-cheat
            new weapon;
            new ammo;
            for (new x = 0; x < 13; x++)
            {
                if (tazerDrawn[i]) weaponHackWarn[i][x] = 0;
                else
                {
                    if (timeoutCount[i] < 2)
                    {
                        GetPlayerWeaponData(i, x, weapon, ammo);
                        if (weapon != 0)
                        {
                            playerAmmo[i][x] = ammo;
                            if (playerWeapon[i][x] != weapon) {
                                weaponHackWarn[i][x]++;
                                format(string, sizeof(string), "Weapon hack warn! %d %d", weapon, playerWeapon[i][x]);
                                printf("%s Weapon hack warn! %d %d", ReturnPlayerName(i), weapon, playerWeapon[i][x]);
                                SendClientMessage(i, COLOUR_ERROR, string);
                                if (weaponHackWarn[i][x] >= 4) {
                                    KickPlayer(i, "AUTO", "Cheat ID 1 Detected!");
                                }
                            }
                            else { weaponHackWarn[i][x] = 0; }
                        }
                        else
                        {
                            if (playerWeapon[i][x] > 0)
                            {
                                weaponHackWarn[i][x]++;
                                format(string, sizeof(string), "Weapon loss warn! %d %d", weapon, playerWeapon[i][x]);
                                SendClientMessage(i, COLOUR_ERROR, string);
                                printf("%s Weapon loss warn! %d %d", ReturnPlayerName(i), weapon, playerWeapon[i][x]);
                                if (weaponHackWarn[i][x] >= 4) {
                                    printf("%s Weapons were removed %d %d", ReturnPlayerName(i), weapon, playerWeapon[i][x]);
                                    playerWeapon[i][x] = 0;
                                    playerAmmo[i][x] =0;
                                }
                            }
                            else { weaponHackWarn[i][x] = 0; }
                        }
                    }
                }
            }
Reply
#2

I think that will help you out.
Quote:
Originally Posted by cessil
Посмотреть сообщение
Sync System
A thing I've been keeping a secret for a while is a sync system to check that a player is syncing right, it takes the method of the health checking system and applies it to armour, ammo and position ect.
here's an example of how it works with armour.
variables:
1. what their armour should be (armourShouldBe)
2. what their last updated armour was (lastArmourChecked)
3. is the player's armour synced (armourSynced)
4. the armour on the check (currentArmour)

every time you set a players armour, set armourShouldBe to the new value and set armourSynced to 0 (false)
then every x you check the player's armour, if the armourSynced is set to 0 then check if currentArmour is the same as armourShouldBe, if it is then set armourSynced to 1.

if armourSynced is set to 1 then check currentArmour to lastArmourChecked, if currentArmour is larger than lastArmourChecked they used a non scripted way to get armour, if its less set lastArmourChecked to currentArmour.

Break down into steps:
1. SetPlayerArmour sets armourShouldBe to the new value, sets armourSynced to 0
2. Your AC check goes off and stores their current armour as currentArmour
3a. If armourSynced is 0 check if currentArmour is the same as armourShouldBe, if it is then set armourSynced to 1
3b. If armourSynced is set to 1 then check currentArmour against lastArmourChecked, if currentArmour is less than lastArmourChecked then set lastArmourChecked to currentArmour.
If currentArmour is larger than lastArmourChecked then flag the player for a possible cheater

script:
I've changed variable names and inserted a lot of comments to make it more clearer, so sorry if I've made a mistake
pawn Код:
//I had mine set up as an include for every script to use, then I could use SetPlayerHealth in any script without worrying about the AC, all I had to do was #include <anticheat>
//so this is inside the include, it's a hook for SetPlayerHealth
stock AC_SetPlayerHealth(playerid,Float:amount)
{
    if(amount > 99.0) amount = 99.0;
    if(amount < 0.0) amount = 0.0;
   
    DeletePVar(playerid,"healthSynched");
    SetPVarFloat(playerid,"healthShouldBe",amount);//what the player's health SHOULD be in the future
    return SetPlayerHealth(playerid,amount);
}

#if defined _ALS_SetPlayerHealth
    #undef SetPlayerHealth
#else
    #define _ALS_SetPlayerHealth
#endif
#define SetPlayerHealth AC_SetPlayerHealth
pawn Код:
public OnPlayerUpdate(playerid)
{
    if(GetPVarInt(playerid,"banned")) return 1;//no need to accept any more updates, I had a 1000ms timer from the time they were banned to the time they were kicked from the server so they got any messages I sent them
    if(GetPVarInt(playerid,"specid") != -1) return 1;//for admins, otherwise it'd trigger speed AC
    if(GetPVarInt(playerid,"dead")) return 0;//I had server side health and damage from other players using OnPlayerGiveDamage, players that had "dead" set to 1 could not harm others with attacks

    new time = gettime();
    SetPVarInt(playerid,"ACUpdated",time);//so you can use in another script to display the players last update, incase someone was pausing or having inconsistant updates

    if(lastHealthUpdate[playerid] < time)//to make sure it would only check once per second max
    {
        //1 second intervals
        lastHealthUpdate[playerid] = time;

        new Float:currentHealth;
        GetPlayerHealth(playerid,currentHealth);//gets the players current health as last reported to the server

        new currentHealthInt = floatround(currentHealth,floatround_round);//gets the current health as an integer, they are easier to work with

        new healthShouldBeInt = floatround(GetPVarFloat(playerid,"healthShouldBe"),floatround_round);
        SetPVarFloat(playerid,"currentHealth",currentHealth);//what the players current health is, I used this to show in admin information on players

        if(currentHealthInt == healthShouldBeInt) SetPVarInt(playerid,"healthSynched",1); //if their health is synced

        if(!GetPVarInt(playerid,"healthSynched"))//health is not synched
        {
            if(currentHealthInt > healthShouldBeInt)//health is greater than what it should be
            {
                healthUpdateFail{playerid}++;//increase a variable to count to x
                switch(healthUpdateFail{playerid})//depending on how long it's been, do something
                {
                    case 30, 45:
                    {
                        MessageAdminsEx("%d %n Health Desynced For %ds Attempting To Resync %d/%d",playerid,playerid,healthUpdateFail{playerid},currentHealthInt,healthShouldBeInt);//warn the admins about players health being desynched, this is a custom function, don't complain when it doesn't work for you
                        SetPlayerHealth(playerid,GetPVarFloat(playerid,"healthShouldBe"));//try to set the players health to what it should be
                    }
                    case 60:
                    {
                        TimeOut(playerid,"Health Desynced For Over 1min");//if they don't resynch their health for x then they probably won't ever, just make them rejoin
                        return 1;
                    }
                }
            }
        }
        else //health IS synched
        {
            healthUpdateFail{playerid} = 0;//reset the variable as they are now synched
            if(healthShouldBeInt > currentHealthInt)// if their health has dropped and they are synched
            {
                SetPVarFloat(playerid,"healthShouldBe",currentHealth); //drop the variable to their current health
            }
            if(currentHealthInt > healthShouldBeInt && currentHealthInt <= 100 && currentHealthInt  > 0)//if health is in sane limits, and it's gotten higher somehow, set it to what it should be
            {
                SetPlayerHealth(playerid,GetPVarFloat(playerid,"healthShouldBe"));
                /*I found that even with these checks you still cannot be 99% certain that they were cheating,
                so I set their health to what it should be.
                If they are cheating and they keep setting their health to high amounts then they'd get kicked anyway*/

            }
            //if their health is above 100 or below 0 bans
            if(currentHealthInt > 100 || currentHealthInt < 0)
            {
                BanPlayerEx(playerid,BanAC,"Invalid Health: %f",currentHealth);//another custom function, I found anyone with health higher than 100 or less than 0 to be a cheater
                return 1;
            }
        }
    }
}
I use the sync system for health, armour, weapons, ammo, vehicleids, vehicle health, position, vehicle position

Checking if a player is AFK or not can also help
http://forum.sa-mp.com/showpost.php?...8&postcount=43

For an example implementation
http://forum.sa-mp.com/showpost.php?...6&postcount=46
Reply
#3

Thanks for your reply! Ill definitely give this a good read and definitely looks like the answer.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)