-
Notifications
You must be signed in to change notification settings - Fork 10
Rich Presence
- Introduction
- How Does it work?
- Example
- Macros
- Lookups
- Format
- Built-in macros
- Display
- Example Lookup Breakdown
- Conditional Display Strings
- Limits
- Rich Presence Restrictions
- Tips and Tricks
- Value Properties
- Unicode Standard Symbols
- Developing Rich Presence
- Parse Errors
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, which is created by Developers. The script checks the player's game memory and 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 once every two minutes.
A good rich presence should inform other users how far into the game you are (level/ town/dungeon), and give an overall sense of how the player is doing (score/remaining lives/character level). Additional details may be useful, depending on their context within the game. Try not to go overboard with the detail though. Other players don't care what you named your dog, or need to know how many bullets you have left, especially since it only updates every two minutes. Additionally, the more fluff you add to a rich presence display, the more diluted the important information becomes.
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.
The game's Rich Presence script is downloaded with the achievement and leaderboard data for a game. As soon as the emulator validates this data, it starts a session for the user. This sets the user's 'Last Seen In' to 'Playing [game]'. 30 seconds later, the Rich Presence script will be evaluated and the result sent to the server. This will update the 'Last Seen In' for the player, which is also used in the 'Active Players' list on the front page. Every two minutes after that, the Rich Presence script will be evaluated again, and the 'Last Seen In' will be updated again. This continues until the user closes the emulator.
If there isn't a Rich Presence script for the game, the user has disabled rich presence, or the user has modified the Rich Presence script, the text will remain 'Playing [game]'.
The user's 'Last Seen In' may also say 'Fixing Achievements' or 'Developing Achievements' if they have any of the tool windows open. The first will display if achievements have been published for the set. The second if achievements have not been published for the set.
The Rich Presence script for a game can be found under the development section on each game's page:
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.
A macro converts a value into a user-friendly string. Macros are placed within a display string and will be replaced when the display string is evaluated. A macro starts with an @
, then the name of the macro, an opening parenthesis, the value to evaluate, and a closing parenthesis.
@Macro(0xh1234)
says read the byte at $1234, transform it using the Macro
macro, and place the result in the display string where the macro had been.
Parameters for each macro are constructed using a value definition.
Lookups are defined like this:
Lookup:NameOfLookup
1=Text When 1
2=Text When 2
...
When a display string references a Lookup, it's value will be located in the table and the associated text will be displayed.
NOTE: Values in the Lookup should be in decimal. If you want to use hex values, include a 0x
prefix. (i.e. 0x12=Eighteen
)
You can also specify multiple values for a single text string:
1-5,10=Text When These Values
*=Text For All Other Values
In this example, the values 1, 2, 3, 4, 5, and 10 all map to the first text string.
Anything that doesn't match an item defined in the lookup table will return the text associated with *
. If *
does not appear in the lookup table, no text will be returned if a value doesn't have an entry.
Format tables are defined like this:
Format:Score
FormatType=VALUE
Begin with Format:
, then the name of the Format converter. On the next line, give FormatType=
, then one of the following:
-
VALUE
: generic value, no leading zeroes. -
SCORE
/POINTS
: generic value, padded with leading 0s to 6 digits. -
TIME
/FRAMES
: value describes the number of frames elapsed, and will be turned into 00:00.00 by multiplying by 100 (hundredths of a second) and dividing by 60 (frames per second). If your system runs at something other than 60 fps, you'll have to do the conversion yourself and useMILLISECS
. -
MILLISECS
: value describes the number of hundredths of a second elapsed, and will be turned into 00:00.00 -
SECS
: value describes the number of seconds elapsed, and will be turned into 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 -
FLOAT1
-FLOAT6
: formats a floating point number with the specified number of digits after the decimal
As of the 1.0 version of the DLL, you can use predefined macros for the most common formats. The following macros are now implicit:
Macro | Format | Description |
---|---|---|
@Number() |
VALUE |
A generic value with no leading zeroes |
@Score() |
SCORE |
A generic value, padded with leading zeroes to six digits |
@Centiseconds() |
MILLISECS |
The number of hundreths of a second elapsed, and will be formatted as 00:00.00
|
@Seconds() |
SECS |
The number of seconds elapsed, and will be formatted as 00:00
|
@Minutes() |
MINUTES |
The number of minutes elapsed, and will be formatted as 0h00
|
@Float1() |
FLOAT1 |
A floating point number, formatted with one digit after the decimal |
@Float2() |
FLOAT2 |
A floating point number, formatted with two digits after the decimal |
@Float3() |
FLOAT3 |
A floating point number, formatted with three digits after the decimal |
@Float4() |
FLOAT4 |
A floating point number, formatted with four digits after the decimal |
@Float5() |
FLOAT5 |
A floating point number, formatted with five digits after the decimal |
@Float6() |
FLOAT6 |
A floating point number, formatted with six digits after the decimal |
@ASCIIChar() |
n/a | Converts a value from 0x20-0x7F into a character using the ASCII character map. Other values will be converted to ?
|
@UnicodeChar() |
n/a | Converts a value into a character using the 16-bit Unicode character map. Unknown values will be converted to �
|
Display is a string that gets shown in the 'Active Players' box on the front page and the 'Last Seen In' section of the player's profile.
It is built by replacing any macros in the display string with text from a Lookup or formatted values from a Format converter. Each macro is identified by a single '@', which is followed by the name for the Lookup or Format (case sensitive!), and immediately after, in parenthesis, a value specifying what to send to that Lookup or Format object.
Using @Powerup(0xh756)!
This means use the Lookup or Format that's called Powerup
, and give it whatever 8-bit value is in the address 0x756. After converting, put the result in between "Using " and "!".
NOTE: Lookup/Format names are case sensitive and must exactly match the usage in the Display string: @test(0x1234)
will not find Format:Test
NOTE: Lookup/Format definitions cannot contain spaces before or after the name. @test(0x1234)
will not find Format:test
or Format: test
-
@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 Mario is Small, big or has fire power. -
Mario in
- Static text to string lookup and format objects together. -
@Digit(0xh75f_v1)
-Digit
is a format object defined as a value. The address 0xh75f is the World minus 1 (because it is 0 based, as in it starts counting at 0)._v1
Means + value 1._v+1
is also correct. -
-
- More static text to split World and Level. as in the hyphen in World 1-1. -
@Digit(0xh75c_v1)
- Another use of theDigit
format 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 theDigit
format 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.
Display:
?0x 000085=0?Title Screen
?0xT00007c=1?Custom Map in @Landscape(0xH00016c)
Playing Battle @Battle(0x 00007c*0.2) in @Landscape(0xH00016c)
The existing 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.
-
?0xH1234=32_0xH2345=0?and example
if the 8-bit value at $1234 is 32 and the 8-bit value at $2345 is 0, display
and example
-
?0xH1234=32S0xH2345=1S0xH2345=2?or example
if the 8-bit value at $1234 is 32 and the 8-bit value at $2345 is 1 or 2, display
or example
-
?0xH1234=32_0xH5678=33S0xH2345=1S0xH2345=2?and/or example
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
and/or example
Pro-tip: Conditions can be created using the achievement editor. Once you have your condition defined, use the Copy Def
button to copy the achievement definition to the clipboard so you can paste it into the rich presence script.
- 65,535 character limit for script
- 255 character limit for what is displayed
- Unicode characters are allowed, but use count as more than 1 character towards the specified limits.
Rich Presence that directly displays custom player input text is prohibited. The most common example being displaying what a player inputs as their character or file name directly into Rich Presence.
This restriction helps in preventing inappropriate or offensive content from showing on various site pages as well as makes the moderation of Rich Presence more manageable, in addition to protection player privacy as players may not realize their text is being displayed publicly.
- 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. - 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 (
0xh0001
can be shortened to0xh1
). - 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.
- Each
Lookup
orFormat
named mapping can be referenced multiple times with the same or different addresses. You can define a singleFormat:Number FormatType=VALUE
instead 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
@Pause
,@Star
,@Swimming
, and @Mode do.
When using lookup and format objects @object()
it's possible to combine and perform calculations. Macro parameters are value definitions, so you can use multiplication, addition, and even some logic to generate values that aren't directly available in memory.
Example
@Score(0xh28*10_0xh29*1000_0xh26*100000) points
This means use the Lookup or Format Score
, and give it the sum of:
- the 8-bit value at 0x28 times 10, ADD
- the 8-bit value at 0x29 times 1000, ADD
- the 8-bit value at 0x26 times 100000
You can without doubt use these symbols in rich presence.
▌▌=Paused
🔁=Continues
⏰=In Game Time/Game Clock
🔑=Keys
💣=Bombs
❤️ or ❤=In a game with hearts (e.g. Zelda)
💰=Money
🚩=Level/Stage
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:
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.
NOTE: The 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.
Rich Presence files utilize Condition Syntax for Memory Sizes, Prefixes and Logical Flags. These are also represented if you copy and paste achievement logic from the Achievement Editor.
Code | Enum | Description |
---|---|---|
-2 | INVALID_MEMORY_OPERAND |
A memory operand was expected and not found. Memory operands start with 0x , then a size indicator. The most common causes of this are forgetting the 0x or having an _S or __ in the script. |
-3 | INVALID_CONST_OPERAND |
A lookup key could not be evaluated. The most common cause of this is using hex without the 0x prefix. This may also occur on older versions of RetroArch when using CSV or range keys for lookups. |
-6 | INVALID_OPERATOR |
An unknown operator was encountered. The most common cause of this is using ! instead of !=
|
-16 | MISSING_VALUE |
A macro was encountered without providing a value (i.e. @Points instead of @Points(0xh1234) ) |
-18 | MISSING_DISPLAY_STRING |
The rich presence script did not contain a Display: element, or contained only conditional display strings |
-20 | RC_INVALID_VALUE_FLAG |
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 [Unknown macro]Points(0xh1234)
.
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.
- User Guidelines
- Developer Guidelines
- Content Guidelines
- FAQ
- Setup Guide
- Emulator Support and Issues
- Ways to Contribute
- RABot, the RA Discord Robot
- Events
- Overlay Themes
- Useful Links
- Contributing with the docs
- About Us
- Tutorials
- Developer Docs
- How to Become an Achievement Developer
- Getting Started as an Achievement Developer
- Game Identification
- Achievement Design
- Achievement Scoring
- Difficulty Scale and Balance
- Progression and Win Condition Typing
- Badge and Icon Creation
- Achievement Development Overview
- Flags
- BitCount Size
- Alt Groups
- Hit Counts
- Delta Values
- Prior Values
- Value Definition
- Condition Syntax
- Minimum Required Versions for Logic Features
- Memory Inspector
- Real Examples
- Set Development Roadmap
- Achievement Templates
- Tips and Tricks
- Leaderboards
- Rich Presence
- RATools
- Console Specific Tips
- Emulator Hotkeys for Developers
- libretro core support
- Docs To Do List
- WIP User Code of Conduct
- WIP CoC FAQ
- WIP Content Guidelines
- WIP-Jr
- WIP---Dev-Tips---Code-Notes-En-Masse
- WIP-‐-Reauthorship-Policy
- Manifesto RetroAchievements
- Código de Conduta do Usuário
- FAQ - Perguntas Frequentes
- Como contribuir se você não é um desenvolvedor
- Tutorial para Jogos Multi-Discos
- Introdução
- Primeiros Passos como um Desenvolvedor de Conquistas
- Recursos de Lógica para Achievements
- Exemplos Reais
- Dicas e Truques
- Dicas Específicas de Console
- Modelos de Achievement
- Escala de Dificuldade e Equilíbrio
- Roteiro de Desenvolvimento de um Set de Conquistas
- Criação de Ícones e Emblemas
- Leaderboards
- Rich Presence
- Design de Conquistas
- Manifesto RetroAchievements
- Código de Conducta del Usuario
- FAQ - Preguntas Frecuentes
- Tablas Globales y Reglas para la Casería de Logros
- Mi juego no esta cargando los logros
- Como contribuir si no eres un desarrollador
- Por que no deberías utilizar la función de cargar estado
- Contribuyendo con los documentos
- Como funciona la Documentación de RA
- Descargas
- Intro
- Código de Conducta del Desarrollador
- Como convertirme en un Desarrollador de Logros
- Primeros pasos como un Desarrollador de Logros
- Un vistazo al Inspector de Memoria
- Características en la Logica de un Logro
- Ejemplos Reales
- Intro
- Utilizando Hit Counts como un Temporizador
- Utilizando Valores Delta y Hit Counts para Detectar un Incremento
- Un Ejemplo Simple en como evitar el Abuso de Estados de Guardado
- Evitar el Problema de que un Contador se Incremente Dos Veces en el Mismo Frame
- Creando un Temporizador con un ResetIf Hits basándote en la Velocidad de un Juego
- Plantillas para Logros
- Tips y Trucos
- Escala de Dificultad y Balance
- Diseño de Logros
- Mapa de Desarrollo de Set
- Revisiones en Set de Logros
- Creación de Iconos y Badges
- Tablas de Clasificación
- Rich Presence
- Trabajando con el ROM apropiado
- Identificación del Juego
- Guía para Sets Bonus
- Logros para ROM hacks
- Tips Específicos por Consola