Подразделы Get Skript

Введение

Внутривенное?

Skript – это плагин сценариев для платформы Bukkit (в том числе его модификации: spigot, paper и др).
Его легко использовать для простых задач, но с его помощью, также, можно создавать сложные вещи.
Синтаксис Skript близок к английскому, поэтому очень прост к освоению, конечно, если вы знаете английский язык на базовом уровне. В данный момент русская документация по языку Skript так и не была создана. К тому же, как оказалось, рядовому ру-майн пользователю, достаточно сложно в нем разобраться. Поэтому мы собрались всей командой crewpvp.xyz (и не только), чтобы попытаться написать действительно эффективный обучающий материал по аспектам программирования на языке Skript. Начнем с того, что как вы уже знаете (вы должны это знать, иначе зачем вам это читать?) плагины устанавливаются путем перемещения компилированного JAR файла в папку /plugins/ в корне сервера. Так вот, получить актуальную версию плагина Skript можно в Github репозитории разработчика Документацию с описанием всех функций, типов и выражений всегда можно получить здесь Кроме того, исходный код плагина открыт для любого пользователя, поэтому вы можете создавать его модификации при должном знании Java.

Тут будут находится, возможно, незнакомые для вас слова, назовем это - глоссарий

Bukkit - глобальная серверная модификация, предоставляющая API для взаимодействия с игровым миром и созданная для упрощения создания плагинов к многопользовательскому-minecraft-серверу

Синтаксис - набор правил, описывающий комбинации символов алфавита, считающиеся правильно структурированной программой (документом) или её фрагментом

Компилированный - готовый, собранный (в данном контексте речь идет о программе)

Github - сервис онлайн-хостинга репозиториев, обладающий всеми функциями распределённого контроля версий и функциональностью управления исходным кодом Репозиторий - место, где хранятся и поддерживаются какие-либо данные

JAR - расширение файла компилированного кода языка JAVA

JAVA - строго типизированный объектно-ориентированный язык программирования общего назначения

Проба пера

Не под ребро

После того как вы установили плагин на свой сервер, внутри папки /plugins/ будет создана папка /Skript/. В ней находятся ваши собственные скрипты, сохраненные переменные и настройки самого плагина. Сейчас нас не совсем интересуют настройки и переменные, хранящиеся в таблице .csv, поэтому попробуем перейти сразу к написанию чего-либо (а зачем медлить?)

Шаг №1

Выберем текстовый редактор. Чтобы начать создавать код своими умелыми ручками нам необходим текстовый редактор. Если у вас уже есть любимый - используйте его (Microsoft Word не подойдёт), но если нет - возьмите любой из списка ниже:

Sublimetext (windows, linux, mac)
Visualstudio (windows, linux, mac)
Notepad++ (windows)

Шаг №2

Создадим файл, где мы будем воплощать IT-художества. Заходим в директорию /plugins/Skript/scripts/, создаем внутри файл (название не важно, но постарайтесь использовать только латиницу и цифры) с расширением .sk - Например myfirstscript.sk Теперь откройте файл в текстовом редакторе.

Шаг №3

Перейдём к воплощению IT-художеств. Skript – это событийно-ориентированный язык, то есть, чтобы что-то выполнилось, должно произойти какое-то событие. Под событием имеется ввиду действие пользователя или самого сервера, так вот начало нашего блока кода должно начинаться с необходимого нам события. Структура блока кода в Skript:

%название события или описание функции%:
  <- #Обязательный отступ после ':', из-за него интерпретатор 
     #понимает внутри какого блока мы работаем
  ваш код...

Отступом может быть 2 пробела, 4 пробела или 1 нажатие клавиши TAB. В одном файле не могут быть использованы разные виды отступов. Выберите тот, который удобен именно вам. Отступы используются не только при объявлении функций или использования события, но вам пока что об этом знать рано, поймете в процессе чтения данной документации В данном примере мы возьмем простое и понятное событие ‘on load’, которое происходит при загрузке файла скрипта, в котором находится данное событие.

on load:
  здесь будет наш код...

Теперь мы можем добавить выражение для вывода текста для всех, это выражение broadcast
Синтакс этого выражения: broadcast %objects% [(to|in) %worlds%] Немного разберем как в документации пишется синтакс:

Все что заключено в проценты % % обозначает тип данных который может использоваться в выражении (в нашем случае это тип Object, что обозначает абсолютно любой тип данных

Внутри % % так же может присутствовать символ /, он используется для перечисления определенных типов внутри выражения, которые могут быть к нему применены

Все что заключено в квадратные скобки [ ] означает, что это не обязательная часть выражения, она может быть, а может не быть (на усмотрение разработчика) (в нашем случае, можно дополнительно указать в каких мирах конкретно нужно выводить сообщение)

Скобки ( ) используются для изменения приоритета (объединения в один блок), словно в математике, сначала будет считываться то, что внутри, а только потом все, что вне

Знак | означает ИЛИ (в нашем случае “(to|in)” в скобки взяты два слова to и in, между ними |, следовательно при написании можно использовать как broadcast %objects% in %worlds% или broadcast %objects% to %worlds%, оба варианта будут рабочими в коде)

Теперь мы можем попробовать вывести какой-то текст на сервере:

on load:
  broadcast "Hello World!"

Текст в языке Skript пишется внутри двойных кавычек (“text”), кроме этого если в тексте надо будет написать служебный символ, например кавычки, их придется удвоить
("Пример: "" "" ", означает текст Пример: " ")

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

#Код ниже передает привет миру при запуске этого скрипта
on load:
  broadcast "Hello World!"

Шаг №4

Теперь изучим основные команды плагина Skript. Главная команда: /skript или просто /sk Подкоманды: /skript reload (путь до скрипта|config|all) – перезагружает определенный файл, конфиг или все сразу /skript enable (путь до скрипта|all) – включает определенный скрипт или все сразу /skript disable (путь до скрипта|all) – отключает определенный скрипт или все сразу /skript info – выводит информацию о плагине и текущих установленных дополнениях /skript update – проверяет наличие новых версий плагина /skript help – пишет то, что было описано выше

Скрипт не будет загруждаться, если первый символ его названия ‘-’, такой скрипт считается выключенным, причем если он уже был загружен в память, переименование скрипта не выключит его, нужно будет написать команду /sk disable НазваниеСкрипта

Кроме этого вы можете включать и выключать целые директории со скриптами. Например у нас есть есть некая папка /main/, в которой находятся файлы скрипта, мы можем быстро включить, выключить или перезагрузить их просто указав директорию: /sk reload main/

Шаг №5

Теперь перезагрузим созданный нами файл: /sk reload НазваниеСкрипта
Если вы выполнили все шаги верно – в чат напишется ‘Hello World!’

Переменные – зарезервированная область памяти в которых хранится какая-либо информация

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

IT (Information Technology) – информационные технологии

Переменные

Где, как и зачем хранить данные?

Переменные есть практически во всех языках программирования (индентификаторы), они необходимы чтобы запоминать какую-либо информацию, в основном она хранится только в оперативной памяти, но в Skript’е можно хранить их и на диске (учитывайте, что любая переменная - занимает память, и хранить что-то долгосрочное, допустим данные игроков, там - не стоит), проблема же оперативной памяти в том, что после выключения сервера - данные просто пропадут. Перейдем к сути. Skript - слабо типизированный язык, то есть при объявлении переменной, нам не нужно указывать ее тип (число это, или текст, или еще что-либо) Синтакс переменной: {название} Области видимости переменной:

– Глобальная, с сохранением на диск
– Глобальная без сохранения на диск (только в оперативной памяти)
– Локальная (только в оперативной памяти)

Чтобы объяснить интерпретатору, какую именно переменную мы создаем, нужно в начале названия дописать определенный символ:

– Для локальной переменной используется символ "_" (нижнее подчеркивание), например {_var}

– Для глобальной переменной, с сохранением на диск – никаких символов не используется

– Для глобальной переменной, без сохранения на диск, нам необходимо исправить конфиг плагина Skript по директории /plugins/Skript/config.sk это:

default:
    type: CSV
    pattern: .*
    file: ./plugins/Skript/variables.csv
    backup interval: 2 hours

исправить на это:

default:
    type: CSV
    pattern: (?!-).*
    file: ./plugins/Skript/variables.csv
    backup interval: 2 hours

После этого исправления – переменная не будет сохранятся на диск, если в начале ее названия есть символ "-", например {-var} Теперь разъясним какие отличия у глобальных и локальных переменных: Глобальные можно изменять и получать их значение в любом блоке кода, даже если он в другом файле. Локальные же можно изменять и получать их значения только в том событии, в котором они используются, причем после окончания выполнения кода в событии они будут автоматически удалены, поэтому в основном, там где не нужно взаимодействие разных событий используются именно они. В Skript есть большое количество типов данных связанных с игровыми аспектами minecraft, их описание можно найти на странице типов в документации: Я ТАК ПОНИМАЮ ЗДЕСЬ НУЖНА ССЫЛКА НА https://docs.skriptlang.org/expressions.html

Использование переменных

В этой части мы будем работать только с базовыми типами, которые есть во многих языках - текст, число и булевый тип (содержащий в себе только true или false) Переменная считается не объявленной до тех пор, пока мы не присвоим ей какое-то значение.
Как присвоить значение? Для этого используется выражение: set %objects% to %objects%
В дословном переводе получится что-то вроде этого: установить %что-то% на %что-то%

Локальные переменные и текстовый тип данных

Основываясь на прошлом уроке, попробуем поместить текст ‘Hello World!’ во внутрь какой-нибудь переменной: set {_var} to "Hello World!"
Данной строкой мы поместили в локальную переменную {_var} текст Hello World! Теперь в выражении вывода мы можем заменить наш текст, на переменную:

on load:
  set {_var} to "Hello World!"
  broadcast {_var}

Перезагрузим скрипт и увидим, что результат аналогичный, что и с кодом до этого. Кроме этого вам скорее всего хотелось бы узнать, как можно соединить две переменные в тексте, или преобразовать переменную с любым типом в текст: Для того нам необходимо вписать переменную в двойные кавычки и заключить эту переменную между %, вот так “%{_var}%”
Причем внутри кавычек мы можем использовать любое количество переменных. Изменим наш прошлый код на этот:

on load:
  set {_var} to "Hello World!"
  broadcast "%{_var}%, %{_var}%"

Перезагрузив скрипт, мы должны увидеть в чате 'Hello World!, Hello World!' (если всё выполнено верно) Можно удалять переменные при помощи выражения: delete %objects%, где аргумент это идентификатор. Приготовьтесь к взрыву мозга: для названия переменной можно использовать значение другой переменной:

on load:
  set {_var} to "var2"
  set {_%{_var}%} to "Hello World!"
  broadcast "%{_%{_var}%}%, %{_var2}%"

Пояснение кода написанного выше:
Создается переменная с текстом 'var2', после чего - создается переменная, в название которой мы помещаем текстовое значение из переменной {_var} и задаем ей значение 'Hello World!', затем выводим значение {_var2} в чат (в обоих вариантах будет одно и то же)

Глобальные переменные

Теперь поговорим насчет глобальных переменных. Для демонстрации их в действии нам необходимо второе событие. Возьмем 'on break', это событие которое вызывается, когда игрок ломает блок.

on load:
  set {-var} to "Hello World!"
on break:
  broadcast "%{-var}%"

Так как глобальные работают во всех блоках кода – в данном коде, при загрузке, создается глобальная переменная {-var}, с текстом 'Hello World!', и при разрушении блока игроком {-var} выведется в чат.

Числовой тип данных

Вернемся к типам данных: числа в отличии от текста, пишутся без кавычек, например: set {_var} to 2 Кроме того, чтобы задать значение числа в переменную, мы можем использовать математические операции сложение, вычитание, умножение, деление, а так же другие числовые переменные Для примера попробуем рассчитать квадрат числа 64 и выведем его в чат:

on load:
  set {_var} to 64*64
  broadcast "%{_var}%"

Если все верно, в чат будет выведено число 4096

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

on load:
  set {_var} to 64
  broadcast "%{_var}*{_var}%"

Тоже выведет нам число 4096, но в переменной {_var} все еще будет 64, но на выводе мы перемножаем ее саму на себя.

Интерпретатор не запрещает нам использовать математические операции с другими типами данных, просто на выходе мы можем получить сомнительный результат, который может не удовлетворять нашим требованиям. Математические операции поддерживают выражения для более удобной работы:
add %number% to %number% и remove %number% from %number% Где первое: добавляет ко второму аргументу - первый аргумент, причем второй аргумент - обязательно переменная, а первый может быть литералом (числом) и переменной (идентификатором).
Второе: делает тоже самое, но вместо увеличения - уменьшает Даже если переменная не задана, но было использовано выражение add, добавляемое число прибавится к нулю и значение запишется в переменную

on load:
  add 64 to {_var}
  broadcast "%{_var}*{_var}%"

Результатом будет всё то же число: 4096

Булев тип данных

Булев тип занимает минимальное количество оперативной памяти, ведь сам он по себе содержит по сути true (1, истина) или false (0, ложь), и используется в основном для проверки чего-либо. Теперь создадим переменную данного типа: set {_var} to true или set {_var} to false

Компилятор – это программа, которая переводит текст, написанный на языке программирования, в набор машинных кодов (то есть в язык, который понимает компьютер)

Интерпретатор – программа, выполняющая интерпретацию. Интерпретация — построчный анализ, обработка и выполнение исходного кода программы или запроса.

Литерал – прямой текст, число

Идентификатор – компонент языка, выражение, переменная

Аргумент – в данном контексте – какие-то данные поступающие в функцию, выражение

Условные операторы

Даже у Нео был выбор

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

if %objects% %условный оператор% %objects%:
  #ваш код

Первый и третий аргумент (%objects%) - это то, что мы собираемся сравнивать, а %условным оператором% выступает критерий по которому мы сравниваем. Под условным оператором подразумеваются знаки, используемые в математических неравенствах, такие как: <, >, = Скрипт позволяет использовать текст вместо символов, и если быть каноничным - лучше использовать именно текст:

> - is greater than 
< - is less than 
= - is 
>= - is greater than or equal to 
<= - is less than or equal to

Это не весь возможный список используемых действий Условие конечно же сработает если критерий внутри него будет удовлетворен, приведем примеры верных и не верных условий (не с точки зрения написания кода):

on load:
  if 1 is 1:
    broadcast "1 is 1"
    #будет выведено '1 is 1'
  if 2 is 1:
    broadcast "2 is 1"
    #условие не сработает, так как оно ложное
  if 2 is greater than 1:
    broadcast "2 > 1"
    #будет выведено '2 > 1'

Очевидно, что помимо литералов, в условии могут быть использованы идентификаторы:

on load:
  set {_var} to 1
  if {_var} is greater than or equal to 1:
    broadcast "%{_var}% is greater than or equal to 1"
    # будет выведено '1 is greater than or equal to 1'

Конструкцию описанную выше, можно расширить с помощью else: что означает ‘иначе’,

if %objects% %логический оператор% %objects%:
  #ваш код
else:
  #ваш код
Блок кода после else будет выполнен в том случае, если условие не верно, например:
on load:
  if 2 is 1:
    broadcast "2 is 1"
    #условие не сработает, так как оно ложное
  else:
    broadcast "2 is not 1"
    #но сработает блок else

Кроме этого, есть так же аналог конструкции swith case из других языков:

on load:
  if 2 is 1:
    broadcast "2 is 1"
    #условие не сработает, так как оно ложное
  else if 2 is 2:
    broadcast "2 is 2"
    #будет выведено '2 is 2'

Конструкция else if что-то вроде проверить еще что-то, если условие выше не верно, причем длина конструкции такого вида не имеет предела, можно бесконечно делать проверки:

on load: 
 set {_var} to 3
 if {_var} is 1:
    broadcast "2 is 1"
    #условие не сработает, так как оно ложное
  else if {_var} is 2:
    broadcast "%{_var}% is 2"
    #условие не сработает, так как оно ложное
  else if {_var} is 3:
    broadcast "%{_var}% is 3"
    #будет выведено '3 is 3'
  #else if ...
  else:
    broadcast "%{_var}% is undefined"
    #сработает только в том случае, если все, что выше не сработало

Но проверка будет завершена раньше, если какое-то из условий истинно. Второй аргумент в условии можно сделать с отрицанием:

on load: 
  set {_var} to 3
  if {_var} is not 1:
    broadcast "%{_var}% is not 1"
    #будет выведено '3 is not 1'

В переводе это что-то типа ‘если {_var} не является 1, то выполнить: …’ Также, помимо not, можно дополнить условие другими булевыми операциями: and и or, что значит и и или соответственно:

on load:
  set {_var} to 3
    if {_var} is (2 or 3):
      broadcast "%{_var}% is 2 or 3"
      #будет выведено, так как 3 это 2 или 3, что удовлетворяет условию
on load:
  set {_var1} to 2
  set {_var2} to 3
  if ({_var1} and {_var2}) is (2 and 3):
    broadcast "%{_var1}% and %{_var2}% is 2 and 3"
    #будет выведено, так как список из {_var1} и {_var2} это 2 и 3, что удовлетворяет условию

Помимо базовых операторов сравнения, в языке присутствуют и другие, список всегда доступен по ссылке: https://docs.skriptlang.org/conditions.html

Мы разберем лишь одно из них: contains, потому что оно ускорят работу со списками и текстом:

Синтакс:

Без отрицания:

if %inventories/texts/objects% contain[(s)] %item types/texts/objects%:
  #ваш код

С отрицанием:

if %inventories/texts/objects% (doesn't|does not|do not|don't) contain %item types/texts/objects%:
  #ваш код

Это может быть использовано при проверке текстовой переменной на содержание определенной последовательности символов:

on load:
  set {_var} to "Hello World!"
  if {_var} contains "Hello":
    broadcast "yes"
    #сработает, так как изначальный текст содержит в себе 'Hello'
Может быть использовано при работе со списком переменных:
on load:
  set {_var1} to 1
  set {_var2} to 5
  if {_var1},{_var2} contains 5:
    broadcast "Yes"
    #сработает, так как одна из двух переменных равняется 5

Не забывайте про производительность, большое количество сложных проверок – ресурсо-затратная операция, процессоры на архитектурах х86 их х64 способны выполнять лишь 1 операцию за такт, то есть если в вашем условии стоит or или and, то это уже как минимум две операции.

При использовании конструкции else if, если вы можете представить в голове поток входящих данных и способны составить процентное соотношение вероятности выпадения каждого варианта, ставьте наиболее частые варианты раньше чем редкие, это сэкономит количество проверок при выполнении кода.

Проверки булевых типов данных быстрее чем числовых, а числовых быстрее чем текстовых.

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

Считаю на этом ваше базовое понимание как устроены условия уже сложилось.

Циклы

И вот мы снова выходим из здания суда…

Иногда возникает ситуация, когда нужно выполнять какие-то строчки кода большое количество раз, чтобы не прописывать это самостоятельно, в языках программирования для этого используются циклы. Циклы существуют даже в языках близким к машинному коду, например в Assembler, в скрипте же есть две конструкции:

while %Условие%:
  #ваш код

и

loop (%number% times|%objects%):
  #ваш код

Рассмотрим первую конструкцию, в дословном переводе это цикл ‘пока’ или ‘до тех пор’, то есть он будет повторяться столько раз, пока условие внутри него не станет верным, например:

set {_a} to 0
while {_a} is less than 5:
  add 1 to {_a}

Данный цикл повторится 5 раз, так как условие - пока {_a} меньше чем 5, а тело цикла - добавить к {_a} единицу Под ‘телом цикла’ подразумевается весь код который находится в этом блоке. Также , достаточно важный термин - итерация цикла Итерация - это ровно 1 повтор какого-либо цикла, далее будем использовать именно этот термин Теперь перейдем к циклу loop, это эдакий скриптовый аналог цикла for из других языков программирования. Чтобы не заходить в тему, которою мы еще не затрагивали, рассмотрим лишь первый вариант этого цикла:

loop %number% times:

Где, %number% - это количество повторений, причем если вы передадите туда переменную, а внутри цикла измените ее значение, цикл будет повторяться столько раз, сколько было указано в значении переменной при его старте. Например:

set {_a} to 0
loop 5 times:
  add 1 to {_a}

По окончанию цикла в переменной {_a} будет число 5

set {_b} to 5
loop {_b} times:
  add 1 to {_b}

По окончанию цикла в переменной {_b} будет значение 10 Думаю тут все предельно ясно, но теперь к тонкостям: Внутри цикла мы можем использовать новые (для нас) выражения:

stop loop 
exit %number% loop
continue 

Первое выражение завершает самый последний цикл (если есть вложенные циклы внутри другого цикла):

set {_a} to 0
while true is true:
  add 1 to {_a}
  if {_a} is greater than 5:
    stop loop

Цикл завершится когда {_a} станет больше чем 5 Второе же наоборот направлено на работу со вложенными циклами, где можно завершить все циклы сразу, просто указав их количество в %number%

Например:

set {_a} to 0
set {_b} to 2
while true is true:
  add 1 to {_a}
  while true is true:
    add {_a} to {_b}
    if {_b} is greater than 20:
      exit 2 loop

Циклы завершатся когда {_b} станет больше 20 Третье выражение позволяет нам пропустить текущую итерацию, без остановки всего цикла

Например:

set {_a} to 0
loop 5 times:
  if {_a} is 3:
    continue
  else:
    broadcast "%{_a}%"
    add 1 to {_a}

В чат будет выведено:

1
2
3
3
3

Если же мы бы поставили stop loop, цикл бы завершился как только {_a} стало бы равно трем.

У цикла loop тоже есть особенные выражения - loop-value Используя это выражение мы получаем значение текущей итерации, пример:

loop 3 times:
  broadcast "%loop-value%"

В чат будет напечатано:

1
2
3

ГЛОССАРИЙ Цикл — любая многократно исполняемая последовательность операций, организованная любым способом. Итерация в цикле — один шаг итерационного, циклического процесса. Тело цикла - это набор действий (операций), которые подразумеваются к повторению раз за разом при работе цикла

Массивы

районы, жилые кварталы, я ухожу, ухожу красиво.

В какой-то момент времени у программистов возникла потребность манипулировать большим количеством данных, перебирать их, где-то складировать, быстро удалять и подобные операции, тогда были созданы массивы (в нашем случае списки)

В прошлых главах, если вы заметили, в типах данных уже мелькало %objects%, %texts%, %numbers% и т.д, так вот отличие от аналогичных типов без s в том, что там были списки. Так в чем же отличия списков от обычных переменных? В ‘::’. Приведем пример обычного списка, который не имеет значений:

{_list::*}

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

set {_list::*} to 2,3,4,5

Теперь в нашем списке 4 значения, это числа от 2 до 5. Используя такой вид присвоения, мы можем легко обращаться к списку при помощи индексирования, элементы будут располагаться начиная от индекса 1, заканчивая их количеством:

broadcast "%{_list::1}%"
broadcast "%{_list::2}%"
broadcast "%{_list::3}%"
broadcast "%{_list::4}%"

Выполнив этот код, в чат будут выведены значения списка, с индексами 1,2,3,4, что соответствует ранее заданным значениям 2,3,4,5 Запомните, ::* - обратиться ко всему списку с его элементами, ::%text% - обратиться к конкретному элементу с его уникальным индексом. Как вы заметили, указано %text%, то есть индекс может быть не только числом, но задавать это уже придется вручную:|

set {_list::first} to 1
set {_list::second} to 2
set {_list::YAebanat} to 5
...

Соответственно, чтобы обращаться к этим элементам, придется указывать именно эти индексы.

Кроме этого, существуют вложенные списки внутри списков:

set {_list::*} to 1,2,3,4
set {_list::1::*} to "Hello world!",1,2,3

Удалив {_list::*} мы также удалим все вложенные списки внутри него:

delete {_list::*}
broadcast "%{_list::1::*}%"

Будет выведено ‘’", так как список пустой. Изучим парочку важных выражений для работы со списками:

add %objects% to %list%

Добавляет указанные значения в указанный список, причем в самый его конец (если были использованы не числовые индексы, то значения добавятся начиная с 1)

НОТИС ТУТ ДАНГЕР

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

indexes of %list%

Возвращает список индексов списка

size of %list%

Возвращает количество элементов списка - число.

Теперь вспомним про циклы из прошлой главы, конкретно loop %objects%: Данная конструкция позволяет нам проитерировать все элементы списка, причем ранее изученное выражение loop-value теперь будет принимать значение текущего элемента итерации, а loop-index будет являться индексом данного элемента списка. Пример:

set {_list::*} to 3,2,1
set {_list::start} to "hello"
loop {_list::*}:
  broadcast "Index: %loop-index% Value: %loop-value%"

В чат будет выведено:

Index: 1 Value: 3
Index: 2 Value: 2
Index: 3 Value: 1
Index: start Value: hello

События

Событийность по матрице судьбы онлайн…

Мы так много говорили про конструкции языка, что вообще забыли про взаимодействие игрока с самим сервером. Исправляем данное недоразумение и представляем вам События, или как их принято называть Events (Эвенты).

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

Список событий всегда можно узнать на странице официальной документации https://docs.skriptlang.org/events.html.

Синтакс события в языке Skript:

on %event%:
  #ваш код

Пример:

on break:
  broadcast "%event-block%"

При ломании какого-либо блока игроком в чат выведется название сломанного блока

Взглянем на синтакс события: [on] [block] (break[ing]|min(e|ing)) [[of] %item types/block datas%]

Исходя из этого мы можем указать какой именно блок должен быть сломан, это аналогично условию внутри этого же события,

пример:

on break of stone:
  broadcast "%event-block%"

on break:
  if event-block is stone:
    broadcast "%event-block%"

Как вы заметили, тут, как и в циклах есть особые выражения, которые представляют какие-либо данные участвующие в событии, в примере выше event-block - это блок который был сломан. В основном почти во всех событиях участвует player - игрок. Все это описано в документации по ссылке выше. Мы могли бы вам это все описать, но событий настолько много, что это будет лишним. Чтобы все это понять вам не нужно быть профессианалом.

Начиная с версии Skript 2.6 события, аналогично событиям в java имеют приоритет.

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

on %event% with priority %priority%:
  #ваш код

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

Список всех приоритетов от вызываемого самым первым и самым последним: LOWEST, LOW, NORMAL, HIGH, HIGHEST, MONITOR

Приведу конкретный пример:

on break with priority LOW:
  broadcast "message 1"
on break with priorty LOWEST:
  broadcast "message 2"

Первым будет выведено ‘message 2’ затем ‘message 1’ Если вы не указываете приоритет события, то его приоритет будет взят из конфига самого плагина

Как отменить, или грубо говоря переписать событие? Для этого есть выражение cancel event

Например:

on break:
  cancel event

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

on break with priority LOWEST:
  cancel event
on break with priority LOW:
  broadcast "Message"

Сообщение не будет выведено потому что данное событие ранее уже было отменено.

Если вы отменили событие, то это еще не значит что код после этой строки не будет выполнен - будет, не будет лишь того, что по приоритету находится ниже чем текущее событие и то, что должно выполнится по стандарту (стандартная функция - имеется ввиду, то что предусмотрено самим ядром, например в событии ломании блока - сломать сам блок)

Мы также можем проверить было-ли отменено какое-либо событие при помощи условия:

on break:
  if event is cancelled:
    broadcast "cancelled"
on break:
  if event is not cancelled:
   broadcast "is not cancelled"

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

ГЛОССАРИЙ

Event - событие, действие

Priority - приоритет, очередность

Макросы

Киллаура, иксрей, флай (нет)

Если вы уже смотрели чей-то код на языке Skript то вы уже наверняка замечали такой блок как Options.

В изучаемом нами языке нету постоянных значений, называемых константами (в дословном переводе постоянная), но есть макросы.

Макросы это какие-либо записи в отведенном для этого месте, которые на моменте обработки интерпретатором будут заменены на какой-либо текст указанный программистом.

То есть это не просто какие-то постоянные значения, это целые строки кода, давайте посмотрим на применение:

options:
  PI: 3.14

on load:
  broadcast "PI is {@PI}"
  broadcast "sin(PI) ~ %sin({@PI})%"

Мы обозначили макрос с именем PI и задали ему значение в виде 3.14 Если вам до сих пор не ясно, как это работает, то вот пояснение с примерами из жизни:

представьте, что вам дали какой-либо текст и вам необходимо по шаблону заменить какие-то слова или фрагменты текста на другой фрагмент или слово, блок options выполняет аналогичную функцию, он ищет перед моментом обработки кода выражения из блока options внутри основного кода и заменяет {@название} на значение этого параметра. Больше примеров для понимания:

options:
  message: "Вы разрушили блок %event-block%"
  block for pay: stone
  money name: руб
  money amount: 50
on break:
  send {@message}
  if event-block is {@block for pay}:
    send "Получено {@money amount} {@money name}"

Если будет сломан любой блок, то игроку будет выведено ‘Вы разрушили блок %имя блока%’, но если сломанный блок - Stone (камень) будет еще написано, что ‘Получено 50 руб’

Вы возможно задаетесь вопросом, почему бы это все не задать в переменные?

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

Функции

Жопа с функцией чайник

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

function НазваниеФункции(Список параметров):
  #ваш код

Функция с возратом каких-либо данных:

function НазваниеФункции(Список параметров) :: ТипВозвращаемогоЗначения:
  #ваш код

Где список параметров - это та информация, которая поступает в нее, базовый вариант для обозначения параметров это:

function НазваниеФункции(parameter1: ТипДанных,parameter2: ТипДанных...):
  #ваш код

Где ТипДанных - это какой тип информации может получить эта функция Кроме этого, можно указать стандартное значение для какого-либо параметра.

function НазваниеФункции(parameter1: number,parameter2: number=10):
  #ваш код

В таком случае, при вызове функции параметр 2 можно не указывать, он самостоятельно примет значение 10. Параметров может быть любое количество, а может не быть вовсе:

function НазваниеФункции():
  #ваш код

или

function НазваниеФункции() :: ТипВозвращаемогоЗначения:
  #ваш код

Параметры внутри блока функции будут являться локальными переменными, существующие только внутри этой функции, например если есть параметр msg: text, то внутри функции значение из этого параметра будет храниться в локальной переменной {_msg} Функции позволяют нам передавать в одном параметре множество значений, а также возвращать множество значений:

function НазваниеФункции(parameter1: numbers,parameter2: numbers) :: numbers:
  #ваш код

Возвращаемый список данных возвращается c нумерными индексами от единицы до кол-ва элементов. Чтобы передать список литералов в параметр - используйте скобки: myFunc((1,2,3,4,5),"text") Функции как глобальные переменные - можно использовать внутри любого скрипта, но она обязательно должна быть определена раньше чем место, где мы будем ее использовать, лучший вариант - создать отдельный файл с функциями, чтобы он по алфавиту был раньше чем файлы с кодом, тогда проблем возникать не будет. Теперь узнаем как использовать написанные функции в коде: У нас есть вот такая определенная в коде функция:

function sendMSG(player: player, msg: text):
  send {_msg} to {_player}

Она банально отправляет текст из параметра 2, игроку в параметре 1
Применение:

on break:
  sendMSG(player,"Вы сломали блок")
При разрушении блока игроку отправится сообщение: Вы сломали блок
Теперь поработаем с параметром, который может принимать список значений, изменим нашу функцию:
function sendMSG(player: player, msg: texts):
  loop {_msg::*}:
    send loop-value to {_player}

В данном случае второй аргумент, так как он указан типом texts будет списком со значениями
Применение:

on break:
  sendMSG(player,("Вы сломали блок","сломайте еще блок"))
Или, аналогичный код без использования литералов в аргументах:
on break:
  set {_text::1} to "Вы сломали блок"
  set {_text::2} to "сломайте еще блок"
  sendMSG(player,{_text::*})

Теперь попробуем написать функцию с каким-либо возвращаемым значением, например функцию для получения куба числа:

function cubeNumber(value: number) :: number:
  return {_value}*{_value}*{_value}

Здесь используется ключевое выражение return %objects% позволяющее нам вернуть результат
Причем код, который будет следовать после выполнения этой строки - не выполнится, в более сложном смысле подпрограмма (функция) передаст управление основному коду, из которого она была вызвана
Применение:

on load:
  set {_cube} to cubeNumber(128)

в переменную {_cube} будет записано число 2097152
Нельзя использовать wait/delay внутри функции с возвратом.
Функции без возвращаемых данных выполняются асинхронно, в отдельном потоке, то есть:

function HelloWorld():
  wait 5 seconds
  broadcast "Hello World!"
on load:
  HelloWorld()
  broadcast "This message first"

Сначала будет выведено ‘This message first’, затем (через 5 секунд) ‘Hello World!’

Игровые команды

Тук-тук я в домике

Создавать команды в Skript очень просто.
Базовый шаблон определения команды следующий:

command /<имя команды> <аргументы>:
  aliases:
  executable by:
  usage:
  description:
  permission:
  permission message:
  cooldown: <timespan>
  cooldown message:
  cooldown bypass:
  cooldown storage: <переменная>
  trigger:
   #ваш код

Все параметры, находящиеся между самой командой и trigger - опциональны (необязательны)\

Разберем каждый раздел

Имя команды - это в сама команда. В имени команды можно использовать любой символ, кроме символа пробела. Если вы используете пробел в имени команды, текст после пробела становится аргументом. Символ косой черты (/) перед именем команды является необязательным (это не означает, что вы можете выполнять команду без косой черты).

Аргументы - это все, что пользователь пишет после команды через пробел. У аргументов может быть тип данных, например Number, Text, Player. Аргументы могут быть обязательными и опциональными, могуть иметь базовое значение, а могут не иметь:
Команда без аргументов

command /mycommand:
  trigger:а
    broadcast "Hello world!"

Команда с обязательными аргументами

command /mycommand <number> <number>:
  trigger:
    broadcast "%arg-1+arg-2%"
    #сложит два числа указанных пользователем через пробел

Команда с необязательными аргументами

command /mycommand [<text>]:
  trigger:
    if arg-1 is set:
      broadcast arg-1
    else:
      broadcast "Hello world!"
    # Выведет указанный текст, если он указан,
    # иначе 'Hello world!'

Команда с базовым значением

command /mycommand [<text="Hello world!">]:
  trigger:
      broadcast arg-1
    # Выведет указанный текст, если он указан,
    # иначе 'Hello world!'

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

[the] last arg[ument][s]
[the] arg[ument][s](-| )%number%
[the] (1st|2nd|3rd|4-90th) arg[ument][s]
[the] arg[ument][s]
[the] %type%( |-)arg[ument][( |-)%number%]
[the] arg[ument]( |-)%type%[( |-)%number%]

Нумерация аргументов происходит в том же порядке, как они указаны в самом описании команды

Aliases - псевдонимы команды; дополнительные имена, при помощи которых она может быть вызвана. Пишутся через запятую на этой же строке.
Пример:

command /mycommand [<text="Hello world!">]:
  aliases: /helloworld,/hello
  trigger:

Executable by - определяет кто может выполнять данную команду, в текущих реалиях это либо игрок, либо консоль Console

command /mycommand [<text="Hello world!">]:
  executable by: console
  trigger:

Player

command /mycommand [<text="Hello world!">]:
  executable by: player
  trigger:

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

command /mycommand <text>:
  usage: Используйте: /mycommand <текст для отправки>
  trigger:

Description - описание команды, выводится при отправке команды /help, либо может быть получено при помощи Bukkit API другими плагинами.

command /mycommand <text>:
  description: отправляет всем указанное сообщение
  trigger:

Permission - право необходимое игроку для выполнения команды.

command /mycommand <text>:
  permission: mycommand.send
  trigger:

Permission message - сообщение получаемое отправителем, если у него отсутствует право на выполнение команды.

command /mycommand <text>:
  permission message: Недостаточно прав для выполнения данной команды
  trigger:

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

command /mycommand <text>:
  cooldown: 1 hour
  trigger:

Перезарядку можно отменить, используя выражения:

(cancel|ignore) [the] [current] [command] cooldown 
un(cancel|ignore) [the] [current] [command] cooldown

Первое отменяет перезарядку команды после ее выполнения, вторая иначе, принудительно ее устанавливает.

Cooldown message - сообщение, которое будет написано отправителю, если время восстановления команды еще не прошло. Вы можете использовать выражения elapsed time и remaining time для получения времени последнего применения команды и времени до окончания перезарядки соответственно.

Cooldown bypass - право, при наличии которого у отправителя команда не будет уходить на перезарядку.

command /mycommand <text>:
  cooldown: 1 hour
  cooldown bypass: hello.bypass
  trigger:

Cooldown storage - тут указывается переменная для сохранения информации о перезарядки. Если ее указать, то перезарядка будет сохранятся даже после выключения сервера.

command /mycommand <text>:
  cooldown: 1 hour
  cooldown bypass: hello.bypass
  cooldown storage: {mycommand::%player's uuid%}
  trigger:

Trigger - в этом блоке непосредственно находится выполняемый командой код.

Примеры команд:

command /speed [<player>] [<number>]:
  permission: sk.speed
  permission message: &c→ &fНедостаточно прав для выполнения данной команды
  usage: &c→ &fИспользуйте: &c/speed &f<&cник игрока&f> &f<&cскорость&f>
  trigger:
    if (arg-1 is set):
      if (sender have permission "sk.speed.any"):
        set {_player} to arg-1        
        if (arg-2 is set):
          set {_speed} to arg-2
        else:
          set {_speed} to 1
        set {_player}'s walking speed to {_speed}/5        
        
        send "&a→ &fСкорость вашего передвижения установлена на &a%{_speed}%" to arg-1
        if {_player} is not player:
          send "&a→ &fСкорость передвижения &a%{_player}% &fустановлена на &a%{_speed}%"
        stop
    if (sender is player):
      if (arg-2 is set):
        set {_speed} to arg-2
      else:
        set {_speed} to 1
      set player's walking speed to {_speed}/5
      send "&a→ &fСкорость вашего передвижения установлена на &a%{_speed}%"
    else:
      send "&c→ &fИспользуйте: &c/speed &f<&cник игрока&f> &f<&cскорость&f>"
command /heal [<player>] [<text>]:
  permission: sk.heal
  permission message: &c→ &fНедостаточно прав для выполнения данной команды
  cooldown: 5 minutes
  cooldown message: &c→ &fПодождите перед следующим выполнением команды
  cooldown bypass: sk.heal.bypass
  trigger:
    if (arg-1 is set):
      if sender have permission "sk.heal.any":
        heal arg-1
        feed arg-1
        set {_player} to arg-1
        send "&a→ &fИгрок &a%{_player}%&f был исцелен"
        stop
    if (sender is player):
      heal player
      feed player
      send "&a→ &fВы были исцелены"
    else:
      send "&c→ &fИспользуйте: &c/heal &f<&cник игрока&f>"