Thank you for the clarification & help
Hey, how would you use CorruptionSignal
etc? do they function the same as events?
Looked at the wrong places in the docs, found it. My bad
I always find something by myself right after posting it
Hey, I’m experiencing some issues with loading in data when a player joins.
It saves the data just fine (printing when they leave), but when they rejoin it gets reset again.
I’m unsure whether this is something on my part or on yours.
Could you contact me on discord? Pyseph#0001
Thanks!
It doesn’t work inside studio, by the way.
Try disabling all scripts that use datastore service, as sometimes they can interfere with ProfileService.
Yeah, I figured it was something to do with studio
I tested in-game, but I still got the same result. Unsure what it is, as I have a data network module which handles all data related stuff: I removed everything that had to do with datastore service.
Issue has been resolved, thank you loleris for helping me out.
The issue was that after rewriting the data network to use this module, I forgot to double check for serialization
Can we have a toggle to set whether or not we want ProfileService to save to live keys while still maintaining Studio access to API services? I don’t like the all-or-nothing nature of the setting. DataStore2 has a feature like this where the inclusion of a BoolValue named SaveInStudio
dictates this and by default, will not save to live keys if the BoolValue does not exist at all.
Can you give me an example of when you need API access enabled, but live datastores disabled?
Hey, Im quite new to using data stores, I
ve been using datastore2 for my previous project. How many extra data stores such as ordered data stores for leaderboards and data stores for processing receipts it can handle together with this? Also, does it require player being online in order for me to edit their data? (I assume so…)
Good job my southern brotha (read username…)
ProfileService sits at around 10% to 18% of a game’s DataStore call budget when every player owns their own Profile in a live game. Using lots of DataStore calls is a more advanced topic and you’ll need to read official documentation to find your answers:
ProfileService API allows you to edit player data regardless of whether they’re online, either by simply loading their profile or with the use of GlobalUpdates.
Well this module sounds like a keeper! Im aware of the game limits of datastore calls, so the 10-18% was helpful... ny understanding is, there
s a total data limit, but also request limits for speciffic key? Thing i forgot to ask is if this module works with any "profiles", not only player data, for example, those very processed receipt id
s? Sorry, i`m sure you mentioned it, i just missed it.
ProfileService is more specialised for player data - use custom DataStore code for other features.
Parts of my game use normal DataStores right now to access global data that doesn’t change often. Additionally, I like to test for different situations using different save data “templates” or my own user data but I don’t want the testing I do to reflect onto the live DataStore. Right now, I develop within the same game as the live server, just different places, so the DataStores are technically shared (although I could set up using different scopes, but then I would lose being able to use my “live” data for testing)
Does ProfileService use journelling (recording what has changed, rather than overriding) on table values?
ProfileService was created with performance as it’s #1 priority, so you can’t rollback data as of now… However, based on Roblox’s roadmap, we might have first-party rollback support in the very near future and ProfileService should be compatible with it as soon as it comes out.
Just a quick performance nitpick. I dont see the need to run this every frame?
RunService.Heartbeat:Connect(function(dt)
-- 1) Auto saving: --
local auto_save_list_length = #AutoSaveList
if auto_save_list_length > 0 then
local auto_save_index_speed = SETTINGS.AutoSaveProfiles / auto_save_list_length
local current_tick = tick()
while current_tick - LastAutoSave > auto_save_index_speed do
LastAutoSave = LastAutoSave + auto_save_index_speed
local profile = AutoSaveList[AutoSaveIndex]
if current_tick - profile._load_timestamp < SETTINGS.AutoSaveProfiles then
-- This profile is freshly loaded - auto-saving immediately after loading will cause a warning in the log:
profile = nil
for i = 1, auto_save_list_length - 1 do
-- Move auto save index to the right:
AutoSaveIndex = AutoSaveIndex + 1
if AutoSaveIndex > auto_save_list_length then
AutoSaveIndex = 1
end
profile = AutoSaveList[AutoSaveIndex]
if current_tick - profile._load_timestamp >= SETTINGS.AutoSaveProfiles then
break
else
profile = nil
end
end
end
-- Move auto save index to the right:
AutoSaveIndex = AutoSaveIndex + 1
if AutoSaveIndex > auto_save_list_length then
AutoSaveIndex = 1
end
-- Perform save call:
-- print("[ProfileService]: Auto updating profile - profile_store_name = \"" .. profile._profile_store._profile_store_name .. "\"; profile_key = \"" .. profile._profile_key .. "\"")
if profile ~= nil then
coroutine.wrap(SaveProfileAsync)(profile) -- Auto save profile in new thread
end
end
end
-- 2) Issue queue: --
-- Critical state handling:
if ProfileService.CriticalState == false then
if #IssueQueue >= SETTINGS.IssueCountForCriticalState then
ProfileService.CriticalState = true
ProfileService.CriticalStateSignal:Fire(true)
CriticalStateStart = tick()
warn("[ProfileService]: Entered critical state")
end
else
if #IssueQueue >= SETTINGS.IssueCountForCriticalState then
CriticalStateStart = tick()
elseif tick() - CriticalStateStart > SETTINGS.CriticalStateLast then
ProfileService.CriticalState = false
ProfileService.CriticalStateSignal:Fire(false)
warn("[ProfileService]: Critical state ended")
end
end
-- Issue queue:
while true do
local issue_tick = IssueQueue[1]
if issue_tick == nil then
break
elseif tick() - issue_tick > SETTINGS.IssueLast then
table.remove(IssueQueue, 1)
else
break
end
end
end)
I replaced it with this so it runs less often and it runs just fine.
local timer = 0
-- Auto saving and issue queue managing:
RunService.Heartbeat:Connect(function(dt)
timer = timer + dt
if timer >= 0.35 then
timer = 0
-- 1) Auto saving: --
local auto_save_list_length = #AutoSaveList
if auto_save_list_length > 0 then
local auto_save_index_speed = SETTINGS.AutoSaveProfiles / auto_save_list_length
local current_tick = tick()
while current_tick - LastAutoSave > auto_save_index_speed do
LastAutoSave = LastAutoSave + auto_save_index_speed
local profile = AutoSaveList[AutoSaveIndex]
if current_tick - profile._load_timestamp < SETTINGS.AutoSaveProfiles then
-- This profile is freshly loaded - auto-saving immediately after loading will cause a warning in the log:
profile = nil
for i = 1, auto_save_list_length - 1 do
-- Move auto save index to the right:
AutoSaveIndex = AutoSaveIndex + 1
if AutoSaveIndex > auto_save_list_length then
AutoSaveIndex = 1
end
profile = AutoSaveList[AutoSaveIndex]
if current_tick - profile._load_timestamp >= SETTINGS.AutoSaveProfiles then
break
else
profile = nil
end
end
end
-- Move auto save index to the right:
AutoSaveIndex = AutoSaveIndex + 1
if AutoSaveIndex > auto_save_list_length then
AutoSaveIndex = 1
end
-- Perform save call:
-- print("[ProfileService]: Auto updating profile - profile_store_name = \"" .. profile._profile_store._profile_store_name .. "\"; profile_key = \"" .. profile._profile_key .. "\"")
if profile ~= nil then
coroutine.wrap(SaveProfileAsync)(profile) -- Auto save profile in new thread
end
end
end
-- 2) Issue queue: --
-- Critical state handling:
if ProfileService.CriticalState == false then
if #IssueQueue >= SETTINGS.IssueCountForCriticalState then
ProfileService.CriticalState = true
ProfileService.CriticalStateSignal:Fire(true)
CriticalStateStart = tick()
warn("[ProfileService]: Entered critical state")
end
else
if #IssueQueue >= SETTINGS.IssueCountForCriticalState then
CriticalStateStart = tick()
elseif tick() - CriticalStateStart > SETTINGS.CriticalStateLast then
ProfileService.CriticalState = false
ProfileService.CriticalStateSignal:Fire(false)
warn("[ProfileService]: Critical state ended")
end
end
-- Issue queue:
while true do
local issue_tick = IssueQueue[1]
if issue_tick == nil then
break
elseif tick() - issue_tick > SETTINGS.IssueLast then
table.remove(IssueQueue, 1)
else
break
end
end
end
end)
That’s what i’m trying to accomplish for my data, is there are source for this right now? Because I have a hard time knowing on how to merge saves without interfering with the data that had already been saved.
Edited…
I have 2 question about the merging process.
- Would I be able to merge things inside tables? Here’s my code:
{
Currency = {
-- Merge (add a table) here
Tokens = 0, Gems = 0, Event_Currency = 0, CurrencyBag = 0
},
Inventory = {
-- Merge (add a table) here
Knives = {{"DefaultKnife"}, KnifeEquip = {"DefaultKnife"}},
Pets = {{"Rabbit"}, PetEquip = {"Rabbit"}},
Radios = {{}, RadioEquip = {}},
Effects = {{}, EffectEquip = {}},
Titles = {{}, TitleEquip = {}}
}
- With the merging process, you could add tables, but could you remove tables as well?
Anything is possible. At this point I recommend you choose the solution that is the most comfortable for your personal coding knowledge level - whether it’s automatic merging or a custom code that checks individual parts in your data table.
Is updating the data being saved automatic? Or is that something we would have to add ourselves? In EncodedLua’s video it seemed to suggest that a coroutine or spawn() function would be required to continually update the data.
It’s assumed that anything you store in Profile.Data
is going to persist automatically assuming you properly call Profile:Release()
after finishing work and not write to Profile.Data
after Profile:ListenToRelease()
is triggered.