ClientCast attempt to index nil with 'Disabled' Error

Hello, while developing a combat game, an error appeared in the ClientCast module.

"ReplicatedStorage.Framework.ClientCast:115: attempt to index nil with ‘Disabled’ " How do I fix this error?

ClientCast: https://create.roblox.com/marketplace/asset/6014401698/ClientCast

This error appears when using SetOwner() in a server script and the humanoid is respawned.

local ClientCast = {}
local Settings = {
	AttachmentName = "DmgPoint", -- The name of the attachment that this network will raycast from
	DebugAttachmentName = "ClientCast-Debug", -- The name of the debug trail attachment

	FunctionDebug = false,
	DebugMode = true, -- DebugMode visualizes the rays, from last to current position
	DebugColor = Color3.new(1, 0, 0), -- The color of the visualized ray
	DebugLifetime = 1, -- Lifetime of the visualized trail
	AutoSetup = true -- Automatically creates a LocalScript and a RemoteEvent to establish a connection to the server, from the client.
}

if Settings.AutoSetup then
	require(script.ClientConnection)()
end

ClientCast.Settings = Settings
ClientCast.InitiatedCasters = {}

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ReplicationRemote = ReplicatedStorage:FindFirstChild("ClientCast-Replication")
local PingRemote = ReplicatedStorage:FindFirstChild("ClientCast-Ping")

local Signal = require(script.Signal)

local function SafeRemoteInvoke(RemoteFunction, Player, MaxYield)
	local ThreadResumed = false
	local Thread = coroutine.running()

	task.spawn(function()
		local TimestampStart = time()
		RemoteFunction:InvokeClient(Player)
		local TimestampEnd = time()

		ThreadResumed = true
		task.spawn(Thread, math.min(TimestampEnd - TimestampStart, MaxYield))
	end)

	task.delay(MaxYield, function()
		if not ThreadResumed then
			ThreadResumed = true
			task.spawn(Thread, MaxYield)
		end
	end)

	return coroutine.yield()
end

local function SerializeParams(Params)
	return {
		FilterDescendantsInstances = Params.FilterDescendantsInstances,
		FilterType = Params.FilterType.Name,
		IgnoreWater = Params.IgnoreWater,
		CollisionGroup = Params.CollisionGroup
	}
end
local function IsA(Object, Type)
	return typeof(Object) == Type
end
local function AssertType(Object, ExpectedType, Message)
	if not IsA(Object, ExpectedType) then
		error(string.format(Message, ExpectedType, typeof(Object)), 4)
	end
end
local function AssertClass(Object, ExpectedClass, Message)
	AssertType(Object, "Instance", Message)
	if not Object:IsA(ExpectedClass) then
		error(string.format(Message, ExpectedClass, Object.Class), 4)
	end
end
local function AssertNaN(Object, Message)
	if Object ~= Object then
		error(string.format(Message, "number", typeof(Object)), 4)
	end
end
local function IsValidOwner(Value)
	local IsInstance = IsA(Value, "Instance")
	if not IsInstance and Value ~= nil then
		error("Unable to cast value to Object", 4)
	elseif IsInstance and not Value:IsA("Player") then
		error("SetOwner only takes player or 'nil' instance as an argument.", 4)
	end
end
local function IsValid(SerializedResult)
	if not IsA(SerializedResult, "table") then
		return false
	end

	return (SerializedResult.Instance ~= nil and (SerializedResult.Instance:IsA("BasePart") or SerializedResult.Instance:IsA("Terrain"))) and
		IsA(SerializedResult.Position, "Vector3") and
		IsA(SerializedResult.Material, "EnumItem") and
		IsA(SerializedResult.Normal, "Vector3")
end

local Replication = {}
local ReplicationBase = {}
ReplicationBase.__index = ReplicationBase

function ReplicationBase:Start()
	local Owner = self.Owner
	AssertClass(Owner, "Player")

	ReplicationRemote:FireClient(Owner, "Start", {
		Owner = Owner,
		Object = self.Object,
		Debug = self.Caster._Debug,
		RaycastParams = SerializeParams(self.RaycastParams),
		Id = self.Caster._UniqueId
	})

	local LocalizedConnection
	LocalizedConnection = ReplicationRemote.OnServerEvent:Connect(function(Player, CasterId, Code, RaycastResult)
		if self.Caster.Disabled or self.Caster._UniqueId ~= CasterId then
			return
		end

		if Player == Owner and IsValid(RaycastResult) and (Code == "Any" or Code == "Humanoid") then
			local Humanoid
			if Code == "Humanoid" then
				local Model = RaycastResult.Instance:FindFirstAncestorOfClass("Model")
				Humanoid = Model and Model:FindFirstChildOfClass("Humanoid")
			end

			if Code == "Humanoid" and Humanoid == nil then
				return
			end

			for Event in next, self.Caster._CollidedEvents[Code] do
				Event:Invoke(RaycastResult, Humanoid)
			end
		end
	end)

	self.Connection = LocalizedConnection
end
function ReplicationBase:Update(AdditionalData)
	local Data = {
		Owner = self.Owner,
		Object = self.Object,
		Debug = self.Caster._Debug,
		RaycastParams = SerializeParams(self.RaycastParams),
		Id = self.Caster._UniqueId
	}
	ReplicationRemote:FireClient(self.Owner, "Update", Data, AdditionalData)
end
function ReplicationBase:Stop(Destroying)
	local Owner = self.Owner

	ReplicationRemote:FireClient(Owner, Destroying and "Destroy" or "Stop", {
		Owner = Owner,
		Object = self.Object,
		Id = self.Caster._UniqueId
	})

	local ReplicationConnection = self.Connection

	if Destroying then
		table.clear(self)
		setmetatable(self, nil)

		if ReplicationConnection then
			ReplicationConnection:Disconnect()
		end
	elseif ReplicationConnection then
		task.delay(1, function()
			if self.Connection == ReplicationConnection then
				ReplicationConnection:Disconnect()
			end
		end)
	end
end
function ReplicationBase:Destroy()
	self:Stop(true)
end

function Replication.new(Player, Object, RaycastParameters, Caster)
	AssertClass(Player, "Player", "Unexpected owner in 'ReplicationBase.Stop' (%s expected, got %s)")
	assert(type(Caster) == "table" and Caster._Class == "Caster", "Unexpect argument #4 - Caster expected")

	return setmetatable({
		Owner = Player,
		Object = Object,
		RaycastParams = RaycastParameters,
		Caster = Caster
	}, ReplicationBase)
end

local ClientCaster = {}

local TrailTransparency = NumberSequence.new({
	NumberSequenceKeypoint.new(0, 0),
	NumberSequenceKeypoint.new(0.5, 0),
	NumberSequenceKeypoint.new(1, 1)
})
local AttachmentOffset = Vector3.new(0, 0, 0.1)

function ClientCaster:DisableDebug()
	local ReplicationConnection = self._ReplicationConnection

	if ReplicationConnection then
		ReplicationConnection:Update({
			Debug = false
		})
	end

	self._Debug = false
	for Trail in next, self._DebugTrails do
		Trail.Enabled = false
	end
end
function ClientCaster:StartDebug()
	local ReplicationConnection = self._ReplicationConnection

	if ReplicationConnection then
		ReplicationConnection:Update({
			Debug = true
		})
	end

	self._Debug = true
	for Trail in next, self._DebugTrails do
		Trail.Enabled = true
	end
end

local CollisionBaseName = {
	Collided = "Any",
	HumanoidCollided = "Humanoid"
}

function ClientCaster:Start()
	self.Disabled = false

	local ReplicationConn = self._ReplicationConnection
	if ReplicationConn then
		ReplicationConn:Start()
	end

	ClientCast.InitiatedCasters[self] = {}
	if self._Debug then
		self:StartDebug()
	end
end
function ClientCaster:Destroy()
	local ReplicationConn = self._ReplicationConnection
	if ReplicationConn then
		self._ReplicationConnection = nil
		ReplicationConn:Destroy()
	end

	if self._DescendantConnection then
		self._DescendantConnection:Disconnect()
		self.__DescendantConnection = nil
	end
	for _, DebugAttachment in next, self._DebugTrails do
		DebugAttachment:Destroy()
	end
	for _, EventsHolder in next, self._CollidedEvents do
		for Event in next, EventsHolder do
			Event:Destroy()
		end
	end

	ClientCast.InitiatedCasters[self] = nil

	self.RaycastParams = nil
	self.Object = nil
	self.Owner = nil
	self.Disabled = true

	for Prop, Val in next, self do
		if type(Val) == "function" then
			self[Prop] = function() end
		end
	end
end
function ClientCaster:Stop()
	local OldConn = self._ReplicationConnection
	if OldConn then
		OldConn:Stop()
	end

	ClientCast.InitiatedCasters[self] = nil
	self.Disabled = true

	local LocalizedState = self._Debug
	self:DisableDebug()
	self._Debug = LocalizedState
