Rich Presence (RP) is brief overview of what active players are currently doing in their game. To have RP in a game you need a Rich Presence Script (RPS) which is created by Developers. The Script checks the player's game memory and as programmed reports the values of certain addresses with definitions assigned by the Developer such as which stage the player is on, how many lives they have, if the game is paused, what game mode they are playing, what the player has accomplished, etc. This information is reported back to the website.
Example of RP in action:
To see the RP live in a game click on the RetroAchievements menu in your emulator and then click on Rich Presence Monitor. A small window will show you your active RP. (Good for debugging)
The best way to understand Rich Presence is to look at various examples in game, look at the addresses used and look at how the text is displayed in the Rich Presence Monitor and on site.
How Does it work?link
Every time a game is launched, it fetches the achievements in a 'patch' file for the ROM which details all the achievements and memory addresses (and leaderboards) that can be watched for. It will also request a Rich Presence Script for the currently loaded ROM. The emulator will report back to the website every 120 seconds. Similarly, every 120 seconds or so, the 'active players' box on the front page will refresh, detailing the last known activity of all active players. If there isn't a rich presence script given, the text will be 'earning achievements' if playing a game with achievements, 'playing [game]' if playing a game without achievements, or 'developing achievements' if the memory dialog is open and visible.
The RPS for each game can be found under the development section on each game's page:
Example (Super Mario Bros.)link
Format:Digit FormatType=VALUE Lookup:Mode 0=[Demo] 2=[World Complete] Lookup:Paused 0x81=▌▌ 0x80=▌▌ 1=▌▌ Lookup:Star 5=🌟 4=🌟 3=🌟 2=🌟 1=🌟 Lookup:Powerup 0=Small 1=Super 2=Fire Lookup:Swimming 1= swimming Lookup:Status 0= [Loading] 1= taking a vine warp 2= entering a warp pipe 3= entering a warp pipe 4= 🚩 5= [Stage Complete] 6= [Game Over] 7= [Entering Area] 9= growing 0xA= shrinking 0xB= 💀 0xC= powering up Lookup:Quest 0x0=1st 0x1=2nd Display: @Mode(0xh770)@Paused(0xh776)@Star(0xM79f_0xN79f_0xo79f_0xP79f_0xQ79f_0xR79f)@Powerup(0xh0756) Mario in @Digit(0xh75f_v1)-@Digit(0xh75c_v1)@Swimming(0xh704)@Status(0xhe), 🚶:@Digit(0xh75a_v1), @Quest(0xh7fc) Quest
It breaks down into a series of Lookup objects, Format objects and one Display object.
Lookups are defined like this:
Lookup:NameOfLookup Value1=Text When This Value Value2=Text When Another Value ...
We give the Lookup a value, consisting of a series of memory addresses and modifiers. More about this later.
Format tables are defined like this:
Format:, then the name of the format type. On the next line, give
FormatType=, then one of the following:
VALUE: generic value, no leading zeroes.
POINTS: generic value, padded with leading 0s to 6 digits.
FRAMES: value describes the number of frames elapsed, and will be turned into 00:00.00
SECS: value describes the number of seconds elapsed, and will be turned into 00:00
MILLISECS: value describes the number of hundredths of a second elapsed, and will be turned into 00:00.00
MINUTES: value describes the number of minutes elapsed, and will be turned into 0h00
SECS_AS_MINS: value describes the number of seconds elapsed, and will be turned into 0h00
Display will be a string that gets shown in the 'Active Players' box on the front page. It refers to the previously defined Lookup and Format objects using a single '@'. It then specifies a name for the lookup or format (case sensitive!), and immediately after, in brackets, a series of memory values specifying what to send to that lookup or format object.
This means use the Lookup or Format that's called
Powerup, and give it whatever value is in 0xh756.
Example Lookup Breakdownlink
@Mode(0xh770)- Lookup for the address that shows if the game is in demo mode or a world has been completed.
@Paused(0xh776)- Lookup for the address that shows if the game is paused (3 values are used, two of them are for pausing and unpausing).
@Star(0xM79f_0xN79f_0xo79f_0xP79f_0xQ79f_0xR79f)- Lookup for the address of if Mario has Star invincibility. More on this later.
@Powerup(0xh756)- Lookup for the address that show if Mairo is Small, big or has fire power.
Mario in- Static text to string lookup and format objects together.
Digitis a format object defined as a value. The address 0xh75f is the World minus 1 (because it it 0 based, as in it starts counting at 0).
_v1Means + value 1.
_v+1is also correct.
-- More static text to split World and Level. as in the hypen in World 1-1.
@Digit(0xh75c_v1)- Another use of the
Digitformat object. This time It's looking up the stage. World 1-X.
@Swimming(0xh704)- Lookup for the address that shows if the player is swimming.
@Status(0xhe)- Lookup for the address that shows Mario's status, such as going through pipes.
, 🚶:- More static text. 🚶 is a symbol for lives.
@Digit(0xh75a_v1)- Third use of the
Digitformat object. This time it's checking the player lives address.
,- Static text.
@Quest(0xh7fc)A lookup to see if the player is in normal or on the 2nd quest, hardmode.
Quest- Static Text.
To specify what size of address you are are checking there are various characters used. (capitalization is ignored)
- A 16bit address is default and has no character designation. At 0x10 the address is two bytes - 16 bits.
- An 8bit address's character is
H). At 0xh10 the address is one byte - 8 bits.
- An upper4 address's character is
U). At 0xu10 the address is one nibble - 4 bits.
- A lower4 address's character is
L). At 0xl10 the address is one nibble - 4 bits.
- A bit0 address's character is
M). At 0xm10 the address is one bit, the lowest bit:
- A bit1 address's character is
N). At 0xn10 the address is one bit, the second bit:
- A bit2 address's character is
O). At 0xo10 the address is one bit, the third bit:
- A bit3 address's character is
P). At 0xp10 the address is one bit, the fourth bit:
- A bit4 address's character is
Q). At 0xq10 the address is one bit, the fifth bit:
- A bit5 address's character is
R). At 0xr10 the address is one bit, the sixth bit:
- A bit6 address's character is
S). At 0xs10 the address is one bit, the seventh bit:
- A bit7 address's character is
T). At 0xt10 the address is one bit, the top bit:
- A 32bit address's character is
X). At 0xx10 the address is four bytes and 32 bits.
- A 24bit address's character is
W). At 0xW10 the address three bytes and 24 bits.
Summarizing on a table:
|location/size||prefix (the letters can be in lower case)||example|
Conditional Display Stringslink
Display: ?0x 000085=0?Title Screen ?0xT00007c=1?Custom Map in @Landscape(0xH00016c) Playing Battle @Battle(0x 00007c*0.2) in @Landscape(0xH00016c)
Display: marker is still used to indicate the start of the display block. If the next line starts with a question mark, it is considered to be a conditional display string. The portion of the line between the two question marks is the conditional clause. If the conditional clause evaluates true, then the remaining portion of the line is used as the display string. If it does not evaluate true, then processing proceeds to the next line. If it starts with a question mark, the same process repeats. If it does not start with a question mark, the entire line is used as the default display string.
NOTE: A default display string is required in case none of the conditional display strings match. If you only have conditional display strings, the script will appear to do nothing.
Looking at this example, if the 16-bit value at $0085 is 0, the display string is
Title Screen. If not, the next line is examined. If the 7th bit of $007C is 1, the display string is
Custom Map in @Landscape(0xH00016c). If not, the final line does not have a conditional clause and is used.
Display strings associated with a conditional clause support all of the same syntax as the default display string. In this example, you can see the
@Landscape lookup is used in both the conditional display string and the default display string. The lookup itself only has to be defined once.
The conditional phrase supports all of the previously mentioned address accessors as well as AND (_) and OR (S) logic. Note that OR clauses still require a 'core' group, just like achievements.
if the 8-bit value at $1234 is 32 and the 8-bit value at $2345 is 0, display
if the 8-bit value at $1234 is 32 and the 8-bit value at $2345 is 1 or 2, display
if the 8-bit value at $1234 is 32 and the 8-bit value at $5678 is 33 and the 8-bit value at $2345 is 1 or 2, display
Binary Coded Decimal (BCD)link
BCD is when the values are store in an address from as 0-9 (one digit) or 0-99 (two digits). Keep in mind that most often values are stored in hexidecimal, but sometime games will store them in this way and here's the best way to handle these addresses in your display.
BCD decoding treats each hex character as a decimal digit. If the memory inspector shows 86 (in hex), the result of BCD decoding the value would be 86 (in decimal).
For value objects you can use the BCD prefix, as in
b0xh1234. This also works with leaderboard values.
Note that you still need to specify the size of the BCD memory address.
b0x1234 reads a 16-bit value.
b0xh1234 reads an 8-bit value and
b0xX1234 reads a 32-bit value.
NOTE: Support for 16-bit and 32-bit BCD decoding is a feature of the 0.075 toolkit.
This is most commonly used for score and time, but often other types of display values.
- 65,535 character limit for script
- 255 character limit for what is displayed
- Unicode characters are allowed, some use more characters than 1
- Lookup keys are decimal by default and hex if you place the prefix of
0x. This mean
- Lookup/Format names are case specific and must exactly match the usage in the Display string:
@test(0x1234)will not find "Format:Test"
- Lookup/Format cannot contain spaces before or after the lookup name.
@test(0x1234)will not find "Format:test " or "Format: test"
- If your Lookup/Format cannot be found, nothing will be displayed.
This is a @test(0x1234).will result in
This is a .if
testcannot be found.
- Comments can be added anywhere in the script. A double slash (
//) indicates the remaining portion of the line should be ignored when processing the script. Note: comments still apply toward the script size limit.
Tips and Trickslink
- Lookup names can be as short as a single character if you need to squeeze in a few extra characters.
- Leading zeros can be removed from addresses (
0xh0001can be shortened to
- Turning all your values from hex into decimal will take up less characters.
- Unicode characters don't always "take up less space" they often take up to four system characters.
- If a lookup doesn't contain a mapping for a value, it will result in a blank (no space) value. You can change this behavior by adding a single line to the lookup mapping '*' to the desired fallback value.
Formatnamed mapping can be referenced multiple times with the same or different addresses. You can define a single
Format:Number FormatType=VALUEinstead of defining individual ones for Lives, Score, Level, etc.
- Putting spaces in your lookups sometimes before or after can allow you to hide certain lookups when they are not needed, like how
@Swimming, and @Mode do.
When using lookup and format objects
@object() it's possible to combine and perform calculations. This can be used to correctly display a score, in game time, etc. or make more advanced lookups.
This means use the Lookup or Format
Score, and give it the sum of:
- 0x28 times 10, ADD
- 0x29 times 1000, ADD
- 0x26 times 100000
_adds the addresses together.
- Or you can add a static value
0x28_v10. This adds 10 to your total, as in whatever the value of
0x28+ 10 will be displayed. You can also subtract
- If you'd like to subtract an address you need to multiply the address by -1.
0x29is now negative.
- If you'd like to perform division you'll need to multiply by a decimal.
0x26will output 1/2 of the value at
- And you can string everything together:
- You can also add addresses together to give you lookups based on the sums of various addresses. This is used in the example in @Star. It's looking up sum the 6 lowest bits of the address 0xh79f. The way this address works is that so long as there is a value there Mario is invincible star mario and it counts down from hex value 0x23 (35 decimal) to 0. 23 in binary is 0010 0011 meaning the max sum of these bits could 5 during 0001 1111 when the count down reaches hex value 0x1f (31 decimal).
Unicode Standard Symbolslink
You can without doubt use these symbols in rich presence.
⏰=In Game Time/Game Clock
❤️ or ❤=In a game with hearts (e.g. Zelda)
While making Rich Presence devs need to be careful that they are communicating clearly. If you are using non-standard symbols they will make sense to you but could be entirely confusing to others. When using non-standard symbols, check them with someone else or a few people to see if the symbols you use make sense. If they don't, use better symbols, use text or use symbols alongside of text.
For custom unicode/emoji ShapeCatcher is an excellent resource, you can draw what you're looking for and an AI will find similar matches. Just be careful to not use unicode that too obscure, as they don't all display on all systems.
Take note of community display preference:
Developing Rich Presencelink
The toolkit does not currently have an integrated Rich Presence editor, but you can test local changes before putting them on the server. Once you've started a game and the current Rich Presence has been downloaded from the server, you can find it in
RACache\Data\XXX-Rich.txt where XXX is the game ID.
The Rich Presence Monitor (openable from the RetroAchievments menu) reads this file and shows the current value every second while the window is open.
If you make changes to the
XXX-Rich.txt file, and reselect the menu option, it will read the new changes and allow you to immediate test them without applying the changes to the server. Continue to make changes and reselect the menu option until the script is behaving as you expect, then copy the contents to the server page to make it available to everyone else.
XXX-Rich.txt file is overwritten with the current server data each time the game is opened. As long as you still have the file open in an editor, you can always save your changes over the updated file after reopening the game.
||A memory operand was expected and not found. Memory operands start with
||A lookup key could not be evaluated. The most common cause of this is using hex without the
||An unknown operator was encountered. The most common cause of this is using
||A macro was encountered without providing a value (i.e.
||The rich presence script did not contain a
||A non-combining flag is used in a non-trigger context. Combining flags are AddSource, SubSource, AddHits, AddAddress and AndNext.|
Additionally, if you see
[Unknown macro], that means the macro name could not be resolved. For example
@Points(0xh1234) without defining
Format:Points would generate
This issue happens sometimes when a Rich Presence is created in the
<GameID>-Rich.txt file in the
RACache>Data folder. In this case the issue can be fixed by adding an empty line at the start of the file.
Last 10 changes on this page:
[2021-02-22 13:06] SporyTike:Updated limits which were pretty outdated
[2020-09-16 12:37] meleu:Updated Rich Presence (markdown)
[2020-04-11 19:06] SporyTike:Added Parse error -20 and how to fix Unknown Macro displayed when deving local
[2020-01-08 06:38] televandalist:Updated Rich Presence (markdown)
[2020-01-08 06:37] televandalist:Added 24bit to the address sizes and table. Updated the 16,000 character limit to 65,000.
[2019-11-16 07:29] Jamiras:add MINUTES and SECS_AS_MINS formats; remove " points" from SCORE format
[2019-08-18 05:52] smuckola:a missing letter
[2019-07-10 19:01] Jamiras:Updated Rich Presence (markdown)
[2019-05-24 20:01] meleu:fix link to Alt Groups doc
[2019-04-22 20:30] Kvon:minor change related to last edit