You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roblox中DataStoreService无法保存加载金币数据求助

Roblox DataStoreService 金币数据保存阻塞问题排查与修复

问题概述

使用DataStoreService实现金币(Coins)数据的保存与加载功能,预期玩家加入时加载已保存的金币数值,退出时保存当前金币数值,但保存环节的pcall出现阻塞,数据无法正常保存。

原代码

local players = game:GetService("Players") 
local dss = game:GetService("DataStoreService")
local gameData = dss:GetDataStore("Nara")
players.PlayerAdded:Connect(function(player) 

    local leaderstats = Instance.new("Folder") 
    leaderstats.Name = "leaderstats" 
    leaderstats.Parent = player 

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Parent = leaderstats

    local success, result = pcall(function()
        local r = gameData:GetAsync(player.UserId)["Coins"]
        
        return r
    end)

    if success then
        print("success: " .. result)
        coins.Value = result
    else
        print(result)
    end

end)

players.PlayerRemoving:Connect(function(player)
    print("player removed")
    local key = player.UserId
    
    local data = {
        ["Coins"] = player:WaitForChild("leaderstats").Coins.Value
    }
    
    local success, err = pcall(function()
        gameData:SetAsync(key, data)
    end)

    if success then
        print("Data saved successfully")
    else
        print("Failed to save data with error: ", err)
    end
    
end)

game.ReplicatedStorage.Buy.OnServerInvoke = function(player, item)
    if not player:FindFirstChild(item) then
        if player.leaderstats.Coins.Value >= game.ReplicatedStorage.Items:WaitForChild(item).cost.Value then
            player.leaderstats.Coins.Value = player.leaderstats.Coins.Value - game.ReplicatedStorage.Items:WaitForChild(item).cost.Value
            local owned = Instance.new("BoolValue")
            owned.Name = item
            owned.Parent = player
            player.Backpack:ClearAllChildren()
            game.ReplicatedStorage.Items:WaitForChild(item):Clone().Parent = player.Backpack
            return item
        else
            return "not enough coins"
        end
    else
        player.Backpack:ClearAllChildren()
        game.ReplicatedStorage.Items:WaitForChild(item):Clone().Parent = player.Backpack
        return "owned " .. item
    end
end

问题分析

  1. 加载数据时未处理空值:新玩家首次加入时,GetAsync返回nil,直接取["Coins"]会触发错误,导致加载逻辑失败,后续保存也可能受影响。
  2. PlayerRemoving时的阻塞风险:使用WaitForChild("leaderstats")可能因玩家对象快速销毁导致阻塞,无法获取金币数值。
  3. 无重试机制:DataStore调用可能因网络波动、服务限流等临时失败,单次调用没有重试会直接导致保存失败。
  4. 未处理服务器关闭场景:服务器关闭时,PlayerRemoving事件可能无法触发,导致剩余玩家数据丢失。
  5. 键名无前缀:直接使用UserId作为键名,可能与其他DataStore的键冲突,引发数据混乱。

修复方案

  1. 加载数据时设置默认值:判断GetAsync返回结果,若为nil则设置默认金币值(如0)。
  2. 避免WaitForChild阻塞:保存前先通过FindFirstChild检查leaderstats是否存在,存在再获取数值。
  3. 添加重试机制:对GetAsync和SetAsync添加循环重试,最多尝试3次,每次间隔1秒。
  4. 绑定服务器关闭事件:在服务器关闭时,遍历所有剩余玩家并保存数据。
  5. 键名添加前缀:使用"Player_" .. player.UserId作为DataStore键名,避免冲突。

完整修复代码

local players = game:GetService("Players") 
local dss = game:GetService("DataStoreService")
local gameData = dss:GetDataStore("Nara")
local MAX_RETRIES = 3 -- 最大重试次数
local RETRY_DELAY = 1 -- 重试间隔(秒)

-- 封装带重试的GetAsync
local function getPlayerData(playerId)
    local key = "Player_" .. playerId
    for i = 1, MAX_RETRIES do
        local success, result = pcall(function()
            return gameData:GetAsync(key)
        end)
        if success then
            return result or {Coins = 0} -- 数据不存在时返回默认值
        else
            warn("获取数据失败,正在重试(第" .. i .. "次):" .. result)
            task.wait(RETRY_DELAY)
        end
    end
    warn("获取数据失败,已达最大重试次数")
    return {Coins = 0} -- 最终失败返回默认值
end

-- 封装带重试的SetAsync
local function savePlayerData(playerId, data)
    local key = "Player_" .. playerId
    for i = 1, MAX_RETRIES do
        local success, err = pcall(function()
            gameData:SetAsync(key, data)
        end)
        if success then
            return true
        else
            warn("保存数据失败,正在重试(第" .. i .. "次):" .. err)
            task.wait(RETRY_DELAY)
        end
    end
    warn("保存数据失败,已达最大重试次数")
    return false
end

players.PlayerAdded:Connect(function(player) 
    local leaderstats = Instance.new("Folder") 
    leaderstats.Name = "leaderstats" 
    leaderstats.Parent = player 

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Parent = leaderstats

    -- 加载数据
    local playerData = getPlayerData(player.UserId)
    coins.Value = playerData.Coins
    print("玩家" .. player.Name .. "加载金币数据:" .. coins.Value)
end)

players.PlayerRemoving:Connect(function(player)
    print("玩家" .. player.Name .. "正在退出,开始保存数据")
    -- 检查leaderstats是否存在
    local leaderstats = player:FindFirstChild("leaderstats")
    if not leaderstats then
        warn("玩家" .. player.Name .. "的leaderstats不存在,跳过保存")
        return
    end
    local coins = leaderstats:FindFirstChild("Coins")
    if not coins then
        warn("玩家" .. player.Name .. "的Coins值不存在,跳过保存")
        return
    end

    local data = {
        Coins = coins.Value
    }
    local success = savePlayerData(player.UserId, data)
    if success then
        print("玩家" .. player.Name .. "数据保存成功")
    else
        warn("玩家" .. player.Name .. "数据保存失败")
    end
end)

-- 处理服务器关闭时的数据保存
game:BindToClose(function()
    print("服务器即将关闭,开始保存所有玩家数据")
    for _, player in ipairs(players:GetPlayers()) do
        local leaderstats = player:FindFirstChild("leaderstats")
        if leaderstats then
            local coins = leaderstats:FindFirstChild("Coins")
            if coins then
                local data = {Coins = coins.Value}
                savePlayerData(player.UserId, data)
            end
        end
        -- 等待数据保存完成
        task.wait(0.1)
    end
    print("所有玩家数据保存完成")
end)

game.ReplicatedStorage.Buy.OnServerInvoke = function(player, item)
    if not player:FindFirstChild(item) then
        local itemObj = game.ReplicatedStorage.Items:FindFirstChild(item)
        if not itemObj then
            return "item not found"
        end
        local cost = itemObj:FindFirstChild("cost")
        if not cost then
            return "cost not found"
        end
        local leaderstats = player:FindFirstChild("leaderstats")
        if not leaderstats then
            return "leaderstats not found"
        end
        local coins = leaderstats:FindFirstChild("Coins")
        if not coins then
            return "coins not found"
        end

        if coins.Value >= cost.Value then
            coins.Value = coins.Value - cost.Value
            local owned = Instance.new("BoolValue")
            owned.Name = item
            owned.Parent = player
            player.Backpack:ClearAllChildren()
            itemObj:Clone().Parent = player.Backpack
            return item
        else
            return "not enough coins"
        end
    else
        local itemObj = game.ReplicatedStorage.Items:FindFirstChild(item)
        if itemObj then
            player.Backpack:ClearAllChildren()
            itemObj:Clone().Parent = player.Backpack
        end
        return "owned " .. item
    end
end

内容的提问来源于stack exchange,提问作者njkilllergg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 13:44:54