Introducing: Modules

                 Modules-256 Modules-256-black

Introducing Modules, a simple dependency loader for Roblox!

Download Model Documentation GitHub

Quick Start – What does it do?

Modules wraps the built-in require function. This lets you use a string to find your modules intelligently – while also retaining all its existing functionality!

-- Overwrite require to get a bunch of cool features:
local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"))
-- Searches for namespace "MyNamespace", then for module "MyModule":
local MyModule = require("MyNamespace:MyModule")
-- Requires the module at:
--  ServerScriptService.MyNamespace.MyModule
-- or ReplicatedStorage.MyNamespace.MyModule

-- You can also use a built-in module like this:
local Event = require("Event")

Modules uses namespaces to look for code. This is just a Folder in ReplicatedStorage or ServerScriptService.

Check out the Getting Started guide →

Features

:battery: Some Batteries Included: A few commonly-used utilities and patterns are built-in: class, Event, Maid, StateMachine. These can be required using require("Event"), etc!

:pizza: Sharing is Caring: In shared modules, require.server(...) and require.client(...) allow you to skip requires if not on a certain network peer.

:package: Easy to Package: With Modules, you can bundle server and client code simply into a single Folder (the namespace) in ServerScriptService, then put client code in a “Replicated” folder. It’s great for Roblox packages!

Download

Version Download Model Changes
latest ↓ 36.7 KB Get
1.1.1 ↓ 36.7 KB Get Linting
1.1.0 ↓ 36.7 KB Get Exclude tests
1.0.0 ↓ 48.8 KB Get Initial release

Docs: docs.ozzypig.com/Modules

Modules is fully documented from doc comments in-code! You can read these directly in Roblox Studio, or check out the documentation website which is built from these comments using a pretty involved toolchain. (.lua → LDoc → ldoc2mkdocs (custom Python tool) → MkDocs → .html)

GitHub: Ozzypig/Modules

The project is built using Rojo 0.5.4, although later versions will work as well :+1:t3:

See also

Feedback

In order of preference, here’s the ways I’d like your feedback on Modules:

Enjoy, and please let me know if you made something cool!

45 Likes

Just a quick note for this thread: If you have questions, comments, or issues, please consider posting a GitHub Issue on the repository instead of posting here. It helps me keep track of issues individually and is generally a better workflow :slight_smile:

image

I’ll still keep an eye out on this thread, but you can help streamline development of Modules if you post issues there instead of here :+1:

6 Likes

The StateMachine link is broken on the Getting Started guide. Edit: apologies for not making this as a GitHub issue initially - wasn’t sure if you wanted docs issues there too.

I could’ve really done with this module sooner! I’ve created two iterations of my own state machine for two games I’m working on at the moment, and they work fine, but they sure aren’t as neat as this. You’ve implemented the transitions and the cleanup just that little bit more elegantly and I hope this gets more people on the state machine train.

3 Likes

Thanks for catching that! I’ve made a GitHub issue to track the fix for this typo, which I’ll do ASAP. Update: It’s been fixed

Glad you like it! I don’t see StateMachines getting enough love nowadays. I like to think this module caters to both a normal event-based usage and a subclass-based usage. I’ve used the sub-machine pattern in a lot of my UI in the past :slight_smile:

3 Likes

Awesome stuff! Really cool.

One thing I’m wondering is why use this over a module loader like Nevermore? From what I can tell Nevermore does the same thing and comes with more modules?

Good question. The short answer is scope, but the longer more involved answer is a bit more complicated. Modules is a lightweight loader with just a few goodies, and will stay that way. Stuff that helps bridge the Lua ↔ Roblox gap, along with data structures you might use in many libraries. In the future, I plan to add Promises and Binders. Basically, if you’ve asked yourself “why isn’t library-so-and-so built in to Roblox itself?”, then I probably will want to add it.

The main difference is that Nevermore is a full-fledged engine, with an armful of bells and whistles. For simple projects, you’ll very likely not use a large majority of its features. This isn’t inherently a problem with its design; you might actually end up using the features in a project eventually - it’s very simply a matter of scope.

Modules makes it easy to scope out what your project will need. Going to need a Ragdoll library? A Markdown parser? Maybe even a debug drawing library? These can all be in their own namespaces, only included where they are needed.

3 Likes

Thanks for clearing that up! I’ll be sure to check it out :slight_smile:

2 Likes

A quick update to this post! Since release, I’ve updated to Modules 1.1.1. Test code is no longer included in the main module, and the code passes the selene linter. I’ve also fixed a number of documentation typos. No new features, but everything’s much tidier.

The OP is also updated with a table of links to downloads on the devforum as well as Roblox models. I’ve also made the whole thing easier to read.

1 Like

How does this accommodate for cyclic requiring?

Cyclic requiring causes scripts to hang indefinitely. Although there’s no way good way to work around this*, Modules will emit a warning displaying which requires are taking too long. For example “A → B is taking too long”. Using that, you’d be able to figure out where your cycle is.

*

Current plan to improve the debugging experience is to run a topological sort on a require call graph in order to figure out what the actual cycle is, but only after the timeout is reached to maintain performance. Again, this is all a matter of assuming a halt is due to a cycle and not necessarily due to a module taking a long time to load. I’m not entirely sure I like this solution, but it might help in some cases.

Sorry for the bump. But i think this specific feature could have some problems. I’ve encountered this problem recently, what happends is that, no matter if you’re editing a Local Script or a Server Script, it will load modules at least, as a client. I had a system put in place for one of my modules which returned an empty table for the module if you were requesting the module via a client. Removing that, allowed me to view all functions everything from the module from another script completely fine. Which leads me to believe the script editor is loading modules as a client. I used RunService to check it… and yeah.

If anyone wants to file a bug report for that, that would be great! I can’t so :frowning:

Apologies for the late response, but can you describe this problem further? It’s a little odd having a ModuleScript return different things based on peer (client/server). I’m not sure if you’re implying that Modules is doing this, or if this was by the design of the module you were requiring.

It would help immensely if you could describe your use case here, or responsibility of the module.

1 Like

Basically the script editor caches modules as a client. So that it can know the functions of a module when you’re scripting and can auto complete for you.

Because of that, my module had a system in place which wouldn’t load the module and instead would only load the client stuff and not the actual module which is server specific.

If you return an empty module if RunService:IsClient() then it would think that it would be a client. So i can’t preview the functions inside my module. I just removed the system in place to protect the client because of that. If you’re confused on something you can tell me I will try to help explain it to you; :slight_smile:

It would return IsClient() as true even if you’re on a server script. So it gets pretty annoying.

It might be by design but it limits the simplicity of the module.

Oh, I see. Unfortunately I don’t really use the in-Studio script editor much nowadays, plus the require.server/require.client pattern is pretty much expected to break autocompletion like this.

1 Like

By the way, I was actually wrong here, the reason I had problems with that, was that actually, module script caching “pretends” there’s no if-statements. The good part about that, is that you can have Script Editor specific code. By doing “if false then” statements. So I started building my modules like that.