Save your player data with ProfileService! (DataStore Module)

Because Data table is nil I think.

1 Like

That doesn’t make sense though. When you assign a variable a specified integer, printing it returns the integer back regardless if it has value or not.

local test = 0
print(test)
--Output prints 0

I’m very frustrated and I’m not sure how to go about this.

1 Like

Can you show the Datamanager module’s :Get() Method?

It looks like It is returning nil.

1 Like
local Players = game:GetService("Players")
local ProfileService = require(game.ReplicatedStorage.ProfileService)

local ProfileStore = ProfileService.GetProfileStore(
	"Player",
	{
		coins = 0;
		gems = 0;
	}
)


local Profiles = {}

local function onPlayerAdded(player)
	local profile = ProfileStore:LoadProfileAsync(
		"Player_" .. player.UserId,
		"ForceLoad"
	)
	
	if profile then
		profile:ListenToRelease(function()
			Profiles[player] = nil
			player:Kick()
		end)
		
		if player:IsDescendantOf(Players) then
			Profiles[player] = profile
		else
			profile:Release()
		end
	else
		player:Kick()
	end
end

local function onPlayerRemoving(player)
	local profile = Profiles[player]
	if profile then
		profile:Release()
	end
end


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

local DataManager = {}

--:Get() Method
function DataManager:Get(player)
	local profile = Profiles[player]
	
	if profile then
		return profile
	end
end

return DataManager
1 Like

You should add this piece of code before playerAdded event:

for _,i in pairs(Players:GetPlayers()) do
    coroutine.wrap(onPlayerAdded)()
end

We do this because sometime the script misses the playerAdded event.

final script:

local Players = game:GetService("Players")
local ProfileService = require(game.ReplicatedStorage.ProfileService)

local ProfileStore = ProfileService.GetProfileStore(
	"Player",
	{
		coins = 0;
		gems = 0;
	}
)


local Profiles = {}

local function onPlayerAdded(player)
	local profile = ProfileStore:LoadProfileAsync(
		"Player_" .. player.UserId,
		"ForceLoad"
	)
	
	if profile then
		profile:ListenToRelease(function()
			Profiles[player] = nil
			player:Kick()
		end)
		
		if player:IsDescendantOf(Players) then
			Profiles[player] = profile
		else
			profile:Release()
		end
	else
		player:Kick()
	end
end

local function onPlayerRemoving(player)
	local profile = Profiles[player]
	if profile then
		profile:Release()
	end
end

for _,i in pairs(Players:GetPlayers()) do
    coroutine.wrap(onPlayerAdded)()
end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)

local DataManager = {}

--:Get() Method
function DataManager:Get(player)
	local profile = Profiles[player]
	
	if profile then
		return profile
	end
end

return DataManager
1 Like

Unfortunately, even with this added, it still gives me the same error regarding to concatenating string with nil.

1 Like

Sorry i made a mistake in it I saw now here’s thee edited snippet

for _,player in pairs(Players:GetPlayers()) do
    coroutine.wrap(onPlayerAdded)(player)
end

I still have the same result.

Though, I was doing some research and apparently searching inside of an array in a for loop will not work and return the elements inside the array as nil. Of course, this is just me speculating if this is the actual core problem.

Is there anyway I can define the player in the ServerScript without using a for loop?

Here is my script If it helps:

-- Stats Service
-- Beastcraft_Gaming
-- January 6, 2021

local DatastoreService: AeroService = {Client = {}}

local Players = game:GetService("Players") 

local ProfileService
DatastoreService.Profiles = {}

local ProfileTemplate: table = {
   ["Cash"] = 100,
   ["Exp"] = 0,
   ["Level"] = 0
}
function DatastoreService:Start()
	local ProfileStore = ProfileService.GetProfileStore(
        "Player_Stats",
        ProfileTemplate
    )

    local function PlayerAdded(player)
        local profile = ProfileStore:LoadProfileAsync(
            "Player_Stats_"..player.UserId,
            "ForceLoad"
        )

        if (profile ~= nil) then
            profile:Reconcile()
                profile:ListenToRelease(function()
                DatastoreService.Profiles[player] = nil
                player:Kick()
            end)
            if (player:IsDescendantOf(Players) == true) then
                DatastoreService.Profiles[player] = profile
                
                --self.Services.PlayerData.LeaderBoardService:MakeLeaderBoard(player, {["Cash"] = profile.Data.Cash, ["Exp"] = profile.Data.Exp, ['Level'] = profile.Data.Level})
                self:Fire("leaderstats", player, {["Cash"] = profile.Data.Cash, ["Exp"] = profile.Data.Exp, ['Level'] = profile.Data.Level})
            else
                profile:Release()
            end
        else
            player:Kick("There was a problem while loading your Data please rejoin")
        end
    end

    for _,player in pairs(Players:GetPlayers()) do
        coroutine.wrap(PlayerAdded)(player)
    end
    Players.PlayerAdded:Connect(PlayerAdded)

    Players.PlayerRemoving:Connect(function(player)
        local profile = DatastoreService.Profiles[player]
        if (profile ~= nil) then
            profile:Release()
        end
    end)
    
end

function DatastoreService:Init()
    ProfileService = self.Modules.ProfileService
    
    self:RegisterEvent("leaderstats")
    self:RegisterEvent("UpdateLeaderstats")
end


return DatastoreService

I use AeroGameFramework If some lines confuses you.

2 Likes

This actually inspired me to go research other tutorials of how people set up ProfileService. (To be specific @EncodedLua.)

His tutorial helped out a lot and I all seems to be working great. Can’t wait to tinker with it more!

1 Like

I’d love to see a data backup implementation with this. This is an awesome module!

I’ve been playing around with this module for a while and I have a follow up question.

I’m interested in the idea of creating a vender where you can add more to your currency, either coins or gems. The wiki for this module clearly states:

ProfileService is supposed to be a ModuleScript which you can place inside your Roblox game’s ServerScriptService or wherever else is preferred. ProfileService can only be used server-side

In a hypothetical scenario, what are the repercussions of me placing this module in ReplicatedStorage (rather than ServerScriptService) and attempting to require the module in this path to increase the client’s data? Should I resort to using remote events (and leave the module in the ServerScriptService)?

If so, how would I even approach this?

That sounds like you’re relying too much on the client, and you shouldn’t do that since exploiters would be able to modify their own data and cheat. All of the code for managing data should be on the server-side.

1 Like

How else can you wheel that information to the client without using a localscript? (Which is pretty much the only way since you can only use local scripts for textbuttons/guis and not serverscripts.)

You can send a RemoteEvent everytime a variable for the player changes, or use a ValueObject parented to the player, but if you use ValueObjects then exploiters can see other player’s data.

ReplicaService is the best way to do this though.

3 Likes

This module cannot be required on a client script as It relies on datastorservice and datastoreservice only works on a server script

1 Like

Is it possible to convert ProfileService data back to just normal datastores?

Like if I wanted to stop using ProfileService and wanted to convert back to how normally people save data.

Also I’m not switching, just wanted to know if there’s a way incase it was needed.

A rough way of doing this is storing the profile’s .Data when it is loaded, releasing it and clearing the profile, and then calling WipeProfileAsync on the datastore key. From there you can either overwrite the key in the same datastore or just transfer to a completely new one, and make a new key in it with the data you retrieved from the profile.

How frequent can I call GlobalUpdateProfileAsync before expecting any problems?

Hey, how can I save the add a value to another player in the script? :confused: