Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.

Реклама



**Как получить V.I.P** (Перейти)
Чтобы заказать рекламу на Pro-Pawn.Ru, обращайтесь в Skype.
Баннерная реклама 100руб/мес, Текстовая 50руб/мес.
Страница 1 из 7 123 ... ПоследняяПоследняя
Показано с 1 по 10 из 61
  1. #1
    Аватар для Daniel_Cortez
    new fuck_logic[0] = EOS;

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    1,419
    Репутация:
    1998 ±

    Подсчёт размера форматируемой строки

    Меня не раз спрашивали, каким образом я рассчитываю размер строк в своих работах (многие из вас могли видеть всякие на первый взгляд сложные формулы типа "sizeof(fmt_str)-2+11-2+MAX_PLAYER_NAME-2+etc."). Поэтому вместо того, чтобы расписывать это всё для каждого отдельно, будет намного лучше составить для всех один урок. Заодно рассмотрим самые частые быдлокодерские ошибки.
    Итак, поехали.




    Допустим, у нас есть команда для вывода ID игрока и его никнейма.
    Исходный вид команды:
    PHP код:
    CMD:myinfo(playeridparams[])
    {
        new 
    string[128];
        new 
    name[24];
        
    GetPlayerName(playeridnamesizeof(name));
        
    format(stringsizeof(string), "Ваш ID: %d, никнейм: %s."playeridname);
        return 
    SendClientMessage(playerid, -1string);

    Теперь разберём этот код.
    В первую очередь бросается в глаза массив name, вернее его размер "24". Так обычно пишут только быдлокодеры.
    Во-первых, размер ника игрока может измениться в будущих версиях SA:MP. В то же время названия констант в новых версиях не меняют. Поэтому, чтобы с выходом новой версии не пришлось по-быдлокодерски заменять все "24" на новые числа, лучше использовать константу.
    Во-вторых, с помощью SetPlayerName возможна установка никнейма длиной до 24 (MAX_PLAYER_NAME) символов (см. здесь), а в строке нужна ещё одна позиция для завершающего нуль-символа ('\0'). Отсюда получаем максимум "MAX_PLAYER_NAME + 1".
    Пэтому следует переделать объявление массива name следующим образом:
    PHP код:
    new name[MAX_PLAYER_NAME 1]; 
    Едем дальше.
    Пока мы получаем никнейм игрока в name, массив string никак не используется.
    Можно сэкономить место в стеке, получив никнейм сразу в string и затем сформатировать содержимое массива в тот же самый массив:
    PHP код:
        new string[128];
        
    GetPlayerName(playeridstringsizeof(string));
        
    format(stringsizeof(string), "Ваш ID: %d, никнейм: %s."playeridstring); 
    А теперь самое главное: рассчитаем оптимальный размер массива string.
    Начнём с того, что вынесем форматную строку из format в отдельный массив, объявив его с помощью ключевых слов static и const.
    PHP код:
        static const fmt_str[] = "Ваш ID: %d, никнейм: %s.";
        new 
    string[128];
        
    GetPlayerName(playeridstringsizeof(string));
        
    format(stringsizeof(string), fmt_strplayeridstring); 
    Теперь составим формулу для расчёта размера массива string.
    Сначала возьмём размер массива с форматной строкой: sizeof(fmt_str).
    Функция format убирает форматные спецификаторы ("%d", "%i", "%s", etc.) и ставит на их место форматируемые значения.
    Поэтому мы в формуле тоже будем убирать длину спецификаторов и прибавлять максимальную длину значений.
    В общем виде формула будет выглядеть так:
    PHP код:
    sizeof(fmt_str)-<длина спецификатора 1>+<максдлина значения 1>-<длина специф2>+<максдлина знач2>-...-<длина специфN>+<максдлина значN
    Также для удобства можно отделить друг от друга пары "спецификатор-значение", взяв их в скобки:
    PHP код:
    sizeof(fmt_str) + (-<длина специф1>+<максдлина знач1>) + (-<длина специф2>+<максдлина знач2>) + ... + (-<длина специфN>+<максдлина значN>) 
    Рассчитаем формулу размера string для нашего случая. Сначала идёт спецификатор "%d". Его длина 2 символа, но нужно ещё узнать максимальную длину для целых чисел.
    Максимальное целочисленное значение: 2147483647 (10 символов), минимальное: -2147483648 (11 символов), следовательно макс. длина будет 11.
    Но следует иметь в виду, что это не просто число, а ID игрока.
    В SA:MP вряд ли будет возможно сделать более 65535 игроков: это что-то вроде лимита, который можно обнаружить в некоторых местах с помощью дизассемблирования кода сервера, а учитывая, что в SA:MP добавляют всего пару мелких фич и несколько новых моделек раз в год... вряд ли лимит игроков изменится.
    Этот лимит проявляется в виде бага в функциях SendDeathMessage и SendDeathMessageToPlayer: Kalcor вместо MAX_PLAYERS сделал сравнение ID игрока с INVALID_PLAYER_ID (65535), из-за чего эти две функции считают правильными не только ID от 0 до 999, но и от 1000 до INVALID_PLAYER_ID - 1 (65534) (иногда такой баг может оказаться полезным).
    65535 - 5 символов. Следовательно, нужно из sizeof(fmt_str) отнять 2 символа (длина спецификатора "%d") и прибавить 5.
    Дальше идёт "%s", на место этого спецификатора ставится никнейм игрока, максимальная длина которого - MAX_PLAYER_NAME символов (не путать с "MAX_PLAYER_NAME + 1" - в форматированную строку завершающий нуль-символ не попадает, поэтому лишняя ячейка не нужна).
    В итоге получаем формулу:
    PHP код:
    sizeof(fmt_str) + (-2+5) + (-2+MAX_PLAYER_NAME
    Настоятельно рекомендую оформлять формулу расчёта длины именно таким образом, заключая подсчёт для каждого форматного спецификатора в отдельную пару скобок для большей наглядности.

    Наконец, подставим полученную формулу в код:
    PHP код:
    CMD:myinfo(playeridparams[])
    {
        static const 
    fmt_str[] = "Ваш ID: %d, никнейм: %s.";
        new 
    string[sizeof(fmt_str) + (-5) + (-MAX_PLAYER_NAME)];
        
    GetPlayerName(playeridstringsizeof(string));
        
    format(stringsizeof(string), fmt_strplayeridstring);
        return 
    SendClientMessage(playerid, -1string);




    Немного усложним задачу: кроме ID и никнейма игрока будем выводить его здоровье.
    Рассчитаем размер буфера для форматирования. Данный пример похож на предыдущий, просто был добавлен вывод вещ. числа, поэтому можно просто взять размер этого числа, вычесть из предыдущей формулы длину спецификатора "%.0f" и прибавить максимальную длину числа.
    Новая форматная строка будет выглядеть так:
    PHP код:
    static const fmt_str[] = "Ваш ID: %d, никнейм: %s, здоровье: %.0f."
    Функция GetPlayerHealth возвращает вещественные числа от 0 до 255. Если здоровье игрока не находится в этом диапазоне, то функция вычисляет остаток от целочисленного деления здоровья на 256.
    При этом дробная часть у возвращаемого здоровья отсутствует, а значит её вместе с запятой можно не выводить.
    Исходя из этого, получаем максимальную длину выводимого числа в 3 символа.
    PHP код:
    CMD:myinfo2(playeridparams[])
    {
        static const 
    fmt_str[] = "Ваш ID: %d, никнейм: %s, здоровье: %.0f.";
        
    // Обратите внимание: размер спецификатора "%.0f" не 2, а 4 символа.
        
    new string[sizeof(fmt_str) + (-5) + (-MAX_PLAYER_NAME) + (-3)];
        new 
    Float:health;
        
    GetPlayerName(playeridstringsizeof(string));
        
    GetPlayerHealth(playeridhealth);
        
    format(stringsizeof(string), fmt_strplayeridstringhealth);
        return 
    SendClientMessage(playerid, -1string);




    Ещё один пример: выведем координаты игрока.
    Числа будут выводиться с точностью в 2 знака после запятой.
    Длина "%.4f" - 4 символа. Но нужно как-то узнать макс. длину вещественного числа.
    Минимальное значение координаты Z: -100 - такое значение возможно, если игрок провалится под текстуру. При высоте меньше -100 его телепортирует обратно на поверхность, так устроен движок GTA:SA.
    Максимум Z ограничен максимальным значением, возможным для типа Float, но нам вряд ли понадобятся числа больше или равные 1 000 000, т.к. на больших координатах графика в GTA:SA становится глючной и замедляется процесс отрисовки.
    Получаем для координаты Z максимум в 6 символов. При этом спецификатор числа "%.4f" будет занимать 4 символа.
    Примерно то же самое с координатами X и Y, только там возможны отрицательные числа меньше -100, а значит наравне с положительными числами могут выводиться отрицательные и следует добавить ещё 1 символ под знак минуса.
    В итоге для Z получаем 6 символов, для X и Y - 7 символов. Добавим к этому два знака после запятой и саму запятую и получим 9 и 10 для Z и X/Y соответственно.
    PHP код:
    CMD:getpos(playeridparams[])
    {
        static const 
    fmt_str[] = "Ваши координаты: X = %.4f, Y = %.4f, Z = %.4f.";
        new 
    string[sizeof(fmt_str) + (-10) * + (-9)];
        new 
    Float:xFloat:yFloat:z;
        
    GetPlayerPos(playeridxyz);
        
    format(stringsizeof(string), fmt_strxyz);
        return 
    SendClientMessage(playerid, -1string);




    И последний пример: выведем ID игрока, никнейм и название машины.
    Стандартными средствами SA:MP нельзя узнать название модели транспорта. Тем не менее, можно самостоятельно составить массив названий и брать нужное название по номеру модели транспорта.
    PHP код:
    // В размере "- 399" означает пропущенные ID от 1 до 399,
    // а "+ 1" - самая первая строка, отведённая под 0-ю (несуществующую) модель транспорта.
    new const vehicle_models_names[611 399 1][] =
    {
        
    /*   0 */ {!"No vehicle"},    // Для случаев, когда (model == 0), т.е. когда игрок не в машине.
        /* 400 */ 
    {!"Landstalker"},    // Также обратите внимание, что строки с названиями упакованные.
        /* 401 */ 
    {!"Bravura"},
        
    /* ... */
        /* 611 */ 
    {!"Utility Trailer"}
    }

    stock GetVehicleModelName(modelnamesize sizeof(name))
    {
        
    // Если номер модели неизвестен (в т.ч. нулевой)...
        
    if (model 400 || 611 model)
            return 
    strunpack(namevehicle_models_names[0], size);
        
    // Модели #400 соответствует название в vehicle_models_names[1], а не [0],
        // поэтому следует прибавить 1 к индексу.
        
    return strunpack(namevehicle_models_names[model 400 1], size);

    Теперь приступим к составлению формулы длины форматной строки.
    Что имеем: ID (в предыдущих примерах обусловиись выделять под него 5 символов), ник (MAX_PLAYER_NAME символов) и название модели транспорта.
    Максимальную длину названия транспорта можно определить с помощью выражения "sizeof(vehicle_models_names[]) - 1".
    Фигурные скобки после названия массива означают, что мы берём размер его второго измерения, а "- 1" добавлено в конец формулы потому, что для формулы нужно определить длину, а не размер её составляющих.
    По той же причине в формуле записывается "MAX_PLAYER_NAME", а не "MAX_PLAYER_NAME + 1". Завершающий нуль-символ уже учтён в "sizeof(fmt_str)".
    Получаем следующую формулу:
    PHP код:
    new fmt_str[] = "Ваш ID: %d, ник: %s, транспорт: %s.";
    const 
    string_size sizeof(fmt_str) + (-2+5) +
        (-
    2+MAX_PLAYER_NAME) + (-2+sizeof(vehicle_models_names[]);
    new 
    string[string_size]; 
    Теперь запишем всю команду целиком:
    PHP код:
    CMD:myinfo3(playeridparams[])
    {
        new 
    fmt_str[] = "Ваш ID: %d, ник: %s, транспорт: %s.";
        const 
    string_size sizeof(fmt_str) + (-2+5) +
            (-
    2+MAX_PLAYER_NAME) + (-2+sizeof(vehicle_models_names[]);
        new 
    string[string_size];
        new 
    name[MAX_PLAYER_NAME 1], vehicle_name[sizeof(vehicle_models_names[])];
        
    GetPlayerName(playeridnamesizeof(name));
        
    GetVehicleModelName(GetPlayerVehicleID(playerid), vehicle_name);
        
    format(stringsizeof(string), fmt_strplayeridnamevehicle_name);
        return 
    SendClientMessage(playerid, -1string);

    Как и в самом первом примере, переменную name можно убрать, записывая ник прямиком в string.
    PHP код:
    CMD:myinfo3(playeridparams[])
    {
        new 
    fmt_str[] = "Ваш ID: %d, ник: %s, транспорт: %s.";
        const 
    string_size sizeof(fmt_str) + (-2+5) +
            (-
    2+MAX_PLAYER_NAME) + (-2+sizeof(vehicle_models_names[]);
        new 
    string[string_size];
        new 
    vehicle_name[sizeof(vehicle_models_names[])];
        
    GetPlayerName(playeridstringsizeof(string));
        
    GetVehicleModelName(GetPlayerVehicleID(playerid), vehicle_name);
        
    format(stringsizeof(string), fmt_strplayeridstringvehicle_name);
        return 
    SendClientMessage(playerid, -1string);

    Но и это ещё не всё. Переменную vehicle_name тоже можно убрать, а название транспорта записывать - угадайте, куда? Тоже в string, но только не в самое начало массива, а с позиции, на которой закончится ник игрока, т.е. с MAX_PLAYER_NAME + 1.
    Места в массиве string должно хватить, т.к. в его размер (string_size) уже входят и длина ника, и длина названия транспорта.
    PHP код:
    CMD:myinfo3(playeridparams[])
    {
        new 
    fmt_str[] = "Ваш ID: %d, ник: %s, транспорт: %s.";
        const 
    string_size sizeof(fmt_str) + (-2+5) +
            (-
    2+MAX_PLAYER_NAME) + (-2+sizeof(vehicle_models_names[]);
        new 
    string[string_size];
        
    GetPlayerName(playeridstringsizeof(string));
        
    GetVehicleModelName(GetPlayerVehicleID(playerid), string[MAX_PLAYER_NAME 1]);
        
    format(
            
    stringsizeof(string), fmt_str,
            
    playeridstringstring[MAX_PLAYER_NAME 1]
        );
        return 
    SendClientMessage(playerid, -1string);




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


    Автор: Daniel_Cortez

    Специально для Pro-Pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора запрещено!

  2. 18 пользователя(ей) сказали cпасибо:
    #NickName (07.02.2016)$continue$ (07.02.2016)Anton Styazhkin (07.02.2016)Astrakhan30 (27.05.2016)BadPawn (08.02.2016)DeimoS (07.02.2016)franked (20.02.2016)kushichka (25.06.2016)Londlem (07.02.2016)Nexius_Tailer (08.02.2016)Nurick (08.02.2016)Processing (15.02.2016)Profyan (07.02.2016)Quays (15.10.2016)Quman (07.02.2016)Redsan (21.06.2016)VVWVV (07.02.2016)[ForD] (26.02.2016)
  3. #2
    Аватар для Anton Styazhkin
    Модератор

    Статус
    Оффлайн
    Регистрация
    15.03.2015
    Адрес
    Slobodskoy
    Сообщений
    648
    Репутация:
    224 ±
    Еще упомяни, что в "\n, \t, ..." и т.д - это один символ.
    code is love

  4. 2 пользователя(ей) сказали cпасибо:
    franked (20.02.2016)Processing (13.03.2016)
  5. #3
    Аватар для Profyan
    Пользователь

    Статус
    Оффлайн
    Регистрация
    23.12.2013
    Адрес
    Омск
    Сообщений
    196
    Репутация:
    22 ±
    Сколько в среднем у вас занимает времени расчет строки?



    Любой дурак может написать код, понятный компьютеру. Хороший программист пишет код, понятный человеку
    Мартин Фаулер


    Skype
    profan99
    VK
    click




  6. #4
    Аватар для Daniel_Cortez
    new fuck_logic[0] = EOS;

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    1,419
    Репутация:
    1998 ±
    Цитата Сообщение от Profyan Посмотреть сообщение
    Сколько в среднем у вас занимает времени расчет строки?
    На первый пример из урока у меня ушло ~40 секунд, с учётом времени ввода форматной строки и объявления форматного буфера с формулой расчёта. А так всё это больше зависит больше от сложности алгоритма, для которого нужно провести подсчёт.


    Цитата Сообщение от Untonyst Посмотреть сообщение
    Еще упомяни, что в "\n, \t, ..." и т.д - это один символ.
    Напишу об этом чуть позже, как только добавлю ещё один пример с диалогом.

  7. Пользователь сказал cпасибо:
    Profyan (07.02.2016)
  8. #5
    Аватар для $continue$
    Пользователь

    Статус
    Оффлайн
    Регистрация
    02.08.2014
    Адрес
    г. Киров (aka Вятка)
    Сообщений
    1,243
    Репутация:
    219 ±
    Опечатка?
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    static const fmt_str[] = "Ваш ID: %d, никнейм: %s, здоровье: %.0f.";
    // Обратите внимание: размер спецификатора "%.0f" на 2, а 4 символа.
    Value your freedom or you will lose it, teaches history. "Don't bother us with politics," respond those who don't want to learn. (c) Richard Stallman

  9. Пользователь сказал cпасибо:
    Daniel_Cortez (07.02.2016)
  10. #6
    Аватар для _lizard
    Пользователь

    Статус
    Онлайн
    Регистрация
    11.02.2016
    Адрес
    Геленджик
    Сообщений
    139
    Репутация:
    23 ±
    A как быть, если нужно два и более раза отформатировать массив?

  11. #7
    Аватар для VVWVV
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    358
    Репутация:
    171 ±
    Цитата Сообщение от _lizard Посмотреть сообщение
    A как быть, если нужно два и более раза отформатировать массив?
    PHP код:
    static const
        
    fmt_str_0[] = "something %s",
        
    fmt_str_1[] = "something %d";

    const
        
    fmt_str_0_size sizeof fmt_str_0 + (-MAX_PLAYER_NAME),
        
    fmt_str_1_size sizeof fmt_str_1 + (-4);

    #if fmt_str_0_size > fmt_str_1_size
        #define size fmt_str_0_size
        // Либо const size = fmt_str_0_size;
    #else
        #define size fmt_str_1_size
        // Либо const size = fmt_str_1_size;
    #endif
    new buffer[size];

    // Если используете const, то данная директива не нужна.
    #undef size 
    Последний раз редактировалось VVWVV; 05.03.2016 в 21:10.

  12. Пользователь сказал cпасибо:
    _lizard (26.02.2016)
  13. #8
    Аватар для _lizard
    Пользователь

    Статус
    Онлайн
    Регистрация
    11.02.2016
    Адрес
    Геленджик
    Сообщений
    139
    Репутация:
    23 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    PHP код:
    new buffer[size]; 
    Мм, немного не догнал, зачем создавать третий массив, если можно использовать два первых?

  14. #9
    Аватар для VVWVV
    Пользователь

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    358
    Репутация:
    171 ±
    Цитата Сообщение от _lizard Посмотреть сообщение
    Мм, немного не догнал, зачем создавать третий массив, если можно использовать два первых?
    Данный массив создан для того, чтобы поместить в него уже отформатированный текст (например, с помощью функции format). С помощью препроцессора мы выбираем самую длинную строку, после чего записываем в макрос, который в дальнейшем будет размером массива (точнее, то, что он подставит вместо себя). Если говорить о двух первых строках, то они сохраняются в сегменте данных.

  15. #10
    Аватар для Daniel_Cortez
    new fuck_logic[0] = EOS;

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    1,419
    Репутация:
    1998 ±
    Цитата Сообщение от VVWVV Посмотреть сообщение
    PHP код:
    static const
        
    fmt_str_0[] = "something %s",
        
    fmt_str_1[] = "something %d";

    const
        
    fmt_str_0_size sizeof fmt_str_0 + (-MAX_PLAYER_NAME),
        
    fmt_str_1_size sizeof fmt_str_1 + (-4);

    #if fmt_str_0_size > fmt_str_1_size
        #define size fmt_str_0_size
    #else
        #define size fmt_str_1_size
    #endif
    new buffer[size]; 
    "#undef size" забыл. А вообще можно было объявить константу (const) вместо макроса.

  16. Пользователь сказал cпасибо:
    VVWVV (27.02.2016)
 

 
Страница 1 из 7 123 ... ПоследняяПоследняя

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

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

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

Ваши права

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