Архитектура Pawn(SA-MP) проекта, часть 1

В этой статье я хочу посоветовать Pawn скриптерам то, как нужно устраивать внутреннюю часть вашего SA-MP проекта. Далеко не секрет, что большая часть скриптеров хранят свои режимы всего в одном .pwn файле. Эта мода пошла от режимов Pen и The Godfather. В действительности, это решение является довольно непрактичным, ибо при увеличении количества кода, его сложность значительно увеличивается.

Чтобы избежать усложнения проекта при его расширении, вам достаточно правильно спроектировать архитектуру вашего проекта до его реализации, или провести рефакторинг уже существующего проекта.

Подготовка

У нас есть каталог projects/, в котором будут храниться все наши Pawn проекты. Конечно, вам потребуется компилятор, лучше всего компилятор не хранить в каждом проекте, а вынести в отдельный каталог compiler/. Проекты будут храниться в каталогах projects/project_1, projects/project_2, где project_1 и project_2 — названия ваших проектов.

В итоге, площадка для разработки должна выглядеть следующим образом:

compiler
    includes
        a_actor.inc
        a_http.inc
        a_npc.inc
        a_objects.inc
        a_players.inc
        a_sampdb.inc
        a_samp.inc
        a_vehicles.inc
        core.inc
        datagram.inc
        file.inc
        float.inc
        string.inc
        time.inc
        libpawnc.so
        pawncc
        pawncc.exe
        pawncc.pdb
        pawnc.dll
        pawnc.pdb
project_1
project_2

Я рекомендую использовать версию Pawn компилятора от Zeex. Эта версия отличается исправлением некоторых ошибок, введением небольшого количества новых возможностей и версией для Linux.

Проектирование проекта

Для тестирования удобно будет расположить сервер в каталоге с проектом, поэтому файлы samp-npc.exe, samp-server.exe, server.cfg и каталоги gamemodes/ scriptfiles/, plugins/ будут расположены в каталоге проекта.

Исходные коды проекта будут размещаться в отдельном каталоге sources/.

Для компиляции проекта нам понадобится исполняемый файл make.bat для Windows и make.sh для Linux, создайте файл make.bat и make.sh в каталоге проекта и поместите туда, изменив в нём имя проекта project_1 на своё, следующее содержимое:

make.bat:

set name=project_1
..\compiler\pawncc.exe -;+ -(+ -i../compiler/includes sources\%name%.pwn
if exist %name%.amx ^
move %name%.amx gamemodes\
pause

make.sh:

#/bin/bash
 
export LD_LIBRARY_PATH=$(pwd)"/../compiler/:$LD_LIBRARY_PATH"
 
NAME=project_1
 
./../compiler/pawncc "-;+" "-(+" "-i../compiler/includes" sources/$NAME.pwn
 
if [ $(stat -c%s "$NAME.amx") -gt 0 ];
then
	mv $NAME.amx gamemodes/
else
	rm $NAME.amx
fi

В итоге, мы получили такую структуру проекта:

project_1
    gamemodes
    plugins
    scriptfiles
    sources
    make.bat
    make.sh
    samp-npc.exe
    samp-server.exe
    server.cfg

Далее необходимо создать шаблон структуры файлов проекта. Я порекомендую наиболее полный, по моему мнению, вариант.

  • Каталог admin/, в котором будут расположены все функции администратора. Файлы: admin/commands.inc, admin/menu.inc, admin/kick.inc, admin/ban.inc, admin/jail.inc, admin/mute.inc. В принципе, по их названию понятно, что в них должно находиться. Ещё нужен файл admin/admin.inc, который будет объединять всё из admin/. И в котором будут находиться небольшие функции, которые не заслужили отдельного файла.
  • Каталог core/, в котором будут храниться функции, связанные с ядром системы. Например, там может находится файл core/log.inc, в котором будет реализована система логирования. Файл core/lang.inc, в котором будет реализована система многоязычности.
  • Каталог gang/, в котором расположены все функции, связанные с бандой. Файлы: gang/commands.inc, gang/menu.inc, gang/level.inc. Ещё нужен файл gang/gang, который будет объединять всё из gang/. И в котором будут находиться небольшие системки, которые не заслужили отдельного файла.
  • Каталог lang/, в котором будут храниться различные локали, нужные при компиляции.
  • Каталог lib/, в котором расположены библиотеки с функциями, такие как mxINI.inc, a_mysql.inc, foreach.inc и другие.
  • Каталог player/, в котором расположены все функции игрока. Файлы: player/commands.inc, player/menu.inc, player/level.inc, player/vip.inc. Ещё нужен файл player/player.inc, который будет объединять всё из player/. И в котором будут находиться небольшие системки, которые не заслужили отдельного файла. А системы, которые можно (и нужно) разделить следует поместить в подкаталог, например player/weapon/, в котором будут находиться файлы, с реализацией системы оружия игрока (например: drop.inc, skill.inc, weapon.inc).
  • Каталог protect/, в котором будут расположены различные античиты и защиты. Вот примерный список: protect/armour.inc, protect/chat.inc, protect/health.inc, protect/idle.inc, protect/jetpack.inc, protect/ping.inc, protect/rconlogin.inc, protect/speed.inc, protect/weapon.inc.
  • Каталог quest/, в котором расположены скрипты всех заданий режима(например: quest/trucker.inc, quest/cashbox.inc). И файл quest/quest.inc, в котором расположена сама система заданий и которая объединяет все задания из каталога quest/.
  • Каталог services/, в котором расположены услуги режима, которые доступны игроку. Например: services/bank.inc, services/business.inc, services/housing.inc, services/skinshop.inc, services/weaponshop.inc, services/bar.inc, services/fastfood.inc, services/lottery.inc, services/vehshop.inc
  • Каталог system/, в котором расположены системы режима, не вошедшие в другие категории. Например: system/deathmath.inc, system/race.inc, system/weather.inc.
  • Каталог vehicle/, в котором расположены функции, для работы с транспортом. Например: vehicle/damage.inc, vehicle/menu.inc, vehicle/radio.inc. Ещё нужен файл vehicle/vehicle.inc, который будет объединять всё из vehicle/.
  • Нам обязательно понадобится файл config.inc, в котором будут храниться все настройки режима.
  • Ну и конечно файл project_1.pwn — это главный файл проекта, который объединяет все системы в единое целое.

Получается такое дерево структуры файлов проекта:

gamemodes
plugins
scriptfiles
sources
    admin
        admin.inc
        ban.inc
        commands.inc
        jail.inc
        kick.inc
        mute.inc
        menu.inc
    core
        db.inc
        lang.inc
        log.inc
        world.inc
    gang
        level.inc
        commands.inc
        menu.inc
        gang.inc
    lang
        en.inc
        ru.inc
    lib
        a_mysql.inc
        foreach.inc
        mxINI.inc
    player
        weapon
            drop.inc
            skill.inc
            weapon.inc
        commands.inc
        menu.inc
        level.inc
        vip.inc
        player.inc
    protect
        armour.inc
        chat.inc
        health.inc
        idle.inc
        jetpack.inc
        ping.inc
        rconlogin.inc
        speed.inc
        weapon.inc
    quest
        cashbox.inc
        trucker.inc
        quest.inc
    services
        bank.inc
        bar.inc
        business.inc
        fastfood.inc
        housing.inc
        lottery.inc
        skinshop.inc
        vehshop.inc
        weaponshop.inc
    system
        deathmath.inc
        race.inc
        weather.inc
    vehicle
        damage.inc
        menu.inc
        radio.inc
        vehicle.inc
    config.inc
    project_1.pwn
announce.exe
make.bat
make.sh
samp-npc.exe
samp-server.exe
server.cfg

Послесловие

Предложенный метод расположения файлов проекта не является истинно правильным, все, предложенные здесь, решения можно оспорить и, возможно, улучшить. Но построение архитектуры проекта на основе предложенной модели будет много лучше чем то, что наблюдается в настоящее время в большинстве проектов.

  • Avertus

    Когда будет продолжение? А то вот например интересно, как поступить если в разных системах, расписанных в разных файлах, необходимо применить один и тот же callbacks.

    • Писать пока не начал, ибо нет времени, но когда-нибудь точно напишу.
      Чтобы применить один и тот же callback в разных скриптах, достаточно подключить библиотеку с этим callback’ом к каждому файлу.

      • Avertus
        • public OnGameModeInit должен быть объявлен только один раз.

          • Avertus

            Получается в отдельных файлах можно создавать только отдельные функции, с вызываемыми функциями можно работать только в основном файле? Я попробовал с другими калбеками, получается та же самая ошибка.

          • Любые функции(автовызываемые в том числе) должны быть в единственном экземпляре. Область видимости функции(и не только) можно ограничить на один файл, используя static.

  • Nsony

    Подскажите пожалуйста с чего начать изучение языка pawn?
    Заранее спасибо.

  • Sergey

    Я зделал все как в уроке, но когда добавляю в инклуд callback и пытаюсь его вызвать из главного файла выдает: error 017
    Это случается только если перед инклудом в котором callback подключен другой инклуд.

    • Странно. Покажите код и я попробую вам помочь.

      • Sergej

        Спасибо, но проблему я уже решил. Компилятору не понравилось то что длина имени одной папки превышала 12 символов.

  • Nsony

    Жду продолжения.

  • Sportik

    Хорошая статья, жду продолжения.
    Внес в закладочки :)

  • reAL

    ??? compiler
    ? ??? includes
    ? ? ??? a_http.inc
    ? ? ??? a_mysql.inc
    ? ? ??? a_npc.inc
    ? ? ??? a_objects.inc
    ? ? ??? a_players.inc
    ? ? ??? a_sampdb.inc
    ? ? ??? a_samp.inc
    ? ? ??? a_vehicles.inc
    ? ? ??? core.inc
    ? ? ??? datagram.inc
    ? ? ??? file.inc
    ? ? ??? float.inc
    ? ? ??? string.inc
    ? ? ??? time.inc
    ? ??? libpawnc.dll
    ? ??? pawncc.exe
    ? ??? pawnc.dll
    ??? project_1
    ??? project_2

    Исправь «includes» на «include»

  • reAL

    Так и не понял, зачем разделять, как оно работает?

    • Затем, чтобы каждая подсистема находилась в своём файле. Такая структуризация упростит модификацию проекта, нахождение в нём ошибок и улучшит внутреннюю красоту кода.

      • reAL

        Подскажи, как подключать тогда из таких папок в основной мод)

        • #include "player\level"
  • Для использования коллбеков в инклудах можно юзать «Hook:», кпримеру:
    мы работаем с инклудом account.inc, то можно использовать для диалогов:
    Hook:Account_OnDialogResponse( playerid, dialogid, response, listitem, inputtext[] )
    {
    return true;
    }

    • Hook доступен с библиотекой YSI, но мне не нравится такой подход.

      • А какой подход лучше тогда использовать?
        Послушав тебя я сейчас просто создавал функции отдельно в каждом инклуде и прописывал в главных коллбэках их вызов.. Посмотрев на код, понял, что этот метод дает контроль над порядком выполнения, что не мало важно)

        • Именно, порядок выполнения очень важен.
          Я рекомендую ориентироваться на Open-GTO, скоро выйдет новая версия, внутренняя архитектура которого будет очень похожа на представленную здесь.

          • Дима

            Когда уже выйдет?

  • Когда будет продолжение статьи? :) Или Павно надоел?

    • По Pawn статей больше не будет, потому что сейчас я уже практически не программирую для SA-MP.

      • Печально =( так ждал статьи

  • Сможешь написать урок про использование MySQL? регистрация там и т.д. загрузку,сохранение,обновление про принцип работы и все как ты умеешь ) Чтоб подробно + и — =) И описание всех команд в MySQL в pawno)

Перейти к верхней панели