Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

How come this save shop items and stats script not work?

Script Location : ServerScriptService Script Type : ModuleScript

-- My scripter and I have been stuck on it for hours, help would be nice! We have no idea why it doesn't work! --

-- Setup table that we will return to scripts that require the ModuleScript.
local PlayerStatManager = {}
 
-- Create variable for the DataStore.
local DataStoreService = game:GetService('DataStoreService')
local Events = workspace:WaitForChild("Events", 30)
local playerData = DataStoreService:GetDataStore('PlayerData')
 
-- Create variable to configure how often the game autosaves the player data.
local AUTOSAVE_INTERVAL = 30
 
-- Number of times we can retry accessing a DataStore before we give up and create
-- an error.
local DATASTORE_RETRIES = 3
 
-- Table to hold all of the player information for the current session.
local sessionData = {}
 
-- Function the other scripts in our game can call to change a player's stats. This
-- function is stored in the returned table so external scripts can use it.
function PlayerStatManager:ChangeStat(player, statName, changeValue)
	sessionData[player][statName] = changeValue
end

function UpdateData(player)
	local playerleaderstats = player:WaitForChild("leaderstats", 30)
	local playerWins = playerleaderstats:WaitForChild("Wins", 30)
	local playerLevel = playerleaderstats:WaitForChild("Level", 30)
	local playerCoins = playerleaderstats:WaitForChild("Coins", 30)
	local playerBoughtItems = player:WaitForChild("BoughtItems", 30)
	local playerTimesVisited = player:WaitForChild("TimesVisited", 30)
	
	local boughtitemst = {}
	
	for i, v in pairs(playerBoughtItems:GetChildren()) do
		boughtitemst[i] = v.Name
	end
	
	sessionData[player] = {
		Wins = playerWins.Value, 
		Level = playerLevel.Value,
		Coins = playerCoins.Value,
		BoughtItems = boughtitemst,
		TimesVisited = playerTimesVisited.Value
	}
end
 
-- Function to retry the passed in function several times. If the passed in function
-- is unable to be run then this function returns false and creates an error.
local function dataStoreRetry(dataStoreFunction)
	local tries = 0	
	local success = true
	local data = nil
	repeat
		tries = tries + 1
		success = pcall(function() data = dataStoreFunction() end)
		if not success then wait(1) end
	until tries == DATASTORE_RETRIES or success
	if not success then
		warn('Could not access DataStore! Warn players that their data might not get saved!')
	end
	return success, data
end
 
-- Function to retrieve player's data from the DataStore.
local function getPlayerData(player)
	Events:WaitForChild("DataIconEvent", 30):FireClient(player, "In Progress", "Getting progress...", true)
	return dataStoreRetry(function()
		Events:WaitForChild("DataIconEvent", 30):FireClient(player, "Success", "Progress loaded!", true)
		return playerData:GetAsync(player.UserId)
	end)
end
 
-- Function to save player's data to the DataStore.
local function savePlayerData(player)
	if sessionData[player] then
		Events:WaitForChild("DataIconEvent", 30):FireClient(player, "In Progress", "Saving progress...", false)
		return dataStoreRetry(function()
			Events:WaitForChild("DataIconEvent", 30):FireClient(player, "Success", "Progress saved!", false)
			print("Saved")
			return playerData:SetAsync(player.UserId, sessionData[player])
		end)
	end
end
 
-- Function to add player to the sessionData table. First check if the player has
-- data in the DataStore. If so, we'll use that. If not, we'll add the player to
-- the DataStore.
local function setupPlayerData(player)
	local success, data = getPlayerData(player)
	if not success then
		-- Could not access DataStore, set session data for player to false.
		sessionData[player] = false
		Events:WaitForChild("DataIconEvent", 30):FireClient(player, "Fail", "Something went wrong...", true)
	else
		if not data then
			-- DataStores are working, but no data for this player
			sessionData[player] = {
				Wins = 0,
				Level = 1,
				Coins = 10,
				BoughtItems = {},
				TimesVisited = 1
			}
			savePlayerData(player)
		else
			-- DataStores are working and we got data for this player
			sessionData[player] = data
			
			local playerleaderstats = player:WaitForChild("leaderstats", 30)
			local playerWins = playerleaderstats:WaitForChild("Wins", 30)
			local playerLevel = playerleaderstats:WaitForChild("Level", 30)
			local playerLvlUpExp = playerLevel:WaitForChild("LvlUpExp", 30)
			local playerCoins = playerleaderstats:WaitForChild("Coins", 30)
			local playerBoughtItems = player:WaitForChild("BoughtItems", 30)
			local playerTimesVisited = player:WaitForChild("TimesVisited", 30)
			
			local CoinsMultiplier = player:WaitForChild("CoinsMultiplier", 30)
			local ExpMultiplier = player:WaitForChild("ExpMultiplier", 30)
			
			playerWins.Value = sessionData[player].Wins
			playerLevel.Value = sessionData[player].Level
			playerLvlUpExp.Value = ((playerLevel.Value * 3) / ExpMultiplier.Value)
			playerCoins.Value = sessionData[player].Coins
			for i, v in pairs(sessionData[player].BoughtItems) do
				local boughtitem = Instance.new("BoolValue", playerBoughtItems)
				boughtitem.Name = v
				boughtitem.Value = true
			end
			playerTimesVisited.Value = sessionData[player].TimesVisited
			
		end
	end	
end
 
-- Function to run in the background to periodically save player's data.
local function autosave()
	while wait(AUTOSAVE_INTERVAL) do
		for player, data in pairs(sessionData) do
			--UpdateData(player)
			savePlayerData(player)
			print("Autosaving...")
		end
	end
end
 
-- Bind setupPlayerData to PlayerAdded to call it when player joins.
game.Players.PlayerAdded:connect(setupPlayerData)
 
-- Call savePlayerData on PlayerRemoving to save player data when they leave.
-- Also delete the player from the sessionData, as the player isn't in-game anymore.
game.Players.PlayerRemoving:connect(function(player)
	--UpdateData(player)
	savePlayerData(player)
	sessionData[player] = nil
end)
 
-- Start running autosave function in the background.
spawn(autosave)
 
-- Return the PlayerStatManager table to external scripts can access it.
return PlayerStatManager

If you do end up fixing it my scripter and I will add you in credits! Thanks!

1 answer

On lines 85-87 you can see you're running your first function after binding it to PlayerAdded. Unto which the lines are:

local function setupPlayerData(player)
    local success, data = getPlayerData(player)
    if not success then

We can then proceed to getPlayerData(player) to which we then see:

local function getPlayerData(player)
    Events:WaitForChild("DataIconEvent", 30):FireClient(player, "In Progress", "Getting progress...", true)
    return dataStoreRetry(function()
        Events:WaitForChild("DataIconEvent", 30):FireClient(player, "Success", "Progress loaded!", true)
        return playerData:GetAsync(player.UserId)
    end)
end

Here's the problem, you're returning getPlayerData as a GetAsync, but, there's no Success value! So your GetAsync gets assigned to Success and it is "truthy" so it passes most tests. Which is where your problems start.

This discussion is preserved as it was. New answers, comments, and votes are closed.