Okay here we go. The problem with the code is that even though you are using tables to store the data you are still calling GetAsync()
and SetAsync()
too many times by saving each of the index’s separately. By storing all of your data in a single table it will only require single calls to those two functions.
I would recommend that you use more descriptive names for your variables too, it makes it difficult to see what they mean by someone reading your code, and even for yourself if you come back to this code in a years time.
Here is a bare bones example of a DataStore ModuleScript, it was adapted from the “Move it Simulator” example I gave earlier. You will require a method of accessing the data contained in the DataStore, i.e. with RemoteFunctions, or BindableFunctions, depends on where you need the information. I would recommend using another server Script to set up a bunch of Events that you can use to access the DataStore, that way the DataStore module is only being required in a single place, i.e. changes to it wont be running under race-conditions. There is a bare-bones example of that below the ModuleScript.
ModuleScript to put in ServerScriptService
--=================================================================================
-- Data store
--=================================================================================
local DataStore = {};
DataStore.__index = DataStore;
--=================================================================================
-- Services
--=================================================================================
local DataStoreService = game:GetService("DataStoreService");
local RunService = game:GetService("RunService");
local Players = game:GetService("Players");
--=================================================================================
-- Bool indicating whether data should save in studio (will not change during runtime)
--=================================================================================
local saveInStudio = false;
--=================================================================================
-- Creates the player data store
--=================================================================================
local playerDataStore = nil;
local DataStoreVersion = 1;
if (not saveInStudio) and game.GameId ~= 0 then
playerDataStore = DataStoreService:GetDataStore("PlayerData"..DataStoreVersion);
end
--=================================================================================
-- Table containing a copy of the player's data
--=================================================================================
local sessionData = {};
--=================================================================================
-- When a players fail to load data, their key is put in a list so that
-- their data does not save and override existing data
--=================================================================================
local tempData = {}
--=================================================================================
-- Constants
--=================================================================================
local AUTOSAVE_DATAINTERVAL = 300; -- 5 minutes auto save interval
local COINS_INDEX = 1; -- these are some indexes for accessing values stored in the Currencies table
local GEMS_INDEX = 2;
--=================================================================================
-- The master Data store table
--=================================================================================
local NEW_PLAYER_DATA =
{
Strength = 0,
Storage = 0,
EquippedDst = 0,
Equipped = 0,
Class = 0,
EquippedClass = 0,
Currencies = {},
OwnedStorage = {},
OwnedTools = {},
OwnedClass = {},
};
--=================================================================================
-- Local Functions
--=================================================================================
local function load(player)
local userId = player.UserId;
local key = "player_" .. tostring(userId);
-- Return data from cache if it's already loaded
if sessionData[key] then
return sessionData[key];
end
-- Try to load data
if playerDataStore then
-- Data not loaded, let's go get it
local success, data = pcall(function()
return playerDataStore:GetAsync(key);
end)
-- Acces to store was successful
if success then
if data then
-- Data exists for this player so assign the session data
sessionData[key] = data;
else
-- Data store is working, but no current data for this player
warn( "New player ", key );
sessionData[key] = NEW_PLAYER_DATA;
end
return true; -- datastore working
end
warn( "Cannot access data store for ", key );
end
-- Could not load data, treat as new player
sessionData[key] = NEW_PLAYER_DATA;
-- When players fails to load data, their key is put in a list so that their
-- data does not save and override existing data
tempData[key] = true;
return false; -- data store failed!
end
--=================================================================================
--
--=================================================================================
local function save(player)
local userId = player.UserId;
local key = "player_" .. tostring(userId);
-- Save data if it exists and is not temporary
if sessionData[key] and not tempData[key] then
-- now save the data.
local tries = 0;
local success = nil;
repeat
tries = tries + 1;
success = pcall(function()
playerDataStore:SetAsync(key,sessionData[key]);
end)
if not success then wait(2) end;
until tries == 3 or success
if not success then
warn( "Cannot save data for ", key );
else
warn( "Saved data for ", key );
end
return success;
end
end
--=================================================================================
--
--=================================================================================
local function saveOnExit(player)
local userId = player.UserId;
local key = "player_" .. tostring(userId);
-- Save data
save(player);
-- Clear cached data
sessionData[key] = nil;
tempData[key] = nil;
end
--=================================================================================
-- Function to periodically save player data
--=================================================================================
local function autoSave()
while wait(AUTOSAVE_DATAINTERVAL) do
warn( "Auto saving player data..." );
for key, data in pairs(sessionData) do
save(key);
end
end
end
--=================================================================================
-- On Startup, Start running "autoSave()" function in the background
--=================================================================================
spawn(autoSave);
--=================================================================================
-- Connections
--=================================================================================
Players.PlayerAdded:Connect(function(player)
DataStore:Load(player);
end)
--=================================================================================
Players.PlayerRemoving:Connect(function(player)
saveOnExit(player);
end)
--=================================================================================
game:BindToClose(function()
if RunService:IsStudio() then return end;
for _, player in ipairs(Players:GetPlayers()) do
saveOnExit(player);
end
warn( "Binding to server close, forced player save.....!" );
end)
--=================================================================================
-- Load/Save data store
--=================================================================================
function DataStore:Load(player)
return load(player);
end
--=================================================================================
function DataStore:Save(player)
save(player);
end
--=================================================================================
function DataStore:GetStat(player,stat)
local userId = player.UserId;
local key = "player_" .. tostring(userId);
if not sessionData[key] then return nil end;
if not sessionData[key][stat] then
return nil;
end
return sessionData[key][stat];
end
--=================================================================================
function DataStore:SetStat(player,stat,value)
local userId = player.UserId;
local key = "player_" .. tostring(userId);
if not sessionData[key] then return nil end;
if not sessionData[key][stat] then
return nil;
end
sessionData[key][stat] = value;
print(stat, " changed to ", value);
return sessionData[key][stat];
end
--=================================================================================
--=================================================================================
return DataStore;
--=================================================================================
-- EOF...
--=================================================================================
This is an event Script also in ServerScriptService that joins the DataStore Module to a bunch of events so that you can access/change the information (usually called Getters and Setters). So you could make all your LeaderStat values here, or send the info back to the client and do that there. This is just an example and is not complete so will need some editing by you to include sanity checks everytime a request is made to change the data. I have put an example for SetStat/GetStat but no examples how to do this for the tables, this will require some fixed index you can use to ensure you change the correct values (that’s what the INDEX_COINS and INDEX_GEMS is for in the ModuleScript). If you want I can help with those too.
PlayerData script:
--=================================================================================
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local ServerEvents = ReplicatedStorage:WaitForChild("ServerEvents",3);
local NotifyChange = ServerEvents:WaitForChild("NotifyChange",3); -- a data changed notify message to return info to the client, to update GUI's or Stats visible to players
local RemoteSetStat = ServerEvents:WaitForChild("SetStat",3);
local RemoteGetStat = ServerEvents:WaitForChild("GetStat",3);
local RemoteGetTable = ServerEvents:WaitForChild("GetTable",3);
local RemoteSetTableValue = ServerEvents:WaitForChild("SetTableValue",3);
local RemoteGetTableValue = ServerEvents:WaitForChild("GetTableValue",3);
local DataStore = require(script.Parent:WaitForChild("DataStore")); -- a single point to access the DataStore Module
--=================================================================================
local function SetStat(player,stat,value)
-- this is called using a RemoteEvent like this:
-- RemoteSetStat:FireServer("Strength",2);
DataStore:SetStat(player,stat,value);
NotifyChange:FireClient(player,"Some info you may want to send back to a GUI or Leaderstats");
end
--=================================================================================
local function GetStat(player,stat)
-- this is called with a RemoteFunction like this:
-- local strength = RemoteGetStat:InvokeServer("Strength");
return DataStore:GetStat(player,stat);
end
--=================================================================================
local function GetTable(player,table_name)
return DataStore:GetTable(player,table_name);
end
--=================================================================================
local function SetTableValue(player,table_name,value)
return DataStore:SetTableValue(player,table_name,value);
end
--=================================================================================
local function GetTableValue(player,table_name,table_index)
return DataStore:GetTableValue(player,table_name,table_index);
end
--=================================================================================
-- remote events (setters)
--=================================================================================
RemoteSetStat.OnServerEvent:Connect(SetStat);
RemoteSetTableValue.OnServerEvent:Connect(SetTableValue);
--=================================================================================
-- remote functions (getters)
--=================================================================================
RemoteGetStat.OnServerInvoke = GetStat;
RemoteGetTable.OnServerInvoke = GetTable;
RemoteGetTableValue.OnServerInvoke = GetTableValue;
--=================================================================================
-- EOF...
--=================================================================================
I hope this helps in some way, it means you will have to make lots of changes to your game, but it makes for easy access to data with minimal calls to SetAsync()/GetAsync().