INTRO
It’s been three days since I started trying to make an inventory system for an Mmo-Rpg game I’m developing. After surfing the developer hub for hours on end looking for something to help create the system I’m going for, I finally picked up bits and pieces of information that finally formed into the system I have created.
How It Works
1. Items within the inventory are stored as Instance Values. The inventory folder is parented to the player. The item then has more instance values stored within it. These values are properties that can be used for sorting. Properties can be changed or added very easily.
2. When Saving the items I use UpdateAsync() instead of SetAsync(). Each item saved is a dictionary containing the name of the item and its properties. A problem I was facing was that PlayerRemoving wasn’t working but thanks to @rogeriodec_games 's solution I was able to fix it using the script he posted. https://devforum.roblox.com/t/studio-wont-correctly-complete-bindtoclose-and-playerremoving/1776593/14?u=trinitydevelepment
local CanCloseServer = false
game.Players.PlayerRemoving:Connect(function(Player)
local NewInventoryData = {}
local InventoryFolder = Player:FindFirstChild("Inventory")
for Inventory, Child in pairs(InventoryFolder:GetChildren()) do
local Item = {}
Item["ItemName"] = Child.Name
for ItemName, ItemProperty in pairs(Child:GetChildren()) do
Item[ItemProperty.Name] = ItemProperty.Value
end
table.insert(NewInventoryData, Item)
end
print(NewInventoryData)
Data:UpdateAsync(Player.UserId.."-Inventory", function(CurrentInventoryData)
if NewInventoryData == CurrentInventoryData then
return CurrentInventoryData
else
return NewInventoryData
end
end)
CanCloseServer = true
end)
game:BindToClose(function()
while RunService:IsRunning() and not CanCloseServer do
task.wait()
end
end)
3. When Loading I create the inventory folder and parent it to the player. I then load items into the folder using string values as the name holder using Instance.name instead of instance.value. then i load the properties and parent it to the name holder.
local DataStore = game:GetService("DataStoreService")
local Data = DataStore:GetDataStore("InventoryDataStore")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
game.Players.PlayerAdded:Connect(function(Player)
local InventoryFolder = Instance.new("Folder")
InventoryFolder.Parent = Player
InventoryFolder.Name = "Inventory"
local LoadInventory = Data:GetAsync(Player.UserId.."-Inventory")
if type(LoadInventory) == "table" then
for Inv, Item in pairs(LoadInventory) do
if Item["ItemName"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder
NewStringVal.Name = Item["ItemName"]
end
if Item["ItemID"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewStringVal.Name = "ItemID"
NewStringVal.Value = Item["ItemID"]
end
if Item["ItemType"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewStringVal.Name = "ItemType"
NewStringVal.Value = Item["ItemType"]
end
if Item["ItemType"] then
local NewIntVal = Instance.new("IntValue")
NewIntVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewIntVal.Name = "ItemAmount"
NewIntVal.Value = Item["ItemAmount"]
end
end
end
end)
ENTIRE DATASTORE SCRIPT
local DataStore = game:GetService("DataStoreService")
local Data = DataStore:GetDataStore("InventoryDataStore")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
game.Players.PlayerAdded:Connect(function(Player)
local InventoryFolder = Instance.new("Folder")
InventoryFolder.Parent = Player
InventoryFolder.Name = "Inventory"
local LoadInventory = Data:GetAsync(Player.UserId.."-Inventory")
if type(LoadInventory) == "table" then
for Inv, Item in pairs(LoadInventory) do
if Item["ItemName"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder
NewStringVal.Name = Item["ItemName"]
end
if Item["ItemID"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewStringVal.Name = "ItemID"
NewStringVal.Value = Item["ItemID"]
end
if Item["ItemType"] then
local NewStringVal = Instance.new("StringValue")
NewStringVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewStringVal.Name = "ItemType"
NewStringVal.Value = Item["ItemType"]
end
if Item["ItemType"] then
local NewIntVal = Instance.new("IntValue")
NewIntVal.Parent = InventoryFolder:FindFirstChild(Item["ItemName"])
NewIntVal.Name = "ItemAmount"
NewIntVal.Value = Item["ItemAmount"]
end
end
end
end)
local CanCloseServer = false
game.Players.PlayerRemoving:Connect(function(Player)
local NewInventoryData = {}
local InventoryFolder = Player:FindFirstChild("Inventory")
for Inventory, Child in pairs(InventoryFolder:GetChildren()) do
local Item = {}
Item["ItemName"] = Child.Name
for ItemName, ItemProperty in pairs(Child:GetChildren()) do
Item[ItemProperty.Name] = ItemProperty.Value
end
table.insert(NewInventoryData, Item)
end
print(NewInventoryData)
Data:UpdateAsync(Player.UserId.."-Inventory", function(CurrentInventoryData)
if NewInventoryData == CurrentInventoryData then
return CurrentInventoryData
else
return NewInventoryData
end
end)
CanCloseServer = true
end)
game:BindToClose(function()
while RunService:IsRunning() and not CanCloseServer do
task.wait()
end
end)
Unique Item Id Script
The unique item id is used using HttpService:GenerateGUID(false). When an item is added to the inventory folder it looks for the property stringval “ItemID”. If the value of ItemID is empty then it generates a unique id for it. If it isnt empty it doesn’t.
local HttpService = game:GetService("HttpService")
game.Players.PlayerAdded:Connect(function(Player)
local InventoryFolder = Player:WaitForChild("Inventory")
InventoryFolder.ChildAdded:Connect(function(Child)
if Child:IsA("StringValue") then
if Child:WaitForChild("ItemID").Value == "" then
Child.ItemID.Value = HttpService:GenerateGUID(false)
end
end
end)
end)
Both scripts are ServerScripts