PDA

Просмотр полной версии : [Урок] Рекомендации по написанию кода



Daniel_Cortez
19.04.2014, 19:26
Здравствуйте, уважаемые пользователи Pro-Pawn.ru (http://www.pro-pawn.ru).

Многие новички не могут разобраться в основах скриптинга потому, что не осознают самых элементарных правил не только скриптинга, но и программирования в целом.
Обычно люди сами выбирают свой стиль кодинга, но согласитесь, будет гораздо проще писать весь свой код по одному стандарту, чем работать без оного с кашей из разных стилей. Да и не стоит забывать, что код, который вы выкладываете на Pro-Pawn, кроме вас будет использоваться другими скриптерами.
Поэтому я собрал самые распространённые правила в один стиль кодинга на Pawn.

Итак, начнём:

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


new Float:x, Float:y, Float:z; // координаты спавна (глобальные переменные)

stock SetSpawnPos()
{
x = 0.0, y = 1.0, z = 2.0; // здесь эти глоб. переменные используются
}

stock PrintPos(playerid)
{
new Float:x, Float:y, Float:z; // а здесь компилятор выдаст предупреждение,
// т.к. переменные "x", "y" и "z" уже были объявлены
// в качестве глобальных переменных
GetPlayerPos(playerid, x, y, z);
printf("%.4f, %.4f, %.4f", x, y, z);
}

Как видно, в данном примере внутри функции PrintPos создаются локальные переменные x, y и z, но эти имена уже заняты. Происходит коллизия имён - конфликт между переменными/функциями/константами с одинаковыми названиями.
Вывод: если создаёте глобальные переменные, задавайте им достаточно длинные и осмысленные названия, чтобы не было конфликтов имён с локальными переменными.

Уточним названия переменных: добавим слово "spawn", чтобы было понятно, что это координаты спавна.
Пример хорошего кода:


new Float:spawn_x, Float:spawn_y, Float:spawn_z; // координаты спавна (глобальные переменные)

stock SetSpawnPos()
{
spawn_x = 0.0, spawn_y = 1.0, spawn_z = 2.0; // здесь эти глоб. переменные используются
}

stock PrintPos(playerid)
{
new Float:x, Float:y, Float:z; // ok (имена "x", "y" и "z" не заняты глобальными переменными)
GetPlayerPos(playerid, x, y, z);
printf("%.4f, %.4f, %.4f", x, y, z);
}

Теперь локальные переменные больше не будут конфликтовать с глобальными, т.к. мы добавили к названиям глобальных переменных префикс "spawn_". Этот префикс выбран потому, что в переменных сохраняются координаты спавна (ваш Кэп). Таким образом удалось избежать коллизии имён.

Не стоит писать названия переменных, констант и функций на транслите. Это должно быть очевидно, как убого выглядит код, написанный на транслите или на ломанном английском.
Если же у вас проблемы с иностранными языками - учите английский. Либо завязывайте со скриптингом, потому что знание английского - неотъемлимая часть в обучении программированию.

Придерживайтесь одного и того же стиля задания имён для переменных, констант и функций.
Ниже описан самый распространённый стиль среди сообщества SA:MP:
Имена констант пишутся большими буквами, слова в названии разделяются знаком подчёркивания (_).
Названия переменных рекомендуется писать маленькими буквами, слова так же разделяются символом подчёркивания.
В именах функций каждое слово пишется с большой буквы, при этом слова не разделяются никакими знаками.
Часть имени переменной может писаться большими буквами, если это сокращение (например, "HTTP_response_code", "forum_URL", "TD_health"). То же самое исключение относится и к именам функций ("HTTPCallback", "OpenURL").
Пример:


// переменная
new logged_players_count;

// константа
const MAX_HOUSES = 256; // либо #define MAX_HOUSES 256

// функция
stock GetMaxHouses()
{
return MAX_HOUSES;
}

Делается это для того, чтобы потом по имени можно было легко понять, константа ли это, переменная или функция.
Примечание: в SA:MP также есть функции "db_open", "tickcount", "printf", и т.д., но все они - всего лишь отголоски тех времён, когда никто особо не задумывался о стандартах. Все писали код по-своему, отсюда и путаница.
Потому логично считать те функции не правилом, а исключением из правил.

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

Textdraw0, Textdraw1, Textdraw2, ...
Из названия этих переменных понятно только то, что это текстдравы, а что это за текстдравы и для чего они предназначены - гадайте сами! Больше никакого смысла в этих названиях нет.
Придётся лишний раз выискивать все места, где используются эти переменные, чтобы понять, для чего они нужны.
Хороший пример:

TD_server_logo;
Здесь сразу становится ясно, что это текстдрав логотипа сервера. При этом префикс "TD" указывает на то, что в переменной хранится ID текстдрава.
Использование префиксов и постфиксов в переменных и константах не обязательно, но всё же рекомендуется, т.к. с ними значительно легче определить тип переменной и область видимости: не нужно смотреть место, где объявлена переменная/константа, достаточно лишь посмотреть на её название.
Список часто используемых префиксов:
"DLG" для констант с номерами диалогов.
"obj" для переменных под ID объектов.
"pobj" для объектов, создающихся для отдельных игроков (функцией CreatePlayerObject).
"TD" для текстдравов (большими буквами потому, что в сокращение попадают только большие буквы из "TextDraw").
"PTD" для текстдравов, создающихся для отдельных игроков (PlayerText).


Старайтесь правильно табулировать код (или, как это ещё называют, писать код лесенкой).
Избегайте каши из пробелов вперемешку с табами - её трудно заметить в редакторе кода, но на форуме из-за такой каши часто становятся заметны проблемы с табуляцией.
Желательно табулировать код только с помощью Tab, т.к. во многих редакторах кода можно настроить под свой вкус их ширину (т.е. 1 таб можно сделать равным 3 пробелам вместо 4), чего нельзя сделать с пробелами. Кроме того, при неправильной табуляции лишний таб заметить намного легче, чем пробел.
Отыскивать лишние пробелы удобнее всего в Notepad++ с включенным отображением невидимых символов.
Помните: правильно оттабулированный код - залог понимания другими скриптерами.

Не пытайтесь записывать весь код в одну строку - компилятору нет дела до стиля оформления исходного кода, результат в AMX будет один и тот же.
Зато таким "утрамбовыванием" вы можете здорово испортить читаемость кода для себя и других скриптеров.
Пример плохого кода (взято прямиком из ада из RLS):


if (strcmp(cmd,"/stuff",true)==0){new string[32];
format(string,sizeof(string),"Ваш уровень: %d",GetPlayerScore(playerid));SendClientMessage(playerid,-1,string);return 1;}

Исправим ситуацию:


if (strcmp(cmd, "/stuff", true) == 0)
{
new string[32];
format(string, sizeof(string), "Ваш уровень: %d", GetPlayerScore(playerid));
SendClientMessage(playerid, -1, string);
return 1;
}

Примечание: на самом деле утрамбовывание строк может привести к уменьшению файла .amx, но это только из-за отладочной информации. Чем больше строк в исходном коде, тем больше весит отладочная информация (~2 байта на строку, результат может варьироваться из-за сжатия содержимого AMX), но на характеристиках выполнения скрипта это никак не сказывается. Если вы хотите, чтобы ваш скрипт меньше весил и сервер тратил меньше памяти при выполнении скрипта, есть смысл вообще убрать всю отладочную информацию - для этого в папке с компилятором (pawncc) создайте файл pawn.cfg и пропишите в нём "-d0".

Избегайте использования "магических чисел" (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29), которые никто, кроме вас, объяснить не может.
Плохой пример:


new some_array[10];

public OnGameModeInit()
{
for (new i = 0; i < 10; i++)
some_array[i] = random(2);
}

CMD:printarr(playerid, params[])
{
for (new i = 0; i < 10; i++)
printf("%d", some_array[i]);
}

Почему пример плохой? Хотя бы потому, что не сразу понятно, откуда взялось число 10 в условии выхода из цикла.
Кроме того, попробуйте поменять размер массива - придётся перерывать весь мод, выискивая каждый цикл, работающий с тем массивом, чтобы заменить размер на новый. Многие начинающие скриптеры именно так и забывают сменить размер в циклах, после чего в работе сервера часто возникают ошибки из-за выхода за пределы массива (привет RLS-никам!)
Хороший пример:


new some_array[10];

public OnGameModeInit()
{
for (new i = 0; i < sizeof(some_array); i++)
some_array[i] = random(2);
}

CMD:printarr(playerid, params[])
{
for (new i = 0; i < sizeof(some_array); i++)
printf("%d", some_array[i]);
}

Теперь достаточно лишь один раз сменить размер массива в месте его объявления, остальную работу компилятор возьмёт на себя. Такой код будет куда более надёжным.
Ещё пример:


const SOME_ARRAY_SIZE = 10;
new some_array[SOME_ARRAY_SIZE];

public OnGameModeInit()
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
some_array[i] = random(2);
}

