local RunService = game:GetService("RunService")
local GameProfileStore = ProfileService.GetProfileStore("PlayerData", ProfileTemplate)
if RunService:IsStudio() == true then
GameProfileStore = GameProfileStore.Mock
end
right?
or is there another step I have to take to make it so the profile is mock so when testing in studio the data doesn’t save
Is there a method that will wait till the profile.Data is loaded? For example, sometimes client loads fast and the profile.Data hasn’t been created yet so Client stuff do not work sometimes.
I used ReplicaService and waited for the replica of the class for the data to be created.
My code looks like this on the client:
-- wait for replicas to be created, then create player data
ReplicaController.ReplicaOfClassCreated(DATA_CLASS_TOKEN, function(replica)
-- get player object
local replicaPlayer = replica.Tags.Player
-- create and store data object
local dataObject = Data.new(replicaPlayer, replica)
data[replicaPlayer] = dataObject
-- handle memory cleanup upon deletion
replica:AddCleanupTask(function()
dataObject:Destroy()
data[replicaPlayer] = nil
end)
-- handle and clear async binds
local binds = asyncBinds[replicaPlayer]
if binds then
-- call functions
for _, func in ipairs(binds) do
func()
end
-- clear binds
asyncBinds[replicaPlayer] = nil
end
end)
Note that Data is a custom Class that handles a specific player’s data, and loads the data by supplying a Player and a Replica into the constructor.
The important thing here, is where I say “handle and clear async binds”. The reason this is important is because I have a function on the client which is:
--[[
Method: waitForDataAsync
Waits for data to exist, then calls the function, will throw if the supplied player does not exist
@param {function} func: function to call when data exists, typically something containing .getValue() or .bindToChange()
@param {Player? or String?} otherPlayer: name or object of another player to get the data from, nil will default to the local player
]]
function DataCache.waitForDataAsync(func, otherPlayer) --> throws if supplied player does not exist
-- get player object
otherPlayer = otherPlayer or Player
if type(otherPlayer) == "string" then
otherPlayer = Players[otherPlayer]
end
-- player does not exist: throw an error
assert(otherPlayer ~= nil, "Supplied player does not exist")
-- asynchronously wait for the player data to exist
-- if it exists,
xpcall(function(...)
-- attempt to error
getDataObject(...)
-- no error means we can continue
func()
end, function(err)
-- error means data does not exist, and we need to wait for it to exist, then call the function
-- this is done by storing the function into a table, of which all the functions in it will be called when the data is created
local binds = asyncBinds[otherPlayer]
if not binds then
binds = {}
asyncBinds[otherPlayer] = binds
end
binds[#binds+1] = func
end, otherPlayer)
end
Basically, it will call the supplied function once the supplied player’s data loads. It will call the supplied function if the data already exists. I think you can make something like this given the code samples I have shown, but if you need any help let me know.
" Updating the template will not include missing template values in existing player profiles! ".
How would I be able to make updates to already existing player profiles when I want to add for example a new currency?
Hey! So I’ve been making a inventory system with ProfileService, and essentially I have the empty table called: “Inventory” under data, all of the data saving works perfectly. But I can’t figure out how to add values to the table. For example:
local testTable = {
-- Already called variables.
Blue = 1
Red = 36
}
-- Both of these already exist.
print(testTable.Blue)
print(testTable.Red)
-- This doesn't exist, but when its called it'll create a value.
testTable.Yellow = 12
print(testTable.Yellow)
But! Whenever I try this with profileservice, like this:
local DataManager = require(game.ReplicatedStorage.DataManager)
local Items = game.ReplicatedStorage.Items
local function onItemAdded(obj)
obj.Main.ProximityPrompt.Triggered:Connect(function(plr)
local Data = DataManager:Get(plr)
if Data then
print(Data)
Data.Inventory[obj.Name] += 1
end
end)
end
IGNORE “Wheat”, that was used for testing earlier.
Anyway, the main goal is to be able to pickup a box in the workspace that has a proximity prompt. When the proximity prompt is triggered, you’ll be given the time. But for some reason, I’m not able to call an entirely new value?
local Players = game:GetService("Players")
local ProfileService = require(game.ReplicatedStorage.ProfileService)
local ProfileStore = ProfileService.GetProfileStore(
"Player",
{
Cash = 0;
LoginTimes = 0;
Avatar = {};
Inventory = {};
}
)
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 = {}
function DataManager:Get(player)
local profile = Profiles[player]
if profile then
return profile.Data
end
end
return DataManager
I just noticed something, “avatar” doesn’t appear whenever I call for the data?
Okay, I think I figured it out. Is there any way I can wipe all of the stuff inside of my profile? Its full of a lot of random stuff when I was adding the reconcile
Yes true! I completely forgot about that plugin. Just a quick question though, where do I find the name of the datastore? Nevermind, I’m dumb. FOUND IT!
Wow, this is really helpful. I thought this was way to confusing and advanced for me, but it is really easy to use after you get going. To anybody thinking about adding this, DO IT. If you think it is too hard check out the youtube tutorials, they help a lot.
The only reason that would happen to you if you kick the player whenever the profile:ListenToRelease call back was called, which is when another server tries to force load a profile. In your case, you may probably joined the game again - having 2 sessions - which ProfileService swears to protect you from.