PDA

Просмотр полной версии : [Урок] Pawn - Интересные факты/советы



^_^
29.11.2013, 23:00
Доброго времени суток, уважаемые форумчане. Решил поделится с вами вопросами, которые в своё время задавал сам, и, собственно, ответами на них, а также некоторыми полезными советами.
Итак, приступим...



1) Самая простая функция которая возвращает случайное натуральное число от Min до Max-1.

stock minrandom(min, max) return random(max-min)+min;
Пример использования:

if(minrandom(1, 11) == 10) SendClientMessage(playerid, -1, "Да ты везунчик просто!");



2) Если вы хотите создать таймер с повтором, лучше всего сделать этот самый повтор вручную, не через SetTimer/SetTimerEx.
Объясняю: создаётся таймер, и параметр repeating равен false, а в конце таймерной функции делается очередной запуск (перезапуск) таймера (repeating остаётся равным false).
Почему именно так, спросите вы? Ответ очень прост. Представьте, что у вас стоит таймер на 300 миллисекунд, а в самой функции куча операторов и функций, которые не успевают выполнится за это время. Тогда уж сервер зависнет и перестанет вызвать таймеры. Вот почему стоит делать повторяющиеся таймеры вручную, ибо это даёт 100% гарантии, что такое никогда не случится.
Пример использования (односекундный таймер):


//Перед этим SetTimer("SecondTimer", 1000, true); заменяется на SetTimer("SecondTimer", 1000, false);
public SecondTimer()
{
//тут куча кода
SetTimer("SecondTimer", 1000, false);
}




3) Как пропустить необязательный параметр?
В Pawn есть возможность создания функций с необязательными параметрами, у которых уже есть значения, задаваемые по умолчанию.
Подробнее про такие функции: http://forum.sa-mp.com/showthread.php?t=321465
Итак, существуют 2 способа пропустить такие параметры:

Если параметр(ы) последний(ие) то просто не будем их указывать. Это самый простой способ пропуска.
Например, нам нужно узнать только час когда мы запустили мод. Для этого мы будем использовать функцию gettime.
Параметры функции:
http://breedpmnr.ru/i/fa549ae22d7061b6591d09731e76.jpg
В большинство чудо модах обычно вот так:


new hours, minutes, seconds;
gettime(hours, minutes, seconds);
SetWorldTime(hours);

А остальные 2 переменные создаются просто так, дабы "удовлетворить" функцию.
Вместо этого можно просто создать одну переменную вместо трёх, и не утруждать память.


new hours;
gettime(hours);
SetWorldTime(hours);

А что если нам нужно узнать только секунды, или минуты? Первые параметры так просто не пропустишь...
Тогда вместо параметра, который нужно пропустить, просто указываем символ подчёркивания _.
Пример:


new minutes;
gettime(_, minutes); //или же gettime(_, minutes, _);
printf("Мод запущен на %i минуте.", minutes);




4) Как можно поделить значения одной переменной целочисленного типа на несколько ровных частей без всяких проверок?
Для этого используется простая функция деления. Допустим нам нужно разделить время нахождения игрока на сервере на 3 части... Конечно, звучит легко и просто:


//До этого создаётся массив TimePart[MAX_PLAYERS];
//В функции успешного логина
new minute;
gettime(_, minute, _);
switch(minute){
case 0 .. 20: TimePart[playerid] = 1;
case 21 .. 40: TimePart[playerid] = 2;
case 41 .. 60: TimePart[playerid] = 3;
}

А что, если нам нужно не 3 части, а целых 10?
Решение:


new minute;
gettime(_, minute, _);
TimePart[playerid] = minute/20 + 1; //Мы можем легко изменить 20 на 10, при этом мы увеличим части с 3 до 6... и так далее

Простая математика и меньше головной боли.
Если перевести код выше на естественный язык языке то получим что-то вроде: Берём целую часть значения и делим на (кол-во частей плюс 1).



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



6) Эффективный перебор игроков.
Хоть плагин vectoralpawn в некоторых случаях и быстрее стандартного перебора игроков с IsPlayerConnected, он всё же уступает по эффективности другим методам, таким как foreach.inc от Y_Less'а и перебор игроков методом Tracker1, поэтому его использование в цикле перебора игроков является неоптимальным выбором.
Если же выбрать между методом Tracker1 и foreach.inc, foreach может быть быстрее быстрее, но при использовании плагина JIT (подробнее: КЛАЦ (http://forum.sa-mp.com/showthread.php?t=326980)) метод Tracker1 вырывается вперёд. Выбор за вами.
Подробнее про переборы игроков:
foreach (Y_Less): http://forum.sa-mp.com/showthread.php?t=92679
Перебор игроков Tracker1: http://pro-pawn.ru/showthread.php?5747
http://breedpmnr.ru/i/cb0fa732f0acda3b5723f476b768.jpg
http://breedpmnr.ru/i/6c48a1eba03cb339c4d791257816.jpg



7) Форматируя сроку, можно указывать не только ширину поля, которое будет дополняться пробелами*, но и нулями, добавляя между % и число указывающее размер поля - 0.

printf("Value: %02d", 9); //Выводит на экран "Value: 09"
Часто встречаемый пример:


// Говнокод для вывода минут в двухсимвольном виде - ищите в любом RLS.
gettime(hours, minuites, seconds);
if (minuite < 10)
format(string, sizeof(string), "~y~%d %s~n~~g~|~w~%d:0%d~g~|", day, mtext, hours, minuites);
else
format(string, sizeof(string), "~y~%d %s~n~~g~|~w~%d:%d~g~|", day, mtext, hours, minuites);

Вместо этого можно сделать так:


gettime(hours, minuites, second);
// Между "%" и "d" мы добавили "02", чтобы минуты отображались в
// двухсимвольном формате с пустыми ячейками поля, заменяющимися на нули.
format(string, sizeof(string), "~y~%d %s~n~~g~|~w~%d:%02d~g~|", day, mtext, hours, minuites);

*При форматировании строк можно указывать максимальную длину поля между символом % и символом спецификатора. Подробнее: КЛАЦ (http://wiki.sa-mp.com/wiki/Format)


8) Функции, названия которых начинаются с символа "@", автоматически получают атрибут public.
Такие функции можно использовать в таймерах, CallLocalFunction и т.п.
*Символ "@" обязательно должен быть первым, а какие символы будут дальше - разницы нет.
Пример:


@TimerFunc();
@TimerFunc()
{
print("Прошла 1 секунда с момента запуска мода");
}

public OnGameModeInit()
{
SetTimer("@TimerFunc", 1000, false);
}



9) Оформление кода никак не влияет на скомпилированный код (.amx).
Чего не скажешь про его читабельность и восприятие автором или другими программистами.
Отличные рекомендации по написанию кода можете найти здесь: http://pro-pawn.ru/showthread.php?8347


10) "Переключаемые" многострочные комментарии.
Думаю, все знакомы с многострочными комментариями в Pawn:


/*
SendClientMessage(playerid, -1, "Some text");
*/

Но что, если вам вдруг нужно раскомментировать этот участок кода?
Правильно, кроме стирания первых "/*" придётся выискивать и завершающие комментарий символы "*/".
Это может оказаться проблемой, если приходится часто комментировать/раскомментировать длинный участок кода (например, при тестировании).
На этот случай можно сделать комментарий "переключаемым":


/*
SendClientMessage(playerid, -1, "Some text");
// */

Теперь добавим перед "/*" один слэш ("/"):


//*
SendClientMessage(playerid, -1, "Some text");
// */

В итоге мы "нейтрализовали" многострочный комментарий.
Чтобы включить его обратно, достаточно лишь убрать первый слэш.
Обратите внимание: между последними двумя слэшами и закрытием многострочного комментария ("//" и "*/") стоит пробел.
Они разделены пробелом, чтобы компилятор обрабатывал их именно как "//" и "*/", а не "/", "/*" и "/" - в этом случае компилятор подумает, что это вложенный комментарий, и выдаст варнинг.


11) Пропуск инициализации ячеек массива нулями.
При объявлении локальных массивов внутри функций компилятор не только резервирует место в стеке под массив, но и инициализирует все его ячейки нулями, чтобы в них не было мусорных значений.
Сделано это из-за быдлокодеров, которые, пользуются переменными, забывая их инициализировать.
Тем не менее, инициализация занимает некоторое время, да и польза от этого сомнительная, если вы способны отдавать отчёт своим действиям.
К счастью, есть способ избежать потерь во времени, пропустив инициализацию:


SomeFunction()
{
// перед объявлением массива перейдём на метку skip_array_init
goto skip_array_init;
// объявляем массив
new arr[256];
skip_array_init:
// в этом месте массив arr будет существовать, но он не будет инициализирован:
// код инициализации существует, но он находится _до_ метки skip_array_init,
// т.е. мы обошли этот код

// дальше - код с использованием массива
}

Почему пропуск инициализации повышает производительность кода?

Скомпилируем код, а затем дизассемблируем его.
Получим следующую последовательность инструкций:


