BindableEvent does not fire

Hello! the title says it, my bindableevent doesn’t fire, which makes my script yield indefinitely, and it is very frustrating! I’ve been searching for a solution for days!!
Can someone please help me?

My modulescript that yields:

local module = {
	__loaded	=	false,
	_replicas	=	{},
}

local LoadedSignal = script.LoadedSignal

function module:GetReplicas()
	if (not self.__loaded) then
		print("Waiting for the replicas...") -- This prints
		LoadedSignal.Event:Wait()
		print("Waited!") -- This does not print
	end
	
	return module._replicas
end


return module

The modulescript’s init script:

local ReplicaController = require(game:GetService("ReplicatedStorage").Lib.ReplicaController)

local replicasModule	=	require(script.Parent)
local loadedSignal		=	script.Parent.LoadedSignal

-- Init
ReplicaController.RequestData()

-- Replicas
ReplicaController.ReplicaOfClassCreated("Time", function(replica)
	print("Replica received!")
	replicasModule._replicas[replica.Class] = replica
end)


replicasModule.__loaded = true
loadedSignal:Fire() -- The BindableEvent fires here
print("Loaded!", replicasModule) -- This prints

The script’s tree:
image

thanks for reading yall

From the code I can see, this seems like a pretty weird issue. I would suggest creating another event connection within that module to see if that connection fires. You could also replace the event wait with a repeat until _loaded is true. I understand this probably isn’t ideal, but it’s the best solution I could come up with the information I have.

1 Like

Are you certain :GetReplicas() is being called before the init script runs?
If the function gets called after the init script runs, then the bindable is being fired before the function can even attach its :Wait() to it, causing the indefinite wait.

dont you need to connect the event?

I was thinking this too, but he does say “Waiting for the replicas…” prints so I figured it was another issue. Although it may of just printed after the event fired and they could have overlooked that.

1 Like

Yes, as the scripts prints “Waiting…”, and if :GetReplicas() was called after the init script ran, it just wouldn’t wait and directly return the replicas.

I tried replacing the BindableEvent.Event:Wait() by a repeat, but now I think that the ModuleScript and the Init script might be running under different identities as the ModuleScript’s values are not the same for both scripts.
EDIT: I checked with printidentity(), and they run under the same identity.

New code that repeats:

local module = {
	__loaded	=	false,
	_replicas	=	{},
}

local LoadedSignal = script.LoadedSignal


function module:GetReplicas()
	repeat
		task.wait(1)
		print(module)
	until module.__loaded == true
	--if (not self.__loaded) then
	--	print("Waiting for the replicas...")
	--	LoadedSignal.Event:Wait()
	--	print("Waited!")
	--end
	
	return module._replicas
end


return module

Output:

08:37:34.649   ▼  {
                    ["GetReplicas"] = "function",
                    ["__loaded"] = true, --[[ Here it says that it is loaded ]]--
                    ["_replicas"] = {} --[[ But there are no replicas in the modulescript ]]--
                 }  -  Client - Init:18
  08:37:35.244  Replica received!  -  Client - Init:11
  08:37:35.244  [ReplicaController]: Initial data received  -  Client - ReplicaController:974
  08:37:35.662   ▼  {
                    ["GetReplicas"] = "function",
                    ["__loaded"] = false, --[[ And here it says that it isn't loaded ]]--
                    ["_replicas"] = {} --[[ It's still empty ]]--
                 }  -  Client - Replicas:12

Wouldn’t you use a remote event instead? According to the docs bindable event fires only stay on their respective side of the client/server boundary

This is a really interesting issue that I haven’t come across before. I tried testing this myself with a simpler version of your scripts, and it works perfectly fine:
image
LocalScript:

local module = require(script.ModuleScript)
task.spawn(function()
	module:GetReplicas()
end)
task.wait(1)
module.__loaded = true
print(module)

ModuleScript:

local module = {
	__loaded	=	false,
	_replicas	=	{},
}


function module:GetReplicas()
	repeat
		task.wait(1)
		print(module)
	until module.__loaded == true
	--if (not self.__loaded) then
	--	print("Waiting for the replicas...")
	--	LoadedSignal.Event:Wait()
	--	print("Waited!")
	--end

	return module._replicas
end


return module

Output:
image
Can we get more information on how and where you are calling GetReplicas? It might have something to do with that.

1 Like

I tried doing the repeat method but in the localscript it says that __loaded is true, but in the modulescript it says that it is false .
I call :GetReplicas only in these LocalScripts: (search for the highlighted --TODO in the start of each script)

Time Gui
-- Modules
local gameModule		=	require(game:GetService("ReplicatedStorage").Lib.GameModule)
local Icon				=	require(game:GetService("ReplicatedStorage").Lib.Icon)
local Replicas			=	require(game:GetService("StarterPlayer").StarterPlayerScripts.Replicas):GetReplicas() --TODO-- here!!

-- Functions
local function secondsFormat(seconds): (string)
	return string.format("%02i:%02i", seconds / 60 % 60, seconds % 60)
end

-- Variables
local currentTime
local formattedTime
local currentType

-- Topbar icon
local icon = Icon.new()
	:align("Center")
	:setLabel(secondsFormat(0))
	:lock()
	:deselect()

-- Code
currentTime		=	Replicas.Time.Data.Time
currentType		=	Replicas.Time.Data.Type
icon:setLabel(secondsFormat(currentTime).."\n"..gameModule.TIME_TYPES[currentType])

Replicas.Time:ListenToChange({"Time"}, function(newTime)
	currentTime = newTime
	icon:setLabel(secondsFormat(currentTime).."\n"..gameModule.TIME_TYPES[currentType])
end)

Replicas.Time:ListenToChange({"Type"}, function(newType)
	currentType = newType
	icon:setLabel(secondsFormat(currentTime).."\n"..gameModule.TIME_TYPES[currentType])
	if newType == 4 then -- Disables the reset button when build checking so that the animations doesn't break
		game:GetService("StarterGui"):SetCore("ResetButtonCallback", false)
	elseif newType == 1 then
		game:GetService("StarterGui"):SetCore("ResetButtonCallback", true)
	end
end)
Background music script
local TweenService = game:GetService("TweenService")
local tweenInfo = TweenInfo.new(1.5)

local musicPlaylists = game.ReplicatedStorage.Audio.Music

local currentTrack: Sound	=	nil
local currentType			=	nil

local gameModule	=	require(game:GetService("ReplicatedStorage").Lib.GameModule)
local Replicas		=	require(game:GetService("StarterPlayer").StarterPlayerScripts.Replicas):GetReplicas() --TODO-- hello im here


-- code

currentType = Replicas.Time.Data.Type

Replicas.Time:ListenToChange({"Type"}, function(newType)
	currentType = newType
	pcall(function()
		local tween = TweenService:Create(currentTrack, tweenInfo, {Volume = 0})
		tween:Play()
		tween.Completed:Connect(function(playbackState: Enum.PlaybackState)
			currentTrack:Stop()
		end)
	end)
end)

while task.wait() do -- NOTE: This does NOT run every tick, as it waits for the track to end
	currentTrack =	musicPlaylists[gameModule[currentType]]
						:GetChildren()[math.random(1, #musicPlaylists[gameModule[currentType]]:GetChildren())]

	currentTrack.Volume = 0
	currentTrack:Play();
	local tween = TweenService:Create(currentTrack, tweenInfo, {Volume = 0.5})
	tween:Play()
	currentTrack:GetPropertyChangedSignal("Playing"):Wait() -- Waits for the track to end
end
Building script
-- Services
local TweenService	=	game:GetService("TweenService")

-- Modules
local gameModule		=	require(game:GetService("ReplicatedStorage").Lib.GameModule)
local Replicas			=	require(game:GetService("StarterPlayer").StarterPlayerScripts.Replicas):GetReplicas() --TODO-- hereeee

-- References
local UserInputService				=	game:GetService("UserInputService")
local LocalPlayer					=	game:GetService("Players").LocalPlayer
local CurrentCamera					=	game.Workspace.CurrentCamera
local selectedBlock: ObjectValue	=	script.Parent.selectedBlock
local BuildGui						=	script.Parent.Parent
local Blocks						=	game:GetService("ReplicatedStorage").Blocks
local LocalBlocks					=	Instance.new("Folder")
local ConfirmUis					=	LocalPlayer.PlayerGui:WaitForChild("Temp").ConfirmUis
local TempUis						=	LocalPlayer.PlayerGui:WaitForChild("Temp")
local ConfirmUi						=	game:GetService("ReplicatedStorage"):WaitForChild("Gui").ConfirmUi
local Zones							=	game.Workspace.Platforms.Zones

LocalBlocks.Name	=	"TempLocalBlocks"
LocalBlocks.Parent	=	game.Workspace

-- Remotes & Bindables
local BuildBlockRemote	=	game:GetService("ReplicatedStorage").Remotes.Building.BuildBlock

-- Variables
local deleteMode	=	script.Parent.deleteMode
local currentType

local connections = {
	onConfirmation = {},
	onClick = nil, -- declared at runtime
	onBlockSelected = nil
}

-- CONSTANTS
local LOCAL_BLOCK_TRANSPARENCY = 0.45

-- functions
local function sendBlock(Block: Instance, mousePosition)
	BuildBlockRemote:FireServer(Block, CurrentCamera.CFrame, CurrentCamera:ViewportPointToRay(mousePosition.X, mousePosition.Y))
end

local function onBlockSelect()
	if connections.onClick then
		connections.onClick:Disconnect()
	end
	local Block: Instance? = selectedBlock.Value

	--TODO// fix swipe gestures detecting as taps \\TODO--
	connections.onClick = UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
		if Block and deleteMode.Value == false
			and input.UserInputType == Enum.UserInputType.MouseButton1
			and gameProcessed == false then


			local mousePosition = UserInputService:GetMouseLocation()
			local mouseRay = CurrentCamera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
			sendBlock(Block, mousePosition)


		elseif Block and deleteMode.Value == false

			--TODO !!!!!!!!!!TODO THIS IS MOBILE ONLYYYYY RRAAAAAAAAA TODO!!!!!!!!!!!! TODO

			and input.UserInputType == Enum.UserInputType.Touch
			and gameProcessed == false
			and #LocalBlocks:GetChildren() == 0 then


			local mousePosition	=	UserInputService:GetMouseLocation()
			local mouseRay		=	CurrentCamera:ViewportPointToRay(mousePosition.X, mousePosition.Y)

			local raycastParams							=	RaycastParams.new()
			raycastParams.FilterType					=	Enum.RaycastFilterType.Exclude
			raycastParams.FilterDescendantsInstances	=	{LocalPlayer.Character}
			local mouseRaycast							=	game.Workspace:Raycast(mouseRay.Origin, mouseRay.Direction * gameModule.REACH, raycastParams)

			local PlayerZone
			for count, CurrentZone in Zones:GetChildren() do
				if CurrentZone:GetAttribute("userId") == LocalPlayer.UserId then
					PlayerZone = CurrentZone
				end
			end

			if mouseRaycast and gameModule.isPointInPart(
				gameModule.snapToGrid(mouseRaycast.Position, mouseRaycast.Normal),
				PlayerZone)
			then

				local NewConfirmUi		=	ConfirmUi:Clone()
				local LocalBlock		=	Block:Clone()
				NewConfirmUi.Adornee	=	LocalBlock
				NewConfirmUi.Parent		=	LocalPlayer.PlayerGui
				LocalBlock.CanTouch		=	false
				LocalBlock.CanQuery		=	false
				LocalBlock.CanCollide	=	false
				LocalBlock.Position		=	gameModule.snapToGrid(mouseRaycast.Position, mouseRaycast.Normal)
				LocalBlock.Transparency	=	0.45
				for _, Texture in LocalBlock:GetChildren() do -- makes all of the textures of the block transparent
					if Texture.ClassName == "Texture" then
						Texture.Transparency = LOCAL_BLOCK_TRANSPARENCY
					end
				end

				LocalBlock.Size = Vector3.new(0, 0, 0)
				LocalBlock.Parent = LocalBlocks

				local AppearTween = TweenService:Create(
					LocalBlock, 
					TweenInfo.new(
						0.5, 
						Enum.EasingStyle.Exponential,
						Enum.EasingDirection.Out), 
					{
						Size = Vector3.new(2.75, 2.75, 2.75),
					})
				AppearTween:Play()
				AppearTween.Completed:Connect(function()
					LocalBlock.Orientation = Vector3.new(0, 0, 0)
					LocalBlock.Size = Vector3.new(2.75, 2.75, 2.75)

				end)



				NewConfirmUi.Frame.Yes.Activated:Once(function()
					sendBlock(Block, mousePosition)
					LocalBlock:Destroy()
					NewConfirmUi:Destroy()
				end)
				NewConfirmUi.Frame.No.Activated:Once(function()
					LocalBlock:Destroy()
					NewConfirmUi:Destroy()
				end)

				NewConfirmUi.Enabled = true
			end
		end
	end)
end

local function activateBuilding()
	BuildGui.Enabled = true
	connections.onBlockSelected = selectedBlock:GetPropertyChangedSignal("Value"):Connect(onBlockSelect)
end

local function deactivateBuilding()
	BuildGui.Enabled = false
	if connections.onBlockSelected then
		connections.onBlockSelected:Disconnect()
	end

	for _, Block in LocalBlocks:GetChildren() do
		Block:Destroy()
	end
	for _, Gui: UIBase in ConfirmUis:GetChildren() do
		Gui:Destroy()
	end
end

-- Replicas
currentType	=	Replicas.Time.Data.Type

Replicas.Time:ListenToChange({"Type"}, function(newType)
	-- Code
	currentType = newType
	if currentType == 3 then
		activateBuilding()
	elseif currentType == 4 then
		deactivateBuilding()
	end
end)

Ah I found it! You are right about the module belonging to two different identities. This is because you are requiring the module through StarterPlayerScripts which is where the server copies the scripts from into the player. The actual location of this script is in game.Players.LocalPlayer.PlayerScripts (at least the one you are setting the .__loaded in). The check for .__loaded should be correct if you call the function from this module script instead of the one in StarterPlayerScripts.

3 Likes

It seems that your BindableEvent is fired before all of GetReplicas function calls are reached.

That won’t work because it will only change the __loaded of the replicasModule in the Init script. A module script is not global, every time you require it, you run it again on the local/server script that you required it from, and get its returned value.

That’s why when you do .__loaded = true in one script, it won’t change it globally in all scripts that require the module.

I’d recommend replacing the BindableEvent with a BoolValue (or an attribute) and do the following:
When the replicas are loaded, set its value to true, and then in the GetReplicas function only wait if it isn’t set to true yet.

Here’s how it would look in code if you replace the BindableEvent with a BoolValue called “LoadedBoolValue”, or a Boolean attribute.
ModuleScript:

local module = {
	_replicas	=	{},
}

local Loaded = script.LoadedBoolValue

function module:GetReplicas()
	if (not self.__loaded) then
		print("Waiting for the replicas...") -- This prints
		if(not Loaded.Value) then --If it's false (not loaded)
			Loaded:Get property changed signal("Value"):Wait() --Wait for the value to change 
		end
                
		print("Waited!") -- This should print when the Loaded value is true
	end
	
	return module._replicas
end


return module

Init local script:

local ReplicaController = require(game:GetService("ReplicatedStorage").Lib.ReplicaController)

local replicasModule	=	require(script.Parent)
local loadedValue		=	script.Parent.LoadedBoolValue

-- Init
ReplicaController.RequestData()

-- Replicas
ReplicaController.ReplicaOfClassCreated("Time", function(replica)
	print("Replica received!")
	replicasModule._replicas[replica.Class] = replica
end)


loadedValue.Value = true -- Update the global BoolValue to let the other scripts know they can stop waiting/don't have to wait.
print("Loaded!", replicasModule) -- This prints

This is called polling, and it’s generally not a good approach.

Thank you!!!
I thought of this but didn’t check properly :skull:

1 Like

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