PDA

Просмотр полной версии : [Урок] Всё о Stock



DeimoS
30.01.2014, 15:56
Я вас категорически приветствую, друзья!
Сегодня я постараюсь объяснить вам, что же за зверь такой, этот stock, и с чем его едят. И сделать это я постараюсь на человеческом языке, дабы всем было понятно. Начнём.


Определение Stock:

Stock - Маркер компилятора, указывающий компилятору на исключение кода функции из конечного amx файла, если функция не используется в коде скрипта © DeimoS












Иными словами код функции или переменные, созданные с использованием маркера "stock", не будут включены в скомпилированную версию мода, если они не будут использованы в моде.
Если же создать функцию/переменную с использованием других маркеров, компилятор включит их в финальную версию, сигнализирует о их бесполезности и AMX машина (сервер) выделит для них память




Способы применения Stock:



Переменные:

Выглядит это так



Целочисленный тип
Вещественный тип
Строка
Одномерный массив
Двумерный массив





new

new variable;

new Float:variable;

new variable[10];

new variable[2] =
{1, 7};

new variable[2][1] =
{ {8}, {15} };





stock

stock variable;

stock Float:variable;

stock variable[10];

stock variable[2] =
{1, 7};

stock variable[2][1] =
{ {8}, {15} };



Использование переменных в stock точно такое же, как и использование переменных в new. В чём же различие, спросите Вы?

А различие именно в том, что stock не регистрируется в памяти сервера. То есть, если переменная объявлена через stock, но в коде она нигде не используется, компилятор автоматически удалит её при компиляции (имеется ввиду то, что в .amx варианте вашего кода этой переменной не будет). Следовательно лишней памяти сервер не будет выделять для этой переменной. Данное правило распространяется и на функции, так что запоминаем сразу (ниже упоминаний об этом не будет).
Но стоит помнить, что Вы никак не узнаете, используется переменная или нет, без ручного поиска кода, в котором используется эта переменная. Ибо, в случае с stock, при компилировании данная ошибка отображаться не будет:

warning 204: symbol is assigned a value that is never used: "%s"



Функции:

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


Создание stock:
Новый stock создаётся вне других функций (public/stock). То есть, точно так же, как и public, но без forward.

Пример создания stock:

stock StockName(arguments)
{
return 1;
}
Где:
StockName - название нашего stock
arguments - название аргументов, в которые будет помещена информация для обработки. Названия могут быть любыми, как и в случае с переменными. Про ограничение на количество этих аргументов в одном stock мне не известно. Если Вы об это что-то знаете, просьба отписаться с предоставлением каких-либо доказательств =) Но уж точно не менее 8 аргументов использовать можно (личный опыт :) )


Пример использования stock:
Допустим, у нас есть диалог, который мы используем в нескольких участках кода (одинаковый). Чтобы каждый раз не писать его, мы можем создать stock с ним

