[SOLVED] BindableFunction Error

Hello Developers,

Warning: I am new to scripting, so the answer is probably very simple. Sorry for time wasted if that’s the case!

I decided to use this music system to add music to my game and to learn a bit about scripting. For some reason, I cannot get it to work, here are the errors in my console:

I haven’t changed anything in the original music system, so I am not sure what’s happening. I’ve never used a Bindable function before, so I don’t know what this means. I was able to fix a different error by making some changes, but this error is still an issue.

If you need some more information, feel free to contact me.

Thanks! :slight_smile:

Could you provide snippets of code from the areas that are throwing those errors? It would be a lot easier for programmers on here to understand the context of what’s going wrong in your program. From an educated guess, could I suggest that:

  • The first error looks to me a lot like a syntax error, whereby you’re missing the end of a clause or something
  • The third one is simply that the object wasn’t found, are you sure the path you’re looking at is correct / are you sure the object is actually there?

Hope this helps,
-Tom :slight_smile:

Alright, here you go, sorry for the inconvenience! By the way, the only error stemming from the music system is the error about OnInvoke. Here is the code:

    local music = game.SoundService.Music -- The path to the sound object you created in the SoundService. If that doesn't work, try replacing Soundscape with SoundService.
local songs = game.ServerStorage.Songs:GetChildren() -- The :GetChildren() method returns a lua table containing the objects inside the instance we used it on. In this case, that's the Songs folder we created in the ServerStorage.
local queue = {} -- Here, we will be putting all the songs in the songs folder and play them all in order. We will also "shuffle" the queue.

script.addSongs.OnInvoke:Connect(function()
	for _,song in pairs(songs) do -- This for loop will iterate over every object in our songs folder.
		if song:IsA("IntValue") then -- We check if the song is an IntValue to make sure it's a song.
			table.insert(queue, {
				["songName"] = song.Name, -- The name of the song. As we mentioned, it is the name of the IntValue.
				["songID"] = "rbxassetid://"..tostring(song.Value) -- The ID of the song. As we mentioned, it is the value of the IntValue. We add rbxassetid:// before it because sound objects have it in that format.
			}) -- This will insert a table inside our queue table containing the information for our song.
		end	
	end
	
	for i = 1, #queue - 1 do
		local r = math.random(i,#queue)
		queue[i], queue[r] = queue[r], queue[i]
	end -- This shuffles the table. This method of shuffling is called the "Fisher-Yates Shuffle". If you wish to find out how it works, go search it up!

    return true
end)

script.playSong.OnInvoke:Connect(function()
	if #queue == 0 then script.addSongs:Invoke() end -- Here, we check if there are no more songs in the queue table. If there aren't, then we add them again.
	
	local songToPlay = queue[1] -- This is the next song in the queue table. Further into this function, we remove that from the table as it's already playing.
	music.SoundId = songToPlay.songID -- We set the sound ID property of the sound object to the songID we defined in the song to play.
	music:Play() -- This plays the song.
	
	table.remove(queue, 1) -- This removes the song from the table as it's already playing.
	
	music.Ended:Wait() -- We wait for the song to finish, and then we play the next one.
	
	local delayBeforeNextSongPlays = 3 -- The amount of time we will wait before the next song plays after the current one is done.
	
	wait(delayBeforeNextSongPlays)
	
	script.playSong:Invoke() -- We run this function again.

    return true
end)

script.addSongs:Invoke() -- Calls the addSongs() function which as we said, adds the songs into the queue table.
script.playSong:Invoke() -- Plays the next song available in the queue. :)

Here is a screenshot of the section in explorer, if that helps:

The issue primarily extends to the use of BindableFunctions.

Essentially bindable functions are used to return a value that will be used. If you want a progressive sequence of events to happen, consider using BindableEvents (https://developer.roblox.com/en-us/api-reference/class/BindableEvent).

In this case, it looks as if you could simply remove the BindableFunctions and call the functions themselves such as:

function addSongs(…)
-- Relevant code
end

addSongs(…) -- call the function defined above.

For more accurate and detailed information about callbacks and functions, see this article: https://developer.roblox.com/en-us/articles/Using-Callbacks-in-Code

Hope this helps,
-Tom :slight_smile:

Could you replace what would be changed, so I can see? I’m a bit confused right now…

Did this also mean replacing the addSongs and playSongs BindableFunctions with BindableEvents?

I’m still a bit confused, could someone please go more into depth on what’s needed?

Hm this came up on my notifications. Basically, I was told that using local functions a ton of times in a script can cause a stack over flow on ROBLOX. However, using Bindable Events/Functions don’t cause that. I decided to use Bindable Functions as Bindable Events do not yield.

What would be a fix? I am so confused right now…

Something I didn’t understand in my music player is that the SoundService seems to play locally. Try changing the position of the sound object to the workspace.

I’ll try that. Not sure if it will fix the problem, but let’s hope.

I have good news and bad news: It fixed the previous error, but now this is happening:

Still no sound.

You need to change the music variable in your script to be game.Workspace.Music

Just did that, now we are back to the first error. Looks like you might have to change it a lot…

Can you show me your current script?

Yes! Here you go:

local music = game.Workspace.Music -- The path to the sound object you created in the SoundService. If that doesn't work, try replacing Soundscape with SoundService.
local songs = game.ServerStorage.Songs:GetChildren() -- The :GetChildren() method returns a lua table containing the objects inside the instance we used it on. In this case, that's the Songs folder we created in the ServerStorage.
local queue = {} -- Here, we will be putting all the songs in the songs folder and play them all in order. We will also "shuffle" the queue.

script.addSongs.OnInvoke:Connect(function()
	for _,song in pairs(songs) do -- This for loop will iterate over every object in our songs folder.
		if song:IsA("IntValue") then -- We check if the song is an IntValue to make sure it's a song.
			table.insert(queue, {
				["songName"] = song.Name, -- The name of the song. As we mentioned, it is the name of the IntValue.
				["songID"] = "rbxassetid://"..tostring(song.Value) -- The ID of the song. As we mentioned, it is the value of the IntValue. We add rbxassetid:// before it because sound objects have it in that format.
			}) -- This will insert a table inside our queue table containing the information for our song.
		end	
	end
	
	for i = 1, #queue - 1 do
		local r = math.random(i,#queue)
		queue[i], queue[r] = queue[r], queue[i]
	end -- This shuffles the table. This method of shuffling is called the "Fisher-Yates Shuffle". If you wish to find out how it works, go search it up!

    return true
end)

script.playSong.OnInvoke:Connect(function()
	if #queue == 0 then script.addSongs:Invoke() end -- Here, we check if there are no more songs in the queue table. If there aren't, then we add them again.
	
	local songToPlay = queue[1] -- This is the next song in the queue table. Further into this function, we remove that from the table as it's already playing.
	music.SoundId = songToPlay.songID -- We set the sound ID property of the sound object to the songID we defined in the song to play.
	music:Play() -- This plays the song.
	
	table.remove(queue, 1) -- This removes the song from the table as it's already playing.
	
	music.Ended:Wait() -- We wait for the song to finish, and then we play the next one.
	
	local delayBeforeNextSongPlays = 3 -- The amount of time we will wait before the next song plays after the current one is done.
	
	wait(delayBeforeNextSongPlays)
	
	script.playSong:Invoke() -- We run this function again.

    return true
end)

script.addSongs:Invoke() -- Calls the addSongs() function which as we said, adds the songs into the queue table.
script.playSong:Invoke() -- Plays the next song available in the queue. :)


local allowedPlayers = {"FxllenCode", "Iostrxses"} -- The people who are allowed to execute the !skip command. If you want, you can also check for a rank instead.

game.Players.PlayerAdded:Connect(function(player) -- The code inside this event listener will happen when a player joins. It passes in the player parameter which is the player who joined.
	player.Chatted:Connect(function(msg) -- The code inside this event listener will happen when the player who joined chats. The msg parameter is the message they chatted.
		if string.lower(msg) == "!skip" then -- We check if the lowercase version of the message they chatted is "!skip". string.lower(string) returns the lowercase version of the string.
			if table.find(allowedPlayers, player.Name) then -- We check if the player's name exists in the allowedPlayers table we created earlier.
				-- IF YOU WANT TO CHECK FOR A RANK, you can easily replace table.find(allowedPlayers, player.Name) with player:GetRankInGroup(groupID) which will return the numerical rank of the player in that group instead of checking if their username exists in the table.
				--[[ Example: 
					if player:GetRankInGroup(000000) > 5 then
						nice code
					end
				--]]
				
				
				table.remove(queue, 1) -- This will remove the currently playing song from the queue table to be able to play the next one.
				script.playSong:Invoke() -- We then simply fire the playSong() function as we already know that handles everything.
			end
		end
	end)
end)

Try changing the BindableFunctions addSongs and playSong to BindableEvents (you can use Reclass or a similar plugin to do it quickly).
EDIT: It should look like this:
image

As I mentioned, BindableEvents don’t yield when they get called. However, Bindable Functions do yield and wait for you to return a value.

Hm, now that I see, it would be impossible to use Bindable Functions due to its returning method. As @AwesomePossum212 mentioned, you should try using Bindable Events instead. Replace any OnInvoke() to Event and any :Invoke() to :Fire()

But in this case (correct me if I’m wrong), the Bindable doesn’t need to yield? It just needs to be fired.