Introduction:
In the Roblox Engine, all changes a client makes that isn’t a part it networkly owns, is not replicated. However, characters, can be directly controlled by clients. This means the client can make their character move, rotate, speed up, speed down however it decides. This is so that you can smoothly control you character on your own client. However, exploiters exploit this by manipulating their character to fly, teleport, fling others, and more. In this tutorial, I will be explaining how to stop one of these; Flying
How it usually works:
as the server, you want to be checking how far players are from the ground. Sometimes, if they’re above the limit you set it could be from the client being laggy on their side, or maybe a really lucky jump. So you usually have to add a “strike” system. The server uses a raycast to check for any ground, and if it finds some, it moves on to the next player. If the player continues to “fly” you can kick them
Step one: set up variables and the strike tables
This is from my free-to-use basic anti-fly
local FlySettings = {
StrikesUntilRefresh = 6; -- if the player is above MaxHeight this many times, they get refreshed
StrikesUntilKick = 5; -- when a player gets refreshed this amount of times, they get kicked
MaxHeight = Vector3.new(0,-15,0); -- if distance between player and ground is above this, the player is consdiered flying
Intervals = 15; -- amount of heartbeats until the game checks all players
AddStrikeIfPlatformStanding = true; -- if player is airborne, add an aditional strike for having platformstand
};
local PlayerStrikes = {};
local KickStrikes = {};
local Players = game:GetService("Players");
local RS = game:GetService("RunService")
Players.PlayerAdded:Connect(function(player) -- adding players to the dictionaries as they join
player.CharacterAdded:Connect(function(character)
PlayerStrikes[player.Name] = 0; -- playerstrike restarts every CharacterAdded
end)
KickStrikes[player.Name] = 0;
end)
Players.PlayerRemoving:Connect(function(player)
PlayerStrikes[player.Name] = nil; -- removes the player from the dictionary since they left the game
KickStrikes[player.Name] = nil;
end)
next, we’d want to make a function that “refreshes” the player. This re-loads the character, and re-places them where they were before, and removes their forcefield. It basically removes any bodymovers in their character that would’ve made them fly. If this was a false positive, it also wouldn’t interfere much with the player, since on their end they just stopped moving for a few frames
local function RefreshPlayer(player, pos) -- this function reloads the player, removing any bodymovers and returning them back to their spot as if nothing happend
player:LoadCharacter();
player.Character:SetPrimaryPartCFrame(CFrame.new(pos));
local ForceField = player.Character:FindFirstChildWhichIsA("ForceField");
if (ForceField) then
ForceField:Destroy();
end
end
Now, we want to make a loop to check each character if they’re near enough on the ground or not. We first make a RaycastParam to blacklist all the characters, so that the raycast doesn’t hit the character itself. I Connected this to RunService.Heartbeat
Keep in mind you shouldn’t loop every heartbeat, since updated player positions wont send for a bit, and there wont be much time for the characters to move. We also want to make a variable to check how many heartbeats have gone since the last check of all the players. You should add an additional strike if the character is PlatformStanding, because almost all fly exploits have the character PlatformStanding.
CurrentInterval = 0;
RS.Heartbeat:Connect(function()
if (CurrentInterval >= FlySettings.Intervals) then
CurrentInterval = 0;
local Characters = {}; -- ignorelist for raycasting
for _, player in pairs(Players:GetPlayers()) do
if (player.Character) then
table.insert(Characters, player.Character);
end
end
local FlyParams = RaycastParams.new() -- creating the params for checking if the players are on the ground or not
FlyParams.FilterType = Enum.RaycastFilterType.Blacklist;
FlyParams.FilterDescendantsInstances = Characters;
-- checking each player --
for _, player in pairs(game.Players:GetPlayers()) do
if (player.Character) then -- if there's no player.Character it can mean the player is dead, but correct me if im wrong
local Root = player.Character:FindFirstChild("HumanoidRootPart");
local Humanoid = player.Character:FindFirstChild("Humanoid");
local Head = player.Character:FindFirstChild("Head");
if (Humanoid and Humanoid.Health > 0) then -- making sure player isn't dead
if (not Root) then -- this is a sign of bypassing anti-TP scripts, so we're refreshing the player to their head's position
RefreshPlayer(player, Head.Position);
continue;
end
local FoundGround = workspace:Raycast(Root.Position, FlySettings.MaxHeight, FlyParams);
if (not FoundGround) then -- if the ground is not found, meaning the player is airborne thus, we give them a strike
if (FlySettings.AddStrikeIfPlatformStanding) then
if (Humanoid:GetState() == Enum.HumanoidStateType.PlatformStanding) then
PlayerStrikes[player.Name] += 2;
else
PlayerStrikes[player.Name] += 1;
end
end
if (PlayerStrikes[player.Name] >= FlySettings.StrikesUntilRefresh) then -- if the player was airborne for too long
if (KickStrikes[player.Name] >= FlySettings.StrikesUntilKick) then -- player kicked for flying often
player:Kick();
continue;
end
local GroundPos = Root.Position; -- position where we're gonna refresh (respawn) the player. It's the root part incase if no ground is found in the next raycast
local FindingGround = workspace:Raycast(Root.Position, Vector3.new(0,-300,0), FlyParams);
if (FindingGround) then
GroundPos = FindingGround.Position+Vector3.new(0,5,0) -- found the ground, so the player will be respawned on the ground
end
PlayerStrikes[player.Name] = 0;
RefreshPlayer(player, GroundPos); -- refreshing the player for beign airborne
KickStrikes[player.Name] += 1;
end
end
end
end
end
end
CurrentInterval += 1;
end)
Conclusions
just like the title says, this is a basic anti-fly script. Each game is different, and there could be a bypass (or false positives) if your game had some “plane” or something allowing the player to fly. Please feel free to reply if you have questions, or if I made a mistake.
You can get my full script from this tutorial here