stock ShowDialog(id)
{
new string[1001];//Да, текст занимает 1001 ячейку. Точнее 1000 ячеек, но нужно же ещё выделить одну для нулевого символа =) И теперь представьте, что если такой диалог нужно вызывать в нескольких местах, каждый раз придется выделять 1001 ячейку. То есть, при создании двух таких переменных, Вы уже выделите 2002 ячейки, при создании трёх - 3003 и т.д. Согласитесь, нерационально это =) (Для самых внимательных: Да, можно создать глобальную переменную. Но зачем забивать мод кучей одинаковых строк, когда можно написать их 1 раз?)
format(string, sizeof(string), "Тут длинный текст, который будет занимать много места в коде. Да и для его хранения придется создавать каждый раз переменную, а это трата памяти. Зачем нам это? Лучше раз создать stock, в котором 1 раз объявить переменную, и уже работать с этим stock");
format(string, sizeof(string), "%sТут длинный текст, который будет занимать много места в коде. Да и для его хранения придется создавать каждый раз переменную, а это трата памяти. Зачем нам это? Лучше раз создать stock, в котором 1 раз объявить переменную, и уже работать с этим stock",string);
format(string, sizeof(string), "%sТут длинный текст, который будет занимать много места в коде. Да и для его хранения придется создавать каждый раз переменную, а это трата памяти. Зачем нам это? Лучше раз создать stock, в котором 1 раз объявить переменную, и уже работать с этим stock",string);
format(string, sizeof(string), "%sТут длинный текст, который будет занимать много места в коде. Да и для его хранения придется создавать каждый раз переменную, а это трата памяти. Зачем нам это? Лучше раз создать stock, в котором 1 раз объявить переменную, и уже работать с этим stock",string);
ShowPlayerDialog(id, 0, DIALOG_STYLE_MSGBOX, "Названия диалога", string, "Кнопа 1", "Кнопа 2");
return 1;
}
Примечание: Хоть для отображения диалога требуется аргумент "playerid", в stock мы указали имя аргумента "id". Поэтому в ShowPlayerDialog мы должны указать именно "id", а уже при вызове stock мы присвоим значение аргументу "id" значение аргумента "playerid".
Обратите внимание, что "id", при создании stock, я написал лишь для того, чтобы Вы поняли, что имя аргумента может быть любое. Вы можете сразу написать "playerid" и в ShowPlayerDialog использовать "playerid". А можете написать "Deimos_noob" и так же использовать в диалоге уже "Deimos_noob". Главное не забывайте менять название аргумента в нативных функциях (кликабельно) (http://wiki.sa-mp.com/wiki/Category:Scripting_Functions) внутри stock на такое, какое Вы указали при создании stock (примером является "id" в создании stock и "playerid" в ShowPlayerDialog). Но менять название нужно именно в тех аргументах, значение которых Вы будете "доставлять" в код, находящийся в stock, при вызове этого stock. В этом примере это был "palyerid" и ниже Вы это увидите

Ну и создадим команду для отображения этого диалога

if (strcmp("/showmydialogue", cmdtext, true, 10) == 0)
{
SendClientMessage(playerid,0xFF0000FF,"А вот и диалог");
ShowDialog(playerid);//Вот тут мы указали откуда именно брать значение для "id". В данном случае в "id" поместится значение ID игрока. Но это может быть и не ID, а та информация, которая вам нужна. Ниже я приведу примеры
return 1;
}
Ну и при коннекте игрока вызовем диалог

public OnPlayerConnect(playerid)
{
ShowDialog(playerid);
return 1;
}

А теперь попробуем создать команду для передачи денег от одного игрока к другому с помощью stock. Сначала создадим stock с тремя аргументами, в которые запишем ID обоих игроков и сумму передачи денег

stock PlayerMoneyToPlayer(player1, player2, money)//Опять же, имена аргументов выбираете только Вы. Я создаю такие только для того, чтобы Вы чётко видели что и где менять
{
new string[71];//57 символов занимает весь текст. Максимальная сумма денег на руках игрока не может превышать 11 символов, поэтому к 57 прибавляем 11 и выделяем 3 ячейки для хранения трёхзначного ID игрока (не видел онлайн в 1000 человек ещё, так что 3 символа хватит). 57 + 11 + 3 = 71
GivePlayerMoney(player1,-=money);//Отнимем сумму у игрока, который решил передать деньги
GivePlayerMoney(player2,+=money);//Выдадим деньги игроку, которому первый игрок решил передать деньги
format(string,sizeof(string),"Вы передали %d$ игроку с ID %d", money, player2);
SendClientMessage(player1,0xFFFFFFFF,string);//Оповестим первого игрока о успешной передаче денег
format(string,sizeof(string),"Вам передали %d$. ID игрока, передавшего вам деньги - %d", money, player2);
SendClientMessage(player2,0xFFFFFFFF,string);//Оповестим второго игрока о успешном получении денег
return 1;//Отсылаем серверу сигнал о том, что всё прошло успешно и можно продолжать выполнять код после stock.
}
И теперь создадим команду, в которой и присвоим нужные значения для аргументов stock
Этот код нужно вставлять в самое начало паблика OnPlayerCommandText, если его нет. Без него функция srtok работать не будет
new cmd[128], idx[128], tmp[128];//Создадим переменные, которые нам понадобятся для дальнейшей работы с функцией strtok
cmd = strtok(cmdtext, idx);//Приравняем нашу переменную к значению strtok и теперь, при создании команду, будем использовать не cmdtext, а cmd (иначе strtok работать не будет).

if (strcmp("/pay", cmd, true, 10) == 0)
{
tmp = strtok(cmdtext, idx);//strtok начнёт искать символы после пробела, которые игрок введёт при использовании команды
if(!strlen(tmp)) return SendClientMessage(playerid,0xFF0000AA,"Вы не ввели ID игрока");//Если игрок не написал ничего после первого пробела - оповестим его об этом и оборвём выполнение команды
new giveplayerid = strval(tmp);//strval преобразует введённые игроком данные в число. Если игрок введёт текст, strval выдаст значение 0. Запишем в giveplayerid ID игрока, которому надо передать деньги
if(playerid == giveplayerid) return SendClientMessage(playerid,0xFF0000AA,"Вы ввели свой ID. Себе деньги передать нельзя");//Если игрок ввёл свой ID, оповестим его об этом и не дадим перевести деньги (это делает return)
if(!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid,0xFF0000AA,"Данного игрока нет в сети");//Если игрок ввёл ID игрока, которого нет на сервере, оповестим его об этом и не дадим перевести деньги
tmp = strtok(cmdtext, idx);//Дадим сигнал моду для поиска ещё одного пробела
if(!strlen(tmp)) return SendClientMessage(playerid,0xFF0000AA,"Вы не ввели сумму для передачи");//Если игрок не написал ничего после второго пробела - оповестим его об этом и оборвём выполнение команды
new pmoney = strval(tmp);//Запишем сумму, которую игрок введёт после ввода IP
if(pmoney < GetPlayerMoney(playerid)) return SendClientMessage(playerid,0xFF0000AA,"У вас недостаточно денег на руках для передачи");//Если денег у игрока меньше чем он ввёл в команду, оповестим его об этом и прервём выполнение кода
//Теперь пора вызывать наш stock. После команды будет объяснение именно этого кода
PlayerMoneyToPlayer(playerid, giveplayerid, pmoney);//Передаём деньги с помощью stock
return 1;
}
"playerid" хранит значение ID игрока, который ввёл команду и передаст это значение аргументу "player1" из нашего stock
"giveplayerid" имеет значение ID игрока, которому надо передать деньги и передаст это значение аргументу "player2" из нашего stock
"pmoney" имеет значение суммы денег для передачи и передаст это значение аргументу "money" из нашего stock
Если кто-то не понял то, как я определил это всё, объясню по другому. Ниже будет предоставлен сам stock (таким, каким мы его создали) и способ вызова этого stock. Одинаковым цветом я выделю те аргументы, значения которых передаются от вызова stock к самому stock

stock PlayerMoneyToPlayer(player1, player2, money)
PlayerMoneyToPlayer(playerid, giveplayerid, pmoney);

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


В данном примере я покажу вам как передать определённый текст с помощью stock на примере команды личных сообщений. Данный stock будет содержать уже аж 5 аргументов, два из которых будут содержать ники игроков, два будут содержать ID и один будет содержать текст. Да, с помощью strok можно и такое :)

