-- 数据存储代理 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