### Starting a Profile Session Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Explains how to initiate a profile session using StartSessionAsync. Includes an example of using the Cancel parameter to abort the request if a player leaves during the loading process. ```luau local Players = game:GetService("Players") local profile = PlayerStore:StartSessionAsync(tostring(player.UserId), { Cancel = function() return player:IsDescendantOf(Players) == false end, }) ``` -------------------------------- ### ProfileStore:StartSessionAsync() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Starts a session for a profile, loading data and establishing a session lock to prevent cross-server conflicts. ```APIDOC ## ProfileStore:StartSessionAsync() ### Description Loads data from the DataStore and establishes a session lock. Returns a Profile object on success. ### Parameters - **profileKey** (string) - Required - The unique identifier for the profile. - **params** (table) - Optional - Configuration table including 'Cancel' function or 'Steal' flag. ### Response - **Profile** (object) - The active profile object or nil if failed. ``` -------------------------------- ### Implement ProfileStore Session Management in Luau Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/tutorial/index.md This script demonstrates how to initialize a ProfileStore, handle player join events to start a session, reconcile data templates, and ensure proper session termination upon player removal. ```luau local ProfileStore = require(game.ServerScriptService.ProfileStore) local PROFILE_TEMPLATE = { Cash = 0, Items = {}, } local Players = game:GetService("Players") local PlayerStore = ProfileStore.New("PlayerStore", PROFILE_TEMPLATE) local Profiles: {[Player]: typeof(PlayerStore:StartSessionAsync())} = {} local function PlayerAdded(player) local profile = PlayerStore:StartSessionAsync(`{player.UserId}`, { Cancel = function() return player.Parent ~= Players end, }) if profile ~= nil then profile:AddUserId(player.UserId) profile:Reconcile() profile.OnSessionEnd:Connect(function() Profiles[player] = nil player:Kick(`Profile session end - Please rejoin`) end) if player.Parent == Players then Profiles[player] = profile print(`Profile loaded for {player.DisplayName}!`) profile.Data.Cash += 100 else profile:EndSession() end else player:Kick(`Profile load fail - Please rejoin`) end end for _, player in Players:GetPlayers() do task.spawn(PlayerAdded, player) end Players.PlayerAdded:Connect(PlayerAdded) Players.PlayerRemoving:Connect(function(player) local profile = Profiles[player] if profile ~= nil then profile:EndSession() end end) ``` -------------------------------- ### Start Player Profile Session Source: https://context7.com/madstudioroblox/profilestore/llms.txt Starts a session for a player's profile, loading their data and establishing a session lock. It includes support for cancelling the session start and handling player disconnects. Upon successful loading, it applies GDPR compliance and reconciles data with the template. ```luau local Players = game:GetService("Players") local ProfileStore = require(game.ServerScriptService.ProfileStore) local PROFILE_TEMPLATE = { Cash = 0, Items = {} } local PlayerStore = ProfileStore.New("PlayerData", PROFILE_TEMPLATE) local Profiles: {[Player]: typeof(PlayerStore:StartSessionAsync())} = {} local function PlayerAdded(player) -- Start a session with cancellation support local profile = PlayerStore:StartSessionAsync(tostring(player.UserId), { Cancel = function() return player.Parent ~= Players -- Cancel if player left end, }) if profile ~= nil then profile:AddUserId(player.UserId) -- GDPR compliance profile:Reconcile() -- Fill missing template values profile.OnSessionEnd:Connect(function() Profiles[player] = nil player:Kick(`Profile session end - Please rejoin`) end) if player.Parent == Players then Profiles[player] = profile print(`Profile loaded for {player.DisplayName}!`) -- Safe to modify data now profile.Data.Cash += 100 else profile:EndSession() end else player:Kick(`Profile load fail - Please rejoin`) end end Players.PlayerAdded:Connect(PlayerAdded) Players.PlayerRemoving:Connect(function(player) local profile = Profiles[player] if profile ~= nil then profile:EndSession() end end) ``` -------------------------------- ### StartSessionAsync API Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/datause/index.md Details the API calls involved in starting a session using ProfileStore's :StartSessionAsync method, including scenarios with existing sessions and conflict resolution. ```APIDOC ## :StartSessionAsync() ### Description Handles the process of starting a new session for a profile. This includes initial calls, handling existing sessions, and conflict resolution mechanisms. ### Method StartSessionAsync ### Endpoint /ProfileStore/api/#startsessionasync ### Parameters None ### Request Example None ### Response None ### Scenarios and API Calls: **Until a session is started:** - Usually uses 1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call. - If another server currently has a session started for the same profile, uses 1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call and 1 [`:PublishAsync()`](https://create.roblox.com/docs/reference/engine/classes/MessagingService#PublishAsync) call, then 5 seconds later (`FIRST_LOAD_REPEAT`) will perform 1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call and 1 [`:PublishAsync()`](https://create.roblox.com/docs/reference/engine/classes/MessagingService#PublishAsync) call. While the session conflict is not resolved, 1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call and 1 [`:PublishAsync()`](https://create.roblox.com/docs/reference/engine/classes/MessagingService#PublishAsync) call will be repeated in 10 second intervals (`LOAD_REPEAT_PERIOD`) for the next 40 seconds (`SESSION_STEAL`) until finally a session will be stolen (1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call). Expect all of these calls to happen when players rejoin your game immediately after experiencing a server crash. ``` -------------------------------- ### ProfileStore:GetAsync() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Loads a profile from the DataStore without starting a session. The returned profile will not auto-save. ```APIDOC ## ProfileStore:GetAsync() ### Description Loads a profile from the DataStore without starting a session. The returned profile will not auto-save. Useful for reading player data without editing it or for admin tools. ### Method ProfileStore:GetAsync(userId) ### Endpoint N/A (Method on a ProfileStore object) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) -- Read a player's data without starting a session local function GetPlayerStats(userId) local profile = PlayerStore:GetAsync(`{userId}`) if profile then print(`Player {userId} has {profile.Data.Cash} cash`) print(`Session status:`, profile.Session) -- nil or {PlaceId, JobId} return profile.Data end return nil end -- Admin command to check any player's data GetPlayerStats(12345678) ``` ### Response #### Success Response (200) - **profile** (Profile object) - The loaded profile object, or nil if not found. - **profile.Data** (table) - The player's data. - **profile.Session** (table or nil) - Session information if the profile is active. #### Response Example ```json { "Data": { "Cash": 100, "Items": [], "Achievements": [], "Settings": { "SoundEnabled": true, "MusicEnabled": true } }, "Session": null } ``` ``` -------------------------------- ### Audit Data Mutation Over Time Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Shows how to iterate through historical snapshots of player data starting from a specific UNIX timestamp. This helps in tracking changes to player currency or inventory over a defined period. ```lua local PlayerStore = ProfileStore.New("PlayerData", {}) local min_date = DateTime.fromUnixTimestamp(1628952101) local print_minutes = 60 * 12 local query = PlayerStore:VersionQuery("Player_2312310", Enum.SortDirection.Ascending, min_date) local finish_update_time = min_date.UnixTimestampMillis + (print_minutes * 60000) while true do local profile = query:NextAsync() if profile ~= nil then if profile.KeyInfo.UpdatedTime > finish_update_time then break end print(DateTime.fromUnixTimestampMillis(profile.KeyInfo.UpdatedTime):ToIsoDate()) print(profile.Data) else break end end ``` -------------------------------- ### ProfileStore:MessageAsync() Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Sends a message to a profile regardless of whether a server has started a session for it. This method is intended for critical data like gifting paid items to online or offline friends. ```APIDOC ## ProfileStore:MessageAsync() ### Description Sends a message to a profile regardless of whether a server has started a session for it. Each `ProfileStore:MessageAsync()` call will use one `:UpdateAsync()` call for sending the message and another `:UpdateAsync()` call on the server that currently has a session started for the profile. This method is only to be used for handling critical data like gifting paid items to in-game friends that may or may not be online at the moment. If you don't mind the possibility of your messages failing to deliver, use `MessagingService` instead. See [`Profile:MessageHandler()`](#messagehandler) to learn how to receive messages. ### Method `MessageAsync` ### Endpoint `ProfileStore:MessageAsync(profile_key, message)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **profile_key** (string) - Required - DataStore key - **message** (table) - Required - Data to be stored in the profile before it's received ### Request Example ```lua -- Example usage (Luau) ProfileStore:MessageAsync("player123", { item = "Sword", quantity = 1 }) ``` ### Response #### Success Response (200) - **is_success** (boolean) - Indicates if the message was successfully sent. #### Response Example ```lua -- Example response (Luau) local success = ProfileStore:MessageAsync("player123", { item = "Sword", quantity = 1 }) print(success) -- true or false ``` ``` -------------------------------- ### Session Management and Auto-Save Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/datause/index.md Describes the API calls made after a session is started and until it ends, including auto-save intervals and MessagingService subscriptions. ```APIDOC ## Session Management and Auto-Save ### Description Details the ongoing operations during an active profile session, including periodic auto-saves and real-time communication via MessagingService. ### Method UpdateAsync, Save, SubscribeAsync, PublishAsync ### Endpoint N/A (Internal operations) ### Parameters None ### Request Example None ### Response None ### Operations during an active session: **After a session is started and until the session ends:** - Uses 1 [`:UpdateAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#UpdateAsync) call in 300 second intervals (`AUTO_SAVE_PERIOD`). - When [`:Save()`](/ProfileStore/api/#save) is used, the 300 second timer will reset for this repeating call. This is the auto-save call. - Subscribes to 1 [`MessagingService`](https://create.roblox.com/docs/reference/engine/classes/MessagingService) topic (1 [`:SubscribeAsync()`](https://create.roblox.com/docs/reference/engine/classes/MessagingService#SubscribeAsync) call) - this topic subscription will be ended when the profile session ends. ``` -------------------------------- ### Profile.OnSessionEnd Signal - Handle Session Termination Source: https://context7.com/madstudioroblox/profilestore/llms.txt Fires when the profile session ends, triggered by calling EndSession(), another server starting a session, or server shutdown. Use this signal to clean up profile data or kick the player. It requires a player object and a profile object, and it connects a callback function to the OnSessionEnd event. ```luau local function SetupProfile(player, profile) profile.OnSessionEnd:Connect(function() Profiles[player] = nil -- Kick player to prevent playing with unsaved data player:Kick(`Your data has been loaded on another server - please rejoin`) end) end ``` -------------------------------- ### Perform a Data Rollback with VersionQuery Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Demonstrates how to query for a specific profile version before a certain date and perform a rollback using SetAsync. This is useful for recovering lost player data. ```lua local PlayerStore = ProfileStore.New("PlayerData", {}) local max_date = DateTime.fromUniversalTime(2021, 08, 13) local query = PlayerStore:VersionQuery("Player_2312310", Enum.SortDirection.Descending, nil, max_date) local profile = query:NextAsync() if profile ~= nil then profile:SetAsync() print("Rollback success!") print(profile.Data) else print("No version to rollback to") end ``` -------------------------------- ### ProfileStore.New() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Initializes a new ProfileStore instance associated with a specific DataStore key and an optional data template. ```APIDOC ## ProfileStore.New() ### Description Creates a new ProfileStore object for reading and writing profiles to a specific DataStore. ### Parameters - **dataStoreName** (string) - Required - The name of the DataStore to use. - **template** (table) - Optional - Default values for new profiles. ### Request Example local PlayerStore = ProfileStore.New("PlayerData", { Cash = 0, Items = {} }) ``` -------------------------------- ### Profile.OnSessionEnd Signal Source: https://context7.com/madstudioroblox/profilestore/llms.txt Fires when the profile session ends, either from calling EndSession(), another server starting a session, or server shutdown. Use to clean up or kick the player. ```APIDOC ## Profile.OnSessionEnd Signal ### Description Fires when the profile session ends, either from calling EndSession(), another server starting a session, or server shutdown. Use to clean up or kick the player. ### Method N/A (This is a signal of a Profile object) ### Endpoint N/A ### Parameters None ### Request Example ```luau local function SetupProfile(player, profile) profile.OnSessionEnd:Connect(function() Profiles[player] = nil -- Kick player to prevent playing with unsaved data player:Kick(`Your data has been loaded on another server - please rejoin`) end) end ``` ### Response N/A (This is an event handler, not an endpoint with a response) ``` -------------------------------- ### ProfileStore Initialization Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/datause/index.md Details the API calls made by ProfileStore on startup in a Roblox Studio environment to check for DataStore API access. ```APIDOC ## On startup in Studio ### Description Uses 1 [`:SetAsync()`](https://create.roblox.com/docs/reference/engine/classes/GlobalDataStore#SetAsync) call to check for DataStore API access. ### Method SetAsync ### Endpoint N/A (Internal operation) ### Parameters None ### Request Example None ### Response None ``` -------------------------------- ### Create ProfileStore Instance Source: https://context7.com/madstudioroblox/profilestore/llms.txt Initializes a new ProfileStore object for a specific DataStore. It can accept an optional template to define default values for new player profiles. This is the first step in using ProfileStore to manage player data. ```luau local ProfileStore = require(game.ServerScriptService.ProfileStore) -- Create a ProfileStore with a template for new players local PROFILE_TEMPLATE = { Cash = 0, Items = {}, Level = 1, Experience = 0, } local PlayerStore = ProfileStore.New("PlayerData", PROFILE_TEMPLATE) -- PlayerStore is now ready to start sessions for player profiles ``` -------------------------------- ### Using ProfileStore Mock for Testing Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Demonstrates how to use ProfileStore.Mock to perform operations that do not persist to the live DataStore, useful for testing environments. It also shows a pattern to automatically switch to Mock mode when running in Roblox Studio. ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) -- Live session local LiveProfile = PlayerStore:StartSessionAsync("profile_key") LiveProfile.Data.Value = 1 LiveProfile:EndSession() -- Mock session (does not save to DataStore) local MockProfile = PlayerStore.Mock:StartSessionAsync("profile_key") MockProfile.Data.Value = 1 MockProfile:EndSession() -- Automatic studio switching local RunService = game:GetService("RunService") local PlayerStore = ProfileStore.New("PlayerData", {}) if RunService:IsStudio() == true then PlayerStore = PlayerStore.Mock end ``` -------------------------------- ### Handling Profile Save Events Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Demonstrates how to connect to the OnSave signal to execute logic immediately before data is persisted to the DataStore. ```luau Profile.OnSave:Connect(function() print("Profile.Data is about to be saved to the DataStore") end) ``` -------------------------------- ### ProfileStore.Mock Source: https://context7.com/madstudioroblox/profilestore/llms.txt A mock version of ProfileStore that operates on a fake DataStore. Data is not persisted and disappears when the server shuts down. Useful for testing without affecting live data. ```APIDOC ## ProfileStore.Mock ### Description A mock version of ProfileStore that operates on a fake DataStore. Data is not persisted and disappears when the server shuts down. Useful for testing without affecting live data. ### Method N/A (This is a property of the ProfileStore object) ### Endpoint N/A ### Parameters None ### Request Example ```luau local RunService = game:GetService("RunService") local ProfileStore = require(game.ServerScriptService.ProfileStore) local PROFILE_TEMPLATE = { Cash = 0, Items = {} } local PlayerStore = ProfileStore.New("PlayerData", PROFILE_TEMPLATE) -- Use mock store in Studio to avoid modifying live data if RunService:IsStudio() == true then PlayerStore = PlayerStore.Mock end -- Now PlayerStore operates normally in production -- but uses fake data in Studio ``` ### Response N/A (This is a configuration setting, not an endpoint with a response) ``` -------------------------------- ### Profile:Save() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Immediately saves Profile.Data to the DataStore. Use sparingly for critical moments. ```APIDOC ## Profile:Save() ### Description Immediately saves Profile.Data to the DataStore. Use sparingly for critical moments like developer product purchases. Auto-save already handles regular saving. ### Method Profile:Save() ### Endpoint N/A (Method on a Profile object) ### Parameters None ### Request Example ```luau local MarketplaceService = game:GetService("MarketplaceService") local productFunctions = {} -- Product that awards 100 cash productFunctions[456456] = function(receipt, player, profile) profile.Data.Cash += 100 -- Immediately save to protect against server crashes profile:Save() end local function processReceipt(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if player then local profile = Profiles[player] if profile ~= nil and profile:IsActive() == true then local handler = productFunctions[receiptInfo.ProductId] local success, result = pcall(handler, receiptInfo, player, profile) if success then return Enum.ProductPurchaseDecision.PurchaseGranted end end end return Enum.ProductPurchaseDecision.NotProcessedYet end MarketplaceService.ProcessReceipt = processReceipt ``` ### Response None (Saves data to DataStore) ### Response Example None ``` -------------------------------- ### ProfileStore:VersionQuery() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Creates a version query to browse historical profile versions using DataStore versioning. Useful for rolling back player data after issues or investigating data changes. ```APIDOC ## ProfileStore:VersionQuery() ### Description Creates a version query to browse historical profile versions using DataStore versioning. Useful for rolling back player data after issues or investigating data changes. ### Method N/A (This is a method of a ProfileStore object, not a standalone endpoint) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) local function RollbackPlayerData(userId, beforeDate) local max_date = DateTime.fromUniversalTime( beforeDate.year, beforeDate.month, beforeDate.day ) local query = PlayerStore:VersionQuery( `{userId}`, Enum.SortDirection.Descending, -- Most recent first nil, -- No minimum date max_date ) local profile = query:NextAsync() if profile ~= nil then print(`Found version from: {DateTime.fromUnixTimestampMillis(profile.KeyInfo.UpdatedTime):ToIsoDate()}`) print(`Data:`, profile.Data) -- Uncomment to actually perform rollback: -- profile:SetAsync() -- print("Rollback complete!") return true else print("No version found before that date") return false end end -- Rollback player 12345678 to before August 14th, 2024 RollbackPlayerData(12345678, { year = 2024, month = 8, day = 14 }) ``` ### Response #### Success Response (200) N/A (This method returns a query object, not a direct response) #### Response Example N/A ``` -------------------------------- ### Monitor Critical State Changes Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Illustrates how to use the OnCriticalToggle signal to react when the ProfileStore enters or exits a critical error state. ```luau ProfileStore.OnCriticalToggle:Connect(function(is_critical) if is_critical == true then print(`ProfileStore entered critical state`) else print(`ProfileStore critical state is over`) end end) ``` -------------------------------- ### Handle ProfileStore Overwrite Events Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Shows how to listen for the OnOverwrite signal, which triggers when invalid data types are detected in a profile. ```luau ProfileStore.OnOverwrite:Connect(function(store_name, profile_key) print(`Overwrite has occurred for Store:{store_name}, Key:{profile_key}`) end) ``` -------------------------------- ### ProfileStore.Mock - Test with Fake Data Source: https://context7.com/madstudioroblox/profilestore/llms.txt A mock version of ProfileStore that operates on a fake DataStore, useful for testing without affecting live data. Data is not persisted and disappears when the server shuts down. This requires the RunService and ProfileStore modules. It's typically used within Studio environments. ```luau local RunService = game:GetService("RunService") local ProfileStore = require(game.ServerScriptService.ProfileStore) local PROFILE_TEMPLATE = { Cash = 0, Items = {} } local PlayerStore = ProfileStore.New("PlayerData", PROFILE_TEMPLATE) -- Use mock store in Studio to avoid modifying live data if RunService:IsStudio() == true then PlayerStore = PlayerStore.Mock end -- Now PlayerStore operates normally in production -- but uses fake data in Studio ``` -------------------------------- ### ProfileStore:VersionQuery() - Browse Historical Data Source: https://context7.com/madstudioroblox/profilestore/llms.txt Creates a version query to browse historical profile versions using DataStore versioning. This is useful for rolling back player data after issues or investigating data changes. It requires the ProfileStore module and DateTime utilities. The function takes a userId, sort direction, and optional date ranges as input, returning profile data or nil. ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) -- Rollback a player's data to before a specific date local function RollbackPlayerData(userId, beforeDate) local max_date = DateTime.fromUniversalTime( beforeDate.year, beforeDate.month, beforeDate.day ) local query = PlayerStore:VersionQuery( `{userId}`, Enum.SortDirection.Descending, -- Most recent first nil, -- No minimum date max_date ) local profile = query:NextAsync() if profile ~= nil then print(`Found version from: {DateTime.fromUnixTimestampMillis(profile.KeyInfo.UpdatedTime):ToIsoDate()}`) print(`Data:`, profile.Data) -- Uncomment to actually perform rollback: -- profile:SetAsync() -- print("Rollback complete!") return true else print("No version found before that date") return false end end -- Rollback player 12345678 to before August 14th, 2024 RollbackPlayerData(12345678, { year = 2024, month = 8, day = 14 }) ``` -------------------------------- ### Implement MarketplaceService ProcessReceipt Callback Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/devproducts/index.md This snippet overrides the default MarketplaceService.ProcessReceipt function to handle product purchases. It validates the purchase ID and executes a registered product function, returning a decision to the Roblox engine. ```Luau local function ProcessReceipt(receipt_info) local player = game.Players:GetPlayerByUserId(receipt_info.PlayerId) if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end if not ProductFunctions[receipt_info.ProductId] then warn("No product function defined for ProductId " .. receipt_info.ProductId .. "; Player: " .. player.Name) return Enum.ProductPurchaseDecision.NotProcessedYet end return PurchaseIdCheckAsync( profile, receipt_info.PurchaseId, function() ProductFunctions[receipt_info.ProductId](receipt_info, player, profile) end ) end MarketplaceService.ProcessReceipt = ProcessReceipt ``` -------------------------------- ### Handle ProfileStore Error Signals Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Demonstrates how to connect to the OnError signal to log DataStore errors, including the error message, store name, and profile key. ```luau ProfileStore.OnError:Connect(function(error_message, store_name, profile_key) print(`DataStore error (Store:{store_name};Key:{profile_key}): {error_message}`) end) ``` -------------------------------- ### Save Profile Data Immediately Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Forces an immediate save of the Profile.Data to the DataStore, but only if the profile session is active. This is recommended for critical data, like Developer Product purchases, before potential server crashes. Each call counts as an UpdateAsync call. ```Luau Profile:Save() ``` -------------------------------- ### Set Profile Data Asynchronously Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Saves the Profile.Data for profiles loaded via ProfileStore:GetAsync() or ProfileStore:VersionQuery(), even if no active session exists. This action will end any existing server sessions for the profile. ```Luau Profile:SetAsync() ``` -------------------------------- ### Profile:Save() - Immediately Save Profile Data Source: https://context7.com/madstudioroblox/profilestore/llms.txt Immediately saves the current Profile.Data to the DataStore, overriding the regular auto-save mechanism. This function should be used sparingly for critical events, such as completing a developer product purchase, to ensure data integrity against server crashes. It takes no arguments and performs a synchronous save operation. ```luau local MarketplaceService = game:GetService("MarketplaceService") local productFunctions = {} -- Product that awards 100 cash productFunctions[456456] = function(receipt, player, profile) profile.Data.Cash += 100 -- Immediately save to protect against server crashes profile:Save() end local function processReceipt(receiptInfo) local player = Players:GetPlayerByUserId(receiptInfo.PlayerId) if player then local profile = Profiles[player] if profile ~= nil and profile:IsActive() == true then local handler = productFunctions[receiptInfo.ProductId] local success, result = pcall(handler, receiptInfo, player, profile) if success then return Enum.ProductPurchaseDecision.PurchaseGranted end end end return Enum.ProductPurchaseDecision.NotProcessedYet end MarketplaceService.ProcessReceipt = processReceipt ``` -------------------------------- ### Profile:Reconcile() Method Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Fills missing variables in Profile.Data using a template table provided during ProfileStore.New(). This is helpful for updating your data structure over time. ```Luau Profile:Reconcile() ``` -------------------------------- ### Handle Developer Product Purchases with ProfileStore (Luau) Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/devproducts/index.md This Luau code snippet shows how to process developer product purchases using Roblox's MarketplaceService and integrate it with ProfileStore for saving player data. It defines functions to handle different product IDs, awarding in-game items or benefits upon successful purchase. Ensure ProfileStore is initialized and accessible for this code to function correctly. ```Luau local Profiles: {[player]: typeof(PlayerStore:StartSessionAsync())} = {} local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local productFunctions = {} -- Example: product ID 456456 awards 100 cash to the user productFunctions[456456] = function(receipt, player, profile) profile.Data.Cash += 100 -- We made changes to the player profile - perform an instant -- save to secure a player purchase against server crashes: profile:Save() end -- Example: product ID 123123 brings the user back to full health productFunctions[123123] = function(receipt, player, profile) local character = player.Character local humanoid = character and character:FindFirstChildWhichIsA("Humanoid") if humanoid then humanoid.Health = humanoid.MaxHealth -- Indicates a successful purchase return true end end local function processReceipt(receiptInfo) local userId = receiptInfo.PlayerId local productId = receiptInfo.ProductId local player = Players:GetPlayerByUserId(userId) if player then local profile = Profiles[player] while profile == nil and player.Parent == Players do profile = Profiles[player] if profile ~= nil then break end task.wait() end if profile ~= nil and profile:IsActive() == true then -- Gets the handler function associated with the developer product ID and attempts to run it local handler = productFunctions[productId] local success, result = pcall(handler, receiptInfo, player, profile) if success then -- The user has received their items -- Returns "PurchaseGranted" to confirm the transaction return Enum.ProductPurchaseDecision.PurchaseGranted else warn(`Failed to process receipt:`, receiptInfo, result) end end end -- The user's items couldn't be awarded -- Returns "NotProcessedYet" and tries again next time the user joins the experience return Enum.ProductPurchaseDecision.NotProcessedYet end -- Sets the callback -- This can only be done once by one server-side script MarketplaceService.ProcessReceipt = processReceipt ``` -------------------------------- ### ProfileStore:GetAsync() - Luau Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Attempts to load the latest or a specified version of a profile from the DataStore without initiating a server session. The returned Profile will not auto-save and does not require EndSession(). Data can be edited and saved using Profile:SetAsync(). Returns nil if no data exists for the profile_key. This is the preferred method for reading player data without modification. ```luau ProfileStore:GetAsync(profile_key, version?) --> [Profile] or nil -- profile_key [string] -- version nil or [string] -- DataStore key version ``` -------------------------------- ### ProfileStore:GetAsync() - Load Profile Without Session Source: https://context7.com/madstudioroblox/profilestore/llms.txt Loads a player's profile data from the DataStore without initiating an active session. The returned profile will not auto-save, making it suitable for read-only operations like checking player statistics or for use in administrative tools. It requires a userId as input and returns the profile object or nil if not found. ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) -- Read a player's data without starting a session local function GetPlayerStats(userId) local profile = PlayerStore:GetAsync(`{userId}`) if profile then print(`Player {userId} has {profile.Data.Cash} cash`) print(`Session status:`, profile.Session) -- nil or {PlaceId, JobId} return profile.Data end return nil end -- Admin command to check any player's data GetPlayerStats(12345678) ``` -------------------------------- ### ProfileStore Critical State Monitoring Source: https://context7.com/madstudioroblox/profilestore/llms.txt Monitors the OnCriticalToggle signal to detect when ProfileStore enters or exits a critical state due to excessive DataStore failures. It prints messages indicating the state change and can be used to notify players of potential save issues. ```Luau local ProfileStore = require(game.ServerScriptService.ProfileStore) ProfileStore.OnCriticalToggle:Connect(function(is_critical) if is_critical == true then print("ProfileStore entered critical state - DataStore issues detected") -- Notify players of possible save issues else print("ProfileStore critical state ended - DataStore operating normally") end end) ``` -------------------------------- ### Profile:SetAsync() Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Saves `Profile.Data` to the DataStore, disregarding any active sessions. Only works for profiles loaded via `ProfileStore:GetAsync()` or `ProfileStore:VersionQuery()`. ```APIDOC ## Profile:SetAsync() ### Description Saves `Profile.Data` of a profile loaded with [ProfileStore:GetAsync()](#getasync) to the DataStore disregarding any active sessions. If there was a server that had an active session for that profile - that session will be ended. !!! warning "Only works for profiles loaded through [ProfileStore:GetAsync()](#getasync) or [ProfileStore:VersionQuery()](#versionquery)" ### Method `Profile:SetAsync()` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```luau Profile:SetAsync() ``` ### Response #### Success Response (200) None #### Response Example None ``` -------------------------------- ### Profile Properties: ProfileStore and Key Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Profile.ProfileStore returns the ProfileStore object associated with the profile. Profile.Key returns the DataStore key used to identify this profile. ```Luau Profile.ProfileStore [ProfileStore] -- ProfileStore object this profile belongs to ``` ```Luau Profile.Key [string] -- DataStore key ``` -------------------------------- ### Profile.OnAfterSave Signal - Verify Data Persistence Source: https://context7.com/madstudioroblox/profilestore/llms.txt Fires after data has been successfully saved to the DataStore. Use this signal to verify which changes have been persisted by checking Profile.LastSavedData. It receives the last saved data as an argument, allowing for confirmation or logging of saved changes. ```luau local function SetupProfile(player, profile) profile.OnAfterSave:Connect(function(last_saved_data) print(`Data saved for {player.Name}:`) print(` Cash: {last_saved_data.Cash}`) print(` Items: {#last_saved_data.Items}`) end) end ``` -------------------------------- ### ProfileStore:MessageAsync() - Send Message to Profile Source: https://context7.com/madstudioroblox/profilestore/llms.txt Sends a message to a player's profile, which will be stored and delivered when the profile is loaded, even if the player is currently offline. This is ideal for critical data transfers like sending in-game gifts to offline players. It takes a target userId and a message payload as input, returning a success boolean. ```luau local PlayerStore = ProfileStore.New("PlayerData", {}) -- Gift items to a player who may be offline local function GiftToPlayer(targetUserId, giftData) local success = PlayerStore:MessageAsync(`{targetUserId}`, { Type = "Gift", FromPlayer = "System", Item = giftData.item, Amount = giftData.amount, Timestamp = os.time(), }) return success end -- Receive messages in the player's session local function SetupMessageHandler(profile) profile:MessageHandler(function(message, processed) if message.Type == "Gift" then table.insert(profile.Data.Items, { Name = message.Item, Amount = message.Amount, }) print(`Received gift: {message.Item} x{message.Amount}`) processed() -- Mark message as handled end end) end -- Usage GiftToPlayer(12345678, { item = "Sword", amount = 1 }) ``` -------------------------------- ### Converting UserData to Serializable Tables in Luau Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/troubleshooting/index.md Illustrates how to convert Roblox UserData types (like Vector3 or CFrame) into serializable tables for storage with ProfileStore. UserData cannot be directly saved to DataStores. ```luau local position = Vector3.new(0, 0, 0) Profile.Data.LastPosition = {position.X, position.Y, position.Z} ``` -------------------------------- ### Profile:Reconcile() - Fill Missing Profile Data Source: https://context7.com/madstudioroblox/profilestore/llms.txt Reconciles a player's profile data by filling in any missing fields from the template defined in ProfileStore.New(). This is crucial when updating the data template after players have existing profiles, ensuring new fields are added gracefully. It takes no arguments and modifies the profile data in place. ```luau local PROFILE_TEMPLATE = { Cash = 0, Items = {}, -- New fields added later Achievements = {}, Settings = { SoundEnabled = true, MusicEnabled = true }, } local PlayerStore = ProfileStore.New("PlayerData", PROFILE_TEMPLATE) local function PlayerAdded(player) local profile = PlayerStore:StartSessionAsync(`{player.UserId}`) if profile then -- Reconcile adds missing fields from template to existing profiles profile:Reconcile() -- Now profile.Data.Achievements and profile.Data.Settings exist -- even for old players who didn't have these fields end end ``` -------------------------------- ### Implement PurchaseId Caching for Reliable Product Processing Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/devproducts/index.md This Luau implementation demonstrates how to cache PurchaseIds in ProfileStore data and yield the receipt processing until the save is confirmed. It ensures that rewards are only granted once and that the purchase is successfully persisted before notifying Roblox. ```luau local PURCHASE_ID_CACHE_SIZE = 100 function PurchaseIdCheckAsync(profile, purchase_id, grant_product): Enum.ProductPurchaseDecision if profile:IsActive() == true then local purchase_id_cache = profile.Data.PurchaseIdCache or {} profile.Data.PurchaseIdCache = purchase_id_cache if table.find(purchase_id_cache, purchase_id) == nil then local success, result = pcall(grant_product) if success ~= true then warn(`Failed to process receipt:`, profile.Key, purchase_id, result) return Enum.ProductPurchaseDecision.NotProcessedYet end if #purchase_id_cache >= PURCHASE_ID_CACHE_SIZE then table.remove(purchase_id_cache, 1) end table.insert(purchase_id_cache, purchase_id) end local function is_purchase_saved() local saved_cache = profile.LastSavedData.PurchaseIdCache return if saved_cache ~= nil then table.find(saved_cache, purchase_id) ~= nil else false end if is_purchase_saved() == true then return Enum.ProductPurchaseDecision.PurchaseGranted end while profile:IsActive() == true do local last_saved_data = profile.LastSavedData profile:Save() if profile.LastSavedData == last_saved_data then profile.OnAfterSave:Wait() end if is_purchase_saved() == true then return Enum.ProductPurchaseDecision.PurchaseGranted end if profile:IsActive() == true then task.wait(10) end end end return Enum.ProductPurchaseDecision.NotProcessedYet end ``` -------------------------------- ### Handling Profile Session End in Luau Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/troubleshooting/index.md Shows how to use the Profile.OnSessionEnd event to detect when a profile session has ended. This is crucial for ensuring data is saved correctly and for preventing issues when players hop between servers. ```luau Profile.OnSessionEnd:Connect(function() print(`Profile session has ended ({Profile.Key}) - Profile.Data will no longer be saved to the DataStore`) end) ``` -------------------------------- ### ProfileStore.SetConstant() - Customize ProfileStore Behavior Source: https://context7.com/madstudioroblox/profilestore/llms.txt Modifies internal ProfileStore constants for advanced customization. This function allows tuning parameters like auto-save periods, loading repeat intervals, and session timeouts. It takes the constant name and its new value as arguments. ```luau local ProfileStore = require(game.ServerScriptService.ProfileStore) -- Change auto-save period from 300 seconds to 60 seconds ProfileStore.SetConstant("AUTO_SAVE_PERIOD", 60) -- Reduce session steal timeout from 40 to 20 seconds ProfileStore.SetConstant("SESSION_STEAL", 20) ``` -------------------------------- ### Profile Properties Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Details about the properties of a Profile object, including its data, save status, session information, and metadata. ```APIDOC ## Profile Properties ### .Data #### Description Represents the savable data for a player's profile, such as progress or settings. Changes to this table are saved automatically under certain conditions or can be manually saved. #### Type `table` #### Access Read/Write #### Notes Changes are guaranteed to save if made after `Profile:IsActive() == true` or before `Profile.OnSessionEnd`. Critical data should be saved immediately after checking `Profile:IsActive()` without yielding. ### .LastSavedData #### Description A read-only snapshot of the profile data that was last successfully saved to the DataStore. #### Type `table` #### Access Read-only ### .FirstSessionTime #### Description The Unix timestamp indicating when the profile was initially created. #### Type `number` #### Access Read-only ### .SessionLoadCount #### Description Tracks the number of times a session has been initiated for this profile. #### Type `number` #### Access Read-only ### .Session #### Description Information about the current or last active server session for the profile, including `PlaceId` and `JobId`. This property is set after a session starts and does not change afterward. #### Type `table?` (nil or `{PlaceId = number, JobId = string}`) #### Access Read-only ### .RobloxMetaData #### Description A table that stores metadata associated with the profile's DataStore key. Be cautious of strict size limits (around 300 characters total content). #### Type `table` #### Access Read/Write #### Notes Changes are saved similarly to `DataStoreSetOptions:SetMetaData()` and are applied during the next auto-save or session end. ### .UserIds #### Description A table containing user IDs associated with this profile. User IDs are managed using `Profile:AddUserId()` and `Profile:RemoveUserId()`. #### Type `table` (e.g., `{user_id [number], ...}`) #### Access Read-only (contents managed by methods) ### .KeyInfo #### Description Provides access to the `DataStoreKeyInfo` instance related to this profile. #### Type `DataStoreKeyInfo` #### Access Read-only ### .OnSave #### Description A signal that fires immediately before any changes to `Profile.Data` are persisted to the DataStore. This includes auto-saves, manual saves via `Profile:Save()`, and saves upon session end. #### Type `RBXScriptSignal` #### Usage Example ```lua Profile.OnSave:Connect(function() print("Profile data is about to be saved.") end) ``` #### Notes While changes made during `OnSave` are expected to be saved, this guarantee is lost after yielding. Use `Profile:IsActive() == true` in such cases. The signal fires before auto-saves, `Profile:Save()`, and session end saves. ``` -------------------------------- ### Accessing Profile Data and Metadata Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Provides an overview of the properties available on a Profile object, including the primary data table, session information, and Roblox metadata. ```luau Profile.Data = { --[[ Player progress data ]] } Profile.LastSavedData -- Read-only version of last saved data Profile.FirstSessionTime -- Unix timestamp of creation Profile.SessionLoadCount -- Number of sessions started Profile.Session -- {PlaceId = number, JobId = string} or nil Profile.RobloxMetaData = { --[[ Metadata table, max 300 chars ]] } Profile.UserIds -- List of associated user IDs Profile.KeyInfo -- DataStoreKeyInfo instance ``` -------------------------------- ### Profile:IsActive() Method Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Checks if changes to Profile.Data will be saved. Returns true if the profile is active and saving is guaranteed. Useful for making immediate changes to multiple profiles, like in trading systems. ```Luau Profile:IsActive() --> [bool] ``` -------------------------------- ### Profile:EndSession() Method Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Stops auto-saving for the profile and performs a final save of Profile.Data. This method should be called after finishing work with a profile obtained via ProfileStore:StartSessionAsync(). ```Luau Profile:EndSession() ``` ```Luau Players.PlayerRemoving:Connect(function(player) local profile = Profiles[player] if profile ~= nil then profile:EndSession() Profiles[player] = nil end end) ``` -------------------------------- ### ProfileStore.SetConstant() Source: https://context7.com/madstudioroblox/profilestore/llms.txt Modifies internal ProfileStore constants for advanced customization. Available constants: AUTO_SAVE_PERIOD, LOAD_REPEAT_PERIOD, FIRST_LOAD_REPEAT, SESSION_STEAL, ASSUME_DEAD, START_SESSION_TIMEOUT, CRITICAL_STATE_ERROR_COUNT, CRITICAL_STATE_ERROR_EXPIRE, CRITICAL_STATE_EXPIRE, MAX_MESSAGE_QUEUE. ```APIDOC ## ProfileStore.SetConstant() ### Description Modifies internal ProfileStore constants for advanced customization. Available constants: AUTO_SAVE_PERIOD, LOAD_REPEAT_PERIOD, FIRST_LOAD_REPEAT, SESSION_STEAL, ASSUME_DEAD, START_SESSION_TIMEOUT, CRITICAL_STATE_ERROR_COUNT, CRITICAL_STATE_ERROR_EXPIRE, CRITICAL_STATE_EXPIRE, MAX_MESSAGE_QUEUE. ### Method N/A (This is a static method of the ProfileStore object) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```luau local ProfileStore = require(game.ServerScriptService.ProfileStore) -- Change auto-save period from 300 seconds to 60 seconds ProfileStore.SetConstant("AUTO_SAVE_PERIOD", 60) -- Reduce session steal timeout from 40 to 20 seconds ProfileStore.SetConstant("SESSION_STEAL", 20) ``` ### Response N/A (This method modifies internal settings and does not return a value) ``` -------------------------------- ### ProfileStore:MessageAsync() - Luau Source: https://github.com/madstudioroblox/profilestore/blob/main/docs/api.md Sends a message to a profile, ensuring delivery even if no server session is active. Each call consumes two UpdateAsync calls. This method is intended for critical data like gifting paid items to potentially offline friends. Use MessagingService if message delivery failures are acceptable. Messages are received via Profile:MessageHandler(). ```luau ProfileStore:MessageAsync(profile_key, message) --> is_success [bool] -- profile_key [string] -- DataStore key -- message [table] -- Data to be stored in the profile before it's received ```