Skip to content

Module Lifecycle

Riptide uses a deterministic 3-phase lifecycle to orchestrate your game modules. This eliminates circular-require issues and guarantees all dependencies are available before game logic runs.

Every ModuleScript descendant inside your configured ModulesFolder (and SharedModulesFolder) is require()’d. The returned table is registered in the internal module registry under a canonical ID.

SharedModulesFolder loaded first → ModulesFolder loaded second

After all modules are loaded, Riptide calls Init on every module synchronously and in order:

function MyService:Init(Riptide: Riptide)
-- Safe to call GetService / GetController here
self.DataService = Riptide.GetService("DataService")
-- Safe to register network handlers
Riptide.Network.Register("FetchCoins", function(player)
return self:GetCoins(player)
end)
end

Since Init is synchronous, all modules have been loaded (but not yet started) when your Init runs. This is the correct place for dependency injection.

After all Init calls complete, Riptide calls Start on every module via task.spawn:

function MyService:Start(Riptide: Riptide)
-- All modules are fully initialized — safe to interact
while true do
self:TickGameLoop()
task.wait(1)
end
end

Since each Start runs in its own coroutine, one module yielding does not block others.


Every module is registered with a canonical ID based on its relative path from the modules folder:

ModulesFolder/
├── Economy/
│ └── PlayerData.lua → "Economy/PlayerData"
├── Combat/
│ └── DamageService.lua → "Combat/DamageService"
└── MatchService.lua → "MatchService"
-- Canonical ID — always works
Riptide.GetService("Economy/PlayerData")
-- Short alias — works only when the name is unique
Riptide.GetService("PlayerData")
-- Ambiguous alias — returns nil + warning
-- (e.g. two modules named "Utils" in different folders)
Riptide.GetService("Utils") -- ⚠️ nil

MethodContextDescription
Riptide.GetModule(name)SharedUniversal module lookup by canonical ID or alias.
Riptide.GetService(name)ServerAlias for GetModule. Errors if called on client.
Riptide.GetController(name)ClientAlias for GetModule. Errors if called on server.

--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RiptidePkg = require(ReplicatedStorage.Packages.Riptide)
type Riptide = RiptidePkg.Riptide
local CoinsService = {}
function CoinsService:Init(Riptide: Riptide)
self.State = Riptide.State
Riptide.Network.Register("GetCoins", function(player)
return self.State:Get("coins", player)
end)
end
function CoinsService:Start(Riptide: Riptide)
print("CoinsService is running!")
end
function CoinsService:OnPlayerAdded(Riptide: Riptide, player: Player)
self.State:SetForPlayer(player, "coins", 100)
end
return CoinsService

require() all ModuleScripts ← Load Phase
Init(Riptide) — synchronous ← Init Phase (inject deps)
Start(Riptide) — task.spawn ← Start Phase (run logic)