Мониторинг и Раскрутка серверов CS 1.6

[Программирование в Pawn] Урок 1. Коротко о главном!

[Программирование в Pawn] Урок 1. Коротко о главном! для CS 1.6

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

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

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

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(EngFuncPrecacheSound, 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;


Теги:

Программирование в Pawn Урок 1 Коротко о главном CS 1.6 cs-fade amx pawn

Опубликовал
Сборки CS 1.6
Рейтинг
Просмотры
237 раз.
Скачан
0 раз.

Отзывы