[Программирование в Pawn] Урок 1. Коротко о главном!
https://cdn.dribbble.com/users/872671/screenshots/2751155/linechessset2.gif

Pawn - простой язык программирования с открытым исходным кодом. Сегодня мы начинаем серию гайдов по программированию на языке Pawn. Я считаю, что в данный момент появилось очень много новичков мало чего понимающих в программировании и постоянно задающие глупые вопросы. Именно для таких людей, я и решил написать гайды.

Не будем рассказывать о "вступлении" в программировании, о работе процессора, процесса компиляции и интерпретации ( or трансляции ) программы, а сразу попытаемся практично подойти к понятию программировании в Pawn
P.S
Если вы желаете больше влиться в мир программ, советую изучить следующие темы:
"Как работает процессор, регистры, архитектура пк",
"Языки программирования, что такое трансляция и компиляция программы",
"Вступление в С++, первые 8-14 уроков до темы "Классы"

Ну что же, давайте разбирать простейший плагин написанный на Pawn для CS 1.6 ( я решил взять броню, ибо считаю здесь есть всё для простого описания )
#include <amxmodx>
#include <fakemeta>
#include <zombieplague>

#define MAX_IN_ROUND 4 // Максимум в раунде

const g_item_cost = 15 // Цена

new const g_sound_buyarmor[] = { "items/tr_kevlar.wav" } // присваиваем константе массиву значение ( строку ) путь до звука

const g_armor_amount = 250 // Покупка за 1 раз
const g_armor_limit = 250 // Лимит
new g_armor_counter[33] = 0 // все 32 значения массива ( счёт начинается с 0 ) будут при компиляции/старта плагина равны 0
new extra_armor[512] // объявление массива

new g_itemid_humanarmor // объявление переменной


public plugin_precache()
{
    precache_sound(g_sound_buyarmor) // кэшируем звук покупки
}


public plugin_init()
{
    register_plugin("[ZP] Extra-Item: Anti-Infection Armor", "1", "Dambas") // регистрируем плагин
    register_event("ResetHUD","Start_round","be")                            // регистрируем событие смены худов CS
    formatex(extra_armor, charsmax(extra_armor), "%L", LANG_SERVER, "EXTRA_ARMOR"); // в массив extra_armor засовываем значение из lang файла которое равно EXTRA_ARMOR
    g_itemid_humanarmor = zp_register_extra_item(extra_armor , g_item_cost, ZP_TEAM_HUMAN) // присваиваем переменной возвращаемое значение функции
    register_dictionary("zombie_plague.txt");                                      // регистрируем lang файл
}


public Start_round(id)
{
    g_armor_counter[id] = 0 // обнуляем лимит покупок у всех игроков в начале раунда
}

public zp_extra_item_selected(id, itemid) // описываем "форвард" для нашего плагина
{
    if (itemid == g_itemid_humanarmor) // проверка на то, что если мы выбрали предмет из нашего плагина
    {
        if (g_armor_counter[id] > MAX_IN_ROUND) // если лимит покупок у игрока не выше того, что мы указали в MAX_IN_ROUND продолжаем идти дальше
        {
            ColorChat(id, "!g[ZP] !yТы купил максимум брони!" ) // вызываем функцию которая отобразит сообщение игроку
            return ZP_PLUGIN_HANDLED // прекращаем данную функцию + возвращаем деньги игроку
        }
        else
        {
            set_pev(id, pev_armorvalue, float(min(pev(id, pev_armorvalue)+g_armor_amount, g_armor_limit))
            // Мы выберем минимальное ( min() ) значение из [b]значений броня игрока+количетсво покупаемой брони[/b] pev(id, pev_armorvalue)+g_armor_amount и [b]максимальное количество брони[/b]
[b]            // То есть, у игрока не может быть брони больше, чем то, что мы указали в g_armor_limit[/b]
            engfunc(EngFunc_EmitSound, id, CHAN_BODY, g_sound_buyarmor, 1.0, ATTN_NORM, 0, PITCH_NORM) // воспроизводим звук указанный в массиве g_sound_buyarmor
            g_armor_counter[id]++                               // уменьшаем для игрока [id] его лимит
            ColorChat(id, "!g[ZP] !yТы купил !g250 брони!" )    // вызываем функцию для вывода сообщению игрока
        }
    }
    return PLUGIN_HANDLED // заканчиваем работу функции
}

stock ColorChat(const id, const input[], any:...) // тут много сложного для новичков
{
        new count = 1, players[32] // создаем переменную и массив для игроков
        static msg[191]  // создаём статичную переменную, чтобы её значение не удалялось после окончания функции 
        vformat(msg, 190, input, 3) // из массива с любым размером в массив msg с размером 191
       
        replace_all(msg, 190, "!g", "^4") // меняем все !g на ^4
        replace_all(msg, 190, "!y", "^1") // меняем все !y на ^1
        replace_all(msg, 190, "!t", "^3") // меняем все !t на ^3
       
        if (id) players[0] = id; else get_players(players, count, "ch") // если id не 0, то в в ячейку массива players[0] кидаем id игрока ( для будущего вывода сообщения именно ему ), ИНАЧЕ получаем id всех игроков для будущего вывода всем игрокам
        {
                for(new i = 0; i < count; i++) // цикл от 0 до количество игроков 
                {
                        if(is_user_connected(players[i])) // проверяем на коннект игрока ( антикраш )
                        {
                                message_begin(MSG_ONE_UNRELIABLE, get_user_msgid("SayText"), _, players[i]) // запускаем сообщение, с параметром вывода текста
                                write_byte(players[i]) // кому выводим
                                write_string(msg) // что выводим
                                message_end() // заканчиваем сообщение
                        }
                }
        }
}

В любой программе есть 3 блока:
1. Блок подключения заголовочных файлов - в ней подключаются все библиотеки ( библиотека - набор готовых функций движка CS ), для разработки плагина
2. Блок описания функции int main() - так как в Pawn, описание данной функции отсутствует, будем считать этой функцией public plugin_int(). Эта функции является функцией с точкой входа. Когда вы запускаете свой сервер, именно данная функция вызовется первой.
3. Блок описания функций, стоков и событий - блок, в котором вы описываете собственные функции и функции событий которые вы описали в public plugin_init()

Переменная.
В любой программе можно объявлять переменные. Они могут быть глобальными, и локальными. Разница в том, что к глобальным переменным можно обращаться из любой части кода, а к локальным только в том, где они объявлены.
Так же, у любой переменной, есть свои свойства, а именно:
  • тип данных
  • значение
  • адрес
  • имя
В Pawn, для объявления переменной общего ( безтипного ) типа, используется структура: new ИмяПеременной;
Чтобы при объявлении сразу присвоить значения, используется оператор "=": new ИмяПеременной = 10;
Чтобы принудительно задать тип переменной, нужно использовать структуру: new ТипДанных:ИмяПеременной.
Список возможных типов переменной:
  • Integer - целочисленный
  • Float - дробный ( пример: 0.045 )
  • Bool - логический ( либо true, либо false ВАЖНО: Не 0, или 1 )
  • Array - массив
Так же есть вид переменных которые не могут менять своё значение во время кода, и присваиваются при объявлении. Такие переменные называются константами. Структура объявления: new const ИмяПеременной = 5; Значение таких переменных нельзя изменить!

P.S
Нельзя забывать про так называемые "инструкции". Через них тоже можно объявлять переменные которые можно использовать в коде. Они схожи с константами, но их основное отличие, что во время компиляции они лишь заменяют своё имя, на их значение в коде
Структура объявления: #define ИМЯ_ПЕРЕМЕННОЙ ЗНАЧЕНИЕ 
#define MYNAME Player


Массивы.
Следующая структура, которую нужно понимать для программирования в Pawn. Массив, грубо говоря, строка из переменных, у которых есть свой адрес и значение.
Для объявления массива, используются структура: new ИмяМассива[РазмерМассива];
В любом языке программирования, так же можно объявить двумерный массив, это, опять же грубо говоря, таблица, в которой в каждой ячейке массива - хранится адрес другого массива. Общую структуру такого массива можно представить как таблицу
new TutMassiv[3][3];

for(new i=0; i < 3; i++)
for(new k=0; k < 3; i++)
TutMassiv[i][k] = i;
TutMassiv[0][0]=0
TutMassiv[1][0]=0
TutMassiv[2][0]=0
TutMassiv[0][1]=1
TutMassiv[1][1]=1
TutMassiv[2][1]=1
TutMassiv[0][2]=2
TutMassiv[1][2]=2
TutMassiv[2][2]=2
Чтобы задать значения массива сразу при объявлении, используется оператор "=" и фигурные скобки:
new tut[3] = { 5, 4, 10 }; - одномерный массив
new tutD[2][2] = { { 3, 2 }, { 4, 6 } }; - двумерный массив ( таблица )
new tutT[3][3][3] = 
{
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  },
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  },
  {
    {1, 2, 3}, {4, 5, 6}, {7, 8, 9}
  }
}; - трёхмерный массив ( пространство )

Функции.
Еще одна важнейшая часть для осознания Pawn'а. Функции нужны для того, чтобы не повторять много раз одни и те-же куски кода, а отделить их отдельным именем и вызывать при необходимости.
Например, вам нужно при убийстве игрока, давать ему броню, здоровье, уровень и еще много много бонусов. Чтобы не загрязнять код, лучше вызывать одну функцию в событии убийства.
Все функции в Pawn должны начинаться со слова public. Общая структура написания функций: public ИмяФункции(Атрибут, Атрибут2, Атрибут3, ..., АтрибутN) { описании функции )
Чтобы вызвать функцию, нужно просто написать её имя, и отослать туда какие либо данные - атрибуты. Имена атрибутов, могут не совпадать с именами переменных которые вы туда отправляете ( НО! Типы должны обязательно совпадать, так как вы не можете присвоить типу bool тип Integer, это вызовет Warning 213 при компиляции )
public NameFunction(id, Health, Armor) // Описание функции
{
 set_user_health(id, Health);
 set_user_armor(id, Armor);
}


// code
NameFunction(id, 300, 300); // Вызов функции
// code

Циклы.
Нужны для автоматического повтора каких либо действий при совпадении определенного условия.
Существует 3 вида циклов:
  • for(идентификация счетчика; условие; инкремент ( прибавление значение счетчика ))
  • while(условие)
  • do{ реализация кода }while(условие)
Чтобы сделать бесконечный цикл, можно использовать конструкцию while(true) -> чтобы выйти из него, используйте операцию break
break - останавливает выполнения цикла
continue - переходит на следующий шаг цикла

Как это работает
  1. Цикл For
    for(new i=0; i < 5; i++) { Тело Цикла }
    Сначала мы объявляем локальную переменную i и присваиваем ей значение 0. Эта локальная переменная будет использоваться только внутри тела цикла. Можно использовать и не локальные переменные написав её внутри for без оператора new и присваивания.
    После инициализации счетчика, мы проверяем условие. Если i < 5, начинаем выполнять операции находящиеся внутри тела цикла. После последней операции цикла, мы добавляем к существующему значению i единицу ( т.е. i = i + 1 ). Это и описано в инкременте счетчика(..; i++)
    После чего снова идёт условие.
  2. Цикл while(i<5) { Тело Цикла }
    Здесь всё так же, только нам придётся внутри тела цикла самому увеличивать значение i. Цикл будет работать, пока условие в скобках после while будет выполняться
  3. Цикл do{ Тело Цикла }while(условие)
    Этот цикл, отличается от других тем, что сначала мы выполняем операции в теле цикла, а только потом проверяем условия, и если оно верно, то снова выполняем операции до момента пока условие не сработает.

Операция Ветвления - if(){} else if(){} else{}
Операции ветвления, или условные операции, самое частое и нужное в программировании. Они нужны для проверки какого либо условия и дальнейшего выполнения или изменения условия.
Обязательная структура:
if(условие)         // ЕСЛИ ...
{
    Операции
}
else if(условие)   // ИНАЧЕ ЕСЛИ...
{
   
}
..... // здесь может быть сколько угодно структур else if(условие)
else if(условие)
{
Операции
}
else         // ИНАЧЕ .... - выполняется, если не одно из else if не выполнилось
{
Операции
}
Если не одна операция ветвления не совпала с условием, и операции else нету, то ВСЯ операция ветвления просто игнорируется и код идёт дальше 
Функция public plugin_init(), и посмотрим, что делает каждая строчка
public plugin_init() // Функция которая вызывается при компиляции плагина или запуска сервера
{
    register_plugin("[ZP] Extra-Item: Anti-Infection Armor", "1", "Dambas")            // Регистрация плагина, и присваивание ему имени
    register_event("ResetHUD","Start_round","be")                                      // Регистрация события, и присваивания ему в данном плагине функции ( в данном случае ResetHUD )
    formatex(extra_armor, charsmax(extra_armor), "%L", LANG_SERVER, "EXTRA_ARMOR");    // formatex - функция работы со строкой, далее мы рассмотрим её поближе
    g_itemid_humanarmor = zp_register_extra_item(extra_armor , g_item_cost, ZP_TEAM_HUMAN) // Переменная как значение функции. Далее мы рассмотрим зачем это нужно
    register_dictionary("zombie_plague.txt");                                            // Подключение lang-файла для русификации плагина
}

Я собрал все нужные для новичка события для создания функций их реализации:
  • register_event - регистрирует некоторые из стандартных событий Half-Life. Полный список тут - События Half-Life
  • register_logevent - регистрирует события которые обычно пишутся в логах. Нам нужны чаще всего для начала и конца раунда
  • RegisterHam - не побоюсь для новичков сказать, что это нужно для того, чтобы отловить какое то событие. Но функционал намного выше. Нам чаще всего нужно для событий получение урона, смерти, спавна и других. Удобность - наличие id игрока
  • register_touch - если углубитесь в тему Pawn'а, то эта функция поможет вам отловить событие касание объектов друг об друга

Функция plugin_precache(), нужна для кэширования ваших ресурсов ( модели, звуки, спрайты ) на сервере и в дальнейшем для передачи их игроку.
Внутри данной функции можно использовать несколько функций кэширования ( разных библиотек )
Fakemeta: engfunc(имя константы fakemeta, имя кэшируемой переменной)
  • Константа EngFunc_PrecacheModel - для кэширования модели или спрайта. Пример: engfunc(EngFunc_PrecacheModel, Model)
  • Константа EngFunc_PrecacheSound - для кэширования звука. Пример engfunc(EngFunc_PrecacheSound, Sound)
Amxmodx: precache_(имя кэшируемой переменной) - Аналогично, но без использования константы
  • precache_model
  • precache_sound

FAQ: ( ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ НОВИЧКОВ )
  • С чего начать писать плагин, на что смотреть, что делать если я не понимаю как работает код

    &Я не понимаю как работает код&

    Для начала, посмотрите на название переменных и событиях описанных в public plugin_init()
    Поймите структуру программы, какие функции вызываются первыми, какие используются чаще, какие важные. По смыслу вы сможете понять, в каком месте кода вы должны искать.
    Например: Вопрос: Мне нужно в определенном плагине, изменить радиус взрыва ракеты.
    Для начала, мне нужно найти функцию, которая отвечает зазапуск самой ракеты. Потом найти, функцию которая отвечает за взрыв ракеты. После чего найти структуру message_begin, ведь скорее всего именно в ней указывается радиус взрыва

    &С чего начать писать плагин&

    Продумайте в голове все свои мысли. Изобразите структуру вашего плагина на листочке или в Paint ( если вы собрались браться за что то большое ). Потом обдумайте, какие библиотеки ваш лучше использовать. Если вы намереваетесь писать плагин связанный как либо внутри-игровыми объектами: задумайтесь что будет лучше, использовать модуль engine или fakemeta (изучите их для начала). Подумайте, как будут использоваться переменные, какие функции вы распишите, какие события затронете.


  • Чем отличается (id) от [id]
    Всё, что находится в круглых скобках - это вызов функции. Квадратные скобки - обращение к ячейки массива с идентификатором указанным внутри []

    То есть, set_user_health(id, Health) -> мы отправляем в функции число которое постоянно хранится в id
    А users[id] -> мы обращаемся к массиву users по номеру который записан в id
    Ни в коем случае не путайте!


  • Как исправлять Warning'и
    Начнем с того, что ошибки(error) и предупреждения(warning) - это разные вещи. Если в вашем плагине ошибка - он не скомпилируется. Если предупреждения - скомпилируются, но компилятор вас предупредит, что в каком то месте допущена синтаксическая ошибка, которая может фатально или не очень повлиять на работу сервера.
    Всё зависит от самого Warning'а. Обычно, переведя его содержания и посмотрев на строчку которая его вызвала - вы сами всё поймете.
    Но не со следующими:
    Warning 217, Warning 213 - иногда новички конкретно зависают над этими предупреждениями.

    Ну что же, давайте рассмотрим возможные варианты их появления


  • %Warning 213%
    Тут может быть несколько вариантов. Чаще всего, предупреждение о не соответствии тэгов появляется когда функция принимает переменную одного типа, а вы отправляете другой
    К примеру. Когда вы работаете с уроном, вы должны понимать, что любой урон в игре имеет тип Float. Поэтому работая с ним, вы либо при вызове функций должны сами присваивать ему тип Float, либо везде держать его в дробном состоянии

    Нашел хороший пример:
    set_pev(iPlayer, pev_velocity, Float:{0.0,0.0,0.0})
    В данной функции при её вызове, вы сами должны указать тип Float, чтобы не столкнуться с проблемой 213 предупреждения.


  • &Warning 217&
    Тут 2 варианта. Либо у вас не выровнены отступы ( где то Табулирование, где то лишние пробелы ), либо неправильная структура кода
    К неправильной структуре я отношу забытые фигурные скобки
    Запомните, в функциях ветвления и циклах, не использовать фигурные скобки можно только в ситуации, когда в теле цикла или ветвления используется только одна операция, то есть
    // Правильно
    if(X>5)
       a = 3;
    else
       a = 5;;
    // Неправильно
    if(X>5)
       a = 3;
       b = 6;
    else
       a = 5;

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

Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.


Основа для вашей сборки
ReHLDS 3.13.0.788

ReHLDS (Reverse-engineered) - это новый шаг вперед, который дает второе дыхание нашим серверам. ReHLDS работает в 2 раза быстрей, чем HLDS.

AmxModx 5.2.9.4

AMXModX - это Metamod дополнение, которое позволяет создавать новые модификации для Half-Life на языке Pawn

Reunion 0.1.0.137

Reunion является продолжением Dproto для ReHLDS. Это metamod плагин, который позволяет заходить 47/48 Non-Steam на сервер.

Revoice 0.1.0.34

Revoice - это Metamod plugin, который дает возможность общения голосовым чатом между non-steam и steam клиентами.

Metamod-r 1.3.0.131

Новый Metamod-r содержит огромное количество оптимизаций производительности и намного более чистый код. Ядро было написано с использованием JIT-компилятора.

Ultimate Unprecacher 1.1

Ultimate Unprecacher являет плагином для MetaMod, работает он по принципу отключение не нужных ресурсов на вашем сервере, тем самым вы сможете освободить места для ресурсов под ваши плагины, с помощью данного модуля можно избавиться от ошибки 512!

ReAuthCheck 0.1.6

ReAuthCheck - это Metamod плагин, который занимается проверкой ваших игроков на валидность, с помощью данного модуля для REHLDS вы сможете защитить свой сервер от ботов, которые постоянно спамят рекламу или просто забивают слот на сервере!

NetBufExtender (NBEX) 1.0

NetBufExtender или NBEX - это метамод-плагин, который расширяет "интернет-буфер": буферы сервера и клиента(гарантия не 100%). Расширяет до 64 кб. Это значит, что у игроков уменьшается вероятность быть кикнутыми с ошибкой "Reliable channel overflowed".

UserInfoNetOptimizer (UINO) 1.0

UINO — metamod-плагин, который позволяет удалять ненужные поля из userinfo(setinfo) когда движок передаёт его другим игрокам на сервере. Данная мера уменьшает объём передаваемых данных и немного сокращает шанс быть кикнутым с "Reliable channel overflowed".

Информация

Добро пожаловать на CS-WZ.COM!

Для того чтобы стать полноценным пользователем нашего портала, вам необходимо пройти регистрацию.
Зарегистрироваться
Создайте собственную учетную запись!

Пройти регистрацию
Авторизоваться
Уже зарегистрированны? А ну-ка живо авторизуйтесь!

Войти на сайт