How could I rewrite this to save and load string values that go from 1 to 64 so I can add stackable items to my game (I am very new to datastores)
`–> Services
local CollectionService = game:GetService(“CollectionService”)
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local DataStoreService = game:GetService(“DataStoreService”)
local ServerStorage = game:GetService(“ServerStorage”)
local RunService = game:GetService(“RunService”)
local Players = game:GetService(“Players”)
→ Dependencies
local GameConfig = require(ReplicatedStorage.GameConfig)
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
→ Variables
local UserData = DataStoreService:GetDataStore(“UserData”)
local SameKeyCooldown = {}
local PlayerData = Instance.new(“Configuration”)
PlayerData.Name = “PlayerData”
PlayerData.Parent = ReplicatedStorage
local _warn = warn
local function warn(warning: string)
_warn("DataManager Failure: "… warning)
end
– Clamp the passed ValueObject (must be int/number) to the given bounds.
local function ClampStat(ValueObject: IntValue, Min: number?, Max: number?)
ValueObject.Changed:Connect(function()
ValueObject.Value = math.clamp(ValueObject.Value, Min or -math.huge, Max or math.huge)
end)
end
– Create and return a stat value object, used for player stats
– ClampInfo is an optional table for clamping it to a min/max. If you don’t need it, simply don’t include it.
local function CreateStat(ClassName: string, Name: string, DefaultValue, ClampInfo)
local Stat = Instance.new(ClassName)
Stat.Name = Name
Stat.Value = DefaultValue
if ClampInfo and (Stat:IsA(“NumberValue”) or Stat:IsA(“IntValue”)) then
ClampStat(Stat, unpack(ClampInfo))
end
return Stat
end
local function CreateDataFolder(Player): Instance
local pData = Instance.new(“Configuration”)
pData.Name = Player.UserId
-- Stats
local Stats = Instance.new("Folder")
Stats.Name = "Stats"
Stats.Parent = pData
local Level = CreateStat("NumberValue", "Level", 1, {1, GameConfig.MaxLevel})
Level.Parent = Stats
local XP = CreateStat("NumberValue", "XP", 0, {0})
XP.Parent = Stats
local Gold = CreateStat("NumberValue", "Gold", 0, {0})
Gold.Parent = Stats
-- Items
local Items = Instance.new("Folder")
Items.Name = "Items"
Items.Parent = pData
local Tool = Instance.new("Folder")
Tool.Name = "Tool"
Tool.Parent = Items
local Armor = Instance.new("Folder")
Armor.Name = "Armor"
Armor.Parent = Items
-- Preferences / Misc
local ActiveArmor = CreateStat("StringValue", "ActiveArmor", "")
ActiveArmor.Parent = pData
local Hotbar = Instance.new("Folder")
Hotbar.Name = "Hotbar"
Hotbar.Parent = pData
for n = 1, 9 do
local ValueObject = CreateStat("StringValue", tostring(n), "")
ValueObject.Parent = Hotbar
end
return pData
end
– Unloads loaded user data table into their game session
local function UnloadData(Player: Player, Data: any, pData: Instance)
– Stats
local Stats = pData:FindFirstChild(“Stats”)
for StatName, StatValue in Data.Stats do
local Stat = Stats:FindFirstChild(StatName)
if Stat then
Stat.Value = StatValue
end
end
local Items = pData:FindFirstChild("Items")
-- Tool
local ToolFolder = Items:FindFirstChild("Tool")
for _, ItemName in Data.Items.Tool do
local Tool = ContentLibrary.Tool[ItemName]
if Tool then
Tool.Instance:Clone().Parent = Player:WaitForChild("StarterGear")
Tool.Instance:Clone().Parent = Player:WaitForChild("Backpack")
CreateStat("BoolValue", ItemName).Parent = ToolFolder
end
end
-- Armor
local ArmorFolder = Items:FindFirstChild("Armor")
for _, ItemName in Data.Items.Armor do
local Armor = ContentLibrary.Armor[ItemName]
if Armor then
CreateStat("BoolValue", ItemName).Parent = ArmorFolder
end
end
-- Preferences / Misc
(pData:FindFirstChild("ActiveArmor") :: StringValue).Value = Data.ActiveArmor
local HotbarFolder = pData:FindFirstChild("Hotbar")
for SlotNumber, ItemName in Data.Hotbar do
(HotbarFolder:FindFirstChild(SlotNumber) :: StringValue).Value = ItemName
end
end
– Attempt to save user data. Returns whether or not the request was successful.
local function SaveData(Player: Player): boolean
if not Player:GetAttribute(“DataLoaded”) then
return false
end
local pData = PlayerData:FindFirstChild(Player.UserId)
local StarterGear = Player:FindFirstChild("StarterGear")
if not pData or not StarterGear then
return false
end
-- Same Key Cooldown (can't write to the same key within 6 seconds)
if SameKeyCooldown[Player.UserId] then
repeat task.wait() until not SameKeyCooldown[Player.UserId]
end
SameKeyCooldown[Player.UserId] = true
task.delay(6, function()
SameKeyCooldown[Player.UserId] = nil
end)
-- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync --
local DataToSave = {}
DataToSave.Stats = {}
DataToSave.Items = {
Tool = {},
Armor = {}
}
-- Stats
local Stats = pData:FindFirstChild("Stats")
for _, ValueObject in Stats:GetChildren() do
DataToSave.Stats[ValueObject.Name] = ValueObject.Value
end
local Items = pData:FindFirstChild("Items")
-- Tool
local Tool = Items:FindFirstChild("Tool")
for _, ValueObject in Tool:GetChildren() do
if ContentLibrary.Tool[ValueObject.Name] then
table.insert(DataToSave.Items.Tool, ValueObject.Name)
end
end
-- Armor
local Armor = Items:FindFirstChild("Armor")
for _, ValueObject in Armor:GetChildren() do
if ContentLibrary.Armor[ValueObject.Name] then
table.insert(DataToSave.Items.Armor, ValueObject.Name)
end
end
-- Preferences / Misc
DataToSave.ActiveArmor = pData.ActiveArmor.Value
DataToSave.Hotbar = {}
for _, ValueObject in pData.Hotbar:GetChildren() do
DataToSave.Hotbar[ValueObject.Name] = ValueObject.Value
end
-- Save to DataStore --
local Success
for i = 1, 3 do
Success = xpcall(function()
return UserData:SetAsync("user/".. Player.UserId, DataToSave, {Player.UserId})
end, warn)
if Success then
break
end
task.wait(6)
end
if Success then
print(("DataManager: User %s's data saved successfully."):format(Player.Name))
else
warn(("DataManager: User %s's data failed to save."):format(Player.Name))
end
return Success
end
– Attempt to load user data. Returns whether or not the request was successful, as well as the data if it was.
local function LoadData(Player: Player): (boolean, any)
local Success, Response = xpcall(function()
return UserData:GetAsync(“user/”… Player.UserId)
end, warn)
if Success and Response then
print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level))
else
print(("DataManager: User %s had no data to load from."):format(Player.Name))
end
return Success, Response
end
local function OnPlayerAdded(Player: Player)
local Success, Data = LoadData(Player)
if not Success then
CollectionService:AddTag(Player, "DataFailed")
Player:Kick("Data unable to load. DataStore Service may be down. Please rejoin later.")
return
end
local pData = CreateDataFolder(Player)
if Data then
UnloadData(Player, Data, pData)
end
pData.Parent = PlayerData
Player:SetAttribute("DataLoaded", true)
end
Players.PlayerAdded:Connect(OnPlayerAdded)
for _, Player in Players:GetPlayers() do
OnPlayerAdded(Player)
end
– Save on leave
Players.PlayerRemoving:Connect(function(Player)
SaveData(Player)
end)
– Server closing (save)
game:BindToClose(function()
task.wait(RunService:IsStudio() and 1 or 10)
end)
– Auto-save
while true do
task.wait(60)
for _, Player in Players:GetPlayers() do
task.defer(SaveData, Player)
end
end`