Запуск игр Steam в Proton с поддержкой Native Wayland

Proton Wayland

Иллюстрации к статье подготовлены нейросетью freepik.com.

В первой части нашей статьи, мы научились собирать Wine с поддержкой Wayland и запустили игру Overwatch 2. Но для большинства геймеров конечно же больший интерес представляет запуск игр Steam, и этот вопрос мы не пройдем стороной. Преимущество нашей сборки Proton будет ещё и в том, что она будет работать напрямую с системными библиотеками и оборудованием, когда обычный Proton и GE Proton Custom загружаются в виртуальном контейнере, подгружают свои библиотеки и драйвера. Тем самым мы получим чуть лучшую производительность и избавимся от еще одного ненужного слоя.

Если вас заинтересовала статья, то добро пожаловать под cut.

Мы не будем полностью копировать работу Proton, так как сам по себе Proton является не стабильным, содержит большое количество ошибок из-за множества грязных хаков в коде Wine. Эти хаки не принимают в основной код Wine и не редко они написаны очень безалаберно, хаотично и где попало в коде. Большинство этих хаков избыточны, не нужны большинству игр и добавлены лишь для запуска каких-то специфичных старых игр в Steam.

Протонируем Wine

Первым делом нам необходимо создать дерево каталогов для нашего Proton и скопировать Wine. В большинстве систем Steam хранит свои файлы в ${HOME}/.steam/root, но у вас может не быть этой директории, а может быть к примеру ${HOME}/.local/share/Steam, или какая-то другая директория, поэтому замените STEAM_DIR на ваш путь к директории Steam в bash скриптах ниже.

По умолчанию манифест для запуска Proton выглядит так:

"manifest"
{
  "version" "2"
  "commandline" "/proton %verb%"
  "require_tool_appid" "1628350"
  "use_sessions" "1"
  "compatmanager_layer_name" "proton"
}

Файл манифеста определяет, какие команды будет выполнять Steam для запуска Proton. Проблема этого манифеста заключается в том, что если манифест содержит поля require_tool_appid и use_sessions, то Proton запустится в виртуальном контейнере с библиотеками Steam. Минус таких библиотек в том, что они собраны только с поддержкой X11 и ничего не знают про Wayland. Таким образом наш манифест должен прийти к виду ниже, что заставит Steam использовать системные библиотеки нашей ОС.

"manifest"
{
  "version" "2"
  "commandline" "/proton %verb%"
}

Так же хочется отметить, что основной python скрипт — proton, является не стандартным, и был адаптирован мной для создания своего Proton с поддержкой Wayland, плюс добавлена опция запуска Wine с приоритетом реального времени. Отличия вы сможете найти, сравнив файлы с помощью утилиты diff. Оригинальный скрипт отыщется по ссылке proton.

Выполним скрипт:

#!/usr/bin/bash

# Имя нашей Proton сборки.
PROTON_NAME="ProtonWay-9.2"
# Директория в которой хранятся файлы Steam.
STEAM_DIR="${HOME:?}/.steam/root"
# Директория в которой хранятся исходные коды.
SOURCE_DIR="${HOME:?}/code"
# Директория в которой установлен наш собранный Wine.
WINE_DIR="${HOME:?}/wine"

# Если возникает ошибка, то прекращаем выполнение дальнейших команд.
set -euo pipefail

# Создаем директорию для нашего Proton, если она не существует.
if [ ! -d "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}" ]; then
    mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}"
fi

# Создаем директорию files где будут файлы нашего Wine.
if [ ! -d "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files" ]; then
    mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files"
fi

# Копируем файлы нашего Wine.
cp -rv "${WINE_DIR:?}/bin" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/bin"
cp -rv "${WINE_DIR:?}/lib" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib"
cp -rv "${WINE_DIR:?}/lib64" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64"
cp -rv "${WINE_DIR:?}/share" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/share"

# Генерируем файл версии unix time + уникальное имя.
echo "$(date +%s) ${PROTON_NAME:?}" > "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/version"

# Создаем манифест.
cat << EOF > "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/toolmanifest.vdf"
"manifest"
{
  "version" "2"
  "commandline" "/proton %verb%"
}

EOF

# Создаем файл совместимости.
cat << EOF > "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/compatibilitytool.vdf"
"compatibilitytools"
{
  "compat_tools"
  {
    "###PROTON_NAME###"
    {
      "install_path" "."
      "display_name" "###PROTON_NAME###"
      "from_oslist"  "windows"
      "to_oslist"    "linux"
    }
  }
}

EOF

sed -i "s/###PROTON_NAME###/${PROTON_NAME:?}/g" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/compatibilitytool.vdf"

# Копируем python скрипты Proton и меняем имя сборки.
cp -vf "${SOURCE_DIR:?}/wine-wayland/proton/proton" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/proton"
cp -vf "${SOURCE_DIR:?}/wine-wayland/proton/settings.py" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/settings.py"
cp -vf "${SOURCE_DIR:?}/wine-wayland/proton/filelock.py" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/filelock.py"
sed -i "s/###PROTON_NAME###/${PROTON_NAME:?}/g" "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/proton"
chmod +x "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/proton"

# Создадим директории где будем хранить наши dxvk, vkd3d-proton, nvapi.
mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/dxvk"
mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/vkd3d-proton"
mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/nvapi"

mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/dxvk"
mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/vkd3d-proton"
mkdir -p "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/nvapi"

Установка dxvk, vkd3d-proton, nvapi

В первой части мы скачали и распаковали нужные файлы, поэтому скопируем файлы dxvk, vkd3d-proton, nvapi в соответствующие директории. Если у вас нет видеокарты Nvidia, то можно пропустить копирование файлов nvapi.dll, nvapi64.dll.

# Имя нашей Proton сборки.
PROTON_NAME="ProtonWay-9.2"
# Директория в которой хранятся файлы Steam.
STEAM_DIR="${HOME:?}/.steam/root"
# Директория в которой хранятся исходные коды.
SOURCE_DIR="${HOME:?}/code"

# Скопируем dxvk.
cp -vf "${SOURCE_DIR:?}/dxvk-2.3/x64/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/dxvk/"
cp -vf "${SOURCE_DIR:?}/dxvk-2.3/x32/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/dxvk/"
# Скопируем vkd3d-proton.
cp -vf "${SOURCE_DIR:?}/vkd3d-proton-2.11.1/x64/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/vkd3d-proton/"
cp -vf "${SOURCE_DIR:?}/vkd3d-proton-2.11.1/x86/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/vkd3d-proton/"

# Скопируем dxvk-nvapi.
cp -vf "${SOURCE_DIR:?}/dxvk-nvapi-v0.6.4/x64/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib64/wine/nvapi"
cp -vf "${SOURCE_DIR:?}/dxvk-nvapi-v0.6.4/x32/"*.dll "${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}/files/lib/wine/nvapi"

Установка шрифтов

Чтобы Proton мог автоматически устанавливать шрифты при установке игр, нам необходимо скопировать шрифты в директорию fonts в files/share нашего Proton. Если мы не создадим эту директорию со шрифтами, то наш Proton будет зависать при запуске. Такова специфика работы скриптов Proton. Самое простое — это взять шрифты из GE Proton Custom, но мы не ищем легких путей и поэтому сгенерируем их самостоятельно. Да-да, здесь нет ошибки, мы именно будем генерировать шрифты на основе открытых шрифтов: Liberation Sans, Liberation Serif, Liberation Mono, Noto Sans, изменяя метаданные исходных шрифтов. Данный метод не дает возможности получить оригинальные Windows шрифты, и лишь заменяет их переименованными открытыми. Это позволяет обойти проблему с авторскими правами на шрифты, и таким методом генерации пользуется компания Valve в Proton. В своей работе мы пропустим генерацию специфичных шрифтов для китайского, японского, индийского, арабского языков, который генерирует Proton, но зато добавим дополнительные начертания для основных русско-английских шрифтов, такие как: жирный, курсив, жирный-курсив. Proton их не все генерирует и часто ограничен только основным Regular шрифтом. Наши шрифты при этом будут весить в 10 раз меньше чем в Proton, что даст более быструю их загрузку.

Для генерации шрифтов нам необходимо установить пакет fontforge и python модуль fonttools. В Fedora Linux это можно сделать командами:

sudo dnf install fontforge
pip install fonttools

Выполним скрипт:

#!/usr/bin/bash

# Имя нашей Proton сборки.
PROTON_NAME="ProtonWay-9.2"
# Директория в которой хранятся файлы Steam.
STEAM_DIR="${HOME:?}/.steam/root"
# Директория в которой хранятся исходные коды.
SOURCE_DIR="${HOME:?}/code"
# Директория в которой будет происходить сборка шрифтов.
BUILD_DIR="/tmp/build-fonts"
# Директория в которой находится Proton, в которую мы установим шрифты.
INSTALL_DIR="${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}"

FONTS_DIR="${SOURCE_DIR:?}/proton/fonts"
LIBERATION_SRCDIR="${FONTS_DIR:?}/liberation-fonts/src"
NOTO_SANS_SRCDIR="${FONTS_DIR:?}/noto"

# Если возникает ошибка, то прекращаем выполнение дальнейших команд.
set -euo pipefail

# Создаем директорию в которой будет проходить сборка, если существует удаляем и создаем заново.
rm -fr "${BUILD_DIR:?}"
mkdir -p "${BUILD_DIR:?}"

# Генерируем основные шрифты: Arial, Times New Roman, Georgia, Courier New
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSans-Regular.sfd" "Arial" "Arial" "Arial" "${BUILD_DIR:?}/arial.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSans-Bold.sfd" "Arial-Bold" "Arial" "Arial Bold" "${BUILD_DIR:?}/arialbd.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSans-Italic.sfd" "Arial-Italic" "Arial" "Arial Italic" "${BUILD_DIR:?}/arialit.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSans-BoldItalic.sfd" "Arial-BoldItalic" "Arial" "Arial Bold Italic" "${BUILD_DIR:?}/arialbdit.ttf"

fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Regular.sfd" "TimesNewRoman" "Times New Roman" "Times New Roman" "${BUILD_DIR:?}/times.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Bold.sfd" "TimesNewRoman-Bold" "Times New Roman" "Times New Roman Bold" "${BUILD_DIR:?}/timesbd.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Italic.sfd" "TimesNewRoman-Italic" "Times New Roman" "Times New Roman Italic" "${BUILD_DIR:?}/timesit.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-BoldItalic.sfd" "TimesNewRoman-BoldItalic" "Times New Roman" "Times New Roman Bold Italic" "${BUILD_DIR:?}/timesbdit.ttf"

fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Regular.sfd" "Georgia" "Georgia" "Georgia" "${BUILD_DIR:?}/georgia.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Bold.sfd" "Georgia-Bold" "Georgia" "Georgia Bold" "${BUILD_DIR:?}/georgiabd.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-Italic.sfd" "Georgia-Italic" "Georgia" "Georgia Italic" "${BUILD_DIR:?}/georgiait.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationSerif-BoldItalic.sfd" "Georgia-BoldItalic" "Georgia" "Georgia Bold Italic" "${BUILD_DIR:?}/georgiabdit.ttf"

