Datastore not saving after resetting in Roblox Studio testrun

Hello! I’ve been messing around with data stores for a little bit and I believe I may have run into a Roblox Studio bug with them. I haven’t found any topics about this issue. In Roblox Studio, I was testing my data stores by running the game by selecting “Play inside the window”.

When I run a play, my data, which is just character values, loads perfectly fine from the data store. As expected, because I enabled Enable Studio Access to API Services. When I leave, the data does save perfectly fine also (I press Esc → Leave Game). However, when I reset my character, respawn, and then leave while in a studio test, my data does not save.

To clarify, I have nothing changing my data upon death. The data is stored in a folder in the player. I went through and tested my code’s logic with print() statements as well, and I found that .PlayerRemoving event was not even being called after I reset and quit. It would, however, be called if I just quit without resetting.

Is this some sort of studio bug, or am I missing something? I went ahead and tested it in an actual game and it worked perfectly fine, even after resetting, so I figured it was a studio issue.

Here’s a section of my code that handles the joining and leaving function:

Around the top of the script:

local it = Instance.new

local function createValue(parent, valType, name, defaultVal)
	local val = it(valType)
	val.Name = name
	val.Value = defaultVal or nil or 0
	val.Parent = parent
	return val
end

Later on in the script:

--Joining
plyS.PlayerAdded:Connect(function(ply)
	
	--PrivateInfo is what only the player can see---------------
	local private = it("Folder")
	private.Name = "privatestats"
	private.Parent = ply
	
	local wins = createValue(private, "IntValue", "Wins", 0)
	local kills = createValue(private, "IntValue", "Kills", 0)
	local bitz = createValue(private, "IntValue", "Bitz", 0)
	
	--Binds, used to store what binds a player is using for their classes
	local binds = it("Folder")
	binds.Name = "Keybinds"
	binds.Parent = private
	
	local skillOneBind = createValue(binds, "StringValue", "Skill_1_Bind", "Q")
	local skillTwoBind = createValue(binds, "StringValue", "Skill_2_Bind", "E")
	local skillThreeBind = createValue(binds, "StringValue", "Skill_3_Bind", "R")
	local skillFourBind = createValue(binds, "StringValue", "Skill_4_Bind", "F")
	local altSkillBind = createValue(binds, "StringValue", "Skill_Alt_Bind", "G")
	local tauntBind = createValue(binds, "StringValue", "Taunt_Bind", "T")
	
	--Unlocks, used to see if players unlocked a certain class or skin or etc
	local unlocks = it("Folder")
	unlocks.Name = "Unlocks"
	unlocks.Parent = private
	
	--Skins, used to see if the player has skins for classes
	local skins = it("Folder")
	skins.Name = "Skins"
	skins.Parent = private
	
	--FORMAT FOR SKIN NAMES:      <class name>_<skin name>

	
	--[[---------- Datastore Logic -----------]]--
	
	local plyKey = KEY_PREFIX .. ply.UserId
	
	--------Stats	
	local plyStatsData = nil
	local success, failure = pcall(function()
		plyStatsData = statsDataStore:GetAsync(plyKey)
	end)
	
	if success then
		wins.Value = plyStatsData.Wins or 0
		kills.Value = plyStatsData.Kills or 0
		bitz.Value = plyStatsData.Bitz or 0
	else
		print("Failure loading " .. ply.Name .. "'s data: " .. failure)
		--TODO: Prompt the player with some message, informing them the situation. Advise them to rejoin.
	end
	
	----------Binds
	local plyBindsData = nil
	local success, failure = pcall(function()
		plyBindsData = bindsDataStore:GetAsync(plyKey)
	end)
	
	if success and plyBindsData then
		skillOneBind.Value = plyBindsData.Skill1 or "Q"
		skillTwoBind.Value = plyBindsData.Skill2 or "E"
		skillThreeBind.Value = plyBindsData.Skill3 or "R"
		skillFourBind.Value = plyBindsData.Skill4 or "F"
		altSkillBind.Value = plyBindsData.SkillAlt or "G"
		tauntBind.Value = plyBindsData.Taunt or "T"
	else
		print("Failure loading " .. ply.Name .. "'s binds: " .. tostring(failure))
		--TODO: Prompt the player with some message, informing them the situation. Advise them to rejoin.
	end
end)

--Leaving
plyS.PlayerRemoving:Connect(function(ply)
	
	local plyKey = KEY_PREFIX .. ply.UserId
	
	------------ Stats data -----------------
	local dataToSave = {
		Wins = ply.privatestats.Wins.Value,
		Kills = ply.privatestats.Kills.Value,
		Bitz = ply.privatestats.Bitz.Value
	}
	
	local success, failure = pcall(function()
		statsDataStore:UpdateAsync(plyKey, dataToSave)
	end)
	
	if not success then
		local reportData = {
			['content'] = "Incoming data loss report for player statistics.",
			
			['embeds'] = {{
				['title'] = "Data Loss Report",
				['description'] = "The following player, " .. ply.Name .. " has lost data! Here is what they had before they left! If the following data seems suspicious or invalid, please report it to a moderator!",
				['color'] = "16711680",
				['url'] = "https://www.roblox.com/users/" .. ply.UserId .. "/profile",
				
				['fields'] = {
					{
						['name'] = "Wins: ",
						['value'] = ply.privatestats.Wins.Value,
					},
					{
						['name'] = "Kills: ",
						['value'] = ply.privatestats.Kills.Value,
					},
					{
						['name'] = "Bitz: ",
						['value'] = ply.privatestats.Bitz.Value,
					},
				}
				
			}}
		}
		
		local finalData = httpService:JSONEncode(reportData)
		httpService:PostAsync(DATASTORE_WEBHOOK_URL, finalData)
	end		--end of if statement
	
	
	---------- Binds data ----------
	
	local bindsFolder = ply.privatestats.Keybinds
	local bindDataToSave = {
		Skill1 = bindsFolder.Skill_1_Bind.Value,
		Skill2 = bindsFolder.Skill_2_Bind.Value,
		Skill3 = bindsFolder.Skill_3_Bind.Value,
		Skill4 = bindsFolder.Skill_4_Bind.Value,
		SkillAlt = bindsFolder.Skill_Alt_Bind.Value,
		Taunt = bindsFolder.Taunt_Bind.Value
	}
	local newSuccess, newFailure = pcall(function()
		bindsDataStore:SetAsync(plyKey, bindDataToSave)
	end)
	
	if not newSuccess then
		local reportData = {
			['content'] = "Incoming data loss report for player binds.",
			['embeds'] = {{
				['title'] = "Bind Loss Report",
				['description'] = "The following player, " .. ply.Name .. " has lost bind data! Here is what they had before they left! If the following data seems suspicious or invalid, please report it to a moderator!",
				['color'] = "16760320",
				['url'] = "https://www.roblox.com/users/" .. ply.UserId .. "/profile",
				
				['fields'] = {
					{
						['name'] = "Skill 1 Bind",
						['value'] = ply.privatestats.Keybinds.Skill_1_Bind.Value,
					},
					{
						['name'] = "Skill 2 Bind",
						['value'] = ply.privatestats.Keybinds.Skill_2_Bind.Value,
					},
					{
						['name'] = "Skill 3 Bind",
						['value'] = ply.privatestats.Keybinds.Skill_3_Bind.Value,
					},
					{
						['name'] = "Skill 4 Bind",
						['value'] = ply.privatestats.Keybinds.Skill_4_Bind.Value,
					},
					{
						['name'] = "Alternate Skill Bind",
						['value'] = ply.privatestats.Keybinds.Skill_Alt_Bind.Value,
					},
					{
						['name'] = "Taunt Bind",
						['value'] = ply.privatestats.Keybinds.Taunt_Bind.Value,
					},	
				}		
			}}
		}
		
		local finalData = httpService:JSONEncode(reportData)
		httpService:PostAsync(DATASTORE_WEBHOOK_URL, finalData)
	end		--end of if statement
end)

You’re missing code in your thread. This sounds like an implementation problem, so sharing your implementation would better help us see what you’re doing and what may be wrong with it.

3 Likes

Will do, thank you! If more code sections are needed, I can just upload a sample place.