Just like @1Minecraft0954 said, you should use game:BindToClose()
because the server might shut down before Players.PlayerRemoving
fires, which can lead to saving issues. I recommend using the onShutdown
function from GEILER123456’s DataStore tutorial.
local function onShutdown()
if RunService:IsStudio() then
task.wait(2)
else
local finished = Instance.new("BindableEvent")
local allPlayers = Players:GetPlayers()
local leftPlayers = #allPlayers
for _,player in ipairs(allPlayers) do
coroutine.wrap(function()
save(player, true)
leftPlayers -= 1
if leftPlayers == 0 then
finished:Fire()
end
end)()
end
finished.Event:Wait()
end
end
game:BindToClose(onShutdown)
Just like what @tycolt3 said, you meant to use dataofuser
instead of ds
.
game.Players.PlayerAdded:Connect(function(plr)
print("executed")
local FolderOwned = Instance.new("Folder",plr)
FolderOwned.Name = "ItemsOwned"
local RadioOwned = Instance.new("BoolValue",FolderOwned)
RadioOwned.Name = "RadioOwned"
----------------------------------------------------
local FolderEquiped = Instance.new("Folder", plr)
FolderEquiped.Name = "ItemsEquiped"
local RadioEquiped = Instance.new("BoolValue",FolderEquiped)
RadioEquiped.Name = "RadioEquiped"
local dataofuser = ds:GetAsync(plr.UserId)
if dataofuser ~= nil then -- anything but nil
print("Found data for " .. plr.Name)
RadioOwned.Value = dataofuser[1]
--------------------------------
RadioEquiped.Value = dataofuser[1]
else
print("Replacing no data with new data.")
end
if checkGamePass(plr, RadiopassID) then
-- Player owns the game pass
print("Player owns the game pass")
RadioOwned.Value = true
else
-- Player does not own the game pass
print("Player does not own the game pass")
RadioOwned.Value = false
end
end)
Just like what @3F1VE said, the first parameter of DataStore:SetAsync()
key should be a string, though the function automatically converts the UserId to a string. The same thing applies to DataStore:GetAsync()
game.Players.PlayerRemoving:Connect(function(plr)
local datasave = {}
local folderitemsowned = plr:WaitForChild("ItemsOwned")
local folderitemequiped = plr:WaitForChild("ItemsOwned")
table.insert(datasave, folderitemsowned:WaitForChild("RadioOwned").Value)
--------------------------
table.insert(datasave, folderitemequiped:WaitForChild("RadioEquiped").Value)
local success,response = pcall(function()
ds:SetAsync(tostring(plr.UserId), datasave)
end)
if success then
print("succesfully saved data of " .. plr.Name)
else
warn(response)
end
end)
Aside from the things mentioned above, I noticed some problems in your script.
I think you meant RadioEquipped
instead of RadioEquiped
.
In the dataofuser
part, I think you meant to set RadioEquipped.Value
to dataofuser[2]
.
In making the table for saving data, I recommend listing them instead of using table.insert()
.
local datasave = {
folderitemsowned:WaitForChild("RadioOwned").Value,
folderitemequiped:WaitForChild("RadioEquiped").Value
}
I don’t think it’s necessary to save RadioOwned
because you’re already checking if the player has bought the gamepass.
I recommend making the functions connected to the events as variables instead so that it will be easier to read, and use it in other ways.
Speaking of the ‘other ways’ I mentioned above, you can make the function connected to Player.PlayerAdded
as a variable named loadData
and use it when iterating every player in the game once the server starts, just in case Player.PlayerAdded
didn’t fire for the players that are already in the server.
for _, Player in ipairs(Players:GetPlayers()) do
task.spawn(loadData, Player) -- similar to coroutine.wrap(loadData)(Player)
end
This also applies in the function connected to Player.PlayerRemoving
which can be made as a variable and be used in the onShutdown()
function mentioned above.
When creating instances, it’s recommended to parent it after setting its properties instead of using the second parameter of Instance.new()
to parent it because when setting the properties of a parented instance, the events of the instance will fire, resulting in memory being used.
Lastly, I don’t think you should pcall()
MarketplaceService#UserOwnsGamePassAsync()
because it won’t error. this means that checkGamePass()
function is not needed anymore and you should use MarketplaceService#UserOwnsGamePassAsync()
directly in the function.
Summary
When applying everything above, the final code should look like this.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("datatest")
local RADIO_PASS_ID = 663864800
local function loadData(Player: Player) -- player joins
local userId = Player.UserId
local FolderOwned = Instance.new("Folder")
FolderOwned.Name = "ItemsOwned"
FolderOwned.Parent = Player
local RadioOwned = Instance.new("BoolValue")
RadioOwned.Name = "RadioOwned"
RadioOwned.Parent = FolderOwned
local FolderEquipped = Instance.new("Folder")
FolderEquipped.Name = "ItemsEquipped"
FolderEquipped.Parent = Player
local RadioEquipped = Instance.new("BoolValue")
RadioEquipped.Name = "RadioEquipped"
RadioEquipped.Parent = FolderEquipped
RadioOwned.Value = MarketplaceService:UserOwnsGamePassAsync(userId, RADIO_PASS_ID)
local data = DataStore:GetAsync(tostring(userId))
if data ~= nil then -- if the player has data
RadioEquipped.Value = data[1]
else -- if the player has no data
RadioEquipped.Value = false
end
end
local function saveData(Player: Player) -- player leaving
local FolderEquipped = Player:FindFirstChild("ItemsEquipped")
if FolderEquipped ~= nil then
local RadioEquipped = FolderEquipped:FindFirstChild("RadioEquipped")
if RadioEquipped ~= nil then
pcall(DataStore.SetAsync, DataStore, tostring(Player.UserId), {RadioEquipped.Value})
end
end
end
local function onShutdown() -- server shut down or studio playtest close
if RunService:IsStudio() == true then
task.wait(2)
else
local finished = Instance.new("BindableEvent")
local allPlayers = Players:GetPlayers()
local leftPlayers = #allPlayers
for _, Player in ipairs(allPlayers) do
task.spawn(function()
saveData(Player)
leftPlayers -= 1
if leftPlayers == 0 then
finished:Fire()
end
end)
end
finished.Event:Wait()
end
end
for _, Player in ipairs(Players:GetPlayers()) do
task.spawn(loadData, Player)
end
Players.PlayerAdded:Connect(loadData) -- player joins
Players.PlayerRemoving:Connect(saveData) -- player leaving
game:BindToClose(onShutdown) -- server shut down or studio playtest close