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)