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