- Создание ASI-плагина с нуля
- Хуки – что это такое и как с ними работать
- Безопасная инициализация и работа с SAMP
- Работа с рендером и Directx9
- Обработка событий окна + ImGui
В этом гайде будет рассказано как инициализироваться без потоков, а также как работать с SAMP'ом
При использовании на других ресурсах необходимо указание авторства и ссылки на оригинальную темы!
Все действия производились на Visual Studio 2019 с параметром
/std:c++17
, в других версиях интерфейс может отличаться.И так, начнем:
Создаем новый проект, настраиваем его.
В свойствах проекта, в вкладке общие, стандарт языка C++ ставим /std:c++17
Добавляем sampapi в наш проект:
Первый способ:
- В верхнем меню жмем Git -> Создать репозиторий Git(Create git repository)
- В открывшемся окне выбираем либо github репозиторий, либо локальный репозиторий и жмем Создать.
- Снова жмем Git -> Открыть в командной строке (Open in command line).
- В консоли пишем
git submodule add https://github.com/BlastHackNet/SAMP-API.git
- Ждем завершения операции и далее жмем Файл(File) -> Добавить(Add) -> Существующий проект (Existing projeect). Открываем папку с нашим проектом, заходим в папку SAMP-API и выбираем sampapi.vcxproj.
- Открываем настройки проекта, выбираем C/C++ -> Общие -> Дополнительные каталоги включаемых файлов(Additional directories of included files). Жмем изменить
В открывшемся окне жмем на иконку файла со звездочкой, и жмем на появившуюся кнопку...
Переходим в директориюpath_to_project\SAMP-API\include
и жмем выбор папки. - Переходим в настройки проекта
sampapi
Набор инструментов платформы(Build tools) - выбираем свое(у меня это v142
Версия пакета SDK для Windows - выбираем свое(у меня это 10.0) - Заходим в настройки нашего основного проекта, Компоновщик(Linker) -> Дополнительные каталоги библиотек(Additional library directories)
Повторяем те же самые операции что в шаге 6, но выбираем папкуpath_to_project\SAMP-API\build\bin
- Далее переходим в вкладку Ввод(Input) и в поле Дополнительные зависимости(Additional dependecies) в начало вписываем
Release\sampapi.lib;
для Release конфигурации проект, иDebug\sampapi.lib;
. Точка с запятой ОБЯЗАТЕЛЬНА - Жмем правой кнопкой по проекту sampapi, Только проект -> Собрать только sampapi(делаем это для Release и Debug конфигураций)
Скачиваем репозиторий, распаковываем по пути
path_to_project
Повторяем шаги 5-10 из первого способа.
Готово, мы подключили sampapi
Теперь добавим библиотеку для хуков в наш проект:
Начну свое описание с подключения моей библиотеки для хуков - kthook
- Скачиваем kthook из репозитория и распаковываем в себе в папку с кодом(Пкм по проекту -> открыть папку в проводнике).
Папку tests перекидывать к себе не нужно. - Скачиваем ktsignal из репозитория и распаковываем содержимое
ktsignal-master
в папкуkthook\ktsignal
- Жмем показать все файлы
- Т.к. мы не используем CMake, тогда мы должны вручную переназначить все зависимости для kthook
- Добавляем папку kthook в Дополнительные каталоги включаемых файлов(Additional directories of included files) (как в шаге 6 у sampapi)
- В kthook.hpp изменяем
#include "ktsignal/ktsignal.hpp"
на#include "ktsignal/include/ktsignal/ktsignal.hpp"
- Также ради собственного удобства добавляем
kthook/include
Дополнительные каталоги включаемых файлов(Additional directories of included files)
Вместо kthook могу посоветовать вам SRHook либо MinHook
SRHook подключается также как и kthook, MinHook можно подключить также двумя способами, как и sampapi.
Но я рекомендую установить его через vcpkg.
vcpkg install minhook
И теперь перейдем к написанию кода:
SAMP инициализирует свои структуры в вызове
CHud::DrawRadar
, поэтому мы перехватим эту функцию, и после ее вызова будем уверены что самп инициализировался.Для инициализации плагина лично я обычно использую хук на
CTimer::Update
C++:
using CTimer__Update = void(__cdecl*)();
void* func_addr = reinterpret_cast<void*>(0x561B10);
Создаем хук:
C++:
#include "kthook/kthook.hpp"
using CHud__DrawRadar = void(__cdecl*)(); // Прототип CHud::DrawRadar
kthook::kthook_simple_t<CHud__DrawRadar> samp_init_hook{ reinterpret_cast<void*>(0x58A330) }; // хук на CHud::DrawRadar
Создаем функцию-коллбэк для хука:
C++:
#include "sampapi/CChat.h"
void HOOK_after_samp_init() {
static bool inited = false; // переменная инициализации
namespace samp = sampapi::v037r3; // Ваша версия сампа, у меня это 037r3
if (!inited && samp::RefChat() != nullptr) { // если еще не инициализировались, и чат сампа уже инициализировался
samp::RefChat()->AddMessage(0xFFFFFFFF, "Этот текст выведен после инициализации сампа"); // Выводим сообщение
samp::RefChat()->m_debugColor = 0xFFFF0000; // Меняем цвет системных сообщений сампа на красный
inited = true; // отмечаем что иницализировались
}
}
samp::RefChat()
аналогичен SF->getSAMP()->getChat()
Подключаем нашу функцию к хуку:
C++:
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
samp_init_hook.after.connect(HOOK_after_samp_init); // Подключаем нашу функцию-коллбэк после вызова CHud::DrawRadar
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
C++:
#include <windows.h>
#include "sampapi/CChat.h"
#include "kthook/kthook.hpp"
using CHud__DrawRadar = void(__cdecl*)(); // Прототип CHud::DrawRadar
kthook::kthook_simple_t<CHud__DrawRadar> samp_init_hook{ reinterpret_cast<void*>(0x58A330) }; // хук на CHud::DrawRadar
void HOOK_after_samp_init() {
static bool inited = false; // переменная инициализации
namespace samp = sampapi::v037r3; // Ваша версия сампа, у меня это 037r3
if (!inited && samp::RefChat() != nullptr) { // если еще не инициализировались, и чат сампа уже инициализировался
samp::RefChat()->AddMessage(0xFFFFFFFF, "Этот текст выведен после инициализации сампа"); // Выводим сообщение
samp::RefChat()->m_debugColor = 0xFFFF0000; // Меняем цвет системных сообщений сампа на красный
inited = true; // отмечаем что иницализировались
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
samp_init_hook.after.connect(HOOK_after_samp_init); // Подключаем нашу функцию-коллбэк после вызова CHud::DrawRadar
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Компилируем, запускаем и видим:
Последнее редактирование: