Why am I getting "Script Timeout: exhausted allowed execution time"

hello,

im creating a sanity system where if you look at an entity, your sanity decreases.
here’s my code so far but i get “Script Timeout: exhausted allowed execution time”
Please note that I already know how to bypass this error. What I’m more concerned about is specifically why I get this error, and how can I not trigger this error at all?

-- services
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")

local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")

-- UI

local plrgui = player:WaitForChild("PlayerGui")
local sansys = plrgui:WaitForChild("SanitySystem")
local backdrop = sansys:WaitForChild("Backdrop")
local cover = backdrop.Cover


-- sanity variables

local maxsanity = 400
local sanity = maxsanity
local sancost = 2
local sanregen = 2
sanity = math.clamp(sanity, 0, maxsanity)

-- keeping track of the regeneration thread counts
local regenerationThreadCount = 0

-- last time when they see a monster
lastseentime = 0

local function updatesan()
	sanity = math.clamp(sanity, 0, maxsanity)
	cover:TweenSize(
		UDim2.new(0.7, 0, (sanity/maxsanity) * 0.969, 0),
		Enum.EasingDirection.InOut,
		Enum.EasingStyle.Linear,
		0
	)
end


local function seen()
	lastseentime = tick()
	sanity -= sancost
	sancost += 0.05
	updatesan()
end

local function regen()
	task.wait(2) -- Hold for 2 sec
	regenerationThreadCount += 1 -- Increasing thread, cancel previous regeneration function.
	-- This line will break the latest regeneration loop if there is any.

	if regenerationThreadCount == 2 then
		regenerationThreadCount = 1 -- Resetting thread count. Keep it at 1 so the loop below can run.
	end
	-- The top line will make the thread count == 2, to see the picture clearly, I use extra if statement.

	-- Start new loop increasing stamina
	while regenerationThreadCount ~= 2 and sanity < maxsanity do
		sancost = 0
		for i = sanity, maxsanity, sanregen do
			sanity = i
			i += sanregen
			sanity = i
			updatesan()
			task.wait(0.05)
		end
	end
end

RS.RenderStepped:Connect(function()
	coroutine.wrap(function()
		for _, part in game:GetService("Workspace").Monsters:GetChildren() do
			local cam = game:GetService("Workspace").CurrentCamera
			
			local Angle = 0.7

			local unit = ((part.Position - char.Head.Position) * Vector3.new(1,0,1)).Unit
			local HeadLookVec = char.Head.CFrame.LookVector * Vector3.new(1,0,1)
			local IsFacing = HeadLookVec:Dot(unit) > Angle

			if IsFacing then
				sanregen = 0
				sancost = 2
				seen()
			else
				sancost = 0
				sanregen = 2
				if tick() - lastseentime > 2 then
					regen()
				end
			end
			
		end
	end)()
end)

i’ve checked online already but most sources don’t explain that well exactly why i’m getting this error, and how to safely avoid getting it without flat out bypassing it.

The “Script Timeout: exhausted allowed execution time” error occurs when your script is taking too long to run and exceeds the maximum allowed execution time. This error is designed to prevent scripts from running indefinitely and causing performance issues in the game.

In your code, you have a while loop inside the regen() function that continuously increases the player’s sanity until it reaches its maximum value. This loop is likely causing the script to exceed the allowed execution time, which is why you are getting the error.

To avoid this error, you can refactor your code to remove the while loop and use a wait function instead. You can replace the loop with a series of wait functions that will pause the script for a short amount of time before continuing to increase the player’s sanity.

Here’s an example of how you can refactor the regen() function to avoid the error:

local function regen()
    task.wait(2) -- Hold for 2 sec
    regenerationThreadCount += 1 -- Increasing thread, cancel previous regeneration function.
    -- This line will break the latest regeneration loop if there is any.

    if regenerationThreadCount == 2 then
        regenerationThreadCount = 1 -- Resetting thread count. Keep it at 1 so the loop below can run.
    end
    -- The top line will make the thread count == 2, to see the picture clearly, I use extra if statement.

    -- Start new loop increasing stamina
    while regenerationThreadCount ~= 2 and sanity < maxsanity do
        sancost = 0
        for i = sanity, maxsanity, sanregen do
            sanity = i
            i += sanregen
            sanity = i
            updatesan()
            task.wait(0.05)
            if sanity >= maxsanity then -- exit the loop if the player's sanity reaches maximum value
                break
            end
        end
    end
end

This updated version of the regen() function uses a for loop to increment the player’s sanity and includes a task.wait(0.05) call inside the loop to pause the script for a short amount of time before continuing. The loop will exit once the player’s sanity reaches its maximum value, which will prevent the script from running indefinitely and causing the error.

