Inventory/Shop Not Working

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I want to fix my games inventory/shop system.
  2. What is the issue? Include screenshots / videos if possible!
    After opening the inventory and trying to equip a tower, the game errors and the tower isn’t equipped. “SelectedTowers is not a valid member of Player ‘Players.ParadoxumDoge’”
    This system used to work when I used arrays, but when I tried implementing skins I swapped arrays to dictionaries to look like this: [“Scout”] = {[“Skin”] = “Default”} causing it to break.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I tried reading through all the scripts that could cause this but I couldn’t find anything.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Shop Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.ShopContainer.Exit
local shop = gui.Shop
local coins = gui.ShopContainer.Coins
local itemsFrame = gui.ShopContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
	if playerData.OwnedTowers[itemName] then
		return "Owned"
	else
		return "For Sale"
 	end
end
local function interactItem(itemName)
	local data = interactItemFunc:InvokeServer(itemName)
	if data then
		playerData = data
		updateItems()
	end
end
function updateItems()
	coins.Text = "$" .. playerData.Coins
	for i, tower in pairs(towers) do
		local oldButton = itemsFrame:FindFirstChild(tower.Name)
		if oldButton then
			oldButton:Destroy()
		end
		local newButton = itemsFrame.TemplateButton:Clone()
		newButton.Name = tower.Name
		newButton.TowerName.Text = tower.Name
		newButton.Image = tower.ImageAsset
		newButton.Visible = true
		newButton.LayoutOrder = tower.Price
		newButton.Parent = itemsFrame
		local status = getItemStatus(tower.Name)
		if status == "For Sale" then
			if typeof(tower.Price) == "number" then
				newButton.Status.Text = "$" .. tower.Price
			else
				newButton.Status.Text = tower.Price
			end
		elseif status == "Owned" then
			newButton.Status.Text = "Owned"
		end
		newButton.Activated:Connect(function()
			interactItem(tower.Name)
		end)
	end
end
local function toggleShop()
	gui.ShopContainer.Visible = not gui.ShopContainer.Visible
	if gui.ShopContainer.Visible then
		playerData = getDataFunc:InvokeServer()
		updateItems()
		gui.Parent.InventoryGui.InventoryContainer.Visible = false
		gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
		gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
	end
end
shop.Activated:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
Shop Server
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local StarterGui = game:GetService("StarterGui")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local database = DataStoreService:GetDataStore("databaseone")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:GetAsync(player.UserId)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
	if success then
		if not playerData then
			playerData = {
				["Coins"] = 0,
				["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
				["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
				["AvailableCodes"] = {"Release", "update1hype"},
				["ExpiredCodes"] = {},
				["RedeemedCodes"] = {}
			}
		end
		data[player.UserId] = playerData
	else
		player:Kick("Error received while loading data, please rejoin!")
	end
end
Players.PlayerAdded:Connect(LoadData)
--[[local function SaveData(player)
	if data[player.UserId] then
		local success = nil
		local playerData = nil
		local attempt = 1

		repeat
			success, playerData = pcall(function()
				return database:UpdateAsync(player.UserId, function()
					return data[player.UserId]
				end)
			end)
			attempt += 1
			if not success then
				task.wait()
			end
		until success or attempt == 3
	end
end]]
Players.PlayerRemoving:Connect(function(player)
	--SaveData(player)
	data[player.UserId] = nil
end)

game:BindToClose(function()
	if not RunService:IsStudio() then
		for index, player in pairs(Players:GetPlayers()) do
			task.spawn(function()
				--SaveData(player)
			end)
		end
	end
end)
local function getItemStatus(player, itemName)
	local playerData = data[player.UserId]
	if playerData.SelectedTowers[itemName] then
		return "Equipped"
	elseif playerData.OwnedTowers[itemName] then
		return "Owned"
	else
		return "For Sale"
	end
end

ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
	local shopItem = towers[itemName]
	local playerData = data[player.UserId]
	if shopItem and playerData then
		local status = getItemStatus(player, itemName)
		if status == "For Sale" and typeof(shopItem.Price) == "number" and shopItem.Price <= playerData.Coins then
			playerData.Coins -= shopItem.Price
			local newTable = {["Skin"] = "Default"}
			playerData.OwnedTowers[shopItem.Name] = newTable
		end
		return playerData
	else
		warn("Player/Tower data does not exist!")
	end
	return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player)
	return data[player.UserId]
end 
Inventory Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.InventoryContainer.Exit
local inventory = gui.Inventory
local limit = gui.InventoryContainer.Limit
local itemsFrame = gui.InventoryContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
	if playerData.SelectedTowers[itemName] then
		return "Equipped"
	elseif playerData.OwnedTowers[itemName] then
		return "Owned"
	else
		return "For Sale"
	end
end
local function interactItem(itemName)
	local data = interactItemFunc:InvokeServer(itemName)
	if data then
		playerData = data
		updateItems()
	end
end
local function getDictionaryLength(dictionary)
	local length = 0
	for i, key in pairs(dictionary) do
		length += 1
	end
	return length
end
function updateItems()
	local length = getDictionaryLength(playerData.SelectedTowers)
	limit.Text = length .. "/5"
	for i, tower in pairs(towers) do
		if playerData.OwnedTowers[tower.Name] then
			local oldButton = itemsFrame:FindFirstChild(tower.Name)
			if oldButton then
				oldButton:Destroy()
			end
			local newButton = itemsFrame.TemplateButton:Clone()
			newButton.Name = tower.Name
			newButton.TowerName.Text = tower.Name
			newButton.Image = tower.ImageAsset
			newButton.Visible = true
			newButton.LayoutOrder = tower.Price
			newButton.Parent = itemsFrame
			local status = getItemStatus(tower.Name)
			if status == "Equipped" then
				newButton.Status.Text = "Unequip?"
			else
				newButton.Status.Text = "Equip?"
			end
			newButton.Activated:Connect(function()
				interactItem(tower.Name)
			end)
		end
	end
end
local function toggleInventory()
	gui.InventoryContainer.Visible = not gui.InventoryContainer.Visible
	if gui.InventoryContainer.Visible then
		playerData = getDataFunc:InvokeServer()
		updateItems()
		gui.Parent.ShopGui.ShopContainer.Visible = false
		gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
		gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
	end
end
inventory.Activated:Connect(toggleInventory)
exit.Activated:Connect(toggleInventory)
Inventory Server
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local database = DataStoreService:GetDataStore("databaseone")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:GetAsync(player.UserId)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
	if success then
		if not playerData then
			playerData = {
				["Coins"] = 0,
				["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
				["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
				["AvailableCodes"] = {"Release", "update1hype"},
				["ExpiredCodes"] = {},
				["RedeemedCodes"] = {}
			}
		end
		data[player.UserId] = playerData
	else
		player:Kick("Error received while loading data, please rejoin!")
	end
end
Players.PlayerAdded:Connect(LoadData)
--[[local function SaveData(player)
	if data[player.UserId] then
		local success = nil
		local playerData = nil
		local attempt = 1

		repeat
			success, playerData = pcall(function()
				return database:UpdateAsync(player.UserId, function()
					return data[player.UserId]
				end)
			end)
			attempt += 1
			if not success then
				task.wait()
			end
		until success or attempt == 3
	end
end]]
Players.PlayerRemoving:Connect(function(player)
	--SaveData(player)
	data[player.UserId] = nil
end)

game:BindToClose(function()
	if not RunService:IsStudio() then
		for index, player in pairs(Players:GetPlayers()) do
			task.spawn(function()
				--SaveData(player)
			end)
		end
	end
end)
local function getItemStatus(player, itemName)
	local playerData = data[player.UserId]
	if playerData.SelectedTowers[itemName] then
		return "Equipped"
	elseif playerData.OwnedTowers[itemName] then
		return "Owned"
	else
		return "For Sale"
	end
end
local function getDictionaryLength(dictionary)
	local length = 0
	for i, key in pairs(dictionary) do
		length += 1
	end
	return length
end
ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
	local shopItem = towers[itemName]
	local playerData = data[player.UserId]
	if shopItem and playerData then
		local status = getItemStatus(player, itemName)
		if status == "Equipped" then
			if getDictionaryLength(playerData.SelectedTowers) > 1 then
				playerData.SelectedTowers[itemName] = nil
			end
		elseif status == "Owned" then
			local newTable = {["Skin"] = "Default"}
			playerData.OwnedTowers[shopItem.Name] = newTable
			if getDictionaryLength(player.SelectedTowers) > MAX_SELECTED_TOWERS then
				playerData.SelectedTowers[1] = nil
			end
		end
		return playerData
	else
		warn("Player/Tower data does not exist!")
	end
	return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player, code)
	local playerData = data[player.UserId]
	if table.find(playerData.AvailableCodes, code) then
		local codeToRemove = table.find(playerData.AvailableCodes, code)
		table.remove(playerData.AvailableCodes, codeToRemove)
		table.insert(playerData.RedeemedCodes, code)
		if code == "Release" then
			playerData.Coins += 1000
		elseif code == "update1hype" then
			playerData.Coins += 1000
		end
		return "Successfully Redeemed"
	elseif table.find(playerData.ExpiredCodes, code) then
		return "Expired"
	elseif table.find(playerData.RedeemedCodes, code) then
		return "Already Redeemed"
	elseif not code then
		return playerData
	else
		return "Invalid"
	end
end
Players.PlayerAdded:Connect(function(player)
	if MarketplaceService:UserOwnsGamePassAsync(player.UserId, 117471311) then
		task.wait(2)
		local playerData = data[player.UserId]
		if playerData.OwnedTowers["Agent"] == nil then
			local newTable = {["Skin"] = "Default"}
			playerData.OwnedTowers["Agent"] = newTable
		end
	end
end)
Code Client (Code Server is inventory)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local codeFrame = script.Parent.CodeFrame
local codeButton = script.Parent.Codes
local exitButton = codeFrame.Exit
local codeBox = codeFrame.CodeBox
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local playerData = {}
codeButton.Activated:Connect(function()
	codeFrame.Visible = not codeFrame.Visible
	script.Parent.Parent.InventoryGui.InventoryContainer.Visible = false
	script.Parent.UpdateLogFrame.Visible = false
	script.Parent.Parent.ShopGui.ShopContainer.Visible = false
end)
exitButton.Activated:Connect(function()
	codeFrame.Visible = false
end)
codeBox.FocusLost:Connect(function()
	local status = getDataFunc:InvokeServer(codeBox.Text)
	if typeof(status) == "string" then
		if status == "Already Redeemed" then
			codeBox.Text = "Code is already redeemed!"
			task.wait(1)
			codeBox.Text = "Enter code here"
		elseif status == "Invalid" then
			codeBox.Text = "Code is invalid!"
			task.wait(1)
			codeBox.Text = "Enter code here"
		elseif status == "Expired" then
			codeBox.Text = "Code is expired!"
			task.wait(1)
			codeBox.Text = "Enter code here"
		else
			codeBox.Text = "Code successfully redeemed!"
			task.wait(1)
			codeBox.Text = "Enter code here"
		end
	end
end)

(this is my first time posting on devforum; please tell me if I did anything wrong)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.You can write your topic however you want, but you need to answer these questions:

What do you want to achieve? Keep it simple and clear!
I want to fix my games inventory/shop system.

What is the issue? Include screenshots / videos if possible!
After opening the inventory and trying to equip a tower, the game errors and the tower isn’t equipped. “SelectedTowers is not a valid member of Player ‘Players.ParadoxumDoge’”
This system used to work when I used arrays, but when I tried implementing skins I swapped arrays to dictionaries to look like this: [“Scout”] = {[“Skin”] = “Default”} causing it to break.
Screenshot (1940)
Screenshot (1940)
1366×768 95 KB

Screenshot (1939)
Screenshot (1939)
1366×768 92 KB

Screenshot (1938)
Screenshot (1938)
1366×768 94.7 KB

Screenshot (1937)
Screenshot (1937)
1366×768 89 KB
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I tried reading through all the scripts that could cause this but I couldn’t find anything.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Shop Client
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local interactItemFunc = ReplicatedStorage:WaitForChild(“InteractItem”)
local gui = script.Parent
local exit = gui.ShopContainer.Exit
local shop = gui.Shop
local coins = gui.ShopContainer.Coins
local itemsFrame = gui.ShopContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
function updateItems()
coins.Text = “$” … playerData.Coins
for i, tower in pairs(towers) do
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == “For Sale” then
if typeof(tower.Price) == “number” then
newButton.Status.Text = “$” … tower.Price
else
newButton.Status.Text = tower.Price
end
elseif status == “Owned” then
newButton.Status.Text = “Owned”
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
local function toggleShop()
gui.ShopContainer.Visible = not gui.ShopContainer.Visible
if gui.ShopContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.InventoryGui.InventoryContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
shop.Activated:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
Shop Server
local MarketplaceService = game:GetService(“MarketplaceService”)
local DataStoreService = game:GetService(“DataStoreService”)
local StarterGui = game:GetService(“StarterGui”)
local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local RunService = game:GetService(“RunService”)
local database = DataStoreService:GetDataStore(“databaseone”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1

repeat
	success, playerData = pcall(function()
		return database:GetAsync(player.UserId)
	end)
	attempt += 1
	if not success then
		task.wait()
	end
until success or attempt == 3
if success then
	if not playerData then
		playerData = {
			["Coins"] = 0,
			["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["AvailableCodes"] = {"Release", "update1hype"},
			["ExpiredCodes"] = {},
			["RedeemedCodes"] = {}
		}
	end
	data[player.UserId] = playerData
else
	player:Kick("Error received while loading data, please rejoin!")
end

end
Players.PlayerAdded:Connect(LoadData)
–[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:UpdateAsync(player.UserId, function()
				return data[player.UserId]
			end)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
end

end]]
Players.PlayerRemoving:Connect(function(player)
–SaveData(player)
data[player.UserId] = nil
end)

game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
–SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end

ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == “For Sale” and typeof(shopItem.Price) == “number” and shopItem.Price <= playerData.Coins then
playerData.Coins -= shopItem.Price
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[shopItem.Name] = newTable
end
return playerData
else
warn(“Player/Tower data does not exist!”)
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player)
return data[player.UserId]
end
Inventory Client
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local interactItemFunc = ReplicatedStorage:WaitForChild(“InteractItem”)
local gui = script.Parent
local exit = gui.InventoryContainer.Exit
local inventory = gui.Inventory
local limit = gui.InventoryContainer.Limit
local itemsFrame = gui.InventoryContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
function updateItems()
local length = getDictionaryLength(playerData.SelectedTowers)
limit.Text = length … “/5”
for i, tower in pairs(towers) do
if playerData.OwnedTowers[tower.Name] then
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == “Equipped” then
newButton.Status.Text = “Unequip?”
else
newButton.Status.Text = “Equip?”
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
end
local function toggleInventory()
gui.InventoryContainer.Visible = not gui.InventoryContainer.Visible
if gui.InventoryContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.ShopGui.ShopContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
inventory.Activated:Connect(toggleInventory)
exit.Activated:Connect(toggleInventory)
Inventory Server
local MarketplaceService = game:GetService(“MarketplaceService”)
local DataStoreService = game:GetService(“DataStoreService”)
local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local RunService = game:GetService(“RunService”)
local database = DataStoreService:GetDataStore(“databaseone”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1

repeat
	success, playerData = pcall(function()
		return database:GetAsync(player.UserId)
	end)
	attempt += 1
	if not success then
		task.wait()
	end
until success or attempt == 3
if success then
	if not playerData then
		playerData = {
			["Coins"] = 0,
			["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["AvailableCodes"] = {"Release", "update1hype"},
			["ExpiredCodes"] = {},
			["RedeemedCodes"] = {}
		}
	end
	data[player.UserId] = playerData
else
	player:Kick("Error received while loading data, please rejoin!")
end

end
Players.PlayerAdded:Connect(LoadData)
–[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:UpdateAsync(player.UserId, function()
				return data[player.UserId]
			end)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
end

end]]
Players.PlayerRemoving:Connect(function(player)
–SaveData(player)
data[player.UserId] = nil
end)

game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
–SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == “Equipped” then
if getDictionaryLength(playerData.SelectedTowers) > 1 then
playerData.SelectedTowers[itemName] = nil
end
elseif status == “Owned” then
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[shopItem.Name] = newTable
if getDictionaryLength(player.SelectedTowers) > MAX_SELECTED_TOWERS then
playerData.SelectedTowers[1] = nil
end
end
return playerData
else
warn(“Player/Tower data does not exist!”)
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player, code)
local playerData = data[player.UserId]
if table.find(playerData.AvailableCodes, code) then
local codeToRemove = table.find(playerData.AvailableCodes, code)
table.remove(playerData.AvailableCodes, codeToRemove)
table.insert(playerData.RedeemedCodes, code)
if code == “Release” then
playerData.Coins += 1000
elseif code == “update1hype” then
playerData.Coins += 1000
end
return “Successfully Redeemed”
elseif table.find(playerData.ExpiredCodes, code) then
return “Expired”
elseif table.find(playerData.RedeemedCodes, code) then
return “Already Redeemed”
elseif not code then
return playerData
else
return “Invalid”
end
end
Players.PlayerAdded:Connect(function(player)
if MarketplaceService:UserOwnsGamePassAsync(player.UserId, 117471311) then
task.wait(2)
local playerData = data[player.UserId]
if playerData.OwnedTowers[“Agent”] == nil then
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[“Agent”] = newTable
end
end
end)
Code Client (Code Server is inventory)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local codeFrame = script.Parent.CodeFrame
local codeButton = script.Parent.Codes
local exitButton = codeFrame.Exit
local codeBox = codeFrame.CodeBox
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local playerData = {}
codeButton.Activated:Connect(function()
codeFrame.Visible = not codeFrame.Visible
script.Parent.Parent.InventoryGui.InventoryContainer.Visible = false
script.Parent.UpdateLogFrame.Visible = false
script.Parent.Parent.ShopGui.ShopContainer.Visible = false
end)
exitButton.Activated:Connect(function()
codeFrame.Visible = false
end)
codeBox.FocusLost:Connect(function()
local status = getDataFunc:InvokeServer(codeBox.Text)
if typeof(status) == “string” then
if status == “Already Redeemed” then
codeBox.Text = “Code is already redeemed!”
task.wait(1)
codeBox.Text = “Enter code here”
elseif status == “Invalid” then
codeBox.Text = “Code is invalid!”
task.wait(1)
codeBox.Text = “Enter code here”
elseif status == “Expired” then
codeBox.Text = “Code is expired!”
task.wait(1)
codeBox.Text = “Enter code here”
else
codeBox.Text = “Code successfully redeemed!”
task.wait(1)
codeBox.Text = “Enter code here”
end
end
end)
(this is my first time posting on devforum; please tell me if I did anything wrong)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different categoryYou can write your topic however you want, but you need to answer these questions:

What do you want to achieve? Keep it simple and clear!
I want to fix my games inventory/shop system.

What is the issue? Include screenshots / videos if possible!
After opening the inventory and trying to equip a tower, the game errors and the tower isn’t equipped. “SelectedTowers is not a valid member of Player ‘Players.ParadoxumDoge’”
This system used to work when I used arrays, but when I tried implementing skins I swapped arrays to dictionaries to look like this: [“Scout”] = {[“Skin”] = “Default”} causing it to break.

What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I tried reading through all the scripts that could cause this but I couldn’t find anything.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Shop Client
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local interactItemFunc = ReplicatedStorage:WaitForChild(“InteractItem”)
local gui = script.Parent
local exit = gui.ShopContainer.Exit
local shop = gui.Shop
local coins = gui.ShopContainer.Coins
local itemsFrame = gui.ShopContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
function updateItems()
coins.Text = “$” … playerData.Coins
for i, tower in pairs(towers) do
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == “For Sale” then
if typeof(tower.Price) == “number” then
newButton.Status.Text = “$” … tower.Price
else
newButton.Status.Text = tower.Price
end
elseif status == “Owned” then
newButton.Status.Text = “Owned”
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
local function toggleShop()
gui.ShopContainer.Visible = not gui.ShopContainer.Visible
if gui.ShopContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.InventoryGui.InventoryContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
shop.Activated:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
Shop Server
local MarketplaceService = game:GetService(“MarketplaceService”)
local DataStoreService = game:GetService(“DataStoreService”)
local StarterGui = game:GetService(“StarterGui”)
local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local RunService = game:GetService(“RunService”)
local database = DataStoreService:GetDataStore(“databaseone”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1

repeat
	success, playerData = pcall(function()
		return database:GetAsync(player.UserId)
	end)
	attempt += 1
	if not success then
		task.wait()
	end
until success or attempt == 3
if success then
	if not playerData then
		playerData = {
			["Coins"] = 0,
			["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["AvailableCodes"] = {"Release", "update1hype"},
			["ExpiredCodes"] = {},
			["RedeemedCodes"] = {}
		}
	end
	data[player.UserId] = playerData
else
	player:Kick("Error received while loading data, please rejoin!")
end

end
Players.PlayerAdded:Connect(LoadData)
–[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:UpdateAsync(player.UserId, function()
				return data[player.UserId]
			end)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
end

end]]
Players.PlayerRemoving:Connect(function(player)
–SaveData(player)
data[player.UserId] = nil
end)

game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
–SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end

ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == “For Sale” and typeof(shopItem.Price) == “number” and shopItem.Price <= playerData.Coins then
playerData.Coins -= shopItem.Price
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[shopItem.Name] = newTable
end
return playerData
else
warn(“Player/Tower data does not exist!”)
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player)
return data[player.UserId]
end
Inventory Client
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local interactItemFunc = ReplicatedStorage:WaitForChild(“InteractItem”)
local gui = script.Parent
local exit = gui.InventoryContainer.Exit
local inventory = gui.Inventory
local limit = gui.InventoryContainer.Limit
local itemsFrame = gui.InventoryContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
function updateItems()
local length = getDictionaryLength(playerData.SelectedTowers)
limit.Text = length … “/5”
for i, tower in pairs(towers) do
if playerData.OwnedTowers[tower.Name] then
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == “Equipped” then
newButton.Status.Text = “Unequip?”
else
newButton.Status.Text = “Equip?”
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
end
local function toggleInventory()
gui.InventoryContainer.Visible = not gui.InventoryContainer.Visible
if gui.InventoryContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.ShopGui.ShopContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
inventory.Activated:Connect(toggleInventory)
exit.Activated:Connect(toggleInventory)
Inventory Server
local MarketplaceService = game:GetService(“MarketplaceService”)
local DataStoreService = game:GetService(“DataStoreService”)
local Players = game:GetService(“Players”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local RunService = game:GetService(“RunService”)
local database = DataStoreService:GetDataStore(“databaseone”)
local towers = require(ReplicatedStorage:WaitForChild(“TowerShop”))

local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1

repeat
	success, playerData = pcall(function()
		return database:GetAsync(player.UserId)
	end)
	attempt += 1
	if not success then
		task.wait()
	end
until success or attempt == 3
if success then
	if not playerData then
		playerData = {
			["Coins"] = 0,
			["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
			["AvailableCodes"] = {"Release", "update1hype"},
			["ExpiredCodes"] = {},
			["RedeemedCodes"] = {}
		}
	end
	data[player.UserId] = playerData
else
	player:Kick("Error received while loading data, please rejoin!")
end

end
Players.PlayerAdded:Connect(LoadData)
–[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:UpdateAsync(player.UserId, function()
				return data[player.UserId]
			end)
		end)
		attempt += 1
		if not success then
			task.wait()
		end
	until success or attempt == 3
end

end]]
Players.PlayerRemoving:Connect(function(player)
–SaveData(player)
data[player.UserId] = nil
end)

game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
–SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return “Equipped”
elseif playerData.OwnedTowers[itemName] then
return “Owned”
else
return “For Sale”
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == “Equipped” then
if getDictionaryLength(playerData.SelectedTowers) > 1 then
playerData.SelectedTowers[itemName] = nil
end
elseif status == “Owned” then
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[shopItem.Name] = newTable
if getDictionaryLength(player.SelectedTowers) > MAX_SELECTED_TOWERS then
playerData.SelectedTowers[1] = nil
end
end
return playerData
else
warn(“Player/Tower data does not exist!”)
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player, code)
local playerData = data[player.UserId]
if table.find(playerData.AvailableCodes, code) then
local codeToRemove = table.find(playerData.AvailableCodes, code)
table.remove(playerData.AvailableCodes, codeToRemove)
table.insert(playerData.RedeemedCodes, code)
if code == “Release” then
playerData.Coins += 1000
elseif code == “update1hype” then
playerData.Coins += 1000
end
return “Successfully Redeemed”
elseif table.find(playerData.ExpiredCodes, code) then
return “Expired”
elseif table.find(playerData.RedeemedCodes, code) then
return “Already Redeemed”
elseif not code then
return playerData
else
return “Invalid”
end
end
Players.PlayerAdded:Connect(function(player)
if MarketplaceService:UserOwnsGamePassAsync(player.UserId, 117471311) then
task.wait(2)
local playerData = data[player.UserId]
if playerData.OwnedTowers[“Agent”] == nil then
local newTable = {[“Skin”] = “Default”}
playerData.OwnedTowers[“Agent”] = newTable
end
end
end)
Code Client (Code Server is inventory)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local codeFrame = script.Parent.CodeFrame
local codeButton = script.Parent.Codes
local exitButton = codeFrame.Exit
local codeBox = codeFrame.CodeBox
local getDataFunc = ReplicatedStorage:WaitForChild(“GetData”)
local playerData = {}
codeButton.Activated:Connect(function()
codeFrame.Visible = not codeFrame.Visible
script.Parent.Parent.InventoryGui.InventoryContainer.Visible = false
script.Parent.UpdateLogFrame.Visible = false
script.Parent.Parent.ShopGui.ShopContainer.Visible = false
end)
exitButton.Activated:Connect(function()
codeFrame.Visible = false
end)
codeBox.FocusLost:Connect(function()
local status = getDataFunc:InvokeServer(codeBox.Text)
if typeof(status) == “string” then
if status == “Already Redeemed” then
codeBox.Text = “Code is already redeemed!”
task.wait(1)
codeBox.Text = “Enter code here”
elseif status == “Invalid” then
codeBox.Text = “Code is invalid!”
task.wait(1)
codeBox.Text = “Enter code here”
elseif status == “Expired” then
codeBox.Text = “Code is expired!”
task.wait(1)
codeBox.Text = “Enter code here”
else
codeBox.Text = “Code successfully redeemed!”
task.wait(1)ree
codeBox.Text = “Enter code here”
end
end
end)
(this is my first time posting on devforum; please tell me if I did anything wrong)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

1 Like

edit:
format messed up because i pasted it sorry