Простейший плагин
Написание простого ASI плагина с plugin-sdk.
Для написания плагина с plugin-sdk необходимо следующее:
Итак, после того, как plugin-sdk настроен, мы можем создать плагин в Visual Studio. Для этого создаём новый проект и выбираем шаблон проекта Plugin-SDK в подразделе Visual C++ - Plugin-SDK:
Не забываем также указать название проекта и выбрать место его расположения. По умолчанию, у нас в проекте есть один файл исходного кода (
.cpp
). Открываем этот файл и подключаем нужные заголовочные файлы:#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"
using namespace plugin;
Мы будем изменять игровое время (класс
CClock
отвечает за работу с игровыми "часами") и выводить сообщение (класс CMessage
предоставляет функции для вывода сообщений).Для удобства, мы разместим все данные и функции в классе. В конструктор этого класса мы поместим код инициализации нашего плагина. В глобальной области видимости мы создадим экземпляр этого класса, таким образом, при подключении нашего плагина (а плагин - это динамическая библиотека с расширением
.asi
) ASI-loader'ом, будет вызван конструктор нашего класса.Такой подход позволяет избавиться от явного использования функции
DllMain
, но, впрочем, никто вам не запрещает выполнять инициализацию плагина так, как Вам удобно.#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"
using namespace plugin;
class TestPlugin {
public:
TestPlugin() {
// код инициализации плагина
}
} test; // создание экземпляра класса TestPlugin в глобальной области видимости
В plugin-sdk реализована идея "событий" (events). Т.е., есть возможность выполнить свой код в какой-то определённый момент игры. Обращаться ко всем событиям можно из пространства имён
plugin::Events
. Вот как это выглядит:Events::gameProcessEvent.Add(MyFunction); // Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Метод
Add
добавляет указанную функцию к конкретному игровому "событию". При этом, у метода Add
есть альтернатива в виде оператора +=
:Events::gameProcessEvent += MyFunction; // Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Также, есть возможность указать - когда именно вызывать нашу функцию - до "события" или после:
Events::gameProcessEvent.before += MyFunction; // Добавить вызов функции MyFunction, перед вызовом CGame::Process
Events::gameProcessEvent.after += MyFunction; // Добавить вызов функции MyFunction, поле вызова CGame::Process (стандартное поведение - т.е. аналогично вызову "Events::gameProcessEvent +=")
Бывают ситуации, когда такая возможность может быть действительно полезной - например, когда надо выполнить какие-то операции до и после отрисовки игрового субъекта (например, транспорта).
Ниже приведен список некоторых событий, доступных в plugin-sdk:
Событие | Функция-прототип | Описание |
---|---|---|
drawingEvent | void() | Отрисовка в 2D |
drawHudEvent | void() | Отрисовка HUD-элементов |
drawRadarEvent | void() | Отрисовка радара |
drawBlipsEvent | void() | Отрисовка иконок на радаре |
drawRadarOverlayEvent | void(bool bInMenu) | Отрисовка зон влияния на радаре |
drawMenuBackgroundEvent | void(void *menuManager) | Отрисовка фона в меню |
initRwEvent | void() | Инициализация графического движка |
shutdownRwEvent | void() | Деинициализация графического движка |
vehicleRenderEvent | void(CVehicle* vehicle) | Рендер транспорта |
pedRenderEvent | void(CPed* ped) | Рендер педа |
objectRenderEvent | void(CObject* object) | Рендер обьекта |
vehicleSetModelEvent | void(CVehicle* vehicle, int modelId) | Установка модели транспорта |
pedSetModelEvent | void(CPed* ped, int modelId) | Установка модели педа |
d3dResetEvent | void() | Пересоздание D3D-девайса |
d3dLostEvent | void() | Утерян доступ к D3D-девайсу |
gameProcessEvent | void() | Обработка разных компонентов игры |
initGameEvent | void() | Инициализация игры |
reInitGameEvent | void() | Повторная инициализация игры |
onPauseAllSounds | void() | Остановка всех звуков в игре |
onResumeAllSounds | void() | Включение всех звуков в игре |
initScriptsEvent | void() | Инициализация скриптов |
processScriptsEvent | void() | Обработка скриптов |
Используя лямбда-выражение в C++, мы можем записать добавление функции в таком виде:
Events::gameProcessEvent += [] {
// код нашей функции
};
Мы сделаем изменение времени после нажатия клавиши
Delete
. Вот так будет выглядеть наш код:#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"
using namespace plugin;
class TestPlugin {
public:
TestPlugin() {
Events::gameProcessEvent += [] {
// Если нажата клавиша Delete
if (KeyPressed(VK_DELETE)) {
// Устанавливаем время
CClock::SetGameClock(12, 0, CClock::ms_nGameClockDayOfWeek);
// Выводим сообщение
CMessages::AddMessageJumpQ("You have set time to ~b~12:00", 3000, 0, false);
}
};
}
} test;
Код готов, переключаемся в режим "Release" и компилируем проект (
F7
).Итак, что мы получаем в случае использования Plugin SDK? Чистые хуки без явно видимых патчей и работы напрямую с адресами памяти.
Автор: DK22Pac