Why Module Scripts?

ModuleScripts play the role of modules/headers in just about every other language. The C++ .hpp, C .h, Python .whateveritscalledmodules, etc.
It’s for keeping code organized and keeping each source purposeful.

You can make a game without them, but it won’t be “generally” organized.

2 Likes

Creating functions to be used similarly to modules by using _G will require you to have two different versions of the code, one to be used on the client and one to be used on the server. If you’re using Remote Events/Functions to achieve a mock-module effect, you’ll just be causing latency issues by having so much unneeded client-server communication.

Example:

I have a script on the server, inside of ServerScriptService that has the following code;

_G["Hello"] = function()
	print("Hello world!");
end;

If I try to call _G.Hello() from a normal server script, it will print “Hello world!” just fine (assuming that the function actually exists in the moment that I attempt to call it.)

However, if I try to call _G.Hello() from a client script, it won’t work because I’ve only created the function inside the global server, not the client. That means that you’ll have to be updating two different versions of the same script!

As previously mentioned, sure, you could use a remote to communicate with the server and use some of these functions (if they’re intended to be shared), but that’s just asking for latency issues. And, if you’re executing the code on the server rather than straight up returning the functions to the client, then the server will be unable to to use these functions to change client-sided objects.

With a ModuleScript, you can just plop it into ReplicatedStorage and it will be capable of being used inside of client AND server scripts without having to manage duplicate code.

This is just one (albeit important) reason. Read the posts above mine for various others.

2 Likes

SO if I had a bunch of NPC AIs I could use a Module Script to easily control all of them with one code?

For something like that, I’d use CollectionService and tag the AI’s, then you can manage them all from one script more easily without having to deal with global tables OR modules, although modules would still be nice to implement for certain things in this scenario.

http://wiki.roblox.com/index.php?title=API:Class/CollectionService

After tagging the models, you can place a script inside of ServerScriptService with the code;

local CollectionService = game:GetService("CollectionService");

function hookup(ai)
	-- write code here to be applied to the ai
end;

CollectionService:GetInstanceRemovedSignal("AI"):Connect(function(obj)
	-- one of the AI was removed from the game, run code here if you need to handle that
end);

CollectionService:GetInstanceAddedSignal("AI"):Connect(function(obj)
	-- a new object was added to the game with the 'ai' tag, run code here to handle it
	hookup(obj);
end);


-- get currently existing objects with the AI tag and apply the hookup function to them
for i,v in ipairs(CollectionService:GetTagged("AI")) do 
	hookup(v);
end;

You could even use this method to tag all of your AI’s with a default AI code, and then add more tags to handle different situations, such as “Archer” or “Assassin”.

Edit: I know that I can just do “:Connect(hookup)” but I left it as an open function as an example to show where the code would be added.

6 Likes

This is similar to asking “Why Functions?”.

We use functions to save space, organize our code, etc. I’m going to assume we agree that functions are incredibly useful.

Now lets take those reasons and just expand on them. Instead of having one extremely large code, you could have several scripts.

But what if those several scripts needed the same function? Instead of having the same function in every single script, you can just make a single module script.

Sure, you have global functions instead, but modules are more organized, or at least I find them.

But for remotes, they definitely can’t replace your modules. You might want the module on the client side, would you call a remote event just for it to call the client back?

3 Likes

If you ever work with someone else on code, it’s best to organize your code into modules that serve a specialized function. While it takes more effort, it makes your code much less esoteric.

I’ve made a semi-popular building plugin that used no modules (it was developed before they were even a thing), and it had thousands of lines. It worked perfectly for my purposes, and it took a lot less time to build than it would have if I had used modules (I tried making a module-based plugin once, but quickly lost all motivation, though I’ve always been absorbed in other projects anyways). With long, single scripts, you can make your code a lot more esoteric and ad-hoc. However, whenever I try making edits to that plugin now, I forget everything about its organization, and have to jump around between functions to figure out what sections of code are responsible for what.

That’s fine if you’re working by yourself and don’t really plan on expanding the codebase any further than what it’s already been expanded to, but if you ever want to work with other people (especially since, on team create, only one person can edit a script at a time), modules let other people (such as your future self) figure out what the hell is going on with your code by the mere fact that everything is organized much more robustly.

When you have modules laid out in a folder, you can pinpoint exactly what part of your code you need to make changes to, rather than having to dig through thousands of lines in functions. It’s also a lot less performance taxing, since shared functions can be grouped together in one big thread (which is what you want in your code).

There are some other things. For example, it forces a server/client distinction (whereas if you have two things of the same name in the _G table, one will override the other in Play Solo, which fundamentally breaks solo testing). Also, one cool thing about modules, is that they’re only stored in memory once on a server or a client. Therefore, they can be used similarly to a localized _G table, that can be interfaced by all of the things that need them. You can read and write to a required module, and it will affect all other instances on the same server/client that accesses that module. Play Solo cuts the time it takes to launch a test in half, so using modules instead of _G for things like that helps a lot.

3 Likes

Somebody brought up _G.

silent reeeee

13 Likes

I think the reason I never use modules is because my game is not as complex as more experienced coder’s games. I just finished making my 3rd. I am a Noob here.

Holy Cheese why have I never heard of Tags?!

Wait! Is a Module Script like a instance with multiple functions Events and properties/variables. That is cool

No but you can put all that inside the actual script.

1 Like

That’s what I mean so it’s a custom instance. That’s so cool

The instance itself does not inherit the require()s returning data, or the instance would essentially upon being parented somewhere where LuaSourceContainers run.

I have no idea what you mean. Explain it in a more noobs language.

The instance isn’t changed. You can have it so when you require your ModuleScript, the actual structure it returns (like a table) has the events and such.

… maybe in Bacon-Hair language. I still don’t understand fully. Do you mean you can’t automatically change it like a intsance because you only (get) functions, variables, etc?

The module script itself is an object, instance, whatever you wanna call it. When you use the require keyword on the module script, you have access to the stuff inside of the module script that you returned.

ex)

ModuleScript located in game.ServerStorage and named “Module”:

local module = {
  ["Key"] = "Value"
}

return module

Script located in ServerScriptService:

local randomModule = require(game.ServerStorage.Module)

print(randomModule["Key"])

>> Value
1 Like

Windows Shuting Down I knew the module was a instance but I ment can you have your own custom Events functions and variables, while using required of course.

You can have all that in a table that you return inside the ModuleScript, but not on the actual ModuleScript instance itself.

Example:

-- ModuleScript.lua in Workspace
local mod = {}
mod.basicBool = true
mod.basicFunc = function()

end)

return mod -- The data returned here and HERE only is sent back as the result of the require()

-- SERVER.LUA in ServerScriptService
-- The following is the module INSTANCE.
-- It has all the generic properties, functions,
-- and events in it such as Instance.Name,
-- Instance:Destroy(), and Instance.Changed.
local moduleInstance = workspace.ModuleScript

-- The next is different. When require()ing the module,
-- I am essentially running the code inside, and asking
-- for whatever that return statement earlier returns
local moduleData = require(moduleInstance)
-- The moduleData variable does not have any of the stock Instance
-- functions, properties, or events built in because it isn't an Instance.
-- It is technically a lua table (thats what the {} mean) containing one 
-- variable (basicBool) and one function (basicFunc). If you want to
-- destroy the module Instance itself, moduleData does not have a
-- Destroy() function. You would have to call Destroy() on the INSTANCE.

1 Like