In line where is local char you need to change this to this i think i am not sure:

local char = player.CharacterAdded or player.Character:Wait()

I don’t think that’s the cause of the error since the error is somewhere completely different and the actual code I wrote was:

local char = player.Character or player.CharacterAdded:Wait()

I tried this and it didn’t work. Are you using ChatGPT to generate your response?

He is. I asked him what GPT engine he uses and he told me: get good monkey…

1 Like

I had a feeling. This is CGI_ERIC (@ esadams188)'s clone.

Who’s CGI_ERIC? A Roblox user?

Yeah, he was a devforum member who basically used ChatGPT to grind Solutions and to allegedly “help” people…

2 Likes

I do it too sometimes but I use my knowledge too. Also, I do check if there is a wrong answer on the message, I’m not getting the answer and copy pasting…

I think many people will do that in the future…

1 Like

Why would anyone in their right mind do that

I just did a little research and I’m getting the feeling that he actually is Eric.

Once the thread count passes 1, additional calls to regen will not cancel prior loops because those loops are not running at the exact moment that regenerationThreadCount is incremented to 2; they will only see it once it has been flipped back to 1. Moreover, that condition is only checked once the for ... do loop finishes what it was doing. This causes threads to stack up.

I haven’t unit tested this, so I apologize if it doesn’t do what it ought to, but by only ever incrementing the regenerationThreadCount and aborting the loop if it changes at all, that should prevent threads from stacking up.

local function regen()
	task.wait(2)
	
	regenerationThreadCount += 1
	local currentThreadCount = regenerationThreadCount

	sancost = 0
	for i = sanity, maxsanity, sanregen do
		sanity = i
		i += sanregen
		sanity = i
		updatesan()
		task.wait(0.05)

		if regenerationThreadCount ~= currentThreadCount then
			break
		end
	end
end
2 Likes

No, this line here:

	regenerationThreadCount += 1 -- Increasing thread, cancel previous regeneration function.
	-- This line will break the latest regeneration loop if there is any.

wont break the execution cycle because a fraction of a millisecond later you reset it back to 1. So the previous cycle may or may not end. Either way you are entering this loop continuously. That means the first loop could complete and sanity reaches maxsanity, but this whole loop is instantly entered again and the for loop will stall because insanity is already equal to maxsanity, i.e. the for loop will not run, so the task.wait() wont fire and the outside while loop will run without a timer, i.e. it will bomb!

Cheap fix: put a tiny wait in the main while loop, i.e.:

	while regenerationThreadCount ~= 2 and sanity < maxsanity do
		sancost = 0
		for i = sanity, maxsanity, sanregen do
			sanity = i
			i += sanregen
			sanity = i
			updatesan()
			task.wait(0.05)
		end

		-- small wait here
		wait();
	end

My opinion alone, but logically this method you are using is entirely wrong.

1 Like

Thanks, and sorry for the slow response (I was quite busy!)
I took your advice and adjusted the script as you say but unfortunately now there are two things that don’t work :sad: :confused:

  1. Most of the time, it doesn’t wait for two seconds before starting to regenerate.
  2. The bar itself begins to glitch quite a lot and is quite inconsistent. I believe it could be because of the changes made to sancost and sanregen.

everything else works, and thanks so much for that part. Here’s a video to show you what’s going on, as well as the code.
Code:

-- services
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")

local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")

-- UI

local plrgui = player:WaitForChild("PlayerGui")
local sansys = plrgui:WaitForChild("SanitySystem")
local backdrop = sansys:WaitForChild("Backdrop")
local cover = backdrop.Cover


-- sanity variables

local maxsanity = 400
local sanity = maxsanity
local sancost = 2
local sanregen = 2
sanity = math.clamp(sanity, 0, maxsanity)

-- keeping track of the regeneration thread counts
local regenerationThreadCount = 0

-- last time when they see a monster
lastseentime = 0

local function updatesan()
	sanity = math.clamp(sanity, 0, maxsanity)
	cover:TweenSize(
		UDim2.new(0.7, 0, (sanity/maxsanity) * 0.969, 0),
		Enum.EasingDirection.InOut,
		Enum.EasingStyle.Linear,
		0
	)
end

local isseen = false

local function seen(isFacingMonster)
	isseen = true
	lastseentime = tick()
	sanity -= sancost
	updatesan()
	task.wait(0.05)
end

local function regen()
	task.wait(2)

	regenerationThreadCount += 1
	local currentThreadCount = regenerationThreadCount

	sancost = 0
	for i = sanity, maxsanity, sanregen do
		sanity = i
		i += sanregen
		sanity = i
		updatesan()
		task.wait(0.05)

		if regenerationThreadCount ~= currentThreadCount then
			break
		end
	end
end

RS.RenderStepped:Connect(function()
	coroutine.wrap(function()
		for _, part in game:GetService("Workspace").Monsters:GetChildren() do
			local cam = game:GetService("Workspace").CurrentCamera

			local Angle = 0.7

			local unit = ((part.Position - char.Head.Position) * Vector3.new(1,0,1)).Unit
			local HeadLookVec = char.Head.CFrame.LookVector * Vector3.new(1,0,1)
			local IsFacing = HeadLookVec:Dot(unit) > Angle

			if IsFacing then
				lastseentime = tick()
				sanregen = 0
				sancost = 2
				seen(IsFacing)
			else
				sanregen = 2
				sancost = 0
				regen(IsFacing)
			end
		end
	end)()
end)

Video: untitled horror game framework testing - Roblox Studio 2023-03-10 18-26-58
P.S. @dduck5tar , your cheap fix worked but unfortunately I found MysteriousVagabond’s solution to be more consistent but I’m still very grateful! If I may ask, what is a better way to do this? I’m not particularly strong on the logical side of programming :sweat_smile:

Yeah, it looks like the code sent didn’t really tackle the issue since the regen loop was being overwritten every frame the player was not currently looking at the monster and not being cancelled when the player was looking at a monster. My bad, I should have looked into it a bit more before providing anything.

I’ve sat down with the code and reworked it a bit to avoid having to keep track of multiple threads, which should reduce the sort of “too many cooks” situation going on originally. Here is the adjusted script:

-- services

local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")

local player = game:GetService("Players").LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local head = char:WaitForChild("Head")				-- added reference to "Head"

local monsters = workspace:WaitForChild("Monsters")	-- added reference to "Monsters"

-- UI

local plrgui = player:WaitForChild("PlayerGui")
local sansys = plrgui:WaitForChild("SanitySystem")
local backdrop = sansys:WaitForChild("Backdrop")
local cover = backdrop:WaitForChild("Cover")

-- sanity variables

local maxsanity = 400
local sanity = maxsanity
local sancost = 100			-- adjusted to points per second (originally 2)
local sanregen = 100		-- adjusted to points per second (originally 2)
sanity = math.clamp(sanity, 0, maxsanity)

-- monster visibility variables

local seen_angle = 0.7		-- moved "Angle" up here since it was a constant
local isseen = false
local time_since_last_seen = 0	-- changed from "lastseentime"
local regen_delay = 2			-- the amount of time before regeneration will begin

local function updatesan()
	sanity = math.clamp(sanity, 0, maxsanity);
	cover:TweenSize(
		UDim2.new(0.7, 0, (sanity/maxsanity) * 0.969, 0),
		Enum.EasingDirection.InOut,
		Enum.EasingStyle.Linear,
		0
	)
end

local function seen(delta)
	isseen = true
	time_since_last_seen = 0
	sanity -= sancost * delta
	updatesan()
end

local function regen(delta)
	isseen = false
	if time_since_last_seen < regen_delay then
		-- regen is on delay
		time_since_last_seen += delta
	else
		-- player can regenerate sanity
		sanity += sanregen * delta
		updatesan()
	end
end

RS.RenderStepped:Connect(function(delta)
	local IsFacing = false
	for _, part in monsters:GetChildren() do
		local unit = ((part.Position - head.Position) * Vector3.new(1,0,1)).Unit
		local HeadLookVec = head.CFrame.LookVector * Vector3.new(1,0,1)
		
		if HeadLookVec:Dot(unit) > seen_angle then
			IsFacing = true
			
			-- no need to check additional monsters since it is known that the player is looking at at least one
			break
		end
	end
	
	if IsFacing then
		seen(delta)
	else
		regen(delta)
	end
end)

The adjusted code does not generate any new threads because the called functions have been changed so that they do not yield and instead use the amount of time each frame took to make their calculations. Rather than tracking the last moment the player looked at a monster, the amount of time that player has not looked at a monster is now tracked, and looking at a monster resets it to 0. The player only begins regenerating sanity once the amount of time they have not looked at a monster is at least 2 seconds.

The largest impact of this change was that sancost and sanregen have been changed to represent points-per-second rather than specific point amounts and may need adjusting to meet your original intent.

1 Like

This is perfect! Thank you so much, and I really appreciate you taking time out of your day to look on this, I’m really happy, cause i really suck at stuff like this lmao. Thank you, once more!

1 Like

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