DataStore with Session Lock

Here is some code I wrote. I intend to use this over Profile Service to eventually benefit from newer features that got added. I’m referring to Datastore v2.0.

local DataStoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")

local DataStore = DataStoreService:GetDataStore("Datastore")

local LOCK_EXPIRE_IN_SECONDS = 1 * 60 

local dataStoreModule = {
	playersData = {} 
}

function dataStoreModule.getTemplateData()
	return {
		sessionId = HttpService:GenerateGUID(false)
	}
end

task.spawn(function()
	while true do
		task.wait(LOCK_EXPIRE_IN_SECONDS / 2)
		for _, player in Players:GetPlayers() do
			local playerData = dataStoreModule.playersData[player.UserId]
			if playerData then
				local success, errorMessage = pcall(function()
					DataStore:UpdateAsync(player.UserId, function(oldPlayerData)
						if oldPlayerData then
							if oldPlayerData.sessionId and oldPlayerData.sessionId~=  playerData.sessionId then
								player:Kick('Your session is active on another server.')
							else
								warn(`{player.UserId} lock recently got released.`)
							end
							return
						else
							return playerData
						end
					end)
				end)
				if not success then
					warn(errorMessage)
				end
			end
		end
	end
end)

function dataStoreModule.loadPlayerData(player: Player)
	if dataStoreModule.playersData[player.UserId] then
		warn(`{player.UserId} data has already been loaded.`)
		return dataStoreModule.playersData[player]
	end
	local isSessionLocked = false
	local loadedPlayerData, dataStoreKeyInfo
	local success, result = pcall(function()
		DataStore:UpdateAsync(player.UserId, function(_loadedPlayerData, _dataStoreKeyInfo)
			loadedPlayerData, dataStoreKeyInfo = _loadedPlayerData, _dataStoreKeyInfo
			isSessionLocked = loadedPlayerData.sessionId ~= nil and (os.time() - dataStoreKeyInfo.UpdatedTime / 1000) < LOCK_EXPIRE_IN_SECONDS
			if isSessionLocked then
				return
			end
			_loadedPlayerData.sessionId = HttpService:GenerateGUID(false)
			return _loadedPlayerData
		end)
		if not loadedPlayerData then
			loadedPlayerData = dataStoreModule.getTemplateData()
		end
	end)
	if not success then
		warn(result)
		player:Kick("An error occurred while loading your data. Please try again later.")
		return
	elseif isSessionLocked then
		warn(`{player.UserId} attempted to join, but their session is locked.`)
		player:Kick(`Your session is temporarily locked. Please try again later.`)
		return
	end
	dataStoreModule.playersData[player.UserId] = loadedPlayerData
	return loadedPlayerData
end

function dataStoreModule.releasePlayerData(player: Player)
	local playerData = dataStoreModule.playersData[player.UserId]
	if not playerData then
		return
	end
	dataStoreModule.playersData[player.UserId] = nil
	local success, errorMessage = pcall(function()
		DataStore:UpdateAsync(player.UserId, function()
			playerData.sessionId = nil
			return playerData
		end)
	end)
	if not success then
		warn(errorMessage)
	end
end

return dataStoreModule
1 Like

Doesn’t Profile Service has Datastore v2 support though?

Ugh, I was unaware of that. Thanks for letting me know, I’ll look into it.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.