Tables are not saving

it doesnt work :frowning:

This text will be blurred

Sorry my bad, I forgot to include the two Module functions to Load and Save the store. I have edited the ModuleScript in the original post above and added these mega important bits! Silly me.

This is a bad take when it comes to DataStores. DataStores are extremely difficult to get right and there are so many edge cases that you may not consider. ProfileService is a proven module and has been throughly tested and optimised, you should use it over an unproven solution.

Avoiding using resources that are already available is bad practice, every programmer (professional or not) is likely to use a large amount of libraries that they did not make. You should really only be making your own resources if you cannot find a resource that does not meet your specific requirements.

Like I said in an earlier post, this method is in Roblox’s own examples (Move It Simulator). I adapted it to suit the data I wanted to store. Most of the time it makes sense to use other peoples code because there is no need to re-invent the wheel all the time. Most learning is done by making mistakes and figuring out why it doesn’t work. I have encountered lots of issues myself using DataStores but through good code breaking and testing I know why these things were happening. My DataStore works exactly as it should and I have had no issues with it.

Just because a method is used by Roblox it does mean it is the better solution. You cannot vouch for your own method over something like ProfileService that has been used by thousands more people. Using something like ProfileService is better for all developers as it is suited for storing whatever sort of data you need, designing a Datastore to only suit a single games need is pointless as you now need to design another Datastore for any other projects you may end up doing. ProfileService is easily the most user friendly Datastore module available at the moment.

I am not vouching for any method over another. You gave a solution and an opinion, I also gave a solution and an opinion. It’s up to the OP or anyone else to decide which opinion and solution they will adopt for themselves. Whether it be yours, mine, or anyone else’s.

2 Likes

it doesnt wrok : ( . here is my code

--playerdata

--=================================================================================
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local ServerEvents = ReplicatedStorage:WaitForChild("Remotes",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("saveandloaddata")); -- 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...
--=================================================================================
--module

--=================================================================================
-- 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;
local STRENGTH_INDEX = 3;
local Storage_INDEX = 4;
local Equipped = 5;
local Equippedst = 6;
local equippedcl = 7;

--=================================================================================
-- 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...
--=================================================================================
1 Like

Okay there are no functions called “GetTableValue”, “SetTableValue” in the DataStore script so it may be why it not functioning. I gave you an incomplete example because I thought you would probably not use it because it nils everything you had done, and required a lot of work (in creating the folder and values, the leaderstats, etc.). We can get this to work now if we simply remove the un-defined functions for now. I can help with these more advanced methods, but let’s just fix it for values and not tables. So change the playerdata Script to this:

--playerdata

--=================================================================================
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local ServerEvents = ReplicatedStorage:WaitForChild("Remotes",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 DataStore = require(script.Parent:WaitForChild("saveandloaddata")); -- 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

--=================================================================================
-- remote events (setters)
--=================================================================================
RemoteSetStat.OnServerEvent:Connect(SetStat);

--=================================================================================
-- remote functions (getters)
--=================================================================================
RemoteGetStat.OnServerInvoke = GetStat;

--=================================================================================
-- EOF...
--=================================================================================

And for the “saveandloaddata” module, those fixed indices are there for when we want to store multiple and different values with a predictable key in a dictionary fashion, but by the very nature of dictionaries we cannot store them as dictionaries because we cannot use a named index so we have to use a fixed and constant index that we define and use to simulate dictionaries using tables.

i.e. if you have an inventory system and you want to store different types of stuff, maybe a sword that has four properties -Name, Speed, Damage, Cost- and throwing dagger that has the same properties -Name, Speed, Damage, Cost-. What we can do is use a fixed index and make a table entry that looks like this:

local SWORD_INDEX = 1;
local DAGGER_INDEX = 2;
local AXE_INDEX = 3;

So for the sword we do this to add it to a table, for as many swords that we have:

{[1]=SWORD_INDEX,[2]="Broadsword",[3]=5,[4]=100,[5]=1000} -- identifier, name, speed, damage, cost
{[1]=SWORD_INDEX,[2]="Scimitar",[3]=50,[4]=200,[5]=2000}

And if we want to add a dagger, or an axe, or whatever, then we do this:

{[1]=DAGGER_INDEX,[2]="Dagger",[3]=10,[4]=20,[5]=100} -- identifier, name, speed, damage, cost (the same system)
{[1]=AXE_INDEX,[2]="Woodsman's Axe",[3]=80,[4]=200,[5]=500}

Now we can check the first entry to the table and it will tell us whether it is a sword, dagger, or axe. We can do the same with anything we want to as long as the information we are storing has the same number of properties (this is a rule for all database storage mechanisms, every entry must have the same number of keys, columns if you think of Excel). You can’t then add a sword to the same table that has five properties (Name,Speed,Damage,Cost,Mana) you will need a new table called “magic inventory” or something.

From your example you have currencies (if we require several of them), i.e.

local COINS_INDEX = 1;
local GEMS_INDEX = 2;
{[1]=COINS_INDEX ,[2]=100000} -- first index = identifier, second index = value
{[1]=GEMS_INDEX ,[2]=20} -- first index = identifier, second index = value

There is no limit to this method, and they are all saveable with primitive methods and no bloated modules to require. So in the “loadsavedata” Module under constants you have added some indices for things, they are not required unless they are defining a ‘fake’ dictionary item to store as exampled above.
So change the Module code to this and you should not error (hopefully, toes and fingers crossed).

--module

--=================================================================================
-- 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

--=================================================================================
-- 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...
--=================================================================================

it now actually saves but for some reason it doesnt load

Is playerDataStore nil when it comes to load it? It may be assigning the tempData instead.

@dduck5tar it prints playerdata1

Can I see the code for your version of the ModuleScript please?

@dduck5tar

--=================================================================================
-- Constants
--=================================================================================
local AUTOSAVE_DATAINTERVAL = 300;	-- 5 minutes auto save interval

--=================================================================================
-- 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 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);


local function load(player)

	local userId = player.UserId;
	local key = "player_" .. tostring(userId);
	print(playerDataStore)

	-- 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

--=================================================================================
--
--=================================================================================


--=================================================================================
-- 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...
--=================================================================================

Just do:

local saveData = {
	Coins = plr.leaderstats.Coins.Value,
	Strength = plr.leaderstats.Strength.Value,
	Gems = plr.leaderstats.Gems.Value,
	--etc...
}
local httpService = game:GetService('HttpService')
ds:SetAsync(plr.UserId,httpService:JSONEncode(saveData))

Then to load, just do:

httpService:JSONDecode(ds:GetAsync(plr.UserId))

in at @dduck5tar’s script or my original script

In your original script. Though don’t just copy/paste, you’ll need to implement it yourself properly. That’s just an example of how it would be constructed.

it says

Argument 1 missing or nil

do u know why?

Could you show your code? ㅤㅤㅤㅤ

local save = {}

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("datamaSnef")
local players = game:GetService("Players")



function save.playeraddede(player)

	local data = ds:GetAsync(player.UserId)
	
	player.leaderstats.Coins.Vslue = data[1]
	player.leaderstats.Strength.Vslue = data[2]
	player.leaderstats.Gems.Vslue = data[3]

	
end

game:BindToClose(function(plr)
	wait(2)
	pcall(function()

		local saveData = {
			Coins = plr.leaderstats.Coins.Value,
			Strength = plr.leaderstats.Strength.Value,
			Gems = plr.leaderstats.Gems.Value
		}
		local httpService = game:GetService('HttpService')
		ds:SetAsync(plr.UserId,httpService:JSONEncode(saveData))
	end)
end)
return save

hello, i sent my code, pls help