stock PrivateMessage(name1[], name2[], playerid, giveplayerid, text[])//name1[] и name2[] будут хранить ники, playerid и giveplayerid будут хранить ID игроков, а text[] - текст. Эти скобки - "[]" - означают, что в аргументе будет хранится текст. Все типы аргументов будут описаны ниже
{
new Message[169];//Создадим массив, в котором будет хранится текст
format(Message,sizeof(Message),">> %s[%d]: %s",name2,giveplayerid,text);//Оповестим первого игрока таким сообщением о том, что сообщение доставлено второму игроку.
//Заметьте, что тут мы уже используем name2 и text без скобок. Так же и с остальными типами аргументов (о которых расскажу ниже)
SendClientMessage(playerid,0xFF0000FF,Message);
format(Message,sizeof(Message),"<< %s[%d]: %s",name1,playerid,text);//Оповестим второго игрока таким сообщением о том, что первый игрок прислал ему сообщение
SendClientMessage(giveplayerid,0xFF0000FF,Message);
return 1;
}
Ну и теперь сама команда
Этот код нужно вставлять в самое начало паблика OnPlayerCommandText, если его нет. Без него функция srtok работать не будет
new cmd[128], idx[128], tmp[128];//Создадим переменные, которые нам понадобятся для дальнейшей работы с функцией strtok
cmd = strtok(cmdtext, idx);//Приравняем нашу переменную к значению strtok и теперь, при создании команду, будем использовать не cmdtext, а cmd (иначе strtok работать не будет).

if(strcmp(cmd, "/pm", true) == 0)
{
new PlayerName[1][MAX_PLAYER_NAME]);//Создадим двумерный массив для записи ников обоих игроков
tmp = strtok(cmdtext, idx);//strtok начнёт искать символы после пробела, которые игрок введёт при использовании команды
if(!strlen(tmp)) return SendClientMessage(playerid,0xFF0000AA,"Вы не ввели ID игрока");//Если игрок не написал ничего после первого пробела - оповестим его об этом и оборвём выполнение команды
new giveplayerid = strval(tmp);//strval преобразует введённые игроком данные в число. Если игрок введёт текст, strval выдаст значение 0. Запишем в giveplayerid ID игрока, которому надо передать деньги
GetPlayerName(playerid, PlayerName[0], MAX_PLAYER_NAME);//Запишем ник первого игрока
GetPlayerName(giveplayerid, PlayerName[1], MAX_PLAYER_NAME);//Запишем ник второго игрока
if(playerid == giveplayerid) return SendClientMessage(playerid,0xFF0000AA,"Вы ввели свой ID. Себе писать в личку нельзя");//Если игрок ввёл свой ID, оповестим его об этом и не дадим написать личное сообщение (это делает return)
if(!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid,0xFF0000AA,"Данного игрока нет в сети");//Если игрок ввёл ID игрока, которого нет на сервере, оповестим его об этом и не дадим перевести деньги
//Далее идёт код, который "ищет" введённый игроком текст
new length = strlen(cmdtext);
while ((idx < length) && (cmdtext[idx] <= ' '))
{
idx++;
}
new offset = idx;
new result[64];
while ((idx < length) && ((idx - offset) < (sizeof(result) - 1)))
{
result[idx - offset] = cmdtext[idx];
idx++;
}
result[idx - offset] = EOS;
//Поиск текста окончен
if(!strlen(result)) return SendClientMessage(playerid,0xFF0000AA,"Вы не ввели текст сообщения");//Если игрок не написал ничего - оповестим его об этом и оборвём выполнение команды
if(strlen(result)>128) return SendClientMessage(playerid,0xFF0000AA,"Максимальная длинна сообщения - 128 символов");//При написании stock, мы создали массив Message. Когда я рассчитывал число ячеек, которые мы выделим для записи текста, я предоставил для текста 128 ячеек. Поэтому сделаем ограничение, дабы текст отобразился корректно
//Всё, что нужно для формирования "вызова" stock, у нас уже имеется. Теперь делаем сам "вызов"
PrivateMessage(PlayerName[0], PlayerName[1], playerid, giveplayerid, (result));//Формируем и отправляем запрос
return 1;
}

Типы данных для аргументов:
У аргументов stock, как и аргументов public, есть типы данных. Эти типы точно такие же, как и у переменных. Всего 4 типа:


Вещественный (дробное число)

stock StockName(Float:arguments)
"Float:arguments"
Логический (bool. То есть true/false)

stock StockName(bool:arguments)
"bool:arguments"
Целочисленный (целые числа)

stock StockName(arguments)
"arguments"
Строковый (символы, то бишь текст)

stock StockName(arguments[])
"arguments[]"
Примечание: Повторюсь, все типы данных работают так же, как и у переменных. Следовательно, использовать их нужно без аргументов ("Float","[]"."bool:"). Пример имеется в примере 3 (извиняюсь за тавтологию).

Прочие виды аргументов:

Ссылка (&):
Данный вид возвращает значения аргумента, получившиеся после выполнения кода функции. (аналогично "return")

stock StockName(&arguments)
"&arguments"



stock Calculator(&n_1, &n_2)//Создадим новый stock, в котором оба аргумента будут являться ссылками
{
n_1 += 3;//К значению первого аргумента прибавим тройку
n_2 -= 17;//От значения второго аргумента отнимем семнадцать
return 1;
}//Выполнение кода окончено и все аргументы, созданные как ссылки, вернут свои значения в место вызова функции


main()
{
new num[2];
num[0] = 10;
num[1] = 20;
printf("\nДо вызова\nЗначение[0] = %d\nЗначение[1] = %d\n",num[0],num[1]);//До вызова стока значения переменных будут равны заданным ранее значениям
Calculator(num[0], num[1]);//До вызова переменные будут равны 10 и 20, а после выполнения их значения станут равны 13 и 3
printf("\nПосле вызова\nЗначение[0] = %d\nЗначение[1] = %d\n",num[0],num[1]);//После вызова значения будут уже другими
}






На этом всё =)
Если есть какие-либо вопросы, если что-то непонятно объяснено или есть какие-либо дополнения/исправления для данного урока, прошу написать об этом ниже. Всем постараюсь помочь, все мнения приму к сведению.
С вами был DeimoS. Спасибо за внимание

Автор урока - DeimoS
При копировании данного материала, обязательно указывайте автора и ссылку на данный урок

Zeror_Dalglish
27.06.2014, 22:48
Хотелось, бы ещё про паблики что то подобное...

Don1412
12.02.2016, 00:52
а вот и не все о sotck... хотелось бы увидеть примеры и описание создания функции с переменным количеством аргументов

DeimoS
12.02.2016, 01:04
а вот и не все о sotck... хотелось бы увидеть примеры и описание создания функции с переменным количеством аргументов

Эмм, а чем

forward FunctionName({Float,_}:...);
public FunctionName({Float,_}:...)
существенно отличается от

stock FunctionName({Float,_}:...)
Объявляется она так же. Работа с такой функцией идёт тоже так же.
Лучше уж тогда написать урок о функциях с переменным числом аргументов, нежели пытаться добавить сюда короткое описание, которое больше вопросов создаст

Don1412
12.02.2016, 01:13
Эмм, а чем

forward FunctionName({Float,_}:...);
public FunctionName({Float,_}:...)
существенно отличается от

stock FunctionName({Float,_}:...)
Объявляется она так же. Работа с такой функцией идёт тоже так же.
Лучше уж тогда написать урок о функциях с переменным числом аргументов, нежели пытаться добавить сюда короткое описание, которое больше вопросов создаст

ну почему бы так не сделать, я вот и ищу пример такой функции

DeimoS
12.02.2016, 01:40
Что именно тебя интересует?
Есть 3 функции:
numargs() - возвращает число аргументов
getarg(arg, index = 0) - возвращает значение аргумента (в случае с массивами придётся попотеть, ибо она может вернуть только значение одной ячейки => извлекать значение массива придётся циклом)
setarg(arg, index=0, value) - установка значения для аргумента
При объявлении функции в ней можно указать те теги которые применимы к аргументам:

stock FunctionName({/*Вот тут*/}:...)
Указываются через запятую. Стандартные тэги указываются так:
_ - Целочисленный тэг
Float - Вещественный тэг
bool - Логический тэг
Для строки/массива тэг указывать не нужно

Самый незамысловатый пример извлечения значения строки:

main()
{
SomeFunc("DeimoS");
}

stock SomeFunc({_}:...)
{
new string[10];
for(new i; ; i++)
{
if(getarg(0, i) == '\0') break;
string[i] = getarg(0, i);
}
printf("%s", string);
}

Что ещё хотелось бы узнать? Я просто не знаю из чего тут целый урок вытянуть можно

Don1412
12.02.2016, 01:50
Что именно тебя интересует?
Есть 3 функции:
numargs() - возвращает число аргументов
getarg(arg, index = 0) - возвращает значение аргумента (в случае с массивами придётся попотеть, ибо она может вернуть только значение одной ячейки => извлекать значение массива придётся циклом)
setarg(arg, index=0, value) - установка значения для аргумента
При объявлении функции в ней можно указать те теги которые применимы к аргументам:

stock FunctionName({/*Вот тут*/}:...)
Указываются через запятую. Стандартные тэги указываются так:
String - строка/массив
_ - Целочисленный тэг
Float - Вещественный тэг
bool - Логический тэг

Самый незамысловатый пример извлечения значения строки:

main()
{
SomeFunc("DeimoS");
}

stock SomeFunc({String}:...)
{
new string[10];
for(new i; ; i++)
{
if(getarg(0, i) == '\0') break;
string[i] = getarg(0, i);
}
printf("%s", string);
}

Что ещё хотелось бы узнать? Я просто не знаю из чего тут целый урок вытянуть можно

отлично, большое спасибо, довольно толковый форум, нафиг всякие павно-инфо галимые)

Don1412
12.02.2016, 02:21
stock SetControlFraction(playerid, {_}:...)
{
new Array[23];
for(new i = 0; i < numargs(); i++)
{
Array[i] = getarg(i);
ControlDialog[playerid][i] = Array[i];
SendMe(playerid, -1, ControlDialog[playerid][i]);
}
return true;
}
SetControlFraction(playerid, 1, 10, 21);
что не так?
нулевой аргумент(playerid) выводит норм, остальные пустые строки...

DeimoS
12.02.2016, 02:27
for(new i = 0; i < numargs(); i++)
на

for(new i = 1; i < numargs(); i++)
Ведь нулевой аргумент является известным
Я бы винил функцию SendMe, ибо весь остальной код выглядит работоспособным

Don1412
12.02.2016, 02:40
for(new i = 0; i < numargs(); i++)
на

for(new i = 1; i < numargs(); i++)
Ведь нулевой аргумент является известным
Я бы винил функцию SendMe, ибо весь остальной код выглядит работоспособным
SendClientMessage(playerid, -1, ControlDialog[playerid][i]);
ничего не изменилось...

DeimoS
12.02.2016, 02:44
SendClientMessage(playerid, -1, ControlDialog[playerid][i]);
ничего не изменилось...

А теперь попробуй так:

stock SetControlFraction(playerid, {_}:...)
{
new Array[23];
printf("numargs() = %d", numargs());
for(new i = 1; i < numargs(); i++)
{
ControlDialog[playerid][i] = getarg(i, 0);
printf("ControlDialog[%i][%i] = %d", playerid, i, ControlDialog[playerid][i]);
}
return true;
}
И покажи как у тебя создаётся ControlDialog
P.S. Лучше бы создал тему в разделе для вопросов.

Don1412
12.02.2016, 02:49
А теперь попробуй так:

stock SetControlFraction(playerid, {_}:...)
{
new Array[23];
printf("numargs() = %d", numargs());
for(new i = 1; i < numargs(); i++)
{
ControlDialog[playerid][i] = getarg(i, 0);
printf("ControlDialog[%i][%i] = %d", playerid, i, ControlDialog[playerid][i]);
}
return true;
}
И покажи как у тебя создаётся ControlDialog
P.S. Лучше бы создал тему в разделе для вопросов.

я уже сам вывел
format(string, sizeof(string), "lol = %d %d %d", getarg(i), Array[i], ControlDialog[playerid][i]);
SendClientMessage(playerid, -1, string);
забавно, что таким методом выводит нулевой аргумент(playerid) криво, а все остальное норм) ладно, спасибо, дальше сам разберусь

yellow
23.12.2022, 09:29
как можно создать функцию, чтобы был необязательный параметр ? что указать в стоке


stock MyFunction(const params[], const options[] = EOS)
{
??????????
}

Pa4enka
23.12.2022, 12:58
как можно создать функцию, чтобы был необязательный параметр ? что указать в стоке


stock MyFunction(const params[], const options[] = EOS)
{
??????????
}


stock SomeFunc(playerid = INVALID_PLAYER_ID, player_name[] = "", Float: value = 0.0)
{
printf("playerid = %d, name = %s, value = %f", playerid, player_name, value);
return true;
}

main()
{
SomeFunc(24, "Levante", 15.2); // debug: playerid = 24, player_name = "Levante", value = 15.2
SomeFunc(); // debug: playerid = 65535, player_name = "", value = 0.0
}