CMD:printarr(playerid, params[])
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
printf("%d", some_array[i]);
}

Здесь размер массива определяется константой, что тоже приемлемо в некоторых ситуациях. Например, во многих системах домов есть константа "MAX_HOUSES" и было бы удобнее использовать её вместо "sizeof(house_info)".

Используйте константы при каждой возможности.
Плохой пример:


public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if (newkeys == 327808)
{
// ...
}
}

А теперь вопрос: почему же пример плохой?
В новых версиях сервера SA:MP значения констант могут измениться. Константы SA:MP сделали разработчики мультиплеера, они же имеют право и изменить их в следующих версиях, если это будет нужно.
В этом случае, если вы используете числа вместо констант, вам придётся перешаривать весь мод, вручную выискивать все нужные числа (среди over 9000 других чисел) и заменять их.
Да, в стагнирующем проекте типа SA-MP такие изменения маловероятны, но это ещё не отменяет их возможности. Использование "магических чисел" (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)#.D0.9F.D0.BB.D0.BE.D1.85.D0.B0.D1.8F_.D0.BF.D1.80.D0.B0.D0.BA.D1.82.D0.B8.D0.BA.D0.B0_.D0.BF.D1.80.D0.BE.D0.B3.D1.80.D0.B0.D0.BC.D0.BC.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D1.8F) вместо констант считается плохой практикой.
Для того, чтобы понять, какие клавиши проверяются в примере, придётся смотреть таблицу (wiki.sa-mp.com/wiki/Keys) (либо постоянно держать эти числа в голове), вместо того, чтобы просто прочитать их названия на английском языке и сразу всё понять. Если же у вас проблемы с иностранным языком - см. пункт 2.

Хороший пример:


public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
if (newkeys == KEY_AIM | KEY_YES | KEY_CTRL_BACK)
{
// ...
}
}

В этом примере названия клавиш уже расписаны на английском языке и не составит никакого труда понять их.

Ещё пример:


public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch (dialogid)
{
case 0:
return 1;
case 1:
{
if ((inputtext[0] != '\0') && (0 == strcmp(inputtext, player_info[playerid][pPassword])))
player_login_status{playerid} = true;
return 1;
}
case 2:
{
// ...
}
// ...
}
return 0;
}

Чтобы правильно составлять диалоги и обработку пользовательского ввода в них, придётся либо заучивать ID всех диалогов наизусть (а их в моде может накопиться под несколько сотен), либо каждый раз перед использованием ShowPlayerDialog заглядывать в OnDialogResponse и выискивать там нужный ID. В любом случае это лишняя работа для скриптера.
А теперь представим такую ситуацию: в моде 100 разных диалогов и вдруг понадобилось удалить диалог под номером 50 - вы не сможете просто так взять и поставить диалог #51 на место #50, #52 на место #51 и т.д. Вам придётся либо потратить уйму времени на перестановки, либо оставить 50-й ID диалога неиспользованным.
Решение: использовать именованные константы для ID всех диалогов.


enum
{
// Компилятор сам распределит значения: константа DIALOG_ID_NONE будет равна нулю, DIALOG_ID_LOGIN - 1, DIALOG_ID_REGISTER - 2 и т.д.
// Если захотите удалить DIALOG_ID_LOGIN, то DIALOG_ID_REGISTER вместо 2 станет равна 1
// и значения всех последующих констант тоже уменьшатся на единицу.
DIALOG_ID_NONE,
DIALOG_ID_LOGIN,
DIALOG_ID_REGISTER,
// ...
};

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch (dialogid)
{
case DIALOG_ID_NONE:
return 1;
case DIALOG_ID_LOGIN:
{
if ((inputtext[0] != '\0') && (0 == strcmp(inputtext, player_info[playerid][pPassword])))
player_login_status{playerid} = true;
return 1;
}
case DIALOG_ID_REGISTER:
{
// ...
}
// ...
}
return 0;
}


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


const MAX_HOUSES = 100; // На сервере всего 100 домов.
const INVALID_HOUSE_ID = 255; // Если в player_info[playerid][pHouseKey] число 255, то у игрока нет дома.
// Почему бы и нет, ведь у меня на сервере только дома с ID от 0 до 254?

Но что будет, если вы потом захотите создать больше 255 домов? Сделаете 255-й дом на координатах, куда не сможет пробраться обычный игрок (например, под землёй)? Это уже костыль. Решается же эта проблема очень просто.
Хороший пример:


const MAX_HOUSES = 300;
const INVALID_HOUSE_ID = -1; // Число -1 не будет конфликтовать с макс. кол-вом домов, т.к. число домов не может быть отрицательным.

Похожая проблема была в моде GodFather (интересно, кто-нибудь из "нынешнего поколения" видел этот мод?): там число 255 использовали как неправильный ID игрока. Потом вышел SA-MP 0.3a, в котором к серверу могли подключиться до 500 игроков - тогда 255 перестал быть неправильным ID, что приводило к багам и весьма неожиданным последствиям для тех, кому не повезло зайти под 255-м ID.

Не путайте целые числа с вещественными.
Пример плохого кода:


new Float:some_float = 10;
new some_float_sqr = floatpower(some_float, 2);
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + 10);

Казалось бы, ничего особенного. Но давайте посмотрим, во что компилятор превращает этот код:


new Float:some_float = float(10);
new some_float_sqr = floatpower(some_float, float(2));
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + float(10));

Видите эти лишние вызовы функции float()? Компилятор подставляет их, если вы используете целые числа там, где нужно использовать вещественные.
Это не смертельно, но может увеличить нагрузку на сервер, ведь он будет тратить лишнее время на то, чтобы преобразовать число из целочисленного формата в число с плавающей запятой (Float) - просто потому, что кто-то поленился дописать ".0" в конце числа.
Пример хорошего кода:


new Float:some_float = 10.0;
some_float += 2.0;
new some_float_sqr = floatpower(some_float, 2.0);
new Float:health;
GetPlayerHealth(playerid, health);
SetPlayerHealth(playerid, health + 10.0);

Как видите, достаточно просто добавить ".0" к числам, чтобы сделать их вещественными. Это не трудно.
Главное только, наоборот, не указывать вещественные числа там, где нужны целые, иначе это может привести к ошибкам.

