Introduction
What is a Instance based datastore?
A Instance based datastore is a datastore where you make use of Instances to easily replicate the changes for the client & server using functions & events like Instance:GetPropertyChangedSignal()
or Instance.Changed
.
Are Instances better than data manager modules?
They aren’t. Using Instances can cause a big performance impact for your game, depending on how many data you’ll have to create for each player.
Example: your server has 30 players playing and each player has a total of 50 Instances, then you have created 1,500 Instances in total.
Summarizing, you should only use Instance based data stores for small projects. players with a good PC will mostly never notice the difference lol…
Coding
We always start by getting the services we’ll need to make our data store.
--# services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local PREFIX= "DataStore:"
local DataStore = DataStoreService:GetDataStore("game")
local JobId = game.JobId
local DefaultData = {
Cash = 0,
Kills = 0,
Deaths = 0
}
local function say(action, ...)
if action == "warn" then
warn(PREFIX, ...)
elseif action == "print" then
print(PREFIX, ...)
end
end
local function createInstances(folder, data)
for name, value in pairs(data) do
if type(value) == "number" then
local NumberValue = Instance.new("NumberValue")
NumberValue.Name = name
NumberValue.Value = value
NumberValue.Parent = folder
elseif type(value) == "string" then
local StringValue = Instance.new("StringValue")
StringValue.Name = name
StringValue.Value = value
StringValue.Parent = folder
elseif type(value) == "boolean" then
local BoolValue = Instance.new("BoolValue")
BoolValue.Name = name
BoolValue.Value = value
BoolValue.Parent = folder
end
end
end
local function getStoredTools(player, toolsData)
for _, toolName in ipairs(toolsData) do
local tool = game.ServerStorage.Backpack:FindFirstChild(toolName)
if tool then
tool:Clone().Parent = player.Backpack
tool:Clone().Parent = player.StarterGear
end
end
end
local function GetData(player)
say("warn", ("attempting to get %s's data."):format(player.Name))
local key = player.UserId
local tries = 0
local success, err = nil, nil
repeat
success, data = pcall(DataStore.GetAsync, DataStore, key)
if not success then
tries = tries + 1
wait(1)
end
until tries >= 4 or success
if success then
local folder = Instance.new("Folder")
folder.Name = player.Name
folder.Parent = ReplicatedStorage
if data then
createInstances(folder, data)
getStoredTools(player, data.Backpack)
else
createInstances(folder, DefaultData)
getStoredTools(player, DefaultData.Backpack)
end
say("print", ("finished to set %s's data."):format(player.Name))
else
player:Kick("failed to get your data, please rejoin after 3-5 minutes.")
end
end
local function SaveData(player)
if RunService:IsStudio() then
warn("cannot save in studio")
return false
end
local folder = ReplicatedStorage:FindFirstChild(player.Name)
if not folder then
say("warn", ("%s's data folder was not found, cannot proceed to save."):format(player.Name))
return false
end
local key = player.UserId
local tries = 0
local success, err = nil, nil
local saveTable = {
["Backpack"] = {}
}
for _, stat in ipairs(folder:GetChildren()) do
saveTable[stat.Name] = stat.Value
end
for _, tool in ipairs(player.Backpack:GetChildren()) do
if tool:IsA("Tool") then
table.insert(saveTable.Backpack, tool.Name)
end
end
repeat
if saveTable == nil then
err = "saveTable is nil"
break
end
success, err = pcall(DataStore.UpdateAsync, DataStore, key, function(data)
if data == nil then
return saveTable
elseif data ~= nil and data.SessionJobId == nil or data.SessionJobId == JobId then
data.SessionJobId = JobId
if data ~= saveTable then
return saveTable
end
return nil
end
return nil
end)
if not success then
tries = tries + 1
wait(6)
end
until tries >= 5 or success
if success then
say("warn", ("%s's data has been saved!"):format(player.Name))
else
warn(err, 2)
end
folder:Destroy() -- destroy folder and everything inside it
end
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(GetData)(player);
end
Players.PlayerAdded:Connect(GetData)
Players.PlayerRemoving:Connect(SaveData)
game:BindToClose(function()
for _, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(SaveData)(player);
end
end)