I am currently working on a system where players can use multiple different characters that can be levelled up. When they unlock the character, the character itself is a StringValue inside of a folder in the player, I also add IntValues inside the StringValue, the current problem is that I only know how to save the StringValue and not the IntValues inside the StringValue. And every time the player rejoins, the IntValues inside the StringValue are no longer there.
-- How I set up the StringValues and IntValues
local vTable = gameinfomodule.Characters[character]
local characterValue = Instance.new("StringValue", plr.Inventory)
characterValue.Name = character
local level = Instance.new("IntValue", characterValue)
level.Name = "Level"
level.Value = 1
local Xp = Instance.new("IntValue", level)
Xp.Name = "XP"
Xp.Value = 0
local MaxXp = Instance.new("IntValue", level)
MaxXp.Name = "MaxXP"
MaxXp.Value = 20
local dmg = Instance.new("IntValue", characterValue)
dmg.Name = "Damage"
dmg.Value = vTable.BaseDmg
local Hp = Instance.new("IntValue", characterValue)
Hp.Name = "HP"
Hp.Value = vTable.BaseHP
local Special = Instance.new("IntValue", characterValue)
Special.Name = "SpecialDmg"
Special.Value = vTable.BaseSpecial
-- DataStore that I use
local datastoreservice = game:GetService("DataStoreService")
local datastore = datastoreservice:GetDataStore("DataStore")
game.Players.PlayerAdded:Connect(function(player)
local inventory = Instance.new("Folder")
inventory.Name = "Inventory"
inventory.Parent = player
local data
local success,errorMsg = pcall(function()
data = datastore:GetAsync(player.UserId)
end)
if data.inventory then
for i, v in pairs(data.inventory) do
local val = Instance.new("StringValue")
val.Name = v
val.Parent = inventory
end
end
end)
game.Players.PlayerRemoving:Connect(function(player)
local data = {}
data.inventory = {}
for i, v in pairs(player.Inventory:GetChildren()) do
table.insert(data.inventory, v.Name)
end
local success,errorMsg = pcall(function()
datastore:SetAsync(player.UserId,data)
end)
if errorMsg then
print("Error found while trying to save data (are roblox datastore servers down?)"..errorMsg)
end
end)
That isn’t the issue, I am trying to save IntValues within a StringValue. The StringValue saves just fine, please read the full post before giving me a reply. (I took some parts of my script out, that is why the table isn’t there.)
local items = folder
local data = { items = {} }
for index, item : StringValue in ipairs(items:GetChildren()) do
data.items[item.Name] = {}
for _, int : IntValue in ipairs(item:GetChildren()) do
data[int.Name] = int.Value
end
end
Here’s a basic, albeit inefficient, serializer and deserializer for StringValues, BoolValues, and NumberValues. If you want to save more complex datatypes like CFrames, Vector3s, Color3s, etc. you’ll need something more advanced than this since those can’t be saved to DataStore, but this should serve your purposes just fine.
type SerializedValueBase = {Name: string, Value: string | number | boolean, Children: {SerializedValueBase}}
local function serializeValueBaseRecursive(vb: StringValue | NumberValue | BoolValue): SerializedValueBase
local t: SerializedValueBase = {
Name = vb.Name;
Value = vb.Value;
Children = {}
}
for _, child: Instance in pairs(vb:GetChildren()) do
if child:IsA("StringValue") or child:IsA("NumberValue") or child:IsA("BoolValue") then
table.insert(t.Children, serializeValueBaseRecursive(child))
end
end
return t
end
local function deserializeValueBaseRecursive(serialized: SerializedValueBase): Instance
local instance: Instance
local name: string, value: any, children: {SerializedValueBase} = serialized.Name, serialized.Value, serialized.Children
if typeof(value) == "string" then
instance = Instance.new("StringValue")
elseif typeof(value) == "number" then
instance = Instance.new("NumberValue")
elseif typeof(value) == "boolean" then
instance = Instance.new("BoolValue")
end
instance.Value = value
for _, childData: SerializedValueBase in pairs(children) do
deserializeValueBaseRecursive(childData).Parent = instance
end
return instance
end
local players = game:GetService("Players")
local dataStores = game:GetService("DataStoreService")
local dataStore = dataStores:GetDataStore("DataStore")
local protectedCall = pcall
local typeCheck = type
local function deserializeData(object, data)
for _, value in ipairs(data) do
local instance = Instance.new(value[2])
instance.Name = value[1]
instance.Value = value[3]
instance.Parent = object:FindFirstChild(value[4], true)
end
end
players.PlayerAdded:Connect(function(player)
local inventory = Instance.new("Folder")
inventory.Name = "Inventory"
inventory.Parent = player
local success, result = protectedCall(function()
return dataStore:GetAsync(player.UserId)
end)
if success then
if result then
if typeCheck(result) == "table" then
deserializeData(player, result)
end
end
else
warn(result)
end
end)
local function serializeDataRecursive(object, data)
for index, stat in ipairs(object:GetChildren()) do
if stat:IsA("ValueBase") then
table.insert(data, {stat.Name, stat.ClassName, stat.Value, stat.Parent.Name})
if stat:FindFirstChildWhichIsA("ValueBase") then
serializeDataRecursive(stat, data)
end
end
end
return data
end
players.PlayerRemoving:Connect(function(player)
local data = serializeDataRecursive(player.Inventory, {})
local success, result = protectedCall(function()
return dataStore:SetAsync(player.UserId, data)
end)
if success then
if result then
print(result)
end
else
warn(result)
end
end)
game:BindToClose(function()
for _, player in ipairs(players:GetPlayers()) do
local data = serializeDataRecursive(player.Inventory, {})
local success, result = protectedCall(function()
return dataStore:SetAsync(player.UserId, data)
end)
if success then
if result then
print(result)
end
else
warn(result)
end
end
end)
I’ve tested this with some random value instances (including the ones you provided) and it works, it stores the type of value instance, the name, its value and its parent (where it should be placed).
One other thing which is unrelated, in the way you’re setting up stats, change this.
local characterValue = Instance.new("StringValue", plr.Inventory)
characterValue.Name = character
To this.
local characterValue = Instance.new("StringValue", plr.Inventory)
characterValue.Name = character.Name
It’s a “StringValue” instance not an “ObjectValue” instance (unless “character” is some reference to a string value, in which case you can ignore this).