Skip to content

RetroAchievements - On Screen Challenge/Progress Indicators - #155

Open
sunlollyking wants to merge 1 commit into
garbear:masterfrom
sunlollyking:achievement-progress-indicator
Open

RetroAchievements - On Screen Challenge/Progress Indicators#155
sunlollyking wants to merge 1 commit into
garbear:masterfrom
sunlollyking:achievement-progress-indicator

Conversation

@sunlollyking

@sunlollyking sunlollyking commented Sep 1, 2026

Copy link
Copy Markdown

Description

Two things are worth a corner of the screen while a game is running: the achievement the player is inside an attempt at, and how far along a counted one is. "Collect 180 rings" is no use as a number you have to open a dialog to read.

The achievement runtime already decides both, and says when to take them away again. The add-on receives those decisions and until now used them only as a hint to republish the whole measured set, so the choice was discarded. It now forwards them, and Estuary draws them in the corner.

Pairs with kodi-game/game.libretro#177, which is the add-on half.

Motivation and context

Kodi could pick an achievement itself — the measured set already arrives, and the one closest to completion is a fair guess. What it cannot know is when to stop: nothing in that set says the player has moved on, so a guess would leave the last thing it chose on screen for the rest of the session. The set keeps its own callback and its own job, every measured achievement so their progress can be listed, while these two are one achievement each and only while it should be seen.

Both are held as sets rather than single slots. A game counting two things at once announces each separately, a few milliseconds apart, so a single slot is handed back and forth between them faster than either can be read — visible as a flicker in the corner. The measured indicator shows whichever is closest to being earned.

They are drawn by a modeless dialog rather than by controls in the fullscreen window. Where the game has its own plane, CGameWindowFullScreen stops marking itself dirty each frame and CApplication skips compositing the GUI layer while nothing else dirties it, so a control there becoming visible never reaches the screen — which is why notifications have always shown over games and controls beside them have not. A dialog does reach it, and marks itself dirty while up; it is only up while there is something to show, so the rest of the session keeps the saving that optimisation exists for. Modeless because the player is still playing and must keep their input.

Game API

Goes to 8.1.0. Both callbacks are appended, so the minimum stays at 8.0.0: add-ons built against it keep loading, and one that sends neither indicator simply shows nothing.

How has this been tested?

On GBM/Wayland GLES with Balloon Fight (fceumm) and Sonic the Hedgehog 2 (genplus), logged into RetroAchievements.

result
Progress indicator "Ring Wrangler I 5/226" on Sonic, "Trip Pop Pro 4%" on Balloon Fight
Challenge indicator shown while inside an attempt, cleared on leaving it
Two counting at once Balloon Fight counts two; the closer one holds the corner without flicker
Setting off corner clears at once, mid-attempt
image image

11 unit tests in TestGamesGUIInfo, including one that announces two achievements in the order that would show the wrong one if the latest simply replaced the last, then stops the leading one and checks the other takes over.

What is the effect on users?

A game client that sends the indicators shows them in the corner while playing, and the setting under RetroAchievements turns them off. Nothing changes for a client that does not send them.

Types of change

  • Bug fix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves existing functionality)
  • New feature (non-breaking change which adds functionality)
  • Breaking change
  • Cosmetic

Checklist

  • My code follows the Code Guidelines of this project
  • My change requires a change to the documentation — the Game API goes to 8.1.0
  • I have read the Contributing document

@sunlollyking
sunlollyking force-pushed the achievement-progress-indicator branch from f50cbad to d52af20 Compare September 1, 2026 12:42
@sunlollyking sunlollyking changed the title Games: Show the achievements being played for, while playing RetroAchievements - On Screen Challenge/Progress Indicators Sep 1, 2026
@sunlollyking
sunlollyking force-pushed the achievement-progress-indicator branch from d52af20 to d1055b3 Compare September 1, 2026 13:10
Two things are worth a corner of the screen while a game is running: the
achievement the player is inside an attempt at, and how far along a counted
one is -- "collect 180 rings" is no use as a number you have to open a
dialog to read.

The achievement runtime already decides both, and says when to take them
away again. The add-on receives those decisions and, until now, used them
only as a hint to republish the whole measured set, so the choice was
thrown away. It now forwards them as RCOnChallengeIndicator and
RCOnProgressIndicator, with a null payload meaning nothing should be shown.

Kodi could choose an achievement itself, since the measured set is already
here and the one closest to completion is a fair guess. What it cannot know
is when to stop: nothing in the set says the player has moved on, so a
guess would leave the last thing it chose on screen for the rest of the
session. The set keeps its own callback and its own job -- every measured
achievement, so their progress can be listed -- while these two are one
achievement each, and only while it should be seen.

Both are held as sets rather than single slots. A game counting two things
at once announces each separately, a few milliseconds apart, so one slot
would be handed back and forth between them faster than either could be
read. The measured indicator shows whichever is closest to being earned.

They are drawn by a modeless dialog rather than by controls in the
fullscreen window. Where the game has its own plane, CGameWindowFullScreen
stops marking itself dirty each frame and CApplication skips compositing
the GUI layer while nothing else dirties it, so a control there becoming
visible never reaches the screen. A dialog does, and marks itself dirty
while it is up; it is only up while there is something to show, so the rest
of the session keeps the saving that optimisation exists for. Modeless
because the player is still playing and must keep their input. It closes
itself from Process(), on the thread that can act at once rather than
posting a close and then testing whether the post has been served.

A setting under RetroAchievements turns them off. Answered at display time
rather than never recorded, so switching it off during an attempt clears
the screen at once and the achievements dialog still knows what is being
attempted.

The Game API goes to 8.1.0, and the minimum with it. Both callbacks are
ones the add-on calls rather than ones it answers, so appending them buys
nothing in the direction the minimum governs: an add-on built against 8.1.0
reads two entries past the end of the table an 8.0.0 frontend hands it, and
calls whatever follows. Every earlier bump that changed the table moved the
minimum alongside it.

Ending an attempt is the one thing the C API cannot say precisely: a null
payload is the hide signal, so it carries no achievement and the runtime
can only clear every attempt it holds. The measured path already did this;
the challenge path did not, and left the indicator on screen for the rest
of the game.

The dialog answers the setting too, not just the labels. Otherwise turning
the feature off still opened it and still marked it dirty every frame, for
a feature drawing nothing -- the exact cost it exists to avoid.

An indicator arriving during the close animation cannot reopen the dialog
from the game thread, because the window manager will not activate one that
is still running; it would finish closing with something to show, and a
challenge that produces no further event would stay hidden for the rest of
the attempt. Process() runs on the GUI thread and reopens it there.
@sunlollyking
sunlollyking force-pushed the achievement-progress-indicator branch from d1055b3 to 046efd4 Compare September 2, 2026 09:29
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.

1 participant