Skip to main content

Init

A module loader for Roblox games. You point Init at folders, and it requires every module in them in a fixed order, calls their Init and Start methods, and wires up the methods you would otherwise connect by hand (OnPlayerAdded, OnHeartbeat, OnCharacterAdded, ...).

One Script on the server and one LocalScript on the client boot the whole game:

-- ServerScriptService/InitServer (Script)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")

local Init = require(ReplicatedStorage.Packages.Init)

Init.start({
	{ Folders = { ReplicatedStorage.Network } },
	{ Folders = { ReplicatedStorage.Services, ServerScriptService.Services }, Predicate = Init.matchesName("Service$") },
	{ Folders = { ReplicatedStorage.Binders.Shared, ServerScriptService.Binders } },
})

A module Init loads is a plain table. Every method is optional:

local PointsService = {}

function PointsService.Init(self: PointsService)
	self.Points = {} -- set up your own state; don't wait for other modules here
end

function PointsService.Start(self: PointsService)
	-- every module has finished Init, so it's safe to use them
end

function PointsService.OnPlayerAdded(self: PointsService, player: Player)
	self.Points[player] = 0
end

return PointsService

See the lifecycle for the exact order things run in.

Types​

Settings​

type Settings = Settings

Every option with its default lives in Settings.luau and on the settings page.

Entry​

interface Entry {
Folders: {Instance}--

where to look; every ModuleScript inside them (at any depth) is a candidate

Predicate: ((module: ModuleScript) → boolean)?--

only load modules this returns true for, e.g. Init.matchesName

Order: number?--

only used when entries are a dictionary (the old Entries-module format): lower runs first

}

One group of modules to load. A ModuleScript inside another ModuleScript is never loaded on its own: it belongs to its parent, which can require it.

Entries​

type Entries = {Entry} | {[string]: Entry}

A list of entries, loaded in list order. A dictionary of named entries (the format of the old SharedEntries/ServerEntries modules) also works and is sorted by each entry's Order.

Modules​

type Modules = {[string]: any}

Every loaded module's returned value, by the ModuleScript's name.

Functions​

start​

This is a yielding function. When called, it will pause the Lua thread that called the function until a result is ready to be returned, without interrupting other scripts. Yields
Init.start(
entries: Entries,--

what to load, in order

settings: {[string]: any}?--

overrides for any of the settings

) → Promise<Modules,number,number>

Loads and starts every module the entries point at. Call it once per realm: from one Script on the server and one LocalScript on the client.

  1. Load: requires the modules, entry by entry. Inside an entry, modules with a lower Priority attribute go first. Modules with an Ignore attribute, modules a Predicate turns down and ModuleScripts that sit inside another ModuleScript are skipped.
  2. Init: calls Init on each module, one at a time, in load order. If one errors or takes longer than InitTimeout, it's reported and that module won't Start.
  3. Start: calls Start on every module at once (a Start that waits doesn't hold up the others). Each module's hooks (OnPlayerAdded, OnHeartbeat, ...) connect as soon as its own Start returns.

The returned promise resolves after every Start has returned (or hit StartTimeout), with the loaded modules by name, how many loaded, and how long it took in seconds.

Init.start(entries):andThen(function(modules, count, seconds)
	workspace:SetAttribute("ServerStarted", true)
end)

matchesName​

Init.matchesName(pattern: string) → (module: ModuleScript) → boolean

Makes a Predicate that keeps modules whose name matches a string pattern.

{ Folders = { ServerScriptService.Services }, Predicate = Init.matchesName("Service$") } -- names ending in Service
Show raw api
{
    "functions": [
        {
            "name": "start",
            "desc": "Loads and starts every module the entries point at. Call it once per realm: from one Script on\nthe server and one LocalScript on the client.\n\n1. **Load:** requires the modules, entry by entry. Inside an entry, modules with a lower\n   `Priority` attribute go first. Modules with an `Ignore` attribute, modules a Predicate turns\n   down and ModuleScripts that sit inside another ModuleScript are skipped.\n2. **Init:** calls `Init` on each module, one at a time, in load order. If one errors or takes\n   longer than `InitTimeout`, it's reported and that module won't Start.\n3. **Start:** calls `Start` on every module at once (a Start that waits doesn't hold up the\n   others). Each module's hooks (`OnPlayerAdded`, `OnHeartbeat`, ...) connect as soon as its\n   own Start returns.\n\nThe returned promise resolves after every Start has returned (or hit `StartTimeout`), with the\nloaded modules by name, how many loaded, and how long it took in seconds.\n\n```lua\nInit.start(entries):andThen(function(modules, count, seconds)\n\tworkspace:SetAttribute(\"ServerStarted\", true)\nend)\n```",
            "params": [
                {
                    "name": "entries",
                    "desc": "what to load, in order",
                    "lua_type": "Entries"
                },
                {
                    "name": "settings",
                    "desc": "overrides for any of the [settings](https://kashtheking.com/init/docs/guides/settings)",
                    "lua_type": "{ [string]: any }?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise<Modules, number, number>"
                }
            ],
            "function_type": "static",
            "yields": true,
            "source": {
                "line": 376,
                "path": "src/Init/init.luau"
            }
        },
        {
            "name": "matchesName",
            "desc": "Makes a Predicate that keeps modules whose name matches a\n[string pattern](https://create.roblox.com/docs/luau/strings#string-patterns).\n\n```lua\n{ Folders = { ServerScriptService.Services }, Predicate = Init.matchesName(\"Service$\") } -- names ending in Service\n```",
            "params": [
                {
                    "name": "pattern",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "(module: ModuleScript) -> boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 475,
                "path": "src/Init/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "Settings",
            "desc": "Every option with its default lives in\n[`Settings.luau`](https://github.com/KashTheKing/init/blob/main/src/Init/Settings.luau) and on the\n[settings page](https://kashtheking.com/init/docs/guides/settings).",
            "lua_type": "Settings",
            "source": {
                "line": 61,
                "path": "src/Init/init.luau"
            }
        },
        {
            "name": "Entry",
            "desc": "One group of modules to load. A ModuleScript inside another ModuleScript is never loaded on its\nown: it belongs to its parent, which can `require` it.",
            "fields": [
                {
                    "name": "Folders",
                    "lua_type": "{ Instance }",
                    "desc": "where to look; every ModuleScript inside them (at any depth) is a candidate"
                },
                {
                    "name": "Predicate",
                    "lua_type": "((module: ModuleScript) -> boolean)?",
                    "desc": "only load modules this returns `true` for, e.g. [Init.matchesName](#matchesName)"
                },
                {
                    "name": "Order",
                    "lua_type": "number?",
                    "desc": "only used when entries are a dictionary (the old Entries-module format): lower runs first"
                }
            ],
            "source": {
                "line": 73,
                "path": "src/Init/init.luau"
            }
        },
        {
            "name": "Entries",
            "desc": "A list of entries, loaded in list order. A dictionary of named entries (the format of the old\n`SharedEntries`/`ServerEntries` modules) also works and is sorted by each entry's `Order`.",
            "lua_type": "{ Entry } | { [string]: Entry }",
            "source": {
                "line": 86,
                "path": "src/Init/init.luau"
            }
        },
        {
            "name": "Modules",
            "desc": "Every loaded module's returned value, by the ModuleScript's name.",
            "lua_type": "{ [string]: any }",
            "source": {
                "line": 94,
                "path": "src/Init/init.luau"
            }
        }
    ],
    "name": "Init",
    "desc": "A module loader for Roblox games. You point Init at folders, and it requires every module in\nthem in a fixed order, calls their `Init` and `Start` methods, and wires up the methods you\nwould otherwise connect by hand (`OnPlayerAdded`, `OnHeartbeat`, `OnCharacterAdded`, ...).\n\nOne Script on the server and one LocalScript on the client boot the whole game:\n\n```lua\n-- ServerScriptService/InitServer (Script)\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal ServerScriptService = game:GetService(\"ServerScriptService\")\n\nlocal Init = require(ReplicatedStorage.Packages.Init)\n\nInit.start({\n\t{ Folders = { ReplicatedStorage.Network } },\n\t{ Folders = { ReplicatedStorage.Services, ServerScriptService.Services }, Predicate = Init.matchesName(\"Service$\") },\n\t{ Folders = { ReplicatedStorage.Binders.Shared, ServerScriptService.Binders } },\n})\n```\n\nA module Init loads is a plain table. Every method is optional:\n\n```lua\nlocal PointsService = {}\n\nfunction PointsService.Init(self: PointsService)\n\tself.Points = {} -- set up your own state; don't wait for other modules here\nend\n\nfunction PointsService.Start(self: PointsService)\n\t-- every module has finished Init, so it's safe to use them\nend\n\nfunction PointsService.OnPlayerAdded(self: PointsService, player: Player)\n\tself.Points[player] = 0\nend\n\nreturn PointsService\n```\n\nSee [the lifecycle](https://kashtheking.com/init/docs/guides/lifecycle) for the exact order things run in.",
    "source": {
        "line": 47,
        "path": "src/Init/init.luau"
    }
}