Firstly add a LocalScript inside of StarterCharacterScripts, and name it whatever you like
local Character = script.Parent
--Services
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--
RunService.RenderStepped:Connect(function(DeltaTime)
if Character:GetAttribute("Stun") then
Character.Humanoid.WalkSpeed = 0
Character.Humanoid.JumpPower = 0
Character.Humanoid.JumpHeight = 0
else
Character.Humanoid.WalkSpeed = 16
Character.Humanoid.JumpPower = 50
Character.JumpHeight = 7.2
end
end)
So this is quite simple, and likely something you’ve already seen. Basically if the Character has an attribute “Stun”, then - make them stun/put their Speed, and Jump to 0. This is under a RenderStepped event has if a player tries to set their speed and Jump back to normal, then the very next frame the code will set it back to 0 if the player is stunned.
On the server side it is quite simple, add a module inside a folder rename it to “Stun”, and put it inside of ServerScriptService.
local module = {}
module.Stuns = {}
function module.Create(Character,Duration)
Character:SetAttribute("Stun",true)
module.Stuns[Character] = workspace:GetServerTimeNow() + Duration
end
return module
All this does is, it adds the attribute “Stun” to the character, and stores a timestamp inside of a table. This will be important later.
Now add a normal script, and put it inside of the same folder as the module. Rename the script to something like “Stun_handler”
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local module = require(script.Parent.Stun)
RunService.Heartbeat:Connect(function()
for Character,Timestamp in pairs(module.Stuns) do
if (Character and Character.Parent) then
if workspace:GetServerTimeNow() >= Timestamp then
Character:SetAttribute("Stun",false)
module.Stuns[Character] = nil
elseif not Character:GetAttribute("Stun") then
Character:SetAttribute("Stun",true)
end
end
end
end)
What this does is it removes the stun off the character, and also removes the stun from the table, when the time has ran out. This is done by a HeartBeat event on the server side. There might be a better way of doing this, but this is what I came up with. So that timestamp comes in handy here as it allows us to stack the stuns so that if the player gets stunned again, whilst already being stunned then that second stun’s timestamp is when the stun will be removed.
Now you may be thinking, can’t the player simply remove the “Stun” attribute? And yes they can, so to counter this we can add a LocalScript inside of StarterCharacterScripts. And add a RemoteEvent inside of ReplicatedStorage renamed to “Detect”- put it wherever in ReplicatedStorage.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Detect = ReplicatedStorage:WaitForChild("Stun").Detect -- This should be whatever path it is for your event
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
Character:GetAttributeChangedSignal("Stun"):Connect(function()
local Time = workspace:GetServerTimeNow() -- For highest accuracy
--The stun attribute has changed
if not Character:GetAttribute("Stun") then
--The stun attribute has been removed
Detect:FireServer(Time)
end
end)
So what happens here is that whenever the “Stun” attribute is changed, and put to false/nil we do a check, asking the server whether or not the Stun was removed or not. However as you may already know their is a slight delay when communicating from Client to Server, so we give the Time of when the “Stun” attribute was changed on the client, as a parameter. That is the “TIme” variable.
Now to finish off, update the “Stun_handler” script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Detect = ReplicatedStorage.Stun.Detect
local module = require(script.Parent.Stun)
Detect.OnServerEvent:Connect(function(Player,Time)
-- The client has detected a change in stun - checking if it is correct
local Character = Player.Character
local Timestamp = module.Stuns[Character]
if Timestamp then
if Time < Timestamp then
--Client is cheating
Player:Kick("Cheating")
end
end
end)
RunService.Heartbeat:Connect(function()
for Character,Timestamp in pairs(module.Stuns) do
if (Character and Character.Parent) then
if workspace:GetServerTimeNow() >= Timestamp then
Character:SetAttribute("Stun",false)
module.Stuns[Character] = nil
elseif not Character:GetAttribute("Stun") then
Character:SetAttribute("Stun",true)
end
end
end
end)
Now here we firstly get the Timestamp inside the module, if we find it (this RemoteEvent is ran no matter if the player is cheating or not, so even if the Stun is removed, because it has run out, the RemoteEvent will still run so the Timestamp can be nil). Then if the Timestamp exist, we check if the Time given by the Client is less than the Timestamp, and if it is then, the player must be cheating as the Stun was removed too quickly.
This is it, if you have any ways of improving this system, please let me know, and I’ll update it.