Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Why does this return an empty folder?

local DSService = game:GetService('DataStoreService'):GetDataStore('WOOPPOLICE1')
local HttpService = game:GetService('HttpService')

local defaultData = {
	PlayerStats = {
		Name = "None",
		Title = "None",
		Rank = "None",
		Race = "None",
		Birth = "None",
		Shirt = "None",
		Pants = "None",
		Class = "None",
		Lives = 5,
		Days = 0,
		Money = 0,
		Karma = 0,
		Gender = "None",
		LastRewardTick = "",
		}, 
	Skills = {
		Skill1 = {
			SkillEXP = 0,
			SkillLevel = 1,
			SkillRank = "Novice",			
		},
	},
	Equipment = {
		Armor = "None",
		MainHand = "None",
		OffHand = "None",
		Epautlet = "None",
	},
	Inventory = {
		Tools = {},
		Materials = {},		
	}
}

local function buildDataEntryFromFolder(folder)
    local dataEntry = {}

    for _, valueObject in next, folder:GetChildren() do
        if valueObject:IsA("ValueBase") then
            dataEntry[valueObject.Name] = {valueType = valueObject.ClassName, value = valueObject.Value}
        end
    end

    return dataEntry
end

function unloadData(player, data)
	local newDataFolder = Instance.new("Folder", player)
	newDataFolder.Name = "PlayerData"
	local function recursive(folder, children)
		for x, y in pairs (children) do
			if typeof(y) == 'table' then
				local newFolder = Instance.new("Folder", folder)
				newFolder.Name = x
				recursive(newFolder, y)
			elseif typeof(y) == 'number' then
				local value = Instance.new("NumberValue", folder)
				value.Name = x
				value.Value = y
			elseif typeof(y) == 'string' then
				local value = Instance.new("StringValue", folder)
				value.Name = x
				value.Value = y
			elseif typeof(y) == 'boolean' then
				local value = Instance.new("BoolValue", folder)
				value.Name = x
				value.Value = y
			else
				local value = Instance.new("StringValue", folder)
				value.Name = x
				value.Value = y
			end
		end
	end
	recursive(newDataFolder, data)
end

local function savePlayerData(player)
    local dataEntry = buildDataEntryFromFolder(player.PlayerData)
    DSService:SetAsync(player.UserId, dataEntry)
end

local function loadPlayerData(player)
    local dataEntry = DSService:GetAsync(player.UserId)

    --  In case there's no data found for the player, construct the data folder from a blank template
    if not dataEntry then
        dataEntry = defaultData
    end

	unloadData(player, dataEntry)
	
end

game.Players.PlayerAdded:Connect(function(player)
	loadPlayerData(player)
end)

game.Players.PlayerRemoving:Connect(function(player)
	savePlayerData(player)
end)

Fresh data reset, it makes my folder. I change a value to see if it saves or not, I rejoin, and it makes the PlayerData folder, but nothing in it. Been working with this for 6'ish hours with so many different variations and at this point i'm just lost. Any help is VERY much appreciated.

1 comment

0

To clear things up you are saying it creates an accurate player data folder when it is fresh data and it only creates an empty folder after rejoining?

2 answers

✓ Accepted answer

Ok, so the function for turning the data into a folder and value based hierarchy works perfectly. However the problem is that when you go to parse that back into a dictionary you are making an error. The problem is you are looping through the top folder and only inserting data if the children are values. That's why see nothing in the folder because it excludes other folders and there are only folders in the top level. To solve this you should apply what you did in the unload function. Just use recursion and treat the folders as the tables and convert them. Heres my solution:

local DSService = game:GetService('DataStoreService'):GetDataStore('WOOPPOLICE1')
local HttpService = game:GetService('HttpService')

local defaultData = {
    PlayerStats = {
        Name = "None",
        Title = "None",
        Rank = "None",
        Race = "None",
        Birth = "None",
        Shirt = "None",
        Pants = "None",
        Class = "None",
        Lives = 5,
        Days = 0,
        Money = 0,
        Karma = 0,
        Gender = "None",
        LastRewardTick = "",
        }, 
    Skills = {
        Skill1 = {
            SkillEXP = 0,
            SkillLevel = 1,
            SkillRank = "Novice",           
        },
    },
    Equipment = {
        Armor = "None",
        MainHand = "None",
        OffHand = "None",
        Epautlet = "None",
    },
    Inventory = {
        Tools = {},
        Materials = {},     
    }
}

