PDA

Просмотр полной версии : [Вопрос] Разбивка мода на инклуды



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

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

Отсюда вырисовывается содержание файла name.pwn следующим:
http://i.imgur.com/my55zzG.jpg

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

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

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

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

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





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

Desulaid
14.02.2016, 23:10
Я всегда выносил только функции, а вызов функций был в самом моде. Если понадобится какой-нибудь колбэк, то перехватим его


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

Item
14.02.2016, 23:18
Я всегда выносил только функции, а вызов функций был в самом моде. Если понадобится какой-нибудь колбэк, то перехватим его


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

В этом случае же подключенные файлы начнут читаться снизу вверх?
Или я все таки ошибаюсь...
http://i.imgur.com/my55zzG.jpg
Первым прочтется command.inc,
business,
house,
...

Alexey_Nikiforov
15.02.2016, 02:04
В этом случае же подключенные файлы начнут читаться снизу вверх?
Или я все таки ошибаюсь...
http://i.imgur.com/my55zzG.jpg
Первым прочтется command.inc,
business,
house,
...
Я тоже пытался сделать что то подобное.
Потом понял что это не тот язык.=)

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

по перехвату вот тут читай http://pro-pawn.ru/showthread.php?10447-%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%B2%D0%B0%D1%82-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B9-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D1%8B

VVWVV
15.02.2016, 03:09
Существует ещё один способ перехвата функций от Y_Less.

Вот, собственно, он:

/*
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:
http://ihost.pro-pawn.ru/image.php?di=BLEJ

http://ihost.pro-pawn.ru/image.php?di=408S

http://ihost.pro-pawn.ru/image.php?di=7YEJ

Daniel_Cortez
15.02.2016, 05:47
Существует ещё один способ перехвата функций от Y_Less.

Вот, собственно, он:

/*
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:
http://ihost.pro-pawn.ru/image.php?di=BLEJ

http://ihost.pro-pawn.ru/image.php?di=408S

http://ihost.pro-pawn.ru/image.php?di=7YEJ
Это устаревший способ, в нём перехватываемая функция вызывается перехватчиком не напрямую, а через автоматон, т.е. производится лишнее присваивание и переход на switch.

VVWVV
15.02.2016, 17:24
Modular Programming (http://forum.sa-mp.com/showthread.php?t=597338)

franked
16.02.2016, 23:31
Я кстати тоже еще как-то летом обдумывал как же сделать не плохую структуру для проектирования. (В принципе до сих пор обдумываю,лол) И у меня в голове такая мысль появилась:

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

Кстати,именно поэтому я создал эту тему (клик). (http://pro-pawn.ru/showthread.php?13471-%D0%A3%D0%B4%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%BE-%D0%B2-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B5-%D1%81-MySQL) Мне удобнее все функции,связанные с mysql запросами хранить в отдельном файле (допустим название его mysql_queries),а в include с системой я пишу лишь функцию,которая сделает запрос.
К примеру:

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

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

Alexey_Nikiforov
20.02.2016, 15:19
Я работаю вот так и мне очень удобно.
Отдельно макросы цветов.
Объекты.
Команды щас перенесу на DC_CMD отдельно.
Возможно еще подумаю что и как разбить на файлы.
http://s017.radikal.ru/i401/1602/d5/ecda7f826a65.jpg