Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Страница 1 из 3 1 2 3 ПоследняяПоследняя
Показано с 1 по 10 из 23
  1. #1
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±

    Исправляем краш при попадании спецификатора "%s" в SendClientMessage(ToAll)

    И вновь я Вас категорически приветствую!
    (что-то нечасто это бывает...)





    Ни для кого не секрет (а может для кого-то и секрет), что в SA-MP есть баг, при котором сервер крашило если в функции SendClientMessage и SendClientMessageToAll попадает спецификатор, который не подвергся форматированию.

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


    Когда Куй наконец решил обратить на нас, обычных смертных, внимание, он добавил "защиту" со стороны клиента, просто вставив идентичный код в обработчик текста, введённого игроком в чат, а не сделав защиту напрямую в SendClientMessage, поэтому и по сей день в SA-MP можно положить любой сервер, где данные, введённые Вами в диалоговое окно, выводятся в чат через SendClientMessage (сообщения в личку, например, или же на RP серверах сообщения на радио, если они реализованы через диалог) и не стоит подобной защиты.
    Так же, если Вы сами в SendClientMessage укажете спецификатор "%s", сервер неминуемо крашнет :с


    В интернете гуляют реализации, но во всех, попавшихся мне, защита реализована идентично той, что стоит в чате (то бишь, знаки процента заменяются на знак решётки), что лично мне не совсем нравится. Поэтому я решил написать свою функцию, которая, по возможности, сможет оставить все знаки процента в сообщении игрока, при этом не доставляя никаких проблем серверу.
    И написал



    Защита реализована, как я уже сказал выше, в виде функции.
     Код функции без описания
    PHP код:
    stock FindSpecifiersInString(message[], array_size sizeof(message))
    {
    new 
    message_length strlen(message);

    for(new 
    imessage_lengthi++)
    {
    if(
    message[i] == '%')
    {
    if(
    message_length array_sizestrins(message"%"i++, message_length++);
    else 
    message[i] = '#';
    }
    }


    Функция имеет один обязательный параметр и один совсем "ненужный" параметр.
    1. message[]
      - собственно, массив(!) с текстом, в котором мы будем обрабатывать символы
      -[/INDENT][/INDENT]
    2. array_size
      Данный параметр нужен для определения размера массива, который Вы передаёте в функцию в качестве параметра. Устанавливать его не нужно, ибо сервер всё сделает сам за Вас (при неверной установке значения в данном массиве функция будет работать неверно!)
        Открыть/закрыть
      Если текст, передающийся в функцию, находится в переменной с неизвестным числом ячеек (как в случае с диалогами и параметром inputtext), функция в любом случае будет заменять все "%" на "%".

      Дабы такого не случилось, перед вызовом функции, запишите inputtext в любой массив. Например:
      PHP код:
      if(dialogid == 1111)
      {
      new 
      inputtext_buff[145];//Объявляем массив
      // 144+1 - именно столько (144) функция SendClientMessage(ToAll) сможет отправить символов за раз (1 - нуль-символ)
      strcat(inputtext_buffinputtext129);// Записываем содержимое inputtext в массив
      //128+1 - именно столько (128) символов игрок может ввести в диалоговое окно (1 - нуль-символ)
      FindSpecifiersInString(inputtext_buff);//Вызываем функцию
      format(inputtext_buffsizeof(inputtext_buff), "Ты написал: %s."inputtext_buff);//Отображаем и, для экономии памяти, записываем текст в тот же массив (в данном случае такое приемлемо)
      SendClientMessage(playerid, -1inputtext_buff);// Отображаем

      //В случае необходимости операцию можно повторять до бесконечности, а-ля
      /*Хотя в данном случае лучше в format указать другой массив и тут уже использовать ранее записанные данные в inputtext_buff
      но это ведь просто пример того, что функцию можно использовать несколько раз подряд для разного текста :)

      strcat(inputtext_buff, inputtext, 129);
      FindSpecifiersInString(inputtext_buff);
      format(inputtext_buff, sizeof(inputtext_buff), "Он написал: %s.", inputtext_buff);
      SendClientMessage(playerid, -1, inputtext_buff);*/
      return 1;

      И теперь функция будет обрабатывать все "%" как и положено

      * Если указать "0", функция не будет пытаться сохранить "%" в первозданном виде, а сразу начнёт заменять все "%" на "#"


    В ней учтено, что, в случае, если в SendClientMessage попадёт, например, "% s", или "% 000 s", или "%02d" - функция распознает данный текст как идентификатор и в SendClientMessage(ToAll) попадёт уже исправленная версия.

    Пример использования:
    PHP код:
    main()
    {
    new 
    string[13] = "% %% %s %%%";

    FindSpecifiersInString(string0);//Все "%" заменит на "#"
    printf("%s"string);

    string "% %% %s %%%";//Так как размер массива всего 13, а символов в строке 11, функция сможет сохранить только 2 первых символа, а всё остальное заменит на #
    FindSpecifiersInString(string);//Продублирует все "%". В случае нехватки места начнёт заменять их на "#"
    printf("%s"string);

     Код функции с описанием
    PHP код:
    stock FindSpecifiersInString(message[], array_size sizeof(message))//Во втором параметре узнаём размер массива, переданный в функцию
    {
    new 
    message_length strlen(message);//Узнаём размер строки, переданной в функцию

    for(new imessage_lengthi++)//Запускаем цикл на кол-во итераций, равное числу символов в строке
    {
    if(
    message[i] == '%')//Ищем символ "%"
    {
    if(
    message_length array_sizestrins(message"%"i++, message_length++);
    //Если нашли, проверяем наличие места в массиве и если свободные ячейки есть - добавляем новый символ, делая соответствующие
    //Изменения в переменных
    else message[i] = '#';//Если места нет, начинаем заменять "%" на "#"
    }
    }



    P.S. Если попытаться в функции указать массив с неизвестным кол-вом ячеек (как в случае с параметром "inputtext" в OnDialogResponse), компилятор выплюнет предупреждение:
    PHP код:
    warning 224indeterminate array size in "sizeof" expression (symbol ""
    Я пока не придумал как это решить, но на работоспособность сей warning влиять не будет.
    Если Вы знаете как обойти данную проблему, прошу отписаться по этому поводу ниже :)




    Автор темы - DeimoS
    Автор кода - DeimoS

    Отдельная благодарность
    Daniel_Cortez
    за ответы на идиотские вопросы с моей стороны :3



    Специально для Pro-Pawn.ru
    Копирование материала без указания авторства запрещено.

    Ставь лайк, подписывайся на канал.
    Ссылка на вебмани, а так же список всей музыки, использованной в видео, в описании.
    Новое видео выложу после 1000 лайков :3
    Последний раз редактировалось DeimoS; 24.08.2019 в 22:03.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  2. #2
    Аватар для $continue$
    Пользователь

    Статус
    Оффлайн
    Регистрация
    02.08.2014
    Адрес
    г. Киров (aka Вятка)
    Сообщений
    1,487
    Репутация:
    276 ±
    "Я нуб в павно - но и ты не компилятор (с) G-I"
    Что, за этакий стиль, переменную для счетчика цикла объявлять в одном for а использовать в другом for?
    В чем профит?

    Хотел бы я посмотреть как тебе отстреливают ногу за goto - СИшники.




    А, да, не охото вчитываться в код, с sizeof проблема в функции?
    Если да, то:
    PHP код:
    stock FindSpecifiersInString(message[], bool:mode falsearray_size sizeof(message))
    {
        new 
    message_length strlen(message);
        for(new 
    ijmessage_lengthi++)
        {
            
    FSIS_For_Start:
            if(
    message[i] == '%')
            {
                for(
    i+1<= message_lengthj++)
                {
                    switch(
    message[j])
                    {
                        case 
    '0'..'9'' ''.''#': continue;
                        default:
                        {
                            if(
    mode == true && array_size != && message_length array_size)
                            {
                                
    strins(message"%"imessage_length);
                                
    message_length++;
                                
    j;
                            }
                            else 
    message[i] -= 0x2;
                            
    i++;
                            goto 
    FSIS_For_Start;
                        }
                    }
                }
            }
        }

    Последний раз редактировалось $continue$; 29.02.2016 в 00:53.
    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

  3. #3
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от $continue$ Посмотреть сообщение
    Что, за этакий стиль, переменную для счетчика цикла объявлять в одном for а использовать в другом for?
    В чем профит?
    Может для того, чтоб при каждой новой итерации в первом цикле не нужно было её инициализировать при старте второго, не? Хотя не удивлюсь, если ты прописываешь инициализацию переменных внутри циклов и считаешь это нормой.


    Цитата Сообщение от $continue$ Посмотреть сообщение
    Хотел бы я посмотреть как тебе отстреливают ногу за goto - СИшники.
    В данном случае без него я не нашёл способа реализовать всё правильно. Да и тут goto не переносит тебя на 1000 строк в совершенно другой участок мода, так что это не так смертельно.

    - - - Добавлено - - -

      Открыть/закрыть
    Цитата Сообщение от $continue$ Посмотреть сообщение
    "Я нуб в павно - но и ты не компилятор (с) G-I"
    Что, за этакий стиль, переменную для счетчика цикла объявлять в одном for а использовать в другом for?
    В чем профит?

    Хотел бы я посмотреть как тебе отстреливают ногу за goto - СИшники.




    А, да, не охото вчитываться в код, с sizeof проблема в функции?
    Если да, то:
    PHP код:
    stock FindSpecifiersInString(message[], bool:mode falsearray_size sizeof(message), message_length strlen(message))
    {
        for(new 
    ijmessage_lengthi++)
        {
            
    FSIS_For_Start:
            if(
    message[i] == '%')
            {
                for(
    i+1<= message_lengthj++)
                {
                    switch(
    message[j])
                    {
                        case 
    '0'..'9'' ''.''#': continue;
                        default:
                        {
                            if(
    mode == true && array_size != && message_length array_size)
                            {
                                
    strins(message"%"imessage_length);
                                
    message_length++;
                                
    j;
                            }
                            else 
    message[i] -= 0x2;
                            
    i++;
                            goto 
    FSIS_For_Start;
                        }
                    }
                }
            }
        }


    strlen прямо в параметрах функции? Окей, странный мальчик с третьей рукой из спины. Посмеёмся вместе когда компилятор крашнет :3

    И да, гений программирования, куда бы ты sizeof не запихал, компилятор выругается в любом случае, ибо проблема именно в том, что в функцию передаётся динамический массив (как в случае с inputtext). Возможно есть какая-то pragma или что-то ещё, но мне пока лень копать мат.часть и выискивать решение.
    Сап
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  4. #4
    Аватар для $continue$
    Пользователь

    Статус
    Оффлайн
    Регистрация
    02.08.2014
    Адрес
    г. Киров (aka Вятка)
    Сообщений
    1,487
    Репутация:
    276 ±
    1) Вынести за пределы for переменную, не?
    2) Не заметил, что используется strlen - отредактировал код, раньше чем ты ответил :3
    На счёт размера - действительно, может можно как то с помощью emit?
    Убогость - Pawn, ничего не поделать.
    Последний раз редактировалось $continue$; 29.02.2016 в 01:20.
    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

  5. #5
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от $continue$ Посмотреть сообщение
    1) Вынести за пределы for переменную, не?
    2) Не заметил, что используется strlen - отредактировал код, раньше чем ты ответил :3
    На счёт размера - действительно, может можно как то с помощью emit?
    Убогость - Pawn, ничего не поделать.
    1) И что изменится? Переменные относятся конкретно к циклам и логично искать инициализацию именно в циклах, не?
    2) Не поверишь, но именно так выглядела функция изначально. Но решил перенести параметр array_size в переменную, дабы молодые умы не решили, что его тоже нужно указывать при вызове функции и не получилась из-за этого куча багов :3
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  6. #6
    Аватар для vovandolg
    Пользователь

    Статус
    Оффлайн
    Регистрация
    17.11.2015
    Адрес
    Stavropol
    Сообщений
    1,369
    Репутация:
    113 ±
    Так а в чём соль, знак будет показываться и без краша или это правильная рабочая заменка?
    А то как нубу не очень дошло что да зачем)
    [Anticheat]___Invisible Fly Hack
    [Anticheat]____Weapon/Ammo Hack
    [Function]______ResetPlayerWeaponSlot
    [Function]_______FIX_SetPlayerAmmo
    [ServerMod]______TDM | Zombie Apokalypse

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

    Статус
    Оффлайн
    Регистрация
    06.11.2015
    Сообщений
    40
    Репутация:
    10 ±
    почему нельзя сделать так?
    PHP код:
    new message_length,
    array_size strlen(message);
    message_length strlen(message); 
    ____________________________UPD
    Сам догнал почему:)
    ____________________________
    Поэтому я решил написать свою функцию, которая, по возможности, сможет оставить все знаки вопроса в сообщении игрока
    Это опечатка? или я через строку читал, что за знаки вопроса?
    (как в случае с параметром "inputtext" в OnRequestClass)
    все таки в OnRequestClass не в OnDialogResponse?
    Последний раз редактировалось SliM; 29.02.2016 в 06:03.

  8. #8
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от SliM Посмотреть сообщение
    Это опечатка? или я через строку читал, что за знаки вопроса?

    все таки в OnRequestClass не в OnDialogResponse?
    Спасибо, исправил. 3 часа ночи было и немного начал "плыть" ;)

    - - - Добавлено - - -

    Цитата Сообщение от vovandolg Посмотреть сообщение
    Так а в чём соль, знак будет показываться и без краша или это правильная рабочая заменка?
    А то как нубу не очень дошло что да зачем)
    Есть функция SendClientMessage в которой ещё с мезозойских времён имеется вот такой баг:







    То бишь, если в функцию передать спецификатор (спецификатор - это "%s", "%d" и т.п.), который не был форматирован до этого в том же format, то сервер из-за этого упадёт (точнее, падает он, вроде, только от спецификатора "%s". Но про это я в теме говорил).
    Куй исправил тему с OnPlayerText и OnPlayerCommandText, обрабатывая введённый игроком текст и заменяя все "%" на "#", но с диалогами он этого не сделал (а это самый распространённый пример. Авось ещё где-то можно подобный баг провернуть).

    Эта функция является более функциональным аналогом тех вариаций, что гуляют в интернете (опять же, я об этом писал в теме: про способы, которые заменяют все "%" на "#"). Хотя что те варианты, что мой - все рабочие, хотя логичнее использовать мой, ибо тогда обрабатываться будут не все диалоги, а только нужные.

    P.S. Если я неправильно понял вопроса и ты спрашивал о том, как работает сей баг, который исправляет функция, вставь этот код:
    PHP код:
    public OnPlayerSpawn(playerid)
    {
        
    ShowPlayerDialog(playerid1112DIALOG_STYLE_INPUT"Crash""Crash""Да""Нет (Да)");
        return 
    1;
    }
    public 
    OnDialogResponse(playeriddialogidresponselistiteminputtext[])
    {
        if(
    dialogid == 1112)
        {
            new 
    inputtext_buff[145];
            
    strcat(inputtext_buffinputtext128);
            
    //FindSpecifiersInString(inputtext_buff, true);
            
    format(inputtext_buffsizeof(inputtext_buff), "Написал: %s."inputtext_buff);
            return 
    SendClientMessage(playerid, -1inputtext_buff);
        }
        return 
    1;

    А после попробуй ввести в это диалоговое окно что-то подобное
    PHP код:
    %%   %%
    И смотри что будет :)
    А потом убери комментирование строки с вызовом функции + вставь код самой функции из урока и посмотри что выйдет после

    - - - Добавлено - - -

    Да или просто в OnPlayerConnect добавь
    PHP код:
    SendClientMessage(playerid, -1"%s"); 
    И при первом вошедшем игроке на сервер, его просто крашнет
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  9. #9
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    1. sizeof нельзя использовать на массивах-параметрах функции.
    2. При вставке ещё одного символа "%" теряется символ в конце строки. Этого побочного эффекта в описании не указано.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  10. #10
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    1. sizeof нельзя использовать на массивах-параметрах функции.
    2. При вставке ещё одного символа "%" теряется символ в конце строки. Этого побочного эффекта в описании не указано.
    1) При этом всё работает Правда, как уже писал в теме, компилятор выплёвывает warning в случае, если попытаться параметром передать массив с неизвестным числом ячеек, но тогда sizeof просто ровна нулю, на чём и построена проверка
    2) Так вставка происходит только если в массиве, переданном в функцию, имеется место, не? Именно для этого я и спрашивал у тебя насчёт sizeof и именно об этой проверке я написал в предыдущем пункте. Если длина текущей строки+1 новый символ не равно или больше размеру массива, полученного через sizeof - добавляем новый символ. Иначе заменяем "%" на "#"
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

 

 
Страница 1 из 3 1 2 3 ПоследняяПоследняя

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

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

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

Ваши права

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