; Начало функции SomeFunction.
proc
; И сразу же - переход на метку l.0 (см. далее).
jump 0
; Выделение места в стеке под массив из 16 ячеек (ffffffc0 означает "-16")
; (этот код никогда не сработает, переход на l.0 был безусловным).
stack ffffffc0
; Заполнение массива нулями происходит побайтово,
; 16 ячеек - 64 байта (40 в шестнадцатеричной системе счисления).
zero.pri
addr.alt ffffffc0
fill 40
; Метка, с помощью которой пропускается инициализация массива.
l.0
; Установка вершины стека в то положение, когда выделено 16 ячеек под массив,
; Обратите внимание: в отличие от обычного создания массива,
; здесь массив не заполняется никакими значениями,
; происходит только резервирование пространства,
lctrl 5
add.c ffffffc0
sctrl 4
; Код с использованием массива.
; ...
; Высвобождение пространства в стеке и возврат из функции.
stack 40
zero.pri
retn

Если кому интересно, вот тест скорости:


#include <a_samp>

const PROFILE_ITERATIONS_MAJOR = 1000;
const PROFILE_ITERATIONS_MINOR = 1000;

const STRING_SIZE = 512;


const SZ = STRING_SIZE;

#define _m1();\
{new a[SZ];}
#define method1();\
_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();

#define _m2();\
{goto skip_init;new a[SZ];skip_init:}
#define method2();\
_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();


bool:IsJITEnabled()
{
#emit lctrl 7
#emit retn
return true;
}

main()
{
printf(
"Testing array initialization..." "\n"\
"(%dx%d iterations, array size: %d, JIT status: %sabled)",
PROFILE_ITERATIONS_MAJOR, PROFILE_ITERATIONS_MINOR,
STRING_SIZE, IsJITEnabled() ? ("en") : ("dis")
);
new t1, t2, t, i, j;
t1 = 0, t2 = 0;
for(i = 0; i < PROFILE_ITERATIONS_MAJOR; ++i)
{
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
}
t1 += GetTickCount()-t;
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
}
t2 += GetTickCount()-t;
}
printf(
"method 1: %d" "\n"\
"method 2: %d",
t1, t2
);
}

Макросы method1 и method2 сделаны, чтобы повторить в них создание локальных массивов по 10 раз.
Кроме того сами макросы используются в функции main по 10 раз (итого получается 10x10 = 100 повторов).
Это не что иное, как развёртывание циклов, сделанное чтобы уменьшить погрешность от циклов и вызовов GetTickCount, таким образом, увеличив точность измерений.
Также в процессе компиляции выдаётся куча варнингов, но они, опять же, из-за макросов, не обращайте на них внимания.
Тестирование проведено на массивах из 32, 256 и 512 ячеек, с включенным и выключенным JIT.
Результаты следующие:



Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: disabled)
method 1: 5581
method 2: 47

Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: enabled)
method 1: 2640
method 2: 4




Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: disabled)
method 1: 31324
method 2: 42

Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: enabled)
method 1: 15106
method 2: 3




Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: disabled)
method 1: 63840
method 2: 40

Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: enabled)
method 1: 29161
method 2: 4


Из измерений можно сделать вывод, что размер массива никак не влияет на результаты метода с пропуском инициализации массива.
Но всё же от трюка не сильно много пользы. Да, цифры в тесте, казалось бы, большие, но не стоит забывать, что они получены с помощью 100 миллионов итераций.
В нормальных же условиях время инициализации массива в 512 ячеек составит всего ~0.0006384 мс или ~0.0000006 секунды.

Также для удобства можно использовать следующий макрос:

#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:
Пример использования:


public OnGameModeInit()
{
// Объявляем массив test и пропускаем обычную инициализацию его ячеек.
noinit:test[120];
// Инициализируем ячейки массива вручную случайными числами.
for (new i = 0; i < sizeof(test); i++)
test[i] = random(sizeof(test));
// ...
}



12) strcat или format?
Какая из этих функций наиболее эффективна для соединения строк?
Согласно тесту скорости (по алгоритму профилирования Daniel_Cortez (http://pro-pawn.ru/showthread.php?12585)), strcat выполняется намного быстрее чем format при конкатенации 2 строк.
Но если нужно соединить 3 или более строк, рано или поздно format вырвется вперёд, т.к. львиная доля времени уходит на вызов нативных функций. Чем больше строк нужно соединить, тем больше раз нужно вызвать функцию strcat. В то же время, функция format может одним вызовом соединить практически неограниченное количество строк - отсюда и выгода.

Опытным путём удалось выяснить, что strcat выгодно использовать при соединении от 2 до 7 строк.

Проверим скорость конкатенации 7 строк.
Вот код настроек профайлера:


const PROFILE_ITERATIONS_MAJOR = 3_000;
const PROFILE_ITERATIONS_MINOR = 1_000;

new const code_snippets_names[2][] =
{
{"format"},
{"strcat(x7)"}
};

#define Prerequisites();\
static\
str1[] = "1234567890123456789012345678901234567890",\
str2[] = "1234567890123456789012345678901234567890",\
str3[] = "1234567890123456789012345678901234567890",\
str4[] = "1234567890123456789012345678901234567890",\
str5[] = "1234567890123456789012345678901234567890",\
str6[] = "1234567890123456789012345678901234567890",\
str7[] = "1234567890123456789012345678901234567890";\
new buffer[512];

#define CodeSnippet1();\
format(buffer, sizeof(buffer), "%s%s%s%s%s%s%s", str1, str2, str3, str4, str5, str6, str7);

#define CodeSnippet2();\
buffer = str1, strcat(buffer, str2), strcat(buffer, str3), strcat(buffer, str4);\
strcat(buffer, str5), strcat(buffer, str6), strcat(buffer, str7);

Результаты теста в интерпретируемом режиме:


Тестирование: <format> vs <strcat(x7)>
Режим: интерпретируемый, 3000x1000 итераций.
format: 5514
strcat(x7): 5209

Результаты с использованием JIT:


Тестирование: <format> vs <strcat(x7)>
Режим: с JIT-компиляцией, 3000x1000 итераций.
format: 5263
strcat(x7): 4990

Как видно, strcat всё ещё немного обгоняет format при соединении 7 строк. При этом, JIT не играет большой роли в результатах: почти всё время уходит на выполнение функций strcat и format.

Теперь добавим ещё одну строку. Итого у нас будет 8 строк:


const PROFILE_ITERATIONS_MAJOR = 3_000;
const PROFILE_ITERATIONS_MINOR = 1_000;

new const code_snippets_names[2][] =
{
{"format"},
{"strcat(x8)"}
};

#define Prerequisites();\
static\
str1[] = "1234567890123456789012345678901234567890",\
str2[] = "1234567890123456789012345678901234567890",\
str3[] = "1234567890123456789012345678901234567890",\
str4[] = "1234567890123456789012345678901234567890",\
str5[] = "1234567890123456789012345678901234567890",\
str6[] = "1234567890123456789012345678901234567890",\
str7[] = "1234567890123456789012345678901234567890",\
str8[] = "1234567890123456789012345678901234567890";\
new buffer[512];

#define CodeSnippet1();\
format(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s", str1, str2, str3, str4, str5, str6, str7, str8);

#define CodeSnippet2();\
buffer = str1, strcat(buffer, str2), strcat(buffer, str3), strcat(buffer, str4);\
strcat(buffer, str5), strcat(buffer, str6), strcat(buffer, str7), strcat(buffer, str8);

Результаты (режим интерпретации):


Тестирование: <format> vs <strcat(x8)>
Режим: интерпретируемый, 3000x1000 итераций.
format: 6084
strcat(x8): 6260

Результаты с использованием JIT:


Тестирование: <format> vs <strcat(x8)>
Режим: с JIT-компиляцией, 3000x1000 итераций.
format: 5850
strcat(x8): 6057


При соединении 8 строк format уже начинает обгонять strcat. Как и предполагалось, при достаточном количестве строк выгода strcat сошла на нет.

Также strcat автоматически распаковывает строку, если попытаться прицепить упакованную строку к неупакованной, и наоборот, упаковывает, если первая строка тоже была упакована.
Так или иначе, в отличие от format, при помощи функции strcat нельзя одним вызовом объединить две строки, записав результат в отдельный массив, или одновременно объединить больше чем две строки - нужно сначала отдельным действием записать в массив для хранения результата одну строку, а потом с помощью strcat добавить другую.


Автор темы: ^_^.
Авторы ответов: 1, 2, 3, 5, 6 - Tracker1; 4, 6, 8, 9, 10, 11 - Daniel_Cortez; 8 - DeimoS; 3,4 - ^_^
Если у вас появился интересный ответ или хороший совет, строго касающегося языка pawn или частных проблем sa-mp, прошу оставить его в комментариях (он будет добавлен в саму тему). Заранее спасибо.
Тема будет дополняться.

^_^
29.11.2013, 23:04
(пост зарезервирован)

Daniel_Cortez
01.12.2013, 09:53
Not bad. Признаюсь честно, не знал, что параметры функций можно пропускать с помощью знака _
Обычно я пропускал их по-другому:

new min;
gettime(.minute=min);


Насчёт пункта (4):

GamePart[playerid] = floatround(PlayerEnteredMinute/20, floatround_tozero) + 1;
Получается, что ты сначала вычисляешь целочисленный результат (PlayerEnteredMinute/20), затем конвертируешь его в вещ. число (после компиляции "PlayerEnteredMinute/20" преобразуется в "float(PlayerEnteredMinute/20)") и затем округляешь обратно до целого.
Зачем тогда вообще использовать floatround?

*Dance*
01.12.2013, 11:54
Очень полезная темка, молодец что здесь ее выложил:)

^_^
01.12.2013, 12:36
Not bad. Признаюсь честно, не знал, что параметры функций можно пропускать с помощью знака _
Обычно я пропускал их по-другому:

new min;
gettime(.minute=min);


Насчёт пункта (4):

GamePart[playerid] = floatround(PlayerEnteredMinute/20, floatround_tozero) + 1;
Получается, что ты сначала вычисляешь целочисленный результат (PlayerEnteredMinute/20), затем конвертируешь его в вещ. число (после компиляции "PlayerEnteredMinute/20" преобразуется в "float(PlayerEnteredMinute/20)") и затем округляешь обратно до целого.
Зачем тогда вообще использовать floatround?
Логично, вероятнее всего я не учёл тот факт что в переменной будет записан именно целочисленный результат, вот и усложнил всё. Тема обновлена.

Osetin
21.12.2013, 23:51
Установил статус "Важная"

^_^
10.01.2014, 23:32
Тема обновлена, добавлен пункт 6.

^_^
15.02.2014, 22:17
Тема обновлена, добавлен пункт 7.

^_^
21.04.2014, 14:52
Тема обновлена, добавлены пункты 8 и 9.

DeimoS
21.04.2014, 15:17
Дополнение к пункту 8. Символ "@" обязательно должен стоять вначале, а вот какие символы будут дальше - разницы нет (естественно, в пределах возможностей Pawn). То бишь, подобные функции можно отделять, например, так:

@__FuncName();
@__FuncName() print("Прошла 1 секунда с момента запуска мода");
public OnGameModeInit()
{
SetTimer("@__FuncName", 1000, false);
}

^_^
21.04.2014, 15:54
Дополнение к пункту 8. Символ "@" обязательно должен стоять вначале, а вот какие символы будут дальше - разницы нет (естественно, в пределах возможностей Pawn). То бишь, подобные функции можно отделять, например, так:

@__FuncName();
@__FuncName() print("Прошла 1 секунда с момента запуска мода");
public OnGameModeInit()
{
SetTimer("@__FuncName", 1000, false);
}
Добавил.

wAx
20.07.2014, 12:53
random(max) - никогда не выдаст max.

Этого пока еще многие новички не знают.

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

SetPlayerCameraPos - не работает сразу после использования TogglePlayerSpectating(playerid, true);

Заставить работать ее можно с помощью простенького таймера.

Пример:

SetTimerEx("@_SetCameraPos", 300, 0, "dfff", playerid,x,y,z);

@_SetCameraPos(playerid, Float:x,Float:y,Float:z);
@_SetCameraPos(playerid,Float:x,Float:y,Float:z)
{
SetPlayerCameraPos(playerid, x,y,z);
return 1;
}

DeimoS
17.08.2014, 22:55
Немного усовершенствую функцию.

stock minrandom(min, max)
{
if(min > max) return printf("Ошибка функции minrandom: Значение min (%d) больше значения max (%d)", min, max);
else if(min == max) return printf("Ошибка функции minrandom: Значение min (%d) равно значению max (%d)", min, max);
else return random(max-min+1)+min;
}


stock minrandom(min, max)
{
if(min == max) return min;
else if(min > max) return random(min-max+1)+max;
else return random(max-min+1)+min;
}
+1 нужен для того, чтоб число, записанное в "max" так же входило в диапазон чисел, выдаваемых функцией random.

Можно ещё такой фиксик, основанный на предыдущем.

stock random_fix(value) return random(value+1);
#define random random_fix
ибо у многих часто появляются проблемы с функцией random, ибо они не знают, что число, введённое в скобки, не входит в диапазон выдаваемых чисел :)

Daniel_Cortez
20.08.2014, 03:17
Немного усовершенствую функцию.

stock minrandom(min, max)
{
if(min > max) return printf("Ошибка функции minrandom: Значение min (%d) больше значения max (%d)", min, max);
else if(min == max) return printf("Ошибка функции minrandom: Значение min (%d) равно значению max (%d)", min, max);
else return random(max-min+1)+min;
}
+1 нужен для того, чтоб число, записанное в "max" так же входило в диапазон чисел, выдаваемых функцией random.
Не думаю, что вывод сообщений через print/printf хорошая идея, так обычно никто не делает. Мало того, при неправильных параметрах функция всё же вернёт значение, из-за чего в коде, использующем такую функцию могут возникнуть ошибки.
Могу предложить 2 более-менее годных выхода из ситуации:
Поменять значения min и max местами, если min больше max, и возвращать max/min, если они равны друг другу.
Возвращать по ссылке дополнительное значение, означающее успех выполнения функции, перебросив задачу по обработке ошибки вызывающему коду.
Пример:


stock DoSomething(param1, &success=cellmin)
{
// Первым делом проверим, пропущен ли параметр success, сравнив его значение с cellmin
// (в Pawn нельзя просто так получить значение указателя, но эту проблему можно обойти).
#emit load.s.pri success // получим значение success
#emit eq.c.pri cellmin // сравним его с cellmin
new success_skipped;
#emit stor.s.pri success_skipped // запишем в переменную 1/0 в зависимости от равенства cellmin
if(SomeShitHappens())
{
if(0 == success_skipped)
success = 0;
return 0;
}
if(0 == success_skipped)
success = 1;
return random(random(param1));
}

DeimoS
20.08.2014, 21:21
Я сообщения поставил лишь как указание на то, что могут быть баги =) А уже возможные пути исправления не продумывал особо, поэтому влепил обычное оповещение. Но твой вариант явно лучше.
Вот код, который предложил Стас (Daniel_Cortez) выше. Авось кому пригодиться

