Due to the latency between server and client, the player sees their character is outside of the hitbox but the server still has the character inside of the range. Hitboxes are cylinder parts with the attributes “Life” for how long they last before being destroyed and then inflicting “Damage” amount of damage to players in the radius.
Here is a video of my problem:
To fix this I attempted to code a solution that follows this logic:
-Warnpart is added
-Client checks debris time on the warnpart
-After lifetime expires client reports position to server
-Server compares position to where the client theoretically could have gone in the span of the damage part spawning (this can be delegated to an anti-teleport/speedhack script running a simulation of all characters)
-Client will report if they got hit as well (can’t be trusted fully but there has to be some level of trust in this system)
So far to implement this logic i have this code, but i’m stuck on what to do next:
Client:
local Remotes = game.ReplicatedStorage.Remotes
local RequestDamagePart = Remotes.RequestDamagePart
RequestDamagePart.OnClientInvoke = function()
return Character.PrimaryPart.Position, workspace:GetServerTimeNow()
end
workspace.DamageParts.ChildAdded:Connect(function(child)
child.Destroying:Connect(function()
for _, v in pairs(workspace:GetPartsInPart(child)) do
if v.Parent == Character then
--Tell server i got hit
end
end
end)
end)
Server:
local Remotes = game.ReplicatedStorage.Remotes
local RequestDamagePart = Remotes.RequestDamagePart
workspace.DamageParts.ChildAdded:Connect(function(child)
local hitTable = {}
if child:IsA("BasePart") then
task.wait(child:GetAttribute("Life"))
for _, player in pairs(game.Players:GetPlayers()) do
if not player.Character or not player.Character:FindFirstChild("Humanoid") then
continue
end
local cachedPosition = player.Character.PrimaryPart.Position
if (child.Position - cachedPosition).Magnitude <= child.Size.Z then
player.Character.Humanoid:TakeDamage(child:GetAttribute("Amount"))
end
if (child.Position - cachedPosition).Magnitude <= child.Size.Z * 3 then
task.spawn(function()
local pingTime = workspace:GetServerTimeNow()
local timeoutcount = 0
local timeout = 5
local result = nil
task.spawn(function()
local position, timestamp
local succ, err = pcall(function()
position, timestamp = RequestDamagePart:InvokeClient(player)
end)
if not succ then
warn(err)
end
if position and timestamp and typeof(position) == "Vector3" and typeof(timestamp) == "number" then
result = "\n\n"..tostring(position)..", "..timestamp
else
--must be exploiting
if (child.Position - cachedPosition).Magnitude <= child.Size.Z then
player.Character.Humanoid:TakeDamage(child:GetAttribute("Amount"))
end
end
end)
repeat
task.wait(1)
timeoutcount = timeoutcount + 1
until timeoutcount >= timeout or result ~= nil
if result then
print(result)
else
warn("Player "..player.Name.." took too long to respond to remote call to ReplicatedStorage.Remotes.RequestDamagePart")
--must be exploiting or laggy
end
end)
end
end
child:Destroy()
end
end)