4. SuperbulletFrameworkV1-Knit
b. Auto Components

Auto Components

Automatically load and run your component modules without manual require statements. Part of SuperbulletFrameworkV1-Knit (2025).

What Does This Do?

Instead of manually requiring and initializing each module, you add Instance = script to your service/controller and Knit handles the rest:

When you pass Instance = script to CreateService() or CreateController(), Knit will automatically:

  1. Look for a Components folder inside your service/controller
  2. Load all component modules and call their Init() functions
  3. Set up Accessor, Mutator, and Components utilities on the service/controller
  4. Call Start() on all components after the service/controller starts

Enabling Component Initialization

Pass Instance = script when creating your service or controller:

Server (Service):

local MyService = Knit.CreateService({
    Name = "MyService",
    Client = {},
    Instance = script,  -- Enables automatic component initialization
})

Client (Controller):

local MyController = Knit.CreateController({
    Name = "MyController",
    Instance = script,  -- Enables automatic component initialization
})

Folder Structure

The Component Initializer expects this folder structure:

MyService/
β”œβ”€β”€ init.lua           -- Main service file
└── Components/
    β”œβ”€β”€ Accessor.lua   -- (Optional) Get component for reading data
    β”œβ”€β”€ Mutator.lua    -- (Optional) Set component for writing data
    └── Others/
        β”œβ”€β”€ FeatureA.lua
        β”œβ”€β”€ FeatureB.lua
        └── Utilities.lua

Legacy Support

For backward compatibility, the system also supports:

  • Get().lua instead of Accessor.lua
  • Set().lua instead of Mutator.lua

Component Lifecycle

Components follow a two-phase lifecycle that mirrors Knit's service/controller lifecycle:

Lifecycle Order:
1. Knit.Start() begins
2. Service/Controller KnitInit() runs
3. ComponentInitializer.Initialize() runs:
   - Loads all modules in Components/Others/
   - Sets up Accessor and Mutator
   - Calls Init() on all component modules
4. Service/Controller KnitStart() runs
5. ComponentInitializer.Start() runs:
   - Calls Start() on all component modules

Init() Phase

The Init() function runs before KnitStart(). Use it for:

  • Setting up initial state
  • Registering client extensions (signals, methods, properties)
  • Getting service/controller references
-- Components/Others/QuestHandler.lua
local QuestHandler = {}
 
function QuestHandler.Init()
    -- Runs during component initialization phase
    local QuestService = Knit.GetService("QuestService")
 
    -- Register dynamic client items here
    Knit.RegisterClientSignal(QuestService, "OnQuestUpdate")
end
 
return QuestHandler

Start() Phase

The Start() function runs after KnitStart(). Use it for:

  • Runtime logic
  • Connecting events
  • Starting gameplay systems
-- Components/Others/QuestHandler.lua
local QuestHandler = {}
 
function QuestHandler.Init()
    -- Setup code
end
 
function QuestHandler.Start()
    -- Runs after service starts
    -- Safe to run gameplay logic here
    QuestHandler:LoadActiveQuests()
end
 
function QuestHandler:LoadActiveQuests()
    -- Implementation
end
 
return QuestHandler

Accessing Components

After initialization, the service/controller has access to:

Components Table

All modules in the Others/ folder are available via self.Components:

function MyService:KnitStart()
    -- Access a component module
    local QuestHandler = self.Components.QuestHandler
    QuestHandler:DoSomething()
end

Accessor (Get Component)

If you have an Accessor.lua (or Get().lua), it's available via self.Accessor:

-- Components/Accessor.lua
local Accessor = {}
 
function Accessor:GetPlayerData(player)
    return DataStore:GetData(player)
end
 
return Accessor
 
-- In your service:
function MyService:KnitStart()
    local data = self.Accessor:GetPlayerData(somePlayer)
    -- or use the alias:
    local data = self.GetComponent:GetPlayerData(somePlayer)
end

Mutator (Set Component)

If you have a Mutator.lua (or Set().lua), it's available via self.Mutator:

-- Components/Mutator.lua
local Mutator = {}
 
function Mutator:SetPlayerData(player, data)
    DataStore:SetData(player, data)
end
 
return Mutator
 
-- In your service:
function MyService:KnitStart()
    self.Mutator:SetPlayerData(somePlayer, newData)
    -- or use the alias:
    self.SetComponent:SetPlayerData(somePlayer, newData)
end

Complete Example

MyService/init.lua:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Knit = require(ReplicatedStorage.Packages.Knit)
 
local MyService = Knit.CreateService({
    Name = "MyService",
    Client = {},
    Instance = script,
})
 
function MyService:KnitStart()
    -- Components are now available
    print("Components loaded:", self.Components)
 
    -- Use the Accessor
    if self.Accessor then
        local data = self.Accessor:GetSomeData()
    end
end
 
function MyService:KnitInit()
    -- Get other services
end
 
return MyService

MyService/Components/Accessor.lua:

local Accessor = {}
 
function Accessor:GetSomeData()
    return { key = "value" }
end
 
return Accessor

MyService/Components/Others/FeatureHandler.lua:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Knit = require(ReplicatedStorage.Packages.Knit)
 
local FeatureHandler = {}
 
function FeatureHandler.Init()
    print("FeatureHandler initializing")
 
    -- Can register client items here
    local MyService = Knit.GetService("MyService")
    Knit.RegisterClientSignal(MyService, "FeatureEvent")
end
 
function FeatureHandler.Start()
    print("FeatureHandler started")
    -- Runtime logic
end
 
return FeatureHandler

Duplicate Prevention

The Component Initializer tracks which modules have been initialized using attributes:

  • Initialized attribute prevents calling Init() twice
  • Started attribute prevents calling Start() twice

This ensures components only run their lifecycle methods once, even if the module is required multiple times.

Error Handling

If a component's Init() or Start() function throws an error, the system:

  • Logs a warning with the full path to the component
  • Continues initializing other components
  • Does not halt the entire Knit startup process
Example warning:
Error initializing component ServerScriptService.MyService.Components.Others.BrokenComponent: attempt to index nil

Best Practices

  1. Keep components focused - Each component should handle one specific feature
  2. Use Init() for setup - Register remotes, set up state
  3. Use Start() for runtime - Connect events, start systems
  4. Don't yield in Init() - This blocks other services from initializing
  5. Access other services in Init() - They're available via Knit.GetService()