Website: Achievements
Step-by-step guide for web developers - read a character's achievement progress from the AchievementProgress table and show cards, points and an achievement-points ladder on the website, with full Nod
The game stores each character's achievement progress as one binary row. This guide shows how to read it and show it on the website: a character's achievement page (cards, tiers, progress, points) and a server-wide points ladder.
For what achievements are and how to configure them, see Achievements.
What you can show
Character profile
Every achievement as a card: name, text, icon, tier, progress bar, claimed / ready / in progress. Points total in the header
Ladder
Characters sorted by achievement points
"Recently completed"
Not possible: completion times are not stored anywhere
Where the data lives
Progress
SQL table AchievementProgress, column Blob
One counter and one "claimed" byte per achievement
Names, texts, tiers, points
Data/Plugins/Achievements/*.xml
Everything you print
Icons
Client files Data/Interface/UI/atlas/achievements.png + achievements.xml
48 × 48 icons
CREATE TABLE AchievementProgress (
Name varchar(10) PRIMARY KEY, -- character name
Blob varbinary(1254) NOT NULL
)No row = the character has no progress yet.
Step 1 - Read the blob
All numbers are little-endian (least significant byte first).
0
1
Version. Must be 1. 0 = empty row, no progress
1
1
Reserved
2
2
Count = number of entries (250)
4
5 × Count
One entry per achievement Id, in Id order (entry 0 = Id 0, entry 1 = Id 1, ...)
Each entry (5 bytes), at offset 4 + Id × 5:
0
4
Counter, signed 32-bit. Below 0 = treat as 0
4
1
Claimed, bit mask. Bit 0 = tier 1 claimed, bit 1 = tier 2, ... bit 7 = tier 8
Example: Id 14 is at offset 4 + 14 × 5 = 74. Bytes 58 02 00 00 01 → Counter 600, tier 1 claimed.
Step 2 - Load the achievement files
Load the folder the same way the GameServer does:
Every
*.xmlinData/Plugins/Achievements/, in alphabetical order.Skip
Settings.xmland every file whose name starts with_.Each file has one
<Category Id Name Order Icon>root.Each
<Achievement Id Name Icon Text Enabled>inside it. SkipEnabled="0".Its
<Tier Target Points>children, in file order. A tier whoseTargetis not bigger than the one before is skipped (the server skips it too). Max 8 tiers.
Keep the first achievement with a given Id; the server skips duplicates.
Parse once and cache. Reload when a file changes.
Step 3 - Work out each card
For one achievement with tiers T[0..n-1], the character's Counter and Claimed:
Tier i reached
Counter >= T[i].Target
Tier i claimed
Claimed & (1 << i) is not 0
Next target
Target of the first tier not reached. None = all reached
Progress bar
Counter / next target (full when all reached)
Points earned
Sum of Points of the claimed tiers
Card state:
Complete
Every tier claimed
Gold / done badge
Ready to claim
A tier is reached but not claimed
Highlight. Players claim in game (Window Menu U → Achievements)
In progress
Counter > 0, nothing to claim
Normal
Not started
Counter = 0
Dimmed
The character's achievement points = sum of "points earned" over every achievement. That is the number the game shows in the window header. Max points = sum of every tier's Points.
Counters that keep the highest value: ReachLevel, Reset and ItemUpgrade achievements store the best value seen (level 350, 12 resets, +13), not a count. Show them as 350 / 400, the same as the others. PlayTime counts minutes.
Step 4 - Icons
The game icons are in one image (achievements.png) with a map of where each icon is (achievements.xml):
Copy achievements.png to the website and use CSS sprites. An achievement's Icon="ach/bloodcastle" → background-position: -50px 0. Missing icon → use ach/default, then an empty box (the game does the same).
Full code
Uses mssql and fast-xml-parser (npm i fast-xml-parser).
Uses PDO (sqlsrv) and SimpleXML.
Rendering a card (React example):
Group the cards by card.def.category and show the categories as tabs, in order, like the game.
Worked example
02 - Progression.xml has (rewards left out):
Blob bytes 74-78 (Id 14): 58 02 00 00 01.
Counter
0x00000258 = 600
Claimed
0x01 = tier 1 claimed
Reached
tiers 1 and 2 (600 ≥ 60, 600 ≥ 600)
Next target
6000 → bar at 10%
Points earned
5 (only tier 1 claimed)
State
Ready to claim (tier 2 reached, not claimed)
Important Behavior
Data delay. Counters are saved every
AutosaveSecseconds (default 180) and on logout. Tier completions and claims save at once. So an online player's progress bars can be up to 3 minutes behind; claimed tiers and points are always current.Rows by character name. A character created after the plugin was enabled has no row until it first logs in.
Deleted characters keep their row. Join with
Character(as the ladder code does) so they do not show up.Plugin off or unlicensed. The rows stay, the game shows nothing. The website cannot see the license; if the paid module is not licensed, do not show the pages.
Removed achievement. Its progress stays in the blob but there is no definition, so it is not shown. Never reuse an
Idfor a different achievement.Rewards are not needed on the website. Players claim in game; the site only shows status.
Common Issues
Related pages
Achievements - triggers, tiers, rewards, settings
Last updated