• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток! Спешите принять участие в конкурсе "Таинственные миры" 2024!
    Ждем именно вас!

    Ссылка на конкурсную тему - тык

2. Создание и запуск плагина / Creating and running a plugin

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.276
Благодарности
4.579
Баллы
625

Русский English


  • * Перед началом ознакомиться с данной темой.

    Создание проекта:
    Запускаем Visual studio, выбираем 'Создать проект', либо зажимаем Ctrl + Shift + N.
    В открывшемся окне находим шаблон плагина UnionPlugin_X_Xx для Visual C++.
    Нажимаем ок и смотрим в окно обозревателя.
    73948

    В нашем распоряжении находятся интерфейсы 4х версий ZenGin и несколько файлов исходного кода. Сейчас нам понадобится Application.cpp.
    Открываем файл и видим несколько функций. Каждая будет вызываться автоматически при выполнении какого-либо игрового события:
    - Game_Entry происходит при входе в главную функцию игры WinMain.
    - Game_Init происходит при инициализации игровых ресурсов и входе в главное меню.
    - Game_Exit происходит при корректном завершении игры.
    - Game_Loop происходит при каждой перерисовке кадра во время игры.
    - Game_SaveBegin происходит перед началом сохранения.
    - Game_SaveEnd происходит после завершения сохранения.
    - LoadBegin происходит перед любой загрузкой игры.
    - LoadEnd происходит после любой загрузки игры.
    - Game_LoadBegin_NewGame происходит перед загрузкой новой игры.
    - Game_LoadEnd_NewGame происходит после загрузки новой игры.
    - Game_LoadBegin_SaveGame происходит перед загрузкой сохраненной игры.
    - Game_LoadEnd_SaveGame происходит после загрузки сохраненной игры.
    - Game_LoadBegin_ChangeLevel происходит перед загрузкой другой локации.
    - Game_LoadEnd_ChangeLevel происходит после загрузки другой локации.
    - Game_LoadBegin_Trigger происходит перед загрузкой другой локации посредством триггера (перед вызовом Game_LoadBegin_ChangeLevel).
    - Game_LoadEnd_Trigger происходит после загрузки другой локации посредством триггера (после вызова Game_LoadEnd_ChangeLevel)
    - Game_Pause происходит когда игра ставится на паузу
    - Game_Unpause происходит когда игра возобновляется
    - Game_DefineExternals происходит перед инициализацией внешний скриптовых функций

    Реализация программного кода:
    Эти функции будут являться базовыми в написании любого плагина. Поэтому в рамках текущего туториала предлагаю проверить их работоспособность.
    При входе в главное меню напишем сообщение с названием текущего плагина, версии Union и текущего движка.
    Воспользуемся готовой функцией Game_Init.
    C++:
    // Функция возвращает имя движка основываясь на его версию
    string GetEngineVersionName(TEngineVersion version) {
      switch( version ) {
      case Engine_G1:  return "Gothic I Classic";
      case Engine_G1A: return "Gothic I Addon";
      case Engine_G2:  return "Gothic II Classic";
      case Engine_G2A: return "Gothic II Addon";
      }
      return "Unknown";
    }
    
    void Game_Init() {
    
      // Получаем указатель на текущий
      // плагин и получаем его имя.
      const CPlugin* plugin = CPlugin::GetCurrentPlugin();
      string pluginName = plugin->GetName();
    
      // Получаем экземпляр версии юниона
      // и преобразуем ее в текстовую строку.
      TVersion unionVersion = Union.GetUnionVersion();
      string unionVersionName = unionVersion.ToString();
    
      // Получаем версию движка и его имя
      TEngineVersion engineVersion = Union.GetEngineVersion();
      string engineVersionName = GetEngineVersionName(engineVersion);
    
      // Выводим сообщение на экран
      Message::Info( string::Combine(
        "Plugin name: %s\nUnion version: %s\nEngine version: %s",
        pluginName,
        unionVersionName,
        engineVersionName
      ) );
    }



    Компиляция и запуск:
    В конфигурации проекта устанавливаем Release. Собираем проект и добавляем его в ini файл. Запускаем игру и ждем загрузки меню.



    Результат:
    73950
    73951

    73952
    73949


  • * Before the beginning, read this topic.

    Creating a project:
    Launch Visual studio, select 'Create project', or hold Ctrl + Shift + N.
    In the window that opens, we find the plugin template UnionPlugin_X_Xx for Visual C ++.
    Click ok and look in the browser window.
    73948

    We have the interfaces of 4 versions of ZenGin and several source code files. Now we need Application.cpp.
    Open the file and you can see several functions. Each will be called automatically when a game event is performed:
    - Game_Entry occurs when initializing game resources and entering the main menu.
    - Game_Init occurs when the game ends correctly.
    - Game_Exit occurs every time the frame is redrawn during the game.
    - Game_Loop happens before saving starts.
    - Game_SaveBegin occurs before saving starts.
    - Game_SaveEnd occurs after saving is completed.
    - LoadBegin before any game loading.
    - LoadEnd occurs after any loading of the game
    - Game_LoadBegin_NewGame happens before loading a new game.
    - Game_LoadEnd_NewGame happens after loading a new game.
    - Game_LoadBegin_SaveGame happens before loading a saved game.
    - Game_LoadEnd_SaveGame occurs after loading a saved game.
    - Game_LoadBegin_ChangeLevel happens before loading another location.
    - Game_LoadEnd_ChangeLevel occurs after loading another location.
    - Game_LoadBegin_Trigger occurs before loading another location via a trigger (before calling Game_LoadBegin_ChangeLevel).
    - Game_LoadEnd_Trigger occurs before loading another location via a trigger (after calling Game_LoadEnd_ChangeLevel)
    - Game_Pause occurs when a game is paused.
    - Game_Unpause occurs when the game resumes.
    - Game_DefineExternals occurs before initializing external script functions.

    Program code implementation:
    These functions will be basic in writing any plugin. Therefore, within the framework of the current tutorial, I propose to check how they work.
    When entering the main menu, we will output a message with the name of the current plugin, version of Union and the current engine.
    We will use the Game_Init function.
    C++:
    // The function returns the name of the engine based on its version.
    string GetEngineVersionName(TEngineVersion version) {
      switch( version ) {
      case Engine_G1:  return "Gothic I Classic";
      case Engine_G1A: return "Gothic I Addon";
      case Engine_G2:  return "Gothic II Classic";
      case Engine_G2A: return "Gothic II Addon";
      }
      return "Unknown";
    }
    
    void Game_Init() {
    
      // We get a pointer to the current
      // plugin and get its name
      const CPlugin* plugin = CPlugin::GetCurrentPlugin();
      string pluginName = plugin->GetName();
    
      // Get the instance version of the union
      // and convert it to a text string.
      TVersion unionVersion = Union.GetUnionVersion();
      string unionVersionName = unionVersion.ToString();
    
      // Get the version of the engine and its name
      TEngineVersion engineVersion = Union.GetEngineVersion();
      string engineVersionName = GetEngineVersionName(engineVersion);
    
      // Show a message on the screen
      Message::Info( string::Combine(
        "Plugin name: %s\nUnion version: %s\nEngine version: %s",
        pluginName,
        unionVersionName,
        engineVersionName
      ) );
    }



    Compilation and running:
    In the project 'Configuration manager', set 'Release'. We collect the project and add it to the ini file. We start the game and wait for the menu to load.



    Result:
    73950
    73951

    73952
    73949

 
Последнее редактирование модератором:

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.034
Благодарности
1.798
Баллы
240
Поэтому в рамках текущего туториала предлагаю проверить их работоспособность.
Что насчет Game_Exit()? Могут ли к моменту этого события статические поля самописных объектов быть уничтожены? Или я где-то накосячил?
C++:
#include "UnionAfx.h"
#include "CGlobalEventManager.h"
#include <Windows.h>

void Game_Entry() {
}

using namespace Gothic_II_Addon;

void Game_Init() {
}

void Game_Exit() {
    Beep(500, 500);
    Beep(1000, 500);
    CGlobalEventManager::Raise(CGlobalEventManager::EXIT);
    Beep(1500, 500); // третьего гудка не будет
    Sleep(10000); // как и 10-секундной задержки
}
C++:
#pragma once
#include <hash_map>
#include <vector>
#include <functional>

class CGlobalEventManager
{
public:
    typedef enum GameEvent
    {
        NONE,
        ENTRY,
        INIT,
        EXIT,
        LOOP,
        SAVE_BEGIN,
        SAVE_END,
        LOAD_BEGIN,
        LOAD_END,
        LOAD_BEGIN_TRIGGER,
        LOAD_END_TRIGGER,
        PAUSE,
        UNPAUSE,
        DEFINE_EXTERNALS
    };

private:
    static std::hash_map<GameEvent, std::vector<std::function<void()>* > > events;
    CGlobalEventManager();

public:
    static void AddListener(GameEvent event, std::function<void()>& listener);
    static void RemoveListener(GameEvent event, std::function<void()>& listener);
    static void Raise(GameEvent event);
};
C++:
#include "CGlobalEventManager.h"
#include <algorithm>
#include "UnionAfx.h"

std::hash_map<CGlobalEventManager::GameEvent, std::vector<std::function<void()>* > > CGlobalEventManager::events;

void CGlobalEventManager::AddListener(CGlobalEventManager::GameEvent event, std::function<void()>& listener)
{
    //auto& callbacks = events[event];
    //auto where = callbacks.end();
    //callbacks.insert(where, &listener);
}

void CGlobalEventManager::RemoveListener(CGlobalEventManager::GameEvent event, std::function<void()>& listener)
{
    //auto& callbacks = events[event];
    //auto it = std::find(callbacks.begin(), callbacks.end(), &listener);
    //callbacks.erase(it);
}

void CGlobalEventManager::Raise(CGlobalEventManager::GameEvent event)
{
    auto& callbacks = events[event]; // Access Vioalation в недрах hash_map
    //std::for_each(callbacks.begin(), callbacks.end(), [](std::function<void()>* callback) { (*callback)(); });
}
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.276
Благодарности
4.579
Баллы
625
Что насчет Game_Exit()
Game_Exit происходит в момент завершения WinMain движка и начале отключения Union от процесса. Деструкторы плагинов будут вызываться раньше, поэтому для твоей задачи имеет смысл добавить код в DllMain в условие DLL_PROCESS_DETACH.
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.276
Благодарности
4.579
Баллы
625
Cbrhex, эта ошибка говорит о том, что скомпилированный модуль ссылается на символ, описанный в .lib файле, но отсутствующий (вообще или отличается по сигнатуре) в dll интнрфейсе. Функция, которую пытается вызвать твой плагин, создаёт расширение в нативном dynamic_cast рантайма, для которого откомпилирована библиотека.
Предположу, что если все установлено верно, ошибка может заключаться в параметрах сборки проекта (/MD или /MT например), версии платформы crt и тд.
То есть чтобы понять природу ошибки, опиши подробно какой Union стоит, какая версия плагина, какой версией компилятора собирал проект, путь к игре, хеш ехе файла игры и выложи сюда shw32.dll, shw32.lib из ProgramData/Union/SDK/<платформа>/lib.

Jr13San у тебя была такая ошибка, ты говорил что она сама прошла? Ее характер софтвеерный или я кривые файлы на сервер загрузил?
 

N1kX


Модостроитель
Регистрация
13 Ноя 2009
Сообщения
5.843
Благодарности
5.267
Баллы
910
Путь к игре видно C:\Program Files (x86)\Showball Interactive\Gothic\System
Виртуализация не помешает запускать плагины?
Потому как винда создает и помешает файлы в C:\Users\имя_пользователя\AppData\Local\VirtualStore\Program Files (x86) если у пользователя нет прав
Я бы лучше все переустановил на С:\Games
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.276
Благодарности
4.579
Баллы
625
Я ещё момент проглядел. Это что за запись?
Код:
[FILES]
dll=TestUnion.dll

Формат для подключения строго такой
Код:
[PLUGINS]
PluginList = plugin1, plugin2, ...

Если игра запускается с ехешника, до добавляем плагин в Union.ini. если через стартер, то в запускаемый им ini.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
435
Благодарности
261
Баллы
230
у тебя была такая ошибка, ты говорил что она сама прошла? Ее характер софтвеерный или я кривые файлы на сервер загрузил?
Этого я уже не узнаю. Но тогда я делал сборку на SDK 1.0с, а вот в папке system или 1.0b или 1.0с стояла - не помню. Делал в торопях, поэтому мог и не обновиться до 1.0с.
А возможно просто lib файлы для SDK 1.0c были старыми от 1.0b.
В общем, если поставить Union 1.0d и SDK 1.0c, то всё должно заработать.
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.276
Благодарности
4.579
Баллы
625
Jr13San, ну вообще функция в ошибке существовала ещё до релизов, мне потому и интересно почему именно на не ругается. Я думаю дело в какой-то неправильной сборке проекта.
 

Cbrhex

Участник форума
Регистрация
5 Окт 2019
Сообщения
42
Благодарности
2
Баллы
100
Формат для подключения строго такой

Убрал dll=...

Сделал так:
Код:
[PLUGINS]
PluginList=Gothic1TestUnion

Ошибки нет, но


Код:
Message::Info( string::Combine(
    "Plugin name: %s\nUnion version: %s\nEngine version: %s",
    pluginName,
    unionVersionName,
    engineVersionName
  ) );

не отрабатывает.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
435
Благодарности
261
Баллы
230
Cbrhex, какой-то неполный код у вас.
Вот, пожалуйста, так всё прекрасно работает:
G1_MsgTest.jpg


Я думаю дело в какой-то неправильной сборке проекта.
Хз, сейчас не удаётся воспроизвести этот момент ни в каких комбинациях. Сборка плагина была всегда по умолчанию в 2012 студии с компилятором от 10-й студии.
 

Cbrhex

Участник форума
Регистрация
5 Окт 2019
Сообщения
42
Благодарности
2
Баллы
100
какой-то неполный код у вас

Я только часть привел.

У меня VS 2019 и билд на VS 2010 (v100)
Пост автоматически объединён:

Я бы лучше все переустановил на С:\Games

Да. Перенес Готику и все заработало. Спасибо.
 
Последнее редактирование:
Сверху Снизу