153 lines
4.2 KiB
Plaintext
153 lines
4.2 KiB
Plaintext
|
-- 数据存储代理
|
||
|
local ArchiveProxy = {}
|
||
|
|
||
|
--> Services
|
||
|
local CollectionService = game:GetService("CollectionService")
|
||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||
|
local DataStoreService = game:GetService("DataStoreService")
|
||
|
local ServerStorage = game:GetService("ServerStorage")
|
||
|
local RunService = game:GetService("RunService")
|
||
|
local Players = game:GetService("Players")
|
||
|
|
||
|
--> Dependencies
|
||
|
local GameConfig = require(ReplicatedStorage.Data.GameConfig)
|
||
|
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
|
||
|
|
||
|
--> Variables
|
||
|
local UserData = DataStoreService:GetDataStore("UserData")
|
||
|
local SameKeyCooldown = {}
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
function ArchiveProxy:IsPlayerDataLoaded(Player: Player)
|
||
|
local timeout = 5
|
||
|
local start = os.clock()
|
||
|
while not Player:GetAttribute("DataLoaded") do
|
||
|
if os.clock() - start > timeout then
|
||
|
return false -- 超时
|
||
|
end
|
||
|
Player:GetAttributeChangedSignal("DataLoaded"):Wait()
|
||
|
end
|
||
|
return true -- 成功加载
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
local PlayerData = Instance.new("Configuration")
|
||
|
PlayerData.Name = "PlayerData"
|
||
|
PlayerData.Parent = ReplicatedStorage
|
||
|
|
||
|
local _warn = warn
|
||
|
local function warn(warning: string)
|
||
|
_warn("DataManager Failure: ".. warning)
|
||
|
end
|
||
|
|
||
|
-- Attempt to save user data. Returns whether or not the request was successful.
|
||
|
local function SaveData(Player: Player): boolean
|
||
|
if not Player:GetAttribute("DataLoaded") then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
local pData = PlayerData:FindFirstChild(Player.UserId)
|
||
|
local StarterGear = Player:FindFirstChild("StarterGear")
|
||
|
if not pData or not StarterGear then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- Same Key Cooldown (can't write to the same key within 6 seconds)
|
||
|
if SameKeyCooldown[Player.UserId] then
|
||
|
repeat task.wait() until not SameKeyCooldown[Player.UserId]
|
||
|
end
|
||
|
SameKeyCooldown[Player.UserId] = true
|
||
|
task.delay(6, function()
|
||
|
SameKeyCooldown[Player.UserId] = nil
|
||
|
end)
|
||
|
|
||
|
-- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync --
|
||
|
local DataToSave = {}
|
||
|
|
||
|
-- Save to DataStore --
|
||
|
local Success
|
||
|
for i = 1, 3 do
|
||
|
Success = xpcall(function()
|
||
|
return UserData:SetAsync("user/".. Player.UserId, DataToSave, {Player.UserId})
|
||
|
end, warn)
|
||
|
|
||
|
if Success then
|
||
|
break
|
||
|
end
|
||
|
task.wait(6)
|
||
|
end
|
||
|
|
||
|
if Success then
|
||
|
print(("DataManager: User %s's data saved successfully."):format(Player.Name))
|
||
|
else
|
||
|
warn(("DataManager: User %s's data failed to save."):format(Player.Name))
|
||
|
end
|
||
|
|
||
|
return Success
|
||
|
end
|
||
|
|
||
|
-- Attempt to load user data. Returns whether or not the request was successful, as well as the data if it was.
|
||
|
local function LoadData(Player: Player): (boolean, any)
|
||
|
local Success, Response = xpcall(function()
|
||
|
return UserData:GetAsync("user/".. Player.UserId)
|
||
|
end, warn)
|
||
|
|
||
|
if Success and Response then
|
||
|
print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level))
|
||
|
else
|
||
|
print(("DataManager: User %s had no data to load from."):format(Player.Name))
|
||
|
end
|
||
|
return Success, Response
|
||
|
end
|
||
|
|
||
|
local function OnPlayerAdded(Player: Player)
|
||
|
local Success, Data = LoadData(Player)
|
||
|
|
||
|
if not Success then
|
||
|
CollectionService:AddTag(Player, "DataFailed")
|
||
|
Player:Kick("Data unable to load. DataStore Service may be down. Please rejoin later.")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if not ArchiveProxy.pData then
|
||
|
ArchiveProxy.pData = {}
|
||
|
end
|
||
|
|
||
|
ArchiveProxy.pData[Player.UserId] = Data
|
||
|
Player:SetAttribute("DataLoaded", true)
|
||
|
end
|
||
|
|
||
|
local function OnPlayerRemoving(Player: Player)
|
||
|
SaveData(Player)
|
||
|
ArchiveProxy.pData[Player.UserId] = nil
|
||
|
ReplicatedStorage.Remotes.PlayerRemoving:Fire(Player)
|
||
|
end
|
||
|
|
||
|
Players.PlayerAdded:Connect(OnPlayerAdded)
|
||
|
for _, Player in Players:GetPlayers() do
|
||
|
OnPlayerAdded(Player)
|
||
|
end
|
||
|
|
||
|
-- Save on leave
|
||
|
Players.PlayerRemoving:Connect(OnPlayerRemoving)
|
||
|
|
||
|
|
||
|
-- Server closing (save)
|
||
|
game:BindToClose(function()
|
||
|
task.wait(RunService:IsStudio() and 1 or 10)
|
||
|
end)
|
||
|
|
||
|
|
||
|
-- Auto-save
|
||
|
task.spawn(function()
|
||
|
while true do
|
||
|
task.wait(60)
|
||
|
for _, Player in Players:GetPlayers() do
|
||
|
task.defer(SaveData, Player)
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
return ArchiveProxy
|