5.10 ModuleScripts

Maybe you have noticed that some of the scripts we have encountered in the last few sections have been getting a bit long.

Even with our alchemy merchant. What happens if we give him an expanded inventory to sell. Or create additional functionality such as the ability to sell back to the merchant.

Or if there are multiple merchants?

Even limiting access to variables in a table, rather quickly our Script turns into a cluttered mess.

Also, the functions that we create in a Script or LocalScript are limited only to that script. Which means we cannot reuse those functions elsewhere.

Because we’ve been doing a lot of abstraction, we can take it one step further and abstract code into their own script.


A ModuleScript is little more than a table.

Try it for yourself. Insert a ModuleScript into your game and view its contents.

Lua
local module = {}

return module

At it’s most basic, the ModuleScript just defines a table and then returns that table.

Technically, it doesn’t even need to return a table. Just some value. But because of how fundamental tables are in Lua we often use that table as a building block to more complex behavior.

Unlike Scripts and LocalScripts, ModuleScripts cannot execute on their own, even when placed in containers that are meant to execute scripts such as ServerScriptService.

Instead, Scripts, LocalScripts, and even other ModuleScripts load a ModuleScript by “requiring” it. This then makes the returned table available in those scripts.

Lua
--/ServerStorage/ModuleScript
for i = 1, 5 do
	print("initializing", i)
end

local module = {}

return module
Lua
--/ServerScriptService/Script
local moduleScript = require(game.ServerStorage.ModuleScript)

--Output: initializing 1
--Output: initializing 2
--Output: initializing 3
--Output: initializing 4
--Output: initializing 5 

task.wait(3)

print("Requiring ModuleScript again")
moduleScript = require(game.ServerStorage.ModuleScript)
--No output

The first time a ModuleScript is required, the code in that specific script will execute once.

Subsequent calls with the require method on that script will not execute but only return the same table. This makes it useful for initializing tasks for the script.

Each ModuleScript unique is per environment. Meaning that for a ModuleScript in ReplicatedStorage, when the server requires it, the server creates its own instance of the script for the server environment.

Then if a client also required that ModuleScript, they would run a different instance in their own client environment. And so does every client that requires the ModuleScript despite the “same” ModuleScript being replicated across all machines.

Because a ModuleScript is created per environment, the same data is shared between all requiring scripts of that environment. If a script updates the ModuleScript data, all other scripts that use that ModuleScript will also see the changes.

Like we learned earlier with closures, ModuleScripts can have its own local data that is hidden in addition to the tabular data that is visible to all requiring scripts.

Lua
--/ServerStorage/ModuleScript
local defaultLength = 10

local function generateList(size)
	local tempList = {}
	for i = 1, size do
		local randomNumber = math.random(-100, 100)
		
		tempList[i] = randomNumber
	end
	return tempList
end

local module = {}
module["list"] = generateList(defaultLength)

function module.regenerateList(size)
	module.list = generateList(size or defaultLength)
end

function module.getListSum()
	local sum = 0
	
	for i = 1, #module.list do
		sum = sum + module.list[i]
	end 
	
	return sum
end

return module
Lua
--/ServerScriptService/Script
local module = require(game.ServerStorage.ModuleScript)
print(module.getListSum(), module.list)

module.regenerateList(5)
print(module.getListSum(), module.list)

We make defaultLength and the function generateList local only to the module for performing background tasks. Then getListSum, regenerateList, and the list table acts as the “public” interface other scripts have access to.