That’s exactly my point, if MessagingService fails you fall back to other methods, but there is no harm in implementing it if it will increase the user experience of your game. I have already created a session locking system with a method using MessagingService alongside DataStores which in theory should prevent this supposed 80 second long wait.
In that bulleted list you make it as if I’m only wanting to use MessagingService which just isn’t the case.
The “critical” part of that line is exactly what category data saving falls under. A player not being able to load their data is considered “critical” in most applications on the internet.
That’s why you implement a system that waits a max amount of time and the querys the datastore again to check if the session owner became nil. You could argue this method might take a long time too but after 1 or 2 attempts if the session owner still isnt nil, then you could just steal ownership. Time between UpdateAsync calls is under 10 seconds, so this process can be done a lot quicker than 80 seconds.
This module is made with my highest expertise - I wouldn’t be able to make any valuable changes to the dead session lock mechanics of ProfileService.
Stop talking about the 80 second wait like it’s going to happen every 10 minutes. No. If you’re not doing witchcraft in your server and Roblox is in healthy shape you’ll never get the 80 second wait.
Also dead session locks are passively cleared after 30 minutes - if the player profile receives a dead session lock and joins later in the day or next day, ProfileService will automatically clear the dead session lock based on the os.time() tag. I can’t go less than 30 minutes due to how wonky os.time() can be.
If you know of a perfect way to improve this you’ll have to make a pull request on the github repo - as I said, this is no longer something I can script based on my Roblox API knowledge and it’s trust limits. MessagingService can’t add speed to ProfileService for clearing dead session locks because the server that locked the profile is DEAD - MessagingService should not be used for checking if a server is dead because there might be other causes preventing a LIVE server from responding.
Ok, but let me explain something to you. MessagingService may be unreliable, but if you notice in both of those posts, it says “exceeds the allowed limit”. Now the -2 billion seconds is definitely an issue, but perhaps that is the result of the errors in the developer’s code. If not, sure lets say MessagingService is unreliable and broken.
Secondly, your third point about MessagingService being useless is completely false. You should be constructing your game in a manner that knows stuff might not work and can continue running if and when those errors happen. It wouldn’t be an issue to use MessagingService in a situation like this as long as long as you know how to create your code with those issues in mind.
I guess saying that it’s useless is an overstatement, but in this case for ProfileService it’s useless.
Sure, that isn’t incorrect but I rather not work with something that can break when it’s not my fault, I rather be in control and know that my code will work 100% of the time not 99.99% or less.
I can understand why DataStore service will sometimes fail, and that’s fine we can handle it appropriately with the error message.
Ironically MessagingService doesn’t have any error message for us despite deliberately stating that “delivery is not guaranteed”, we expect it to be delivered but it’s not this is a major flaw in the API design.
The data limit isn’t nearly enough to do anything substantial and innovative. (We only have cross server chat, server list, and infinite player servers but that’s it from what I’ve seen)
The data budget is limiting and might not even be able to support large games (Top #10 Concurrent users)
With these 3 issues, to me it is useless and not worth using, it’s already hard enough to make games why make it even harder for ourselves? You can definitely use it don’t be discouraged to do so.
Sorry if sound a bit stupid here if I don’t understand this module abit.
I’ve been using ProfileService for the past month in my upcoming game and I’m absolutely loving it. It makes things like trading system’s less vulnerable to things like duplication (of course if you don’t somehow mess up things). In your post and on your documentation, it states that
I used to love DataStore2 but stopped using it after I saw this because of how it doesn’t rely on the Player instance. Now, I’m planning to make a clan system using ProfileService but I’ve hit a roadblock. I want it so players can create clans on their own and invite members from different servers or if they are offline (i’ve already done this part with global updates). Now, the issue I have is with loading and saving clan data.
For example, if Player A is in Server A and Player B is in Server B, I would want to disable SessionLock and have it so data gets updated periodically. The issue is that I have a Clan Shop that uses a currency called Clan Points. Player A & B can be Clan Administrators and therefore both would be allowed to use the currency. How would I make sure that they don’t somehow manage to “duplicate” the clan points by buying stuff from the Clan Shop and not having it reflect in other servers the true amount of clan Points.
This only happens only when I join the game, not when I leave, though.
I also get some warnings on datastore throttles here and there but I don’t really care about them because the module still saves the data nicely.
This is what I’m saving, these are all tables with only booleans, strings, and numbers in them. I’ve been serializing my userdata for a while now, and I also saved in this format. The only difference is that I’m using ProfileService now
Edit: Nvm found it, I forgot to serialize a Color3
I converted my game to using this module from DataStore2. So far I haven’t gotten any reports of data issues and the module is a lot easier to use than DataStore2 in my opinion.
Yes. As long as you know the UserId, you can load up any offline player profile. You can learn more about loading profiles in official documentation and also reading the example code.
ProfileService itself does not know if it’s profiles belong to a Player and whether that player is online - all it takes is a unique DataStore key with a UserId to fetch a saved profile.
Beautiful module!
I’ve been looking for a module like this for quite a while, as, I personally despise standard DataStore APIs and intentionally put off all my DataStore work until I have to implement it. This is basically a perfect example of how I would expect datastores to work, and, I don’t really see myself not using this even for lighter projects.
It’s very feature packed, and, my only complaint is that on the documentation it’s a bit much to take in what is important to a general user and what isn’t, however, ignoring that, its a pretty brilliant utility. (My own projects really struggle with this as well, my documentation are even more of a mess)
This warning caught my eye.
If generally what some might consider hacky solutions aren’t a huge problem to you, perhaps a nice way to handle this more gracefully is via wrapping a __index/__newindex metamethod hook. Yielding within these hooks produces a predictable error message.
For example, you could create the following wrapper implementation to stop yielding (There is really no arguing this isn’t hacky in my opinion, the pass through of arguments and such is ridiculous):
local callUtility = setmetatable({}, {
__index = function(_, callData)
local func, arguments = table.unpack(callData) -- Extract the function and its arguments
return table.pack(func(table.unpack(arguments))) -- Call the function with the given arguments, and pack up its results
end
})
local someTargetFunction
local function someWrappedRepresentation(...)
-- Pass the function and arguments through the metamethod, then unpack the results to the call
return table.unpack(callUtility[{someTargetFunction, table.pack(...)}])
end
A much less hacky alternative implementation would be to use coroutines and simply monitor the coroutine.status result. This is personally what I use for publicized projects, vs the above for private projects (because there are just so many things that would upset people about the above haha).
My framework uses a signal module with no yield support / yield catching in connected listeners for performance reasons - ProfileService is part of this framework module group and I’m trying to provide it as a public resource exactly like it is in my commercial project. ProfileService may evolve with my future projects and I’m going to continue providing it as a commercial-grade public resource.