I have a function, basically, that needs to be used several times throughout my game. Server scripts, local scripts, they all have to do this thing. Instead of using a remote event for the local scripts and copy/pasting the function in all my server scripts, is there where I would use the elusive module script? I’m not entirely sure how module scripts are used, so I may be completely wrong here.
If I do happen to be wrong, can someone provide me with a more efficient solution than all the garbage I mentioned earlier? If it’s even possible?
Yes, for functions that are reused throughout your game, you should use ModuleScripts (place them in ReplicatedStorage if you need both the client and server to access it)
A couple questions:
One, just a brief summary of how I use them? I’m assuming it’s just writing a function to a single script, then just calling that “script” rather than a function?
Two, I need everything to happen on the server- server calls it, nothing changes, client calls it, it’s replicated to/done on the server. How can I make this happen, or is that just the default?
The Roblox Creator Documention goes into depth about them:
-- Example
-- (ModuleScript)
local t = {storage = {}}
function t:store(key, value)
-- 'self' refers to the table that the method was called from
-- in this case, self is 't' because you called t:store(...), which is
-- the same as: t.store(t, ...)
self.storage[key] = value -- store the index and value inside the 'storage' table
end
-- this returns the 't' table from the module script, so any script that uses this ModuleScript
-- will be accessing the table that was returned.
-- ModuleScripts must return a value whether it's 'nil' or anything else
return t
---------------------------------------------------
-- (Script)
local module = require(path.to.module)
-- you use the 'require' function to access the ModuleScript's contents
module:store("index", 12)
-- this calls the 'store' method that was defined inside the module script
print(module.storage.index) --> prints 12
You would need to use Remotes in this case since the client and server both retrieve different versions of the ModuleScript for security purposes.
When making a change, fire a RemoteEvent to either the client or server, then have a callback that makes the specified change to the ModuleScript depending on the arguments you send through the remotes
ModuleScripts are the Roblox version of what’s known in wide-spread programming as a “code package [or just package]”. The point is to isolate your namespace and avoid repetitive code (which also in turn preserves memory).
ModuleScripts run one time per Lua environment. This means that if you call a ModuleScript inside ReplicatedStorage from the client and the server, it will run twice; however, if you call it twice from the server it doesn’t start a second time.
Omega is right, you will need a RemoteEvent (or some form of server invoke at least). And also as he said, it is for security reasons, but the implementation of security can be overstated as “running once per Lua computing environment”.
Real quickly I’d like to give you a small tutorial on usage of ModuleScripts, to maybe aid in understanding. Although it is commonplace to use Modules for tables of functions, their only output requirement is “a single return”. This return can be any data type, not just tables.
--// Some ModuleScript
return function Example(Variable) end -- Some placeholder function.
This alone might seem useless (in some code structures you will see this). What you are much more likely to encounter is function hierarchies across multiple ModuleScripts using tables. Here’s an example.
local Module = {}
Module.FloorAdd = function(int1, int2)
return math.floor(int1 + int2)
end
Module.FloorSubtract = function(int1, int2)
return math.floor(int1 - int2)
end
return Module
Notice how we still return a single time. The important part though, is that once our Lua environment (whether it be server or client machine) has loaded this once, we can start using it across that environment [so long as we require the module, per Luau syntax standards].
You could do this, it all just depends what you need it for. You can use os.time() to run checks on which might be more efficient, but ultimately this is going to be a minor problem as passing through a remote event is infinitely slower than reading a function typically will be (reading is almost always faster than writing/transfer). All this to say either solution is fine.
When we say modules run one time per Lua environment we are saying that it is not repeating itself to be called from different scripts on the same machine. The module needs to load in order to run (when you write require() you implicitely requesting the module to load into the environment, and its contents to compile into memory). Until you actually require the module the first time that environment will be unaware what it contains. It’s a more technical memory partition thing, but at its root what you actually need to know is this:
Once a ModuleScript has been required, in a specific computing environment (Client/Server), it will load there.
You can then ACCESS that ModuleScript and its contents in your code through a variable like any other.
the function id need would create and return a part based on parameters- does that mean i couldnt make several parts if it only runs once, or am i still misunderstanding?
A module script runs once and returns a table. If you try to run it again, it instead gives you the table it already made instead of running the code again. That table has functions in it.
--module script
local module = {}
print(“hi”) -- this runs once, because we need to set up the elements in the table
module.createPart = function(pos) --when this runs it makes module.createPart equal that function so that any reference to module.createPart in the future is to that function.
print”make part”)
end
return module --module is the table that all scripts that require it now can access.
--other script
local module = require(script.ModuleScript) --gets the table that is returned
while true do
task.wait()
module.createPart(nil) -- call that function we defined in the table inside the module script.
end
Yes, because the code in the module is what gets run once. In this case it just creates a function. You can run that function as many times as you want, but it’s only ever created once.