Player data takes time to load, and sometimes it’s not ready whenever scripts need to access it, I’m looking for a good way to solve this
An idea I have
A signal that fires whenever a player is fully loaded, and other scripts won’t do anything with the player before this signal
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Signal = require(ReplicatedStorage.Signal)
local loadedSignal = Signal.new()
local PlayerService = {}
PlayerService.LoadedSignal = loadedSignal
function PlayerService.SetupData(player) -- called once when player joins
local playerInfo = {}
wait(2) -- yielding placeholder
loadedSignal:Fire(player, playerInfo)
end
return PlayerService
local PlayerService
local PetService = {}
function PetService.SetupPlayer(player, playerInfo)
end
PlayerService.LoadedSignal:Connect(PetService.SetupPlayer)
return PetService
I’m sure there is a better way to this, but can’t think of one
I do that a lot, but has some flaws, like what happens if the data isn’t already loaded when that function is called, so you would load and return the data, but what if the function is called by another script while the data is still loading
Don’t know what you mean, I wasn’t talking about both the example and using :Get()
I was refering to something like
local playerDatas = {}
local PlayerService = {}
function PlayerService.LoadData(player)
local playerData = {}
wait(2) -- imagine this as the datastore yielding
playerDatas[player] = playerData
return playerData
end
function PlayerService.GetData(player)
return playerDatas[player] or PlayerService.LoadData(player)
end
return PlayerService
if two different scripts call PlayerService.GetData() at similar times it would end up loading the data twice
to fix this in the past I’ve done
local playerDatas = {}
local PlayerService = {}
function PlayerService.LoadData(player)
local playerData = {}
playerData.Loaded = false
playerData.LoadedSignal = Signal.new()
playerDatas[player] = playerData
wait(2) -- imagine this as the datastore yielding
playerData.Loaded = true
playerData.LoadedSignal:Fire()
return playerData
end
function PlayerService.GetData(player)
local playerData = playerDatas[player] or PlayerService.LoadData(player)
if not playerData.Loaded then
playerData.LoadedSignal:Wait()
end
return playerData
end
return PlayerService
I’m not sure if this is a good way to do it though
Any script that would require player data, so potentially infinite as any scripts dealing with the player would likely need to get the data, but the bug could happen with any count if it’s above only one
yielding can be addressed with pcalls unless its a custom datastore wrapper for instance here is something I wrote
-- Make a class since we don't want new stuff
local PlayerService
do
PlayerService = {}
function PlayerService:constructor()
self.PlayerDatas = {}
end
function PlayerService:LoadData(Player)
-- Player Data
local PlayerData = {}
-- Checks if the Player Data is already there
local _0 = self.PlayerDatas
local _1 = Player.Name
local _2 = _0[_1]
if not (_2 ~= 0 and _2 == _2 and _2 ~= "" and _2) then
local _3 = self.PlayerDatas
local _4 = Player.Name
local _5 = PlayerData
-- ▼ Map.set ▼
_3[_4] = _5
-- ▲ Map.set ▲
end
-- returns it
local _3 = self.PlayerDatas
local _4 = Player.Name
return _3[_4]
end
function PlayerService:GetData(Player)
-- Checks if it exists and returns it
local _0 = self.PlayerDatas
local _1 = Player.Name
local _2 = _0[_1]
if not (_2 ~= 0 and _2 == _2 and _2 ~= "" and _2) then
return nil
else
local _3 = self.PlayerDatas
local _4 = Player.Name
return _3[_4]
end
end
end
return {
PlayerService = PlayerService,
}
Player Joins, and all scripts call this function at the same time on player added
Script A calls :GetData(player) – data doesn’t exist, load data
Script B calls :GetData(player) – data hasn’t finished loaded from Script A, so Script B loads data too
Script C calls :GetData(player) – data hasn’t loaded yet from either A or B so it loads again
as you can see data was loaded 3 times because of yielding
I’ll likely just go with my first idea as I don’t have to worry about yielding at all
What I do is whenever I load the data of a player, I add the player into a data loading queue and when the data is finally loaded, I remove it out of the queue. On my DataService.Client:Get() method, I check if the data is currently loading and yield until it is finally loaded.
function DataService.Load(dataStore, player)
DataLoadingQueue[player.Name] = {DataLoaded = RemoteSignal.new()}
local profile = dataStore:LoadProfileAsync(("%s_%s"):format("Player_", player.UserId), "ForceLoad")
end
function DataService.Client:Get(player)
-- Player's data is in loading queue
if (DataLoadingQueue[player.Name] ~= nil) then
DataLoadingQueue[player.Name].DataLoaded:Wait()
end
return HttpService:JSONEncode(self.Server:Get(player))
end