local function buildDataEntryFromFolder(folder)
    
    local function recursive(folder)
		local parentTable = {}
        for key,value in pairs(folder:GetChildren()) do -- go through all the primary children of the folder first
             if value:IsA("ValueBase") then
                  parentTable[key] = value.Value -- if is a normal value put it in its assigned data table
             elseif value:IsA("Folder") then
				local t = recursive(value)
				parentTable[key] = t -- treat folder like a new table
			end
        end
		return parentTable
    end
	local dataEntry = recursive(folder)
        -- i hope this makes sense how i kind of reversed the recursion process in the unloadData function
    return dataEntry
end

function unloadData(player, data)
    local newDataFolder = Instance.new("Folder", player)
    newDataFolder.Name = "PlayerData"
    local function recursive(folder, children)
        for x, y in pairs (children) do
            if typeof(y) == 'table' then
                local newFolder = Instance.new("Folder", folder)
                newFolder.Name = x
                recursive(newFolder, y)
            elseif typeof(y) == 'number' then
                local value = Instance.new("NumberValue", folder)
                value.Name = x
                value.Value = y
            elseif typeof(y) == 'string' then
                local value = Instance.new("StringValue", folder)
                value.Name = x
                value.Value = y
            elseif typeof(y) == 'boolean' then
                local value = Instance.new("BoolValue", folder)
                value.Name = x
                value.Value = y
            else
                local value = Instance.new("StringValue", folder)
                value.Name = x
                value.Value = y
            end
        end
    end
    recursive(newDataFolder, data)
end

local function savePlayerData(player)
    local dataEntry = buildDataEntryFromFolder(player.PlayerData)
    DSService:SetAsync(player.UserId, dataEntry)
end

local function loadPlayerData(player)
    local dataEntry = DSService:GetAsync(player.UserId)

    --  In case there's no data found for the player, construct the data folder from a blank template
    if not dataEntry then
        dataEntry = defaultData
    end

    unloadData(player, dataEntry)

end

game.Players.PlayerAdded:Connect(function(player)
    loadPlayerData(player)
end)

game.Players.PlayerRemoving:Connect(function(player)
    savePlayerData(player)
end)

1 comment

0

This did it! I never thought about the way you did the recursion. Excellent fix for sure.

Your save data was messed up, heres your fix (look into buildDataEntryFromFolder)

local DSService = game:GetService('DataStoreService'):GetDataStore('WOOPPOLICE1')

local defaultData = {
    PlayerStats = {
        Name = "None",
        Title = "None",
        Rank = "None",
        Race = "None",
        Birth = "None",
        Shirt = "None",
        Pants = "None",
        Class = "None",
        Lives = 5,
        Days = 0,
        Money = 0,
        Karma = 0,
        Gender = "None",
        LastRewardTick = "",
        }, 
    Skills = {
        Skill1 = {
            SkillEXP = 0,
            SkillLevel = 1,
            SkillRank = "Novice",           
        },
    },
    Equipment = {
        Armor = "None",
        MainHand = "None",
        OffHand = "None",
        Epautlet = "None",
    },
    Inventory = {
        Tools = {},
        Materials = {},     
    }
}

local function buildDataEntryFromFolder(folder)
    
	local dataEntry = {}
	
	local total = 0
	recursive = function(tFolder, pFolder) --tableFolder, parentFolder
		
		for _, data in pairs(pFolder:GetChildren()) do
			
			print(data, data:IsA("ValueBase"), data:IsA("ValueBase") and data.Value or false)
			tFolder[data.Name] = data:IsA("ValueBase") and data.Value or {}
			
			if #data:GetChildren() > 0 then
				
				recursive(tFolder[data.Name], data)
				
			end
						
		end
		
	end
	
	recursive(dataEntry, folder)
	
	return dataEntry
   
end

function unloadData(Player, tFolder)
   
	Instance.new("Folder", Player).Name = "PlayerData"

   function recursive(folder, children)
        for k, v in pairs(children) do

			local child = Instance.new(typeof(v), folder)
			child.Name = k
			
			if type(v) ~= "table" then
				child.Value = v
			else
				recursive(child, v)
			end

        end
    end

    recursive(Player.PlayerData, tFolder)

end

local function savePlayerData(player)
    local dataEntry = buildDataEntryFromFolder(player.PlayerData)
    local success, err = pcall(function() DSService:SetAsync(player.UserId, dataEntry) end)
	
	if success then
		print("Saved Successfully")
	else
		print("Error: " .. err)
	end
	
end

local function loadPlayerData(player)
	
    local dataEntry = DSService:GetAsync(player.UserId)

    --  In case there's no data found for the player, construct the data folder from a blank template
    if not dataEntry then
        dataEntry = defaultData
    end

    unloadData(player, dataEntry)

end

game.Players.PlayerAdded:Connect(loadPlayerData)

game.Players.PlayerRemoving:Connect(savePlayerData)

1 comment

0

Made an edit, this will save when you change value in SERVER not client, and load it

This discussion is preserved as it was. New answers, comments, and votes are closed.