Наши партнеры
Реклама на сайте
Список тегов:
Плагины for скачать для cs 1.6 плагин WH Anti сервера winter новому (украшение году кс 1.0 Auto на Red не года со Шапки шапка of Honor Medal сайта strike Counter готовит Новые за или Clan портала сайтов Counter-Strike по новая uCoz Dead уже может GTA Crysis Call Free Kit Admin но c4 Античиты Русификатор новый Патч карты карта стала РС моды amx без Beta Spectator 1.3 1.4 пак ru Amx_super weapons online Игр new With 3ds (4.7Kb) models до Knife Death All Патчи игры playstation style Grenade smoke Advanced deagle (beta) .45ACP awp (w Dark Solid Bomb Blizzard UvBullets COD4 SAS -Style- arctic основе Разработчики Full patch начнется программы создание Creator LOGO лого 1.1 ЕА текста sprite (Rus) MOD Final еще один от мб) Скачать CS Duty v1.6 Steam NonSteam pack (Version v.1.0 Готовые сервера сервер and Edition Готовый zombie server By DeathRun Готовые сервера: JustMan 2011 Public 2010 Activision Jump Новогодний CSDM 1.4.4 под Reallite карт пакет сборник 35hp из Halo damage модели (request) Боты zbot Shield Replacement M7883s.308 Valmet m4a1 Модели игроков модель Модели оружия 47 Elite Зомби CT star gsg9 (1.15Mb) (1.39Mb) Camo WhitE Desert F1 база The fps Windows Нового против war media Prototype 40 игра google Effect часть Sony
Мини профиль
noavatar
Четверг - 23.12.2024 - 07:36
Логин:
Пароль:
Онлайн лист
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Гости сайта Пользователи

, [Полный список]
  • Сегодня нас посетили:
  • Статистика
    • Всего: 199
    • Новых за месяц: 0
    • Новых за неделю: 0
    • Новых вчера: 0
    • Новых сегодня: 0
    Статистика форума
    FrAiDeR Постов [ 314 ]
    123 Постов [ 162 ]
    _Soft_ Постов [ 47 ]
    ПаTиFoN Постов [ 26 ]
    L1nk Постов [ 21 ]
    Lis Постов [ 19 ]
    yandex Постов [ 19 ]
    SEK Постов [ 18 ]
    The-single Постов [ 14 ]
    Sipnein Постов [ 13 ]
    yan Постов [ 12 ]
    JlakocT Постов [ 11 ]
    ARBuuZ Постов [ 10 ]
    Artem Постов [ 7 ]
    SuPeRmEn Постов [ 5 ]
    Чат

    Главная » Статьи » Мои статьи

    Делаем плагины своими руками
    Делаем плагиныСоздание собственного плагина

    Создание плагина в CS не очень сложно, но, тем не менее, всё равно существует несколько issues, о которых часто забывают. В этой статье мы покажем, как Вы можете написать простой плагин и использовать его в своём приложении.
    Определение API плагина

    Первое, что Вам нужно сделать при создании плагина, - это определить для него API. API - это то, что Ваше приложение собирается использовать для общения с плагином. Это интерфейс к плагину, поэтому очень важно правильно его создать. В структуре CS Возможности Разделяемых Классов (@pxref{SCF}) используются для определения API. С этими возможностями Вы создаёте абстрактный интерфейс, содержащий только методы из API. Абстрактный класс в C++ означает, что все методы виртуальные. Это значит, что не задаётся реализация; только объявление методов. Реализация появится позже в коде плагина.

    Это понятие совершенно аналогично механизму интерфейса Java. Преимущество использования этой парадигмы в том, что у Вас есть чёткое разделение между API и реализацией. Это позволяет легко заменять реализацию какого-нибудь API или даже предоставлять несколько реализаций (например, софтверный и OpenGL'евский прорисовщики являются двумя реализациями одного и того же API 3D-прорисовки).

    Вот определение API для нашего простого плагина: #ifndef __GAME_MYAPI_H__
    #define __GAME_MYAPI_H__

    #include <csutil/scf.h>

    class csVector3;

    SCF_VERSION (iMyApi, 0, 0, 1);

    /**
    * Это API для нашего плагина. Рекомендуется использовать
    * комментарии получше этих в реальных ситуациях.
    */
    struct iMyApi : public iBase
    {
    /// Делать чего-нибудь.
    virtual void DoSomething (int param, const csVector3&) = 0;
    /// Получить чего-нибудь.
    virtual int GetSomething () const = 0;
    };

    #endif // __GAME_MYAPI_H__


    Текст выше следует поместить в заголовочный файл. Давайте поместим его в `myapi.h'.

    Сначала мы подключаем `csutil/scf.h'. Это заголовочный файл CS для SCF, откуда мы получим определение `iBase' и определение макро SCF_VERSION(). Затем мы объявляем `csVector3' как класс. Мы делаем это для того, чтобы можно было позже использовать `csVector3' как параметр в одном из методов нашего API. Нам не нужно полное определение `csVector3', т.к. мы собираемся определить метод так, чтобы он принимал вектор как ссылку (by reference).

    После этого мы используем макро SCF_VERSION() для определения версии этого интерфейса. Версии можно использовать для запроса определённой версии интерфейса. Это может оказаться полезным позже при расширении API, не нарушая существующие приложения. Версия состоит из трёх частей: главная, меньшая и наименьшая.

    В конце мы определяем API путём создания структуры, которая наследует от `iBase'. Мы используем `struct' вместо `class' просто потому, что для структур видимость по умолчанию является `открытой (public)', а не `закрытой (private)' (для классов наоборот). Это просто удобно. Других различий между `struct' и `class' в C++ нет (переводчик не согласен).

    Имя `iMyApi' не случайно. CS использует такое соглашение об именах (начинающихся с `i') для интерфейсов SCF потому, что так легче увидеть, что они ссылаются на интерфейсы SCF.

    Мы наследуем от `iBase', т.к. это основа всех интерфейсов SCF. Все интерфейсы SCF должны наследовать от `iBase' либо прямо, либо косвенно. Это удостоверит в том, что у нас есть подсчёт ссылок (подробнее об этом позже). Это также позаботится об внутренних issues of SCF.

    В этой структуре мы определяем два метода: DoSomething() и GetSomething(). Заметьте, что каждый метод определён следующим образом: virtual … = 0;


    `= 0' означает, что здесь не будет реализации. Реализация будет предоставлена плагином (см. дальше).

    Заметьте, что использование `const' во всех возможных местах является хорошей практикой. В объявлении GetSomething() мы добавили `const' в конце для указания, что этот метод не изменит объект. Это полезно по нескольким причинам:
    Это даёт больше информации читателю API.
    Хороший компилятор может делать некоторые оптимизации, если он знает, что метод не будет изменять объект.
    Создание реализации плагина (заголовочный файл)

    После определения API для плагина самое время написать фактическую реализацию. Сначала нужно определить заголовочный файл `myplug.h' со следующим содержимым: #ifndef __GAME_MYPLUG_H__
    #define __GAME_MYPLUG_H__

    #include <iutil/comp.h>
    #include <csgeom/vector3.h>
    #include <myapi.h>

    struct iObjectRegistry;

    /**
    * Это реализация для нашего API и плагина.
    */
    class MyPlugin : public iMyApi
    {
    private:
    iObjectRegistry* object_reg;
    csVector3 store_v;

    public:
    SCF_DECLARE_IBASE;

    MyPlugin (iBase* parent);
    virtual ~MyPlugin ();
    bool Initialize (iObjectRegistry*);

    virtual void DoSomething (int param, const csVector3&);
    virtual int GetSomething () const;

    struct Component : public iComponent
    {
    SCF_DECLARE_EMBEDDED_IBASE (MyPlugin);
    virtual bool Initialize (iObjectRegistry* r)
    { return scfParent->Initialize (r); }
    } scfiComponent;
    };

    #endif // __GAME_MYPLUG_H__


    Здесь нужно немного пояснить. Структура плагина CS требует, чтобы каждый названный класс SCF, который будет запрошен по имени из плагин-модуля с помощью менеджера/загрузчика плагинов CS, должен реализовывать интерфейс `iComponent'. Этот интерфейс имеет единственный метод Initialize(), которым класс будет инициализирован после it is instantiated. Это даёт единице шанс выполнить разные инициализирующие операции и предоставляет единице указатель на глобальный реестр объектов.

    Но наш плагин также нуждается в реализации своего собственного интерфейса `iMyApi'. Появилась ситуация, когда один класс должен реализовать два интерфейса в одно и то же время. Существует в основном два способа это сделать: множественное наследование и использование встроенного класса SCF. Мы используем здесь вторую технику, т.к. приложение становится более портируемым и работает лучше со старыми компиляторами, у которых могут возникнуть проблемы с поддержкой множественного наследования.

    В примере выше класс `MyPlugin' наследует от `iMyApi'. Методы из `iMyApi' реализованы прямо в `MyPlugin'. Для этого объявления методов из `iMyApi' скопированы в `MyPlugin', лишь убрано `= 0'. Для указания того, что этот класс представляет собой реализацию интерфейса SCF, нам также нужно макро SCF_DECLARE_IBASE(). Это макро позаботится об объявлении функций DecRef() и IncRef(), занимающихся подсчётом ссылок. Также макро объявляет QueryInterface(), так что можно запрашивать другие интерфейсы (как `iComponent') из этого класса. Вам нет нужды сильно беспокоится об этом.

    Заметьте, что `MyPlugin' нуждается в конструкторе, принимающем параметр `iBase*'. Иначе SCF не сможет instantiate этот класс.

    Для реализации `iComponent' мы добавляем внутренний класс `Component'. Этот новый класс будет наследовать прямо от `iComponent', что позволяет `MyPlugin' реализовывать `iComponent' косвенно. Т.к. это встроенный интерфейс, нам теперь нужно макро SCF_DECLARE_EMBEDDED_IBASE(). Это макро позаботится о том факте, что этот интерфейс на самом деле принадлежит родительскому классу (который передаётся как параметр). Оно делает единственную вещь: объявляет переменную `scfParent' типа `MyPlugin'. Через эту переменную реализации методов встроенного интерфейса могут получить доступ к главному классу. После объявления класса `Component' мы сразу же создаём её единицу `scfiComponent'. Это имя выбрано не случайно. Оно составлено из `scf' + имя интерфейса SCF, который реализует этот встроенный объект. Макро SCF_IMPLEMENTS_EMBEDDED_INTERFACE() (см. ниже) принимает имена именно в таком формате.

    Иногда встроенные классы также делаются друзьями (friend) главного класса, чтобы получить доступ к закрытой информации. В данном случае это не нужно. Единственное, что содержит `Component', - это метод Initialize(), возвращающий управление родительскому открытому методу Initialize().
    Создание реализации плагина (файл ресурсов)

    Теперь мы создадим главный файл ресурсов, содержащим реализацию нашего плагина. Давайте назовём его `myplug.cpp': #include <cssysdef.h>
    #include <myplug.h>
    #include <iutil/objreg.h>
    #include <iutil/plugin.h>

    CS_IMPLEMENT_PLUGIN

    SCF_IMPLEMENT_IBASE (MyPlugin)
    SCF_IMPLEMENTS_INTERFACE (iMyApi)
    SCF_IMPLEMENTS_EMBEDDED_INTERFACE (iComponent)
    SCF_IMPLEMENT_IBASE_END

    SCF_IMPLEMENT_EMBEDDED_IBASE (MyPlugin::Component)
    SCF_IMPLEMENTS_INTERFACE (iComponent)
    SCF_IMPLEMENT_EMBEDDED_IBASE_END

    SCF_IMPLEMENT_FACTORY (MyPlugin)

    MyPlugin::MyPlugin (iBase* parent) : object_reg(0)
    {
    SCF_CONSTRUCT_IBASE (parent);
    SCF_CONSTRUCT_EMBEDDED_IBASE (scfiComponent);
    }

    MyPlugin::~MyPlugin ()
    {
    SCF_DESTRUCT_EMBEDDED_IBASE (scfiComponent);
    SCF_DESTRUCT_IBASE ();
    }

    bool MyPlugin::Initialize (iObjectRegistry* r)
    {
    object_reg = r;
    return true;
    }

    void MyPlugin::DoSomething (int param, const csVector3& v)
    {
    // Просто какое-то поведение.
    if (param == 1)
    store_v = v;
    else
    store_v = -v;
    }

    int MyPlugin::GetSomething () const
    {
    return (int)store_v.x + (int)store_v.y + (int)store_v.z;
    }



    Первое макро - это CS_IMPLEMENT_PLUGIN(). Оно указывает структуре CS, что этот модуль является плагином (а не приложением или библиотекой). На некоторых платформах это на самом деле составляет разницу; на других нет. Для лучшей портируемости Вам следует использовать это макро лишь в одном файле C++ для каждого плагин-модуля.

    SCF_IMPLEMENT_IBASE() описывает, какие интерфейсы реализует класс `MyPlugin'. Этот раздел сообщает, что `MyPlugin' реализует `iMyApi' напрямую, а `iComponent' через встраивание.

    SCF_IMPLEMENT_EMBEDDED_IBASE() перечисляет интерфейсы, реализуемые классом `MyPlugin::Component' (встроенный класс). В данном случае `MyPlugin::Component' реализует лишь `iComponent'.

    Важно правильно использовать вышеназванные макро. Эти макро удостоверяют, что реализация для IncRef(), DecRef() и QueryInterface() предоставляется Вашим собственным классом.

    SCF_IMPLEMENT_FACTORY() сообщает, что класс C++ `MyPlugin' представляет собой фабрику SCF, позволяющую SCF instantiate объекты из этого класса. В дополнение к некоторым административным задачам это макро определяет функцию, способную создавать объекты класса `MyPlugin'. Заметьте, что один плагин-модуль может определять несколько различно названных классов SCF. В этом случае Вам нужно несколько строк SCF_IMPLEMENT_FACTORY(); по одной для каждого экспортированного класса SCF.

    SCF_IMPLEMENT_…_IBASE() и SCF_IMPLEMENT_FACTORY() являются макро SCF обычного назначения. Поэтому они не относятся специально к плагин-модулям, но скорее помогают определить классы SCF.

    В конструкторе `MyPlugin' Вы должны вызвать SCF_CONSTRUCT_IBASE() для главного интерфейса и SCF_CONSTRUCT_EMBEDDED_IBASE() для каждого встроенного интерфейса. Эти макро удостоверят, что объект инициализируется правильно (т.е. количество ссылок установлено в 1 и т.д.). В деструкторе Вы должны вызвать соответствующие макро SCF_DESTRUCT_IBASE() и SCF_DESTRUCT_EMBEDDED_IBASE().

    Остальная часть плагина ясна. Важно понимать, что Вам следует делать большинство инициализации плагина в функции Initialize(), а не в конструкторе. Причина этого в том, что во время создания Вы не можете зависеть от уже полностью готовой структуры CS. Также при вызове Initialize() Вы получаете указатель на реестр объектов, необходимый для расположения других модулей и плагинов, загружаемых структурой CS.
    Сообщаем SCF о своём плагине

    SCF находят плагины автоматически и динамически. SCF определяют, какие модули определяют какие классы SCF, просматривая мета-информацию, ассоциированную с каждым плагином. Файл мета-информации для Вашего плагина должен иметь то же имя, что и Ваш собранный плагин, но с расширением `.csplugin'. Например, если плагин из примера собран с именем `myplugin.so' (Unix) или `myplugin.dll' (Windows), ассоциированный файл мета-информации можно встроить прямо в плагин-модуль, если это поддерживается платформой и встраивание включено. Иначе файл `.csplugin' будет располагаться рядом с собранным плагин-модулем.

    Файл мета-информации - это документ XML-формата. Он может содержать любую информацию, относящуюся к плагин-модулю; он не ограничен содержать лишь информацию SCF. Сами SCF ожидают найти узел с именем <scf>, содержащий SCF-информацию о плагин-модуле.

    Файл мета-информации `myplugin.csplugin' для нашего плагин-модуля может выглядеть так:
    <!-- myplugin.csplugin -->
    <plugin>
    <scf>
    <classes>
    <class>
    <name>crystalspace.mygame.myplugin</name>
    <implementation>MyPlugin</implementation>
    <description>My Special Game Plugin</description>
    <requires>
    <class>crystalspace.graphics3d.</class>
    </requires>
    </class>
    </classes>
    </scf>
    </plugin>


    Каждый названный класс SCF, экспортируемый плагином, должен быть представлен в узле <class> в пределах группы <classes>. Каждый класс имеет: <name> - SCF-имя класса; <implementation> - имя класса C++, реализующего класс SCF; <description> и необязательный узел <requires>, перечисляющий другие классы SCF, от которых зависит этот класс. В группе <requires> может находится любое количество классов. Если Ваш плагин зависит лишь от определённого типа класса, чем от определённого класса SCF, то Вы перечисляете лишь часть желаемого типа класса, как показано в нашем примере (где мы хотим любой 3D-прорисовщик).
    Компиляция плагина

    В зависимости от используемых средств разработки Вам следует обратиться к одному из HOWTO по теме сборки внешних модулей CS.
    @ref{HOWTO Проект CS}
    @ref{HOWTO Создание внешнего приложения с использованием KDevelop}
    @ref{HOWTO Создание внешнего приложения с использованием MSVC 7}
    @ref{HOWTO Создание внешнего приложения с использованием MSVC 6}
    Загрузка плагина в приложении

    Сначала подключим заголовочный файл, определяющий API нашего плагина: #include <myapi.h>


    Не подключайте заголовочный файл `myplug.h', т.к. это реализация плагина, которую Вы не должны использовать напрямую. Если Вы так делаете, то пропадает весь смысл создания плагинов.

    Для загрузки плагина существует несколько возможностей. Вы можете загрузить плагин вручную с помощью CS_LOAD_PLUGIN(): csRef<iPluginManager> plugin_mgr =
    CS_QUERY_REGISTRY (object_reg, iPluginManager);
    csRef<iMyApi> myapi = CS_LOAD_PLUGIN (plugin_mgr,
    "crystalspace.mygame.myplugin", iMyApi);
    if (myapi.IsValid())
    {

    }


    Это получит менеджера плагинов из реестра объектов. Это модуль, ответственный за загрузку и выгрузку плагинов. Затем код использует менеджера плагинов для загрузки модуля. Заметьте, что эта загрузка может провалиться. Вам следует всегда проверять возвращаемое значение, чтобы оно было верным.

    Другой способ загрузки плагина заключается в использовании метода RequestPlugins(), вызываемого во время инициализации: if (!csInitializer::RequestPlugins (object_reg,
    CS_REQUEST_VFS,
    CS_REQUEST_SOFTWARE3D,
    CS_REQUEST_ENGINE,

    CS_REQUEST_PLUGIN("crystalspace.mygame.myplugin", iMyApi),
    CS_REQUEST_END))
    {

    }


    csRef<iMyApi> myapi = CS_QUERY_REGISTRY (object_reg, iMyApi);


    Этот способ имеет несколько преимуществ. Во-первых, он позволяет пользователю переопределить Ваш плагин в командной строке или в файле настроек (если у Вашей программы имеется таковой). Когда существует много реализаций одного и того же API, то это может быть важным рассмотрением. In cases where there are multiple implementations for the same API this can be an important consideration. It is by doing this, for example, that it is possible to switch between software and OpenGL renderers with the command-line `--video=' option, or via configuration file.

    Во-вторых, он регистрирует плагин в реестре объектов так, чтобы было легче найти Ваш модуль позже. Это также позволяет другим плагинам найти Ваш плагин путём запроса в реестре объектов.
    Использование плагина в приложении

    После загрузки плагина Вы можете использовать плагин просто путём вызова методов, определённых в API: myapi->DoSomething (1, csVector3 (2, 3, 4));
    printf ("%d\n", myapi->GetSomething ());


    Это должно вывести 9.
    Категория: Мои статьи | Добавил: FrAiDeR (02.03.2011)
    Просмотров: 847 | Рейтинг: 0.0/0
    Всего комментариев: 0
    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]