Unknown script error

So the scripts below are part of a trail system I have in my game the only problem is when the player first spawns in and clicks on the equip button the trail does not attach until they die. After that the script works fine. What is the issue and how can I fix it?

First script

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local trails = game.ReplicatedStorage:WaitForChild("Trails")

function saveData(plrLeaving)
	local ownedTrails = {}
	for i, trail in pairs(plrLeaving.OwnedTrails:GetChildren()) do
		table.insert(ownedTrails, trail.Name)
	end
	local success, err = pcall(function()
		ds:SetAsync("trails-" .. plrLeaving.UserId, ownedTrails)
		ds:SetAsync("Coins-" .. plrLeaving.UserId, plrLeaving.leaderstats.Coins.Value)
	end)
end

game.Players.PlayerAdded:Connect(function(plr)
	local trailsOwned = {}
	pcall(function()
		trailsOwned = ds:GetAsync("trails-" .. plr.UserId) or {}
	end)
	local ownedFolder = Instance.new("Folder")
	ownerFolder.Parent = plr
	ownedFolder.Name = "OwnedTrails"
	for i, owned in pairs(trailsOwned) do
		if trails:FindFirstChild(owned) then
			trails[owned]:Clone().Parent = ownedFolder
		end
	end
	plr.CharacterAdded:Connect(function(char)
		local hrp = char:WaitForChild("HumanoidRootPart")
		local atchTop = Instance.new("Attachment")
		atchTop.Parent = hrp
		atchTop.Name = "TrailTop"
		atchTop.Position = Vector3.new(0, 0.766, 0)
		local atchBtm = Instance.new("Attachment")
		atchBtm.Parent = hrp
		atchBtm.Name = "TrailBottom"
		atchBtm.Position = Vector3.new(0, -0.766, 0)
		if plr:FindFirstChild("LastTrail") then
			local newTrail = plr:FindFirstChild("LastTrail").Value:Clone()
			newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
			newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
			newTrail.Parent = char.HumanoidRootPart
		end
	end)
end)

game.Players.PlayerRemoving:Connect(saveData)

game:BindToClose(function()
	for i, plrLeaving in pairs(game.Players:GetPlayers()) do
		saveData(plrLeaving)
	end
end)

game.ReplicatedStorage.TrailSelectedRE.OnServerEvent:Connect(function(plr, buying, trail)
	if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins
		if price <= coins.Value then
			coins.Value -= price
			trail:Clone().Parent = plr.OwnedTrails
			local char = plr.Character
			if not char or not char:FindFirstChild("HumanoidRootPart") then return end
			for i, child in pairs(char.HumanoidRootPart:GetChildren()) do
				if child:IsA("Trail") then child:Destroy() end
			end
			local newTrail = trail:Clone()
			newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
			newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
			newTrail.Parent = char.HumanoidRootPart
		end
	elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
		if plr:FindFirstChild("LastTrail") then
			plr:FindFirstChild("LastTrail").Value = trail
		else
			local obj = Instance.new("ObjectValue")
			obj.Parent = plr
			obj.Name = "LastTrail"
			obj.Value = trail
		end
		local char = plr.Character
		if not char or not char:FindFirstChild("HumanoidRootPart") then return end
		for i, child in pairs(char.HumanoidRootPart:GetChildren()) do
			if child:IsA("Trail") then child:Destroy() end
		end
		local newTrail = trail:Clone()
		newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
		newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
		newTrail.Parent = char.HumanoidRootPart
	end
end)

Second script

local shopFrame = script.Parent:WaitForChild(“ShopFrame”)
local invFrame = script.Parent:WaitForChild(“InventoryFrame”)

local trailsFolder = game.ReplicatedStorage:WaitForChild(“Trails”)
local ownedTrailsFolder = game.Players.LocalPlayer:WaitForChild(“OwnedTrails”)

function updateInventory()

for i, child in pairs(invFrame.TrailsScroller:GetChildren()) do

	if child:IsA("ImageLabel") then child:Destroy() end
end


local ownedTrails = ownedTrailsFolder:GetChildren()

table.sort(ownedTrails, function(a, b)
	return trailsFolder[a.Name].Price.Value < trailsFolder[b.Name].Price.Value or trailsFolder[a.Name].Price.Value == trailsFolder[b.Name].Price.Value and a.Name < b.Name
end)


for i, trail in pairs(ownedTrails) do

	local item = script.Item:Clone()
	item.SelectButton.Text = "EQUIP"
	item.TrailName.Text = trail.Name
	item.Trail.UIGradient.Color = trail.Color

	item.Parent = invFrame.TrailsScroller


	if game.Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart"):FindFirstChild(trail.Name) then
		item.SelectButton.Text = "EQUIPPED"
	end


	item.SelectButton.MouseButton1Click:Connect(function()
		game.ReplicatedStorage.TrailSelectedRE:FireServer(false, trail)

		for i, itemButton in pairs(item.Parent:GetChildren()) do

			if itemButton:IsA("ImageLabel") then
				itemButton.SelectButton.Text = "EQUIP"
			end
		end
		item.SelectButton.Text = "EQUIPPED"
	end)
end


end

function updateShop()


for i, child in pairs(shopFrame.Frame1Effects.TrailsScroller:GetChildren()) do

	if child:IsA("ImageLabel") then child:Destroy() end
end


local shopTrails = trailsFolder:GetChildren()

table.sort(shopTrails, function(a, b)
	return a.Price.Value < b.Price.Value or a.Price.Value == b.Price.Value and a.Name < b.Name
end)


for i, trail in pairs(shopTrails) do

	local item = script.Item:Clone()

	item.SelectButton.Text = "BUY for " .. trail.Price.Value
	if ownedTrailsFolder:FindFirstChild(trail.Name) then item.SelectButton.Text = "Owned." end

	item.TrailName.Text = trail.Name
	item.Trail.UIGradient.Color = trail.Color

	item.Parent = shopFrame.Frame1Effects.TrailsScroller


	item.SelectButton.MouseButton1Click:Connect(function()
		game.ReplicatedStorage.TrailSelectedRE:FireServer(true, trail)
	end)
end
end

updateShop()
updateInventory()

ownedTrailsFolder.ChildAdded:Connect(function()
updateInventory(); updateShop()
end)
ownedTrailsFolder.ChildRemoved:Connect(function()
updateInventory(); updateShop()
end)

trailsFolder.ChildAdded:Connect(updateShop)
trailsFolder.ChildRemoved:Connect(updateShop)
1 Like

You have yielding code in your PlayerAdded function which will push back the time that the CharacterAdded connection is created. By the time the connection is made the player may already have a character. This is not an error, this is you not accounting for something that may potentially exist.

The problem is the same for your PlayerAdded function by the way. You may not have encountered issues with it right now but in the future you may or will. You always need to handle objects that may already exist by the time your connections are established.

PlayerAdded can be resolved by putting the code in a function that you can both connect to and run on all existing players:

local Players = game:GetService("Players")

local function playerAdded(player)
end

Players.PlayerAdded:Connect(playerAdded)
for _, player in ipairs(Players:GetPlayers()) do
    playerAdded(player)
end

The character code can be done in the same manner:

local function playerAdded(player)
    local function characterAdded(character)
    end

    player.CharacterAdded:Connect(characterAdded)
    if player.Character then
        characterAdded(player.Character)
    end
end
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local rs = game:GetService("ReplicatedStorage")
local trails = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local players = game:GetService("Players")

players.PlayerAdded:Connect(function(player)
	local trailsOwned = nil
	local ownedFolder = Instance.new("Folder")
	ownedFolder.Parent = player
	ownedFolder.Name = "OwnedTrails"
	task.spawn(function()
		pcall(function()
			trailsOwned = ds:GetAsync("trails-" .. player.UserId) or {}
		end)
	end)
	player.CharacterAdded:Connect(function(char)
		repeat task.wait()
		until type(trailsOwned) == "table"
		for _, owned in ipairs(trailsOwned) do
			if trails:FindFirstChild(owned) then
				trails[owned]:Clone().Parent = ownedFolder
			end
		end
		local hrp = char:WaitForChild("HumanoidRootPart")
		local atchTop = Instance.new("Attachment")
		atchTop.Parent = hrp
		atchTop.Name = "TrailTop"
		atchTop.Position = Vector3.new(0, 0.766, 0)
		local atchBtm = Instance.new("Attachment")
		atchBtm.Parent = hrp
		atchBtm.Name = "TrailBottom"
		atchBtm.Position = Vector3.new(0, -0.766, 0)
		if player:FindFirstChild("LastTrail") then
			local newTrail = player:FindFirstChild("LastTrail").Value:Clone()
			newTrail.Attachment0 = hrp.TrailTop
			newTrail.Attachment1 = hrp.TrailBottom
			newTrail.Parent = hrp
		end
	end)
end)

players.PlayerRemoving:Connect(function(player)
	local ownedTrails = {}
	for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
		table.insert(ownedTrails, trail.Name)
	end
	local success1, err1 = pcall(function()
		ds:SetAsync("trails-" .. player.UserId, ownedTrails)
	end)
	local success2, err2 = pcall(function()
		ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
	end)
end)

game:BindToClose(function()
	for _, player in ipairs(players:GetPlayers()) do
		local ownedTrails = {}
		for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
			table.insert(ownedTrails, trail.Name)
		end
		local success1, err1 = pcall(function()
			ds:SetAsync("trails-" .. player.UserId, ownedTrails)
		end)
		local success2, err2 = pcall(function()
			ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
		end)
	end
end)

selectedRemote.OnServerEvent:Connect(function(plr, buying, trail)
	if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins
		if price <= coins.Value then
			coins.Value -= price
			local trailClone = trail:Clone()
			trailClone.Parent = plr.OwnedTrails
			local char = plr.Character
			if not char or not char:FindFirstChild("HumanoidRootPart") then
				return
			end
			local hrp = char:WaitForChild("HumanoidRootPart")
			for _, child in ipairs(hrp:GetChildren()) do
				if child:IsA("Trail") then child:Destroy() end
			end
			local newTrail = trail:Clone()
			newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
			newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
			newTrail.Parent = char.HumanoidRootPart
		end
	elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
		if plr:FindFirstChild("LastTrail") then
			plr:FindFirstChild("LastTrail").Value = trail
		else
			local obj = Instance.new("ObjectValue")
			obj.Parent = plr
			obj.Name = "LastTrail"
			obj.Value = trail
		end
		local char = plr.Character
		if not char or not char:FindFirstChild("HumanoidRootPart") then
			return
		end
		local hrp = char:WaitForChild("HumanoidRootPart")
		for _, child in ipairs(hrp:GetChildren()) do
			if child:IsA("Trail") then child:Destroy() end
		end
		local newTrail = trail:Clone()
		newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
		newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
		newTrail.Parent = char.HumanoidRootPart
	end
end)

I think the main issue was the following typo.

local ownedFolder = Instance.new("Folder")
ownerFolder.Parent = plr --ownerFolder instead of ownedFolder here
ownedFolder.Name = "OwnedTrails"

Although I made some additional revisions and general cleaning up.

local gui = script.Parent
local shopFrame = gui:WaitForChild("ShopFrame")
local frameEffects = shopFrame:WaitForChild("Frame1Effects")
local trailsScroller2 = frameEffects:WaitForChild("TrailsScroller")
local invFrame = gui:WaitForChild("InventoryFrame")
local trailsScroller = invFrame:WaitForChild("TrailsScroller")
local players = game:GetService("Players")
local player = players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
local ownedTrailsFolder = player:WaitForChild("OwnedTrails")
local rs = game:GetService("ReplicatedStorage")
local trailsFolder = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local item = script:WaitForChild("Item")

function updateInventory()
	for _, child in ipairs(trailsScroller:GetChildren()) do
		if child:IsA("ImageLabel") then
			child:Destroy()
		end
	end
	
	local ownedTrails = ownedTrailsFolder:GetChildren()
	
	table.sort(ownedTrails, function(a, b)
		return trailsFolder[a.Name].Price.Value < trailsFolder[b.Name].Price.Value or trailsFolder[a.Name].Price.Value == trailsFolder[b.Name].Price.Value and a.Name < b.Name
	end)
	
	for _, trail in ipairs(ownedTrails) do
		local item = item:Clone()
		item.SelectButton.Text = "EQUIP"
		item.TrailName.Text = trail.Name
		item.Trail.UIGradient.Color = trail.Color
		item.Parent = invFrame.TrailsScroller
		if hrp:FindFirstChild(trail.Name) then
			item.SelectButton.Text = "EQUIPPED"
		end
		item.SelectButton.MouseButton1Click:Connect(function()
			selectedRemote:FireServer(false, trail)
			for _, itemButton in ipairs(trailsScroller:GetChildren()) do
				if itemButton:IsA("ImageLabel") then
					itemButton.SelectButton.Text = "EQUIP"
				end
			end
			item.SelectButton.Text = "EQUIPPED"
		end)
	end
end

function updateShop()
	for _, child in ipairs(trailsScroller2:GetChildren()) do
		if child:IsA("ImageLabel") then
			child:Destroy()
		end
	end
	
	local shopTrails = trailsFolder:GetChildren()
	
	table.sort(shopTrails, function(a, b)
		return a.Price.Value < b.Price.Value or a.Price.Value == b.Price.Value and a.Name < b.Name
	end)
	
	for _, trail in ipairs(shopTrails) do
		local item = item:Clone()
		item.SelectButton.Text = "BUY for " .. trail.Price.Value
		if ownedTrailsFolder:FindFirstChild(trail.Name) then
			item.SelectButton.Text = "Owned."
		end
		item.TrailName.Text = trail.Name
		item.Trail.UIGradient.Color = trail.Color
		item.Parent = shopFrame.Frame1Effects.TrailsScroller
		item.SelectButton.MouseButton1Click:Connect(function()
			selectedRemote:FireServer(true, trail)
		end)
	end
end

updateShop()
updateInventory()

ownedTrailsFolder.ChildAdded:Connect(function()
	updateInventory(); updateShop()
end)

ownedTrailsFolder.ChildRemoved:Connect(function()
	updateInventory(); updateShop()
end)

trailsFolder.ChildAdded:Connect(updateShop)
trailsFolder.ChildRemoved:Connect(updateShop)

and some revisions to the Gui handler.

1 Like

Ok thanks I’ll let you know if it works.

So it works but it is duping the trails I’m going to try and find out what the issue is.

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("DATA")
local rs = game:GetService("ReplicatedStorage")
local trails = rs:WaitForChild("Trails")
local selectedRemote = rs:WaitForChild("TrailSelectedRE")
local players = game:GetService("Players")

players.PlayerAdded:Connect(function(player)
	local trailsOwned = nil
	local ownedFolder = Instance.new("Folder")
	ownedFolder.Parent = player
	ownedFolder.Name = "OwnedTrails"
	task.spawn(function()
		pcall(function()
			trailsOwned = ds:GetAsync("trails-" .. player.UserId) or {}
		end)
		repeat task.wait()
		until type(trailsOwned) == "table"
		for _, owned in ipairs(trailsOwned) do
			if trails:FindFirstChild(owned) then
				trails[owned]:Clone().Parent = ownedFolder
			end
		end
	end)
	player.CharacterAdded:Connect(function(char)
		local hrp = char:WaitForChild("HumanoidRootPart")
		local atchTop = Instance.new("Attachment")
		atchTop.Parent = hrp
		atchTop.Name = "TrailTop"
		atchTop.Position = Vector3.new(0, 0.766, 0)
		local atchBtm = Instance.new("Attachment")
		atchBtm.Parent = hrp
		atchBtm.Name = "TrailBottom"
		atchBtm.Position = Vector3.new(0, -0.766, 0)
		if player:FindFirstChild("LastTrail") then
			local newTrail = player:FindFirstChild("LastTrail").Value:Clone()
			newTrail.Attachment0 = hrp.TrailTop
			newTrail.Attachment1 = hrp.TrailBottom
			newTrail.Parent = hrp
		end
	end)
end)

players.PlayerRemoving:Connect(function(player)
	local ownedTrails = {}
	for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
		table.insert(ownedTrails, trail.Name)
	end
	local success1, err1 = pcall(function()
		ds:SetAsync("trails-" .. player.UserId, ownedTrails)
	end)
	local success2, err2 = pcall(function()
		ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
	end)
end)

game:BindToClose(function()
	for _, player in ipairs(players:GetPlayers()) do
		local ownedTrails = {}
		for _, trail in ipairs(player.OwnedTrails:GetChildren()) do
			table.insert(ownedTrails, trail.Name)
		end
		local success1, err1 = pcall(function()
			ds:SetAsync("trails-" .. player.UserId, ownedTrails)
		end)
		local success2, err2 = pcall(function()
			ds:SetAsync("Coins-" .. player.UserId, player.leaderstats.Coins.Value)
		end)
	end
end)

selectedRemote.OnServerEvent:Connect(function(plr, buying, trail)
	if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins
		if price <= coins.Value then
			coins.Value -= price
			local trailClone = trail:Clone()
			trailClone.Parent = plr.OwnedTrails
			local char = plr.Character
			if not char or not char:FindFirstChild("HumanoidRootPart") then
				return
			end
			local hrp = char:WaitForChild("HumanoidRootPart")
			for _, child in ipairs(hrp:GetChildren()) do
				if child:IsA("Trail") then child:Destroy() end
			end
			local newTrail = trail:Clone()
			newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
			newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
			newTrail.Parent = char.HumanoidRootPart
		end
	elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
		if plr:FindFirstChild("LastTrail") then
			plr:FindFirstChild("LastTrail").Value = trail
		else
			local obj = Instance.new("ObjectValue")
			obj.Parent = plr
			obj.Name = "LastTrail"
			obj.Value = trail
		end
		local char = plr.Character
		if not char or not char:FindFirstChild("HumanoidRootPart") then
			return
		end
		local hrp = char:WaitForChild("HumanoidRootPart")
		for _, child in ipairs(hrp:GetChildren()) do
			if child:IsA("Trail") then child:Destroy() end
		end
		local newTrail = trail:Clone()
		newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
		newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom
		newTrail.Parent = char.HumanoidRootPart
	end
end)
1 Like

It works no more duping thanks homie. Quick question though is there a way for me to get rid of the dupped trails in my personal inventory or do I have to create a delete button gui?