Module System
The Module System is the core of Riptide’s dependency injection. It automatically require()s all ModuleScript descendants in your configured folders, registers them under canonical IDs, and provides type-safe lookup methods.
type Config = { ModulesFolder: Folder | { Folder }, SharedModulesFolder: (Folder | { Folder })?, ComponentsFolder: Folder?,}The internal registry types:
-- Internal module entry{ name: string, module: any }
-- Registry (not directly accessible)_modules: { [string]: any } -- canonicalId → module table_moduleAliases: { [string]: string | false } -- shortName → canonicalId | falseModule Lookup Methods
Section titled “Module Lookup Methods”GetModule
Section titled “GetModule”Riptide.GetModule(name: string) -> anyUniversal module lookup. Works on both server and client. Resolves in this order:
- Exact match against canonical ID (e.g.,
"Economy/PlayerData") - Alias match against short name (e.g.,
"PlayerData") - Returns
nil+ warning if not found or ambiguous
local data = Riptide.GetModule("Economy/PlayerData")GetService
Section titled “GetService”Riptide.GetService(name: string) -> any -- server onlyServer-side alias for GetModule. Throws an error if called on the client.
-- In a server service Init:function MyService:Init(Riptide) self.DataService = Riptide.GetService("DataService")endGetController
Section titled “GetController”Riptide.GetController(name: string) -> any -- client onlyClient-side alias for GetModule. Throws an error if called on the server.
-- In a client controller Init:function UIController:Init(Riptide) self.InputController = Riptide.GetController("InputController")endCanonical Module IDs
Section titled “Canonical Module IDs”Modules are identified by their relative path from the modules folder, using / as separator:
ModulesFolder/├── Economy/│ └── PlayerData.lua → "Economy/PlayerData"├── Combat/│ └── DamageService.lua → "Combat/DamageService"└── MatchService.lua → "MatchService"Alias Resolution
Section titled “Alias Resolution”Short names (the ModuleScript.Name) are automatically created as aliases:
| Scenario | Result |
|---|---|
| Name is unique across all folders | Alias works. GetModule("PlayerData") → ✅ |
| Name collides with another module | Alias is disabled (marked false). A warning is emitted. Use canonical ID. |
| Canonical ID is the same as name | No alias needed. Direct match. |
Loading Order
Section titled “Loading Order”When Launch is called:
- Shared modules (
SharedModulesFolder) are loaded first. - Side-specific modules (
ModulesFolder) are loaded second. - Deduplication — if a
ModuleScriptappears in multiple folders (e.g., via linked instances), it is only loaded once.
This guarantees shared utilities are available before services or controllers that depend on them.
Entry Point Types
Section titled “Entry Point Types”The framework exports clean types for use in your modules:
export type RiptideServer = { Network: NetworkAPI, Signal: typeof(Signal), Async: typeof(Async), ComponentService: ComponentServiceAPI, State: StateReplicationAPI, PlayerLifecycle: any, GetModule: (name: string) -> any, GetService: (name: string) -> any, Server: { Launch: (config: Config) -> () },}
export type RiptideClient = { Network: NetworkAPI, Signal: typeof(Signal), Async: typeof(Async), ComponentService: ComponentServiceAPI, State: StateReplicationAPI, GetModule: (name: string) -> any, GetController: (name: string) -> any, Client: { Launch: (config: Config) -> () },}
export type Riptide = RiptideServer & RiptideClientUse in your modules for full autocomplete:
local RiptidePkg = require(ReplicatedStorage.Packages.Riptide)type Riptide = RiptidePkg.Riptide