patch -Np1 -d "${LIBERATION_SRCDIR:?}" -i "${FONTS_DIR:?}/patches/LiberationMono-Regular.patch" -o "${BUILD_DIR:?}/LiberationMono-Regular.sfd"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${BUILD_DIR:?}/LiberationMono-Regular.sfd" "CourierNew" "Courier New" "Courier New" "${BUILD_DIR:?}/cour.ttf"
rm "${BUILD_DIR:?}/LiberationMono-Regular.sfd"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationMono-Bold.sfd" "CourierNew-Bold" "Courier New" "Courier New Bold" "${BUILD_DIR:?}/courbd.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationMono-Italic.sfd" "CourierNew-Italic" "Courier New" "Courier New Italic" "${BUILD_DIR:?}/courit.ttf"
fontforge -quiet -script "${FONTS_DIR:?}/scripts/generatefont.pe" \
    "${LIBERATION_SRCDIR:?}/LiberationMono-BoldItalic.sfd" "CourierNew-BoldItalic" "Courier New" "Courier New Bold Italic" "${BUILD_DIR:?}/courbdit.ttf"

# Копируем скрипт объединения шрифтов семейства Noto Sans в один файл, и включаем игнорирование отсутствующих unicode символов в шрифтах.
cp -v "${FONTS_DIR:?}/scripts/merge.py" "${BUILD_DIR:?}/merge.py"
sed -i "s/ignore_missing_unicodes=False/ignore_missing_unicodes=True/" "${BUILD_DIR:?}/merge.py"

# Создаем шрифт Microsoft Sans Serif на основе Noto Sans.
"${BUILD_DIR:?}/merge.py" \
    "${NOTO_SANS_SRCDIR:?}/NotoSans-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansMath-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansMono-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansSymbols-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansSymbols2-Regular.ttf" \
    "MicrosoftSansSerif" "Microsoft Sans Serif" "Regular" \
    "${FONTS_DIR:?}/ranges/micross" "${BUILD_DIR:?}/micross.ttf"

# Создаем альтернативную версию шрифта Arial на основе Noto Sans.
mkdir -p "${BUILD_DIR:?}/alt"
"${BUILD_DIR:?}/merge.py" \
    "${NOTO_SANS_SRCDIR:?}/NotoSans-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansMath-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansMono-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansSymbols-Regular.ttf" \
    "${NOTO_SANS_SRCDIR:?}/NotoSansSymbols2-Regular.ttf" \
    "Arial" "Arial" "Regular" \
    "${FONTS_DIR:?}/ranges/arial" "${BUILD_DIR:?}/alt/arial.ttf"

rm -vf "${BUILD_DIR:?}/merge.py"

# Создаем директорию шрифтов в нашем Proton.
if [ ! -d "${INSTALL_DIR:?}/files/share/fonts" ]; then
    mkdir -p "${INSTALL_DIR:?}/files/share/fonts"
fi

