How can I make this not exploitable

So how my code works is that the client plays an animation, and after a couple of seconds, it creates a hitbox and sends the hit players to the server. Now there are sanity checks, such as determining the distance between players, but if they’re exploiting, can’t they just spam the remote? It will essentially be like a constant AOE. How can I fix this? Here’s my code:

-- Client
local Character : Model = script.Parent
local Humanoid : Humanoid = Character:WaitForChild('Humanoid')
local HitAnimation : AnimationTrack = Humanoid:WaitForChild('Animator'):LoadAnimation(script.Hit)

local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")

local AOE_Cooldown = false
local Attacking = false

UserInputService.InputBegan:Connect(function(Input, GameProcesedEvent)
	if not GameProcesedEvent then
		if Input.UserInputType == Enum.UserInputType.MouseButton1 then
			if AOE_Cooldown or Attacking then return end
			Attacking = true
			task.delay(0.5, function()
				local VisualHitbox = Instance.new("Part")
				VisualHitbox.Size = Vector3.one*20
				VisualHitbox.Shape = Enum.PartType.Ball
				VisualHitbox.CanCollide = false
				VisualHitbox.CanQuery = false
				VisualHitbox.Anchored = true
				VisualHitbox.Massless = true
				VisualHitbox.BrickColor = BrickColor.new("Really red")
				VisualHitbox.Position = Character.HumanoidRootPart.Position
				VisualHitbox.Transparency = 0.75
				VisualHitbox.Material = Enum.Material.Neon
				VisualHitbox.Parent = workspace
				task.delay(1, function()
					local Tween = TweenService:Create(VisualHitbox, TweenInfo.new(0.5), {Transparency=1})
					Tween:Play()
					Tween.Completed:Connect(function()
						VisualHitbox:Destroy()
					end)
				end)
				for _, Target in ipairs(game.Workspace:GetChildren()) do
					if Target:IsA("Model") and Target:FindFirstChild("HumanoidRootPart") and Target:FindFirstChild("Humanoid") and Target ~= Character and (Character.HumanoidRootPart.Position - Target.HumanoidRootPart.Position).Magnitude <= 20 then
						game.ReplicatedStorage.Remotes.Attack:FireServer('AOE', Target)
					end
				end
			end)
			task.delay(1, function()
				Attacking = false
			end)
			AOE_Cooldown = true
			task.delay(6, function()
				AOE_Cooldown = false
			end)			
		end
	end
end)

local OldHP = Humanoid.Health

-- Animation is played here 
Humanoid.HealthChanged:Connect(function(CurrentHP)
	if OldHP > CurrentHP then
		HitAnimation:Play()
	end
	OldHP = CurrentHP
end)
-- Server
game.ReplicatedStorage.Remotes.Attack.OnServerEvent:Connect(function(Player, Attack, Target)
	local Character = Player.Character
	if Attack == "AOE" then
		if (Character.HumanoidRootPart.Position - Target.HumanoidRootPart.Position).Magnitude <= 25 then
			Target.Humanoid:TakeDamage(10)
		end
	end
end)

Try storing the last time of an attack for each player (in the server script), and check if the difference between the time when the event is fired and the stored value is greater than x seconds.
I believe you can achieve this using tick()

I personally prefer to use os.time() because tick() returns epoch time depending on timezones and os.time() always returns it in UTC. Not sure if this would be an issue in this case but i just prefer consistency.

1 Like

Oh yeah, that makes sense. In that case os,time() is an even better solution.

Hello, a bit late, not sure if you ve figured it out but what i would do is first of all have the client keep all the targets in a table and then if the amount of targets are more than 0, then fire the attack event with the targets table.
In the server i would keep a table on top of your remote event attack called something like playersOnCooldown, where i would store every player that is on cooldow, just like you have a cooldown for the animation on the client, and everytime that the remote gets fired i would check if the player is stored in the playersOnCooldownTable

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.