29.08.2014, 19:47
Д0брого времени суток.
В данном туториале я бы хотел рассказать об одном методе как можно устраивать внутреннюю часть вашего SA-MP проекта. Как известно большая часть скриптеров хранят свои режимы всего в одном .pwn файле. На самом деле, это очень не правильное решение. Потому что, при увеличении количества кода проекта, его сложность в дальнейшей модификации значительно усложняется. Чтобы избежать значительного усложнения проекта при его модификации, вам достаточно правильно спроектировать архитектуру вашего проекта. Многие кто разбивают мод по инклудам сталкиваются с проблемой, связать все части в одну систему. И тут начинается подключение инклудов в строгом прядке, переброс функций, создание промежуточных инклудов а иногда вовсе скриптеры входят в тупик. Как же избежать таких мучений и тупиков? - спросите вы, для этого в других языках программирования существуют:Заголовочные файлы Советую почитать тем, кто не знаком с ними, что бы в дальнейшем не было недопонимания.
Внимание! Если вы пользуетесь Pawno, то ниже предоставленный метод работать не будет. Данный метод работает на Notepad++
О том, как настроить Notepad++ вы узнаете тут: PAWN for SA-MP in Notepad++
>>Часть Первая
Для начала ознакомлю Вас с методом проектирования архитектуры.
Давайте создадим небольшой проект. Создадим папочку TestProject в любом удобном для вас месте.
В данной папочке создадим два файла, они будут главными файлами проекта, главные файлы проекта должны иметь формат "_*.*":
_main.h; -Заголовочный файл, в нём мы будем указывать явные зависимости (строгий порядок подключений хидеров *.h)
_main.pwn; - Файл кода, в нём мы будем указывать явные зависимости (строгий порядок подключений файлов кода *.pwn)
_main.h:
pawn Code:
#if !defined _MAIN_H_
#define _MAIN_H_
//==============================================================================
#include <a_samp> //Явная зависимость
//==============================================================================
#endif
pawn Code:
#include <stdafx.h> //Предварительно откомпилированный заголовок
//==============================================================================
#if !defined _MAIN_PWN_
#define _MAIN_PWN_
//==============================================================================
main(){}
//==============================================================================
#endif
_Includes; - В данную папку мы будем помещать инклуды других разработчиков но в формате *.h, например: streamer.h; mysql.h; Нужно это для того, чтобы не объявлять явные зависимости и не засорять компилятор.
Source; - В данной папке будут располагаться ресурсы нашего мода.
Напишем не большой мод, будем загружать и выгружать дома и бизнесы. Так же дома и бизнесы будут дробится на разные подтипы со своим функционалом.
Перейдём в папку Source, создаём файлы:
Callback.h:
pawn Code:
#if !defined _CALLBACK_H_
#define _CALLBACK_H_
//==============================================================================
//==============================================================================
#endif
pawn Code:
#if !defined _CALLBACK_PWN_
#define _CALLBACK_PWN_
//==============================================================================
public OnGameModeInit()
{
LoadHouse(); //Загрузим дома
LoadBiznes(); //Загрузим бизнесы
reutrn 1;
}
//==============================================================================
public OnGameModeExit()
{
UnLoadHouse(); //Выгрузим дома
UnLoadBiznes(); //Выгрузим бизнесы
return 1;
}
//==============================================================================
#endif
TestProject/Source/House/House.h; TestProject/Source/House/House.pwn;
TestProject/Source/Biznes/Biznes.h; TestProject/Source/Biznes/Biznes.pwn;
В папках две подпапки: Type_0; Type_1;
В подпапках по два файла, уже наверное догадались какие
TestProject/Source/House/Type_0/House_Type_0.h
TestProject/Source/House/Type_0/House_Type_0.pwn
Аналогично для Type_1 и бизнеса.
Для примера опишем только дома. Перейдем в папку: House
House.h:
pawn Code:
#if !defined _HOUSE_H_
#define _HOUSE_H_
//==============================================================================
#define MAX_HOUSE 5
//==============================================================================
new House_Count = 0; //Счетчик количества домов
new House_DB_ID[MAX_HOUSE]; //Каждый дом запоминает ИД в Базе Данных
new House_Type[MAX_HOUSE]; //Каждый дом запоминает тип
//==============================================================================
forward LoadHouse();
forward UnLoadHouse();
//==============================================================================
#endif
pawn Code:
#if !defined _HOUSE_PWN_
#define _HOUSE_PWN_
//==============================================================================
public LoadHouse()
{
for(new i = 0; i < MAX_HOUSE; i++) //Побежим по всем домам
{
House_DB_ID[House_Count] = i; //Запомним ид БД
House_Type[House_Count] = random(2); //Запомним тип дома
if(House_Type[House_Count] == 0) //Если тип дома равен 0
{
House_Create_Type_0(); //Вызвать создание 0 типа
}
else if(House_Type[House_Count] == 1) //Если тип дома равен 1
{
House_Create_Type_1(); //Вызвать создание 1 типа
}
House_Count++; //Количество домов увеличилось
}
return 1;
}
public UnLoadHouse()
{
for(new i = 0; i < House_Count; i++) //Бежим по всем домам
{
if(House_Type[i] == 0) //Если тип дома равен 0
{
House_Destroy_Type_0(); //Вызвать разрушение 0 типа
}
else if(House_Type[i] == 1) //Если тип дома равен 1
{
House_Destroy_Type_1(); //Вызвать разрушение 1 типа
}
}
return 1;
}
//==============================================================================
#endif
Удобно? Надеюсь что так и есть. Давайте продолжим, перейдём в папку:TestProject/Source/House/Type_0/
House_Type_0.h:
pawn Code:
#if !defined _HOUSE_TYPE_0_H_
#define _HOUSE_TYPE_0_H_
//==============================================================================
new House_Type_0_Count = 0;
//==============================================================================
#endif
pawn Code:
#if !defined _HOUSE_TYPE_0_PWN_
#define _HOUSE_TYPE_0_PWN_
//==============================================================================
stock House_Create_Type_0()
{
House_Type_0_Count++;
printf("Дом тип 0, Меня уже: %d", House_Type_0_Count);
}
stock House_Destroy_Type_0()
{
House_Type_0_Count--;
printf("Дом тип 0, Меня осталось: %d", House_Type_0_Count);
}
//==============================================================================
#endif
Многие скажут, что это полная ересь, что оно даже компилироваться не будет. Да и вообще так нельзя делать.
Скрывать не буду, да так и есть, обычным методом данный проект не скомпилировать. Но вы наверное заметили , вначале мы используем какой-то там #include <stdafx.h> и мы нигде его не видели.
Об этом я вам сейчас расскажу и расскажу почему данный метод не работает на Pawno. Pawno - деревянный.
>>Часть вторая
Вот мы и разобрались с архитектурой будущего проекта, но многое осталось не ясным, чтож, раскрываю карты господа.
#include <stdafx.h> - является результатом сборки, которая происходит перед компиляцией всего проекта и имеет следующий вид.
pawn Code:
#if !defined _stdafx_h_
#define _stdafx_h_
//==============================================================================
#include "C:\TestProject\_main.h"
#include "C:\TestProject\Source\Callback.h"
#include "C:\TestProject\Source\Biznes\Biznes.h"
#include "C:\TestProject\Source\Biznes\Type_0\Biznes_Type_0.h"
#include "C:\TestProject\Source\Biznes\Type_1\Biznes_Type_1.h"
#include "C:\TestProject\Source\House\House.h"
#include "C:\TestProject\Source\House\Type_0\House_Type_0.h"
#include "C:\TestProject\Source\House\Type_1\House_Type_1.h"
//==============================================================================
#include "C:\TestProject\_main.pwn"
#include "C:\TestProject\Source\Callback.pwn"
#include "C:\TestProject\Source\Biznes\Biznes.pwn"
#include "C:\TestProject\Source\Biznes\Type_0\Biznes_Type_0.pwn"
#include "C:\TestProject\Source\Biznes\Type_1\Biznes_Type_1.pwn"
#include "C:\TestProject\Source\House\House.pwn"
#include "C:\TestProject\Source\House\Type_0\House_Type_0.pwn"
#include "C:\TestProject\Source\House\Type_1\House_Type_1.pwn"
//==============================================================================
#endif
#include "C:\TestProject\_main.h" - так как этот файл у нас в сборке всегда первый, то мы можем прописать в нём исключения: Явные зависимости. Пример:
pawn Code:
#if !defined _MAIN_H_
#define _MAIN_H_
//==============================================================================
#include <a_samp> //Явная зависимость
//==============================================================================
#include "C:\TestProject\Source\Biznes\Biznes.h"
#include "C:\TestProject\Source\Callback.h" // Callback.h явно зависит от Biznes.h и теперь всё в порядке, Такие зависимости могут вызывать макросы
//==============================================================================
#endif
Вы наверное подумали, что stdafx.h нужно постоянно собирать вручную и при каждом изменении архитектуры изменять его - конечно же нет, хотя если вам не лень, то можно.
Для начала вам нужно написать bat файл сборщика... шучу я его уже написал. Создаём в папке с компилятором файл: Compiler.bat, правой кнопкой по файлу -> изменить, вставляем ниже приведённый код. Вам также доступны некоторые настройки самого bat файла.
pawn Code:
@rem Нужно указать расширения файлов:
@rem header - заголовочный файл, хранит переменные и forward-ы
@rem source - файл кода, хранит код программы, stock-и и public-и
@rem stdafx - предкомпилированный заголовок, в нём собираются все header и source файлы
@rem move_to - переместит скомпилированный файл в указанную директорию.
@set header=h
@set source=pwn
@set stdafx=stdafx
@set move_to=\
@chcp 65001>nul
@rem Запоминаем и выводим время старта компиляции
@setlocal EnableDelayedExpansion
@set t0=!time!
@echo Время запуска: !t0!
@setlocal DisableDelayedExpansion
@rem Автоматические переменные
@set name=Created_by_White_116
@set old_path_to_code=v1.0
@set path_to_code=%CD%
@set path_to_pawn=%0
@rem Вырежем путь до компилятора
@for %%i in (%path_to_pawn%) do @(set path_to_pawn=%%~dpi)
@rem Ищем корневой каталог по главному файлу компиляции
:back1
@if exist %path_to_code%\_*.%source% goto next1
@for %%i in ("%path_to_code%\..") do @(set path_to_code=%%~fi)
@if %path_to_code% == %old_path_to_code% (
@echo Ошибка: Не найден корневой каталог для этого проекта!
@goto next2
) else (
@set old_path_to_code=%path_to_code%
)
@goto back1
:next1
@rem Ищем имя главного файла компиляции
@for %%i in ("%path_to_code%\_*.%source%") do @(set name=%%~ni)
@rem Заполняем Precompiled Headers
@(
@(echo.#if !defined _stdafx_h_)
@(echo.#define _stdafx_h_)
@(echo.//==============================================================================)
@for /F "delims=" %%i in ('dir /s /b %path_to_code%\*.%header%') do @(echo.#include "%%i")
@(echo.//==============================================================================)
@for /F "delims=" %%i in ('dir /s /b %path_to_code%\*.%source%') do @(echo.#include "%%i")
@(echo.//==============================================================================)
@(echo.#endif)
)>%path_to_pawn%\include\%stdafx%.%header%
@rem Компилируем проект
@%path_to_pawn%pawncc.exe -;+ -(+ %path_to_code%\%name%.%source% -o%path_to_code%\%name%.amx %1 %2 %3 %4 %5 %6 %7 %8 %9
@rem Если не нужно перемещать файл то перепрыгиваем, иначе перемещаем
@if %move_to% == \ goto next2
@rem Если существует файл переместить его
@if exist %path_to_code%\%name%.amx ^
move %path_to_code%\%name%.amx %move_to%
@rem Выводим время завершения и затраченное время на компиляцию
:next2
@setlocal EnableDelayedExpansion
@set t1=!time!
@for /F "tokens=1-8 delims=:.," %%a in ("!t0: =0!:!t1: =0!") do @(set /a "a=(((1%%e-1%%a)*60)+1%%f-1%%b)*6000+1%%g%%h-1%%c%%d, a+=(a>>31) & 8640000")
@echo Время завершения: !t1! Затрачено времени: !a!0 мс.
@setlocal DisableDelayedExpansion
@rem Покинем, всё OK!
@exit /b 1
Quote:
-Засекать время компиляции проекта. -При редактировании любого файла проекта и при нажатии кнопки компилирования, автоматически находить главный файл проекта который и будет компилироваться, главный файл проекта должен иметь формат _*.pwn -Создавать предкомпилированный заголовок, создаётся автоматически в папке с инклудами, чтобы не мудрить с подключением в проекте. -Перемещать скомпилированный файл в указанный каталог. -Передавать параметры компилятору. |
Quote:
cd $(CURRENT_DIRECTORY) "P:\Сompiler.bat" -O2 |
Quote:
cd $(CURRENT_DIRECTORY) -указывает текущий каталог компилируемого файла. "P:\Сompiler.bat" -путь до нашего батника, который должен лежать рядом с компилятором. -O2 - параметр компиляции передаваемый компилятору. |
>>Заключение
В данном туториале мы разобрались с заголовочными файлами, с архитектурой мода, с маленькими хитростями.
Надеюсь что в данном туториале всё описано доступно и понятно.
Файл проекта вы можете скачать во вложениях.