StarterPlayerScript local script running inconsistently

what am i trying to accomplish by posting this?
attempting to fix this bug (most likely a loading problem) and help others fix it

whats the problem?
in order to understand the issue thoroughly im going to explain some back ground information.
im attempting to follow a framework (idk if im using this term correctly) in which i have 1 local script on the client that requires a ton of module scripts, and 1 script on the server that requires a ton of module scripts.

the problem is in the local script which is a child to StarterPlayerScript.
half the time it will run properly, and the other half of the time it just stops before the main loop loops through the modules and requires them individually. i’ve determined that it is most likely a loading problem.
here is the code, and below the code will be a video showcasing the logic error

local modulesFolder = script.Parent:WaitForChild("Modules")

print("client 1")

-- THIS DOESN'T SEEM TO FIX ANYTHING
modulesFolder.ChildAdded:Connect(function(child)
	if child:IsA("ModuleScript") then
		require(child).Start()
		print(child.Name)
	end
end)

print("client 2")
-- OFTEN TIMES HALTS HERE
for i, v in pairs(modulesFolder:GetChildren()) do
	
	if v:IsA("ModuleScript") then
		require(v).Start()
		print(v.Name)
	end
	
	task.wait()
end

print("client 3")

half the time, the code will stop at “client 2”, the other half the time the code will run completely.
video showcasing the problem: Watch loading problem | Streamable
notice that the second run will work as intended, and the first one halts at “client 2”

solutions i’ve tried
i’ve attempted to :WaitForChild() as seen in script.
i’ve attempted to repeat task.wait until v (don’t entirely know if this works)
i’ve attempted to task.wait(4) at the start of the script, still somehow doesn’t fix it
i’ve attempted to if not game:IsLoaded then game:IsLoaded.Wait() end
i’ve attempted to add a .ChildAdded function in hopes that the modules will load after the loop is ran

thank you for your time, any thoughts would be appreciated.

1 Like

It appears to stop at the “Data Replicator Handler” ModuleScript. Try searching in that module for the problematic line(s) of code. Should you struggle to find a solution, would you be fine with pasting that module’s contents so we can try solving it?

2 Likes

i don’t think data replicator handler is the issue, however you did help me realize that the status handler is the issue instead (plot twist)
realized this because i moved the print(v.Name) before the require(mod).Start()
and every time it tried to load the character status first it couldn’t load the data handler


vs

here is the client status script instead, which is the script its stopping at
it’s responsibilities are to add functionality to the health bars and eventually handle the slow system


local statusMod = {}
--// SERVICES
local playerService = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")

if not playerService.LocalPlayer.Character then
	playerService.LocalPlayer.CharacterAdded:Wait()
end
--// VARIABLES
local localPlayer: Player = playerService.LocalPlayer
local char: Model = localPlayer.Character
local humanoid: Humanoid = char:WaitForChild("Humanoid")
local playerGui: PlayerGui = localPlayer:WaitForChild("PlayerGui")

local statusScreenGUI = playerGui:WaitForChild("Status")
local healthBarGUI = statusScreenGUI:WaitForChild("Health")
local manaBarGUI = statusScreenGUI:WaitForChild("Mana")
local staminaBarGUI = statusScreenGUI:WaitForChild("Stamina")

local healthBar = healthBarGUI:WaitForChild("Bar")
local manaBar = manaBarGUI:WaitForChild("Bar")
local staminaBar = staminaBarGUI:WaitForChild("Bar")

local dataRepMod = require(script.Parent:WaitForChild("Data Replicator Handler"))
repeat task.wait() until dataRepMod.getReplica()
local replicaData = dataRepMod.getReplica().Data
local clientDataBindable = ReplicatedStorage.Bindables.ClientData

local statusFolder = char:WaitForChild("Status")

local health = humanoid.Health
local manaInstance: NumberValue = statusFolder:WaitForChild("Mana")
local staminaInstance: NumberValue = statusFolder:WaitForChild("Stamina")

local maxHealth: number = humanoid.MaxHealth
local maxMana: number = manaInstance:GetAttribute("MaxMana")
local maxStamina: number = staminaInstance:GetAttribute("MaxStamina")

--// PRIVATE FUNCTIONS
local function refreshVariablesOnDeath()
	localPlayer = playerService.LocalPlayer
	char = localPlayer.Character
	humanoid = char:WaitForChild("Humanoid")
	playerGui = localPlayer:WaitForChild("PlayerGui")
	
	statusScreenGUI = playerGui:WaitForChild("Status")
	healthBarGUI = statusScreenGUI:WaitForChild("Health")
	manaBarGUI = statusScreenGUI:WaitForChild("Mana")
	staminaBarGUI = statusScreenGUI:WaitForChild("Stamina")
	
	healthBar = healthBarGUI:WaitForChild("Bar")
	manaBar = manaBarGUI:WaitForChild("Bar")
	staminaBar = staminaBarGUI:WaitForChild("Bar")
	
	statusFolder = char:WaitForChild("Status")
	
	health = humanoid.Health
	manaInstance = statusFolder:WaitForChild("Mana")
	staminaInstance = statusFolder:WaitForChild("Stamina")
	
	maxHealth = humanoid.MaxHealth
	maxMana = manaInstance:GetAttribute("MaxMana")
	maxStamina = staminaInstance:GetAttribute("MaxStamina")
end

local function refreshStatusBar(bar: string?)
	if bar == "Health" then
		healthBar.Size = UDim2.new(math.clamp(humanoid.Health / maxHealth, 0, 1), 0, 1, 0)
		
	elseif bar == "Mana" then
		manaBar.Size = UDim2.new(math.clamp(manaInstance.Value / maxMana, 0, 1), 0, 1, 0)
		
	elseif bar == "Stamina" then
		staminaBar.Size = UDim2.new(math.clamp(staminaInstance.Value / maxStamina, 0, 1), 0, 1, 0)
		
	elseif not bar or bar == "All" then
		healthBar.Size = UDim2.new(math.clamp(humanoid.Health / maxHealth, 0, 1), 0, 1, 0)
		manaBar.Size = UDim2.new(math.clamp(manaInstance.Value / maxMana, 0, 1), 0, 1, 0)
		staminaBar.Size = UDim2.new(math.clamp(staminaInstance.Value / maxStamina, 0, 1), 0, 1, 0)
		
	else
		print(`CLIENT STATUS HANDLER | BAR NOT RECOGNIZED: {bar}`)
	end
end

local function setupStatusBars()
	
	humanoid:GetAttributeChangedSignal("MaxHealth"):Connect(function()
		maxHealth = humanoid:GetAttribute("MaxHealth")
	end)
	
	manaInstance:GetAttributeChangedSignal("MaxMana"):Connect(function()
		maxMana = manaInstance:GetAttribute("MaxMana")
	end)
	
	staminaInstance:GetAttributeChangedSignal("MaxStamina"):Connect(function()
		maxStamina = staminaInstance:GetAttribute("MaxStamina")
	end)
	
	humanoid:GetPropertyChangedSignal("Health"):Connect(function()
		refreshStatusBar("Health")
	end)
	
	manaInstance:GetPropertyChangedSignal("Value"):Connect(function()
		refreshStatusBar("Mana")
	end)
	
	staminaInstance:GetPropertyChangedSignal("Value"):Connect(function()
		refreshStatusBar("Stamina")
	end)
end

local function setupSlowListener()
	-- IGNORE THIS, WIP
end

--// PUBLIC FUNCTIONS
function statusMod.refreshStatusBars()
	refreshStatusBar()
end

function statusMod.Start()
	
	playerService.LocalPlayer.CharacterAdded:Connect(function(character)
		refreshVariablesOnDeath()
		
		setupStatusBars()
		setupSlowListener()
		refreshStatusBar()
	end)
	
	setupStatusBars()
	setupSlowListener()
	refreshStatusBar()
end

return statusMod

i can’t lie this code is kinda bad
im trying to adhere to that framework, so i have to manually refresh the gui variables and the character variables every time the character dies, where as if i were to put it into startergui or startercharacter it would just run the script everytime it dies automatically doing it (this is besides the point)

I’m theorising that when the “Client Status Handler” is run earlier than DRH, it requires that module, but that DRH’s Start() function is not yet called.

Since you’re waiting for a “replica” to be valid (the line repeat task.wait() until dataRepMod.getReplica()), your script hangs since DRH is not initialised (its Start() function is not called.)

Either you use task.spawn() for your LocalScript:

for i, v in pairs(modulesFolder:GetChildren()) do

	if v:IsA("ModuleScript") then
		task.spawn(function()
			require(v).Start()
		end)
		print(v.Name)
	end

	task.wait()
end

which will load the modules without waiting for them to finish their initialisation

or use a non-keyed table (Array) and change the order of ModuleScript loading:

local arrangedModules = {
	modulesFolder["Data Replicator Handler"],
	modulesFolder["Client Status Handler"],
}

for i, v in pairs(arrangedModules) do
	require(v).Start()
	print(v.Name)
end

which will guarantee the proper ordering of your scripts, but you will have to manually add new modules in the table.

1 Like

you’re awesome thank you for your time


i wrapped the require in a task.spawn and it fixed the problem entirely

1 Like

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