How to get components with Knit

I am trying to learn Knit, but am running into constant problems with tutorials and the fact everything has seemingly changed.

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Knit = require(ReplicatedStorage.Packages.Knit)
local Component = require(Knit.Util.Component)

Knit.AddServices(script.Parent.Services)

Component.Auto(script.Parent.Components) -- Error

ServerScriptService.Game.Server:8: attempt to call a nil value

.Auto does not exist anywhere in the Knits code. But this is what the tutorial uses

and there’s no mention of it under the Component in the git. So I’m clueless

2 Likes

It appears that Sleitnick has removed the Component.Auto() function of his Component Module. Here is what It used to be:

and here is what It looks like now:
image

If you want to have the Component.Auto() function to work, you can use Wally and add "Component = “sleitnick/component@1.1.0"” instead of "Component = “sleitnick/component@2.1.0"” as a dependency. otherwise, you will not be able to use the Auto function, and you will have to just go over the children in the folder and add all of the components manually.

Knit gets updated very often and not all code in the tutorial can still be working

Is there a way to get older versions? Seems kinda bad to have it automatically update and have a dozen things change, get removed, etc. :confused: I can’t imagine the anger I’d have if I had 40-50k lines of code and then the framework changes overnight and i have to rewrite everything

1 Like

Yes, you can check the older versions of Knit by going on sleitnick’s GitHub Releases · Sleitnick/Knit · GitHub

If you are using Wally, you can just install an earlier version (“sleitnick/component@1.1.0”), If not, I could send you the older version if you would like.

Well, the module wouldn’t update unless you insert it back from the toolbox

Here is the Auto function that is in the older version of Component:

--[=[
	@param parent Instance
	@return RBXScriptConnection

	Scans all descendants of `parent` and loads any ModuleScripts found, then
	calls `Component.new` on those loaded modules.

	Each component module class must have a `Tag` string property to map it
	to the proper tag.
]=]
function Component.Auto(parent)
	local function Setup(moduleScript)
		local m = require(moduleScript)
		assert(type(m) == "table", "Expected table for component")
		assert(type(m.Tag) == "string", "Expected .Tag property")
		Component.new(m.Tag, m, m.RenderPriority, m.RequiredComponents)
	end
	for _,v in ipairs(parent:GetDescendants()) do
		if v:IsA("ModuleScript") then
			Setup(v)
		end
	end
	return parent.DescendantAdded:Connect(function(v)
		if v:IsA("ModuleScript") then
			Setup(v)
		end
	end)
end

Trying to run this function with the new version on Component will not work now though as Component.new() now takes in new parameters, and a class like the old one took in is not one of them.

It seems to have changed indeed. I would join the Roblox OSS Discord and ask it in #knit. The creator of the framework is quite active in the chat. You can find an invite in the footer of the Rojo home page.

Sleitnick has made a new Knit tutorial, though he does not use Component in it :slightly_frowning_face:

2 Likes

Component.Auto() was removed in favor of manually requiring the components in Knit’s bootstrap code like so:

Knit:Start():andThen(function()
	for i, component in pairs(script.Parent.Components:GetChildren()) do
		if component:IsA("ModuleScript") then
			require(component)
		end
	end
end):catch(warn)

What’s the point of using the Component Util if I just can use CollectionService myself and save the hassle? It seems like more work unless the Components can be seen via the client and vice versa, I see no reason to use it.

This is true but with Knit Components you have metatables which is nice to have in a component. When you do it normally with CollectionService it is not as powerful. Obviously there is ways to have metatables with the “normal” way but in this case Knit does it for you so you don’t have to do all the extra stuff.

And tbh its not that much code, it may even be less than if you are doing it with CollectionService

local Component = require(ReplicatedStorage.Packages.Component)

local MyComponent = Component.new({
	Tag = "MyComponent";
})

function MyComponent:Construct()
	-- New Component
end

function MyComponent:Stop()
	-- Component Destroyed
end

return MyComponent
4 Likes

Typically when I use components they act as singletons, It’s just a weird way to look at it for me since I’m using to calling Object.new() instead of Object:Construct()

Is there any getting started/tutorial related to the current version of Components? I can’t find anything which tells me how to use the current version, and the docs have been cropped down to basically a list of methods.

Not that I know of, I honestly don’t use components because I use inheritance and I don’t know how to do that with components.

I figured out how to use components through trial an error as the documentation is pretty much non-existent. But it was well worth it; I now use components all the time. It’s actually much simpler to use than it appears. Here’s a quick start guide:

I load all the components after the Knit server has started like this:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Knit = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("knit"))

local Components = script.Parent:WaitForChild("Components") -- PUT YOUR OWN PATH TO COMPONENTS FOLDER!

Knit.Start():andThen(function()
    Knit.Components = {}
    for _, c in pairs(Components:GetChildren()) do
        Knit.Components[c.Name] = require(c)
    end
end):catch(warn)

You could just require each component, but for me it’s useful to be able to access them all in one place, so I store the required components into a new “Components” variable on the Knit instance.

Then create components into the Components folder, such as Health.lua:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Component = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("component"))

local Health = Component.new({
	Tag = "Health",
})

function Health:Construct()
	self.value = 100
end

function Health:Start()
	print(self.Instance.." now has a health component")
end

function Health:Stop()

end

function Health:HeartbeatUpdate(dt)
end

function Health:SteppedUpdate(dt)
end

function Health:RenderSteppedUpdate(dt)
end

return Health

Now anything with a “Health” tag added to it will have this component. Pretty simple.

If you wanted to access all the instances which have health from anywhere else in your code (from server) you could just do this:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Knit = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("knit"))

Knit.OnStart():await() -- Make sure Knit server is already started

local allHealth = Knit.Components.Health:GetAll()

for _, health in pairs(allHealth) do
	print(health.Instance," has Health of ", health.value)
end
8 Likes

As far at inheritance goes you can do it with components like this. Let’s say you have Car and Truck which are both Vehicles. You’d create a Vehicle component. You’d then have a “Car” component, and a “Truck” component, which can access the Vehicle which is attached to their instance.

You’d need to add both a “Vehicle” tag and a “Truck” tag to a truck, and a “Vehicle” tag and a “Car” tag to a car.

While this is not inheritance in the OOP sense, it is essentially the same thing.
Vehicle Component:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Component = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("component"))

local Vehicle = Component.new({
	Tag = "Vehicle",
})

function Vehicle:Construct()
	self.speed = 0
end

return Vehicle

Truck Component:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Component = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("component"))
local Knit = require(ReplicatedStorage:WaitForChild("Packages"):WaitForChild("component"))

local Truck = Component.new({
	Tag = "Truck",
})

function Truck:Construct()
	
end

function Truck:Start()
	self.vehicle = Knit.Components.Vehicle:FromInstance(self.Instance)
	self.vehicle.speed = 10
end

return Truck
3 Likes

Could you explain what components are and what they’re used for? Thank you in advance