【问题标题】:dictionary help/DataStore字典帮助/数据存储
【发布时间】:2019-11-08 02:52:55
【问题描述】:

问题是我有一个字典来保存我的所有数据,并且它应该能够在复制存储中变成一个目录,其中所有值都是字符串,然后在玩家离开时又变成带有所有键的字典。但是,我不知道如何变成字典(带键)。

我坐了几个小时测试东西,但在第一层值之后,我无法想办法将更深的值和键放入表中

local DataTable = 
{
    ["DontSave_Values"] =
    {
        ["Stamina"] = 100;
    };
    ["DontSave_Debounces"] = 
    {

    };
    ["TestData"] = 1;
    ["Ship"] = 
    {
        ["Hull"] = "Large_Ship";
        ["Mast"] = "Iron_Tall";
        ["Crew"] = 
        {
            ["Joe One"] = 
            {
                ["Shirt"] = "Blue";
                ["Pants"] = "Green"
            };
            ["Joe Two"] = 
            {
                ["Shirt"] = "Silver";
                ["Pants"] = "Brown";
                ["Kids"] = 
                {
                    ["Joe Mama1"] =
                    {
                        ["Age"] = 5
                    };
                    ["Joe Mama2"]=
                    {
                        ["Age"] = 6
                    };
                }
            };
        }
    };
    ["Level"] = 
    {
    };
    ["Exp"] = 
    {
    };
}
------Test to see if its an array
function isArray(Variable)
    local Test = pcall(function() 
        local VarBreak = (Variable.." ")
    end)
    if Test == false then
        return true
    else 
        return false
    end
end

------TURNS INTO FOLDERS 
function CreateGameDirectory(Player, Data)
    local mainFolder = Instance.new("Folder")
    mainFolder.Parent = game.ReplicatedStorage
    mainFolder.Name = Player.UserId
    local function IterateDictionary(Array, mainFolder)
        local CurrentDirectory = mainFolder
        for i,v in pairs(Array) do
            if isArray(v) then
                CurrentDirectory = Instance.new("Folder", mainFolder)
                CurrentDirectory.Name = i
                for o,p in pairs(v) do
                    if isArray(p) then
                        local TemporaryDir = Instance.new("Folder", CurrentDirectory)
                        TemporaryDir.Name = o
                        IterateDictionary(p, TemporaryDir)
                    else
                        local NewValue = Instance.new("StringValue", CurrentDirectory)
                        NewValue.Name = o
                        NewValue.Value = p
                    end
                end
            else
                local value = Instance.new("StringValue", mainFolder)
                value.Name = i
                value.Value = v
            end
        end
    end
    IterateDictionary(Data, mainFolder)
end


------To turn it back into a table
function CreateTable(Player)
    local NewDataTable = {}
    local Data = RS:FindFirstChild(Player.UserId)
    local function DigDeep(newData, pData, ...)
        local CurrentDir = newData
        for i,v in pairs(pData:GetChildren()) do
            if string.sub(v.Name,1,8) ~= "DontSave" then

            end
        end
    end
    DigDeep(NewDataTable, Data)
    return NewDataTable
end

我希望当玩家离开时运行 createtable 函数并将复制存储中的所有实例转回带有键的字典。

【问题讨论】:

    标签: lua roblox


    【解决方案1】:

    为什么不在您的数据表中存储额外的信息,以帮助轻松地来回转换。例如,为什么不让您的数据看起来像这样:

    local ExampleData = {
        -- hold onto your special "DON'T SAVE" values as simple keys in the table.
       DONTSAVE_Values = {
           Stamina = 0,
       },
    
        -- but every element under ReplicatedStorage will be used to represent an actual Instance.
        ReplicatedStorage = {
    
            -- store an array of Child elements rather than another map.
            -- This is because Roblox allows you to have multiple children with the same name.
            Name = "ReplicatedStorage",
            Class = "ReplicatedStorage",
            Properties = {},
            Children = {
                {
                    Name = "Level",
                    Class = "NumberValue",
                    Properties = {
                        Value = 0,
                    },
                    Children = {},
                },
                { 
                    Name = "Ship", 
                    Class = "Model",
                    Properties = {},
                    Children = {
                        {
                          -- add all of the other instances following the same pattern :
                          -- Name, Class, Properties, Children
                        },
                    },
                },
            }, -- end list of Children
        }, -- end ReplicatedStorage element
    };
    

    您可以使用简单的递归函数创建此表:

    -- when given a Roblox instance, generate the dataTable for that element
    local function getElementData(targetInstance)
        local element = {
            Name = targetInstance.Name,
            Class = targetInstance.ClassName,
            Properties = {},
            Children = {},
        }
    
        -- add special case logic to pull out specific properties for certain classes
        local c = targetInstance.ClassName 
        if c == "StringValue" then
            element.Properties = { Value = targetInstance.Value }
        -- elseif c == "ADD MORE CASES HERE" then
        else
            warn(string.format("Not sure how to parse information for %s", c))
        end
    
       -- iterate over the children and populate their data
       for i, childInstance in ipairs(targetInstance:GetChildren()) do
           table.insert( element.Children, getElementData(childInstance))
       end
    
       -- give the data back to the caller
       return element
    end
    
    
    -- populate the table
    local Data = {
        ReplicatedStorage = getElementData(game.ReplicatedStorage)
    }
    

    现在Data.ReplicatedStorage.Children 应该有整个文件夹的数据表示。如果愿意,您甚至可以将整个表格保存为字符串,将其传递给 HttpService:JSONEncode()

    当您准备好将它们转换回实例时,请使用您存储的数据为您提供有关如何重新创建元素的足够信息:

    local function recreateElement(tableData, parent)
        -- special case ReplicatedStorage
        if tableData.Class == "ReplicatedStorage" then
            -- skip right to the children
            for i, child in ipairs(tableData.Children) do
                recreateElement(child, parent)
            end
    
            -- quick escape from this node
            return
        end
    
        -- otherwise, just create elements from their data
        local element = Instance.new(tableData.Class)
        element.Name = tableData.Name
    
        -- set all the saved properties
        for k, v in pairs(tableData.Properties) do
            element[k] = v
        end
    
        -- recreate all of the children of this element
        for i, child in ipairs(tableData.Children) do
           recreateElement(child, element)
        end
    
        -- put the element into the workspace
        element.Parent = parent
    end
    
    -- populate the ReplicatedStorage from the stored data
    recreateElement( Data.ReplicatedStorage, game.ReplicatedStorage)
    

    您应该注意选择保存这些数据的方式和时间。如果你在玩多人游戏,你应该小心这种逻辑只为第一个加入服务器的玩家更新 ReplicatedStorage。否则,您将面临玩家加入并覆盖其他所有人所做的一切的风险。

    由于无法迭代 Roblox 实例的属性,因此您必须手动更新 getElementData 函数以正确存储您关心的每种对象类型的信息。希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      如果其他人有这个问题,我使用的解决方案只是将实例海峡转换为 JSON 格式(我使用名称作为键)。这样我就可以保存它,然后当玩家重新加入时,我只是使用 JSONDecode 将它变成我需要的字典。

      function DirToJSON(Player)
          local NewData = RS:FindFirstChild(Player.UserId)
          local JSONstring="{"
          local function Recurse(Data)
              for i, v in pairs(Data:GetChildren()) do
                  if v:IsA("Folder") then
                      if #v:GetChildren() < 1 then
                          if i == #Data:GetChildren()then
                              JSONstring=JSONstring..'"'..v.Name..'":[]'
                          else
                              JSONstring=JSONstring..'"'..v.Name..'":[],'
                          end
                      else
                          JSONstring=JSONstring..'"'..v.Name..'":{'
                          Recurse(v)
                          if i == #Data:GetChildren()then
                              JSONstring=JSONstring..'}'
                          else
                              JSONstring=JSONstring..'},'
                          end
                      end
                  else
                      if i == #Data:GetChildren()then
                          JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'"'
                      else
                          JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'",'
                      end     
                  end
              end
          end
          Recurse(NewData)
          JSONstring = JSONstring.."}"
          return(JSONstring)
      end
      

      【讨论】: