Доступ к готовым решениям

Переход в группу "Пользователь"

300.00 ₽
Одноразовый платёж
Быстрый переход в группу "Пользователи", без надобности написания постов и ожидания.

Покупка дает возможность:
Быть полноправным участником форума
Нормальное копирование кода
Создавать темы
Скачивать файлы
Доступ к архиву Pawno-Info

Мануал Анти-падение сервера при вводе "%" в dialog

Статус
В этой теме нельзя размещать новые ответы.

seriu

Специалист
Всем привет, способ довольно простой, но оптимизированный.

Все мы знаем, что при вводе в диалог символа "%","%s","%d" сервер падает. Баг вроде как был давно профикшен, но есть и иная строна этого бага, которая совсем не зависит от диалога.

В чем заключается баг?
- Баг заключается в SendClientMessage.
- Если использовать диалог для передачи какой либо информации, то вам несомненно придется использовать SendClientMessage.
В передаваемой информации игрок может написать знак процента, специально не специально это не важно. Важно то, что когда вы будете форматировать строку к примеру "Вася пупкин[0]: %s" У вас появится еще знак процента( Вася пупкин[0]: text and %) который написал игрок, отсюда следует критическая ошибка за которой следует падение сервера.
- Как это избежать?
Об этом я и хочу рассказать в этом уроке.
- Если вы очень ленивый и вам не охота искать все эти диалоги где используется format > SendClientMessage, то используйте 1/2 Вариант по выбору
- Если вы заботитесь о своем моде и хотите наиболее лучшей производительности, то используйте вариант № 3 или Вариант № 4 (от Deimos ), но предварительно уберите из OnDialogResponse грязь вроде
PHP:
for(new i=0;i<strlen(inputtext);i++)if(inputtext[i]=='%')inputtext[i]='#';
    while(strfind(inputtext,"%s",true)!=-1)
    {
        strdel(inputtext,strfind(inputtext,"%s",true),strfind(inputtext,"%s",true)+2);
    }
    if(strfind(inputtext, "%", true) != -1 || strfind(inputtext, "'", true) != -1 || strfind(inputtext, "=", true) != -1) return 1;
Почему мой способ оптимизированный?
- Потому что проводить проверку на запрещенные символы мы будем делать только тогда, когда это будет нужно.

Ранее многие "профессионалы" предлагали делать проверку на запрещенные символы inputtext , в принципе они правы. Но если мы показываем игроку DIALOG_STYLE_LIST/MSG_BOX/TABLIST/TABLIST_HEADERS - inputtext все равно присутствует и тем самым проверка идет и у этих типов диалогов.

Я придумал довольно простой метод, как это можно реализовать.

В начала мода, после include.
Вариант 1
PHP:
new CHECK_DIALOGS[32767 char];
stock SPD(playerid, dialogid, style, caption[], info[], button1[], button2[])
{
    CHECK_DIALOGS{dialogid} = style;
    return ShowPlayerDialog(playerid, dialogid, style, caption, info, button1, button2);
}
#if defined _ALS_ShowPlayerDialog
    #undef ShowPlayerDialog
#else
    #define _ALS_ShowPlayerDialog
#endif


#define ShowPlayerDialog SPD
откуда я взял 32767?
- 32767 это максимальный ид диалого по лимиту SAMP
- если вы используете emun в качестве нумератора диалогов то в конце добавьте dMAX_DILOGID и добавляйте другие диалоги до этого значения. Так же замените 32767

в начало паблика OnDialogResponse
PHP:
if(CHECK_DIALOGS{dialogid} == DIALOG_STYLE_INPUT || CHECK_DIALOGS{dialogid} == DIALOG_STYLE_PASSWORD)
    {
        for(new i; inputtext[i] != 0x0; i++){
            if(inputtext[i] == 0x25) inputtext[i] = 0x23;
        }
    }
Вариант 2
PHP:
new CHECK_STYLE[MAX_PLAYERS char];
stock SPD(playerid, dialogid, style, caption[], info[], button1[], button2[])
{
    CHECK_STYLE{playerid} = style;
    return ShowPlayerDialog(playerid, dialogid, style, caption, info, button1, button2);
}
#if defined _ALS_ShowPlayerDialog
    #undef ShowPlayerDialog
#else
    #define _ALS_ShowPlayerDialog
#endif


#define ShowPlayerDialog SPD
PHP:
if(CHECK_STYLE{playerid} == DIALOG_STYLE_INPUT || CHECK_STYLE{playerid} == DIALOG_STYLE_PASSWORD)
    {
        for(new i; inputtext[i] != 0x0; i++){
            if(inputtext[i] == 0x25) inputtext[i] = 0x23;
        }
    }
п.с 2й вариант лучше использовать когда максимальный ид диалога больше максимального ида игрока
Вариант 3
PHP:
stock FindSpecifiersInStringBy_seriu(message[]){
    for(new i; i < strlen(message); i++){
            if(message[i] == 0x25) message[i] = 0x23;
        }
    return 1;
}
Пример использования:
PHP:
enum // dialogid
{
    dTest
}


CMD:Test(playerid,params[])
{
    ShowPlayerDialog(playerid,dTest,DIALOG_STYLE_INPUT,"Проверка","Введите рандомные числа и знак процента\"%\"","Готово","");
    return 1;
}


if(dialogid == dTest)
{
    new strlen_text_test;
    strlen_text_test = strlen(inputtext);
    if(strlen_text_test < 1 || > 90) return ShowPlayerDialog(playerid,dTest,DIALOG_STYLE_INPUT,"Проверка","Введите рандомные числа и знак процента\"%\"","Готово","");
    FindSpecifiersInStringBy_seriu(inputtext);
    new str_test[144],name[MAX_PLAYER_NAME];
    GetPlayerName(playerid,name,MAX_PLAYER_NAME);
    format(str_test,144,"Тест: %s[%d]: %s",name,playerid,inputtext);
    SendClientMessage(playerid,0xFFFF00FF,str_test);
}
Вариант by Deimos(Eims)
А вот более универсальная функция, которая совмещает в себе оба метода.
Не хочешь, чтоб все "%" заменились на "#"? Делай так, как я показал в примере выше (создавай массив) и параметр "mode" указывай как "true",
тогда все знаки процента, какие будет возможно заменить (на какие хватит места в массиве), будут оставлены, а всё остальное заменится решётками. А иначе функция будет работать полностью идентично твоей.
Кстати говоря о тексте. Вот ещё одна реализация, в которой будет учитываться длина строки в SendClientMessage без inputtext:
PHP:
stock FindSpecifiersInString(message[], array_size = sizeof(message)){
    new message_length = strlen(message);
    
    for(new i; i < message_length; i++)
    {
        if(message[i] == '%')
        {
            if(array_size == 0) message[i] = '#';
            else if(message_length < array_size) strins(message, "%", i++, message_length++);
            else message[i] = '#';
        }
    }
}
И использование:
PHP:
if(dialogid == 1111)
{
    new inputtext_buff[145];
    strcat(inputtext_buff, inputtext, 128);
    FindSpecifiersInString(inputtext_buff, true, "Написал: .");
    format(inputtext_buff, sizeof(inputtext_buff), "Написал: %s.", inputtext_buff);
    return SendClientMessage(playerid, -1, inputtext_buff);
}
При подсчёте кол-ва заменяемых "%", сервер будет учитывать размер конечной строки
Немного пояснение кода
в 1м и 2м варианте я использовал массив типа char
почему не bool или одномерный ?
- Потому, что для таких массивов память жрет по 4 байта на кол-во(Пример new check[MAX_PLAYERS]; MAX_PLAYERS*4 столько кол-во байт потребуется. )
- Массив типа char использует всего 1 байт на кол-во (Пример new check[MAX_PLAYERS char]; MAX_PLAYERS*1 столько кол-во байт потребуется. Использование check{id} = 0..255; п.с значение должно быть от 0 и не боле 255)

в 3м и 4м варианте я использовал 0x25 и 0x23
Что это такое?
это текст как его видит компилятор, то есть 0x25 = %, 0x23 = # тем самым заменяем % на # или в случаи варианта Deimos мы добавляем к % еще 1 % и получается %%
Пример с
- Довольно простой способ вставить знак % в строку

PHP:
new string[35];
format(string,sizeof(string),"43%s of my shirts are black.","%%");
SendClientMessage(playerid,0xFFFFFAA,string);
В варианте Deimos мы уже получаем отформатированный '%s' который заменяется на '%%'.
То есть если игрок введет в диалог "100 %" то, при формате это будет выглядеть так "100 %%". Отсюда следует, что ошибки не будет, а текст который отправится будет "100 %".

При копирование данной статьи на другие порталы, плиз указывайте автора и сайт откуда скопировали :ce:
ну или просто автора :ca:
 
Последнее редактирование:
Статус
В этой теме нельзя размещать новые ответы.
Сверху Снизу