end
function ClientCaster:SetOwner(NewOwner)
	local Remainder = time() - self._Created
	task.spawn(function()
		if Remainder < 0.1 then
			task.wait(0.1 - Remainder)
		end

		IsValidOwner(NewOwner)
		local OldConn = self._ReplicationConnection
		local ReplConn = NewOwner ~= nil and Replication.new(NewOwner, self.Object, self.RaycastParams, self)
		self._ReplicationConnection = ReplConn

		if OldConn then
			OldConn:Destroy()
		end
		self.Owner = NewOwner

		if ClientCast.InitiatedCasters[self] then
			if NewOwner ~= nil and ReplConn then
				ReplConn:Start()
			end
		end
	end)
end
function ClientCaster:GetOwner()
	return self.Owner
end
function ClientCaster:SetMaxPingExhaustion(Time)
	AssertType(Time, "number", "Unexpected argument #1 to 'ClientCaster.SetMaxPingExhaustion' (%s expected, got %s)")
	AssertNaN(Time, "Unexpected argument #1 to 'ClientCaster.SetMaxPingExhaustion' (%s expected, got NaN)")
	if Time < 0.1 then
		error("The max ping exhaustion time passed to 'ClientCaster.SetMaxPingExhaustion' must be longer than 0.1", 3)
		return
	end

	self._ExhaustionTime = Time
end
function ClientCaster:GetMaxPingExhaustion()
	return self._ExhaustionTime
end
function ClientCaster:GetPing()
	if self.Owner == nil then
		return 0
	end

	return SafeRemoteInvoke(PingRemote, self.Owner, self._ExhaustionTime)
end
function ClientCaster:SetObject(Object)
	self.Object = Object

	for _, DebugAttachment in next, self._DebugTrails do
		DebugAttachment:Destroy()
	end
	table.clear(self._DebugTrails)
	table.clear(self._DamagePoints)

	local OldConnection = self._DescendantConnection
	if OldConnection then
		OldConnection:Disconnect()
		self._DescendantConnection = nil
	end

	if self.Owner == nil then
		for _, Descendant in next, Object:GetDescendants() do
			self._OnDamagePointAdded(Descendant)
		end
	end
	self._DescendantConnection = self.Owner == nil and Object.DescendantAdded:Connect(self._OnDamagePointAdded) or nil

	local ReplicationConnection = self._ReplicationConnection
	task.spawn(function()
		if ReplicationConnection then
			local Remainder = time() - self._Created
			if Remainder < 1 then
				task.wait(1 - Remainder)
			end

			ReplicationConnection:Update({
				Object = Object
			})
		end
	end)
end
function ClientCaster:GetObject()
	return self.Object
end
function ClientCaster:EditRaycastParams(RaycastParameters)
	self.RaycastParams = RaycastParameters
	local ReplicationConnection = self._ReplicationConnection
	if ReplicationConnection then
		local Remainder = time() - self._Created

		task.spawn(function()
			if Remainder < 1 then
				task.wait(1 - Remainder)
			end
			ReplicationConnection:Update({
				RaycastParams = RaycastParameters
			})
		end)
	end
end
function ClientCaster:SetRecursive(Bool)
	AssertType(Bool, "boolean", "Unexpected argument #1 to 'ClientCaster.SetRecursive' (%s expected, got %s)")
	self.Recursive = Bool

	local Remainder = time() - self._Created
	task.spawn(function()
		if Remainder < 0.1 then
			task.wait(0.1 - Remainder)
		end

		local ReplicationConnection = self._ReplicationConnection
		if ReplicationConnection then
			ReplicationConnection:Update({
				Recursive = Bool
			})
		end
	end)
end
function ClientCaster:__index(Index)
	local CollisionIndex = CollisionBaseName[Index]
	if CollisionIndex then
		local CollisionEvent = Signal.new()
		self._CollidedEvents[CollisionIndex][CollisionEvent] = true

		return CollisionEvent.Invoked
	end

	return rawget(ClientCaster, Index)
end
function ClientCaster:removePoints(object: Part)
	if not object then return end
	
	for _, point in ipairs(object:GetChildren()) do
		if point:IsA("Attachment") then
			if point.Name == Settings.DebugAttachmentName then
				point:Destroy()
			end
		end
	end
end

local UniqueId = 0
local function GenerateId()
	UniqueId += 1
	return UniqueId
end
function ClientCast.new(Object, RaycastParameters, NetworkOwner)
	IsValidOwner(NetworkOwner)
	AssertType(Object, "Instance", "Unexpected argument #2 to 'CastObject.new' (%s expected, got %s)")
	AssertType(RaycastParameters, "RaycastParams", "Unexpected argument #3 to 'CastObject.new' (%s expected, got %s)")
	local CasterObject

	local DebugTrails = {}
	local DamagePoints = {}

	local function OnDamagePointAdded(Attachment)
		if Attachment.ClassName == "Attachment" and Attachment.Name == Settings.AttachmentName and not DamagePoints[Attachment] then
			local DirectChild = Attachment.Parent == CasterObject.Object
			DamagePoints[Attachment] = DirectChild

			if CasterObject.Owner == nil then
				local Trail = Instance.new("Trail")
				local TrailAttachment = Instance.new("Attachment")

				TrailAttachment.Name = Settings.DebugAttachmentName
				TrailAttachment.Position = Attachment.Position - AttachmentOffset

				Trail.Color = ColorSequence.new(Settings.DebugColor)
				Trail.Enabled = CasterObject._Debug and (DirectChild or CasterObject.Recursive)
				Trail.LightEmission = 0
				Trail.Transparency = TrailTransparency
				Trail.FaceCamera = false
				Trail.Lifetime = Settings.DebugLifetime

				Trail.Attachment0 = Attachment
				Trail.Attachment1 = TrailAttachment

				Trail.Parent = TrailAttachment
				TrailAttachment.Parent = Attachment.Parent

				task.spawn(function()
					repeat
						Attachment.AncestryChanged:Wait()
					until not Attachment:IsDescendantOf(CasterObject.Object)

					TrailAttachment:Destroy()
					DebugTrails[Trail] = nil
					DamagePoints[Attachment] = nil
				end)
				DebugTrails[Trail] = TrailAttachment
			end
		end
	end
	CasterObject = setmetatable({
		RaycastParams = RaycastParameters,
		Object = Object,
		Owner = NetworkOwner,
		Disabled = true,
		Recursive = false,

		_CollidedEvents = {
			Humanoid = {},
			Any = {}
		},
		_ToClean = {},
		_Created = time(),
		_ReplicationConnection = false,
		_Debug = Settings.DebugMode,
		_ExhaustionTime = 1,
		_UniqueId = GenerateId(),
		_DamagePoints = DamagePoints,
		_DebugTrails = DebugTrails,
		_OnDamagePointAdded = OnDamagePointAdded,
		_Class = "Caster"
	}, ClientCaster)

	for _, Descendant in next, Object:GetDescendants() do
		OnDamagePointAdded(Descendant)
	end

	CasterObject._DescendantConnection = Object.DescendantAdded:Connect(OnDamagePointAdded)
	CasterObject._ReplicationConnection = NetworkOwner ~= nil and Replication.new(NetworkOwner, Object, RaycastParameters, CasterObject)
	return CasterObject
end

local function UpdateCasterEvents(Caster, RaycastResult)
	if RaycastResult then
		for CollisionEvent in next, Caster._CollidedEvents.Any do
			CollisionEvent:Invoke(RaycastResult)
		end

		local ModelAncestor = RaycastResult.Instance:FindFirstAncestorOfClass("Model")
		local Humanoid = ModelAncestor and ModelAncestor:FindFirstChildOfClass("Humanoid")
		if Humanoid then
			for HumanoidEvent in next, Caster._CollidedEvents.Humanoid do
				HumanoidEvent:Invoke(RaycastResult, Humanoid)
			end
		end
	end
end
local function UpdateAttachment(Attachment, Caster, LastPositions)
	local CurrentPosition = Attachment.WorldPosition
	local LastPosition = LastPositions[Attachment] or CurrentPosition

	if CurrentPosition ~= LastPosition then
		local RaycastResult = workspace:Raycast(LastPosition, CurrentPosition - LastPosition, Caster.RaycastParams)

		UpdateCasterEvents(Caster, RaycastResult)
	end

	LastPositions[Attachment] = CurrentPosition
end
RunService.Heartbeat:Connect(function()
	for Caster, LastPositions in next, ClientCast.InitiatedCasters do
		local CasterObject = Caster.Object

		if Caster.Owner == nil and CasterObject then
			local RecursiveCaster = Caster.Recursive

			for Attachment, DirectChild in next, Caster._DamagePoints do
				if DirectChild or RecursiveCaster then
					UpdateAttachment(Attachment, Caster, LastPositions)
				end
			end
		end
	end
end)

return ClientCast

If you know how to fix the problem, please send us the solution log!

1 Like

Replace line 115 with:

if not self.Caster or self.Caster.Disabled or self.Caster._UniqueId ~= CasterId then

However this may not solve the problem. Why is the caster value nil?

I am getting the same error. I am trying to clear the clientcast, and reinitialize it everytime a player respawns to give them a new hitbox