AntiCheats, in the general case, are targeted towards preventing exploits in a specific manner. A lot of the time (and for a lot of games), this is targeted towards Remotes - whether event or function. This tutorial won’t be too in-depth but if you’d like to request how to write an AntiCheat for a specific thing then I can add it to this; just leave a reply.
Lets take Tower Defense Simulator as an example. You can have 5 towers, no more (but you can have less). Either way, the server knows what towers you do have and therefore what towers you don’t have. If I fire a Remote telling the server to place a tower I don’t have when I know my game does not provide that option on the client, well I now know they’re an exploiter. An example of what this may look like:
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local RemoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent");
local PlayerTowers = {
[SomePlayerInstance] = {
"Tower1",
"Tower2",
"Tower3",
nil,
nil
}
}
-- Assume that PlayerTowers was made when/before the game started
function PlayerHasTower(Player, Tower)
local LocalTowers = PlayerTowers[Player];
if (LocalTowers) then
return (table.find(LocalTowers, Tower)) and true or false;
else
Player:Kick() -- Failed to retrieve towers; sometimes bugs can cause this or poor data management
end
end
RemoteEvent.OnServerEvent:Connect(function(Player, ...)
local Args = {...};
if (Args[1]:lower() == "place") then
if (Args[2]) then
if (PlayerHasTower(Player, Args[2])) then
-- Place the tower
else
Player:Kick() -- Player tried to place a tower they don't have
end
else
Player:Kick() -- Player tried to break remote, assuming that you can code correctly
end
end
end)
To generalise this, we are making sure that the server agrees that the player does indeed own or has access to these things. If they don’t, they’re firing the remotes by themselves and trying to exploit the game which we don’t want.
Another pretty common one is for games like Bubblegum Simulator. To open eggs, we want players to be within x studs of the egg before they can open it. We also want to make sure that this egg actually exists. An example may look like this:
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local RemoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent");
local Workspace = game:GetService("Workspace");
local GameEggs = Workspace:WaitForChild("Eggs");
local EggsTable = {};
for _, Egg in next, GameEggs:GetChildren() do
table.insert(EggsTable, Egg.Name); -- Insert the name of an egg into a list of eggs from the instances
-- It's better to do it this way since it's automatic and you won't have to keep writing out a new list when you add/remove eggs
end
function EggExists(Egg)
return (table.find(EggsTable, Egg)) and true or false;
end
function M(V1, V2)
return (V2 - V1).Magnitude;
end
RemoteEvent.OnServerEvent:Connect(function(Player, ...)
local Args = {...};
if (Args[1]:lower() == "openegg") then
local Egg = Args[2];
if (Egg) then
if (EggExists(Egg.Name)) then -- Assuming the 2nd argument is the model/instance of the egg and not a string
-- You should check if the player has enough money before or here
local Character = Player.Character;
if (Character) then
local Magnitude = M(Character.PrimaryPart.Position, Egg.PrimaryPart.Position);
if (Magnitude <= 10) then
-- Open egg if they're within 10 studs of the egg (you can change 10 to what you deem appropriate)
else
-- Player tried to fire a remote from too far away
end
end
end
else
Player:Kick() -- Again, player tried to break the remote, assuming you can code properly
end
end
end)