Quick question. I was reading an article on the Roblox Developer website on DataStores (can be found here) and in the script example, they spawned a local function as well as connected local functions to player events.
This confuses me as I thought that module scripts only execute via the require() function. Do functions that are not included in the module’s table execute without the require() function? Or are my initial thoughts correct?
Sorry, if I make no sense.
Code (for people who don’t want to go to the article):
-- Set up table to return to any script that requires this module script
local PlayerStatManager = {}
local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")
-- Table to hold player information for the current session
local sessionData = {}
local AUTOSAVE_INTERVAL = 60
-- Function that other scripts can call to change a player's stats
function PlayerStatManager:ChangeStat(player, statName, value)
assert(typeof(sessionData[playerUserId][statName]) == typeof(value), "ChangeStat error: types do not match")
local playerUserId = "Player_" .. player.UserId
if typeof(sessionData[playerUserId][statName]) == "number" then
sessionData[playerUserId][statName] = sessionData[playerUserId][statName] + value
else
sessionData[playerUserId][statName] = value
end
end
-- Function to add player to the 'sessionData' table
local function setupPlayerData(player)
local playerUserId = "Player_" .. player.UserId
local success, data = pcall(function()
return playerData:GetAsync(playerUserId)
end)
if success then
if data then
-- Data exists for this player
sessionData[playerUserId] = data
else
-- Data store is working, but no current data for this player
sessionData[playerUserId] = {Money=0, Experience=0}
end
else
warn("Cannot access data store for player!")
end
end
-- Function to save player's data
local function savePlayerData(playerUserId)
if sessionData[playerUserId] then
local success, err = pcall(function()
playerData:SetAsync(playerUserId, sessionData[playerUserId])
end)
if not success then
warn("Cannot save data for player!")
end
end
end
-- Function to save player data on exit
local function saveOnExit(player)
local playerUserId = "Player_" .. player.UserId
savePlayerData(playerUserId)
end
-- Function to periodically save player data
local function autoSave()
while wait(AUTOSAVE_INTERVAL) do
for playerUserId, data in pairs(sessionData) do
savePlayerData(playerUserId)
end
end
end
-- Start running 'autoSave()' function in the background
spawn(autoSave)
-- Connect 'setupPlayerData()' function to 'PlayerAdded' event
game.Players.PlayerAdded:Connect(setupPlayerData)
-- Connect 'saveOnExit()' function to 'PlayerRemoving' event
game.Players.PlayerRemoving:Connect(saveOnExit)
return PlayerStatManager
You’re correct - none of this will run without first requiring the module. I’m assuming this script would run with the assumption that another script requires it first at game startup even though that wasn’t mentioned
When require() is called on a ModuleScript, you are basically running the code inside from beginning to end like any old script. This means you can also spawn functions, create loops, define local functions, and do other things, just like any other script. You don’t only have to return tables of functions/values and things of this style from a ModuleScript.
In fact, you can think of ModuleScripts as a giant function encapsulated in its own Instance, with the only difference being that it has to return one value.
And, to answer your question, I believe a function wrapped in a spawn would be created every time you require the module, like you said. It would be like calling the same function over and over.
Correction: I just tested it, and this is not true. If you require a ModuleScript multiple times, it will only run everything in the script once, and never again. Sorry about that…
CorrectionCorrection: I tested further, and if you require a ModuleScript multiple times, it will send its designated return value, but only in its cached form from memory.
e.g., This in a Script:
while wait(1) do
require(path.to.ModuleScript)
print("required")
end
To just summarise this:
Code in modules runs only the first time it is required (unless it is cloned, and that is required).
Any further require()s of it will return the current table contents of the module.
All code inside module scripts run only once.
That means your example would be invalid because it would first calculate the result and then return it any time you require the module script. Actual result:
-- script
local result = require(path.to.ModuleScript)
print(result) --> same result any time you require it again!
-- module
return math.random()
If you wanted it to work like this, you would have to call the function from outside the module:
-- script
local result = require(path.to.ModuleScript)() -- note the extra ()'s
print(result) --> its good now!
-- module
return function()
return math.random()
end
The way most people use modules is actually to return a table or a function and do stuff in the script which required it. Modules are nothing more than storage which executes all code when required the first time.
Hopefully, this cleared some misunderstandings about modules and didn’t confuse you further.