Issue with My Data Store for Packing and Unpacking Player Data

Hello everyone! I have been utilizing various online sources to create a Datastore that I believed was functioning effectively. It involved packing and unpacking data written on a table to the player, allowing me to easily integrate this Datastore into any of my games. Unfortunately, while the Datastore works for a single player, it completely disregards any other players who join the game. Strangely enough, none of the desired folders and values are added to the player when they join for the first time if there is already another player in the game.

Here is the script for the Datastore:

local module = require(script.unite)

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local dataStore = DataStoreService:GetDataStore("datastore_021")

local default = {
	SessionLock = false,
	folder = {
		{
			folder_name = "info",
			children = {
				{
					int_name = "level",
					value = 0,

					children = {
						{
							int_name = "xp_needed",
							value = 25
						};
						{
							int_name = "xp",
							value = nil
						};
					};
				};
				{
					int_name = "wins",
					value = 0,
				};
				{
					bool_name = "music_enabled",
					value = true
				};
			}
		},
	}
}

local updateAsync = Enum.DataStoreRequestType.UpdateAsync

local function waitForRequestBudget()
	local currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)

	while currentBudget < 1 do
		currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)
		wait(5)
	end
end

local function setUp(player)
	local name = player.Name
	local userId = player.UserId
	local key = "Player_" .. userId

	local success, data, shouldWait
	repeat
		waitForRequestBudget()
		success, data = pcall(dataStore.UpdateAsync, dataStore, key, function(oldData)
			oldData = oldData or default
			if oldData.SessionLock then
				if os.time() - oldData.SessionLock < 1800 then
					shouldWait = true
				else
					oldData.SessionLock = os.time()
					data = oldData
					return data
				end
			else
				oldData.SessionLock = os.time()
				data = oldData
				return data
			end
		end)

		if shouldWait then
			task.wait(5)
			shouldWait = false
		end
	until (success and data) or not Players:FindFirstChild(name)
	if success and data then
		data = module.unpack(player, data.folder)
	end
end

local function save(player, dontLeave, dontWait)
	local userId = player.UserId
	local key = "Player_" .. userId

	local player_folder = player:FindFirstChild("info")
	local to_pack = {}
	module.pack(player_folder, to_pack)
	--warn(to_pack)

	local success
	repeat
		if not dontWait then
			waitForRequestBudget()
		end
		success = pcall(dataStore.UpdateAsync, dataStore, key, function(oldData)
			oldData = oldData or default
			oldData.folder = to_pack
			oldData.SessionLock = dontLeave and os.time() or nil
			return oldData
		end)
	until success
end

local function onShutdown()
	if RunService:IsStudio() then
		task.wait(2)
	else
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers

		for _, player in ipairs(allPlayers) do
			coroutine.wrap(function()
				save(player, nil, true)
				leftPlayers = leftPlayers - 1
				if leftPlayers == 0 then
					wait(2) -- Allow some time for the last save to complete
					game:Shutdown()
				end
			end)()
		end
	end
end

for _, player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(setUp)(player)
end

Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)

while true do
	wait(60)
	for _, player in ipairs(Players:GetPlayers()) do
		coroutine.wrap(save)(player, true)
	end
end

And here is the pack/unpack module that this Datastore uses:

local module = {}

module.unpack = function(parent, children)
	if not parent or not parent:IsA("Instance") then
		return
	end

	for i, child in ipairs(children) do
		local new 

		if child.folder_name then
			new = Instance.new("Folder")
			new.Name = child.folder_name
		elseif child.int_name then
			new = Instance.new("IntValue")
			new.Name = child.int_name
		elseif child.bool_name then
			new = Instance.new("BoolValue")
			new.Name = child.bool_name
		elseif child.string_name then
			new = Instance.new("StringValue")
			new.Name = child.string_name
		end

		if not new:IsA("Folder") and child.value then
			new.Value = child.value
		end

		new.Parent = parent

		if child.children then
			module.unpack(new, child.children)
		end
	end
end

module.pack = function(instance, Table)
	Table = Table or {} -- Initialize Table as an empty table if not provided

	local new_table = {}

	if instance:IsA("Folder") then
		new_table["folder_name"] = instance.Name
	elseif instance:IsA("IntValue") then
		new_table["int_name"] = instance.Name
	elseif instance:IsA("BoolValue") then
		new_table["bool_name"] = instance.Name
	elseif instance:IsA("StringValue") then
		new_table["string_name"] = instance.Name
	end

	if not instance:IsA("Folder") then
		new_table["value"] = instance.Value
	end

	if #instance:GetChildren() > 0 then
		local children_table = {}
		new_table["children"] = children_table

		for i, child in ipairs(instance:GetChildren()) do
			module.pack(child, children_table)
		end
	end

	table.insert(Table, new_table)

	return Table
end

return module

Thank you for taking the time to review my code and provide any assistance or insights you may have!

you should really consider using profileservice for player data, may look complex at first but its extremely simple and useful. also easy to drag and drop between games

1 Like