Простейший плагин

Написание простого ASI плагина с plugin-sdk.

Для написания плагина с plugin-sdk необходимо следующее:

  • Установить Visual Studio (желательно последнюю версию - 2017). Подробнее - здесь;

  • Скачать 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. Вот как это выглядит:

// Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Events::gameProcessEvent.Add(MyFunction); 

Метод Add добавляет указанную функцию к конкретному игровому "событию". При этом, у метода Add есть альтернатива в виде оператора +=:

// Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Events::gameProcessEvent += MyFunction; 

Также, есть возможность указать когда именно вызывать нашу функцию: до "события" или после:

// Добавить вызов функции MyFunction, перед вызовом CGame::Process
Events::gameProcessEvent.before += MyFunction; 

// Добавить вызов функции MyFunction, поле вызова CGame::Process
// (стандартное поведение - т.е. аналогично вызову "Events::gameProcessEvent +=")
Events::gameProcessEvent.after += MyFunction; 

Бывают ситуации, когда такая возможность может быть действительно полезной. Например, когда надо выполнить какие-то операции до и после отрисовки игрового субъекта (например, транспорта).

Ниже приведен список некоторых событий, доступных в 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

Last updated