BindableFunction
An object that allows you to give access to functions to external scripts. Functions put in BindableFunctions will not be replicated, therefore making it impossible to use these objects to pass functions between scripts. Functions are invoked through BindableFunction:Invoke(), which calls BindableFunction.OnInvoke.
BindableFunctions do not allow for communication between the server and client. If you are looking for this functionality use RemoteFunctions.
BindableEvents vs BindableFunctions
Unlike BindableFunctions, BindableEvents only allow for one-way communication between two scripts:
- When a script invokes a BindableFunction it yields until the event is handled and returned by the subscribed script synchronously. This allows for scripts to effectively pass data during and after an event.
- When a script fires a BindableEvent, however, it does not yield for a return. The script continues executing as the event is handled by the subscribed script asynchronously.
BindableFunctions vs RemoteFunctions
While BindableFunctions allow for two-way communication server-server scripts or client-client LocalScripts, RemoteFunctions allow for two-way communicate the server and client. For more information on RemoteFunctions, see Remote Events and Functions.
Limitations
Invocations will yield until the corresponding callback is found. If the callback was never set, the script that invokes it will not resume execution.
Subscription
Only one function can be bound to BindableFunction:Invoke() at a time. If you assign multiple functions, only the last one assigned will be used.
Parameter Limitations
Any type of Roblox object such as an Enum, Instance, or userdata can be passed as a parameter when a RemoteEvent is fired or a RemoteFunction invoked. Lua types such as numbers, strings, and booleans can also be passed, although there are some limitations on how data can be passed.
Table Identity
Copies of tables are produced when passed as arguments to or returned from the OnInvoke callback. This means that means that tables passed as arguments will not be exactly equivalent to those provided on invocation, and tables returned to the invoker will not be exactly equivalent to the ones returned by the OnInvoke callback. You can demonstrate this by running the following script in a BindableFunction:
local bindableFunction = script.Parent
bindableFunction.OnInvoke = function (t)
print("OnInvoke", tostring(t), t)
t = {bar = "foo"}
print("OnInvoke2", tostring(t), t)
return t
end
local t = {foo = "bar"}
print("Subscriber", tostring(t), t)
local retVal = script.Parent:Invoke(t)
print("return", tostring(retVal), retVal)
The above code may produce the following results in the output. Notice how the memory addresses of every table printed are completely different from each other, indicating they each represent different tables:
13:55:22.228 Subscriber table: 0xc7daaba4d5307f10 ▶ {...} - Publisher:1113:55:22.229 OnInvoke table: 0x2ee92a7818e7d210 ▶ {...} - Publisher:413:55:22.229 OnInvoke2 table: 0xfa4ee529ddadf290 ▶ {...} - Publisher:613:55:22.229 return table: 0x35b7bc1bc323d510 ▶ {...} - Publisher:13
Mixed Tables
Avoid passing a mixed table (some values indexed by number and others by key), as only the data indexed by number will be passed. For example, when the server receives the colorData table illustrated below, it only sees indices 1 and 2 containing "Blue" and "Yellow" while the other data is lost in the transfer. Note, however, that sub-tables do not need to be indexed in the same way as their parent — in other words, as long as each individual sub-table is indexed with the same type, all of the data is preserved.
Metatables are not preserved.
Non-String Indices
If any indices of a passed table are non-string type (Instance, userdata, function, another table, etc.), those indices will be converted to a string.
-- Mixed tablelocal colorData = {}colorData[1] = "Blue"colorData[2] = "Yellow"colorData["Color1"] = "Green"colorData["Color2"] = "Red"-- Table with two key-indexed sub-tableslocal playerData = {}playerData["CharData"] = {-- All children indexed by keyCharName = "Diva Dragonslayer",CharClass = "Knight"}playerData["Inventory"] = {-- All children numerically indexed"Sword","Bow","Rope"}
Functions
Functions passed as parameters will not be replicated, therefore making it impossible to use these objects to pass functions between scripts.
Code Samples
local bindableFunction = script.Parent
-- Define a function for use with the BindableFunction
local function AddTwoNumbers(a, b)
return a + b
end
-- Set the OnInvoke callback to be our function
bindableFunction.OnInvoke = AddTwoNumbers
-- Invoke the BindableFunction
print(bindableFunction:Invoke(25, 44))
local bindableFunction = script.Parent
-- Dummy function
bindableFunction.OnInvoke = function(...)
return ...
end
-- These values CAN be sent to/from BindableFunctions
bindableFunction:Invoke() -- nil
bindableFunction:Invoke(25) -- numbers
bindableFunction:Invoke("hello") -- strings
bindableFunction:Invoke(true) -- booleans
bindableFunction:Invoke("buy", 25) -- multiple values are OK
bindableFunction:Invoke({ 1, 2, 3 }) -- tables as arrays with no gaps
-- note the curly braces
bindableFunction:Invoke({ -- tables with string keys only
hello = "world",
goodbye = "world",
})
bindableFunction:Invoke({ -- tables with string keys
point = { 1, 2 }, -- whose values are also valid
point2 = { 3, 4 },
})
bindableFunction:Invoke({ -- tables as arrays
{ 1, 2, 3 }, -- whose values also are valid
{ hello = "world" },
})
-- These are some values you CANNOT send to/from BindableFunctions
bindableFunction:Invoke({ 1, nil, 3 }) -- tables as arrays cannot have nil gaps
bindableFunction:Invoke({
[{}] = "hello", -- table keys can only be numbers OR strings
})
bindableFunction:Invoke({ -- tables keys cannot be BOTH numbers AND strings
[1] = "apple",
hello = "world",
})
Summary
Properties
Methods
Calls the BindableFunction.OnInvoke callback of the BindableFunction, returning any values returned by the callback.
Events
Callbacks
Properties
Methods
Invoke
Calls the BindableFunction.OnInvoke callback and returns any values that were returned by the callback.
Limitations
Invocations will yield until the corresponding callback is found. If the callback was never set, the script that invokes it will not resume execution.
Subscription
Only one function can be bound to BindableFunction:Invoke() at a time. If you assign multiple functions, only the last one assigned will be used.
Parameter Limitations
Any type of Roblox object such as an Enum, Instance, or userdata can be passed as a parameter when a RemoteEvent is fired or a RemoteFunction invoked. Lua types such as numbers, strings, and booleans can also be passed, although there are some limitations on how data can be passed.
Table Identity
Copies of tables are produced when passed as arguments to or returned from the OnInvoke callback. This means that means that tables passed as arguments will not be exactly equivalent to those provided on invocation, and tables returned to the invoker will not be exactly equivalent to the ones returned by the OnInvoke callback. You can demonstrate this by running the following script in a BindableFunction:
local bindableFunction = script.Parent
bindableFunction.OnInvoke = function (t)
print("OnInvoke", tostring(t), t)
t = {bar = "foo"}
print("OnInvoke2", tostring(t), t)
return t
end
local t = {foo = "bar"}
print("Subscriber", tostring(t), t)
local retVal = script.Parent:Invoke(t)
print("return", tostring(retVal), retVal)
The above code may produce the following results in the output. Notice how the memory addresses of every table printed are completely different from each other, indicating they each represent different tables:
13:55:22.228 Subscriber table: 0xc7daaba4d5307f10 ▶ {...} - Publisher:1113:55:22.229 OnInvoke table: 0x2ee92a7818e7d210 ▶ {...} - Publisher:413:55:22.229 OnInvoke2 table: 0xfa4ee529ddadf290 ▶ {...} - Publisher:613:55:22.229 return table: 0x35b7bc1bc323d510 ▶ {...} - Publisher:13
Mixed Tables
Avoid passing a mixed table (some values indexed by number and others by key), as only the data indexed by number will be passed. For example, when the server receives the colorData table illustrated below, it only sees indices 1 and 2 containing "Blue" and "Yellow" while the other data is lost in the transfer. Note, however, that sub-tables do not need to be indexed in the same way as their parent — in other words, as long as each individual sub-table is indexed with the same type, all of the data is preserved.
Metatables are not preserved.
Non-String Indices
If any indices of a passed table are non-string type (Instance, userdata, function, another table, etc.), those indices will be converted to a string.
-- Mixed tablelocal colorData = {}colorData[1] = "Blue"colorData[2] = "Yellow"colorData["Color1"] = "Green"colorData["Color2"] = "Red"-- Table with two key-indexed sub-tableslocal playerData = {}playerData["CharData"] = {-- All children indexed by keyCharName = "Diva Dragonslayer",CharClass = "Knight"}playerData["Inventory"] = {-- All children numerically indexed"Sword","Bow","Rope"}
Functions
Functions passed as parameters will not be replicated, therefore making it impossible to use these objects to pass functions between scripts.
Returns
Values passed to the callback.
Code Samples
local bindableFunction = script.Parent
-- Define a function for use with the BindableFunction
local function AddTwoNumbers(a, b)
return a + b
end
-- Set the OnInvoke callback to be our function
bindableFunction.OnInvoke = AddTwoNumbers
-- Invoke the BindableFunction
print(bindableFunction:Invoke(25, 44))
local bindableFunction = script.Parent
-- Dummy function
bindableFunction.OnInvoke = function(...)
return ...
end
-- These values CAN be sent to/from BindableFunctions
bindableFunction:Invoke() -- nil
bindableFunction:Invoke(25) -- numbers
bindableFunction:Invoke("hello") -- strings
bindableFunction:Invoke(true) -- booleans
bindableFunction:Invoke("buy", 25) -- multiple values are OK
bindableFunction:Invoke({ 1, 2, 3 }) -- tables as arrays with no gaps
-- note the curly braces
bindableFunction:Invoke({ -- tables with string keys only
hello = "world",
goodbye = "world",
})
bindableFunction:Invoke({ -- tables with string keys
point = { 1, 2 }, -- whose values are also valid
point2 = { 3, 4 },
})
bindableFunction:Invoke({ -- tables as arrays
{ 1, 2, 3 }, -- whose values also are valid
{ hello = "world" },
})
-- These are some values you CANNOT send to/from BindableFunctions
bindableFunction:Invoke({ 1, nil, 3 }) -- tables as arrays cannot have nil gaps
bindableFunction:Invoke({
[{}] = "hello", -- table keys can only be numbers OR strings
})
bindableFunction:Invoke({ -- tables keys cannot be BOTH numbers AND strings
[1] = "apple",
hello = "world",
})
Events
Callbacks
OnInvoke
Returns
Code Samples
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
-- TempDataStore (Script 1)
local GetData = Instance.new("BindableFunction")
GetData.Name = "GetData"
GetData.Parent = ServerStorage
local SetData = Instance.new("BindableFunction")
SetData.Name = "SetData"
SetData.Parent = ServerStorage
local PlayerData = {}
local function getData(player)
return PlayerData[player.UserId]
end
local function setData(player, value)
PlayerData[player.UserId] = value
return PlayerData[player.UserId]
end
local function addPlayerData(player)
table.insert(PlayerData, player.UserId, 0)
end
local function removePlayerData(player)
table.remove(PlayerData, player.UserId)
end
GetData.OnInvoke = getData
SetData.OnInvoke = setData
Players.PlayerAdded:Connect(addPlayerData)
Players.PlayerRemoving:Connect(removePlayerData)
-- ActivateButton (Script 2)
local ClickDetector = Instance.new("ClickDetector")
ClickDetector.Parent = script.Parent
local function getDataClickDetector(player)
print(player.Name .. "'s Current Balance: " .. GetData:Invoke(player))
end
local function setDataClickDetector(player, value)
value = value or 100
print("Set " .. player.Name .. "'s Balance to: " .. SetData:Invoke(value))
end
local function incrementDataClickDetector(player)
local balance = GetData:Invoke(player)
print("Set " .. player.Name .. "'s Balance to: " .. SetData:Invoke(player, balance + 1))
end
ClickDetector.MouseClick:Connect(incrementDataClickDetector)
ClickDetector.RightMouseClick:Connect(setDataClickDetector)
ClickDetector.MouseHoverEnter:Connect(getDataClickDetector)
local bindableFunction = script.Parent
-- Define a function for use with the BindableFunction
local function AddTwoNumbers(a, b)
return a + b
end
-- Set the OnInvoke callback to be our function
bindableFunction.OnInvoke = AddTwoNumbers
-- Invoke the BindableFunction
print(bindableFunction:Invoke(25, 44))
local bindableFunction = script.Parent
-- Dummy function
bindableFunction.OnInvoke = function(...)
return ...
end
-- These values CAN be sent to/from BindableFunctions
bindableFunction:Invoke() -- nil
bindableFunction:Invoke(25) -- numbers
bindableFunction:Invoke("hello") -- strings
bindableFunction:Invoke(true) -- booleans
bindableFunction:Invoke("buy", 25) -- multiple values are OK
bindableFunction:Invoke({ 1, 2, 3 }) -- tables as arrays with no gaps
-- note the curly braces
bindableFunction:Invoke({ -- tables with string keys only
hello = "world",
goodbye = "world",
})
bindableFunction:Invoke({ -- tables with string keys
point = { 1, 2 }, -- whose values are also valid
point2 = { 3, 4 },
})
bindableFunction:Invoke({ -- tables as arrays
{ 1, 2, 3 }, -- whose values also are valid
{ hello = "world" },
})
-- These are some values you CANNOT send to/from BindableFunctions
bindableFunction:Invoke({ 1, nil, 3 }) -- tables as arrays cannot have nil gaps
bindableFunction:Invoke({
[{}] = "hello", -- table keys can only be numbers OR strings
})
bindableFunction:Invoke({ -- tables keys cannot be BOTH numbers AND strings
[1] = "apple",
hello = "world",
})