> For the complete documentation index, see [llms.txt](https://lessons.sannybuilder.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lessons.sannybuilder.com/00100/00700/000100.md).

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

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

* Установить Visual Studio (желательно последнюю версию - 2017). Подробнее - [здесь](https://github.com/DK22Pac/plugin-sdk/wiki/%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D1%81%D1%80%D0%B5%D0%B4%D1%8B-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D0%BA%D0%B8);
* Скачать plugin-sdk и настроить его. Подробнее - [здесь](https://github.com/DK22Pac/plugin-sdk/wiki/%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-plugin-sdk).

Итак, после того, как plugin-sdk настроен, мы можем создать плагин в Visual Studio. Для этого создаём новый проект и выбираем шаблон проекта Plugin-SDK в подразделе Visual C++ - Plugin-SDK:

[![Plugin Project Wizard](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/2/87644493.png)](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/2/87644493.png)

Не забываем также указать название проекта и выбрать место его расположения. По умолчанию, у нас в проекте есть один файл исходного кода (`.cpp`). Открываем этот файл и подключаем нужные заголовочные файлы:

```cpp
#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"

using namespace plugin;
```

Мы будем изменять игровое время (класс `CClock` отвечает за работу с игровыми "часами") и выводить сообщение (класс `CMessage` предоставляет функции для вывода сообщений).

Для удобства, мы разместим все данные и функции в классе. В конструктор этого класса мы поместим код инициализации нашего плагина. В глобальной области видимости мы создадим экземпляр этого класса, таким образом, при подключении нашего плагина (а плагин - это динамическая библиотека с расширением `.asi`) ASI-loader'ом, будет вызван конструктор нашего класса.

Такой подход позволяет избавиться от явного использования функции `DllMain`, но, впрочем, никто вам не запрещает выполнять инициализацию плагина так, как Вам удобно.

```cpp
#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"

using namespace plugin;

class TestPlugin {
public:
    TestPlugin() {
        // код инициализации плагина
    }
} test; // создание экземпляра класса TestPlugin в глобальной области видимости
```

### Инициализация плагина

В plugin-sdk реализована идея "событий" (events). Т.е., есть возможность выполнить свой код в какой-то определённый момент игры. Обращаться ко всем событиям можно из пространства имён `plugin::Events`. Вот как это выглядит:

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

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

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

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

```cpp
// Добавить вызов функции 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++, мы можем записать добавление функции в таком виде:

```cpp
Events::gameProcessEvent += [] {
    // код нашей функции
};
```

### Код

Мы сделаем изменение времени после нажатия клавиши `Delete`. Вот так будет выглядеть наш код:

```cpp
#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**
