Skip to content

Instantly share code, notes, and snippets.

@Allavaz
Last active February 12, 2024 18:58
Show Gist options
  • Save Allavaz/23bb92ec4a69fa74f01088cb846b3029 to your computer and use it in GitHub Desktop.
Save Allavaz/23bb92ec4a69fa74f01088cb846b3029 to your computer and use it in GitHub Desktop.
Running BakkesMod on Linux with Steam Proton

This guide is outdated

Check this one out: https://github.com/CrumblyLiquid/BakkesLinux

Running BakkesMod on Linux with Steam Proton

Prerequisites

I had to install these packages along the process. I'm on Fedora 36 though, so package names may vary depending on what distro you're on:

  • protontricks
  • mingw64-gcc-c++
  • mingw64-winpthreads-static

Installing BakkesMod

Download BakkesMod from the official website.

This installer is only compatible with Windows 10, and proton prefixes are Windows 7 by default. You can get the Windows 7 version of BakkesMod or just change your Windows version like I did:

  1. Run protontricks 252950 --gui. Ignore any warnings about 64bit prefixes, everything's okay.
  2. Select the default wineprefix w10step1
  3. Change settings w10step2
  4. Tick the win10 box w10step3

Now you can run the installer like this:

protontricks -c '/home/$USER/Downloads/BakkesModSetup.exe' 252950

This should run the installer in the same wine prefix where Rocket League is installed. 252950 is Rocket League's Steam APPID.

Now you might be tempted to just run BakkesMod with protontricks, but it won't work:

vcredisterror

Installing vcredistx64 doesn't fix anything, it still gives that error (trust me, I tried). It's the injector that doesn't play well with Proton for some reason, so we'll have to make our own.

Making our own injector

First create a file named inject.cpp with this content:

#include <windows.h>

#include <tlhelp32.h>

#include <iostream>
#include <string>

#define LOG_LINE(x, msg) std::cout << msg << std::endl;

DWORD GetProcessID64(std::wstring processName)
{
  PROCESSENTRY32 processInfo;
  processInfo.dwSize = sizeof(processInfo);

  HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
  if (processesSnapshot == INVALID_HANDLE_VALUE)
    return 0;

  Process32First(processesSnapshot, &processInfo);
  if (_wcsicmp(processName.c_str(), processInfo.szExeFile) == 0)
  {

    BOOL iswow64 = FALSE;
    // https://stackoverflow.com/questions/14184137/how-can-i-determine-whether-a-process-is-32-or-64-bit
    // If IsWow64Process() reports true, the process is 32-bit running on a
    // 64-bit OS So we want it to return false (32 bit on 32 bit os, or 64 bit on
    // 64 bit OS, since we build x64 the first condition will never satisfy since
    // they can't run this exe)

    auto hProcess =
        OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInfo.th32ProcessID);
    if (hProcess == NULL)
    {
      LOG_LINE(INFO, "Error on OpenProcess to check bitness");
    }
    else
    {

      if (IsWow64Process(hProcess, &iswow64))
      {
        // LOG_LINE(INFO, "Rocket league process ID is " <<
        // processInfo.th32ProcessID << " | " << " has the WOW factor: " <<
        // iswow64);
        if (!iswow64)
        {
          CloseHandle(processesSnapshot);
          return processInfo.th32ProcessID;
        }
      }
      else
      {
        LOG_LINE(INFO, "IsWow64Process failed bruv " << GetLastError());
      }
      CloseHandle(hProcess);
    }
  }

  while (Process32Next(processesSnapshot, &processInfo))
  {
    if (_wcsicmp(processName.c_str(), processInfo.szExeFile) == 0)
    {
      BOOL iswow64 = FALSE;
      auto hProcess =
          OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInfo.th32ProcessID);
      if (hProcess == NULL)
      {
        LOG_LINE(INFO, "Error on OpenProcess to check bitness");
      }
      else
      {

        if (IsWow64Process(hProcess, &iswow64))
        {
          // LOG_LINE(INFO, "Rocket league process ID is " <<
          // processInfo.th32ProcessID << " | " << " has the WOW factor: " <<
          // iswow64);
          if (!iswow64)
          {
            CloseHandle(processesSnapshot);
            return processInfo.th32ProcessID;
          }
        }
        else
        {
          LOG_LINE(INFO, "IsWow64Process failed bruv " << GetLastError());
        }
        CloseHandle(hProcess);
      }
    }
    // CloseHandle(processesSnapshot);
  }

  CloseHandle(processesSnapshot);
  return 0;
}

int wmain(int argc, wchar_t* argv[])
{
  DWORD processID;
  while (true)
  {
    processID = GetProcessID64(L"RocketLeague.exe");
    if (processID != 0)
      break;
    Sleep(100);
  }

  HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, processID);
  if (h)
  {
    LPVOID LoadLibAddr = (LPVOID)GetProcAddress(
        GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
    auto ws = L"C:\\users\\steamuser\\Application Data\\bakkesmod\\bakkesmod/dll\\bakkesmod.dll";
    auto wslen = (std::wcslen(ws) + 1) * sizeof(WCHAR);
    LPVOID dereercomp = VirtualAllocEx(
        h, NULL, wslen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    WriteProcessMemory(h, dereercomp, ws, wslen, NULL);
    HANDLE asdc = CreateRemoteThread(
        h,
        NULL,
        NULL,
        (LPTHREAD_START_ROUTINE)LoadLibAddr,
        dereercomp,
        0,
        NULL);
    WaitForSingleObject(asdc, INFINITE);
    DWORD res = 0;
    GetExitCodeThread(asdc, &res);
    LOG_LINE(INFO, "GetExitCodeThread(): " << (int)res);
    LOG_LINE(INFO, "Last error: " << GetLastError());
    VirtualFreeEx(h, dereercomp, wslen, MEM_RELEASE);
    CloseHandle(asdc);
    CloseHandle(h);
    return res == 0;
  }
  return 1;
}

Once you have it, compile it with this command:

x86_64-w64-mingw32-g++ inject.cpp -municode -mconsole -lpsapi -std=c++17 -o inject.exe -static

It should spit out a file called inject.exe. You can test it by starting Rocket League, waiting for the game to load completely and running your shiny new injector:

protontricks -c 'wine ~/inject.exe' 252950

After a few seconds you should be able to hit F2 and see the BakkesMod GUI.

This injector should work in any Proton version, I'm using Proton 6.3-8.

Running the injector when Rocket League starts

For this trick we're gonna make a shell script that runs Rocket League, waits for it to launch completely and then runs the injector.

Create a file called bakkesinject.sh with this content:

echo "" > ~/steam-252950.log

eval 'PROTON_LOG=1 "$@"' &

while ! grep "Initializing Engine Completed" ~/steam-252950.log > /dev/null; do
    sleep 1
done

protontricks -c 'wine ~/inject.exe' 252950

Make it executable with chmod +x bakkesinject.sh and add it to the launch parameters of Rocket League like this:

launchparams

Updating BakkesMod

The injector doesn't take care of updating BakkesMod, so whenever a new version comes out, you'll have to run the good old GUI like this:

protontricks -c 'wine "/home/$USER/.steam/steam/steamapps/compatdata/252950/pfx/drive_c/Program Files/BakkesMod/BakkesMod.exe"' 252950

You can make this an alias or another bash script, like I did.

Once it finishes updating you can close it. IMPORTANT: Do not attempt to launch Rocket League while the BakkesMod GUI is running, it won't work. For some reason the game doesn't like when you launch it while something else is running in its prefix (you can run anything after the game has launched though).

References

Everything is taken from this GitHub issue. The custom injector code comes from this GitHub gist, which is also referenced in the GitHub issue. The launch script is heavily inspired on this comment from the GitHub issue, I just simplified some parts of it.

@xaetacore
Copy link

Thank you for your full answer, I am sure it will help a lot of people.

But I can still not run it. I'am not sure if that is the reason but how did you install bakkesmod in your home folder ? When I install it, I am only able to install it in the c_drive at that location: /home/luca/.var/app/com.valvesoftware.Steam/.steam/steam/steamapps/compatdata/252950/pfx/drive_c/Program Files/BakkesMod/BakkesMod.exe

On my side I was able to run BakkesMod without a problem. I just ticked win10 on the protontricks gui. But It never detects my game.

Are you using the flatpak version of steam ? I am using flatpak, but that is maybe the problem.

I'm using the steam package from the arch repo.

i mounted my home directory within protontricks, an easier work around to install BakkesMod in your home folder would be to add the setup.exe as a non steam game and setup the compatibility to use proton.

@JoaoGabrielPuhlMachado
Copy link

i'm on garuda-linux (dragonized version), i dont have rocket league on steam, just in heroic games (epic games), it's the same thing to download bakkes?

@xaetacore
Copy link

i'm on garuda-linux (dragonized version), i dont have rocket league on steam, just in heroic games (epic games), it's the same thing to download bakkes?

The method i described is for steam only,

For Heroic it works a little different

@JoaoGabrielPuhlMachado
Copy link

The method i described is for steam only,

For Heroic it works a little different

So, can you help me for Heroic? or knows anyone that can help with?

@CrumblyLiquid
Copy link

CrumblyLiquid commented Jan 13, 2024

I've written a guide that might help you a bit: https://github.com/CrumblyLiquid/BakkesLinux
The guide is primarily written for Steam but the steps should still roughly apply to Heroic. I'll update it with steps for Heroic (or other lauchers) when I get to it.

The steps outlined in this gist are out of date. You don't need a custom injector anymore. You used to have to do this but thankfully you don't have to anymore.

I was able to get Rocket League with BakkesMod running on Heroic but it's not that straight forward.

Running the BakkesModInstaller is the same as on Steam. On Heroic you can also use the Run On Prefix feature that you can find when you go to the game's specific settings to make running the BakkesModInstaller in the correct prefix easier.

After the installation it gets a bit tricky. You can't just run BakkesMod.exe with Run On Prefix again because you have to launch it in the same way that Rocket League was launched. That is if you have enabled Fsync in the game's settings you'll have to launch BakkesMod from the terminal with WINEFSYNC=1or Esync with WINEESYNC=1

So for Rocket League running with Fsync enabled you would start BakkesMod like this: WINEFSYNC=1 WINEPREFIX="<RL prefix>" <wine binary> "<RL prefix>/drive_c/Program Files/BakkesMod/BakkesMod.exe"

You'll have to replace <RL prefix> with the path you find in the game's settings (in Heroic) under Wine Prefix Folder and <wine binary> with the one you find in the game's settings under Wine Path

Once you have Rocket League open, run this command.

Chances are it will work but if you see Mod is out of date, waiting for an update you'll have to go to (in the BakkesMod window) Settings and disable Enable safe mode. It will give you a warning before injecting and if you click Yes it will (probably) successfully inject.

@JoaoGabrielPuhlMachado
Copy link

i'm trying to do the "WINEFSYNC=1 WINEPREFIX="" "/drive_c/Program Files/BakkesMod/BakkesMod.exe""

but i dont know where to find and in my computer (i'm using garuda linux)

@OmriPH
Copy link

OmriPH commented Feb 12, 2024

this just gives me GetExitCodeThread(): 0
Last error: 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment