Разработчик
Проверенный
Участник
Пользователь
- Сообщения
- 207
- Реакции
- 420
- Помог
- 10 раз(а)
Введение в программирование на SourcePawn. Функции, их типы.
Из коробки, SourceMod предоставляет нам довольно мощную библиотеку функций. Мы можем работать с базами данных, файлами, другими плагинами, и многое другое. Все эти функции Вы можете увидеть в официальном API. Мы обязательно рассмотрим некоторые "библиотеки".
Помимо официальной библиотеки, мы можем создавать свои функции, или пользоваться функциями других плагинов. Свои функции обычно используются для избежания повторяемого кода, а функции других плагинов - для интеграции. К примеру, мы можем добавить свой пункт в официальное меню админки SourceMod. К этому всему мы обязательно вернёмся ещё. Сейчас мы рассмотрим возможность создавать свои функции.
Выделяются четыре возможных типа функций:
- private - приватные функции. Обычно при объявлении таких функций просто пишется возвращаемый тип данных и её название.
Код:void DoSomeAction() { // code... }
- public - публичная функция. В отличие от приватных, её можно вызвать извне, зная только её название. Для этого используется функция GetFunctionByName(), которая возвращает уже указатель на функцию.
Ещё компилятор не "жалуется" на неиспользуемые получаемые переменные в этом типе функций.
Код:public void DoSomeAction() { // code... }
- stock - тоже приватная функция, но если не используется - не включается компилятором в конечный "бинарник".
Код:stock void DoSomeAction() { // code... }
- static - ещё одна разновидность приватной функции. Я редко видел, чтобы этот тип вообще применяли, но в целом он даёт нам возможность запретить её вызывать сторонним файлам, которые подключают файл с этой функцией.
Код:// some_include.inc static void DoSomeAction() { // code... } // file1.sp #include <some_include> public void OnPluginStart() { DoSomeAction(); // сгенерирует ошибку, что функция "не существует" }
Все эти типы можно так же применять на переменных.
Код:
int g_iSomeVar; // приватная переменная
public int g_iSomeVar; // публичная переменная (расширения и ядро SourceMod могут её редактировать без адреса, зная только её имя)
stock int g_iSomeVar;
static int g_iSomeVar;
- Сначала определяем тип функции. Если её будет вызывать в последствии само ядро SM (например, функция будет служить каллбеком для команды) или сторонние плагины, делаем тип public. В противном случае - private/stock.
- Указываем тип возвращаемых данных. Если функция ничего не возвращает - пишем void (пустота).
- В скобках перечисляем типы и названия аргументов, которые должна принимать функция. Если функция ничего на вход не принимает - оставляем скобки пустыми.
- Открываем тело функции, которое является блоком (см. раздел 3).
- Пишем код.
- Если функция должна что-то возвращать - используем return в нужных местах.
Пример простейшей функции. Ищет игрока указанной команды, и возвращает первого попавшегося. На вход принимает номер команды и "бул"-переменную, должен ли быть игрок живым. Если поиск не удался, возвращает 0.
Код:
stock int GetClientFromTeam(int iTeam, bool bShouldIsPlayerAlive = false) {
for (int i = MaxClients; i != 0; --i)
if (IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == iTeam && (!bShouldIsPlayerAlive || IsPlayerAlive(i)))
return i;
return 0;
}
"Ссылки" на переменные
Пожалуй, стоит рассмотреть ещё одну очень удобную штуку. Когда мы передаём некоторые переменные, виртуальная машина выполняет копирование значений. То есть, в вызванной функции мы можем их редактировать, но их значения из места, откуда функция вызвана, отредактированы не будут.
Исключения составляют массивы (строки - массивы символов). Массивы сразу передаются указателями, потому виртуальная машина никогда не знает точного размера оных (это, кстати, та самая причина, по которой функции, работающие с массивами, требуют указывать их размеры).
Это очень удобно, если не хочется плодить тысячи переменных, и сэкономить памяти (которой, вообще-то, не так уж и много):
Код:
public void OnCheckClientTimer(Handle hTimer, int iClient) {
if ((iClient = GetClientFromUserId(iClient)) == 0)
return;
// some logic here...
}
Записывается это в объявлении переменных с помощью знака "амперсанда" (
&
). На старом синтаксисе - перед типом переменной, а на новом - перед самим именем:
Код:
// старый синтаксис
public GetData(iClient, &Float:flConnectionTime, &iConnectedFrom) { /** ... */ }
// новый синтаксис
public void GetData(int iClient, float &flConnectionTime, int &iConnectedFrom) { /** ... */ }
- Получение значения из вызванной функции не требуется
- Handle (он сам по себе уже указатель)
- Функция и так возвращает нужное значение через оператор
return
Когда обычно применяются "ссылочные переменные"? Чаще всего, в событиях, на которые можно как-то повлиять:
OnTakeDamage
, например. Мы можем отредактировать урон или игрока, который его нанёс. И не только.Ещё видел такое использование в официальной "библиотеке" SM:
Код:
/**
* Retrieves a value in a Map.
*
* @param map Map Handle.
* @param key Key string.
* @param value Variable to store value.
* @return True on success. False if the key is not set, or the key is set
* as an array or string (not a value).
* @error Invalid Handle.
*/
native bool GetTrieValue(Handle map, const char[] key, any &value);
Локальные статические переменные
Хотя это частично и не относится к теме этой главы, тоже интересно. Как правильно отметил ниже в комментариях wopox1337, в SourcePawn так же есть "локальные статические" переменные, и работают они как глобальные частично.
Для этого "подраздела" воспользуемся примером кода с локальными и глобальными переменными из главы 3:
Код:
int g_iData;
public void OnPluginStart() {
g_iData++;
DoLogic();
g_iData++;
DoLogic();
}
void DoLogic() {
int iData;
iData++;
PrintToServer("%d %d", g_iData, iData);
}
Если мы к локальной переменной
iData
добавим "кейворд" static
перед типом переменной, мы получим локальную переменную, значение которой не обнуляется при завершении функции.
Код:
void DoLogic() {
static int iData;
iData++;
PrintToServer("%d %d", g_iData, iData);
}
Код:
1 1
2 2
Последнее редактирование: