Здравствуйте господа. Сегодня я расскажу вам о том, как одна ошибка в скрипте баша может похоронить вам все, что есть на вашей VPS. Поехали.

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

Я думаю описывать дальше что случилось не стоит… баш просто  запрашивал папку в корне, не получал ее, и вайпал все в текущей директории, то есть в /opt/ и в итоге вайпнул все в папке opt.

А дальше начались приколы:
1) Бекапы на ftp были очень старыми, так как ftp перестал отвечать на загрузку из скрипта
2) Те бекапы которые я сделал вручную не выгрузило из opt и ихвыйпнул скрипт выше

ДА НАЧНЕТСЯ ПОЖАР!

В итоге безвозвратно потеряны:

  • Каталог блога, со всеми загрузками, плагинами и их настройками, мелкими правками движка и т.д.
  • Несколько проектов которые приносили мне маломальский доход, бекапам которых от 4 месяцев до года (спасибо ftp).
  • Мелочи вроде svn и gogs которые я недавно себе поднял

В связи с чем я понял одно, бекапить надо в несколько мест и желательно в облако. Посмотрев на то что я использую, было выбрано 2 места, Google Drive и Яндекс Диск. Первый отпал по причине сложности работы с ним, а второй подошел отлично.

Старая система копирования была устроена так:

  • Раз в день на ftp улетал дамп всех баз данных
  • Раз в неделю на ftp улетали архивы папок блоков сервера
  • Раз в 2 недели улетали архивы etc/nginx
  • Раз в месяц улетал весь /etc полностью

При каждом бекапе происходила отправка на ftp.

От бекапа только каталога nginx решено было отказаться, бекап базы данных раз в день был не сильно нужен, ибо записи и изменения происходили не часто. Далее, самое важное, доверие к FTP было подорвано. Решено было выгружать в облако. Благо Яндекс Диск поддерживает WebDav из коробки, так что это не выглядело сложной задачей. Так что был написан простенький bash скрипт который запускается через cron и делает свое дело:

Надеюсь теперь то все точно будет восстанавливаемо. Слава богу базы данных остались на mysql сервере и их удалось выгрузить…

По сути своей цикл — повторение определенного действия указанное количество раз. Образно циклы можно сразу поделить на две большие категории, циклы бесконечные, работающие постоянно, и конечные, исполняющие инструкции заданное количество раз. Все циклы в А2 реализованы как циклы с предусловием, то есть проверяют условие своего функционирования перед запуском инструкций, постусловие можно навесить искусственно. И да, ниже по тексту мы будем очень много считать… Ну прямо очень много. Ибо счет это наиболее наглядная демонстрация работы циклов.

Всего в скриптовом языке Армы нам доступны следующие циклы:

  • for и все его разновидности
  • while
  • waitUntill
  • forEach
  • onEachFrame

Вроде ничего не забыл. Начнем с самого сложного.

for

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

Самый простой цикл for-do будет выглядеть так:

Что происходило во время работы цикла? Каждый запуск кода (далее итерацию) происходила запись суммы _var и текущего шага цикла в переменную _var. Можно проиллюстрировать это как:

  • _var = 0 + 0;
  • _var = 0 + 1;
  • _var = 1 + 2;
  • _var = 999 + 1000;

На выходе мы имеем: каждый шаг цикла переменная _i увеличивается на 1, так как шаг по умолчанию +1. В итоге через переменную _i мы всегда можем получить текущий шаг.

Мы можем менять шаг цикла, давайте получим сумму чисел от 0 до 1000 кратных 5. Сделаем это переключив шаг (step) в инициализации цикла:

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

Но если мы поставим верхний предел кратный 5, допустим 20. То количество запусков будет равно 5.

Так же мы можем и назад посчитать:

Ну и любые возможные комбинации так же можно записать, никаких проблем нет, пока нет какого-нибудь математического парадокса:

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

while

Самый простой для выполнения цикл. Единственной его особенностью в Arma является то, что условием у него является не логическое выражение, а код. Пока фрагмент кода выполним — цикл работает. Самый простой пример:

В «условии» мы выполнили арифметическую операцию и затем логическую. Условие блока while записывается как код, независимо от выполнимых операций. К слову данный цикл ко всему еще и является «бесконечным», он будет выполняться всегда. Так как 2+1 всегда = 3. Тем самым запись бесконечного цикла while можно свести к:

При этом существует единственное ограничение, while может быть вызван конечное количество раз (10000), если инициирован в игровом редакторе. Потом скриптовый движок сочтет цикл бесхозным и выключит его за ненадобностью.

«Бесконечный» цикл for

Цикл for сам по себе функционирует как постоянная проверка логического условия, в принципе его можно записать как цикл while (реализован он крайне похоже):

«Пока сумма нижнего предела и текущего шага меньше, чем верхний предел повторять инструкцию» — как то так оно и звучит. Но, что если мы организуем небольшой парадокс, и заставим цикл for работать с нулевым шагом

В итоге можно записать выродившийся цикл выше, как:

waitUntil

Так же выродившийся цикл while. Предназначен для создания задержки перед каким-либо событием. Допустим ожидания значения какой-либо переменной, заодно докажем что это цикл. Будем ждать пока _var будет больше 10:

Вырожденный цикл можно записать как:

Выход из цикла (прерывание)

Крайне часто стоит задача выполнять какой-либо цикл, но при этом мы не имеем ни точного количества итераций, ни состояния по которому необходимо отследить выполнение. Я забегу немного вперед и зацеплю немного массивов. Ибо наиболее простой пример это нахождение элемента в массиве, мы реализуем выполнение задачи требующей выхода с помощью двух «бесконечных» циклов for и while, сильно извращаться не будем.

Сформулируем задачу:
На входе имеем переменную и массив произвольной длинны, нам необходимо узнать есть ли данная переменная в массиве, и если есть вывести соответствующее сообщение:

Что мы имеем: переменную, массив произвольной длинны, добавим к этому индикатор _state. Далее мы организуем цикл for с шагом +1, в котором будем поочередно перебирать элементы массива, а далее самое интересное: при совпадении значения элемента с нашей контрольной переменной мы запишем в _i хранящую наш текущий шаг значение большее, чем наш верхний предел, ну и переключим наш индикатор. А далее простое условие, если индикатор переключен — нашли, если нет, не нашли. Можете поиграться с длинной массива и наличием переменной в нем.

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

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

onEachFrame

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

А цикл foreach мы подробнее рассмотрим когда будем говорить о массивах.

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

Здесь нам и приходит на помощь возможность проверять определенные логические условия и строить конструкции.Читать далее

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

С чего же начинается путь любого скриптера в Arma? Чаще всего вы хотите сделать какую-то карту, реже аддон, и понимаете что вам нужна какая-то плюшка, которую стандартными инструментами игры ну никак не сделать. Вы начинаете искать, гуглить, и находите на форуме Бисов или где-то еще странные выкладки кода которые сложно понимаемы  и не очень понятно что с ними делать.

Вот моя задача сделать что-то, что при поиске в гугле заведет вас сюда и в краткие строки научит вас реализовывать ваши задумки путем написания простеньких sqf скриптиков. Да, sqs мы даже не затронем, потому что… просто не затронем. Слишком долго объяснять. Ну и не будем затрагивать скрипты в аддонах, наверное только по началу, не знаю насколько меня хватит. Так что рассматривать будем только скрипты для миссий. Вообще, честно говоря, учиться кодить с ноля всегда сложно, ибо то что ты учишь сейчас, слишком сильно зависит от того, что ты изучишь потом. Но я постараюсь сделать все как можно более понятным и расписать конкретные примеры.

И так, что же нам доступно для написания скрипта?

Переменные

В Арме, слава богу, отсутствует как таковой тип переменной. Интерпретатор сам выбирает нужный тип, ну иногда нужно подсказать ему перевести численные в строковый, но это нужно крайне редко. Сразу себе нужно уяснить две следующие вещи:

1) Существует два типа переменных, локальные и глобальные. На самом деле чуть больше, но об этом чуточку позже. В чем отличие? Из тех которые нас интересуют на начальном этапе —  область видимости. Допустим:

Как вы успели заметить объявление локальной переменной происходит путем добавления _ (нижнее подчеркивание) перед ее навзанием. Этого достаточно для того, что бы движек понял что данная переменная локальная к текущей области и не забивал ею общую память. После завершения текущей области кода переменная канет в лету, иными словами данные переменные ко всему не сохраняют свое значение после работы скрипта.

Что это нам дает? Если мы будем писать сложный скрипт, с множеством разных потоков и циклов, которые должны будут взаимодействовать с некой информацией, мы можем не передавать ее из функции в функцию и из цикла в цикл, а сделать глобальную переменную, из которой мы потом с легкостью достанем и с легкостью перепишем нужное нам значение.

Но что если мы хотим быть уверены в том, что наша вроде бы локальная переменная уж точно не ушла в другие вложенные участки кода? Тогда нам нужно использовать простую директиву private. Данная деректива позволит нам указать интерпретатору что созданная нами переменная с таким именем еще не была объявлена в конкретной области видимости и ее необходимо переобъявить:

 

В сущности private делает переменную в родительском блоке как бы неприкасаемой, что ты во вложенном блоке кода не делай, на ее значение это никак не повлияет. Крайне полезно при написании функций, но об этом ниже.

Так же существуют несколько локальных магических переменных, специальных переменных которые создаются некоторыми скриптовыми командами. Самые распространенные: _this, _x, _target. Первые две используются крайне часто, а про последнюю надо помнить при создании скриптов затрагивающих взаимодействие с объектами, ибо команда addAction имеет директиву _target, и очень часто эта же переменная создается в скрипте, что вызывает лишние ошибки. Переменную _this генерирует бесчисленное множество команд, а _x  связана с перебором разного рода массивов.

Такие же переменные могут быть и глобальными, самые используемые: player, всякие математические константы и операторы вроде pi, date и time

2) Переменные хранятся на конкретной машине, в конкретной миссии MissionNamespace, ну или «Области видимости миссии», и не раздаются в сеть, для этих целей используется publicVariable. Главное что стоит запомнить: значения этих переменных хранятся на этапе от их инициализации до завершения вашего прибывания на миссии. То есть, при перезаходе — переменные инициализируются заново. И с этим надо что-то делать, но об этом позже. Есть несколько приемов.

Есть еще несколько областей видимости:
UiNamespace — область видимости, нужна в основном для аддонов. Объявляет переменную значение которой хранится с момента объявления, до момента закрытия игры, то есть выключения ее процесса.
ProfileNamespace — область видимости профиля, то же для аддонов. Хранит информацию о переменной в вашем профиле, яркими примерами являются настройки игры в вашем профиле. Если когда-либо открывали и видели там что-то вроде:

Это все переменные записанные именно в ProfileNamespace.

Функции

Так же мы можем назначить переменной выполнение определенного участка кода, создав тем самым простейшую функцию:

Таким образом мы создали простейшую функцию которая выведет нам переданную ей переменную. Но как нам собственно передать ее в функцию?

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

И точно так же как и с переменными kuz_fnc_test будет видна во всех скриптах, а _kuz_fnc_test только в конкретном.

При этом мы можем не только передавать информацию в функцию, но и получать ее после ее выполнения. Допустим:

В этом примере мы дали на вход функции два значения, на выходе получили их сумму. Вывод из функции реализуется обращением к переменной в последней исполняемой строке. Ну и дабы записать куда-то это значение, выполнение функции присваивается переменной, в нашем случае _var

На этом пока все. В следующей записи расскажу о Циклах и Условиях.