Knit Documentation
repository·main·Indexed 20 days ago
https://github.com/sleitnick/knitA lightweight framework for Roblox that simplifies server-client communication by abstracting networking infrastructure like RemoteFunctions and RemoteEvents. Knit utilizes a service-oriented architecture with server-side Services and client-side Controllers, featuring a structured lifecycle (KnitInit and KnitStart) and Promise-based startup sequence.
What's inside Knit
- Knit is a lightweight framework designed to simplify communication between core parts of a Roblox experience. It provides a structured way to bridge the gap between the server and the client by providing a built-in networking layer and organizing logic into Services and Controllers.
Client-accessed Services Limitation
mainThe Intellisense optimization (directrequireinstead ofGetService) only works for server-side services or client-side controllers. Services that must be accessed from the client via networking must still useKnit.GetService, and therefore will not benefit from the direct-require Intellisense pattern.Expose Service methods to the Client
mainTo allow clients to call methods on a server-side Service, you must define those methods within a
Clienttable inside the service.Knit automatically handles the networking by creating a
RemoteFunctionunder the hood. Inside theClienttable, you can useself.Serverto reference the main service instance and call its server-side methods.-- Inside your Service definition on the server function MoneyService.Client:GetMoney(player) -- 'self.Server' references the root MoneyService return self.Server:GetMoney(player) endUnderstand the Knit lifecycle: KnitInit and KnitStart
mainKnit provides two optional lifecycle methods to help orchestrate communication between services:
KnitInit: Fired after all services are created but before any service is guaranteed to be ready for consumption.- Use for: Setting up internal state, connecting to engine events (like
Players.PlayerRemoving), or referencing other services. - Avoid: Calling methods or accessing events on other services here, as they may not be fully initialized.
- Use for: Setting up internal state, connecting to engine events (like
KnitStart: Fired after allKnitInitmethods have completed.- Use for: Inter-service communication. At this stage, you can safely call methods on other services or connect to their events because all services are guaranteed to be initialized.
Best Practice: Set up your service's core logic in
KnitInit(or earlier in the ModuleScript) so that by the timeKnitStartruns, the service is ready for use by others.-- Example of using KnitInit for cleanup function PointsService:KnitInit() game:GetService("Players").PlayerRemoving:Connect(function(player) self.PointsPerPlayer[player] = nil end) endUse Signals for Server-to-Client and Client-to-Server communication
mainKnit uses signals (based on
RemoteEvent) to facilitate two-way communication between the server and client. You define these signals within theClienttable of a service usingKnit.CreateSignal().Server-to-Client Events
To notify clients of an event (e.g., a value changing), use
self.Client.SignalName:Fire(player, ...)on the server. Clients can listen using:Connect().Client-to-Server Events
To allow clients to trigger server logic without expecting a return value, define a signal in the
Clienttable. The server can listen for these inKnitInitusingself.Client.SignalName:Connect(function(player) ... end). Clients trigger them using:Fire().Unreliable Signals
For non-critical data (like cosmetic effects) where bandwidth is more important than guaranteed delivery or order, use
Knit.CreateUnreliableSignal()in theClienttable. This utilizes Roblox'sUnreliableRemoteEvent.-- Server: Creating and firing signals local PointsService = Knit.CreateService { Name = "PointsService", Client = { PointsChanged = Knit.CreateSignal(), GiveMePoints = Knit.CreateSignal(), PlayEffect = Knit.CreateUnreliableSignal(), }, } function PointsService:AddPoints(player, amount) self.Client.PointsChanged:Fire(player, newPoints) end -- Server: Listening to client signals in KnitInit function PointsService:KnitInit() self.Client.GiveMePoints:Connect(function(player) print(player.Name .. " requested points") end) end -- Client: Listening and firing local PointsService = Knit.GetService("PointsService") PointsService.PointsChanged:Connect(function(points) print("Points updated:", points) end) PointsService.GiveMePoints:Fire()How Knit middleware works
mainKnit uses the Comm module to provide middleware at both the inbound and outbound levels of the networking layer.
- Inbound Middleware: Fires before a service method or signal is executed. Use this for sanitizing data, deserializing complex types, or validating permissions.
- Outbound Middleware: Fires after a service method or signal is executed. Use this for transforming return values or serializing data before it is sent over the network.
Middleware can be applied globally (affecting all services/controllers) or per-service (overriding global middleware for that specific service).
Understand the Knit Lifecycle
mainKnit follows a specific execution flow to ensure all services and controllers are initialized before the application starts.
- Require Knit: Import the Knit module.
- Create Services/Controllers: Define your services (server) or controllers (client) as ModuleScripts.
- Call
Knit.Start(): This returns a Promise that manages the startup sequence:- All
KnitInitmethods are invoked simultaneously.Knit.Start()waits for all of them to complete. - Once all
KnitInitmethods finish, allKnitStartmethods are invoked simultaneously. - After all
KnitStartmethods finish, the promise returned byKnit.Start()resolves.
- All
Important: Services and controllers cannot be created after
Knit.Start()has been called. Once created, they persist until the server shuts down or the player leaves.local Knit = require(game:GetService("ReplicatedStorage").Packages.Knit) -- Load services or controllers here Knit.Start():catch(warn)Core concepts of Knit: Services and Controllers
mainKnit's architecture is centered around two primary abstractions:
- Services: Server-side objects that hold core game logic and can expose endpoints to the client.
- Controllers: Client-side objects that interact with Services.
By orienting logic around these objects, developers gain cleaner organization and easier maintainability. Knit provides a built-in networking layer that allows Services to expose specific endpoints to the client through declarative code, removing the need to manually manage
RemoteEventandRemoteFunctionobjects.What are Controllers and how to create them
mainControllers are singleton provider objects used on the client side. They act as the client-side equivalent of server-side Services. They are typically implemented as
ModuleScripts.To create a controller, use
Knit.CreateControllerwith a requiredNamefield. This name is the unique identifier used by other parts of your code to retrieve the controller viaKnit.GetController.local CameraController = Knit.CreateController { Name = "CameraController" } return CameraControllerUse events within a Service
main-- Load the Signal module and create PointsChanged signal: local Signal = require(Knit.Util.Signal) PointsService.PointsChanged = Signal.new() -- Modify AddPoints to fire the event: function PointsService:AddPoints(player, amount) local points = self:GetPoints(player) points += amount self.PointsPerPlayer[player] = points if amount ~= 0 then self.PointsChanged:Fire(player, points) end end -- In another service, listen to the event: function SomeOtherService:KnitStart() local PointsService = Knit.GetService("PointsService") PointsService.PointsChanged:Connect(function(player, points) print("Points changed for " .. player.Name .. ":", points) end) end-- Load the Signal module and create PointsChanged signal: local Signal = require(Knit.Util.Signal) PointsService.PointsChanged = Signal.new() -- Modify AddPoints to fire the event: function PointsService:AddPoints(player, amount) local points = self:GetPoints(player) points += amount self.PointsPerPlayer[player] = points if amount ~= 0 then self.PointsChanged:Fire(player, points) end endUse RbxUtil Loader for Custom Framework Bootstrapping
mainIf you are building a custom framework or want a cleaner way to handle the Intellisense-friendly bootstrapping, you can use the
Loadermodule fromRbxUtil. This allows you to filter modules by name and automatically trigger startup methods likeOnStartusingSpawnAll.-- Server Startup Example local services = Loader.LoadDescendants(ServerScriptService, Loader.MatchesName("Service$")) Loader.SpawnAll(services, "OnStart") -- Client Startup Example local controllers = Loader.LoadDescendants(ReplicatedStorage, Loader.MatchesName("Controller$")) Loader.SpawnAll(controllers, "OnStart")Install Knit
mainKnit can be installed using either the Roblox Studio workflow or the Rojo/Wally workflow.
Roblox Studio workflow
- Get Knit from the Roblox library.
- Place the module directly within
ReplicatedStorage.
Rojo/Wally workflow
- Add Knit to your
wally.tomldependency list:Knit = "sleitnick/knit@^1.7" - Require Knit like any other module managed by Wally.
Knit = "sleitnick/knit@^1.7"