Jump to content
Korean Random
Pavel3333

[DONE] XFW Native - долгая загрузка

Recommended Posts

Клиент с XFW Native грузится в 3 раза дольше, чем без него. По предложению @GPCracker, это связано с регулярным поиском сигнатур функций питона в памяти.

 

Есть предложение от @GPCracker

Цитата

а нельзя ли эти точки закешировать в пределах неизменности контрольной суммы exe клиента и значимых dll?

 

То есть при каждом патче пройтись по exe'шнику танков и записать смещения функций питона. Мне кажется, это очень разумно и будет намного быстрее по времени загрузки, чем каждый раз сканировать память клиента на предмет нахождения сигнатур функций.

Edited by Mixaill
  • Upvote 2

Share this post


Link to post

Short link
Share on other sites
18 minutes ago, Pavel3333 said:

То есть при каждом патче пройтись по exe'шнику танков и записать смещения функций питона. Мне кажется, это очень разумно и будет намного быстрее по времени загрузки, чем каждый раз сканировать память клиента на предмет нахождения сигнатур функций.

Ну по факту, сканировать все же придется (использовать старые координаты вслепую не самая лучшая идея), однако запоминая смещения функций, которые были при предыдущем запуске клиента игры, и используя их в качестве места для предварительного поиска, можно весьма серьезно ускорить запуск клиента.

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
6 минут назад, GPCracker сказал:

вслепую

Не совсем вслепую, при совпадении хэшей exe'шника танков

не совпадает - пофиг, пляшем от регулярного поиска сигнатур по шаблону

@sirmax, @Mixaill?

--------------

Надо найти точку входа главной питоновской функции и уже от нее считать смещения. Или, если они все располагаются непоследовательно, как предложил @GPCracker, запоминать смещения в памяти танков и искать сигну по шаблону уже в области этих смещений

Edited by Pavel3333

Share this post


Link to post

Short link
Share on other sites
21 minutes ago, Pavel3333 said:

Не совсем вслепую, при совпадении хэшей exe'шника танков

Существование таких вещей как таблицы для CheatEngine и ArtMoney явно подтверждает тот факт, что адреса большинства переменных в памяти не изменяются между запусками приложения, однако в случае с динамическим распределением памяти это может не прокатить. Вариант с предварительным поиском по старому адресу просто элементарно надежнее (исключает ошибочное определение адреса) и скорее всего даже быстрее (не нужно тратить время на вычисление контрольных сумм и определять что от чего зависит, а поиск-проверка в известных координатах выполняется достаточно быстро).

В любом случае, кастуем сюда @Mixaill, ибо именно он стоит за коммитами в тот самый репозиторий.

21 minutes ago, Pavel3333 said:

Надо найти точку входа главной питоновской функции и уже от нее считать смещения.

Если большая часть смещений между запусками не изменяются, нет смысла заморачиваться с ограничением зоны поиска, временем первого запуска можно пренебречь, при условии что он будет занимать не более чем занимает сейчас.

Edited by GPCracker
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

Прежде чем чего-то советовать хотелось бы мельком увидеть как реализован поиск сигнатур сейчас.

Share this post


Link to post

Short link
Share on other sites
1 minute ago, StranikS_Scan said:

Прежде чем чего-то советовать хотелось бы мельком увидеть как реализован поиск сигнатур сейчас.

А в чем собственно проблема? Репозиторий лежит здесь.

Share this post


Link to post

Short link
Share on other sites
6 минут назад, GPCracker сказал:

Существование таких вещей как таблицы для CheatEngine и ArtMoney явно подтверждает тот факт, что адреса большинства переменных в памяти не изменяются между запусками приложения, однако в случае с динамическим распределением памяти это может не прокатить.

 

Мы же ведем речь об импортах функций, экспорт которых просто убрали из exe. Там должны быть статичные адреса в пределах одной сборки exe.

Только что, GPCracker сказал:

А в чем собственно проблема? Репозиторий лежит здесь.

 

Ну конечно проблем нет. Это как сослаться на библиотеку. Она же стоит и книг там много и вход свободный. Я не разбираюсь что там к чему. Мне нужен конкретный модуль.

Share this post


Link to post

Short link
Share on other sites

@GPCracker @StranikS_Scan вот на примере helper.c:

DWORD WOTPYTHON_FindFunction(DWORD startpos, DWORD endpos, DWORD* curpos, const char* pattern, const char* mask)
{
    DWORD pos;

    for (pos=(*curpos); pos < endpos; pos++)
    {
        if (WOTPYTHON_DataCompare((char*)pos, pattern, mask))
        {
            (*curpos) = pos;
            return pos;
        }
    }

    for (pos = startpos; pos < (*curpos); pos++)
    {
        if (WOTPYTHON_DataCompare((char*)pos, pattern, mask))
        {
            (*curpos) = pos;
            return pos;
        }
    }

    (*curpos) = startpos;
    return 0;
}

То есть задается область памяти (от void* startpos до void* endpos), в ней байт за байтом считываются блоки данных и сверяются по маске.

 

 

fix_winapi.c

FARPROC WINAPI GetProcAddress_libpython(HMODULE hModule, LPCSTR lpProcName)
{
    FARPROC ret = NULL;
    if(hModule==PyWin_DLLhModule)
    {
        ret = (FARPROC)WOTPYTHON_GetRealAddress(lpProcName);       
        if (ret != NULL)
        {
            return ret;
        }
    }

    return GetProcAddress_real(hModule, lpProcName);
}

 

Ищется адрес python27.lib. Не нашли - ищем во ВСЕЙ памяти процесса.

Share this post


Link to post

Short link
Share on other sites
5 minutes ago, StranikS_Scan said:

Мы же ведем речь об импортах функций, экспорт которых просто убрали из exe. Там должны быть статичные адреса в пределах одной сборки exe.

Я конечно разбираюсь во всем этом как белый медведь в апельсинах, но факт остается фактом - если адреса функций в памяти не изменяются в таких вполне себе ощутимых пределах как сборка exe (или микропатч), вычислять их при каждом запуске методом полного расчета без проверки актуальности вычисленных ранее (без предварительной проверки в "старом" месте) как минимум неправильно. А поскольку я не привык считать маловероятные события невозможными, и на этом основании их исключать из рассмотрения, я все же не торопился бы принимать за аксиому неизменность адресов, тем более что проверка по конкретному адресу (вместо утверждения этого адреса вслепую) не будет оказывать сколь бы то ни было заметного влияния на скорость загрузки, и притом такой подход исключает как необходимость анализа контрольных сумм (или других критериев наличия изменений в файлах игры), так и возможность получения неверных данных, да и в случае изменяющегося адреса будет вполне корректно работать (пусть и медленно, но в такой ситуации другого варианта все равно нет).

Share this post


Link to post

Short link
Share on other sites

При сборке порядок поиска выставляется так, чтобы был только один проход по памяти Worldoftanks.exe. Но так как порядок линковки объектных файлов в MSVC недетерминирован, то довольно часто случается так, что этот порядок сбивается, и при поиске каждой сигнатуры приходится пролопачивать значительно большую часть памяти процесса.

 

Как костыль можно просто перебилдить библиотеку, но более верное решение, как тут уже отметили, сейвить результаты в какой-то файловый кэш.

Edited by Mixaill

Share this post


Link to post

Short link
Share on other sites
51 minutes ago, StranikS_Scan said:

Мне нужен конкретный модуль.

Ну так бы сразу и сказал. А то я уж было подумал, что ты не знаешь про репозиторий :)

Share this post


Link to post

Short link
Share on other sites

@Mixaill кэш можно реализовать, как понимаю, в виде "сигнатура - позиция, по которой она последний раз была найдена".

 

