seriu
Специалист
Всем привет, способ довольно простой, но оптимизированный.
Все мы знаем, что при вводе в диалог символа "%","%s","%d" сервер падает. Баг вроде как был давно профикшен, но есть и иная строна этого бага, которая совсем не зависит от диалога.
В чем заключается баг?
- Баг заключается в SendClientMessage.
- Если использовать диалог для передачи какой либо информации, то вам несомненно придется использовать SendClientMessage.
В передаваемой информации игрок может написать знак процента, специально не специально это не важно. Важно то, что когда вы будете форматировать строку к примеру "Вася пупкин[0]: %s" У вас появится еще знак процента( Вася пупкин[0]: text and %) который написал игрок, отсюда следует критическая ошибка за которой следует падение сервера.
- Как это избежать?
Об этом я и хочу рассказать в этом уроке.
- Если вы очень ленивый и вам не охота искать все эти диалоги где используется format > SendClientMessage, то используйте 1/2 Вариант по выбору
- Если вы заботитесь о своем моде и хотите наиболее лучшей производительности, то используйте вариант № 3 или Вариант № 4 (от Deimos ), но предварительно уберите из OnDialogResponse грязь вроде
Почему мой способ оптимизированный?
- Потому что проводить проверку на запрещенные символы мы будем делать только тогда, когда это будет нужно.
Ранее многие "профессионалы" предлагали делать проверку на запрещенные символы inputtext , в принципе они правы. Но если мы показываем игроку DIALOG_STYLE_LIST/MSG_BOX/TABLIST/TABLIST_HEADERS - inputtext все равно присутствует и тем самым проверка идет и у этих типов диалогов.
Я придумал довольно простой метод, как это можно реализовать.
В начала мода, после include.
Вариант 1
в начало паблика OnDialogResponse
Вариант 2
п.с 2й вариант лучше использовать когда максимальный ид диалога больше максимального ида игрока
Вариант 3
Пример использования:
Вариант by Deimos(Eims)
в 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 % и получается %%
Пример с
- Довольно простой способ вставить знак % в строку
В варианте Deimos мы уже получаем отформатированный '%s' который заменяется на '%%'.
То есть если игрок введет в диалог "100 %" то, при формате это будет выглядеть так "100 %%". Отсюда следует, что ошибки не будет, а текст который отправится будет "100 %".
При копирование данной статьи на другие порталы, плиз указывайте автора и сайт откуда скопировали :ce:
Все мы знаем, что при вводе в диалог символа "%","%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
- 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;
}
}
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;
}
}
Вариант 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);
}
Немного пояснение кодаА вот более универсальная функция, которая совмещает в себе оба метода.
Не хочешь, чтоб все "%" заменились на "#"? Делай так, как я показал в примере выше (создавай массив) и параметр "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);
То есть если игрок введет в диалог "100 %" то, при формате это будет выглядеть так "100 %%". Отсюда следует, что ошибки не будет, а текст который отправится будет "100 %".
При копирование данной статьи на другие порталы, плиз указывайте автора и сайт откуда скопировали :ce:
ну или просто автора :ca:
Последнее редактирование:
