Save your player data with ProfileService! (DataStore Module)

This is because you are trying to store type which is a lua function, wrap type in [“”] such as [“type”]

2 Likes

Thank you for the response. I have changed type to iType, but it still does not work. In the mean time, I think I just gonna use shallow copy. Please @me if you guys found the solution.

oh well, I have figured it out. It was all because I use the class for Inventory. I have items which are all items in the inventory and other functions. the problem is I directly set the Inventory class to Inventory I set up with ProfileService. This causes an error since the Inventory class has functions which is not allowed to be saved. I fixed it by setting items in the Inventory class to Inventory(which I renamed to InventoryItems) that I set up with ProfileService. I hope this could help someone else as It was a good experience for me.

NOTES: always change the datastore name when you change something in the template. I ve had trouble changes not replicated because of this
local ProfileStore = ProfileService.GetProfileStore("Test3", Template)

how can I turn off autosave, I often will have option on my game for events to disable data saving, I just need the :Release() function.

EDIT: I nuked the autosave function on heart beat, and deleted some autosave parts. idk if its the correct way, but not my fault.

don’t mind me asking, but how do i code a leaderboard system using profileservice? it seems confusing for me using datastoreservice:getasync() with it.

I would recommend you watch this video (originally linked in the OP), which includes how to do what you asked: Use Profile Service as Your Data Store! Roblox Development - YouTube

1 Like

I use a ModuleScript already to access ProfileService (seen below), and I still run into that error too.

1 Like

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?