Implementing method to warn and kick player if DataStore doesn't load?

--// Services \\--
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local DataStoreService = game:GetService("DataStoreService")

--// Variables \\--
local dataStore = DataStoreService:GetDataStore("TestData")

--// Modules \\--
local Modules = ReplicatedStorage:FindFirstChild("Modules")
local ParticleEffects = require(Modules.ParticleEffects)

--// Objects to clone \\--
local objectsToClone = ReplicatedStorage:FindFirstChild("ObjectsToClone")
local levelUpSFX = objectsToClone:FindFirstChild("LevelUpSFX")
local levelUpParticle = objectsToClone:FindFirstChild("LevelUpReference").Attachment

--// Functions \\--

-- Budget checking for retries
local function waitForRequestBudget(requestType)
	local currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)
	while currentBudget < 3 do
		currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)
		task.wait(5)
	end
end

-- Sets up player data
local function setUp(player)
	local userId = player.UserId
	local key = "Player_" .. userId
	
	player:SetAttribute("isVip", false)
	player:SetAttribute("currentPowerUp", "None")
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	
	local points = Instance.new("NumberValue")
	points.Name = "Points"
	
	local coins = Instance.new("NumberValue")
	coins.Name = "Coins"
	
	local level = Instance.new("NumberValue")
	level.Name = "Level"
	
	local exp = Instance.new("NumberValue")
	exp.Name = "Exp"
	
	local success, returnValue
	repeat
		waitForRequestBudget(Enum.DataStoreRequestType.GetAsync)
		success, returnValue = pcall(dataStore.GetAsync, dataStore, key)
	until success or not Players:FindFirstChild(player.Name)
	
	if success then
		if returnValue == nil then
			returnValue = {
				Points = 0,
				Coins = 20,
				Level = 1,
				Exp = 0,
			}
		end
	
		points.Value = returnValue["Points"] or 0
		coins.Value = returnValue["Coins"] or 20
		level.Value = returnValue["Level"] or 1
		exp.Value = returnValue["Exp"] or 0
	
		points.Parent = leaderstats
		coins.Parent = leaderstats
		level.Parent = leaderstats
		exp.Parent = leaderstats

		leaderstats.Parent = player
		
		exp:GetPropertyChangedSignal("Value"):Connect(function()
			if exp.Value >= level.Value * 100 then
				
				local character = player.Character
				local HumanoidRootPart = character:FindFirstChild("HumanoidRootPart")
				local levelUpParticleClone = levelUpParticle:Clone()
				local levelUpSFXClone = levelUpSFX:Clone()
				
				exp.Value -= level.Value * 100
				level.Value += 1
				
				levelUpParticleClone.Parent = HumanoidRootPart
				levelUpSFXClone.Parent = HumanoidRootPart
				levelUpSFXClone:Play()
				
				levelUpSFXClone.Ended:Connect(function()
					levelUpSFXClone:Destroy()
					levelUpSFXClone = nil
				end)
				
				ParticleEffects.EmitParticles(levelUpParticleClone, character)
				
			end
		end)
		
	else
		print("Error loading "..player.Name.."'s data!")
	end	
end

-- Saving player data
local function save(player, dontWait)
	local userId = player.UserId
	local key = "Player_" .. userId
	local leaderstats = player:FindFirstChild("leaderstats")
	
	if leaderstats then
		local pointValue = leaderstats.Points.Value
		local coinValue = leaderstats.Coins.Value
		local levelValue = leaderstats.Level.Value
		local expValue = leaderstats.Exp.Value
		
		local dataTable = {
			Points = pointValue,
			Coins = coinValue,
			Level = levelValue,
			Exp = expValue,
		}
		
		local success, returnValue 
		
		repeat
			if not dontWait then
				waitForRequestBudget(Enum.DataStoreRequestType.SetIncrementAsync)				
			end
			success, returnValue = pcall(dataStore.UpdateAsync, dataStore, key, function()
				return dataTable
			end)
		until success

		if success then
			print(player.Name.."'s data has been saved!")
		else
			print("There was an error saving "..player.Name.."'s data!")
		end
	end
end

-- Handling saving for players already in game
for _, player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(setUp)(player)
end

-- Delays the game from shutting down by X seconds so the PlayerRemoving Event will 100% fire
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)
				leftPlayers -= 1
				if leftPlayers == 0 then
					finished:Fire()
				end
			end)()
		end
		finished.Event:Wait()
	end
end

Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)

--// Autosaving every 5 minutes
while true do 
	task.wait(300)
	for _, player in ipairs(Players:GetPlayers()) do
		coroutine.wrap(save)(player)
	end
end

Hello I’m currently working on a DataStore and I followed a DataStore Tutorial and changed it to my needs. However, I want to implement a way to warn the player if the data hasn’t loaded completely and also implement a method to kick the player if data hasn’t loaded but I’m unsure of where to place it?

Datastores are something that I have difficulties with and I can’t think of a way to reciprocate datastores failing to test if my methods would work. In addition, is there anything I would need to improve for this datastore? I noticed when players leave a moment after it autosave, queues begin to throttle. All help is appreciated!

Thank you!

Although I do not have a good solution to your issue, I’d like to recommend you to switch to either DataStore2 or ProfileService, as they are much better and will likely solve your issues. ProfileService

1 Like

I was considering using DataStore2 or ProfileService, however, after looking through the modules I have some questions:

  • How can I display a global leaderboard if I’m not using roblox’s leaderstats?
  • Do I have to replicate to the client, a change in the player’s stats if a change occurs on the server?

Overall, they seem like great modules, however, it looks a bit complicated for me to understand how to use them properly.

You could try simply pcall() wrapping a conditional that determines whether or not the stats have loaded.

if errormsg then kick.