Free use Module Loader

Module loaders are a very powerful resource that most newer developers are unaware of. Here is a simple one I made using lemonSignal. You can either use rojo with wally to install, or just get the lemonSignal module from the data-orient-house github here and redirect the require to the right place. Anyway, here is a module loader that works well for most projects:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RunService = game:GetService("RunService")
local ServerScriptService = game:GetService("ServerScriptService")

-- [[IMPORTANT]] If you arent using rojo/wally, you will need to install this manually
local lemonsignal = require(ReplicatedStorage.Packages.lemonsignal)

local _isServer = RunService:IsServer()
local _inst

-- [[IMPORTANT]] Make sure this directory points to your client and server modules
local _root = _isServer and Players.LocalPlayer.PlayerScripts.Controllers or ServerScriptService.Services

local modules: {[string]: any}

local finishedLoading = false
local finishedSignal = lemonsignal.new()

local Private = {}
local ModuleLoader = {}
ModuleLoader.__index = ModuleLoader

function Get()
    if not _inst then
        _inst = setmetatable({}, ModuleLoader)

        Private._RequireModules()
    end

    return _inst
end

function ModuleLoader.Import(moduleIndex: string)
    if modules[moduleIndex] and finishedLoading then
        return modules[moduleIndex]
    else
        local running = coroutine.running()
        local _timeoutThread
        local _conn
        
        _timeoutThread = task.delay(10, function()
            coroutine.resume(running)
        end)

        _conn = finishedSignal:Connect(function()
            coroutine.resume(running)
        end)

        coroutine.yield(running)

        coroutine.close(_timeoutThread)
        _conn:Disconnect()

        return modules[moduleIndex]
    end
end

function Private._SetupModules(bulk: {[string]: any})
    for _index: string, contents: any in bulk do
        if contents.Init then
            contents:Init()
        end
    end

    for _index: string, contents: any in bulk do
        if contents.Start then
            task.defer(contents.Start, contents)
        end
    end
    modules = bulk

    task.wait()

    finishedLoading = true
    finishedSignal:Fire()
end

function Private._RequireModules()
    local unloadedModules = _root:GetChildren()
    local toSetup = {}

    for _, module: ModuleScript in unloadedModules do
        if module.ClassName ~= "ModuleScript" then
            continue
        end
        toSetup[module.Name] = require(module)
    end

    Private._SetupModules(toSetup)
end

return Get

To use this, just put the module in replicated storage and have a local and server script require it from PlayerScripts or ServerScriptService. Alternatively, if your not on rojo, you can put those scripts under the module in replicated storage and set their run context to client and server. If you end up doing that instead, the runcontext property only exists on server scripts but you can change the client on to run on the client
Explorer


For using them in script, theres 2 phases you need to understand in order to use this module loader effectively: the initializing phase, and the start phase. The initializing phase focusing on seting up the module and making sure everything other scripts would use is available. For example, if you wanted to have a signal in your module that fires when a player gets a point, you could do something like this:

PointService:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local lemonsignal = require(path.to.lemonSignal)

local Private = {}
local PointService = {}
PointService.__index = PointService

function PointService:Init()
    PointService.PointsAwared = lemonsignal.new()
end

function PointService:AwardPoints(player: Player, points: number)
    PointService.PointsAwared:Fire(player, points)
    print(`Player: {player} got {points} points!`)
end

return setmetatable({}, PointService)

AnotherService:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ModuleLoader = require(path.to.ModuleLoader)

local PointService = ModuleLoader.Import("PointService")

local Private = {}
local AnotherService = {}
AnotherService .__index = AnotherService 

function AnotherService:Init()
    -- Dont connect here, as it creates a race condition.
    -- Connecting in the Start function is ideal as it will be called after the
    -- init function, where the connection is made, eliminating race conditions entirely.
end

function AnotherService:Start()
    PointService.PointsAwarded:Connect(function(plr: Player, points: number)
        print(`AnotherService knows that player: {plr} was awarded: {points} points!`)
    end)
end

return setmetatable({}, AnotherService)

If you have any concerns or questions, reply here or talk to me on discord: Sjdinsd.

1 Like

Hey, thanks for sharing this!
It looks like you’re trying to offer a cleaner approach to module management, especially with the way you separate the Init and Start phases. That’s a great practice and definitely something newer developers can benefit from. I appreciate that you included code samples and a working example. that’s more than a lot of people post.

That said, if the goal is to make this a true community resource, I’d recommend a few improvements to make it easier to follow, especially for beginners:

  1. Add a clear introduction:
    Start with a short paragraph explaining what a module loader is, why it’s helpful, and who the post is for. Right now, the post jumps into the code too quickly without setting the stage.

  2. Explain dependencies like lemonSignal:
    Not everyone knows what it is. Just one or two lines explaining what it does and why you chose it would go a long way.

  3. Include a step-by-step setup guide:
    Even a numbered list or a short bullet-point guide would help beginners understand how to install and use your loader in their own projects.

  4. Clarify how Init/Start phases work:
    I like that you separated them. maybe add a short note on when each phase is called and why it matters.

  5. Use consistent formatting and comments:
    Consider cleaning up the formatting a bit, adding more inline comments, and breaking up large blocks of code. It makes it more readable and easier to follow.

Overall, the idea is solid and the effort is appreciated. With just a bit more structure and explanation, this could be a really valuable tool for the community.

2 Likes

There’s no explanation for any of it. No intro, no reason why you’d use this, no basic setup steps, not even a sentence to define what lemonSignal is or how it works
— ModuleLoader
Module loaders are a very powerful resource that most newer developers are unaware of. Here is a simple one I made using lemonSignal. You can either use rojo with wally to install, or just get the lemonSignal module from the data-orient-house github here and redirect the require to the right place. Anyway, here is a module loader that works well for most projects:

— no basic setup steps
To use this, just put the module in replicated storage and have a local and server script require it from PlayerScripts or ServerScriptService. Alternatively, if your not on rojo, you can put those scripts under the module in replicated storage and set their run context to client and server. If you end up doing that instead, the runcontext property only exists on server scripts but you can change the client on to run on the client

not even a sentence to define what lemonSignal is or how it works

You saw a full module loader explaining two phases, example of usage, and a literal code example, and yet your decision was to report it for spam cause you didn’t understand it? Everything is a search away, the more they engage the better they learn. If this post simply didn’t suit you, it’s better to just move on rather than reporting it as spam because you didn’t understand what the OP gave.

2 Likes

image

Write in your own words. Don’t rely on AI, even if I agree with the points made it makes your message less credible :slight_smile: