Save your player data with ProfileService! (DataStore Module)

Can you show the get/set methods of your module?
Make sure you only load the profile once, and keep it referenced in a table for later use (+release!).

Does this support saving structures placed in the world? I’ve heard mixed answers about using this for saving player structures and I don’t have a bold idea if it’s safe to do or not.

I’m not quite sure I understand what you mean by structures, do you mean builds that a player did? i.e A house built with f3x?

1 Like

Yes. Like a custom building system with blueprints and the blueprints saving with their position and extra properties like colors or wood types, etc.

The module creates a datastore queue warning, because it makes a GetAsync request to check if it can save in studio. That makes sense, but I had to remove this part in the module. Is there not any other way to check if you can save in studio?

(Wrong reply)

I mean, like any DataStore module you can technically do that by making your own methods.

A function like that isn’t baked in ProfileService for obvious reasons, it’s up to the developer to add what they want, the only thing ProfileService provides you with is a nice way to save data for players, but to save anything other than normal types such as a string, arrays, numbers etc you will have to make your own method.

You can make a method like […]:SaveModelBlueprint(model: Model)
which transforms it into an array of arrays. It’s complicated but look at this post: How do i create a saving system for parts using DataStores? - #17 by OnlyJaycbee

1 Like

Thanks for the information, but i do have one more question. Since the datastore limits got increased, can i just save them in an array instead of using compression or json conversion? Id much rather make a structures table in the default profile and table.insert when a player places their structure rather than compression, decompression, etc. Would this work well with it?

Hey, I’m glad my previous message helped you.

As for the Datastore limit, it’s 4MB per key (aka per player). It’s hard to tell whether or not it’ll be enough, I’m not sure how big the builds will be and how much data you will be saving. I haven’t really thought of Data limits ever.

Say you are saving something like this (NOTE: I can’t find the actual byte size):

export type data = {
    Color: string,
    Material: string, 
    Size: string,
    CFrame: string,
}

If you do this to every part, it’ll stack up pretty quickly I assume.
I strongly recommend making your own module, or like I said before, a Method such as […]:SaveModelBlueprint(model: Model) to save the data. If you make everything in a ModuleScript it’ll be a flawless transition

Here are some tips for compression which I advise you to look at: This how do i extremely reduced data packet size

Ideas:

local Blueprinter = {}

export type Blueprint = {
    Data: { [string]: { [string]: { any } | string } },
    Date: number,
    Creator: string,
}

local function Compress(blueprint: Blueprint): Blueprint
    
end

local function Decompress(blueprint: Blueprint): Blueprint
    
end

function Blueprinter:ConstructBlueprint(parent: any): Blueprint -- Makes a blueprint of your model
    --TODO: Make the layout of the blueprint, example:
    --[[
    {
        ["Model1"] = {
            ["Part1"] = {
                Color = "1,1,1",
                Material = "Plastic",
                Size = "1,1,1",
                CFrame = "...",
                type = "Part",
            }
            type = "Model",
        }
    }
    ]]--
    local layout = {}

    return Compress(layout)
end

function Blueprinter:ConstructModel(data: { any })
    
end

function Blueprinter:ReadBlueprint(blueprint: Blueprint) -- Converts that blueprint in an actual model
    local layout = Decompress(blueprint)

    return self:ConstructModel(layout)
end

return Blueprinter
1 Like

Hello, Is it possible to convert the datastore2 module I’m using to profileservice without losing data?

Yes. In the template, you can have a variable called PreviousDataLoaded which should be set to false. When the profile is loaded, if the PreviousDataLoaded variable is false, you check to see if there is old data. If there is old data, then add it to the profile. Then you set PreviousDataLoaded to true (even if there was no previous data). This has worked for me in the past without any issues.

1 Like
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")


local ReplicaService = require(ServerScriptService.ReplicaService)
local ProfileService = require(ServerScriptService.ProfileService)


local ProfileTemplate = {
	CurrentCar = "",
	Vehicles = {},
}

for _, VH in pairs(game:GetService("ReplicatedStorage").Vehicles:GetChildren()) do
	local CarData = {
		VehicleName = VH.Name,
		aOwnedV = false,
		Primary = tostring(VH.Body:FindFirstChild("PrimaryColor").Color) or "1, 1, 1",
		RimColor = "1, 1, 1",
	}
	table.insert(ProfileTemplate.Vehicles, CarData)
end


local ReplicaTestServer = {

}

local plrCars = {}

local UpdateEvent = ReplicatedStorage.Events.UpdateCustomization

local PlayerProfileClassToken = ReplicaService.NewClassToken("PlayerProfile")