Старайтесь, чтобы ваш код укладывался в лимит: 80 символов на строку. Если не вмещается - переносите.
В коде будет больше строк, но зато вам не придётся лишний раз тянуться к полосе горизонтальной прокрутки в редакторе кода, чтобы посмотреть на то, что не влезает на экран.
Обычно лимит в 80 символов выделяется линией в pawno, SynWrite (http://pro-pawn.ru/showthread.php?1543) и многих других редакторах кода, аналогично полям в школьных тетрадях.
Пример плохого кода:


SetObjectMaterialText(objectid, "Sample text", 0, OBJECT_MATERIAL_SIZE_512x512, "Arial", 12, 1, 0x00000000, 0xFFFFFFFF, OBJECT_MATERIAL_TEXT_ALIGN_CENTER); //155

Строка кода занимает 155 символов, придётся тянуться за полосой горизонтальной прокрутки в редакторе, чтобы увидеть последние параметры.
Исправим ситуацию, перенеся параметры функции на новые строки:


SetObjectMaterialText(
objectid, "Sample text", 0, OBJECT_MATERIAL_SIZE_512x512,
"Arial", 12, 1, 0x00000000, 0xFFFFFFFF, OBJECT_MATERIAL_TEXT_ALIGN_CENTER //77 симв.
);

Цель достигнута: самая длинная строка кода занимает 77 символов, что укладывается в лимит <=80.

Ещё пример:


CreateObject(404, 1138.000, 146.0000, 0.0000, 0.0000, 0.0000, 128.0000, 500.0000); //82

Исправляем, сгруппировав координаты и углы поворота:


CreateObject(
404,
1138.000, 146.0000, 0.0000, //31 символ
0.0000, 0.0000, 128.0000,
500.0000
);

Или даже так, сэкономив пространство по вертикали:


CreateObject(
404, 1138.000, 146.0000, 0.0000, 0.0000, 0.0000, 128.0000, 500.0000); // 73


И ещё один пример:


format(buffer, sizeof(buffer), "Имя: %s\nУровень: %d\nДеньги: %d$\nАдрес: %s, %d\nОрганизация: %s\n", player_name, level, money, player_city, player_house_id, player_family_name); //179

Переносим параметры:


format(
buffer, sizeof(buffer),
"Имя: %s\n"\
"Уровень: %d\n"\
"Деньги: %d$\n"\
"Адрес: %s, %d\n"\
"Организация: %s",
player_name, level, money, player_city, // 43
player_house_id, player_family_name
);

Обратите внимание: строковое значение разбито на несколько строк. Также их уровень табуляции увеличен на 1, чтобы показать, что это одна строка, а не 5 разных строк.

Тело циклов for, do и while (если они не пустые) и ветвления if следует переносить на отдельную строку для лучшей читаемости.
Плохой пример:


if (0 == IsPlayerAdmin(playerid)) return SendClientMessage(playerid, -1, "Ошибка: Вы не администратор!");

Хороший пример:


if (0 == IsPlayerAdmin(playerid))
return SendClientMessage(playerid, -1, "Ошибка: Вы не администратор!");


Аббревиатуры в названиях должны записываться в верхнем регистре.
Примеры:


// HTTP - HyperText Transfer Protocol
native HTTP(index, type, url[], data[], callback[]);

// NPC - Non-Player Character
native ConnectNPC(name[], script[]);
forward OnNPCSpawn();

// EOF - End Of File
const EOF = -1;


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


// из стандартных функций SA:MP
native IsObjectMoving(playerid);
native IsPlayerConnected(playerid);
native IsPlayerInAnyVehicle(playerid);

// примеры пользовательских функций
stock bool:IsHouseUnoccupied(houseid);
stock IsRPNick (http://pro-pawn.ru/showthread.php?7528)(name[]); // Название кликабельно.


Префиксы Get/Set должны использоваться в названиях функций, которые получают или изменяют (устанавливают) значение какого-либо атрибута.
Таким атрибутом может быть здоровье игрока, его уровень, игровое время на сервере - что угодно, к чему нельзя получить доступ из скрипта напрямую, как к переменной, но можно получить/изменить с помощью функции.
Примеры:


// из стандартных функций SA:MP
native GetPlayerPos(playerid, &Float:x, &Float:y, &Float:z);
native SetPlayerPos(playerid, Float:x, Float:y, Float:z);
native GetVehicleHealth(vehicleid, &Float:health);
native SetVehicleHealth(vehicleid, Float:health);
native SetGravity(Float:gravity);
native SetWeather(weatherid);

// примеры пользовательских функций
stock GetHouseValue(houseid);
stock SetHouseValue(houseid, price);
stock GetBusinessFee(businessid);
stock SetBusinessFee(businessid, fee);


Массивы, у которых в ячейках сохраняются только значения от 0 до 255, следует объявлять с указанием слова char в размере, чтобы сэкономить кол-во отводящейся под массив памяти, используя под ячейку всего 1 байт вместо 4.
Доступ к ячейкам таких массивов должен осуществляться с помощью фигурных скобок ({}) вместо квадратных ([]).
Пример:


new player_login_status[MAX_PLAYERS char];
#define IsPlayerLoggedIn(%0) (player_login_status{%0})

public OnPlayerConnect(playerid)
{
player_login_status{playerid} = 0;
// ...
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
if (dialogid == DIALOG_ID_LOGIN)
{
player_login_status{playerid} = 1;
// ...
}
// ...
}

CMD:do(playerid, params[])
{
if (0 == IsPlayerLoggedIn(playerid))
return SendClientMessage(playerid, -1, "Ошибка: Вы не залогинены!");
// ...
}


Инклуды (.inc) должны содержать защиту от повторного подключения (include guard (https://ru.wikipedia.org/wiki/Include_guard)).
Раньше этот метод защиты был совершенно не нужен, поскольку сам компилятор Pawn не позволял подключить один и тот же файл дважды.
Однако эта возможность работала не на всех платформах (некорректная работа в Linux и OS X), из-за чего скрипты, компилировавшиеся под Windows могли не скомпилироваться под OS X.
В новой версии компилятора от Zeex данная возможность убрана, чтобы компилятор одинаково работал на всех платформах.
Поэтому теперь есть смысл самостоятельно защищать свои файлы от повторного использования.
Пример:


// файл: my_functions.inc

#if defined MY_FUNCTIONS_INC // если файл уже был подключен ранее -
#endinput // прервать дальнейшую обработку файла
#endif
#define MY_FUNCTIONS_INC // объявить макрос, чтобы компилятор в следующий раз
// "знал" о том, что этот файл уже был подключен

// ваш код
#include <a_samp>

//...


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


// файл: jetpack.inc

static stock player_has_a_jetpack[MAX_PLAYERS char];

stock IsPlayerInAJetpack(playerid)
return player_has_a_jetpack{playerid};


В инклудах должна быть возможность настройки и задания параметров из скрипта, в котором инклуд подключается.
Рассмотрим это на примере простого античита, который через заданный интервал времени проверяет, есть ли у игроков запрещённое оружие (пулемёт или гранатомёт, например), и при его наличии банит.
Античит через заданный интервал времени производит проверку всех игроков на наличие запрещённого оружия. Время проверки настраивается с помощью макроса MY_ANTICHEAT_CHECK_INTERVAL (2000 мс по умолчанию).
Плохой пример:


// файл: my_anticheat.inc

// Нужны более частые проверки - отредактируйте инклуд.
// Если выйдет новая версия инклуда - редактируйте заново.
#define MY_ANTICHEAT_CHECK_INTERVAL 2000

// ...

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

Хороший пример:


// файл: mode.pwn

#define MY_ANTICHEAT_CHECK_INTERVAL 500 // Перед тем, как подключить инклуд с античитом, задаём интервал проверки.
#include "my_anticheat.inc" // Подключаем инклуд (проверка будет выполняться примерно через каждые 500 мс).



// файл: my_anticheat.inc

#if !defined MY_ANTICHEAT_CHECK_INTERVAL // Если интервал не был задан в скрипте перед подключением инклуда...
#define MY_ANTICHEAT_CHECK_INTERVAL 2000 // ... устанавливаем время по умолчанию
#endif

Как видите, в инклуд потребовалось добавить всего пару строк. Это не так уж и сложно.
Теперь чтобы изменить те или иные настройки, пользователю не придётся лезть в сам инклуд.
По такому же принципу оформлены настройки в YSI (например, _YSI_NO_VERSION_CHECK отключает проверку обновлений YSI при каждой загрузке скрипта) и других профессионально реализованных инклудах. Кроме того, аналогичная практика используется во многих фреймворках и библиотеках на C/C++.


В дальнейшем содержимое темы будет исправляться и дополняться.

P.S.:
кому как удобно пусть так и оформляют
Стрельба себе в ногу - дело добровольное. Я никому этого не запрещаю.


Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

Spectrum
21.04.2014, 00:21
Не пытайтесь записывать весь код в одну строку - компилятору нет дела до стиля оформления исходного кода
Зато есть дело аптимизаторам, которые гонятся чтобы в коде было как можно меньше строк

DeimoS
21.04.2014, 04:44
Зато есть дело аптимизаторам, которые гонятся чтобы в коде было как можно меньше строк

Так от этого нет никакой оптимизации...

Лишняя скобка:

format(string, (sizeof(string), "Ваш уровень: %d", GetPlayerScore(playerid));


------------------------------------

thx, fixed //DC
------------------------------------

Stanley
07.05.2014, 14:09
всегда так делаю

Hidden
20.05.2014, 00:43
В пункте 1 в примере хорошего кода в stock PrintPos(playerid) комментарий отредактируй.

gangzone.ini
16.02.2015, 15:04
Спасибо )

Desulaid
01.05.2015, 18:41
Я бы добавил еще один пункт - CamelCase и under_score. И советовал бы использовать только такие типы нотаций. Но больше склоняюсь к "верблюжьей" нотации =)
Хорошо:


//CamelCase
ItSimplyFunctionNameExample
//under_score
it_simply_function_name_example

Плохо:


pLayername
namE
strinGgG
show_PLAYER

Видал я много плохих примеров

Daniel_Cortez
01.05.2015, 20:42
Я бы добавил еще один пункт - CamelCase и under_score.
Они уже описаны в п.3.


И советовал бы использовать только такие типы нотаций.
А константы?

Desulaid
01.05.2015, 22:09
Они уже описаны в п.3.


А константы?
under_score :)

Daniel_Cortez
01.05.2015, 22:36
under_score :)

http://breedpmnr.ru/i/E0AA33

А вот Калькор с вами не согласен. И вместе с ним наверняка ещё куча других скриптеров.
По сути это ни что иное, как навязывание стиля. Just deal with it.
Просто есть люди, которые этого в упор не хотят понимать, им больше нравится и дальше продолжать жрать кактус страдать из-за говнокода, в котором вообще нет никаких определённых правил.

Desulaid
01.05.2015, 22:50
Я это обычно называю макросы. :dirol:

Говоря про константы, я имел в виду


new const example = 228;

Daniel_Cortez
01.05.2015, 23:29
Я это обычно называю макросы. :dirol:

Говоря про константы, я имел в виду


new const example = 228;
Это не константа, а скорее простая переменная, значение которой нельзя изменять.

const THE_ANSWER_TO_THE_UNIVERSE = 42;
Вот это константа.
В отличие от вашего примера, она не занимает места в памяти - её значение подставляется компилятором непосредственно на место применения (не путать с макросами - там совсем другая, макроподстановка).

Что же касается макросов, они могут "притворяться" как константами, так и функциями.
А потому в стилях кодинга их названия могут подчиняться разным правилам, в зависимости от ситуации.
Но раз макрос, используемый, как константа, пишется заглавными буквами, то вполне логично утверждать, что в SA:MP константы должны писаться в стиле ALL_CAPS.

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

Simlish
02.05.2015, 11:25
Вот мечты мечтами, все равно все стараются для себя, чтобы было удобнее. Например не всем удобен вот такой код

имя(параметр){
//
}
Хотя он мне кажется добным. Сразу видно, что функци имеет 'тело'. При сравнении

имя(параметр);
имя(параметр){
//
}
Все обычно используют

имя(параметр)
{
//
}
Хотя это дело принципа.

Daniel_Cortez
02.05.2015, 15:20
Вот мечты мечтами, все равно все стараются для себя, чтобы было удобнее.
Данная статья написана именно для тех, кто с самого начала собирается выложить код на всеобщее обозрение.

По поводу фигурных скобок могу сказать только, что стиль Олмана наиболее распространён в сообществе SA:MP.
SA:MP wiki (http://wiki.sa-mp.com) - прекрасное тому доказательство. Попробуйте найти там хоть один пример с фигурными скобками в стиле K&R (также известном, как "египетские скобки").

Если вы делаете код для себя и ставите скобки в стиле K&R - бога ради.
Только если вдруг решите выложить такой код в паблик - не удивляйтесь, если для других он будет трудночитаемым.

Daniel_Cortez
24.05.2015, 02:08
Обновил статью, добавлены 9 новых пунктов (9-17).

Desulaid
03.07.2015, 01:28
Думаю, что пора перестать говорить, что SAMP придерживается таких фигурных скобок


{
//...
}


https://pp.vk.me/c628127/v628127894/59a5/iXgboYkFKeQ.jpg
:read: линк (http://wiki.sa-mp.com/wiki/Script_Examples_RU)


Правда только на русском языке такая фигня.

Daniel_Cortez
03.07.2015, 13:58
Думаю, что пора перестать говорить, что SAMP придерживается таких фигурных скобок
А я и не говорил, что его придерживаются абсолютно все скриптеры, речь была только о большинстве.

По поводу фигурных скобок же могу сказать только, что стиль Олмана (в котором фигурные скобки переносятся на отдельную строку) наиболее распространён в сообществе SA:MP.





https://pp.vk.me/c628127/v628127894/59a5/iXgboYkFKeQ.jpg
:read: линк (http://wiki.sa-mp.com/wiki/Script_Examples_RU)


Правда только на русском языке такая фигня.
Ок, вы нашли отрывок в разделе, развиваемом чуть более, чем никак, в котором даже не могут определиться со стилями скобок и названий идентификаторов (не говоря уже о табуляции).
Можете взять с полки пирожок -_-

Daniel_Cortez
10.12.2015, 20:35
Обновил 7-й пункт, теперь в нём вместо совета по использованию sizeof содержится рекомендация к избежанию "магических чисел" (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29) и рассматривается использование как sizeof, так и констант.

jeraqiv
24.01.2016, 13:03
Спасибо большое за статью. Узнал много нового.

Daniel_Cortez
25.01.2016, 19:47
Изменил пункт 9, теперь вместо инициализации вещественных переменных целочисленными значениями рассмотрена вся проблема путаницы между вместо вещественными числами и целыми.

Daniel_Cortez
10.02.2016, 21:16
Ответы на несколько часто задаваемых вопросов. Основано на диалогах, имевших место в последние... полгода (или год?)



Я причитал эту тему, но не согласен со всеми её пунктами.
Ваше право.



Мне больше нравится называть переменные в стиле camelCase, например isHouseOccupied, getPlayerLevel.
Лучше попробуйте так:

if(isPlayerConnected(playerid))
Ах да, ВЫ ЖЕ НЕ МОЖЕТЕ.

Могу

#define isPlayerConnected IsPlayerConnected
Всегда поражала такая упёртость (или упоротость?): готовы напичкать свой код костылями, лишь бы что-то доказать.



Почему ты заставляешь всех кодить только так, как тебе нравится?
Во-первых, в предлагаемом мной стандарте мало общего с лично моими предпочтениями. Основными критериями при создании статьи были логичность и одобрение большинством скриптеров на данном портале.
Во-вторых, статья была сделана частью правил для выкладываемых здесь работ для того, чтобы привести их в порядок и не позволить другим превратить портал в ещё одну свалку говнокода.
Вообще это распространённая практика, особенно во многих организациях, когда программисты договариваются о едином стиле кодинга, чтобы не путаться в коде друг у друга.
Точно так же и здесь: лучше продвигать единый стандарт, чтобы, например, в модах, создающихся по урокам и мануалам с данного портала, авторы не путались из-за половины переменных в lower_case и половины в CamelCase, а также прочих "а мне так удобно", "так сойдёт" или "скажите спасибо, что ещё выложил" от авторов сторонних работ.

Если вы хотите выложить какую-нибудь работу на Pro-Pawn, то перед вами выбор:
присоединиться к сообществу скриптеров, работающих по одному стандарту
или
продолжить и дальше вносить хаос в и без того грязное сообщество SA:MP, слепо твердя о свободе выбора стиля.
При выборе второго варианта имейте в виду: вы имеете право самостоятельно выбирать стиль кодинга, но никто не обязан одобрять ваш выбор. Более того, если вашу работу удаляют с портала, ваша свобода выбора не нарушается.

Alexey_Nikiforov
28.02.2016, 14:26
10 пункт тут при чем?:black_eye:
Граница в 80 символов действительно там не просто так.
Она сделана для мониторов 4:3
И на таких мониторах выглядит примерно так:
http://s017.radikal.ru/i433/1602/bb/02f0b96d4b82.jpg

На 16:9(10) вот так:
http://s017.radikal.ru/i419/1602/50/e2e214e4c126.jpg

Времена калькуляторов ушли а МИФ остался =)

В notepad есть такая функция: "авто перенос строк по ширине окна".
Очень удобная оптимизация кода выходит.

[ForD]
28.02.2016, 14:52
10 пункт тут при чем?:black_eye:
Граница в 80 символов действительно там не просто так.
Она сделана для мониторов 4:3
И на таких мониторах выглядит примерно так:
http://s017.radikal.ru/i433/1602/bb/02f0b96d4b82.jpg

На 16:9(10) вот так:
http://s017.radikal.ru/i419/1602/50/e2e214e4c126.jpg

Времена калькуляторов ушли а МИФ остался =)

В notepad есть такая функция: "авто перенос строк по ширине окна".
Очень удобная оптимизация кода выходит.

при чем тут соотношение сторон??? с 4:3 выставить высокое разрешение будет показываться точно так-же как у вас с 16:9..
У меня ПК к сдоровой плазме подрублен,и если я врублю фул разрешение поддерживающиеся моей видяхой,то какойнить самп рп мод можно будет в 1 строку запихать и идти вдоль экрана читать его..с лупой правда,но все-же..
И кстати не все используют Н++
Так-же насколько понял,имелась ввиду читаемость,когда код идет по полочкам => 80 знаков в длину,намного удобнее читать его,нежели бегать глазами во всему экрану.
Далее,это рекомендации а не упрек делать именно так а не иначе,и теперь если кому-то,что-то не нравится он вполне может оставить мнение при себе,я не думаю что DC постоянно где-то рядом с вами,и если уж вы сделаете не так как он сказал,он выскочит из-за угла и отрежет вам мизинец..

Alexey_Nikiforov
28.02.2016, 15:21
;70417']при чем тут соотношение сторон??? с 4:3 выставить высокое разрешение будет показываться точно так-же как у вас с 16:9..
У меня ПК к сдоровой плазме подрублен,и если я врублю фул разрешение поддерживающиеся моей видяхой,то какойнить самп рп мод можно будет в 1 строку запихать и идти вдоль экрана читать его..с лупой правда,но все-же..
И кстати не все используют Н++
Так-же насколько понял,имелась ввиду читаемость,когда код идет по полочкам => 80 знаков в длину,намного удобнее читать его,нежели бегать глазами во всему экрану.
Далее,это рекомендации а не упрек делать именно так а не иначе,и теперь если кому-то,что-то не нравится он вполне может оставить мнение при себе,я не думаю что DC постоянно где-то рядом с вами,и если уж вы сделаете не так как он сказал,он выскочит из-за угла и отрежет вам мизинец..

Как говорится на фломастеры разные, но мне на много удобнее когда функция записана в одну строку.

$continue$
28.02.2016, 15:29
Как говорится на фломастеры разные, но мне на много удобнее когда функция записана в одну строку.

Очень интересно. А в С++/Java/etc, класс бы тоже записывали в одну строчку?
Удобно же.

Nexius_Tailer
28.02.2016, 15:51
Очень интересно. А в С++/Java/etc, класс бы тоже записывали в одну строчку?
Удобно же.
А мы пишем на C++ и у нас есть классы?

Alexey_Nikiforov
28.02.2016, 16:03
Очень интересно. А в С++/Java/etc, класс бы тоже записывали в одну строчку?
Удобно же.
ну пиши вот так значит:

InterpolateCameraPos
(
playerid
,
1879.984252
,
-2908.610107
,
20.996365
,
1879.984252
,
-2908.610107
,
20.996365
,
100000
)
;

В 1 столбик еще удобнее тебе может покажется.
И с телефона можно код редактировать не заморачиваясь.

Daniel_Cortez
29.02.2016, 19:01
ну пиши вот так значит:

InterpolateCameraPos
(
playerid
,
1879.984252
,
-2908.610107
,
20.996365
,
1879.984252
,
-2908.610107
,
20.996365
,
100000
)
;

В 1 столбик еще удобнее тебе может покажется.
И с телефона можно код редактировать не заморачиваясь.
Пытаетесь перейти из одной крайности в другую? Грязный трюк.
Btw,


Либо так, сгруппировав координаты и углы поворота:


CreateObject(
404,
1138.000, 146.0000, 0.0000, //31
0.0000, 0.0000, 128.0000,
500.0000
);

Или даже так, сэкономив пространство по вертикали:


CreateObject(
404, 1138.000, 146.0000, 0.0000, 0.0000, 0.0000, 128.0000, 500.0000 // 71
);





Граница в 80 символов действительно там не просто так.
Она сделана для мониторов 4:3
Учите матчасть, прежде чем делать такие смелые утверждения.

Blood
25.08.2016, 11:07
new g_arr[10];

public OnGameModeInit()
{
for(new i=0; i<sizeof(arr); i++)
g_arr[i] = random(2);
}
sizeof(arr) не должно ли тут быть g_arr ?


------------------------------------

Исправил \\DC
------------------------------------

Daniel_Cortez
25.08.2016, 21:08
Исправил некоторые ошибки в статье. Заодно добавил пункт 18, в котором объясняется, как сделать удобные настройки для инклуда.

VVWVV
25.08.2016, 22:35
Хотел бы дополнить, что помимо основной защиты (т.е. fileguard), стоит добавить защиту от идентичных имен.

Допустим, что есть файл с именем file1.inc, но помимо него есть какая-то папка, в которой содержится файл с идентичным названием.
Из этих файлов загрузится тот, который подключён раньше.

Пример:


#include <file1> // Файл будет загружен
#include "folder/file1" // Увы, но файл не будет загружен.


Фикс данной ошибки:


#if defined _inc_file1
#undef _inc_file1
#endif


Вы, наверное, видели, что в некоторых файлах YSI используется данный код.

Впрочем, это лишь для тех, кто до сих пор пользуется старым компилятором. В компиляторе от Zeex данная ошибка устранена: отключена автоматическая генерация _inc_* (однако, используя compatibility mode, можно включить).

Daniel_Cortez
24.12.2016, 20:41
Добавил правило №9 - поместил его в середину списка после других правил, связанных с константами.


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


const MAX_HOUSES = 100; // На сервере всего 100 домов.
const INVALID_HOUSE_ID = 255; // Если в player_info[playerid][pHouseKey] число 255, то у игрока нет дома. Почему бы и нет?

Но что, если вы потом захотите создать больше 255 домов? Сделаете 255-й дом на координатах, куда не сможет пробраться обычный игрок (например, под землёй)? Это уже костыль. Решается же эта проблема очень просто.
Хороший пример:


const MAX_HOUSES = 300;
const INVALID_HOUSE_ID = -1; // Число -1 не будет конфликтовать с макс. кол-вом домов, т.к. число домов не может быть отрицательным.

Похожая проблема была в моде GodFather (интересно, кто-нибудь из "нынешнего поколения" видел этот мод?): там числом 255 обозначался неправильный ID игрока. Потом вышел SA-MP 0.3a, в котором к серверу могли подключиться до 500 игроков - тогда 255 перестал быть неправильным ID, что приводило к багам и весьма неожиданным последствиям для тех, кому не повезло зайти под 255-м ID.

Nexius_Tailer
25.12.2016, 00:59
Добавил правило №9 - поместил его в середину списка после других правил, связанных с константами.
Можно также и 65535 использовать вполне, в отличие от 255 это число почти недостягаемо для обычных идов чего-либо

VVWVV
25.12.2016, 01:15
Можно также и 65535 использовать вполне, в отличие от 255 это число почти недостягаемо для обычных идов чего-либо

Если честно, то лучше использовать всё разумно, а не так, как это делают многие: изменяют размерность массива, забывая о данных числах.

Nexius_Tailer
25.12.2016, 01:32
Если честно, то лучше использовать всё разумно, а не так, как это делают многие: изменяют размерность массива, забывая о данных числах.
Суть большинства из пунктов темы в принципе опирается на разумное написание кода. Просто я к тому, что конкретные числа (именно -1 или именно какое-либо ещё) абсолютно непринципиальны, главное просто не делать откровенно тупых действий

Daniel_Cortez
25.12.2016, 08:07
Можно также и 65535 использовать вполне, в отличие от 255 это число почти недостягаемо для обычных идов чего-либо
Вот только оно всё равно остаётся досягаемым, "почти" не считается. Рано или скоро 70-100 тыс. игроков на сервере станут нормой - пусть даже и не в SA-MP, а в каком-нибудь другом мультиплеере, но рано или поздно этот момент наступит.

Думать нужно не исходя из современных реалий, а с расчётом на будущее. И не столько для того, чтобы потом не переписывать свой код (SA-MP стабильно стагнирует, и я очень сомневаюсь, что появится ещё один мультиплеер с таким же плохо продуманным API), сколько чтобы не пришлось переучиваться со старых "вредных" привычек.
Предусмотрительность - одно из качеств, выделяющих хорошего программиста. Это уже давно стало аксиомой.

Seregamil
25.12.2016, 09:45
кому как удобно пусть так и оформляют
Да я внатуре отбитый даун был. Или есть.

Пельмень
25.12.2016, 20:28
Как на счёт самого элементарного обнуления enum? Как тут щас в сампе принятно? Через цикл обнулять или через какой ни будь memcpy
Почему бы просто не делать так?

new eInfo[MAX_PLAYERS][enumInfo],
eResetData[enumInfo];
eInfo[playerid] = eResetData;

DeimoS
26.03.2018, 22:13
Всем выйти из сумрака!


Ещё пример:


const SOME_ARRAY_SIZE = 10;
new some_array[SOME_ARRAY_SIZE];

public OnGameModeInit()
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
some_array[i] = random(2);
}

CMD:printarr(playerid, params[])
{
for (new i = 0; i < SOME_ARRAY_SIZE; i++)
printf("%d", some_array[i]);
}

Здесь размер массива определяется константой, что тоже приемлемо в некоторых ситуациях. Например, во многих системах домов есть константа "MAX_HOUSES" и было бы удобнее использовать её вместо "sizeof(house_info)".


Кстати, вот тут я бы "поспорил".
Смоделируем простую ситуацию: Мы имеем массив и цикл, который "перебирает" массив, установив в качестве условия макрос "MAX_PLAYERS"

new gArray[MAX_PLAYERS];

// .....
for(new i; i < MAX_PLAYERS; i++)
{
printf("%i", gArray[i]);
}

Если вдруг однажды нам понадобится изменить размер массива, установив значение какой-то другой константы, то придётся выискивать все случаи работы с массивом и так же изменять значения. А вот если бы мы использовали sizeof

new gArray[MAX_PICKUPS];

// .....
for(new i; i < sizeof(gArray); i++)
{
printf("%i", gArray[i]);
}
То никаких проблем бы не было.

Поэтому мне кажется, что стоит наоборот по максимуму использовать sizeof там, где код зависим от размерности конкретного массива, дабы впоследствии достаточно было изменить всего 1 строку, а не шарить по всему моду.
Да и, как мне кажется, использование "sizeof" делает код более "говорящим", ибо сразу видно, что цикл зависим от размера конкретного массива, а значит основная работа будет происходить именно с этим массивом.

Ну это моё ИМХО :)

Danny Marcelo
12.05.2020, 08:59
SetObjectMaterialText(
objectid, "Sample text", 0, OBJECT_MATERIAL_SIZE_512x512,
"Arial", 12, 1, 0x00000000, 0xFFFFFFFF, OBJECT_MATERIAL_TEXT_ALIGN_CENTER //77 симв.
);

и


CreateObject(
404,
1138.000, 146.0000, 0.0000, //31 символ
0.0000, 0.0000, 128.0000,
500.0000
);




Не спорю, что бегунок вечно нужно использовать, чтобы посмотреть что там в коде, но для меня лично это выглядит некрасиво, как то всё налеплено, в некоторых случаях как по мне в одну строку лучше написать, просто разделив пробелом для отделения каждой функции, ну каждый видит так, как ему удобнее, но это не значит что нужно всё подряд лепить, как с примером п.6)) А вот за диалоги отдельное спасибо, я слышал ранее про эту функцию, но не вникал, думал сложная она, оказывается ничего сложного, буду теперь ею пользоваться, вместо номерных ид. Только один вопрос, а 0 id диалога таким же образом использовать? Ну допустим мне нужно просто сделать в некоторых командах диалоги с пояснениями, которые никаких функций не несут, кроме как для игрока. Ранее допустим я такие писал так:



ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "Название", "Вы прочитали полезную информацию", "Ок", "");


