Some stuff in datastore not being stored

basically, when i tried to make a variable in the data for the players’ kills on each class… it doesnt really save properly… yeah uhh i dont know what im doing wrong, because it does save on the current session but resets when i restart the game in studio.
heres the big script
by the way, ignore all the other stuff. what i really need help with is where i do sessionData[plr.UserId][plrr.class.Value…“KILLS”] = killsgotten. it simply doesnt save, just thought that giving the whole script would help out

local players = game:GetService("Players")
local runs = game:GetService("RunService")
local dataserv = game:GetService("DataStoreService")
local database = dataserv:GetDataStore("data")
local sessionData = {}

local function playerjoin(plr)
	local secretvalues = Instance.new("Folder")
	secretvalues.Name = "secretstats"
	
	local evensecreter = Instance.new("Folder")
	evensecreter.Name = "notvalues"
	evensecreter.Parent = secretvalues
	
	local killvalues = Instance.new("Folder")
	killvalues.Name = "classkillvalues"
	killvalues.Parent = secretvalues

	local subs = Instance.new("BoolValue")
	subs.Name = "hasSubspace"
	subs.Parent = secretvalues
	
	local jugg = Instance.new("BoolValue")
	jugg.Name = "hasJuggernaut"
	jugg.Parent = secretvalues
	
	local sergy = Instance.new("BoolValue")
	sergy.Name = "hasSergeant"
	sergy.Parent = secretvalues
	
	local cyboring = Instance.new("BoolValue")
	cyboring.Name = "hasCyborg"
	cyboring.Parent = secretvalues
	
	local credits = Instance.new("IntValue")
	credits.Name = "credits"
	credits.Parent = secretvalues
	
	local alltimekills = Instance.new("IntValue")
	alltimekills.Name = "allTimeKills"
	alltimekills.Parent = secretvalues

	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:GetAsync(plr.UserId)
		end)
		attempt+=1
		if not success then
			warn("not success")
			task.wait(3)
		end
	until success or attempt == 5

	if success then
		if playerData == nil then
			print("no data 4", plr.Name, "makin new")
			playerData = {
				["hasSubspace"] = false,
				["hasJuggernaut"] = false,
				["hasSergeant"] = false,
				["hasCyborg"] = false,
				["credits"] = 0,
				["allTimeKills"] = 0
			}
		end
		if playerData.hasSubspace == nil then
			playerData.hasSubspace = false
		end
		if playerData.hasJuggernaut == nil then
			playerData.hasJuggernaut = false
		end
		if playerData.hasSergeant == nil then
			playerData.hasSergeant = false
		end
		if playerData.hasCyborg == nil then
			playerData.hasCyborg = false
		end
		if playerData.credits == nil then
			playerData.credits = 0
		end
		if playerData.allTimeKills == nil then
			playerData.allTimeKills = 0
		end

		sessionData[plr.UserId] = playerData
		print("loadd data")
	else
		plr:Kick("sumthin weird happened,, (no this is not secret. your data didnt load so im kicking you alright?)")
	end

	subs.Value = sessionData[plr.UserId].hasSubspace
	subs.Changed:Connect(function()
		sessionData[plr.UserId].hasSubspace = subs.Value
	end)
	jugg.Value = sessionData[plr.UserId].hasJuggernaut
	jugg.Changed:Connect(function()
		sessionData[plr.UserId].hasJuggernaut = jugg.Value
	end)
	sergy.Value = sessionData[plr.UserId].hasSergeant
	sergy.Changed:Connect(function()
		sessionData[plr.UserId].hasSergeant = sergy.Value
	end)
	cyboring.Value = sessionData[plr.UserId].hasCyborg
	cyboring.Changed:Connect(function()
		sessionData[plr.UserId].hasCyborg = cyboring.Value
	end)
	
	credits.Value = sessionData[plr.UserId].credits
	credits.Changed:Connect(function()
		sessionData[plr.UserId].credits = credits.Value
	end)
	alltimekills.Changed:Connect(function()
		sessionData[plr.UserId].allTimeKills = alltimekills.Value
	end)
	print(sessionData[plr.UserId][plr.class.Value.."KILLS"]) -- prints nil
	task.spawn(function()
		local bindy = Instance.new("BindableEvent")
		bindy.Name = "killevent"
		bindy.Event:Connect(function(plrr, killsgotten)
			if plrr == plr then
				if sessionData[plr.UserId][plrr.class.Value.."KILLS"] == nil then -- pls help with this stuff
					--table.insert(sessionData[plr.UserId], {[plrr.class.Value.."KILLS"] = killsgotten})
					sessionData[plr.UserId][plrr.class.Value.."KILLS"] = killsgotten -- prints the right amount
					local instancevalue = killvalues:FindFirstChild(plrr.class.Value)
					if instancevalue == nil then
						local thiskillvalue = Instance.new("IntValue")
						thiskillvalue.Name = plrr.class.Value
						thiskillvalue.Value = killsgotten
						thiskillvalue.Parent = killvalues
					end
					print(sessionData[plr.UserId][plrr.class.Value.."KILLS"])
				elseif sessionData[plr.UserId][plrr.class.Value.."KILLS"] ~= nil then
					sessionData[plr.UserId][plrr.class.Value.."KILLS"] += killsgotten
					print(sessionData[plr.UserId][plrr.class.Value.."KILLS"]) -- prints the right amount (in this session)
					local instancevaluee = killvalues:FindFirstChild(plrr.class.Value)
					if instancevaluee ~= nil then
						print("um lets killgive") -- prints
						instancevaluee.Value = sessionData[plr.UserId][plrr.class.Value.."KILLS"]
					elseif instancevaluee == nil then
						print("aww its nil")
					end
				end
			end
		end)
		bindy.Parent = evensecreter
	end)
	
	secretvalues.Parent = plr
end
players.PlayerAdded:Connect(playerjoin)




local function PlayerLeaving(plr)
	if sessionData[plr.UserId] then
		local success = nil
		local errormsg = nil
		local attempt = 1

		repeat
			success, errormsg = pcall(function()
				database:SetAsync(plr.UserId, sessionData[plr.UserId])
				
			end)
			attempt+=1
			if not success then
				warn("didnt save now",plr.Name)
				task.wait(3)
			end
		until success or attempt == 5
		if success then
		else
			warn("ddidnt save",plr.Name)
		end
	end
end
players.PlayerRemoving:Connect(PlayerLeaving)

local function shutdown()
	if runs:IsStudio() then
		-- if id need to make it not save in studio i would do return here btw so it should theoretically save anyway
	end

	for i, player in ipairs(players:GetPlayers()) do
		task.spawn(function()
			PlayerLeaving(player)
		end)
	end
end
game:BindToClose(shutdown)

oh wait, actually, none of my variables save now!! i wonder what could have happened… cuz they did work before…

I recommend replacing the shutdown function with this:

local function shutdown()
	if runs:IsStudio() then
		-- if id need to make it not save in studio i would do return here btw so it should theoretically save anyway
		task.wait(1) -- You might need to increase the duration, depending on the performance of your PC
	end

	local x, y = 0, 0

	for i, player in ipairs(players:GetPlayers()) do
		x += 1

		task.spawn(function()
			PlayerLeaving(player)
			y += 1
		end)
	end

	repeat task.wait() until y == x -- This will prevent the game from shutting down either until the 30 second limit is reached...
	-- ...or all of the players in your game's data has a chance to save
end

Studio usually closes the play-test too quickly to give data a chance to save properly, the task.wait will force it to stay open. Functions connected to BindToClose will also stop executing once the code finishes running, it doesn’t take into account any active threads. This means that a repeat task.wait() loop is required to force the game to stay open, either until the 30 second limit is reached, or all the of the players in your game’s data has a chance to save

uhh… i dont think that this is the problem, as, when i do success, errormsg = pcall(function() database:SetAsync(plr.UserId, sessionData[plr.UserId]) print('saved') end) it does indeed print “saved”.

I still recommend replacing it, though. If there’s enough time for the data to attempt to save, then there could be two possible causes:

  • Something is causing sessionData to be overwritten, causing it to be a blank table or nil
  • If certain conditions are met, sessionData is never created for the player

okayy ill use it anyway buut also when i do print(sessiondata) it prints

   [569022178] =  {
      [1] =  {
         ["swordKILLS"] = 1
      },
      [2] =  {
         ["swordKILLS"] = 1
      },
      [3] =  {
         ["swordKILLS"] = 1
      },
      [4] =  {
         ["swordKILLS"] = 1
      },
      [5] =  {
         ["swordKILLS"] = 1
      },
      [6] =  {
         ["swordKILLS"] = 1
      },
      [7] =  {
         ["swordKILLS"] = 1
      },
      [8] =  {
         ["swordKILLS"] = 1
      },
      [9] =  {
         ["swordKILLS"] = 1
      },
      [10] =  {
         ["swordKILLS"] = 1
      },
      [11] =  {
         ["swordKILLS"] = 1
      },
      [12] =  {
         ["slingshotKILLS"] = 1
      },
      [13] =  {
         ["swordKILLS"] = 1
      },
      ["allTimeKills"] = 1,
      ["credits"] = 0,
      ["hasCyborg"] = false,
      ["hasJuggernaut"] = false,
      ["hasSergeant"] = false,
      ["hasSubspace"] = false,
      ["swordKILLS"] = 1
   }
}

maybe i just need a better way to put the variables in the table buut to be honest i dont think i know how to…

I’m noticing a problem: It seems that sessionData is a mixed table (Has a mixture of numerical keys and string keys)

Mixed tables aren’t recommended to be used for saving data, they can become corrupted when SetAsync converts them into JSON format, causing values to be missing. You should use either an array or a dictionary with string keys only to guarantee that the data is saved correctly

1 Like

oh uh… i wasnt really intending for it to be a mixed table. it seems that when i try to do sessionData[plr.UserId][plrr.class.Value.."KILLS"] = killsgotten it automatically makes it into an array… uhh how do i make it save as [“”] and not [number]??

1 Like

You can use the tostring function to convert the player’s user ID into a string (Remember to update any comparisons and indexes, as "123" ~= 123 and sessionData[player.UserId] ~= sessionData[tostring(player.UserId)])

From what I can tell, there doesn’t seem to be a need to change the killsgotten array, but there’s a way that you can optimize it to reduce the amount of data you’re trying to save

Instead of creating a new table for each kill, you could check to see if swordKILLS already exists, and if so, increment its value (You could do the same for the other types of kills, of course)

thats what ive been trying to do!! for some reason, it just doesnt want to and instead makes a new variable each time. pleeeease help me with that

I found out why that’s happening: sessionData[player.UserId] is an array, but you’re trying to index it using a string. This means that sessionData[player.UserID][plrr.class.Value.."KILLS"] is always nil

I’ll try to fix it, but it might need some adjustments afterwards

oh, that explains it a bit better. also, it won’t hurt me to change the database, no one really plays the game so no big trouble from data loss lols

1 Like

This should work:

local players = game:GetService("Players")
local runs = game:GetService("RunService")
local dataserv = game:GetService("DataStoreService")
local database = dataserv:GetDataStore("data")
local sessionData = {}

local function playerjoin(plr)
	local secretvalues = Instance.new("Folder")
	secretvalues.Name = "secretstats"

	local evensecreter = Instance.new("Folder")
	evensecreter.Name = "notvalues"
	evensecreter.Parent = secretvalues

	local killvalues = Instance.new("Folder")
	killvalues.Name = "classkillvalues"
	killvalues.Parent = secretvalues

	local subs = Instance.new("BoolValue")
	subs.Name = "hasSubspace"
	subs.Parent = secretvalues

	local jugg = Instance.new("BoolValue")
	jugg.Name = "hasJuggernaut"
	jugg.Parent = secretvalues

	local sergy = Instance.new("BoolValue")
	sergy.Name = "hasSergeant"
	sergy.Parent = secretvalues

	local cyboring = Instance.new("BoolValue")
	cyboring.Name = "hasCyborg"
	cyboring.Parent = secretvalues

	local credits = Instance.new("IntValue")
	credits.Name = "credits"
	credits.Parent = secretvalues

	local alltimekills = Instance.new("IntValue")
	alltimekills.Name = "allTimeKills"
	alltimekills.Parent = secretvalues

	local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:GetAsync(plr.UserId)
		end)
		attempt+=1
		if not success then
			warn("not success")
			task.wait(3)
		end
	until success or attempt == 5

	if success then
		if playerData == nil then
			print("no data 4", plr.Name, "makin new")
			playerData = {
				["hasSubspace"] = false,
				["hasJuggernaut"] = false,
				["hasSergeant"] = false,
				["hasCyborg"] = false,
				["credits"] = 0,
				["allTimeKills"] = 0
			}
		end
		if playerData.hasSubspace == nil then
			playerData.hasSubspace = false
		end
		if playerData.hasJuggernaut == nil then
			playerData.hasJuggernaut = false
		end
		if playerData.hasSergeant == nil then
			playerData.hasSergeant = false
		end
		if playerData.hasCyborg == nil then
			playerData.hasCyborg = false
		end
		if playerData.credits == nil then
			playerData.credits = 0
		end
		if playerData.allTimeKills == nil then
			playerData.allTimeKills = 0
		end

		sessionData[plr.UserId] = playerData
		print("loadd data")
	else
		plr:Kick("sumthin weird happened,, (no this is not secret. your data didnt load so im kicking you alright?)")
	end

	subs.Value = sessionData[plr.UserId].hasSubspace
	subs.Changed:Connect(function()
		sessionData[plr.UserId].hasSubspace = subs.Value
	end)
	jugg.Value = sessionData[plr.UserId].hasJuggernaut
	jugg.Changed:Connect(function()
		sessionData[plr.UserId].hasJuggernaut = jugg.Value
	end)
	sergy.Value = sessionData[plr.UserId].hasSergeant
	sergy.Changed:Connect(function()
		sessionData[plr.UserId].hasSergeant = sergy.Value
	end)
	cyboring.Value = sessionData[plr.UserId].hasCyborg
	cyboring.Changed:Connect(function()
		sessionData[plr.UserId].hasCyborg = cyboring.Value
	end)

	credits.Value = sessionData[plr.UserId].credits
	credits.Changed:Connect(function()
		sessionData[plr.UserId].credits = credits.Value
	end)
	alltimekills.Changed:Connect(function()
		sessionData[plr.UserId].allTimeKills = alltimekills.Value
	end)
	print(sessionData[plr.UserId][plr.class.Value.."KILLS"]) -- prints nil
	task.spawn(function()
		local bindy = Instance.new("BindableEvent")
		bindy.Name = "killevent"
		bindy.Event:Connect(function(plrr, killsgotten)
			if plrr == plr then
				local killType = plrr.class.Value.."KILLS"
				local valueWasFound = false
				local totalKills = 0

				for _, v in sessionData[plr.UserId] do
					if typeof(v) == "table" and v[killType] then
						valueWasFound = true
						v[killType] += killsgotten
						totalKills = v[killType]
						break
					end
				end

				if valueWasFound == false then
					table.insert(sessionData[plr.UserId], {[killType] = killsgotten})
					totalKills = killsgotten
				end

				local instancevalue = killvalues:FindFirstChild(plrr.class.Value)

				if instancevalue then
					instancevalue.Value = totalKills
				else
					local killsvalue = Instance.new("IntValue")
					killsvalue.Name = plrr.class.Value
					killsvalue.Value = totalKills
					killsvalue.Parent = killvalues
				end

				print(sessionData[plr.UserId])
			end
		end)
		bindy.Parent = evensecreter
	end)

	secretvalues.Parent = plr
end
players.PlayerAdded:Connect(playerjoin)




local function PlayerLeaving(plr)
	if sessionData[plr.UserId] then
		local success = nil
		local errormsg = nil
		local attempt = 1

		repeat
			success, errormsg = pcall(function()
				database:SetAsync(plr.UserId, sessionData[plr.UserId])

			end)
			attempt+=1
			if not success then
				warn("didnt save now",plr.Name)
				task.wait(3)
			end
		until success or attempt == 5
		if success then
		else
			warn("ddidnt save",plr.Name)
		end
	end
end
players.PlayerRemoving:Connect(PlayerLeaving)

local function shutdown()
	if runs:IsStudio() then
		-- if id need to make it not save in studio i would do return here btw so it should theoretically save anyway
		task.wait(1) -- You might need to increase the duration, depending on the performance of your PC
	end

	local x, y = 0, 0

	for i, player in ipairs(players:GetPlayers()) do
		x += 1

		task.spawn(function()
			PlayerLeaving(player)
			y += 1
		end)
	end

	repeat task.wait() until y == x -- This will prevent the game from shutting down either until the 30 second limit is reached...
	-- ...or all of the players in your game's data has a chance to save
end
game:BindToClose(shutdown)

I tried to avoid making major modifications in-order to keep it familiar and compatible with the current system


@Dimayuplay I’ve edited the code to prevent a possible bug from occuring

no way, the kills value for classes actually saves!! although, the other data (like allTimeKills or hasSubspace) still doesnt save. if you could pleeease help me with that ill be so thankful!!

1 Like

As I previously mentioned, the issue that some values aren’t saving correctly could be caused due to the fact that it’s a mixed table, and that would require rewriting the system to fix

I do happen to have enough time today to write a new system for you if you’re really willing to sacrifice the current data

oh yeah, loss of data shouldnt be that much of a problem. thanks a lot!!!

1 Like

oh wait!! nevermind! i actually got it to work myself. basically instead of doing table.insert i just declared the variable in the table so now it looks more like this
also changed the name of datastore

if plrr == plr then
				local killType = plrr.class.Value.."KILLS"
				local valueWasFound = false
				local totalKills = 0

				--for _, v in sessionData[plr.UserId] do
				--	if typeof(v) == "table" and v[killType] then
				--		valueWasFound = true
				--		v[killType] += killsgotten
				--		totalKills = v[killType]
				--		break
				--	end
				--end
				if sessionData[plr.UserId][killType] ~= nil then
					valueWasFound = true
					sessionData[plr.UserId][killType] += killsgotten
				end

				if valueWasFound == false then
					sessionData[plr.UserId][killType] = killsgotten
					totalKills = killsgotten
				end

				local instancevalue = killvalues:FindFirstChild(plrr.class.Value)

				if instancevalue then
					instancevalue.Value = totalKills
				else
					local killsvalue = Instance.new("IntValue")
					killsvalue.Name = plrr.class.Value
					killsvalue.Value = totalKills
					killsvalue.Parent = killvalues
				end

				print(sessionData[plr.UserId])
			end

anyway, THANKS A LOT FOR YOUR HELP!!! if it werent for you our game would have been doomed lol. now i definitely understand more about tables and datastores.

1 Like

I’ll still share the new system, since I was almost done testing it before I noticed your reply :grin:

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local dataStore = DataStoreService:GetDataStore("data") -- Remember to delete the keys before using the new system, unless you're changing the name of the DataStore


local sessionData = {}


local function getData(userId)
	local attempts = 0

	repeat
		attempts += 1

		local success, data = pcall(dataStore.GetAsync, dataStore, userId)
		if success then return true, data end

		task.wait(3)
	until attempts == 5

	return false, nil
end

Players.PlayerAdded:Connect(function(player)
	local success, data = getData(player.UserId)

	if success then
		if data == nil then
			data = {
				hasSubspace = false,
				hasJuggernaut = false,
				hasSergeant = false,
				hasCyborg = false,
				credits = 0,
				allTimeKills = 0,
				killData = {}
			}
		end

		sessionData[player.UserId] = data

		local secretValues = Instance.new("Folder")
		secretValues.Name = "secretstats"
		secretValues.Parent = player

		local hasSubspace = Instance.new("BoolValue")
		hasSubspace.Name = "hasSubspace"
		hasSubspace.Value = data.hasSubspace
		hasSubspace.Parent = secretValues

		local hasJuggernaut = Instance.new("BoolValue")
		hasJuggernaut.Name = "hasJuggernaut"
		hasJuggernaut.Value = data.hasJuggernaut
		hasJuggernaut.Parent = secretValues

		local hasSergeant = Instance.new("BoolValue")
		hasSergeant.Name = "hasSergeant"
		hasSergeant.Value = data.hasSergeant
		hasSergeant.Parent = secretValues

		local hasCyborg = Instance.new("BoolValue")
		hasCyborg.Name = "hasCyborg"
		hasCyborg.Value = data.hasCyborg
		hasCyborg.Parent = secretValues

		local credits = Instance.new("IntValue")
		credits.Name = "credits"
		credits.Value = data.credits
		credits.Parent = secretValues

		local allTimeKills = Instance.new("IntValue")
		allTimeKills.Name = "allTimeKills"
		allTimeKills.Value = data.allTimeKills
		allTimeKills.Parent = secretValues

		hasSubspace.Changed:Connect(function(value) sessionData[player.UserId].hasSubspace = value end)
		hasJuggernaut.Changed:Connect(function(value) sessionData[player.UserId].hasJuggernaut = value end)
		hasSergeant.Changed:Connect(function(value) sessionData[player.UserId].hasSergeant = value end)
		hasCyborg.Changed:Connect(function(value) sessionData[player.UserId].hasCyborg = value end)
		credits.Changed:Connect(function(value) sessionData[player.UserId].credits = value end)
		allTimeKills.Changed:Connect(function(value) sessionData[player.UserId].allTimeKills = value end)

		local evenSecreter = Instance.new("Folder")
		evenSecreter.Name = "notvalues"
		evenSecreter.Parent = secretValues

		local killValues = Instance.new("Folder")
		killValues.Name = "classkillvalues"
		killValues.Parent = evenSecreter

		local killEvent = Instance.new("BindableEvent")
		killEvent.Name = "killevent"
		killEvent.Parent = evenSecreter

		killEvent.Event:Connect(function(plr, killsGotten)
			if plr == player then
				local playerClass = player.class.Value
				local killType = playerClass.."KILLS"

				local killValue = killValues:FindFirstChild(playerClass)

				if killValue == nil then
					local killValue = Instance.new("IntValue")
					killValue.Name = playerClass
					killValue.Parent = killValues
				end

				if sessionData[player.UserId].killData[killType] then
					sessionData[player.UserId].killData[killType] += killsGotten
					killValue.Value += killsGotten
				else
					sessionData[player.UserId].killData[killType] = killsGotten
					killValue.Value = killsGotten
				end

				allTimeKills.Value += killsGotten
			end
		end)
	else
		player:Kick("sumthin weird happened,, (no this is not secret. your data didnt load so im kicking you alright?)")
	end
end)

Players.PlayerRemoving:Connect(function(player)
	local data = sessionData[player.UserId]

	if data then
		local attempts = 0

		repeat
			attempts += 1

			local success = pcall(dataStore.SetAsync, dataStore, player.UserId, data)
			if success then break end

			task.wait(3)
		until attempts == 5
	end
end)

game:BindToClose(function()
	if RunService:IsStudio() then
		task.wait(1)
	else
		local x, y = 0, 0

		local function saveData(userId)
			local data = sessionData[userId]

			if data then
				pcall(dataStore.SetAsync, dataStore, userId, data)
			end

			y += 1
		end

		for _, player in Players:GetPlayers() do
			x += 1

			task.spawn(saveData, player.UserId)
		end

		repeat task.wait() until y == x
	end
end)