DualShock 4 Unstable Unity Fix

On last post DualShock 4 Unstable on Unity Eiyuden Chronicle: Rising, I fixed the issue with modifying one of binary/assembly files using dnSpy. Next step, lets create non-obstructive solution by using patches/mods and being scallable to other games.

There will be two method to achieve this:

  • The patcher or mods to alter original behaviour.
  • The injector to attach the mods.

For Unity with C#/Mono, The one of among populars to patch is using Harmony by  Andreas Pardeike and to inject is using BepInEx.

TL;DR; Just grab this UnityInputManagerFilter.

Tutorial:

Based on previous post, I only need to avoid the noissy input being registered by AddDevice method. We can utilize the Prefix hoox to AddDevice, filter the device that being provided and return false to reject or true to accept. By returning false on Prefix hook, the original method won’t be called (skipped).

Since the AddDevice method is defined inside internal class UnityEngine.InputSystem.InputManager, it is impossible to use Harmony decorator. Alternatively using reflector via AccessTools is possible and patching with manual-patching.

Get the original method with: AccessTools.Method(AccessTools.TypeByName("UnityEngine.InputSystem.InputManager"), "AddDevice", new[] { typeof(UnityEngine.InputSystem.InputDevice) });

Notice here:

  • AccessTools.TypeByName will return internal class, since It cannot be hinted or referenced from UnityEngine.InputSystem.
  • The AddDevice is overloaded function (it has more than one signature), the exact parameter list will be required.

Create the Prefix hook function/method with:

static bool PrefixAddDevice(object __instance, UnityEngine.InputSystem.InputDevice device)

Notice here:

  • The prefix hook return type is bool not void, because we want to control wether the original AddDevice being called after.
  • The object __instance may be omitted, but here the type is object since it is internal class (cannot be hinted). Later on we could access the instance’s methods using dynamic. (See: Harmony patching-injections)

Then register the prefix hool with: harmony.Patch(original, new HarmonyMethod(prefixPatch));

DualShock 4 Unstable on Unity Eiyuden Chronicle: Rising

When I turn on my Dual Shock 4 Bluetooth and playing Eiyuden Chrnoicle: Rising, the input brutally being uncontrollable spamming wrong input events and make the game being impossible to play. After futher investigation there were cuase of DS4Windows virtual controller.

I’ve found 3 cases how to use it:

  • Case 1: Without DS4Windows service being run, turn on the Dual Shock 4.
  • Case 2A: With DS4Windows as Xbox need patch to Unity.InputSystem
  • Case 2B: With DS4Windows as Dual Shock 4, need patch to Unity.InputSystem

Case 1

The Dual Shock 4 Bluetooth on Windows 10 will be known as Wireless Controller, and from Control Panel we can see it works normally also in the game.

Case 2

DS4Windows registers new Virtual Controller but leave the original to exist. The original become untestable in Gamepad Control Panel and it send noisy input events to the Unity games.

Quick Patch with dnSpyEx

If the Unity Game compiled with C# then it is possible to patch using dnSpyEx. Fortunately Eiden Chronicle: Rising using C#. There were two possible locations to fix the input problems:

  • Assembly-CSharp
  • Unity.InputSystem

The Assembly-CSharp contains the developer codes and very specific for each games. Found nnPad::InputMgr for handle input events but the codes here seems filled by callback functions. The worst thing is it didn’t provided by which gamepad device the event comes from. It is impossible to distinguish the noisy device. Need to go further deep down to the who call this callback.

If we set breakpoint we know that caller which is Unity.InputSystem. To enable breakpoint see Debugging-Unity-Games. At first we need to get the Unity version. The BepInEx plugin manager will show the Unity version thru its logs. To debug the game thru dnSpyEx, replace <root>\<GAME>_Data\MonoBleedingEdge\EmbedRuntime\mono-2.0-bdwgc.dll. Download or build it from dnSpy-Unity-mono-unity2021.xx. For easy debugging with Debug.LogErrorFormat function, follow the “Turning a release build into a debug build”. Additionally there are great BepInEx plugins for futher mods: RuntimeUnityEditor

Since it whitelist all gamepad inputs, the strategy is to disable DualShock4GamepadHID during device register (some times it detected as DualShock3GamepadHID). We still can use the gamepad since DS4Windows register it as XInputControllerWindows. So edit the UnityEngine.InputSystem.InputManager AddDevice(InputDevice device) to skip this device being registered. After changes don’t forget to do Save Module and restart the game to apply the changes. The DualShock4 via Xbox (DS4Windows) should be normal works.

bool flag = true;
// sometimes it detect as DS3
if (device.name == "DualShock3GamepadHID") {
   flag = false;
}
if (device.name == "DualShock4GamepadHID") {
  // check if DS4 is emulated by DS4Windows
  if (device.description.manufacturer != "Sony Computer Entertainment"))
  {
	flag = false;
  }
}

By the default DS4Windows will set Profile to enumlate as Xbox 360. But we could change it into DualShock4.

To distinguish the nosiy gamepad with the emulated one, we could check the manufacturer information. The emulated one using:

Sony Computer Entertainment

in contrast with the noisy or original one:

Sony Interactive Entertainment

It might be incorrect and tricky depends on the type and the production of DualShock4. For futher information: Sony Computer Entertainment Changes Its Name Today