WhiteMagic / JoystickGremlin

A tool for configuring and managing joystick devices.
http://whitemagic.github.io/JoystickGremlin/
GNU General Public License v3.0
321 stars 46 forks source link

Nice-to-haves to enable force feedback bridging #566

Open code-monet opened 1 week ago

code-monet commented 1 week ago

Continuation of discussion at the Dill issues page, moving here for relevance.

I have a prototype force feedback bridge that works with the current release (R13) of Joystick Gremlin, via user plugins. The user plugin loads my library, which will be available as a precompiled Python extension. The user is offered an output device selection via SelectionVariable.

I'm still working on finishing touches before making it widely available; meanwhile here are a few things that would be nice-to-have in the next release so that it keeps working, and can be made to work with less hackery:

  1. Update vJoy DLL to the latest from BrunnerInnovation's fork. (Current workaround is to just replace the DLL in the Gremlin dir with the 32-bit DLL from their release). Also update docs to tell users to install that version of the vJoy driver, it has FFB fixes.
  2. Implement _process_registry_value in SelectionVariable. I currently patch this function on that class.
  3. Have a nicer decorator than gremlin.input_devices.periodic for my callback that passes the selection value to the library to set as output device. Ideally the decorated function would be called on value selection only, but using periodic isn't that different so this is low priority. The periodic decorator does seem to be broken as well (for me it doesn't fire on the first "Activate" but does fire on subsequent activations).

The whole thing relies on a few things which I'm capturing below:

  1. Joystick Gremlin will acquire the vJoy device (my extension does not, and no force feedback events will be handled if the vJoy device is not acquired).
  2. The plugins system continues to allow importing modules.
  3. Plugins continue to be run in the same process that acquires the vjoy device.
  4. HidHide is basically essential for FFB forwarding so that the game doesn't try to take over the output device with an exclusive acquire (required for FFB).

Looking forward to your next release!

WhiteMagic commented 1 week ago
  1. I'm very hesitant on that front, the vJoy version that this is based on (njz3) is known to cause grief and I would need to know what it actually does to warrant moving from the known to work version.
  2. Yeah that seems like a general bug that needs fixing, though that entire system will change with R14 in an unknown way.
  3. Added that to the R14 list to finally find out what broke that thing.

I'm still really unclear how/what you intend this to work, I get forwarding FFB but not the actual mechanism so it's hard to actually say if any of that can/will work with Gremlin.

code-monet commented 11 hours ago
  1. Let me get back to you on that one. I haven't experienced any issues, and looking around briefly here and on Reddit, I think those issues have been fixed (Windows 11) or mitigated (warn about >8 axes). I believe a lot of FF handing aspects were fixed in that fork, but I didn't get into the details; I can take a closer look.
  2. SG
  3. Thanks! Meanwhile I'll bind a physical input to force feedback enable/disable, seems like a good safety feature as well.

How it works:

  1. Enable force feedback effects on vJoy via the config app
  2. Add my plugin to the Joystick Gremlin profile in which FF forwarding is desired. Bind physical button for FF on/off
  3. Use the button at any time to enable/disable FF forwarding.

Internals: A vJoy device with FF enabled will by default simply drop all force feedback commands, while returning a success state at the driver level. The vJoy interface offers a function: FfbRegisterGenCB which you use to register a callback function that handles all FF commands for all vJoy devices. This callback is called in a separate thread created by vJoy. Once you acquire the vJoy device via AcquireVJD, FF handling is set in motion.

So I have a (C++) implementation of this function, which keeps track of FF state and forwards a single vJoy device's incoming FF commands to a single DirectInput FF capable device, which needs to be acquired in (DirectInput) exclusive mode. The whole thing is compiled into a DLL and a precompiled Python module; for Joystick Gremlin, the latter is more suitable. The plugin mechanism is used to import and register the callback function in the same process that acquires the vJoy device; if you ever isolate the plugins into their own process, a different hook will be needed.

Note re: acquiring the device via DirectInput: The same process can acquire the device multiple times in exclusive or inclusive mode so this isn't a conflict at all with how Joystick Gremlin acquires the device. I've also implemented my plugin to not reacquire the device if Joystick Gremlin releases it, which would turn off physical input forwarding as well as FF forwarding at the same time.

Note re: running a precompiled Python module via plugins - if the module has a crash at the C++ level (and I have found more than one segfault bug in DirectInput, sigh), Joystick Gremlin will be taken down with it. So it's my responsibility to ensure I find these bugs and take all steps to NOT hit them :) I'm currently testing out the plugin with R13.3 and a few different games and have to polish up the plugin interface before it can be released.

The idea at a high level is to use Joystick Gremlin to improve compatibility between games and wheels that either are unplayable or give crappy FF effects, for example:

  1. Use Joystick Gremlin to combine accelerator and brake, while also getting force feedback via the plugin (older games that assume combined pedals, like Need for Speed Most Wanted OG).
  2. Deal with game bugs such as low FPS in Crew series with certain wheels, or crash to desktop in Crew Motorfest.
  3. Certain wheels don't implement effects correctly - my plugin will eventually implement these host-side.