То есть в enum нужно добавить DIALOG_ID_NULL и в дальнейшем в таких диалогах делать так, я правильно понял?



ShowPlayerDialog(playerid, DIALOG_ID_NULL , DIALOG_STYLE_MSGBOX, "Название", "Вы прочитали полезную информацию", "Ок", "");

SteveStage
12.05.2020, 09:59
А вот за диалоги отдельное спасибо, я слышал ранее про эту функцию, но не вникал, думал сложная она, оказывается ничего сложного, буду теперь ею пользоваться, вместо номерных ид. Только один вопрос, а 0 id диалога таким же образом использовать? Ну допустим мне нужно просто сделать в некоторых командах диалоги с пояснениями, которые никаких функций не несут, кроме как для игрока. Ранее допустим я такие писал так:



ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "Название", "Вы прочитали полезную информацию", "Ок", "");


То есть в enum нужно добавить DIALOG_ID_NULL и в дальнейшем в таких диалогах делать так, я правильно понял?



ShowPlayerDialog(playerid, DIALOG_ID_NULL , DIALOG_STYLE_MSGBOX, "Название", "Вы прочитали полезную информацию", "Ок", "");


Да, именно так, и в коде DIALOG_ID_NULL просто поставь return true;

case DIALOG_ID_NULL: return true;

Shaolinka
13.05.2020, 21:04
Да, именно так, и в коде DIALOG_ID_NULL просто поставь return true;

case DIALOG_ID_NULL: return true;

это ещё зачем?) так или иначе, если нет прописанных действий для указанного ид, то он не выполнится. К прошлому ответу хочу дополнить, что это всего лишь константа, потому если ты напишешь 0, то диалог так или иначе покажется. Лично я использую константу под именем INVALID_DIALOG_ID, дабы соблюдать стандарты SA:MP.