Запуск Anomaly: Warzone Earth на Linux

После большого количества различных игр в жанре Tower Defence (в моем личном топе лучшая из них — Defense Grid: The Awakening) хочется попробовать чего-то нового. Таковыми стали две игры — Sanctum 2 и Anomaly: Warzone Earth. Sanctum 2 представляет из себя микс TD с шутером от первого лица, а Anomaly — Tower Defence наоборот, т.е. дает попробовать себя в роли тех самых ходоков по лабиринту. Игра, вышедшая в 2011 году одна из первых заявила о поддержке Linux. Проблема в том, что на современных ОС она не запускается. Точнее после вступительного ролика игра бесконечно загружается. О том как это победить дальнейшая статья.


Беглое гугление приводит сразу на несколько топиков в Steamcommunity, а так же на тред на гитхабе, посвященный клиенту Steam под Linux. Среди основных пробем там описывается две — невозможность запуска из командной строки и собственно вышеописанный баг с бесконечной загрузкой. Чтение треда привело к этому посту на Steamcommunity. Смысл бага, который не исправиляют вот уже 7 лет с момента выхода игры в следующем:

Basically they call clock_gettime() and add 10000000 to the nanosecs, but they don’t check for the nanosecs getting bigger than 1000000000 (1s), which glibc checks for. The returned error condition (-1/EINVAL) isn’t handled by the game and instead it tries to wait even longer (until 0 is returned). Thus it just hangs.

Или в грубом переводе: Они вызывают [библиотечную функцию] clock_gettime() и добавляют 10000000 к возвращаемому значению наносекунд, но не проверяют, превышает ли получившееся значение больше чем 1000000000 (1 секунда), а вот glibc — проверяет. Возвращаемый результат проверки (-1/EINVAL) не обрабатывается игрой и вместо исключения игра пытается подождать еще правильного значения (т.е. до тех пор пока не вернется 0). И поэтому просто зависает.

Исправление этой проблемы заключается в следующем. Берем код из этого же поста и вставляем его в файл loadfix.c:

#define _GNU_SOURCE
#include <dlfcn.h>
#include <semaphore.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
static int (*_realSemTimedWait)(sem_t *, const struct timespec *) = NULL;
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
{
 if (abs_timeout->tv_nsec >= 1000000000)
 {
 ((struct timespec *)abs_timeout)->tv_nsec -= 1000000000;
 ((struct timespec *)abs_timeout)->tv_sec++;
 }
 return _realSemTimedWait(sem, abs_timeout);
}
__attribute__((constructor)) void init(void)
{
 _realSemTimedWait = dlsym(RTLD_NEXT, "sem_timedwait");
}

и затем компилируем его, для сборки необходимо, чтобы были установлены зависимости в виде libc6-dev-i386:

gcc -m32 -o loadfix.so loadfix.c -ldl -shared -fPIC -Wall -Wextra

Затем копируем файл loadfix.so в папку с игрой и добавляем в параметры запуска командную строку: LD_PRELOAD=$LD_PRELOAD:./loadfix.so %command%.

Так же рекомендую отличную вики от Arch Linux со списком обходов различных проблем в этой и других играх.

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.