# Копируем наши шрифты.
cp -rv ${BUILD_DIR:?}/* "${INSTALL_DIR:?}/files/share/fonts/"

# Очищаем директорию сборки.
rm -fr "${BUILD_DIR:?}"

Если все прошло успешно, то мы увидим наши шрифты в директории files/share/fonts.

Сборка lsteamclient и steam_helper

После того, как мы создали дерево каталогов и скопировали файлы, нам нужно собрать lsteamclient и steam_helper, которые необходимы для авторизации игр. Сделать это можно с помощью скрипты ниже:

#!/usr/bin/bash

# Имя нашей Proton сборки.
PROTON_NAME="ProtonWay-9.2"
# Директория в которой хранятся файлы Steam.
STEAM_DIR="${HOME:?}/.steam/root"
# Директория в которой установлен наш собранный Wine.
WINE_DIR="${HOME:?}/wine"
# Директория в которой хранятся исходные коды.
SOURCE_DIR="${HOME:?}/code"
# Директория в которой будет происходить сборка.
BUILD_DIR="/tmp/build-proton"
# Флаги компиляции 64 битной версии версии lsteamclient и steam_helper.
BUILD_CFLAGS="-march=native -mtune=native"
# Флаги компиляции 32 битной версии lsteamclient и steam_helper.
BUILD_CFLAGS_32="-mtune=native"
# Директория в которой находится Proton, в который мы установим lsteamclient и steam_helper.
INSTALL_DIR="${STEAM_DIR:?}/compatibilitytools.d/${PROTON_NAME:?}"

# Если возникает ошибка, то прекращаем выполнение дальнейших команд.
set -euo pipefail

# Сбрасываем все флаги сборки, если наши переменные окружения установлены.
unset CPPFLAGS
unset CFLAGS
unset CXXFLAGS
unset LDFLAGS

# Добавим в PATH директорию bin нашего Wine. Это необходимо для поиска инструментов сборки, который устанавливаются вместе с Wine.
export PATH="${WINE_DIR:?}/bin:${PATH}"

# Создаем директорию, в которой будет проходить сборка, если существует — удаляем и создаем заново.
rm -fr "${BUILD_DIR:?}"
mkdir -p "${BUILD_DIR:?}"

# Устанавливаем флаги для сборки 32 битных библиотек.
export CFLAGS="${BUILD_CFLAGS_32} -Wno-attributes"
export CXXFLAGS="${BUILD_CFLAGS_32} -fpermissive -Wno-attributes"
MAKEFLAGS="--nosource-fix --nolower-include --nodlls --nomsvcrt -I${SOURCE_DIR:?}/wine/include -I${SOURCE_DIR:?}/wine/include/wine"

# Создаем директорию для сборки 32 битной версии lsteamclient, копируем исходный код и переходим в нее.
rm -fr "${BUILD_DIR:?}/lsteamclient.win32"
cp -r "${SOURCE_DIR:?}/proton/lsteamclient" "${BUILD_DIR:?}/lsteamclient.win32"
cd "${BUILD_DIR:?}/lsteamclient.win32"

# Собираем lsteamclient.
winemaker --wine32 --dll ${MAKEFLAGS:?} \
    -DSTEAM_API_EXPORTS \
    -Dprivate=public \
    -Dprotected=public \
    .

sed -re 's@_LDFLAGS=@_LDFLAGS= -static-libgcc -static-libstdc++ -ldl @' -i Makefile
make -e CC="winegcc -m32" CXX="wineg++ -m32 -std=gnu++11" -j$(nproc)

# Собираем fake dll для lsteamclient.
winebuild --dll --fake-module -m32 -o lsteamclient.dll.fake -E lsteamclient.spec
mv lsteamclient.dll.fake lsteamclient.dll

# Создаем директорию для сборки 32 битной версии steam_helper, копируем исходный код и переходим в нее.
rm -fr "${BUILD_DIR:?}/steam.win32"
cp -r "${SOURCE_DIR:?}/proton/steam_helper" "${BUILD_DIR:?}/steam.win32"
cd "${BUILD_DIR:?}/steam.win32"

# Собираем steam_helper.
winemaker --wine32 --guiexe ${MAKEFLAGS:?} \
    -I${SOURCE_DIR:?}/proton/openvr/headers \
    -I${SOURCE_DIR:?}/proton/lsteamclient/steamworks_sdk_142 \
    .

make -e CC="winegcc -m32" CXX="wineg++ -m32" LIBRARIES="-L${SOURCE_DIR:?}/proton/steam_helper/32 -static-libgcc -static-libstdc++ -lsteam_api -lshlwapi -lmsi -lole32 -ldl" -j$(nproc)

# Собираем fake exe для steam_helper
touch steam.spec
winebuild --exe --fake-module -m32 -o steam.exe -E steam.spec

# Устанавливаем флаги для сборки 64 битных библиотек.
export CFLAGS="${BUILD_CFLAGS} -Wno-attributes"
export CXXFLAGS="${BUILD_CFLAGS} -fpermissive -Wno-attributes"

# Создаем директорию для сборки 32 битной версии lsteamclient, копируем исходный код и переходим в нее.
rm -fr "${BUILD_DIR:?}/lsteamclient.win64"
cp -r "${SOURCE_DIR:?}/proton/lsteamclient" "${BUILD_DIR:?}/lsteamclient.win64"
cd "${BUILD_DIR:?}/lsteamclient.win64"

# Собираем lsteamclient.
winemaker --dll ${MAKEFLAGS:?} \
    -DSTEAM_API_EXPORTS \
    -Dprivate=public \
    -Dprotected=public \
    .

sed -re 's@_LDFLAGS=@_LDFLAGS= -static-libgcc -static-libstdc++ -ldl @' -i Makefile
make -e CC="winegcc -m64" CXX="wineg++ -m64 -std=gnu++11" -j$(nproc) 

# Собираем fake dll для lsteamclient.
winebuild --dll --fake-module -m64 -o lsteamclient.dll.fake -E lsteamclient.spec
mv lsteamclient.dll.fake lsteamclient.dll

# Создаем директорию для сборки 64 битной версии steam_helper, копируем исходный код и переходим в нее.
rm -fr "${BUILD_DIR:?}/steam.win64"
cp -r "${SOURCE_DIR:?}/proton/steam_helper" "${BUILD_DIR:?}/steam.win64"
cd "${BUILD_DIR:?}/steam.win64"

# Собираем steam_helper.
winemaker --guiexe ${MAKEFLAGS:?} \
    -I${SOURCE_DIR:?}/proton/openvr/headers \
    -I${SOURCE_DIR:?}/proton/lsteamclient/steamworks_sdk_142 \
    .

make -e CC="winegcc -m64" CXX="wineg++ -m64" LIBRARIES="-L${SOURCE_DIR:?}/proton/steam_helper/64 -static-libgcc -static-libstdc++ -lsteam_api -lshlwapi -lmsi -lole32 -ldl" -j$(nproc)

# Собираем fake exe для steam_helper.
touch steam.spec
winebuild --exe --fake-module -m64 -o steam.exe -E steam.spec

# Установка библиотек.
cp -v "${BUILD_DIR:?}/lsteamclient.win32/lsteamclient.dll.so" "${INSTALL_DIR}/files/lib/wine/i386-unix/lsteamclient.dll.so"
cp -v "${BUILD_DIR:?}/lsteamclient.win32/lsteamclient.dll" "${INSTALL_DIR}/files/lib/wine/i386-windows/lsteamclient.dll"
cp -v "${BUILD_DIR:?}/steam.win32/steam.exe.so" "${INSTALL_DIR}/files/lib/wine/i386-unix/steam.exe.so"
cp -v "${BUILD_DIR:?}/steam.win32/steam.exe" "${INSTALL_DIR}/files/lib/wine/i386-windows/steam.exe"
cp -v "${BUILD_DIR:?}/steam.win32/32/libsteam_api.so" "${INSTALL_DIR}/files/lib/libsteam_api.so"

cp -v "${BUILD_DIR:?}/lsteamclient.win64/lsteamclient.dll.so" "${INSTALL_DIR}/files/lib64/wine/x86_64-unix/lsteamclient.dll.so"
cp -v "${BUILD_DIR:?}/lsteamclient.win64/lsteamclient.dll" "${INSTALL_DIR}/files/lib64/wine/x86_64-windows/lsteamclient.dll"
cp -v "${BUILD_DIR:?}/steam.win64/steam.exe.so" "${INSTALL_DIR}/files/lib64/wine/x86_64-unix/steam.exe.so"
cp -v "${BUILD_DIR:?}/steam.win64/steam.exe" "${INSTALL_DIR}/files/lib64/wine/x86_64-windows/steam.exe"
cp -v "${BUILD_DIR:?}/steam.win64/64/libsteam_api.so" "${INSTALL_DIR}/files/lib64/libsteam_api.so"

# Удаляем директории сборки.
rm -fr "${BUILD_DIR:?}/steam.win32"
rm -fr "${BUILD_DIR:?}/steam.win64"
rm -fr "${BUILD_DIR:?}/lsteamclient.win32"
rm -fr "${BUILD_DIR:?}/lsteamclient.win64"
rm -fr "${BUILD_DIR:?}"

Далее запускаем Steam. Переходим в меню Steam->Settings, пункт Compatibility.
Включаем все переключатели и выбираем наш Proton из списка. Перезапускаем Steam.

Далее устанавливаем необходимую игру. Мною проверены игры: Overwatch 2, Aimbeast, KovaaK’s, Apex Legends и это игры, в которые играю сам.

После установки игры, чтобы Proton-Wine мог использовать Wayland драйвер, нам необходимо добавить соответствующие записи в реестр. Сделать это очень просто. Сначала нам необходимо узнать ID игры. Для этого жмем ПКМ по игре и выбираем Properties, переходим в пункт Updates, смотрим App ID. Это и есть наш идентификатор. Для Aimbeast он равен 1100990.

Для запуска программ в нашем префиксе игры, например winecfg или regedit, мы можем использовать команду proton runinprefix,
которая возьмет пути из переменных STEAM_COMPAT_DATA_PATH, STEAM_COMPAT_INSTALL_PATH, STEAM_COMPAT_LIBRARY_PATHS, STEAM_COMPAT_CLIENT_INSTALL_PATH и запустит необходимую программу.

# Путь где находится Wine префикс игры.
export STEAM_COMPAT_DATA_PATH="${HOME:?}/.local/share/Steam/steamapps/compatdata/1100990"
# Путь куда установлена игра.
export STEAM_COMPAT_INSTALL_PATH="${HOME:?}/.local/share/Steam/steamapps/common/Aimbeast"
# Директория steamapps в Steam.
export STEAM_COMPAT_LIBRARY_PATHS="${HOME:?}/.local/share/Steam/steamapps"
# Главная директория Steam.
export STEAM_COMPAT_CLIENT_INSTALL_PATH="${HOME:?}/.local/share/Steam"

# Перейдем в директорию нашего Proton.
cd "${HOME:?}/.steam/root/compatibilitytools.d/ProtonWay-9.2"

# Добавим необходимые записи в реестр.
./proton runinprefix reg.exe add HKCU\\Software\\Wine\\Drivers /v Graphics /d x11,wayland
./proton runinprefix reg.exe add HKCU\\Software\\Wine\\Wayland\ Driver /v unaccelerated_pointer /t REG_DWORD /d 1

Повторить необходимо для каждой установленной игры, если вы не делали это ранее.

Следующим шагом нам необходимо раскомментировать строку "WAYLAND": "1" в settings.py нашего Proton, что включит Wayland для всех игр, или можно установить параметры запуска игры DISPLAY="" %command% для конкретной игры. Также вы можете включить запуск игр с приоритетом планировщика в реальном времени "REALTIME": "1" или установить любые кастомные переменные окружения. Смотрите settings.py.

Теперь вы можете запускать игру и наслаждать ей в Wayland.

Игру Apex Legends желательно запускать в режиме Direct X 12, так производительность игры лучше. Сделать это можно с помощью параметров запуска %command% -eac_launcher_settings SettingsDX12.json. Так же я советую использовать патчи NtSync из первой части.

Поздравляю, теперь вы разбираетесь в том, как работает Wine и Proton. Можете создавать свои собственные сборки, запускать игры, наслаждаться плавной их работой в Wayland. Это хорошая отправная точка. Надеюсь, ваш путь не остановиться на прочтении этой статьи и вы пойдете дальше, сможете решать более сложные задачи связанные с Wine и Proton.

PS. Для подготовки данной статьи была проведена огромная работа по изучению работы Proton, портированы патчи в Wine 9.0, написаны скрипты. Также на этом пути было встречено куча сложных проблем, на решение которых ушло не мало времени, и данная статья в реальности сложнее, чем может подумать читатель, где даны уже полуготовые решения. Спасибо за прочтение и до встречи на Хабре!


 

Источник

Native, steam, Wayland, запуск, игр, поддержкой, Протон

Читайте также