1
votes

The Issue is I have a dictionary that holds all my data and its supposed to be able to turn into a directorys in replicated storage with all the values being strings then turn back into a dictionary with all the keys when the player leave. However, I cant figure out how to turn into a dictionary(With keys).

ive sat for a few hours testing things but after the first layer of values I cant get figure out a way to get the deeper values and keys into the table

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

I expected to when the player leaves run createtable function and turn all the instances in replicated storage back into a dictionary with keys.

2

2 Answers

0
votes

Why not just store extra information in your data table to help make it easy to convert back and forth. As an example, why not have your data look like this :

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
};

You can create this table with a simple recursive function :

-- 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)
}

Now Data.ReplicatedStorage.Children should have a data representation of the entire folder. You could even save this entire table as a string by passing it to HttpService:JSONEncode() if you wanted to.

When you're ready to convert these back into instances, use the data you've stored to give you enough information on how to recreate the elements :

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)

You should be careful about how and when you choose to save this data. If you are playing a multiplayer game, you should be careful that this kind of logic only updates ReplicatedStorage for the first player to join the server. Otherwise, you run the risk of a player joining and overwriting everything that everyone else has been doing.

Since there isn't a way to iterate over properties of Roblox Instances, you'll have to manually update the getElementData function to properly store the information you care about for each type of object. Hopefully this helps!

0
votes

If anyone else has this problem the solution I used was just to convert the instances strait into JSON format(I used the names as keys). so that way I can save it, and then when the player rejoins I just used JSONDecode to turn it into the dictionary I needed.

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