Русское сообщество по скриптингу

Изучение основ скриптинга под AMX Mod X

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Изучение основ скриптинга под AMX Mod X

Сообщение Subb98 » 26 май 2016, 12:57

ВНИМАНИЕ! Публикация данного материала на сторонних ресурсах возможна только по предварительному согласованию с автором!

Автор: Subb98

Введение:
В этой статье я расскажу об одном из методов, который поможет вам понять принцип написания скриптов для AMX Mod X, принцип работы языка Pawn. Когда-то по данной методике учился и я сам.

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

Приступим. Как и в любом деле, чтобы к чему-либо приступить, сперва нужно определить цель. Цель - понять принцип работы языка Pawn, на котором и пишутся скрипты для AMX Mod X. Необходимо отметить, что это так называемая "конечная" цель, на пути к ней будут также и "промежуточные" цели, которые также необходимо будет определять, хотя на некоторых из них мы не будем акцентировать внимание. Определение последовательности действий для достижения конечной цели также является алгоритмом.

Итак, цель определена. Для её достижения мы можем пойти разными путями, но вне зависимости от выбранного пути, в нём должны присутствовать две вещи, без которых достижение конечной цели не представляется возможным:

  1. Теория: её изучение, понимание
  2. Практика: закрепление теории в скриптах, наглядное видение работы языка
Только что мы определили две промежуточные цели. Поговорим более подробно о первой промежуточной цели, то есть, о теории.

Теоретическая часть:
Итак, изучать теорию такого языка программирования, как Pawn, можно по-разному.

Можно открыть Вы должны зарегистрироваться, чтобы видеть ссылки. на английском языке, и долго и кропотливо в нём разбираться. Такой способ изучения требует огромной усидчивости и явно не подходит для начинающих скриптеров, особенно для тех, кто не слишком дружен с английским языком. Но и это не основные причины для того, чтобы на начальном этапе не рассматривать такой метод изучения теории: скрипты для AMX Mod X пишутся не на "чистом" Pawn'е, а с использованием специфики AMX Mod X. То есть, даже если вы найдёте в себе волю прочесть вышеупомянутое руководство от начальной до конечной страницы, то всё равно писать скрипты для AMX Mod X, увы, не сможете. А всё потому, что AMX Mod X, как и многие другие модули, имеет свои функции, свой синтаксис, Вы должны зарегистрироваться, чтобы видеть ссылки., одним словом, использовать который вам и придётся в конечном итоге, чтобы писать собственные скрипты. Однако не стоит "списывать со счетов" это руководство полностью. В нём вы сможете найти ответы на вопросы, которые касаются специфики языка Pawn, а такие вопросы у вас могут возникнуть в дальнейшем. Этот материал стоит "взять на заметку".

Можно открыть тему Вы должны зарегистрироваться, чтобы видеть ссылки. на официальном форуме AMX Mod X, в ней собраны полезные учебные материалы, гайды и ссылки. Из основных недостатков для начинающих скриптеров, не считая языкового барьера, следует отметить, что всё находится "в одной куче": темы как для новичков, так и для опытных скриптеров. Начинающим может быть непросто сориентироваться, найти нужную для себя информацию. На нашем форуме вы сможете найти некоторые оригинальные статьи, переведённые на русский язык, например, Вы должны зарегистрироваться, чтобы видеть ссылки. (она же Вы должны зарегистрироваться, чтобы видеть ссылки.) или Вы должны зарегистрироваться, чтобы видеть ссылки. (она же Вы должны зарегистрироваться, чтобы видеть ссылки.). Однако "критический" недостаток данного способа изучения теории на начальном этапе заключается не в трудности поиска нужной информации и не в языковом барьере, который можно не учитывать, принимая во внимание наличие статей на русском языке.

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

Что же, всё-таки, стоит прочесть в самую первую очередь? Есть многим известный сайт товарища Вы должны зарегистрироваться, чтобы видеть ссылки.'а, Вы должны зарегистрироваться, чтобы видеть ссылки., где есть такой раздел, как "Вы должны зарегистрироваться, чтобы видеть ссылки.". В данном разделе также есть материал как для начинающих скриптеров, так и для опытных. Ниже перечислены те статьи, которые следует обязательно и внимательно изучить всем без исключения новичкам, поскольку это - основы основ (если, конечно, не считать стандарты языка, но я уверен, что ни один новичок не станет их изучать: это сложно, муторно и отнимет много сил и времени, да и сам я их до сих пор не знаю, чего греха таить).

Итак, с тем, что именно из теории лучше всего изучать на начальном этапе, мы разобрались. Разберёмся теперь как именно следует закреплять теорию на практике.

Практическая часть:
Разбирать мы это будем на примере первого урока, "Числа и работа с числами (ответ на вопрос что такое %d)". Из данного урока мы узнаём о том, как создать новую переменную, что бывают переменные, которые хранят в себе целые числа, бывают переменные, которые хранят в себе дробные числа, а также бывают переменные, которые хранят в себе множества чисел ("массивы"). Узнаём о том, что переменным можно присвоить значение (число, целое или дробное, в зависимости от типа переменной), что при необходимости, можно произвести математические действия, и выполняются они с переменными, а не напрямую с числами (значениями этих переменных), что массив имеет размер, и т.д. Информации немало и уже на данном этапе могут быть вопросы. Даже, если их нет сейчас, они могут появиться в процессе практики. А наша конечная цель - понять принцип работы языка Pawn. Без практики это невозможно, следовательно, чем больше будет практики, тем лучше будет понимание, а начинать понимать лучше всего на самых начальных этапах. Поэтому необходимо придумать задачу, в которой будут задействованы переменные, числа, какие-либо вычислительные действия. В задаче может и не быть никакой логики, сейчас для нас главное - видеть результат наших действий и чётко понимать, откуда он взялся.

Поставим себе задачу: вывести в консоль сервера значения объявленных нами переменных. Напишем следующий код:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    new iVar1, iVar2
    server_print
("iVar1 = %d, iVar2 = %d", iVar1, iVar2)

Теперь скомпилируем наш плагин и установим его на сервер. При запуске сервера мы должны увидеть следующую строку в консоли:

Код: Выделить всё
iVar1 = 0, iVar2 = 0

Это значит, что мы всё сделали правильно и мы видим, что обе наши переменные имеют значение 0. Исходя из этого, мы можем сделать вывод, что в языке Pawn при объявлении переменная имеет значение 0. Теперь разберём этот код более подробно, чтобы чётко понимать, что мы сделали.

Код: Выделить всё
#include <amxmodx> // подключение инклуда или заголовочного файла amxmodx.inc

public plugin_init() { /* "отлов" форвард-функции plugin_init, вызывается при запуске
сервера или смене карты; о типах функций читайте в уроке "Какие бывают функции. Типы функций" */
    new iVar1, iVar2 // объявление целочисленных переменных с именами iVar1 и iVar2
    server_print("iVar1 = %d, iVar2 = %d", iVar1, iVar2) /* вызов натив-функции
    server_print, посредством которой происходит вывод значений переменных
    iVar1 и iVar2 в консоль сервера */

О том, что вы не знаете пока каких-либо функций AMX Mod X - не беспокойтесь. Ваша задача сейчас состоит в том, чтобы понять, как работает язык Pawn. Ссылки на полезную документацию будут в конце статьи, так что у вас будет время и возможность во всём подробно разобраться. Сейчас очень важно понять принцип работы, понять, откуда получается конечный результат и что происходит в скрипте. Немного изменим наш код, присвоим объявленным переменным значения:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    new iVar1 = 10, iVar2 = 20
    server_print
("iVar1 = %d, iVar2 = %d", iVar1, iVar2)

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

Код: Выделить всё
iVar1 = 10, iVar2 = 20

Теперь произведём с переменными какие-нибудь арифметические действия. Давайте произведём деление одной переменной на другую. Для этого будем использовать соответствующий оператор, / :

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    new iVar1 = 10, iVar2 = 20
    new iResult 
= iVar1 / iVar2 /* объявляем новую переменную с именем iResult,
    в которой будет храниться результат деления значения переменной iVar1 на iVar2 */
    server_print("iResult = %d", iResult) /* выводим значение переменной iResult
    в консоль сервера */

Как вы понимаете, мы разделили меньшее число на большее и в результате получим целое число 0:

Код: Выделить всё
iResult = 0

Однако, если бы мы работали с переменными типа Float, которые могут принимать дробные значения, то результат был бы как и положено по всем законам арифметики: 0.5. Давайте проверим, изменим наш код ещё раз:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    new Float:fVar1 = 10.0, Float:fVar2 = 20.0
    new Float
:fResult = fVar1 / fVar2
    server_print
("fResult = %.1f", fResult)

Код: Выделить всё
fResult = 0.5

Отлично. В результате простейших арифметических действий мы получили конкретный результат. Теперь давайте попробуем как-либо его использовать. Например, поставим задачу, в зависимости от значения переменной отображать в консоли сервера то или иное сообщение, изменим наш код:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    new Float:fVar1 = 10.0, Float:fVar2 = 20.0
    new Float
:fResult = fVar1 / fVar2
    if
(fResult == 0.5) { // если результат равен 0.5, то
        server_print("Condition has passed") // выводим сообщение, что условие выполнилось
    } else { // или
        server_print("Condition not passed") // выводим сообщение, что условие не выполнилось
    }

Теперь мы всегда будем видеть в консоли сервера одно и то же сообщение:

Код: Выделить всё
Condition has passed

Поскольку наше условие всегда будет выполняться, вернее до тех пор, пока значение переменной fVar1 будет ровно в два раза меньше значения fVar2. Теперь самое время задуматься над тем, когда ещё мы могли бы совершать подобные вычислительные действия, проверки, да и вообще любые другие действия, которые могут быть нам необходимы. Только ли при запуске сервера и смене карты? А если мы не знаем заранее, когда мы захотим совершить то или иное действие, как же быть? Давайте разберёмся в этом.

Внесём некоторые изменения в наш код:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    register_concmd("get_result", "CmdGetResult") /* регистрируем консольную команду
    get_result */
}

public CmdGetResult() { // обрабатываем команду get_result
    new Float:fVar1 = 10.0, Float:fVar2 = 20.0
    new Float
:fResult = fVar1 / fVar2
    if
(fResult == 0.5) {
        server_print("Condition has passed")
    } else {
        server_print("Condition not passed")
    }

Теперь сообщение "Condition has passed" будет отображаться в по команде get_result в консоль сервера. Обратите внимание, что все вычислительные операции, а также объявление переменных происходят внутри функции-обработчика команды get_result (CmdGetResult), то есть, локально. На самом деле, они могут происходить и в любом другом месте, например, до инициализации или при инициализации (plugin_init). Согласно нашей простейшей задумке, в этом нет необходимости, но попробуем рассмотреть пример кода, когда такая необходимость может возникнуть. А также попробуем разобраться, когда мы не можем производить какие-либо действия вообще.

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

Код: Выделить всё
#include <amxmodx>

new g_iNum = 576 /* объявляем глобальную переменную с именем g_iNum
и присваиваем ей значение 576 */

public plugin_init() {
    register_concmd("get_result", "CmdGetResult")
}

public CmdGetResult() {
    server_print("g_iNum = %d", g_iNum) /* по команде get_result
    отображаем значение глобальной переменной */
}

Итак, переменная объявлена верно, код компилируется, по команде get_result сервер отображает сообщение "g_iNum = 576". Всё верно. Давайте проверим, что получится, если объявить глобальную переменную не до инициализации, а перед самой функцией-обработчиком:

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    register_concmd("get_result", "CmdGetResult")
}

new g_iNum = 576

public CmdGetResult
() {
    server_print("g_iNum = %d", g_iNum)

И в этом случае всё хорошо, всё работает. Но что будет, если объявить переменную в конце нашего кода, то есть, после функции-обработчика?

Код: Выделить всё
#include <amxmodx>

public plugin_init() {
    register_concmd("get_result", "CmdGetResult")
}

public CmdGetResult() {
    server_print("g_iNum = %d", g_iNum)
}

new g_iNum = 576

А будет ошибка при компиляции:

Код: Выделить всё
error 017: undefined symbol "g_iNum"

Ошибка дословно означает, что символ g_iNum неизвестен. Почему так происходит? Компиляция происходит последовательно и на момент компиляции строки, содержащей натив-функцию server_print, в скрипте нигде не упоминается переменная g_iNum. То есть, компилятор ещё "не знает" о её существовании, но в коде она уже используется.

Рассмотрим ещё один пример, который поможет нам понять принцип работы языка Pawn. Изменим наш код ещё раз: вернём глобальную переменную на положенное ей место и попытаемся присвоить значение позже, сразу после объявления:

Код: Выделить всё
#include <amxmodx>

new g_iNum
g_iNum 
= 576

public plugin_init
() {
    register_concmd("get_result", "CmdGetResult")
}

public CmdGetResult() {
    server_print("g_iNum = %d", g_iNum)
}

И снова мы видим ошибку при компиляции, только уже другого характера:

Код: Выделить всё
error 010: invalid function or declaration

Это значит, что компилятор считает функцию или действие ("декларацию" / "заявление", как это сказано в тексте ошибки) недействительным. То есть, присваивание значения - это точно такое же действие. И его можно выполнить либо сразу при объявлении переменной, либо уже внутри какой-нибудь функции.

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

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

Напомню, что мы делали в практической части, кроме того, что пытались понять принцип работы языка Pawn: мы разбирали на примере Вы должны зарегистрироваться, чтобы видеть ссылки.. Вам же нужно будет освоить на таких же примерах ещё 7 уроков, как минимум, не забывайте об этом.

Ну вот, собственно, и всё. В данном материале (включая полезные ссылки) описан самый необходимый базис, который нужно знать и понимать каждому начинающему скриптеру. Если вы твёрдо решили освоить скриптинг под AMX Mod X, то запомните ещё кое-что: главное - это желание и усидчивость. Изучение теории требует усидчивости и терпения, а применение знаний на практике непременно будет вознаграждено результатом. И ещё одно немаловажное напутствие: никогда не пытайтесь охватить всё и сразу, в короткие сроки, что называется, "пройтись по верхушкам". Это худшее освоение чего-либо, так вы никогда не достигните хороших результатов.

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

П.с.: возможно, в статью будут внесены корректировки, следите за изменениями. Окончательный вариант статьи будет уже без постскриптума.
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Изучение основ скриптинга под AMX Mod X

Сообщение RevCrew » 26 май 2016, 18:08

немножко собственного мнения:
Всегда хочется чего-нибудь большего, ну то есть, я вот не начал бы изучать язык, с того чтобы вывести значения переменных в консоль, потому, что это не реальная цель, а вот написать свое Vip Menu, цель вполне реальная, в том смысле, что имеет больше толка :-) Проблема в том, что после того как ты научился печатать Hello World, ты не приблизился к своей цели написать vip menu, все еще есть огромная и кажется, бесконечная пропасть.
Еще, я начал заниматься скриптингом именно с переработки готовых плагинов, ведь можно скачать уже готовый Vip Menu, а разбираться в коде, мне тогда, казалось, легче чем писать готовый, не знаю никаких функций. И через год, после этого я начал заниматься теорией, т.к начал задумываться об оптимизации и прочее и прочее. :-[
Аватара пользователя
RevCrew
Скриптер
 
Сообщения: 1648
Зарегистрирован: 15 июл 2013, 20:45
Благодарил (а): 273 раз.
Поблагодарили: 357 раз.
Языки программирования: Unkown

Re: Изучение основ скриптинга под AMX Mod X

Сообщение Subb98 » 26 май 2016, 18:11

RevCrew, всё верно: я ошибусь, если скажу, что хоть один из нас не начинал с редактирования чужих работ "под себя" и с менюшек, но эта статья именно о том, как следует дальше учиться правильно, чтобы достичь чего-то большего. :thumbs_up
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Изучение основ скриптинга под AMX Mod X

Сообщение Fedcomp » 26 май 2016, 19:57

точно также начинал с того что копипастил код с других плагинов. Впрочем до сих пор так делаю, разумеется понимаю его полностью и адаптирую под себя. Правда опыт кодинга до этого был.
Не помогаю в ЛС - есть форум.
Плагины тоже не пишу, на форуме достаточно хороших скриптеров.


"я ставлю зависимости потому что мне приятно" - subb98 @ 2017
Аватара пользователя
Fedcomp
Администратор
 
Сообщения: 4936
Зарегистрирован: 28 авг 2009, 20:47
Благодарил (а): 813 раз.
Поблагодарили: 1317 раз.
Языки программирования: =>
pawn / php / python / ruby
javascript / rust

Re: Изучение основ скриптинга под AMX Mod X

Сообщение iplague » 04 окт 2017, 13:04

По своему опыту скажу, что не имея никакого IT образования можно научиться писать простенькие плагины ~ за месяц. Сначала по традиции написал вип-меню :-[ Теперь на 50% переписал под себя war3mod, разобрался во всём включая mysql и прочие сложности, написал сам себе простенькие плагины.

Спасибо за подобные статьи, подробные ответы и общедоступность исходников некоторых ваших плагинов.
vk.com/amxxdevelopment
Аватара пользователя
iplague
 
Сообщения: 46
Зарегистрирован: 23 май 2016, 13:50
Благодарил (а): 2 раз.
Поблагодарили: 13 раз.
Опыт программирования: Около года
Языки программирования: pawn

Re: Изучение основ скриптинга под AMX Mod X

Сообщение Non Sense » 07 дек 2017, 00:07

Ну, я начинал тоже с корректировки чужих плагинов, просто логически пытаясь определить, что, где, куда и зачем. Но это все равно не давало представления о большинстве функций, и я не понимал как конкретно это работает. Все равно что печь пирог, смотря как это делает тот кто умеет, но заменяя ингредиенты на свои.
Благодаря этой статье немного понял о том, почему, что и в какой последовательности нужно делать. Возможно из остальных пойму и остальные аспекты.
Респектую.
Аватара пользователя
Non Sense
 
Сообщения: 10
Зарегистрирован: 26 май 2014, 17:27
Благодарил (а): 1 раз.
Поблагодарили: 1 раз.

Re: Изучение основ скриптинга под AMX Mod X

Сообщение Non Sense » 10 дек 2017, 19:05

Итак, прочитал и понял эту тему, и те что в ней рекомендуются (и пару дополнительных попутно). И в правду - основные знания получил, и хочется идти дальше. За что браться? Изучать английский? Зубрить инклюды, начиная с amxmodx.inc? Начинать практиковаться, пользуясь AmxxStudio, пользуясь его подсказками?
Аватара пользователя
Non Sense
 
Сообщения: 10
Зарегистрирован: 26 май 2014, 17:27
Благодарил (а): 1 раз.
Поблагодарили: 1 раз.

Re: Изучение основ скриптинга под AMX Mod X

Сообщение артист666 » 10 дек 2017, 20:16

Non Sense писал(а):За что браться?

Ну ты же с какой-то целью читал статью?

Non Sense писал(а):Изучать английский?

А онлайн переводчиком религия не позволяет пользоваться?

Non Sense писал(а):Зубрить инклюды, начиная с amxmodx.inc?

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

Non Sense писал(а):Начинать практиковаться, пользуясь AmxxStudio, пользуясь его подсказками?

Синтаксис хотя бы будешь знать.
Ну и не ошибёшься, с подсказками-то.
Если вы хотите чему-то научиться, пробуйте, задавайте вопросы.
Иначе компенсируйте потраченное время на ваши "проблемы".
Аватара пользователя
артист666
 
Сообщения: 2706
Зарегистрирован: 09 апр 2013, 17:46
Благодарил (а): 284 раз.
Поблагодарили: 639 раз.
Опыт программирования: Больше трех лет
Языки программирования: С, C++
Delphi
HTML, CSS, Java Script
Pawn

Re: Изучение основ скриптинга под AMX Mod X

Сообщение Non Sense » 11 дек 2017, 00:12

артист666 писал(а):Ну ты же с какой-то целью читал статью?

Да. С целью понять как работает амхмод. Что делать дальше мне все ещё не понятно. Браться писать плагин с ноля, или хотя бы понять как построен готовый плагин и править его, я все ещё недостаточно компетентен, как собственно в статье и описывалась.

артист666 писал(а):
Non Sense писал(а):Изучать английский?

А онлайн переводчиком религия не позволяет пользоваться?

Грубить не обязательно) я сюда за помощью и советом пришёл, и написал лишь догадки, чем можно продолжить. Может ещё что почитать предварительно стоит. Многие темы здесь я пока все ещё не понимаю.
Аватара пользователя
Non Sense
 
Сообщения: 10
Зарегистрирован: 26 май 2014, 17:27
Благодарил (а): 1 раз.
Поблагодарили: 1 раз.


Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12