stock minrandom(min, max)
{
if(min == max) return min;
else if(min > max) return random(min-max+1)+max;
else return random(max-min+1)+min;
}

L0ndl3m
03.11.2014, 17:23
Получаем некоторую часть текста, которую вернула функция.

Например, есть функция, которая возвращает строку:


SomeFunction()
{
static const
some_string[] = "Lorem ipsum!";

return some_string;
}


Вывод текста начнётся с той ячейки, которую указали в квадратных скобках, пример извлечения:

print((SomeFunction())[6]);
или так:

print((SomeFunction())[strfind(SomeFunction(), " ") + 1]); // пусть функция strfind сама найдёт пробел
Выведет: ipsum!

Ещё небольшой пример, если нужно вернуть последний символ в строке:


print((SomeFunction())[strlen(SomeFunction()) - 1]);
// не очень хороший пример вызова двух раз функции,
// но это используется как пример использования
В данном примере индекс, который нам нужен мы получили с помощью функции strlen ( функция возвращает длину строки ). От того значения, которая вернула функция strlen отняли единицу для того, чтобы не получить нулевой символ завершения строки ( '\0' ).

Выведет: !

alfazlo
20.03.2015, 22:29
Возможно оптимизацию можно получить используя одномерные массивы информации(о игроках, домах и т.д) вместо двухмерных, поясняю:
вместо:


enum pInfo
{
id,
name,
lvl
//...
}
new PlayerInfo[MAX_PLAYER][pInfo]; //Создается двухмерный массив

Используем следующий код


enum pInfo
{
id[MAX_PLAYER],
name[MAX_PLAYER],
lvl[MAX_PLAYER]
//...
}
new PlayerInfo[pInfo]; //Создается одномерный массив доступ к данным осуществляется также удобно как и в первом варианте

new idx = PlayerInfo[id][playerid]; //Пример получения id юзера.

А смысл всего этого в том что скорость доступа к данным одномерного массива выше чем к данным двухмерного, ну это для тех у кого как и у меня глюк на макимальном выйгрыше в 1 микросекунду :D

L0ndl3m
21.03.2015, 15:23
Наверное очень полезно, если кто-то хотел сделать char-массивы в стате игрока.

alfazlo
24.03.2015, 12:25
самый быстрый цикл for
for(new i = value; 0 != i; i--)

Daniel_Cortez
24.03.2015, 15:11
А смысл всего этого в том что скорость доступа к данным одномерного массива выше чем к данным двухмерного, ну это для тех у кого как и у меня глюк на макимальном выйгрыше в 1 микросекунду :D
И где вы здесь видите одномерный массив? В данном случае перечисление (enum) pInfo устроено точно так же, как и обычный массив, разве что доступ к элементам осуществляется не по индексам, а по именам. В результате получаем всё ту же кучку двумерных массивов - ни о какой оптимизации и речи быть не может.

alfazlo
24.03.2015, 15:46
достаточно во первых просто применить sizeof arrayname чтобы убедиться, и достаточно просто в цикле посчитывать значения чтобы убедиться в том что производительность присутствует.

enum pInfo {
id[1000],
money[1000]
}

new array1[pInfo];
new array2[1000];
new array3[1000][15];

printf("%d",sizeof array1); //2000
printf("%d",sizeof array2); //1000
printf("%d %d",sizeof array3, sizeof array3[]); //1000 //15

любой двухмерный массив в любом случае можно представить одномерным с расчетом индекса, но компилятор в павн не всегда использует самые оптимизируемые варианты доступа особенно с массивами.

Daniel_Cortez
03.07.2015, 15:19
Копирование строк.

Старый способ, с использованием нативной функции strcat:


new src[] = "Sample text";
new dest[sizeof(src)];
dest[0] = EOS, strcat(dest, src);


Новый способ, без нативных функций:


new src[] = "Sample text";
new dest[sizeof(src)];
for(new i = 0; (dest[i] = src[i]) != EOS; ++i) {}

Новый способ, как и написано выше, лучше тем, что не использует нативных функций, что хорошо скажется на производительности, особенно при использовании JIT.
Внимание! Размер dest должен быть не меньше, чем у src, иначе может произойти выход за пределы массива.

