Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 9 из 9
  1. #1
    Аватар для Item
    Пользователь

    Статус
    Оффлайн
    Регистрация
    13.02.2016
    Сообщений
    27
    Репутация:
    4 ±

    Разбивка мода на инклуды

    Доброго воскресного вечера.Уже не первый день ломаю голову при попытках разбить мод на файлы (инклуды)

    На данный момент есть мод (name.pwn) с десятью тысячами строк кода.
    Допустим в нем уже реализованы следующие системы: система домов, система бизнесов, администратирования, регистрации а также авторизации
    Нужно этот мод разбить на файлы, так, чтобы у нас получилось примерно так:
      Открыть/закрыть


    Отсюда вырисовывается содержание файла name.pwn следующим:
      Открыть/закрыть


    И вроде бы все идет хорошо.Но, вся проблема в том, что почти в каждом из этих инклудов нам придется использовать одни и те же колбэки.
    Поясню на примере OnGameModeInit:
    global/house.inc - это может быть отправка запроса на подгрузку домов, или же создание иконок на мини-карте
    global/business.inc - та же самая картина: банальная загрузка или же какая-то дополнительная сортировка домов по их нумерации
    player/register.inc - подгрузка объектов из которых составлена сцена выбора внешности персонажа

    Эту цепь можно продолжать бесконечно; вариантов целая куча.Но что делать?Мы же не можем в каждом инклуде объявлять этот колбэк.Компилятор нам этого не позволит, указав в окошке, что этот колбэк уже используется и второй раз его нельзя загрузить

    Была еще идея: в самом моде (name.pwn) создать колбэк OnGameMode и в нем уже указать на функции, которые мы будем использовать в инклудах (файлах) (наверняка я тут не правильно выразился, поэтому на всякий случай прикреплю скриншот)
      Открыть/закрыть

    Вроде бы не плохо придуманно, но все равно это доставляет дискомфорт; происходит путаница

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

    Пробовал даже пользоваться сторонними скриптами (y_hooks) и даже плагинами (название точное не помню), но в этом случае, если к примеру нужно будет перехватить колбек OnPlayerPickUpDynamicPickup компилятор выведет ошибку, точно не помню, но знаю точно, что в ней указывается про обрезания названия колбека до 31 символа





    Других вариантов я нашел.
    Как быть, как создать идеальную "архитектуру" проекта и быть уверенным в том, что это все не посыпется при онлайне?

  2. #2
    Аватар для Desulaid
    лесоруб продакшен

    Статус
    Оффлайн
    Регистрация
    15.03.2015
    Адрес
    Slobodskoy
    Сообщений
    667
    Репутация:
    236 ±
    Я всегда выносил только функции, а вызов функций был в самом моде. Если понадобится какой-нибудь колбэк, то перехватим его

    PHP код:
    public OnGameModeInit()
    {
        
    /* код */
    #if defined hook_OnGameModeInit
        
    return hook_OnGameModeInit();
    #else
        
    return 1;
    #endif
    }
    #if defined _ALS_OnGameModeInit
        #undef OnGameModeInit#else
    #else
        #define _ALS_OnGameModeInit
    #endif
    #define OnGameModeInit hook_OnGameModeInit
    #if defined    hook_OnGameModeInit
    forward hook_OnGameModeInit();
    #endif 

  3. #3
    Аватар для Item
    Пользователь

    Статус
    Оффлайн
    Регистрация
    13.02.2016
    Сообщений
    27
    Репутация:
    4 ±
    Цитата Сообщение от Untonyst Посмотреть сообщение
    Я всегда выносил только функции, а вызов функций был в самом моде. Если понадобится какой-нибудь колбэк, то перехватим его

    PHP код:
    public OnGameModeInit()
    {
        
    /* код */
    #if defined hook_OnGameModeInit
        
    return hook_OnGameModeInit();
    #else
        
    return 1;
    #endif
    }
    #if defined _ALS_OnGameModeInit
        #undef OnGameModeInit#else
    #else
        #define _ALS_OnGameModeInit
    #endif
    #define OnGameModeInit hook_OnGameModeInit
    #if defined    hook_OnGameModeInit
    forward hook_OnGameModeInit();
    #endif 
    В этом случае же подключенные файлы начнут читаться снизу вверх?
    Или я все таки ошибаюсь...
      Открыть/закрыть

    Первым прочтется command.inc,
    business,
    house,
    ...

  4. #4
    Аватар для Alexey_Nikiforov
    Уровень знаний 1

    Статус
    Оффлайн
    Регистрация
    12.02.2016
    Сообщений
    61
    Репутация:
    1 ±
    Цитата Сообщение от Item Посмотреть сообщение
    В этом случае же подключенные файлы начнут читаться снизу вверх?
    Или я все таки ошибаюсь...
      Открыть/закрыть

    Первым прочтется command.inc,
    business,
    house,
    ...
    Я тоже пытался сделать что то подобное.
    Потом понял что это не тот язык.=)

    Лучшее что тут можно убрать в инклуды это макросы. объекты карты и транспорт.

    по перехвату вот тут читай http://pro-pawn.ru/showthread.php?10...BE%D0%B2%D1%8B

  5. #5
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Существует ещё один способ перехвата функций от Y_Less.

    Вот, собственно, он:
     Hook Method 4 (by Y_Less):
    PHP код:
    /*
    These functions need to appear just ONCE ever, regardless of how many hooked
    functions you have (so could be in another file already).  They define our two
    states "hooked" and "unhooked", and a function for fall-back when neither state
    is defined.  They also might not appear in the final AMX, but I've not tested
    this theory (the lack of the "public" keyword MIGHT invoke the correct bug, I
    don't know about with states).  Anyway, these functions are never used.
    */

    #include <a_samp>

    #if !defined _ALS_
        
    forward public _ALS_();
        
    _ALS_()<_ALS_:unhooked>{}
        
    _ALS_()<_ALS_:hooked>{}
        
    _ALS_()<>{}
    #endif

    /*
    Here is our first hooked function, it is always a good idea to hook a ScriptInit
    function so you can set "_ALS_" state, but you can also do that in any other
    callback, this is just merely a small optimisation.
    */
    public OnGameModeInit()
    {
        
    /*
        Now we set "_ALS_" to "hooked".  It doesn't matter if the next function in
        the chain exists or not (we don't even need to check), just set it to
        hooked and leave it.  In fact, given that other libraries may have come
        before yours, it might already be set to "hooked", but that's fine - we can
        just set it again.
        */
        
    state _ALS_:hooked;
        
    /*
        Now call the next callback in the chain.  This previously required
        "CallLocalFunction" to avoid a compiler error if the function didn't exist,
        but not any more - this is the major improvement in this method over other
        methods as you will see shortly.
        */
        
    return Streamer_OnGameModeInit();
    }

    /*
    Forward the next callback in the chain.
    */
    forward Streamer_OnGameModeInit();

    /*
    Normal ALS redefinition checks, see previous topics on ALS for more information.
    */
    #if defined _ALS_OnGameModeInit
        #undef OnGameModeInit
    #else
        #define _ALS_OnGameModeInit
    #endif

    /*
    Now this is where it starts getting interesting.  Because we call
    "Streamer_OnGameModeInit" (obviously "Streamer_" is our example library here),
    the code MUST include this function, but because we are hooking it, we don't
    know if it will or not - so we create it!  But if we create it, then what's in
    the next library (or mode) in the chain?  The answer is they are BOTH the next
    function in the chain thanks to the magic of states.

    The first line here defines "OnGameModeInit" when "_ALS_" is set to "unhooked",
    this is actually NEVER the case, but is still important as it tells the compiler
    which automata our "Streamer_OnGameModeInit" function is controlled by (i.e.
    "_ALS_").  The second line is the "fallback" function - if "_ALS_" is in a state
    for which there is no specific implementation, this one will get called instead
    and just instantly return (maybe you can now see the trick).  If "_ALS_" is set
    to "hooked" and there is no other function, then the compiler will identify this
    fallback function as the correct one to call (or the runtime will rather), if
    the hooked function DOES exist, then that more specialised version will be
    called instead.

    If we didn't have the "_ALS_:unhooked" function, then the fallback wouldn't work
    as what's it falling back from?
    */
    public Streamer_OnGameModeInit() <_ALS_:unhooked> return 1;
    public 
    Streamer_OnGameModeInit() <> return 1;

    /*
    Because we are now using states, we need a slightly more complex redefinition of
    the next callback's function definition - we need to transparently add the
    "_ALS_:hooked" state to it, so that's exactly what this line does.

    Remember that "_ALS_" only needs to be defined once in a mode ever, and
    "Streamer_" is just the standard unique ALS prefix I've used in this example -
    it needs to be unique.
    */
    #define OnGameModeInit(%0) Streamer_OnGameModeInit(%0)<_ALS_:hooked> 


    Results:
      Открыть/закрыть







  6. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    Существует ещё один способ перехвата функций от Y_Less.

    Вот, собственно, он:
     Hook Method 4 (by Y_Less):
    PHP код:
    /*
    These functions need to appear just ONCE ever, regardless of how many hooked
    functions you have (so could be in another file already).  They define our two
    states "hooked" and "unhooked", and a function for fall-back when neither state
    is defined.  They also might not appear in the final AMX, but I've not tested
    this theory (the lack of the "public" keyword MIGHT invoke the correct bug, I
    don't know about with states).  Anyway, these functions are never used.
    */

    #include <a_samp>

    #if !defined _ALS_
        
    forward public _ALS_();
        
    _ALS_()<_ALS_:unhooked>{}
        
    _ALS_()<_ALS_:hooked>{}
        
    _ALS_()<>{}
    #endif

    /*
    Here is our first hooked function, it is always a good idea to hook a ScriptInit
    function so you can set "_ALS_" state, but you can also do that in any other
    callback, this is just merely a small optimisation.
    */
    public OnGameModeInit()
    {
        
    /*
        Now we set "_ALS_" to "hooked".  It doesn't matter if the next function in
        the chain exists or not (we don't even need to check), just set it to
        hooked and leave it.  In fact, given that other libraries may have come
        before yours, it might already be set to "hooked", but that's fine - we can
        just set it again.
        */
        
    state _ALS_:hooked;
        
    /*
        Now call the next callback in the chain.  This previously required
        "CallLocalFunction" to avoid a compiler error if the function didn't exist,
        but not any more - this is the major improvement in this method over other
        methods as you will see shortly.
        */
        
    return Streamer_OnGameModeInit();
    }

    /*
    Forward the next callback in the chain.
    */
    forward Streamer_OnGameModeInit();

    /*
    Normal ALS redefinition checks, see previous topics on ALS for more information.
    */
    #if defined _ALS_OnGameModeInit
        #undef OnGameModeInit
    #else
        #define _ALS_OnGameModeInit
    #endif

    /*
    Now this is where it starts getting interesting.  Because we call
    "Streamer_OnGameModeInit" (obviously "Streamer_" is our example library here),
    the code MUST include this function, but because we are hooking it, we don't
    know if it will or not - so we create it!  But if we create it, then what's in
    the next library (or mode) in the chain?  The answer is they are BOTH the next
    function in the chain thanks to the magic of states.

    The first line here defines "OnGameModeInit" when "_ALS_" is set to "unhooked",
    this is actually NEVER the case, but is still important as it tells the compiler
    which automata our "Streamer_OnGameModeInit" function is controlled by (i.e.
    "_ALS_").  The second line is the "fallback" function - if "_ALS_" is in a state
    for which there is no specific implementation, this one will get called instead
    and just instantly return (maybe you can now see the trick).  If "_ALS_" is set
    to "hooked" and there is no other function, then the compiler will identify this
    fallback function as the correct one to call (or the runtime will rather), if
    the hooked function DOES exist, then that more specialised version will be
    called instead.

    If we didn't have the "_ALS_:unhooked" function, then the fallback wouldn't work
    as what's it falling back from?
    */
    public Streamer_OnGameModeInit() <_ALS_:unhooked> return 1;
    public 
    Streamer_OnGameModeInit() <> return 1;

    /*
    Because we are now using states, we need a slightly more complex redefinition of
    the next callback's function definition - we need to transparently add the
    "_ALS_:hooked" state to it, so that's exactly what this line does.

    Remember that "_ALS_" only needs to be defined once in a mode ever, and
    "Streamer_" is just the standard unique ALS prefix I've used in this example -
    it needs to be unique.
    */
    #define OnGameModeInit(%0) Streamer_OnGameModeInit(%0)<_ALS_:hooked> 


    Results:
      Открыть/закрыть






    Это устаревший способ, в нём перехватываемая функция вызывается перехватчиком не напрямую, а через автоматон, т.е. производится лишнее присваивание и переход на switch.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  7. #7
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±

  8. #8
    Аватар для franked
    Пользователь

    Статус
    Оффлайн
    Регистрация
    04.02.2015
    Адрес
    Ташкент епта
    Сообщений
    10
    Репутация:
    0 ±
    Я кстати тоже еще как-то летом обдумывал как же сделать не плохую структуру для проектирования. (В принципе до сих пор обдумываю,лол) И у меня в голове такая мысль появилась:

    Для каждой системы создавать свою папку. Например.
    1) Сперва я создам саму папку,где будут храниться системы,назову ее sys.
    2) Далее в этой папке,для каждой системы буду отдельно создавать еще папку. К примеру регистрация. - Registration.
    3) Дальше,уже в этой папке я буду писать регистрацию,разделяя весь код (что конечно не особо удобно,но я прижился с этим методом) по кускам. К примеру для переменных я отдельно создам файл vars.inc ,для public'ов,я также создам отдельный файл к примеру OnPlayerConnect.inc, где опишу свои действия. P.S я не публикую там заного сам public OnPlayerConnect. Я просто пишу код,который потом вставится посредством include в OnPlayerConnect. Просто процедурный код,т.к 2 раза паблик если объявить,сам знаешь что будет.
    4) Затем я открою new.pwn свой (это не понты) и там уже найду public OnPlayerConnect и пропишу там путь до файла OnPlayerConnect.inc оператором #include. //#include <sys/Registration/OnPlayerConnect>

    А переменные,вначале мода тоже: #include <sys/Registration/vars>

    Таким образом,я,зайдя в папку sys/Registration найду определенные куски кода,которые мне нужны по названию файла. И опять же таким образом,я пойму к какой системе что принадлежит,копаясь в new.pwn по папке Registration например,что там я подключаю переменные из системы регистрации.

    Например,я знаю,что у меня неполадки в OnPlayerConnect'e в такой-то системе. Я захожу в папку с системой,ищу файл OnPlayerConnect и роюсь там. (Конечно мне могут и понадобятся другие файлы из этой же системы,но я говорю,я прижился,мне в принципе удобно так делать,юзаю IDE Visual Studio,раскладываю все по вкладочкам и редактирую)

    Примечание: Строка подключения includ'a ограничена сколькими-то символами, не определил сколькими,но как-то у меня как раз не работала система регистрации,как раз один файлик не подключался из-за длинного названия системы,поэтому папки некоторые,а также файлы я сокращаю. И допустим если я хочу создать файл с функцией для той или иной системы,где функция будет иметь длинное название,к примеру: GetPlayerFullCoordinatesNowMotherFucker,то сам файл я назову: GetCoords или GetCrds. Я все равно буду знать к какой системе принадлежит эта функция если что,посмотрев в какой папке она находится. И рядом со строкой include в new.pwn я ставлю комментарий названия полной функции,чтобы потом через CTRL+F найти по комментарию название функции целиком,а там и рядом путь до подключаемого файла

    Кстати,именно поэтому я создал эту тему (клик). Мне удобнее все функции,связанные с mysql запросами хранить в отдельном файле (допустим название его mysql_queries),а в include с системой я пишу лишь функцию,которая сделает запрос.
    К примеру:

    Создам функцию SpawnGetData и опишу ее в файле mysql_queries,а в include с системой я пишу лишь SpawnGetData(playerid);

    Таким образом мой код в системе становится достаточно чистым,а если уж я и захочу посмотреть на сам запрос,то я открываю mysql_queries и копаюсь там. (Вообще он у меня на протяжении всей сессии открыт в отдельной вкладке,так что,далеко не бегаю).
    Последний раз редактировалось franked; 16.02.2016 в 23:56.

  9. #9
    Аватар для Alexey_Nikiforov
    Уровень знаний 1

    Статус
    Оффлайн
    Регистрация
    12.02.2016
    Сообщений
    61
    Репутация:
    1 ±
    Я работаю вот так и мне очень удобно.
    Отдельно макросы цветов.
    Объекты.
    Команды щас перенесу на DC_CMD отдельно.
    Возможно еще подумаю что и как разбить на файлы.

 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •