Why might I need this?
It’s possible that this might be useful if you want to store more than one value for say a player’s items that have multiple stats that can be different for each player. For this tutorial we will be using vanilla datastore and will be storing the two items, A pickaxe and a sword. and we will be storing two properties for each one, Strength and Wear.
Define the datastore
the easiest part
Ok so first just define the datastore that we’ll be saving this to, we’ll call ours inventory. Here’s the code
game:GetService("DataStoreService"):GetDataStore("2wef9asd32a3a278") --characters datastore
Alright now that we’ve done the easy part. It’s time to move onto the hard part.
How to Save the values
Ok so first you have to define the folders that we will be saving the and writing the values to
game.Players.PlayerAdded:Connect(function(player)--when the player is added
local folder = Instance.new("Folder", player)--creates a character folder
folder.Name = "Tools"--names the folder
local savedData = nil --sets datastore to nil by default
savedData = dataStore:GetAsync(player.UserId) --sets saved data varible to the players datastore location
Ok now that we have the folders we’ll be saved to and written to lets write code to get the datastore that we write the values used in from
game.Players.PlayerAdded:Connect(function(player)--when the player is added
local folder = Instance.new("Folder", player)--creates a character folder
folder.Name = "Tools"--names the folder
local savedData = nil --sets datastore to nil by default
savedData = dataStore:GetAsync(player.UserId) --sets saved data varible to the players datastore location
if savedData ~= nil then -- if theres value in the datastores
local lastIt = Instance.new("IntValue",player)--the variable that stores the index values of the last indexed item
lastIt.Value = 1 --the default item to see if the index value is the first number that can be a item
local llaassttIItt = Instance.new("StringValue",player)--The name of the last indexed item
print("returning player")--prints that the player has been here before(Not neccessary, but nice for testing)
print(savedData) --prints datastore's value(also not neccessary, but nice for testing)
for i,v in pairs(savedData) do --gets the datastore's values
if i == lastIt.Value + 2 then --checks if the indexed value is the second indexed number from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with the strength value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Strength" --names the Value Strength
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value + 1 then--checks if the indexed value is the first indexed number away from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with a wear value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Wear" --names the Value HP
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value or i == lastIt.Value + 3 then --checks if the indexed value is the third indexed number from the character or at least the first index value that should appear
local B = Instance.new("StringValue")-- creates a value to overwrite with the name value
B.Parent = player.Tools--parents it to the Tools folder
B.Name = v --names the Value the name of the Item
B.Value = v --writes the value with the indexed value of the saved data table
lastIt.Value = i--changes the last indexed value to it's indexed value
llaassttIItt.Value = v--changes the last item value to it's value(so other values can know what it needs to be parented to)
end
print(i)
end
Testing the values
Now that we have the the values that we will write to, let’s create the values that we’ll feed to the compiler to test the saving of the value. For the sake of this example we’ll have it set to create these values if you have never played before. This can be different for all games, but since this isn’t a tutorial on a full inventory system I decided I’d use a different approach as it still teaches the main subject matter and is much easier to program. You can get full inventory tutorials elsewhere.
game.Players.PlayerAdded:Connect(function(player)--when the player is added
local folder = Instance.new("Folder", player)--creates a character folder
folder.Name = "Tools"--names the folder
local savedData = nil --sets datastore to nil by default
savedData = dataStore:GetAsync(player.UserId) --sets saved data varible to the players datastore location
if savedData ~= nil then -- if theres value in the datastores
local lastIt = Instance.new("IntValue",player)--the variable that stores the index values of the last indexed item
lastIt.Value = 1 --the default item to see if the index value is the first number that can be a item
local llaassttIItt = Instance.new("StringValue",player)--The name of the last indexed item
print("returning player")--prints that the player has been here before(Not neccessary, but nice for testing)
print(savedData) --prints datastore's value(also not neccessary, but nice for testing)
for i,v in pairs(savedData) do --gets the datastore's values
if i == lastIt.Value + 2 then --checks if the indexed value is the second indexed number from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with the strength value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Strength" --names the Value Strength
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value + 1 then--checks if the indexed value is the first indexed number away from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with a wear value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Wear" --names the Value HP
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value or i == lastIt.Value + 3 then --checks if the indexed value is the third indexed number from the character or at least the first index value that should appear
local B = Instance.new("StringValue")-- creates a value to overwrite with the name value
B.Parent = player.Tools--parents it to the Tools folder
B.Name = v --names the Value the name of the Item
B.Value = v --writes the value with the indexed value of the saved data table
lastIt.Value = i--changes the last indexed value to it's indexed value
llaassttIItt.Value = v--changes the last item value to it's value(so other values can know what it needs to be parented to)
end
print(i)
end
else --the saved data has no value
print("New Player")
local g = Instance.new("StringValue", folder)--new string value for the
g.Name = "Pickaxe"--pickaxe.
g.Value = "Pickaxe"--just adding a value to add more to further declare it as such(Not Necessary)
g.Parent = folder --(Parents it to the items folder)
local f = Instance.new("StringValue", folder)--{
f.Name = "Sword"--{Same Thing
f.Value = "Sword"--{Here as the last one but with different values
f.Parent = folder--{
local Heg = Instance.new("IntValue")--creates an intval for the property
Heg.Name = "Strength"--strength.
Heg.Parent = g--parents it to an item
Heg.Value = 10 --Arbitrary Value
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Strength"--{same thing here but it parents it to a
Hevg.Parent = f --{ different item
Hevg.Value = 6 --{
local Heg = Instance.new("IntValue")--{
Heg.Name = "Wear"--{Same thing here but
Heg.Parent = g--{a different property name
Heg.Value = 8--{
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Wear"--{
Hevg.Parent = f --{you get the gist at this point
Hevg.Value = 5--{
end
end)
Saving the Values
ok now onto the last and undeniably easier parts, but still can be quite hard which is saving the data. Now datastores can’t store folders since it can only store UTC-8 Characters so we’ll have to store values in a table then feed the values in that table to the datastore so it can put it in it’s own table to be indexed later.
game.Players.PlayerAdded:Connect(function(player)--when the player is added
local folder = Instance.new("Folder", player)--creates a character folder
folder.Name = "Tools"--names the folder
local savedData = nil --sets datastore to nil by default
savedData = dataStore:GetAsync(player.UserId) --sets saved data varible to the players datastore location
if savedData ~= nil then -- if theres value in the datastores
local lastIt = Instance.new("IntValue",player)--the variable that stores the index values of the last indexed item
lastIt.Value = 1 --the default item to see if the index value is the first number that can be a item
local llaassttIItt = Instance.new("StringValue",player)--The name of the last indexed item
print("returning player")--prints that the player has been here before(Not neccessary, but nice for testing)
print(savedData) --prints datastore's value(also not neccessary, but nice for testing)
for i,v in pairs(savedData) do --gets the datastore's values
if i == lastIt.Value + 2 then --checks if the indexed value is the second indexed number from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with the strength value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Strength" --names the Value Strength
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value + 1 then--checks if the indexed value is the first indexed number away from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with a wear value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Wear" --names the Value HP
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value or i == lastIt.Value + 3 then --checks if the indexed value is the third indexed number from the character or at least the first index value that should appear
local B = Instance.new("StringValue")-- creates a value to overwrite with the name value
B.Parent = player.Tools--parents it to the Tools folder
B.Name = v --names the Value the name of the Item
B.Value = v --writes the value with the indexed value of the saved data table
lastIt.Value = i--changes the last indexed value to it's indexed value
llaassttIItt.Value = v--changes the last item value to it's value(so other values can know what it needs to be parented to)
end
print(i)
end
else --the saved data has no value
print("New Player")
local g = Instance.new("StringValue", folder)--new string value for the
g.Name = "Pickaxe"--pickaxe.
g.Value = "Pickaxe"--just adding a value to add more to further declare it as such(Not Necessary)
g.Parent = folder --(Parents it to the items folder)
local f = Instance.new("StringValue", folder)--{
f.Name = "Sword"--{Same Thing
f.Value = "Sword"--{Here as the last one but with different values
f.Parent = folder--{
local Heg = Instance.new("IntValue")--creates an intval for the property
Heg.Name = "Strength"--strength.
Heg.Parent = g--parents it to an item
Heg.Value = 10 --Arbitrary Value
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Strength"--{same thing here but it parents it to a
Hevg.Parent = f --{ different item
Hevg.Value = 6 --{
local Heg = Instance.new("IntValue")--{
Heg.Name = "Wear"--{Same thing here but
Heg.Parent = g--{a different property name
Heg.Value = 8--{
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Wear"--{
Hevg.Parent = f --{you get the gist at this point
Hevg.Value = 5--{
end
end)
game.Players.PlayerRemoving:Connect(function(player) --when the player leaves the game
local save = {} --table to save data
for _, Child in pairs(player.Tools:GetChildren()) do --gets the tool storage folder
if Child and dataStore ~= nil then --if the child exists and the datastore has value
table.insert(save, Child.Value) --inserts the item name into the save table
table.insert(save, Child:FindFirstChild("Strength").Value) --inserts the item's strength into the save table
table.insert(save, Child:FindFirstChild("Wear").Value)--inserts the item's Wear into the save table
end
end
print(save)--prints the table(Not necessary and has no real use outside of testing)
dataStore:SetAsync(player.UserId, save) --saves the table's values into the datastore
end)
One last Thing
In order to prevent data loss from a server shutdown, we’ll just set the server to wait from fully closing for just a second while it wraps everything up. You may notice that when testing your game it’ll take a few seconds before debugging officially stops. This is fine, this is just once again the server wrapping up all of it’s unfinished business before heading out.
game.Players.PlayerAdded:Connect(function(player)--when the player is added
local folder = Instance.new("Folder", player)--creates a character folder
folder.Name = "Tools"--names the folder
local savedData = nil --sets datastore to nil by default
savedData = dataStore:GetAsync(player.UserId) --sets saved data varible to the players datastore location
if savedData ~= nil then -- if theres value in the datastores
local lastIt = Instance.new("IntValue",player)--the variable that stores the index values of the last indexed item
lastIt.Value = 1 --the default item to see if the index value is the first number that can be a item
local llaassttIItt = Instance.new("StringValue",player)--The name of the last indexed item
print("returning player")--prints that the player has been here before(Not neccessary, but nice for testing)
print(savedData) --prints datastore's value(also not neccessary, but nice for testing)
for i,v in pairs(savedData) do --gets the datastore's values
if i == lastIt.Value + 2 then --checks if the indexed value is the second indexed number from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with the strength value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Strength" --names the Value Strength
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value + 1 then--checks if the indexed value is the first indexed number away from the indexed item
local B = Instance.new("IntValue")-- creates a value to overwrite with a wear value
B.Parent = player.Tools[llaassttIItt.Value]--parents it to the last item indexed
B.Name = "Wear" --names the Value HP
B.Value = v --writes the value with the indexed value of the saved data table
elseif i == lastIt.Value or i == lastIt.Value + 3 then --checks if the indexed value is the third indexed number from the character or at least the first index value that should appear
local B = Instance.new("StringValue")-- creates a value to overwrite with the name value
B.Parent = player.Tools--parents it to the Tools folder
B.Name = v --names the Value the name of the Item
B.Value = v --writes the value with the indexed value of the saved data table
lastIt.Value = i--changes the last indexed value to it's indexed value
llaassttIItt.Value = v--changes the last item value to it's value(so other values can know what it needs to be parented to)
end
print(i)
end
else --the saved data has no value
print("New Player")
local g = Instance.new("StringValue", folder)--new string value for the
g.Name = "Pickaxe"--pickaxe.
g.Value = "Pickaxe"--just adding a value to add more to further declare it as such(Not Necessary)
g.Parent = folder --(Parents it to the items folder)
local f = Instance.new("StringValue", folder)--{
f.Name = "Sword"--{Same Thing
f.Value = "Sword"--{Here as the last one but with different values
f.Parent = folder--{
local Heg = Instance.new("IntValue")--creates an intval for the property
Heg.Name = "Strength"--strength.
Heg.Parent = g--parents it to an item
Heg.Value = 10 --Arbitrary Value
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Strength"--{same thing here but it parents it to a
Hevg.Parent = f --{ different item
Hevg.Value = 6 --{
local Heg = Instance.new("IntValue")--{
Heg.Name = "Wear"--{Same thing here but
Heg.Parent = g--{a different property name
Heg.Value = 8--{
local Hevg = Instance.new("IntValue")--{
Hevg.Name = "Wear"--{
Hevg.Parent = f --{you get the gist at this point
Hevg.Value = 5--{
end
end)
game.Players.PlayerRemoving:Connect(function(player) --when the player leaves the game
local save = {} --table to save data
for _, Child in pairs(player.Tools:GetChildren()) do --gets the tool storage folder
if Child and dataStore ~= nil then --if the child exists and the datastore has value
table.insert(save, Child.Value) --inserts the item name into the save table
table.insert(save, Child:FindFirstChild("Strength").Value) --inserts the item's strength into the save table
table.insert(save, Child:FindFirstChild("Wear").Value)--inserts the item's Wear into the save table
end
end
print(save)--prints the table(Not necessary and has no real use outside of testing)
dataStore:SetAsync(player.UserId, save) --saves the table's values into the datastore
end)
game:BindToClose(function() --when the game shuts down
for i, player in pairs(game.Players:GetPlayers())do --gets the player list
if player then --if the player exists
player:kick("The game is shutting down, please rejoin later") --kick message
end
end
wait(2)--wait to make sure the data saves
end)
Fin
And there you go let’s just test our final script out real quick
Ignore the first line that’s for a separate script, As you can see it recognizes me as someone who has no value in the data store. And when I return I’ll get…
…this…
…and this
That’s it. I hope that this tutorial helped someone I made this because I had to make a way to store a player’s character’s levels and it took forever to figure out how. It seems like there isn’t much documentation about storing multiple of a player’s Item’s properties in a datastore. Thanks to my coworker @LUMINADY1 who helped me out on getting it to work. And this is my first tutorial so I hope I did well on informing y’all on how to do this. Bye.