Skip to content

Fix action binding device lookup (pause / reset VR binding not working)#160

Open
chrisraff wants to merge 1 commit into
gro-ove:masterfrom
chrisraff:claude/fix-action-bindings-PqxPJ
Open

Fix action binding device lookup (pause / reset VR binding not working)#160
chrisraff wants to merge 1 commit into
gro-ove:masterfrom
chrisraff:claude/fix-action-bindings-PqxPJ

Conversation

@chrisraff
Copy link
Copy Markdown

@chrisraff chrisraff commented Apr 21, 2026

This PR fixes a bug where non-gameplay action bindings (start race, pause, reset VR, menu navigation) would stop working on subsequent launches. In my experience, repeated launches would cause wheel and button box inputs to stop registering for these actions.
I have limited experience with C#, especially for this large of a project, so I used Claude to help author the fix. I've reviewed the changes myself and validated the behavior through about a week of normal gameplay. Happy to dig into this further or work with contributors if any changes are needed.

AI summary of change:

MemoryListener.Start() was looking up joysticks by raw positional index (ElementAtOrDefault(joySlot)) into a fresh DirectInput scan. The JOY slot values in controls.ini are written based on the device enumeration order at save time, but the MemoryListener's scan happens later—after AC has started and potentially after the background DirectInputScanner has refreshed _staticData with a new enumeration order.

AC itself identifies devices by GUID via the [CONTROLLERS] __IGUID entries, so it's immune to enumeration order changes. The MemoryListener was not, causing non-gameplay action bindings (pause, start race, reset VR, etc.) to silently map to the wrong or missing joystick on repeated launches.

Fix: read __IGUID{slot} from controls.ini [CONTROLLERS] in the constructor, then in Start() resolve each JOY slot to a joystick by matching instance GUID rather than by position. Falls back to positional lookup for older configs that lack __IGUID entries.

Also tighten the usedJoysticks filter from x.Joystick != null (always true, since JoystickHolder is always constructed) to x.Joystick.Device != null, so slots that resolve to no device are excluded upfront.

MemoryListener.Start() was looking up joysticks by raw positional index
(ElementAtOrDefault(joySlot)) into a fresh DirectInput scan. The JOY slot
values in controls.ini are written based on the device enumeration order at
save time, but the MemoryListener's scan happens later—after AC has started
and potentially after the background DirectInputScanner has refreshed
_staticData with a new enumeration order.

AC itself identifies devices by GUID via the [CONTROLLERS] __IGUID entries,
so it's immune to enumeration order changes. The MemoryListener was not,
causing non-gameplay action bindings (pause, start race, reset VR, etc.) to
silently map to the wrong or missing joystick on repeated launches.

Fix: read __IGUID{slot} from controls.ini [CONTROLLERS] in the constructor,
then in Start() resolve each JOY slot to a joystick by matching instance GUID
rather than by position. Falls back to positional lookup for older configs
that lack __IGUID entries.

Also tighten the usedJoysticks filter from `x.Joystick != null` (always true,
since JoystickHolder is always constructed) to `x.Joystick.Device != null`,
so slots that resolve to no device are excluded upfront.

https://claude.ai/code/session_01WpWqqEVanpbdUffLwYd3tV
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