P.S.: Для тех, кому лень много печатать, вот сокращённый вариант:

for(new i;(dest[i]=src[i++]);){}

NewGreen
03.07.2015, 16:33
Копирование строк.
... что хорошо скажется на производительности, особенно при использовании JIT.

Ни чем "новый способ" не лучше, даже наоборот, strcat работает в разы быстрее. Что касается JIT, при его подключении, скорее всего скорость работы "нового способа" и strcat - станет одинаковой - но это, не факт, длина строки тоже играет не последнюю роль.

$continue$
03.07.2015, 19:11
Cкорее всего я не правильно провожу тест, ибо метод от Стаса ой как без JIT Compiler`a проигрывает, подправьте код да затестите :)
На большое количество текста (2457):


#define PROFILING_ITERS 1_000_000
new _str_[2500],
string_to_strcar[2500];
main()
{
new time = 0;
static const fmt_str[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|tes";

static const fmt_str_1[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|tes";

static const fmt_str_2[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|tes";

static const fmt_str_3[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|tes";

static const fmt_str_4[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|tes";

static const fmt_str_5[] = "test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|\
test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test|test";
format(_str_, sizeof(_str_), "%s%s%s%s%s%s", fmt_str, fmt_str_1, fmt_str_2, fmt_str_3, fmt_str_4, fmt_str_5);
time = GetTickCount();
printf("Запускаю цикл на %d итераторов | Нативный вызов функций | С JIT-компиляцией", PROFILING_ITERS);
for(new i = 0; i < PROFILING_ITERS; i++)
{
strcat(string_to_strcar, _str_);
}
printf("Время выполнение кода: %d", GetTickCount()-time);

new dest[sizeof(_str_)];
time = GetTickCount();
printf("Запускаю цикл на %d итераторов | Метод от Стаса (Daniel_Cortez'a) | С JIT-компиляцией", PROFILING_ITERS);
for(new u = 0; u < PROFILING_ITERS; u ++)
{
for(new z = 0; (dest[z] = _str_[z]) != EOS; ++z) {}
}
printf("Время выполнение кода: %d", GetTickCount()-time);
}



[17:58:52] Запускаю цикл на 1000000 итераторов | Нативный вызов функций | С JIT-компиляцией
[17:58:54] Время выполнение кода: 2512
[17:58:54] Запускаю цикл на 1000000 итераторов | Метод от Стаса (Daniel_Cortez'a) | С JIT-компиляцией
[17:59:04] Время выполнение кода: 9927

А пока без JIT - компиляцией выводились логи я успел за печенкой сходить xD (Но это наверное моя ошибка)


[18:02:20] Запускаю цикл на 1000000 итераторов | Метод от Стаса (Daniel_Cortez'a) | Без JIT-компиляцией
[18:05:08] Время выполнение кода: 168233
[18:05:08] Number of vehicle models: 3

На маленький текст (Взял 30)


#define PROFILING_ITERS 1_000_000
new
string_to_strcar[30];
main()
{
new time = 0;
static const fmt_str[] = "test|test|test|test|test|test|";
time = GetTickCount();
printf("Запускаю цикл на %d итераторов | Нативный вызов функций | С JIT-компиляцией", PROFILING_ITERS);
for(new i = 0; i < PROFILING_ITERS; i++)
{
strcat(string_to_strcar, fmt_str);
}
printf("Время выполнение кода: %d", GetTickCount()-time);

new dest[sizeof(fmt_str)];
time = GetTickCount();
printf("Запускаю цикл на %d итераторов | Метод от Стаса (Daniel_Cortez'a) | С JIT-компиляцией", PROFILING_ITERS);
for(new u = 0; u < PROFILING_ITERS; u ++)
{
for(new z = 0; (dest[z] = fmt_str[z]) != EOS; ++z) {}
}
printf("Время выполнение кода: %d", GetTickCount()-time);
}


Без джита:


[18:09:30] Запускаю цикл на 1000000 итераторов | Нативный вызов функций | Без JIT-компиляцией
[18:09:30] Время выполнение кода: 161
[18:09:30] Запускаю цикл на 1000000 итераторов | Метод от Стаса (Daniel_Cortez'a) | Без JIT-компиляцией
[18:09:33] Время выполнение кода: 2396
[18:09:33] Number of vehicle models: 3

C джитом:


[18:10:24] Запускаю цикл на 1000000 итераторов | Нативный вызов функций | С JIT-компиляцией
[18:10:25] Время выполнение кода: 108
[18:10:25] Запускаю цикл на 1000000 итераторов | Метод от Стаса (Daniel_Cortez'a) | С JIT-компиляцией
[18:10:25] Время выполнение кода: 153

P.S: Разорвите меня в пух и прах, что тут не так! :)

Daniel_Cortez
03.07.2015, 19:42
Чёрд, и ведь правда, на длинных строках работает медленнее =p
Я тестировал на строке из 24 символов (25 с учётом EOS):


#include <a_samp>

#define PROFILE_ITERS_MAJOR 1000000
#define PROFILE_ITERS_MINOR 100


main()
{
new src[] = "1234567890""1234567890""1234";
new dest[sizeof(src)];
new t, t0 = 0, t1 = 0;
print("\nProfiling, please wait...");
for(new i=0,j; i != PROFILE_ITERS_MAJOR; ++i)
{
t = GetTickCount();
for(j=0; j != PROFILE_ITERS_MINOR; ++j)
dest[0] = EOS, strcat(dest, src);
t0 += GetTickCount()-t;
t = GetTickCount();
for(j=0; j != PROFILE_ITERS_MINOR; ++j)
for(new x = 0; (dest[x] = src[x]) != EOS; ++x){}
t1 += GetTickCount()-t;
}
printf("strcat:\t%d", t0);
printf("for:\t%d", t1);
}


Проверял с включенным JIT и параметром компилятора -O1, результаты следующие:


Profiling, please wait...
strcat: 12454
for: 11377


Отсюда вывод, что у моего метода будет преимущество, если например нужно скопировать ник игрока.
Однако, как было замечено выше, с более длинными строками strcat начинает работать быстрее.

$continue$
03.07.2015, 19:55
Что, за параметр "-O1"? :)

Daniel_Cortez
03.07.2015, 20:47
Что, за параметр "-O1"? :)
Оптимизация генерируемого кода, указывается в pawn.cfg

NewGreen
04.07.2015, 15:51
Проверял с включенным JIT и параметром компилятора -O1, результаты следующие:


Profiling, please wait...
strcat: 12454
for: 11377


Отсюда вывод, что у моего метода будет преимущество, если например нужно скопировать ник игрока.
Однако, как было замечено выше, с более длинными строками strcat начинает работать быстрее.
Ради интереса проверил у себя, используя Ваш код теста выше:
Вот результат:
С JIT и параметром -O1


Profiling, please wait...
strcat: 4775
for: 6789


Без подключения JIT


Profiling, please wait...
strcat: 7102
for: 64421

$continue$
04.07.2015, 16:19
Ради интереса проверил у себя, используя Ваш код теста выше:
Вот результат:
С JIT и параметром -O1


Profiling, please wait...
strcat: 4775
for: 6789


Без подключения JIT


Profiling, please wait...
strcat: 7102
for: 64421

Ну тут тоже зависит вроде как от процессора. Сейчас у себя затестирую :)

C JIT и параметром компилятора -O1:


Profiling, please wait...
[15:27:48] strcat: 9945
[15:27:48] for: 12787

Без JIT и без параметра компилятора -O1:


Profiling, please wait...
[15:33:16] strcat: 18811
[15:33:16] for: 189713

Процессор Intel Core I7 - 2630QM с частотой 2.0

________________________________________________

Так же затестировал на хостинге от game-server.ru (Не пиар, думаю многий знают о этом хостиге, он же игрохост" (http://pro-pawn.ru/showthread.php?4751-%D0%9E%D0%9E%D0%9E-%D0%98%D0%B3%D1%80%D0%BE%D0%A5%D0%BE%D1%81%D1%82-%E2%84%961-%D0%B2-%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B8-%D1%81-2005-%D0%B3%D0%BE%D0%B4%D0%B0))
Там установлена OS Linux.
C JIT и параметром компилятора -O1:


Profiling, please wait...
[15:42:34] strcat: 19892
[15:42:34] for: 14060

Без JIT и без параметра компилятора -O1:


Profiling, please wait...
[15:47:01] strcat: 20715
[15:47:01] for: 92858

NewGreen
04.07.2015, 16:24
Ну тут тоже зависит вроде как от процессора. Сейчас у себя затестирую :)
И не только от процессора еще от ОСи на ПК/сервере.
На сервере, обычно это GNU/Linux, на линуксе время теста может еще больше склониться в пользу нативной функции strcat.

Daniel_Cortez
04.07.2015, 16:41
И не только от процессора еще от ОСи на ПК/сервере.
На сервере, обычно это GNU/Linux, на линуксе время теста может еще больше склониться в пользу нативной функции strcat.
Как раз таки наоборот, на Linux интерпретатор байткода AMX организован эффективнее, чем в версии для Widnows, за счёт расширений GNU C (см. здесь (http://www.compuphase.com/pawn/Pawn_Implementer_Guide.pdf), стр. 6, 71, 76).
Впрочем, это вряд ли касается ситуации, когда код выполняется под JIT.

IMHO, самый верный способ профайлинга - на сервере под Linux, желательно с JIT, там условия наиболее приближены к реальным (если не брать в расчёт аппаратную часть). Наверное, так и буду тестить в следующий раз. Главное не запускать тесты на хостинге - там обычно есть защита, которая автоматом вырубает сервер при большой нагрузке.

$continue$
04.07.2015, 16:49
Обновил #30 (http://pro-pawn.ru/showthread.php?5249-Pawn-%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%B5%D1%81%D0%BD%D1%8B%D0%B5-%D1%81%D0%BE%D0%B2%D0%B5%D1%82%D1%8B-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%8B&p=62288&viewfull=1#post62288) пост

NewGreen
04.07.2015, 17:02
Как раз таки наоборот, на Linux интерпретатор байткода AMX организован эффективнее, чем в версии для Widnows, за счёт расширений GNU C (см. здесь (http://www.compuphase.com/pawn/Pawn_Implementer_Guide.pdf), стр. 6, 71, 76).
Впрочем, это вряд ли касается ситуации, когда код выполняется под JIT.

IMHO, самый верный способ профайлинга - на сервере под Linux, желательно с JIT, там условия наиболее приближены к реальным (если не брать в расчёт аппаратную часть). Наверное, так и буду тестить в следующий раз. Главное не запускать тесты на хостинге - там обычно есть защита, которая автоматом вырубает сервер при большой нагрузке.

Согласен с Вами, в условиях приближенных к реальным, результат будет более объективным)

Daniel_Cortez
27.07.2015, 09:36
"Переключаемые" многострочные комментарии.

Думаю, все знакомы с многострочными комментариями в Pawn:


/*
SendClientMessage(playerid, -1, "Some text");
*/

Но что, если вам вдруг нужно раскомментировать этот участок кода?
Правильно, кроме стирания первых "/*" придётся выискивать и завершающие комментарий символы "*/".
Это может оказаться проблемой, если приходится часто комментировать/раскомментировать длинный участок кода (например, при тестировании).
На этот случай можно сделать комментарий "переключаемым":


/*
SendClientMessage(playerid, -1, "Some text");
// */

Теперь добавим перед "/*" один слэш ("/"):


//*
SendClientMessage(playerid, -1, "Some text");
// */

В итоге мы "нейтрализовали" многострочный комментарий.
Чтобы включить его обратно, достаточно лишь убрать первый слэш.
Обратите внимание: между последними двумя слэшами и закрытием многострочного комментария ("//" и "*/") стоит пробел.
Они разделены пробелом, чтобы компилятор обрабатывал их именно как "//" и "*/", а не "/", "/*" и "/" - в этом случае компилятор подумает, что это вложенный комментарий, и выдаст варнинг.



Пропуск инициализации ячеек массива нулями.

При объявлении локальных массивов внутри функций компилятор не только резервирует место в стеке под массив, но и инициализирует все его ячейки нулями, чтобы в них не было мусорных значений.
Сделано это из-за быдлокодеров, которые, пользуются переменными, забывая их инициализировать.
Тем не менее, инициализация занимает некоторое время, да и польза от этого сомнительная, если вы способны отдавать отчёт своим действиям.
К счастью, есть способ избежать потерь во времени, пропустив инициализацию:


SomeFunction()
{
// перед объявлением массива перейдём на метку skip_array_init
goto skip_array_init;
// объявляем массив
new arr[256];
skip_array_init:
// в этом месте массив arr будет существовать, но он не будет инициализирован:
// код инициализации существует, но он находится _до_ метки skip_array_init,
// т.е. мы обошли этот код

// дальше - код с использованием массива
}

Скомпилируем код, а затем дизассемблируем его.
Получим следующую последовательность инструкций:


; Начало функции SomeFunction.
proc
; И сразу же - переход на метку l.0 (см. далее).
jump 0
; Выделение места в стеке под массив из 16 ячеек (ffffffc0 означает "-16")
; (этот код никогда не сработает, переход на l.0 был безусловным).
stack ffffffc0
; Заполнение массива нулями происходит побайтово,
; 16 ячеек - 64 байта (40 в шестнадцатеричной системе счисления).
zero.pri
addr.alt ffffffc0
fill 40
; Метка, с помощью которой пропускается инициализация массива.
l.0
; Установка вершины стека в то положение, когда выделено 16 ячеек под массив,
; Обратите внимание: в отличие от обычного создания массива,
; здесь массив не заполняется никакими значениями,
; происходит только резервирование пространства,
lctrl 5
add.c ffffffc0
sctrl 4
; Код с использованием массива.
; ...
; Высвобождение пространства в стеке и возврат из функции.
stack 40
zero.pri
retn

Если кому интересно, вот тест скорости:


#include <a_samp>

const PROFILE_ITERATIONS_MAJOR = 1000;
const PROFILE_ITERATIONS_MINOR = 1000;

const STRING_SIZE = 512;


const SZ = STRING_SIZE;

#define _m1();\
{new a[SZ];}
#define method1();\
_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();_m1();

#define _m2();\
{goto skip_init;new a[SZ];skip_init:}
#define method2();\
_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();_m2();


bool:IsJITEnabled()
{
#emit lctrl 7
#emit retn
return true;
}

main()
{
printf(
"Testing array initialization..." "\n"\
"(%dx%d iterations, array size: %d, JIT status: %sabled)",
PROFILE_ITERATIONS_MAJOR, PROFILE_ITERATIONS_MINOR,
STRING_SIZE, IsJITEnabled() ? ("en") : ("dis")
);
new t1, t2, t, i, j;
t1 = 0, t2 = 0;
for(i = 0; i < PROFILE_ITERATIONS_MAJOR; ++i)
{
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
method1();
}
t1 += GetTickCount()-t;
t = GetTickCount();
for(j = 0; j < PROFILE_ITERATIONS_MINOR; ++j)
{
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
method2();
}
t2 += GetTickCount()-t;
}
printf(
"method 1: %d" "\n"\
"method 2: %d",
t1, t2
);
}

Макросы method1 и method2 сделаны, чтобы повторить в них создание локальных массивов по 10 раз.
Кроме того сами макросы используются в функции main по 10 раз (итого получается 10x10 = 100 повторов).
Это не что иное, как развёртывание циклов. Я сделал именно так, чтобы уменьшить погрешность от циклов и вызовов GetTickCount, таким образом, увеличив точность измерений.
Также в процессе компиляции выдаётся куча варнингов, но они, опять же, из-за макросов, не обращайте на них внимания.
Тест я запускал на массивах из 32, 256 и 512 ячеек, с включенным и выключенным JIT.
Результаты следующие:



Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: disabled)
method 1: 5581
method 2: 47

Testing array initialization...
(1000x1000 iterations, array size: 32, JIT status: enabled)
method 1: 2640
method 2: 4




Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: disabled)
method 1: 31324
method 2: 42

Testing array initialization...
(1000x1000 iterations, array size: 256, JIT status: enabled)
method 1: 15106
method 2: 3




Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: disabled)
method 1: 63840
method 2: 40

Testing array initialization...
(1000x1000 iterations, array size: 512, JIT status: enabled)
method 1: 29161
method 2: 4


Из измерений можно сделать вывод, что размер массива никак не влияет на результаты метода с пропуском инициализации массива.
Но всё же от трюка не сильно много пользы. Да, цифры в тесте, казалось бы, большие, но не стоит забывать, что они получены с помощью 100 миллионов итераций. В нормальных же условиях время инициализации массива в 512 ячеек составит всего ~0.0006384 мс или ~0.0000006 секунды.

^_^
12.08.2015, 02:27
Тема обновлена, спасибо Daniel_Cortez и Londlem.

Daniel_Cortez
12.08.2015, 13:48
Почему пропуск инициализации сокращает время декларации переменных?
Во-первых, слово "декларация" в русском языке больше относится к юридической сфере.
В программировании гораздо чаще упортебляется слово "объявление".
Во-вторых, объявление переменных относится к высокоуровневым языкам.
В скомпилированном коде никаких объявлений нет, есть только выделение стекового пространства.
Нет, серьёзно, как ты вообще представляешь "сокращение времени декларации переменных"? Это какое-то ускорение набора букв на клавиатуре?
Если имеешь в виду повышение производительности кода, то так и пиши.


пропускаем дефолтную инициализацию
Нет такого слова в русском языке. Да и вообще, это русскоязычный форум, а не МКС или Брайтон-Бич (https://en.wikipedia.org/wiki/Runglish).
В русском языке для таких целей есть слово "обычный".


массив мануально инициализирован
По мануалам что ли?
Ок, шутки в сторону, есть в живом великорусском языке слово "вручную".


иницальные значения
То же самое. "Начальные".


Декларируем массив
См. самое начало поста.




#define explicit^%0[%1]; goto _%1_%0;new %0[%1];_%1_%0:
http://ihost.pro-pawn.ru/image.php?di=1MUK
Да эта тема уже переполнена "правильным смыслом"...
И да, зачем добавлять к названию метки размер массива? Название и без размера будет уникальным, нельзя же объявить 2 разных массива с одинаковыми именами и разными размерами.

Могу предложить такой вариант:

#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:
Использовать символ ^ я бы не стал, т.к. за ним нужно тянуться в верхний ряд клавиатуры, двоеточие в этом случае будет намного удобнее.

Desulaid
19.08.2015, 01:14
Еще есть многократное присваивание.

Первый вариант:



new x, y, z;
x = abc;
y = abc;
z = abc;


Второй вариант с многократным присваиванием



new x, y, z;
x =
y =
z = abc;

Второй вариант быстрее и оптимизированнее.

assembly version

Первый вариант:

load.pri c ;Get abc
stor.pri 8 ;Store it in X
break ; 20
load.pri c ;Get abc
stor.pri 4 ;Store it in Y
break ; 34
load.pri c ;Get abc
stor.pri 0 ;Store it in Z

Второй вариант:

load.pri c ;Get abc
stor.pri 0 ;Store in X
stor.pri 4 ;Store in Y
stor.pri 8 ;Store in Z

$continue$
19.08.2015, 02:25
Наверное стоит дополнить к посту #36, как инициализируется массив в Pawn:


static array[2000]; // взял специально так много

Это будет равно тому же:


#include <a_samp>

main()
{
new array[2000];
for(new i = 0; i < sizeof(array); i++)
{
array[i] = 0;
}
}

Если откомпилировать на ASM, то будет видно:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 4
; line 5
break ; c
zero.pri
retn


DATA 0 ; 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

STKSIZE 1000


Где dump - забивание 2000 ячеек в нули.

Daniel_Cortez
26.09.2015, 20:25
Наверное стоит дополнить к посту #36, как инициализируется массив в Pawn:


static array[2000]; // взял специально так много

Это будет равно тому же:


#include <a_samp>

main()
{
new array[2000];
for(new i = 0; i < sizeof(array); i++)
{
array[i] = 0;
}
}

В случае с new место под массив резервируется в стеке и сам массив будет существовать до тех пор, пока функция не завершит свою работу или пока не закончится локальный блок, внутри которого объявлен массив - что раньше. Если же объявить массив с помощью static, он всё время будет существовать в секции данных. И быть такого не может, чтобы те 2 куска кода компилировались одинаково.

EDIT: Решил проверить, сравнил 2 идентичных отрывка кода.
Отличие лишь в том, что в 1-м случае массив объявляется с помощью ключевого слова static, а во 2-м - с помощью new.
Размер массивов задал 256, чтобы не так сильно засорять листинг.


main()
{
new arr[256];
for (new i = 0; i < sizeof(arr); ++i)
arr[i] = 0;
}



main()
{
static arr[256];
for (new i = 0; i < sizeof(arr); ++i)
arr[i] = 0;
}


Компилируем с ключом "-a".
Первый отрывок:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 2
; line 3
; line 4
;$lcl i fffffffc
push.c 0
;$exp
jump 2
l.0 ; 1c
; line 4
inc.s fffffffc
;$exp
l.2
load.s.pri fffffffc
const.alt 100
jsgeq 1
;$exp
; line 5
zero.alt
load.s.pri fffffffc
idxaddr
move.alt
zero.pri
stor.i
;$exp
jump 0
l.1 ; 60
stack 4
zero.pri
retn


DATA 0 ; 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
dump 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

STKSIZE 1000


Второй отрывок:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 2
; line 3
;$lcl arr fffffc00
stack fffffc00
zero.pri
addr.alt fffffc00
fill 400
; line 4
;$lcl i fffffbfc
push.c 0
;$exp
jump 2
l.0 ; 38
; line 4
inc.s fffffbfc
;$exp
l.2
load.s.pri fffffbfc
const.alt 100
jsgeq 1
;$exp
; line 5
addr.alt fffffc00
load.s.pri fffffbfc
idxaddr
move.alt
zero.pri
stor.i
;$exp
jump 0
l.1 ; 80
stack 4
stack 400
zero.pri
retn


STKSIZE 1000


Как видно, в первом случае массив находится в секции данных. В функции main() место в стеке выделяется лишь под переменную i.
Во втором же случае секция данных совершенно пустая и место под массив выделяется именно в стеке.

Ч.т.д.

Daniel_Cortez
29.09.2015, 15:22
Объединение строк и массивов.


Компилятор Pawn не умеет самостоятельно объединять неизменяемые (const) массивы, т.е. если объявить два постоянных массива с одинаковым содержимым, компилятор не станет разделять между ними одну копию содержимого.
Пример:


#include <a_samp>

main()
{
print("Hello world!");
print("Hello world!");
}

Здесь "Hello world!" - это массив, заданный в неявном виде непосредственно на месте использования.
Скомпилируем код с ключом "-a" и посмотрим, что получилось:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 4
; line 5
push.c 0
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
; line 6
push.c 34
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
zero.pri
retn


DATA 0 ; 0
dump 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0 48 65 6c
dump 6c 6f 20 77 6f 72 6c 64 21 0

STKSIZE 1000

Обратите внимание на последовательность "48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0".
Эта последовательность - ни что иное, как строка "Hello world!" и она повторяется в секции данных 2 раза.

Проблему легко исправить, объявив строку в виде постоянного массива явным образом:


#include <a_samp>

main()
{
static const str_hello_world[] = "Hello world!";
print(str_hello_world);
print(str_hello_world);
}

Проверим результат:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 4
; line 5
; line 6
push.c 0
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
; line 7
push.c 0
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
zero.pri
retn


DATA 0 ; 0
dump 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0

STKSIZE 1000

Заметьте, последовательность "48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0" не повторяется, а значит строка находится в секции данных всего в одном экземпляре.

А теперь к практике: особенно часто можно заметить тот недостаток при использовании макросов.
Пример:


#define SendNotAnAdminMsg(%0); SendClientMessage(%0, -1, "Вы не администратор!");

Здесь создаётся не 1, не 2 копии строки, а ровно столько, сколько вы использовали его во всех командах, диалогах, etc.
Исправляем:


static const __msg_not_an_admin[] = "Вы не администратор!";
#define SendNotAnAdminMsg(%0); SendClientMessage(%0, -1, __msg_not_an_admin);

2 символа подчёркивания в начале названия массива - не более, чем перестраховка от его использования не по назначению (не из макроса).




Упаковка строк.


Возьмём для примера простой скрипт:


#include <a_samp>

main()
{
print("Hello world!");
}

Возможно ли как-то оптимизировать этот код?
Вполне, можно упаковать строку "Hello world!". На производительности это практически не отразится, но зато строки в упакованном виде будут занимать меньше места в памяти.
Для упаковки строки достаточно лишь поставить перед ней знак "!":


#include <a_samp>

main()
{
print(!"Hello world!");
}

Для доказательства экономии сравним, во что скомпилируется код в обоих вариантах.
Без упаковки строки:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 4
; line 5
push.c 0
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
zero.pri
retn


DATA 0 ; 0
dump 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0

STKSIZE 1000

С упаковкой строки:


CODE 0 ; 0
;program exit point
halt 0

proc ; main
; line 4
; line 5
push.c 0
;$par
push.c 4
sysreq.c 0 ; print
stack 8
;$exp
zero.pri
retn


DATA 0 ; 0
dump 48656c6c 6f20776f 726c6421 0

STKSIZE 1000

Как можно заметить, содержимое секции кода в обоих вариантах полностью совпадает.
Однако, в секции данных в первом варианте зарезервировано 13 ячеек: длина строки "Hello world!" - 12 символов, ещё 1 символ сигнализирует о завершении строки, при этом в неупакованном виде на каждый символ приходится по 1 ячейке.
Во втором же варианте в секции данных всего 4 ячейки: 13/4 = 3+1 (13 не делится на 4 нацело, поэтому остаток размещается в дополнительной ячейке).
В каждой ячейке могут вместиться 4 символа упакованной строки. Отсюда и экономия: упакованные строки занимают до 4 раз меньше места в памяти.
Внимание: Функции format и printf не умеют правильно работать с упакованными строками (как в форматной строке, так и в форматируемых параметрах).
Что не так уж и удивительно, если учесть, что к этим функциям прикасался куй.

Unreal
21.10.2015, 22:25
можно отдельный урок для

3) Как пропустить необязательный параметр?
В Pawn есть возможность создания функций с необязательными параметрами, у которых уже есть значения, задаваемые по умолчанию.

пожалуйста

[ForD]
21.10.2015, 22:37
можно отдельный урок для


пожалуйста

зачем? просто если параметр не нужен ставишь "землю"( _ ) и все


gettime(_, minutes, seconds);
gettime(hours, _, seconds);
gettime(hours, minutes, _);

Unreal
21.10.2015, 22:38
;65005']зачем? просто если параметр не нужен ставишь "землю"( _ ) и все


gettime(_, minutes, seconds);
gettime(hours, _, seconds);
gettime(hours, minutes, _);


А какие еще функций это поддерживают, как узнать? и.т.д.

[ForD]
21.10.2015, 23:24
А какие еще функций это поддерживают, как узнать? и.т.д.

должны поддерживать любые где есть параметры,эсперименты никто не отменял

Daniel_Cortez
22.10.2015, 04:46
А какие еще функций это поддерживают, как узнать? и.т.д.
Открыть папку pawno/include и искать в инклудах знак "=", после этого знака обычно указывают значения аргументов по умолчанию.

Скажу сразу, в SA:MP полно таких функций с необязательными аргументами, но они сделаны только для обратной совместимости скриптов с предыдущими версиями.
Например, в версии 0.2 была функция CreatePickup:

native CreatePickup(model, type, Float:X, Float:Y, Float:Z);
Затем в 0.3a реализовали виртуальные миры и добавили возможность ставить пикапы только в один из таких миров, добавив в CreatePickup параметр virtualworld.

native CreatePickup(model, type, Float:X, Float:Y, Float:Z, virtualworld = 0);
Параметр сделан опциональным, поэтому, если попытаться скомпилировать скрипт для сервера версии 0.2, в котором параметр virtualworld не указан, то скрипт скомпилируется и с инклудами версии 0.3a и новее.

Unreal
23.10.2015, 19:29
а что тогда озночает & ?

н-р:
native GetPlayerPos(playerid, &Float:x, &Float:y, &Float:z);

$continue$
23.10.2015, 20:12
а что тогда озночает & ?

н-р:
native GetPlayerPos(playerid, &Float:x, &Float:y, &Float:z);

ссылка (https://ru.wikipedia.org/wiki/Ссылка_(программирование)), но на самом деле это "псевдо ссылка"

Desulaid
18.11.2015, 00:48
Код из первого пункта можно на:


stock min_max_random(min=0, max)
return random(max - min) + min;
#if defined _ALS_random
#undef random
#else
#define _ALS_random
#endif
#define random(%0,%1) min_max_random(%0,%1)

Использование:

random(10); // 0;10
random(10,20); // 10;20

Daniel_Cortez
18.11.2015, 06:28
stock min_max_random(min=0, max)
return random(max - min) + min;
#if defined _ALS_random
#undef random
#else
#define _ALS_random
#endif
#define random(%0,%1) min_max_random(%0,%1)

Использование:

random(10); // 0;10
random(10,20); // 10;20
Я просто оставлю это здесь:


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

vovandolg
06.10.2016, 14:29
#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:

А что если надо было бы не 1 переменную, а 2 или 3, по очереди так и писать?


noinit:str[10];
noinit:strr[20];
noinit:strrr[30];

ziggi
06.10.2016, 16:31
#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:

А что если надо было бы не 1 переменную, а 2 или 3, по очереди так и писать?


noinit:str[10];
noinit:strr[20];
noinit:strrr[30];



#define new%0noinit<%1[%2]%3>; goto _noinit_%1; new %1[%2]%3; _noinit_%1:
Использовать так:

new noinit<str[10], strr[20], strrr[30]>;

vovandolg
06.10.2016, 16:51
#define new%0noinit<%1[%2]%3>; goto _noinit_%1; new %1[%2]%3; _noinit_%1:
Использовать так:

new noinit<str[10], strr[20], strrr[30]>;


warning 201: redefinition of constant/macro (symbol "new%0noinit<%1[%2]%3>;")

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

Кину полностью из за чего может быть ругается:


zm.pwn(82) : warning 201: redefinition of constant/macro (symbol "new%0noinit<%1[%2]%3>;")
zm.pwn(911) : error 001: expected token: ";", but found "||"
zm.pwn(911) : error 001: expected token: ";", but found "new"
zm.pwn(911) : warning 203: symbol is never used: "Y_FOREACH_SECOND"
zm.pwn(911) : error 017: undefined symbol "Player"
zm.pwn(911) : fatal error 107: too many error messages on one line


Строки в коде:



function StartMap()
{
InfectedTime = 0;
if(oPlayers > 0)
{
foreach(new i: Player) //911 line
{
if(pLogged{i} == 1)
{
if(mInfo[Interior] != pInt{i})
SetPlayerInterior(i, mInfo[Interior]);
SetPlayerPos(i, mInfo[HumanSpawnX], mInfo[HumanSpawnY], mInfo[HumanSpawnZ] + 0.6, 0);
TogglePlayerControllable(i, 1);
SetPlayerHealth(i, 100.0);
HumanSetup(i);
}
}
}
TextDrawHideForAll(WinGUI);
TextDrawHideForAll(ZombieWinText);
TextDrawHideForAll(HumanWinText);
TextDrawShowForAll(RoundStarted);
SeriaKiller = 255;
maptime = MAX_MAPTIME;
mapvar = 1;
new noinit<strr[145]>;
format(strr, 145, "["COLOR_MAP"Loading Map"COLOR_TEXT2"] Map <%s> created by <%s>", mInfo[MapName], mInfo[CreatedBy]);
SendClientMessageToAll(COLOR_TEXT1, strr);

randvar = SetTimer(!"RandomZombie", MAX_ZOMBIE_TIME, false);
SetTimer(!"HideMessage2", 2500, false);
return 1;
}

ziggi
06.10.2016, 18:53
warning 201: redefinition of constant/macro (symbol "new%0noinit<%1[%2]%3>;")

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

Кину полностью из за чего может быть ругается:


zm.pwn(82) : warning 201: redefinition of constant/macro (symbol "new%0noinit<%1[%2]%3>;")
zm.pwn(911) : error 001: expected token: ";", but found "||"
zm.pwn(911) : error 001: expected token: ";", but found "new"
zm.pwn(911) : warning 203: symbol is never used: "Y_FOREACH_SECOND"
zm.pwn(911) : error 017: undefined symbol "Player"
zm.pwn(911) : fatal error 107: too many error messages on one line


Строки в коде:



function StartMap()
{
InfectedTime = 0;
if(oPlayers > 0)
{
foreach(new i: Player) //911 line
{
if(pLogged{i} == 1)
{
if(mInfo[Interior] != pInt{i})
SetPlayerInterior(i, mInfo[Interior]);
SetPlayerPos(i, mInfo[HumanSpawnX], mInfo[HumanSpawnY], mInfo[HumanSpawnZ] + 0.6, 0);
TogglePlayerControllable(i, 1);
SetPlayerHealth(i, 100.0);
HumanSetup(i);
}
}
}
TextDrawHideForAll(WinGUI);
TextDrawHideForAll(ZombieWinText);
TextDrawHideForAll(HumanWinText);
TextDrawShowForAll(RoundStarted);
SeriaKiller = 255;
maptime = MAX_MAPTIME;
mapvar = 1;
new noinit<strr[145]>;
format(strr, 145, "["COLOR_MAP"Loading Map"COLOR_TEXT2"] Map <%s> created by <%s>", mInfo[MapName], mInfo[CreatedBy]);
SendClientMessageToAll(COLOR_TEXT1, strr);

randvar = SetTimer(!"RandomZombie", MAX_ZOMBIE_TIME, false);
SetTimer(!"HideMessage2", 2500, false);
return 1;
}


Похоже из-за foreach нельзя использовать new в качестве макроса.
Тогда возьми это:

#define @new%0\32%1[%2]%3; goto _noinit_%1; new %1[%2]%3; _noinit_%1:
и используй так:

@new str[10], strr[20], strrr[30];
Синтаксис лучше придумать не смог.

vovandolg
06.10.2016, 19:05
Синтаксис лучше придумать не смог.


http://cs5.pikabu.ru/post_img/big/2015/12/04/5/1449210847155432089.jpg

Blood
12.10.2016, 15:22
Похоже из-за foreach нельзя использовать new в качестве макроса.
Тогда возьми это:

#define @new%0\32%1[%2]%3; goto _noinit_%1; new %1[%2]%3; _noinit_%1:
и используй так:

@new str[10], strr[20], strrr[30];
Синтаксис лучше придумать не смог.

Через этот макрос есть ли вероятность бага (http://pro-pawn.ru/showthread.php?14157-%D0%A2%D0%B0%D0%BA-%D0%B7%D0%B0%D0%B4%D1%83%D0%BC%D0%B0%D0%BD%D0%BE-%D0%B8%D0%BB%D0%B8-%D1%8D%D1%82%D0%BE-%D0%B1%D0%B0%D0%B3) с goto ?

DeimoS
12.10.2016, 17:22
Через этот макрос есть ли вероятность бага (http://pro-pawn.ru/showthread.php?14157-%D0%A2%D0%B0%D0%BA-%D0%B7%D0%B0%D0%B4%D1%83%D0%BC%D0%B0%D0%BD%D0%BE-%D0%B8%D0%BB%D0%B8-%D1%8D%D1%82%D0%BE-%D0%B1%D0%B0%D0%B3) с goto ?

Эмм, если ты внимательно посмотришь код, то увидишь, что там создаётся всего 1 метка.

Daniel_Cortez
12.10.2016, 18:08
Через этот макрос есть ли вероятность бага (http://pro-pawn.ru/showthread.php?14157-%D0%A2%D0%B0%D0%BA-%D0%B7%D0%B0%D0%B4%D1%83%D0%BC%D0%B0%D0%BD%D0%BE-%D0%B8%D0%BB%D0%B8-%D1%8D%D1%82%D0%BE-%D0%B1%D0%B0%D0%B3) с goto ?
Название метки зависит от названия первого массива, объявляемого с помощью макроса, так что оно должно быть уникально. Если использовать макрос несколько раз внутри одной и той же функции, названия меток получатся разными и никакого бага не будет.

Alpano
07.04.2017, 13:06
Наблюдаю "странный" баг при использовании макроса

#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:

При повторном использовании функции где идет создание массива таким методом, то текст предыдущего использования функции остается, и получается "накладывание" текстов.

Возможно ли это из-за того что текст заполняется через strcat?
Тестить не тестил, но помоему и при заполнении через format такая же шляпа.

Может стоит как-то заменить стоковую инициализацию каким-то более быстрым вариантом?
Или всё-же отказаться от "заводской" инициализации при заполнении через strcat...

ziggi
07.04.2017, 13:12
Наблюдаю "странный" баг при использовании макроса

#define noinit:%0[%1]; goto _noinit_%0;new %0[%1];_noinit_%0:

При повторном использовании функции где идет создание массива таким методом, то текст предыдущего использования функции остается, и получается "накладывание" текстов.

Возможно ли это из-за того что текст заполняется через strcat?
Тестить не тестил, но помоему и при заполнении через format такая же шляпа.

Может стоит как-то заменить стоковую инициализацию каким-то более быстрым вариантом?
Или всё-же отказаться от "заводской" инициализации при заполнении через strcat...

Зачем использовать то, что не понимаешь? noinit само за себя говорит: "без инициализации", что означает не заполнять массив нулями.

Alpano
07.04.2017, 13:24
Зачем использовать то, что не понимаешь? noinit само за себя говорит: "без инициализации", что означает не заполнять массив нулями.

так я и хочу понять, разобраться.
я в принципе пока писал комментарий сам понял почему так, но всё-же решил опубликовать)

vovandolg
07.04.2017, 18:48
Кому то просто нужно ближе подобраться к понятию инициализация

SooBad
07.04.2017, 21:39
так я и хочу понять, разобраться.
я в принципе пока писал комментарий сам понял почему так, но всё-же решил опубликовать)

Данный приём стоит использовать только тогда, когда массив имеет большое кол-во ячеек.
Ибо инициализация таких объемных наборов данных занимает достаточно продолжительное время, особенно в тех массивах, кои занимают по 1500-2000 байт и выше.

Применять оператор goto, или отправлять инструкцию jump, в местах, например, где необходимо проинициализировать массив, заточенный под форматирование - бессмысленно.

VVWVV
07.04.2017, 23:38
Данный приём стоит использовать только тогда, когда массив имеет большое кол-во ячеек.
Ибо инициализация таких объемных наборов данных занимает достаточно продолжительное время, особенно в тех массивах, кои занимают по 1500-2000 байт и выше.

Глупо использовать локальные массивы размером 1500-2000 байт. Для таких массивов существует статическая память.

DeimoS
16.09.2017, 14:29
Так и не решился создавать отдельную тему, так что отпишу сюда.

Очень часто в чужих скриптах вижу, как один и тот же диалог повторяется в нескольких местах. Это мало того, что потребляет лишнюю память, так ещё и доставляет трудности в случае, если вдруг захочется изменить текст этого диалога (да, можно пользоваться автозаменой, но я хочу предложить альтернативу, которая и уменьшит потребление памяти, и не заставит мучиться с автозаменой).
А если это целая система, построенная на диалогах - всё становится ещё страшнее, ибо переплетение в виде кучи вызовов разных диалогов напрочь убивает читаемость.

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


stock ShowRegistrationDialog(playerid, dialogid)
{
switch(dialogid)
{
case id_диалога_1:
{
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case id_диалога_2:
{
//Тут любой код, что требуется выполнять при каждом показе диалога
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case id_диалога_3:
{
ShowPlayerDialog(playerid, /*тут можно именно dialogid и указывать, ибо там в любом случае будет хранится нужный ID диалога, если условие в switch сработает*/, прочая_информация);
}
}
return 1;
}

И уже просто в том месте, где нужно (повторно) показать диалог, прописываем:

ShowRegistrationDialog(playerid, id_диалога_1);
Всё это и код в более опрятный вид приводит, ибо нет строк, что за границы экрана уходят, и позволяют без проблем вызывать диалоги там, где это требуется, без каких-либо копирований целого диалога.

Только стоит обязательно учитывать, что не стоит в одну такую функцию пихать кучу диалогов, ибо это будет, опять же, неудобно. Лучше, как я писал выше, для каждой системы выделять одну функцию.

И если использовать перечисление для хранения ID диалогов, то код становится ещё опрятнее:

enum
{
dRegisterMain,
dRegisterInputName,
dRegisterInputPass
}


stock ShowRegistrationDialog(playerid, dialogid)
{
switch(dialogid)
{
case dRegisterMain:
{
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case dRegisterInputName:
{
//Тут любой код, что требуется выполнять при каждом показе диалога
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case dRegisterInputPass:
{
ShowPlayerDialog(playerid, /*тут можно именно dialogid и указывать, ибо там в любом случае будет хранится нужный ID диалога, если условие в switch сработает*/, прочая_информация);
}
}
return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch(dialogid)
{
case dRegisterMain:
{
if(!response)
return 1;
else
return ShowRegistrationDialog(playerid, dRegisterInputName);

}
case dRegisterInputName:
{
if(!response)
return ShowRegistrationDialog(playerid, dRegisterMain);
else
return ShowRegistrationDialog(playerid, dRegisterInputPass);
}
case dRegisterInputName:
{
if(!response)
return ShowRegistrationDialog(playerid, dRegisterInputName);
else
return SpawnPlayer(playerid);
}
}
}

Собственно, всё. Вроде, вполне логичное оформление кода, но, судя по коду в паблике, практически никто не доходит до использования подобного.

$continue$
16.09.2017, 15:46
Для подобных случаев есть: TDW Dialog Library (http://pro-pawn.ru/showthread.php?14633-TDW-Dialog-Library)

Так и не решился создавать отдельную тему, так что отпишу сюда.

Очень часто в чужих скриптах вижу, как один и тот же диалог повторяется в нескольких местах. Это мало того, что потребляет лишнюю память, так ещё и доставляет трудности в случае, если вдруг захочется изменить текст этого диалога (да, можно пользоваться автозаменой, но я хочу предложить альтернативу, которая и уменьшит потребление памяти, и не заставит мучиться с автозаменой).
А если это целая система, построенная на диалогах - всё становится ещё страшнее, ибо переплетение в виде кучи вызовов разных диалогов напрочь убивает читаемость.

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


stock ShowRegistrationDialog(playerid, dialogid)
{
switch(dialogid)
{
case id_диалога_1:
{
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case id_диалога_2:
{
//Тут любой код, что требуется выполнять при каждом показе диалога
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case id_диалога_3:
{
ShowPlayerDialog(playerid, /*тут можно именно dialogid и указывать, ибо там в любом случае будет хранится нужный ID диалога, если условие в switch сработает*/, прочая_информация);
}
}
return 1;
}

И уже просто в том месте, где нужно (повторно) показать диалог, прописываем:

ShowRegistrationDialog(playerid, id_диалога_1);
Всё это и код в более опрятный вид приводит, ибо нет строк, что за границы экрана уходят, и позволяют без проблем вызывать диалоги там, где это требуется, без каких-либо копирований целого диалога.

Только стоит обязательно учитывать, что не стоит в одну такую функцию пихать кучу диалогов, ибо это будет, опять же, неудобно. Лучше, как я писал выше, для каждой системы выделять одну функцию.

И если использовать перечисление для хранения ID диалогов, то код становится ещё опрятнее:

enum
{
dRegisterMain,
dRegisterInputName,
dRegisterInputPass
}


stock ShowRegistrationDialog(playerid, dialogid)
{
switch(dialogid)
{
case dRegisterMain:
{
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case dRegisterInputName:
{
//Тут любой код, что требуется выполнять при каждом показе диалога
ShowPlayerDialog(playerid, dialogid, прочая_информация);
}
case dRegisterInputPass:
{
ShowPlayerDialog(playerid, /*тут можно именно dialogid и указывать, ибо там в любом случае будет хранится нужный ID диалога, если условие в switch сработает*/, прочая_информация);
}
}
return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch(dialogid)
{
case dRegisterMain:
{
if(!response)
return 1;
else
return ShowRegistrationDialog(playerid, dRegisterInputName);

}
case dRegisterInputName:
{
if(!response)
return ShowRegistrationDialog(playerid, dRegisterMain);
else
return ShowRegistrationDialog(playerid, dRegisterInputPass);
}
case dRegisterInputName:
{
if(!response)
return ShowRegistrationDialog(playerid, dRegisterInputName);
else
return SpawnPlayer(playerid);
}
}
}

Собственно, всё. Вроде, вполне логичное оформление кода, но, судя по коду в паблике, практически никто не доходит до использования подобного.

DeimoS
16.09.2017, 15:56
Для подобных случаев есть: TDW Dialog Library (http://pro-pawn.ru/showthread.php?14633-TDW-Dialog-Library)

Ну, во-первых, знания о том, как такое можно реализовать, лишними не будут никогда. Слепое пользование чужим инструментом часто приводит к тому, что пользователь не может решить, казалось бы, простые проблемы, связанные с этим инструментом.
Во-вторых, изучать синтаксис целой библиотеки (который, к слову, далеко не самый удачный, как по мне) ради подобного - не самое лучшее распределение собственного времени =)

$continue$
16.09.2017, 16:09
Пишу мод разбитый на модули, данную библиотеку взял для данных целей. Работает все прекрасно (и синтаксис довольно таки удачный)

Ну, во-первых, знания о том, как такое можно реализовать, лишними не будут никогда. Слепое пользование чужим инструментом часто приводит к тому, что пользователь не может решить, казалось бы, простые проблемы, связанные с этим инструментом.
Во-вторых, изучать синтаксис целой библиотеки (который, к слову, далеко не самый удачный, как по мне) ради подобного - не самое лучшее распределение собственного времени =)

DeimoS
16.09.2017, 16:27
Пишу мод разбитый на модули, данную библиотеку взял для данных целей. Работает все прекрасно (и синтаксис довольно таки удачный)

Использую самописанную библиотеку для тех же целей, похожую на версию от Стяжкина (http://pro-pawn.ru/showthread.php?13626-u_dialogs-inc) (то бишь, синтаксис максимально приближен к синтаксису оригинальных функций), но это не помешало написать мне тот пост :) У каждого свои предпочтения и, как можно заметить, большинство предпочитает не пользоваться какими-то сторонними библиотеками для обработки диалогов. И моя информация именно для них