Skip to content

Conversation

@phobos665
Copy link
Contributor

@phobos665 phobos665 commented Nov 21, 2025

This PR brings in Steam Achievements 🎉

Steam Blocks were very difficult to figure out, but ultimately the prorots had keyvalues that were decodable and I was able to figure it out.

TL;DR Explanation:

  • Figured out the real ID, unlock time & whether they were locked/unlocked of individual Achievements via the timestamps index and, thanks to this, was able to then match them with the schema metadata to retrieve the rest of the information.

  • Parse bit-level achievements from the keyvalue schema

  • Create a callback to pull in the achievement full with metadata.

  • Parse achievement blocks into individual achievements with names, descriptions, icons, and unlock times

  • Support for base game and DLC achievements organized in stat blocks

In-depth Explanation:

AchievementBlocks come from the UserStatsCallback:


List<AchievementBlocks> blocks = callback.getAchievementBlocks(); 

Blocks mean that theres collections of multiple achievements. For Example, Borderlands has 3 blocks. Which is Game + DLC 1 and DLC 2.

By delving into this, I managed to figure out that the timestamps were an list of unlockTimestamps in as numbers. The interesting part is that they're always in order of AchievementId and if it is not unlocked based on whether it was 0 or a value above 0.

From there, we were then able to iterate over the "bits" in the schema, which contains the individual Achievement metadata:

                        val displayName = displaySection?.get("name")?.get("english")?.value
                        val description = displaySection?.get("desc")?.get("english")?.value
                        val icon = displaySection?.get("icon")?.value
                        val iconGray = displaySection?.get("icon_gray")?.value
                        val hidden = displaySection?.get("hidden")?.value == "1"
                        

The Achievement Metadata

Metadata Fields (from schema)

  • name: String? - Internal achievement name (e.g., "TF_PLAY_GAME_EVERYCLASS")
  • displayName: String? - Localized display name shown to users
  • description: String? - Achievement description text
  • icon: String? - Icon hash for unlocked (color) version
  • iconGray: String? - Icon hash for locked (grayscale) version
  • hidden: Boolean - Whether achievement is hidden until unlocked

Checklist

  • Code compiles correctly
  • All tests passing
  • Samples run successfully
  • Extended the README / documentation, if necessary

- Add getExpandedAchievements() to UserStatsCallback to parse bit-level achievements from schema
- Add Sample 32 to demonstrate achievement retrieval with full metadata
- Parse achievement blocks into individual achievements with names, descriptions, icons, and unlock times
- Support for base game and DLC achievements organized in stat blocks
@phobos665
Copy link
Contributor Author

Achievements.txt
Here's the text file where I was able to prove I could get the achievements correctly.

I'd love to talk more about this pull request because I think I've nailed it and need someone else to verify with and see what we can do to tighten up the code and ship it.

@LossyDragon
Copy link
Collaborator

Thanks for helping with this!

@LossyDragon LossyDragon merged commit 8e7dcd4 into Longi94:master Dec 6, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants