Всем привет. В этой теме я расскажу о простом способе поиска багов в Вашем коде. Для меня стал удивлением тот факт, что 99% пользователей, которые задают вопросы, не знают об этом простом способе.
Введение
И так, представим что Вы написали уникальную систему для вашей очередной уникальной копии Аризоны. Код скомпилировался без ошибок и варнингов, и вроде бы даже работает. Но работает он не так, как было задумано. Что же делать в таком случае? Все просто, добавлять отладку.
Вам понадобятся всего 2 функции - print и printf. Что они делают? Выводят сообщение в консоль. Можно провести аналогию с функцией SendClientMessage. Только SendClientMessage выводит сообщение в чат, а print в консоль.
print
Пример: добавим в public OnGameModeInit функцию print с произвольным текстом
Скомпилируем и запустим сервер. При запуске сервера открывается консоль, но думаю это вы и так знаете. В эту же консоль будет выводить текст функция print.
По итогу в консоли мы увидим следующее:
При запуске сервера вызвался public OnGameModeInit и наша функция print, которая и вывела сообщение в консоль.
Что? И как это поможет найти ошибку?
Тут мы плавно подходим к самому простому варианту поиска неработающего или работающего, но не так как нужно кода. Я бы конечно назвал это гордым словом breakpoint, но к сожалению на сегодняшний день у нас с вами нет таких мощных инструментов для отладки. Ближе к теме. И так, скажем у нас есть некая абстрактная команда, который выдает нам любой аксессуар, пусть это будет шляпа, или попугай - не важно. Весь код, написанный ниже и далее по теме будет максимально плохим во всех его проявлениях. Но - он будет максимально реалистичным, и если посмотреть темы с вопросами, то большинство кода там будет именно таким.
Здесь мы будем использовать print после каждого условия, чтобы понять, выполняется оно или нет. Если сообщение в консоли появилось - условие прошло. Если нет - нет. Добавляем print.
Мы добавили print после каждой строчки кода и теперь в консоли мы сможем наблюдать за его выполнением.
Запускаем сервер, заходим в игру и вводим команду /datigrokuaksy. Мы видим сообщение в чате "Введите /datigrokuaksy {ID игрока} {ID аксессуара}".
В консоли мы увидим следующее:
А где остальные сообщения? - спросите вы. В этом случае все верно, потому что первом условии у нас есть return, который пропускает весь код ниже. Тем самым можно понять, что условие со sscanf выполнилось. Теперь проверим, выполняется ли условие со sscanf с аргументами.
Вводим /datigrokuasky 24 10. И видим что в чате сообщения не было. А у игрока с ID 24 шляпы так и не появилось. Открываем консоль и видим там следующее:
Проверка sscanf прошла, а на фулл доступ не прошла. Как же так? Ведь я добавил себя в БД, во все define да и вообще сервер мой и я заплатил за его хостинг 80 рублей. А у меня не работает главная фишка Аризоны.
Теперь включаем логику - если в консоли не было сообщения о том, что проверка на фулл доступ прошла - значит проблема именно в нем. Смотрим в чем же у нас проблема. Функция fulldostup возвращает 1, если доступ есть и 0, если доступа нет. В нашем случае доступ есть и функция вернула 1. Условие стало true и выполнился результат - return. Весь код ниже пропущен. В данном случае был пропущен "!" перед условием, добавляем и смотрим.
Компилируем, перезапускаем сервер и заходим. Вводим команду /datigrokuasky 24 10 и счастливый игрок с ID 24 получает свою шляпу. Все ошибки исправлены и команда отработала так, как и было задумано.
В консоли видим следующее:
Теперь разберем более простой пример
Если мы запустим этот код, то в консоли мы увидим следующее:
Первое условие. Если a (1) + b (4) = 5, условие true, т.к. 1 + 4 действительно равно 5. Выводим сообщение в консоль.
Второе условие. Если b (4) - a (1) = 0, условие false, т.к. 4 - 1 = 3, а не 0. Соответственно условие не выполнено и функция print вызвана не будет.
Третье условие. Если а (1) = 1, условие true, 1 действительно равен 1. Функция print будет вызвана.
Четвертое условие. Если b (4) = 2. 4 не равно 2, условие false, print не будет вызвана.
На этом с функцией print закончим. Мы поняли, что она выводит сообщение в консоль и с помощью нее мы можем проверить, выполняется ли какое-нибудь условие или функция.
printf
Теперь поговорим о функции printf. Буква f в конце означает format. С помощью этой функции мы сможем выводить в консоль значения переменных и результаты выполнения функций. Почему бы тогда просто не создать переменную, записать в нее нужное нам сообщение при помощи функции format и не вывести это через print. Во первых с printf вам не нужно создавать переменных и использовать format - это банально быстрей и удобней. Во вторых - мы не создаем переменных и не занимаем лишнюю память. Отладка отдельно от кода - верное решение.
Функция printf принимает 2 аргумента - это текст и специфицаторы, такие же, как и в функции format
Разберем простой пример
Результатом выполнения кода выше будет сообщение в консоль:
Как мы видим, все просто. Синтаксис практически такой же, как у функции format, исключая создание переменных. Теперь давайте усложним задачу и возьмем код из первого примера.
Вводим команду и видим что ничего не работает. Смотрим в консоль:
Что? Я же выдавал себе фулл доступ. В чем же проблема?
Тут то нам и поможет функция printf, которая сможет показать нам результат выполнения функции. Как мы помним, функция fulldostup возвращает 0 или 1. 0 и 1 - это целое число. Спецификатор для него будет %i или %d, без разницы.
Наша функция будет следующего вида:
Теперь такой момент. В каком месте нам ее разместить. Ответ - в любом, до условия, которое не выполняется. Условие с функцией fulldostup у нас не выполняется, соответственно после него размещать нашу printf смысла просто нет - не выполнится условие, не выполнится и функция. Я размещу перед условием.
Запускаем, вводим команду /datigrokuaksy 24 10, опять же ничего не происходит. Смотрим в консоль:
И видим что проблема в нашей функции. Смотрим - а фулл доступ то мы себе и не дали, т.к. мы уже скачали новую копию аризоны и еще не настроили ее.
Вот так просто с помощью функций print и printf можно находить ошибки и исправлять их самостоятельно, без создания темы с вопросом и ожиданием ответа сутками.
Введение
И так, представим что Вы написали уникальную систему для вашей очередной уникальной копии Аризоны. Код скомпилировался без ошибок и варнингов, и вроде бы даже работает. Но работает он не так, как было задумано. Что же делать в таком случае? Все просто, добавлять отладку.
Вам понадобятся всего 2 функции - print и printf. Что они делают? Выводят сообщение в консоль. Можно провести аналогию с функцией SendClientMessage. Только SendClientMessage выводит сообщение в чат, а print в консоль.
Пример: добавим в public OnGameModeInit функцию print с произвольным текстом
C-like:
public OnGameModeInit()
{
print("Моя доработка аризоны загружена успешно!");
return 1;
}
Скомпилируем и запустим сервер. При запуске сервера открывается консоль, но думаю это вы и так знаете. В эту же консоль будет выводить текст функция print.
По итогу в консоли мы увидим следующее:
Моя доработка аризоны загружена успешно!
При запуске сервера вызвался public OnGameModeInit и наша функция print, которая и вывела сообщение в консоль.
Что? И как это поможет найти ошибку?
Тут мы плавно подходим к самому простому варианту поиска неработающего или работающего, но не так как нужно кода. Я бы конечно назвал это гордым словом breakpoint, но к сожалению на сегодняшний день у нас с вами нет таких мощных инструментов для отладки. Ближе к теме. И так, скажем у нас есть некая абстрактная команда, который выдает нам любой аксессуар, пусть это будет шляпа, или попугай - не важно. Весь код, написанный ниже и далее по теме будет максимально плохим во всех его проявлениях. Но - он будет максимально реалистичным, и если посмотреть темы с вопросами, то большинство кода там будет именно таким.
C-like:
cmd:datigrokuaksy(playerid, params[])
{
if(sscanf(params, "dd", params[0], params[1]))
return SCM(playerid, RANDOMCOLOR, "Введите: "BELIY_CVET"/datigrokuaksy {ID игрока} {ID аксессуара}");
if(fulldostup(playerid)) return 0;
AttachAksNaIgroka(params[0], params[1]);
new stringer[4096];
format(stringer, 2048, ""ZELENIY_CVET"Администратор %s выдал вам аксессуар \"ШЛЯПА\"", GetImyaIgroka(playerid));
SCM(params[0], BELIY_CVET, stringer);
return 1;
}
C-like:
cmd:datigrokuaksy(playerid, params[])
{
print("Команда вызвана");
if(sscanf(params, "dd", params[0], params[1]))
return SCM(playerid, RANDOMCOLOR, "Введите: "BELIY_CVET"/datigrokuaksy {ID игрока} {ID аксессуара}");
print("Проверка sscanf прошла");
if(fulldostup(playerid)) return 0;
print("Проверка на фулл доступ прошла");
AttachAksNaIgroka(params[0], params[1]);
print("Аксессуар был прицеплен к игроку");
new stringer[4096];
print("Переменная была создана"); // Тут print не обязательна, просто для примера
format(stringer, 2048, ""ZELENIY_CVET"Администратор %s выдал вам аксессуар \"ШЛЯПА\"", GetImyaIgroka(playerid));
print("Отформатировали строку"); // Тут print не обязательна, просто для примера
SCM(params[0], BELIY_CVET, stringer);
print("Команда выполнена успешно");
return 1;
}
Запускаем сервер, заходим в игру и вводим команду /datigrokuaksy. Мы видим сообщение в чате "Введите /datigrokuaksy {ID игрока} {ID аксессуара}".
В консоли мы увидим следующее:
Команда вызвана
А где остальные сообщения? - спросите вы. В этом случае все верно, потому что первом условии у нас есть return, который пропускает весь код ниже. Тем самым можно понять, что условие со sscanf выполнилось. Теперь проверим, выполняется ли условие со sscanf с аргументами.
Вводим /datigrokuasky 24 10. И видим что в чате сообщения не было. А у игрока с ID 24 шляпы так и не появилось. Открываем консоль и видим там следующее:
Команда вызвана
Проверка sscanf прошла
Проверка sscanf прошла, а на фулл доступ не прошла. Как же так? Ведь я добавил себя в БД, во все define да и вообще сервер мой и я заплатил за его хостинг 80 рублей. А у меня не работает главная фишка Аризоны.
Теперь включаем логику - если в консоли не было сообщения о том, что проверка на фулл доступ прошла - значит проблема именно в нем. Смотрим в чем же у нас проблема. Функция fulldostup возвращает 1, если доступ есть и 0, если доступа нет. В нашем случае доступ есть и функция вернула 1. Условие стало true и выполнился результат - return. Весь код ниже пропущен. В данном случае был пропущен "!" перед условием, добавляем и смотрим.
C-like:
cmd:datigrokuaksy(playerid, params[])
{
print("Команда вызвана");
if(sscanf(params, "dd", params[0], params[1]))
return SCM(playerid, RANDOMCOLOR, "Введите: "BELIY_CVET"/datigrokuaksy {ID игрока} {ID аксессуара}");
print("Проверка sscanf прошла");
if(!fulldostup(playerid)) return 0; // Исправление здесь
print("Проверка на фулл доступ прошла");
AttachAksNaIgroka(params[0], params[1]);
print("Аксессуар был прицеплен к игроку");
new stringer[4096];
print("Переменная была создана"); // Тут print не обязательна, просто для примера
format(stringer, 2048, ""ZELENIY_CVET"Администратор %s выдал вам аксессуар \"ШЛЯПА\"", GetImyaIgroka(playerid));
print("Отформатировали строку"); // Тут print не обязательна, просто для примера
SCM(params[0], BELIY_CVET, stringer);
print("Команда выполнена успешно");
return 1;
}
В консоли видим следующее:
Команда вызвана
Проверка sscanf прошла
Проверка на фулл доступ прошла
Аксессуар был прицеплен к игроку
Переменная была создана
Отформатировали строку
Команда выполнена успешно
Теперь разберем более простой пример
C-like:
new a = 1;
new b = 4;
if((a + b) == 5) print("a + b = 5");
if((b - a) == 0) print("b - a = 0");
if(a == 1) print("Значение a = 1");
if(b == 2) print("Значение b = 2");
И так, переменная a у нас равна 1, переменная b - 4.a + b = 5
Значение a = 1
Первое условие. Если a (1) + b (4) = 5, условие true, т.к. 1 + 4 действительно равно 5. Выводим сообщение в консоль.
Второе условие. Если b (4) - a (1) = 0, условие false, т.к. 4 - 1 = 3, а не 0. Соответственно условие не выполнено и функция print вызвана не будет.
Третье условие. Если а (1) = 1, условие true, 1 действительно равен 1. Функция print будет вызвана.
Четвертое условие. Если b (4) = 2. 4 не равно 2, условие false, print не будет вызвана.
На этом с функцией print закончим. Мы поняли, что она выводит сообщение в консоль и с помощью нее мы можем проверить, выполняется ли какое-нибудь условие или функция.
printf
Теперь поговорим о функции printf. Буква f в конце означает format. С помощью этой функции мы сможем выводить в консоль значения переменных и результаты выполнения функций. Почему бы тогда просто не создать переменную, записать в нее нужное нам сообщение при помощи функции format и не вывести это через print. Во первых с printf вам не нужно создавать переменных и использовать format - это банально быстрей и удобней. Во вторых - мы не создаем переменных и не занимаем лишнюю память. Отладка отдельно от кода - верное решение.
Функция printf принимает 2 аргумента - это текст и специфицаторы, такие же, как и в функции format
%i, %d | Целое число |
%s | Строка |
%f | Число с плавающей точкой, в pawn переменные данного типа начинаются с тега Float: |
%c | ASCII символ, принимает в себя целое число, которое является номером символа. Подробней вы можете ознакомиться тут: https://ru.wikipedia.org/wiki/ASCII |
%x | Число в шестнадцатеричной системе счисления |
%b | Число в двоичной системе счисления |
%% | Знак процента (%) |
%q | Экранирование специальных символов для SQLite (Начиная с версии 0.3.7) |
Разберем простой пример
C-like:
new a = 1;
new b = 4;
printf("Значение a - %i, b - %i", a, b);
Значение а - 1, b - 4
Как мы видим, все просто. Синтаксис практически такой же, как у функции format, исключая создание переменных. Теперь давайте усложним задачу и возьмем код из первого примера.
C-like:
cmd:datigrokuaksy(playerid, params[])
{
print("Команда вызвана");
if(sscanf(params, "dd", params[0], params[1]))
return SCM(playerid, RANDOMCOLOR, "Введите: "BELIY_CVET"/datigrokuaksy {ID игрока} {ID аксессуара}");
print("Проверка sscanf прошла");
if(!fulldostup(playerid)) return 0;
print("Проверка на фулл доступ прошла");
AttachAksNaIgroka(params[0], params[1]);
print("Аксессуар был прицеплен к игроку");
new stringer[4096];
print("Переменная была создана");
format(stringer, 2048, ""ZELENIY_CVET"Администратор %s выдал вам аксессуар \"ШЛЯПА\"", GetImyaIgroka(playerid));
print("Отформатировали строку");
SCM(params[0], BELIY_CVET, stringer);
print("Команда выполнена успешно");
return 1;
}
Команда вызвана
Проверка sscanf прошла
Что? Я же выдавал себе фулл доступ. В чем же проблема?
Тут то нам и поможет функция printf, которая сможет показать нам результат выполнения функции. Как мы помним, функция fulldostup возвращает 0 или 1. 0 и 1 - это целое число. Спецификатор для него будет %i или %d, без разницы.
Из старой темы:
Лично я использую %i, т.к. это у меня ассоциируется с integer. Но опять же, в реальных задачах никакой разницы нет.%i от %d отличается тем, что %i может принимать числа как в привычной нам десятеричной системой счисления (цифры от 0 до 9), так и в 16-ти и 8-миричных системах счисления.
Наша функция будет следующего вида:
C-like:
printf("Результат выполнения функции fulldostup для игрока ID %i, - %i", playerid, fulldostup(playerid));
C-like:
cmd:datigrokuaksy(playerid, params[])
{
print("Команда вызвана");
if(sscanf(params, "dd", params[0], params[1]))
return SCM(playerid, RANDOMCOLOR, "Введите: "BELIY_CVET"/datigrokuaksy {ID игрока} {ID аксессуара}");
print("Проверка sscanf прошла"); // Последнее, что мы видим в консоли
// Вот наша функция printf
printf("Результат выполнения функции fulldostup для игрока ID %i, - %i", playerid, fulldostup(playerid));
if(!fulldostup(playerid)) return 0; // Это условие уже не выполняется
print("Проверка на фулл доступ прошла");
AttachAksNaIgroka(params[0], params[1]);
print("Аксессуар был прицеплен к игроку");
new stringer[4096];
print("Переменная была создана");
format(stringer, 2048, ""ZELENIY_CVET"Администратор %s выдал вам аксессуар \"ШЛЯПА\"", GetImyaIgroka(playerid));
print("Отформатировали строку");
SCM(params[0], BELIY_CVET, stringer);
print("Команда выполнена успешно");
return 1;
}
Команда вызвана
Проверка sscanf прошла
Результат выполнения функции fulldostup для игрока ID 23, - 0
И видим что проблема в нашей функции. Смотрим - а фулл доступ то мы себе и не дали, т.к. мы уже скачали новую копию аризоны и еще не настроили ее.
Вот так просто с помощью функций print и printf можно находить ошибки и исправлять их самостоятельно, без создания темы с вопросом и ожиданием ответа сутками.
Последнее редактирование: