๐ฃ๏ธAdvanced Profiles
Public rich profiles available all the time
Last updated
Public rich profiles available all the time
Last updated
Support us as a GitHub Sponsor and get instant access to all our assets, exclusive tools and assets, escalated support and issue tracking and our gratitude. These articles are made possible by our GitHub Sponsors ... become a sponsor today!
Rich public user profiles can be significant to social games
To be effective these profiles need to always be available any time a player sees another player's name so as an offline friend, as an entry in a leaderboard, as a teammate in a match, as an opponent that stomped you in a match, etc. The profile needs to always be accessible to every user who might see it without needing to be a friend or actively in a lobby or server with that player.
This means that you can't depend on Steam's rich presence alone though that can be of use, notice the Main Menu text under that user's name. That is from Steam's rich presence but would only list people who are you or are your friends.
All of the rest of that data is stored in a profile object, this being DOTA it's likely stored on the account after all DOTA has access to so much more than we as regular Steam developers do. That said we can do nearly the same using Leaderboards to house profile data.
In summary, the idea is to create a publicly accessible rich account profile as you would see with DOTA and similar games where users can show off their in-game accomplishments, favourite builds, top stats and more.
Leaderboards are used since anyone can query a leaderboard and fetch the data contained in it. Leaderboards can be easily written by anyone and can contain more than just a score and rank. The key to this use case is the use of a leaderboard details array and leaderboard attachments.
The examples shown here are written in C# but would be similar in C++ or via Unreal Blueprints
Your first step is always to decide what data you want the users to store on/in their public profiles. You will also need to create a serializable struct or object that can house that information.
Example serializable
As much as possible try to pack that information into an int[] with 64 or fewer entries. Of course, if your values are already ints then this is very easy, for example, the level value in ProfielDataObject above would be a good candidate as would the GameMode assuming it was an enum. Enums are just ints with names.
Booleans are something we use a lot as game developers and an int can represent 32 of them quite easily. Below I show a bit of how you might pack up to 32 Booleans in a single int
Yes, you can use bitwise operations to do this and yes it is less code to use bitwise and arguably faster operations to execute but bitwise operations tend to make some people's heads smoke and really if you're doing this so often the performance impact is an issue then you should be cashing profile results.
.NET gives us a more scripter-friendly way to monkey with bits that not many seem to know about so I will show that here. You can read more about BitArray here
https://docs.microsoft.com/en-us/dotnet/api/system.collections.bitarray?view=net-5.0
You can read more about bitwise operations here
Leaderboards store โdetailsโ per entry as an int[] with up to 64 entries. This is fast to read and doesnโt require any serialization so less error prone. You can get clever with this and pack all sorts of data into an int array โฆ for example, you can think of an int as 32 Booleans but what you do with it is up to you.
Leaderboards can have 1 attachment per user, this attachment can honestly be any file compatible with Remote Storage but we have already built tools to help you save and load serializable types as files so best to use that.
Our system will use JsonUtility to serialize the file and convert it to byte[] for storage on Steam Remote Storage, we will then mark the file as shared and attach it to the leaderboard entry for you. Once attached it will be available to people reading the board even if the original is removed from remote storage โฆ or so Valve says.
Let's assume you have defined a leaderboard that will be used as your player_profles and you have linked it with your game as you would any other leaderboard.
To save a userโs profile you need only create the serializable object and set up the detail array you wish to store and then call upload with a score different than the current score โฆ for ease what we do is an alternative between 1 and 0 e.g. if the current score is 1 we force an update to 0 if it's 0 we force an update to 1. You could also increment the score where the score represents a โversionโ number if you like; that is up to you.
Yes, I use a lot of expression there; here is what's happening
We simply upload our score and details with a ForceUpdate upload method
When that completes we attach our profile data myProfileObject
and name it "gamename.profile" You really could name it anything or if you wanted to be extra safe makes it name the string value of a GUID. Those are assured to be unique and the name doesn't matter for a human.
When that completes we check for errors but otherwise are done
Now that itโs a leaderboard you can do all sorts of very cool things depending on what you stored as score. For now, though let's assume you just want to fetch a profile for a specific user
The callback will contain the entries for each of the users in your users' list. You can use the LeaderboardEntry object to read the details and fetch the attachment e.g.
The callback will contain a populated ProfileDataType assuming there is a valid JSON attachment it will return the default so null for classes or new for structs.