При дальнейших запусках клиента смотреть в кэш. Если не совпало - надо делать по старинке.

Edited by Pavel3333

Share this post


Link to post

Short link
Share on other sites
1 minute ago, Pavel3333 said:

При дальнейших запусках клиента смотреть в кэш. Если не совпало - надо делать по старинке.

Собственно, так я и предложил делать в самом начале темы. Использовать предыдущие координаты в качестве места для предварительной проверки.

3 minutes ago, Pavel3333 said:

кэш можно реализовать, как понимаю, в виде "сигнатура - позиция, по которой она последний раз была найдена".

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

  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
Только что, Pavel3333 сказал:

@Mixaill кэш можно реализовать, как понимаю, в виде "сигнатура - позиция, по которой она найдена".

 

Поддержу. Нужен однократный алгоритм поиска всех адресов исключительно с однократным прогоном по памяти. С кэшированием адресов в файл. В результате с одной стороны все последующие загрузки клиента будут без задержек, а с другой стороны - время поиска будет меньше сейчас, так как действительно многократный повторный поиск с нуля - жрёт кучу времени.

 

Я код посмотрел, я не специалист, но мне кажется если если сравниваться будут не Char, а Byte, то будет быстрее. Но я в си не рублю совсем потому могу ошибаться.

 

И еще меня удивило что там чуть ли не на каждый байт памяти игры вызывается функция сравнения с сигнатурой WOTPYTHON_DataCompare . Это если 50 Мб скажем игры, то 50 млн вызовов.

Edited by StranikS_Scan

Share this post


Link to post

Short link
Share on other sites
2 минуты назад, StranikS_Scan сказал:

но мне кажется если если сравниваться будут не Char, а Byte, то будет быстрее

а что char, что byte - это одно и то же. Самый быстрый тип байта в Си - это ЕМНИП unsigned char, он же uint8_t

Share this post


Link to post

Short link
Share on other sites
Только что, Pavel3333 сказал:

а что char, что byte - это одно и то же. Самый быстрый тип байта в Си - это ЕМНИП unsigned char, он же uint8_t

 

Там нет разницы, там типа char = uint8_t прописано?

Share this post


Link to post

Short link
Share on other sites
5 минут назад, StranikS_Scan сказал:

Это если 50 Мб скажем игры, то 50 млн вызовов.

Должно быть заинлайнено при текущих флагах, но ок, пропишу тут явно.

Share this post


Link to post

Short link
Share on other sites
1 minute ago, Pavel3333 said:

а что char, что byte - это одно и то же. Самый быстрый тип байта в Си - это ЕМНИП unsigned char, он же uint8_t

Это все проверяется компиляцией и просмотром кода на ассемблере. И что-то мне подсказывает, что "скорость типа" определяется исключительно его размером (ну сложностью самой операции над этим типом).

Share this post


Link to post

Short link
Share on other sites

@StranikS_Scan нет, наоборот, прописано следующее:

typedef unsigned char      uint8_t;

самый быстрый тип байта - uint_fast8_t, тоже определен как unsigned char:

typedef unsigned char      uint_fast8_t;

Подробнее - в stdint.h (хедер, использующий его - iostream)

Share this post


Link to post

Short link
Share on other sites
Только что, GPCracker сказал:

Это все проверяется компиляцией и просмотром кода на ассемблере. И что-то мне подсказывает, что "скорость типа" определяется исключительно его размером (ну сложностью самой операции над этим типом).

 

Вот. Я когда изучал и потом сам писал и юзал поисковики адресов в ОЗУ, правда на ОbPas, придерживался двух принципов

- ни каких промежуточных вызовов и обращений куда-то там в теле цикла

- только простейщие операции и типы, а это Byte = 0...255, считанный блок из ОЗУ как массив числе, и сигнатура предварительно переведена в массив чисел

Share this post


Link to post

Short link
Share on other sites
Guest
This topic is now closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...