Hitbox Combat Attribute Issue

  1. What do you want to achieve?
    I’m trying to make client sided combat

  2. What is the issue?
    The player character has an attribute called state, the server script shouldnt run if the state isn’t Idle but it does. The server script works if the task.wait is 1 but it doesn’t if it’s .5

you can see here I press twice and attack works even thought it prints it can’t

local script

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local uips = game:GetService("UserInputService")
local remote = game.ReplicatedStorage.RemoteEvent


uips.InputBegan:Connect(function(input)
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		
		local state = character:GetAttribute("State")
		local torso = character.HumanoidRootPart
		
		if state ~= "Idle" then print("Cant hit") return end
		
		local overlap = OverlapParams.new()
		overlap.FilterDescendantsInstances = {character}
		
		overlap.FilterType = Enum.RaycastFilterType.Exclude
		
		local attacklist = {}
		
		local heart = game:GetService("RunService").Heartbeat:Connect(function()
			
			local hitbox = workspace:GetPartBoundsInBox(torso.CFrame + torso.CFrame.LookVector*4, Vector3.new(5,4,5), overlap)
			
			for i,v in hitbox do
				local opponent = v.Parent:FindFirstChild("Humanoid")
				
				if opponent then
					attacklist[opponent.Parent.Name] = opponent.Parent
				end
			end
			
			
			remote:FireServer(attacklist)
		end)
		
		task.wait(1)
		heart:Disconnect()
	end
end)

server script

local remote = game.ReplicatedStorage.RemoteEvent
local players = game:GetService("Players")

players.PlayerAdded:Connect(function(newplayer)
	local character = newplayer.Character or newplayer.CharacterAdded:Wait()
	
	character:SetAttribute("State", "Idle")
end)

remote.OnServerEvent:Connect(function(player, attacklist)
	local character = player.Character
	local state = character:GetAttribute("State")
	
	if state ~= "Idle" then return end
	print("attacking", state)
	
	for i,v in attacklist do
		local opponentstate = v:GetAttribute("State")
		
		if opponentstate == "Evade" then return end
		
		v.Humanoid:TakeDamage(10)
	end
	
	character:SetAttribute("State", "Attack")
	task.wait(.5) -- doesn't work if it's .5 but works if it's 1
	character:SetAttribute("State", "Idle")
end)

Shouldnt it be state == idle if you want it to return?

no because then it will never fire because Idle is the default state, if the state is already Attack I want to prevent it from doing hitbox again

Then im guessing its due to the hit box being in heartbeat for 1 second.

I would recommend adding a debounce on the client and setting the state on the client to avoid remote delays.

Or change the heartbeat delay to 0.5 as well.

I don’t think its because of the heartbeat, if you look at the video at 2 seconds and pause the heartbeat is still running yet when I double click the heartbeat is still running, it prints it cant but it still does attack and the state attribute is still Attack. This is just debugging code, so I can make it work, I have a debounce on the more complicated one and same issue.

Also setting attributes on the client doesnt replicate which would cause issues if I want to check the states on the server and the client could exploit it.

I tried to print the state in the local script of the character and it seems it never updates on the client, I tried adding a while true do loop in the server and printing the characters state on the server, and in the local script I print the characters state too, in the local script it seems to always be Idle.

you can see in the video that the local script only prints Idle, and the serverscript prints it changed and it resets back to default, as it prints returned to idle.

987f9e277bb85871221a0c777b7f7297

local script

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character.Humanoid
local uips = game:GetService("UserInputService")
local remote = game.ReplicatedStorage.RemoteEvent

uips.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.F then
		local torso = character.HumanoidRootPart
		local state = character:GetAttribute("State")
		
		local overlap = OverlapParams.new()
		overlap.FilterDescendantsInstances = {character}
		overlap.FilterType = Enum.RaycastFilterType.Exclude
		
		local heart = game:GetService("RunService").Heartbeat:Connect(function()
			print(state)
			local hitbox = workspace:GetPartBoundsInBox(torso.CFrame + torso.CFrame.LookVector*4, Vector3.new(5,4,5), overlap)
			
			for i,v in hitbox do
				
			end
			
			remote:FireServer()
		end)
		
		task.wait(1)
		heart:Disconnect()
	end
end)

server script

local remote = game.ReplicatedStorage.RemoteEvent
local player = game.Players

player.PlayerAdded:Connect(function(newplayer)
	local character = newplayer.Character or newplayer.CharacterAdded:Wait()
	
	character:SetAttribute("State", "Idle")
	while true do
		task.wait()
		print("server state", character:GetAttribute("State"))
	end
end)

remote.OnServerEvent:Connect(function(player)
	local character = player.Character
	local state = character:GetAttribute("State")
	
	if state ~= "Idle" then return end
	--print("Attack", state)
	
	character:SetAttribute("State", "Attack")
	wait(1)
	character:SetAttribute("State", "Idle")
	print("returned to idle")
end)
1 Like

Dont forget to update your state variable inside the hearbeat loop

print(char:GetAttribute(state))

my mistake, the local state is changing if I print the updated character:GetAttribute() but what I’m still confused is even though the local and server is changing, if I double press attack it will work even though it should return because it’s already attacking.

I press twice here, and it prints Attack Idle twice even though it shouldnt

1 Like

if I put a if state ~= Idle on the local script the extra Attack doesnt run, but I think the player would be able to hack and remove the if statement, I’m not sure why it doesnt return in the server script

Thanks for your effort debugging, shows some real sincerity to your problem.

I think its because in the same frame the server sets it to idle, the client can immediate sneak in a remote event in that exact same frame and trigger another attack.

Checkout the frame number in the time stamp

For reference with some prints,
image

I’m still not sure what exactly it is you want, but I’m guessing you should probably use os.clock() or tick() somehow instead of task.wait(1)