Studio freezing somehow without "exhausted allotted execution time" error ever showing up

I’m trying to make a NPC that attacks the player if they are touching and attacks again if the player is still touching after a short cooldown.

so after I updated the NPC script (the go_to_player function) to start attacking more than once if the player is still touching the NPC roblox studio will freeze if the NPC touches the player. but the freeze isn’t 100% freeze and it doesn’t start using 100% of my CPU. it’s almost like I somehow completely stopped the game rendering. but studio still doesn’t stop the game even after waiting for a while and stops responding but only after I press the stop button.

I’ve tried adding a bunch of print statements that are now commented out as you can see because they just told me the script was getting to the end of the function.
so I’m just at a loss of what to even try.

updated script:

humanoid = script.Parent.Humanoid
model = humanoid.Parent

local trigger_dist = 40
local see_dist = trigger_dist
local damage_wait_time = 0.5
local damage_per_hit = 4
canDoDamge = true

humanoid.Died:Connect(function()
	local mod = require(game.ReplicatedStorage.respawn)
	local childs = model:GetChildren()
	-- remove the body parts that still hurt you after the npc dies
	for i = 1, #childs do 
		if childs[i]:IsA("Part") and childs[i].Name ~= "HumanoidRootPart" then
			childs[i]:Destroy()
		end
	end
	mod.respawn_after_auto_time("debug", script.Parent.Parent)
	-- delete the model
	humanoid.Parent:Destroy()
end)

function go_to_player(player)
	local p_HRT = player.Character.HumanoidRootPart
	humanoid:MoveTo(p_HRT.Position)
	p_HRT.Touched:connect(function(part)
		local stop_attacking = false
		local should_stop_attacking = false
		while not stop_attacking do
--			print("about to GetTouchingParts ")
			local parts = part:GetTouchingParts()
			
			local size2 = 0
			for _ in pairs(parts) do size2 = size2 + 1 end
			
--			print("got" .. size2 .. "touching parts")
			should_stop_attacking = true
--			print("got after should stop attacking")
			for i = 1, #parts do
--				print("i: "..i)
				local hum = parts[i].Parent:FindFirstChildWhichIsA("Humanoid")
				if hum then
					should_stop_attacking = false
--					print("damage check")
					if canDoDamge then
						player.Character.Humanoid.Health -= damage_per_hit
						canDoDamge = false
						wait(damage_wait_time)
						canDoDamge = true
					end
				end
			end
--			print("got to if")
			if should_stop_attacking then
--				print("inside if")
				stop_attacking = true
--				print("after stop attacking")
			end
		end
	end)
end
while true do
	
	local NPC_POS = script.Parent.HumanoidRootPart.CFrame.Position
	for _, player in pairs(game.Players:GetPlayers()) do
		
		p_dist = player:DistanceFromCharacter(NPC_POS)
		
		if p_dist < trigger_dist then
			local status, err = pcall(go_to_player, player)
			
			if not status then
				print("error, player probably just joined. "..err)
			end
		end
	end
	
	repeat
		for _, player in pairs(game.Players:GetPlayers()) do
			p_dist = player:DistanceFromCharacter(NPC_POS)	
		end
		if p_dist == nil then
			p_dist = see_dist+1
		end
		wait()
	until p_dist < see_dist

end

video:

It’s the type of thing you might get if you don’t have a wait() inside of a while true loop but you have a wait so it’s strange to me.

Your problem is in your go_to_player function. If it’s touching a part that has a humanoid in its parent, then it won’t exit the loop, but it also doesn’t yield/wait any time either, meaning that it just immediately checks over and over again if it’s touching the same part that it’s inside of.

You can fix that, by yielding at the same scope as you set “should_stop_attacking = false”. In the line after that if-statement ends, add in a wait, and set the time to be whatever you want the attack speed of the NPC to be.

If I had to guess, it might not be throwing the warning when it freezes, because it’s inside of a pcall with a connection- but who knows, really. That warning is only meant to be a nicety by Roblox, to help people out a bit whenever it could.

As far as code-quality though, making a new connection inside of a loop is not going to end well for you. What’s going to end up happening, is you’ll have thousands of connections being made, and it’s going to start lagging your game at some point. Try and develop a system that avoids a mixture of loops and connections like you have.