local GameProfileStore = ProfileService.GetProfileStore(
	"PlayerData",
	ProfileTemplate
)

local PlayerProfile -- PlayerProfile object
local PlayerProfiles = {} -- [player] = {Profile = profile, Replica = replica}

local LastPayout = os.clock()

local function LoadData(player, profile)
	local CurrentCar = Instance.new("StringValue", player)
	CurrentCar.Name = "CurrentCar"
	CurrentCar.Value = profile.Data.CurrentCar

	local folder = Instance.new("Folder", player)
	folder.Name = "Vehicles"

	for i, v in pairs(game:GetService("ReplicatedStorage").Vehicles:GetChildren()) do
		local Cars = Instance.new("BoolValue", folder)
		Cars.Name = v.Name

		for o, a in pairs(profile.Data.Vehicles) do
			if a.VehicleName == v.Name then
				for q, e in pairs(a) do
					Cars:SetAttribute(q, e)
				end
			end
		end
	end
end
local function PlayerAdded(player)
	local profile = GameProfileStore:LoadProfileAsync(
		"#Game" .. player.UserId,
		"ForceLoad"
	)
	if profile ~= nil then
		profile:AddUserId(player.UserId)
		profile:Reconcile()
		profile:ListenToRelease(function()
			PlayerProfiles[player].Replica:Destroy()
			PlayerProfiles[player] = nil
			player:Kick()
		end)
		if player:IsDescendantOf(Players) == true then
			local player_profile = {
				Profile = profile,
				Replica = ReplicaService.NewReplica({
					ClassToken = PlayerProfileClassToken,
					Tags = {Player = player},
					Data = profile.Data,
					Replication = "All",
				}),
				_player = player,
			}
			setmetatable(player_profile, PlayerProfile)
			PlayerProfiles[player] = player_profile
			LoadData(player, profile)
		else
			profile:Release()
		end
	else
		player:Kick() 
	end
end
----- Public functions -----

-- PlayerProfile object:
PlayerProfile = {
	--[[
		_player = player,
	--]]
}
PlayerProfile.__index = PlayerProfile



function PlayerProfile:IsActive() --> is_active
	return PlayerProfiles[self._player] ~= nil
end

----- Initialize -----

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



Players.PlayerAdded:Connect(PlayerAdded)

Players.PlayerRemoving:Connect(function(player)
	local player_profile = PlayerProfiles[player]
	if player_profile ~= nil then
		player_profile.Profile:Release()
	end
end)

i want it when i add new car the old cars data stays it does that but it dont add the data for the new cars, im using the boolvalue and the data is the attributes

Hey, is it possible to make a guild system using profileservice? Since it does session locking and stuff it looks hard to share data across multiple servers, except for using the Global UpdatesAPI. I was thinking of making a new profile for every guild created and store it the usual way, but if I want to edit the info such as the players in it, or make a ranking system within the guild, I would need to access and modify data on multiple servers and this could happen at the same time. Is it safe to do it with Profileservice and will using the Global UpdatesAPI work?

Please let me know if someone has done something similar or can give me the basic ways to implement it. Thanks

How would you use ProfileService for things other than players?

sorry for bumping, but yes how would i go on about saving things other than players, like cars? honestly this is a good dataservice. and have been using for alot of my games but. one thing i have not figured out yet is things other than leaderstats.

I don’t think it’s recommended to use it for non-players, due to datastore limits. Make sure to keep track of datastore ‘queue’ warnings in the output.

You can create a new ProfileStore. Then use it to store profiles for your cars. Every profile needs to have a unique key (UUID probably). Then use the profile as you would normally.

This is assuming the cars have the same behavior as players: they are created/destroyed during server runtime.

Perhaps using regular datastores would be best depending on your usecase.

@TheFastest10001

1 Like

i was thinking of using StringValues instead, so pretty much save string values with profile service, have their values be as the names of the cars, store that in a folder in replicatedstorage called “Cars” , have profileservice make a new folder called “Car Lists” inside the player, and when a player buys a car, it would find the stringvalue inside the Cars Folder and just put it inside Cars List. and then just have profile service save that. would that work? i havent tested it yet at all.

If you want to save cars of a specific player then yes.

But if cars aren’t linked to a player, you shouldn’t have it be in a player’s data.

You can always go without StringValue instances, giving you more control over replication.

got it to work, but hey appreciate for helping though.

Hi to be honest I won’t use ProfileService to make a guild system I would make my own system with UpdateAsync()

1 Like

is it needed to kick the player when the profile is released? this functionality makes ListenToHopReady unusuable unless I’m missing something, if I am please guide me in the right direction