I need help with ProfileService

Hi, I’m making a game where you bring your unit from inventory into the game place kind of like a Tower Defense.

I’m new to profileservice (literally like 3 hours ago) and I need help with how would I:
-Update/Save/Store data
-Get data from profileservice from server/local scripts
-Get data that’s not in the leaderboard (I don’t want the roblox leaderboard)

Also I’m having problem with loading the name to my GUI
codes:

Code in the GUI

local player = game.Players.LocalPlayer
local spawnevent = game.ReplicatedStorage.SpawnEvent
local folderownerevent = game.ReplicatedStorage.FolderOwnerEvent
local datamanager = require(game.ServerScriptService.Modules.DataManager)

local playerfolder = nil

while playerfolder == nil do
	for i, v in pairs(game.Workspace.Bots:GetChildren()) do
		if v.Name == player.Name then
			playerfolder = v
			print("Got "..player.Name.."'s folder")
		end
	end
end

script.Parent.TextLabel.Text = player:WaitForChild("Unit1", math.huge).Value

script.Parent.MouseButton1Up:Connect(function()
	local Unit = player:WaitForChild("Unit1", math.huge).Value
	spawnevent:FireServer(playerfolder, Unit)
end)

Data Manager:

local Players = game:GetService("Players")
local serverscriptservice = game:GetService("ServerScriptService")
local mod = serverscriptservice:WaitForChild("Modules")

local ProfileService = require(mod:WaitForChild("ProfileService"))
local template = require(script:WaitForChild("Template"))
local leaderstats = require(script:WaitForChild("Leaderstats"))

local datakey = "_DataStore"
local profileStore = ProfileService.GetProfileStore(datakey, template)

local datamanager = {}
datamanager.Profiles = {}

local function PlayerAdded(player: Player)
	local profile = profileStore:LoadProfileAsync("Player_"..player.UserId)
	
	if profile ~= nil then
		profile:AddUserId(player.UserId)
		profile:Reconcile()
		
		profile:ListenToRelease(function()
			datamanager.Profiles[player] = nil
			player:Kick("Cannot Find Player's Data Profile")
		end)
		
		if player:IsDescendantOf(Players) then
			datamanager.Profiles[player] = profile
			leaderstats:Create(player, profile)
		else
			profile:Release()
		end
	else
		player:Kick("Cannot Find Player's Data Profile")
	end
end

local function PlayerRemoving(player: Player)
	local profile = datamanager.Profiles[player]
	
	if profile ~= nil then
		profile:Release()
	end
end

for _, player in Players:GetPlayers() do
	task.spawn(PlayerAdded, player)
end

Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)

return datamanager

Template:

local module = {
	Cash = "0",
	Unit1 = "",
	Unit2 = "",
	Unit3 = "",
	Unit4 = "",
	Unit5 = "",
	Unit6 = "",
	BaseSkin = ""
}

return module

Leaderstats:

local module = {}

function module:Create(player: Player, profile)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local cash = Instance.new("IntValue")
	cash.Name = "Cash"
	cash.Value = profile.Data.cash
	cash.Parent = leaderstats
end

return module

Simple Explanation/Introduction and the fix would be greatly appreciated, thanks!

1 Like

I think using datastores are much better at saving datas

1 Like

the reason I uses profile service is because datastore is a pain to use in the first place

1 Like

Profile Service already uses DataStore, but makes it easier and more reliable

wait, really?? thats actually good

1 Like

Did you think it was using a third party database?

1 Like

bro i was too stupid that i thought profile service was actually a roblox service

3 Likes

If you want to have easy access of read and write, I’d recommend replicating the data in folders, for example: Cash goes in leaderstats, Unit1->Unit6 goes in Units. Whenever any of the value are changed in the folders, update the profile accordingly, this is how i usually do it

Then whenever you want to edit the data, just change the value in the server!

3 Likes

Hmm, I think the easiest thing to do for saving/loading player data is to simply create a method for them. Example:

function datamanager:GetData(player)
	local profile = datamanager.Profiles[player]

	if profile ~= nil then
		return profile.Data
	end
end

function DataManager:UpdateData(player, dataToUpdate, newData)
	local data = self:GetData(player)

	if data ~= nil then
		local oldDataValueType = typeof(data[dataToUpdate])

		if oldDataValueType == typeof(newData) then
			data[dataToUpdate] = newData
			
			if dataToUpdate == "Something" then -- remove this if you do not need to update any leaderstats value (those values displayed on game leaderboard)
				player.leaderstats["Something"].Value = newData
			end
		end
	end
end

As for displaying your data to a GUI, you can’t access DataStore from client (I see you requiring the datamanager module which is inside ServerScriptService → the client can’t even see whats inside lol)

So, I recommend you to use ReplicaService:
ReplicaService

If you do use ReplicaService tho, you need to change your :UpdateData() method

function DataManager:UpdateData(player, dataToUpdate, newData)
	local data = self:GetData(player)
	local dataReplica = -- reference the replica of player's data here

	if data ~= nil then
		local oldDataValueType = typeof(data[dataToUpdate])

		if oldDataValueType == typeof(newData) then
			data[dataToUpdate] = newData
			dataReplica:SetValue({dataToUpdate}, newData)
			
			if dataToUpdate == "Something" then -- remove this if you do not need to update any leaderstats value (those values displayed on game leaderboard)
				player.leaderstats["Something"].Value = newData
			end
		end
	end
end
2 Likes

are you suggesting that I should do something like this?

local module = {}

function module:Create(player: Player, profile)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local cash = Instance.new("IntValue")
	cash.Name = "Cash"
	cash.Value = profile.Data.cash
	cash.Parent = leaderstats
	
	local Units = Instance.new("Folder")
	Units.Name = "Units"
	Units.Parent = player
	
	local Unit1 = Instance.new("IntValue")
	Unit1.Name = "Unit1"
	Unit1.Value = profile.Data.Unit1
	Unit1.Parent = Units
end

return module

not exactly, the way i potray is: the data within the profile data will be synced with the leaderstats/other folders (not sure if theres any flaws but this is how I go with it in my games)

for example, if you change the cash value, it will update the profile data’s cash value as well

1 Like

Can you give me a mock example?

I took one of my module for replication that supports multidimensional arrays, it works like the thing i just said but practically.

Module
-- // Package

local DataReplication = {}

-- // Functions

function DataReplication:BindProfileToFolder(folder: Folder, linkedData, path)	
	local function ConnectDataReference(ref: ValueBase)
		local function OnValueChanged()			
			if path then
				local DataTokens = path:split('.')
				local BufferName = table.remove(DataTokens, 1)

				local Buffer = linkedData[BufferName]

				for _, token in DataTokens do
					Buffer = Buffer[token]
					BufferName = token
				end

				Buffer[ref.Name] = (ref:IsA('ObjectValue') and ref.Name or ref.Value)
			else
				linkedData[ref.Name] = ref.Value
			end
		end

		ref:GetPropertyChangedSignal('Value'):Connect(OnValueChanged)
		OnValueChanged()
	end
	
	local function ConnectDataReferenceOnRemove(ref: ValueBase)
		if path then
			local DataTokens = path:split('.')
			local BufferName = table.remove(DataTokens, 1)

			local Buffer = linkedData[BufferName]

			for _, token in DataTokens do
				Buffer = Buffer[token]
				BufferName = token
			end

			Buffer[ref.Name] = nil
		else
			linkedData[ref.Name] = nil
		end
	end
	
	folder.ChildAdded:Connect(ConnectDataReference)
	folder.ChildRemoved:Connect(ConnectDataReferenceOnRemove)
	
	for _, reference: ValueBase in folder:GetChildren() do
		ConnectDataReference(reference)
	end
end

-- ex paths: { 'Inventory.Gears', 'Inventory.Accessories' }
function DataReplication:ReplicateData(paths: {string}, player: Player, linkedData)
	for _, path in paths do
		local DataTokens = path:split('.')
		
		local BufferName = table.remove(DataTokens, 1)
		local Buffer = linkedData[BufferName]
				
		for _, token in DataTokens do
			Buffer = Buffer[token]
			BufferName = token
		end
		
		local Folder = Instance.new('Folder')
		Folder.Name = BufferName
		
		for k, v in Buffer do
			local ReferenceName = type(k) == 'number' and v or k
			local ValueType = type(v)

			ValueType = ValueType == 'boolean' and 'bool' or ValueType

			local DataReference = Instance.new(`{ValueType:gsub('^.', string.upper)}Value`)
			DataReference.Name = ReferenceName
			DataReference.Value = v

			DataReference.Parent = Folder
		end
		
		DataReplication:BindProfileToFolder(Folder, linkedData, path)
		
		Folder.Parent = player
	end
end

return DataReplication

Usage Example:

		DataReplication:BindProfileToFolder(
			playerProfile:CreateProfileLeaderstats(player, { 'PartBux', 'Minutes' }), -- creates the leaderstats
			ProfileData
		) -- binds it
		
		DataReplication:ReplicateData(
			{
				'DialogueMemory',
				'Inventory.Gears', 'Inventory.Accessories', 'Inventory.BoxSkins',
				'EquippedAccessories'
			},
			player, ProfileData
		) -- create the folders and values, then bind it automatically

result: …
image