Приветствую всех читателей Хабра!

Расскажу предысторию создания данного поста

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

Как можно видеть в самом видео, человек, нажимая на кнопку в Майнкрафте, включает у себя лампу в реальной жизни.

Круто! Но как это работает?

После прочтения комментариев (и ответа самого создателя видео) всё встало на свои места:

Комментарий создателя
Комментарий создателя

Перевод:

Прежде чем я начну объяснять, как это работает, давайте назовем устройства, которые участвуют в процессе:
MCPC - компьютер ComputerCraft в Minecraft
MyServ - мой IRL-сервер
PowSwitch - интеллектуальный выключатель питания, подключенный к лампе

Lamp подключен к коммутатору питания Wi-Fi (PowSwitch). PowSwitch подключается к Интернету и имеет API для управления им.

Компьютеры ComputerCraft могут выполнять простые HTTP-веб-запросы. Они не способны выполнять запросы / вызовы API.

Вот тут-то и появляется MyServ - он реагирует на HTTP-веб-запрос MCPC и "переводит" его в вызов API PowSwitch.

Проще говоря - когда вы нажимаете кнопку в Minecraft, MCPC пытается загрузить случайные данные из MyServ. MyServ обнаруживает запрос MCPC и реагирует отправкой команды через API PowSwitch. Эта команда API заставляет PowSwitch переключать питание и, следовательно, включать лампу enitre.

"Ну ничего себе!" - подумал я - "Это сколько же возможностей даёт один единственный мод!".
И... так оно и есть. Через модификацию и HTTP можно связаться с сервером и через него передавать данные!

Однако обо всём по порядку.

Сейчас я собираюсь рассказать о том, как конкретно можно это сделать на примере отправки сообщений через Telegram-бота через фреймворк Express.js (на базе Node).

Опишем все "инструменты" по порядку (Если вдруг чего-то из этого не знаете, не волнуйтесь! В процессе объяснения всё будет понятно, а вместо node.js Вы можете использовать любой инструмент, позволяющий прописывать API - в данном случае, это большой роли не играет):

  • Майнкрафт мод - "Computer Craft (CC) Tweaked".

  • Версия Майнкрафта - 1.20.1 (Forge).

  • Туннель ("зеркало" нашего локального сервера под уникальным доменом + созданным с включенным SSL (https), откуда будут идти запросы) - ngrok.

  • Обращения к телеграм-боту - node-telegram-bot-api.

  • API на Node + Express.js.

Важно учитывать то, что подчёркнуто!


Возможно, у внимательных читателей возникнет вполне резонный вопрос

Почему создатель того видео использует Computer Craft, а мы используем именно CC Tweaked?

Вопрос хороший! На него есть ответы.

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

CC Tweaked - это форк оригинального Computer Craft.

Всё, что применимо к Computer Craft - применимо к CC Tweaked.

 Репозиторий CC Tweaked на момент 14.12.2024
Репозиторий CC Tweaked на момент 14.12.2024

К сожалению, то же самое нельзя сказать про оригинальный Computer Craft - его последняя версия была выпущена в 2017 году на версию 1.12.2, а репозиторий обновляется раз в несколько лет ради каких-то совсем незначительных изменений.

Коммиты репозитория Computer Craft
Коммиты репозитория Computer Craft

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

Скрин с официального сайта www.computercraft.info
Скрин с официального сайта www.computercraft.info

- Во-вторых, протокол HTTP живёт своей жизнью на оригинальном моде.

Скрин с версии 1.8.9 (стабильной) Computer Craft при попытках обратиться по http к "www.google.com" (что используется в туториалах) и к собственному ngrok-туннелю.
Скрин с версии 1.8.9 (стабильной) Computer Craft при попытках обратиться по http к "www.google.com" (что используется в туториалах) и к собственному ngrok-туннелю.

При попытках взаимодействия с http постоянно вылазят ошибки, что "невозможно получить данные с сервера" - грубо говоря, nil. На оригинальном моде вообще В ПРИНЦИПЕ при обращении к любому эндпоинту будет возвращаться ниловое значение.

Забавно, что в поиске решений проблемы люди рекомендуют поменять значения в конфигах мода для работы с http. (Представили моё лицо, когда оказалось, что там уже прописано ровно то, что и предлагают при решении этой ошибки?)

Предложение решения на Computer Craft
Предложение решения на Computer Craft
Буквально тот же самый запрос без замен конфига, но уже на CC Tweaked (Правильный респонс)
Буквально тот же самый запрос без замен конфига, но уже на CC Tweaked (Правильный респонс)

Вероятно, CC Tweaked, в связи с рефакторингом, исправлением старых ошибок и привнесением нового функционала в мод (напомню, последняя версия оригинала - 2017 год), настроен на более новую отправку запросов по http, поэтому Computer Craft в этом плане выглядит проигрышно, нежели его форк.

Если уяснить это, вопросов на данный счёт возникать не должно.
Ну и, вероятно, автор сразу имел в виду конкретно Tweaked, т.к. оригинальное видео вышло всего-лишь 4 года назад, но это лишь мелочи.


Приступим к созданию!

Для начала нужно подготовить всё нужное для мода:

  1. Скачать мод;

  2. Создать бота;

  3. Зарегистрироваться и установить ngrok.

  4. Создать наш node.js проект.

  5. Включить ngrok и создать туннель между локалхостом.

Начнём с первого и самого простого пункта - мод

Переходим по ссылке (у вас сразу должна начаться загрузка на версию Forge 1.20.1).
После скачивания копируем из папки загрузки и переходим в путь - "C:\Users\Пользователь\AppData\Roaming\.minecraft\mods" - куда и вставляем наш мод.

Конгратулатионс - мод у нас в кармане.

Двигаемся дальше - создание бота

Здесь всё тоже не очень сложно.
Заходим в Telegram и ищем BotFather.

Начинаем с ним диалог, после чего создаём нового бота:

Создание бота в BotFather
Создание бота в BotFather
Любопытненько.

Не пытайтесь использовать этого бота.
На момент выкладки поста его уже не существует.

Далее просто начинаем переписку с ним:

/start у нашего бота
/start у нашего бота

"HTTP API token", который он нам выдал, понадобится при создании нашего бота с помощью Ноды.

Третий пункт - ngrok.

Если Вы из России (как я), то просто так Вам на этой платформе не зарегистрироваться:

Your location was determined using your IP address
Your location was determined using your IP address

Однако это нас не остановит.

Качайте VPN (любой, который нравится и работает), используйте почту НЕ MAIL.RU и смело снова пытайтесь зарегистрироваться - нажимайте на кнопочку "Sign Up".
Я обычно захожу через GitHub, но можете зарегистрироваться прямо через почту или Гугл-аккаунт.

Далее нас встречает приветственная страничка.

Welcome!
Welcome!

Ngrok требует VPN только на этапе регистрации.
Далее он не понадобится - можете его отключить.

Windows окошко
Windows окошко
  1. Переходим вниз к пункту "Connect" - выбираем "Download" и качаем для своей системы (64\32 бита).

  2. Нам скачался архив с exe-файлом. Просто перекидываем на своё рабочее место и открываем.

  3. После открытия вставляем всю команду с нашим "add-authtoken" и нажимаем Enter.

  4. Радуемся жизни!

Четвёртый пункт - создание проекта.

В моём случае, Express.js (Однако Вы можете писать, на чём душе будет угодно).

Всё как всегда.

  1. Инициализируете проект (npm init -y);

  2. Скачиваете пакеты express, nodemon (для реал-тайм перезагрузок) и node-telegram-bot-api (npm i express nodemon node-telegram-bot-api).

Создание проекта
Создание проекта

После чего создаёте index.js в той же директории и меняете скрипты запуска в package.json, вставляя туда обращение к nodemon при дев-сервере и к node при старте билда.
Не забываем также указать, что мы используем ES-модули, чтобы использовать импорты как в обычном проекте на js.

package.json
package.json

Инициализируем Express в нашем index.js файле:

Код express.js
Код express.js

И далее в терминале просто запускаем наш код через npm run dev:

Запуск проекта
Запуск проекта

Мы запустили наш проект, однако этого недостаточно для Tweaked. Мод не может слушать localhost, поэтому мы должны, скажем, отзеркалить его на какой-то рабочий домен.

Последнее - туннель ngrok

Чувствую, что немного устали)
Не волнуйтесь, данный шаг станет последним, после чего зайдём в сам Майнкрафт.

Чтобы создать туннель с проектом нужно воспользоваться командой ngrok http 3000. Где "3000" - это порт, который мы задали через Express (app.listen(3000)).
Грубо говоря, мы говорим ngrok'у: "Чувак, создай нам туннель через http по порту, который мы дали локалхосту":

Создаём туннель
Создаём туннель

После нажатия Enter у нас открывается окошко, в котором мы можем взять наш URL, по которому сможем обращаться в Майнкрафте (https://be84-95-106-165-71.ngrok-free.app):

Мы создали туннель!
Мы создали туннель!

Поздравляю! Мы готовы двигаться дальше! А дальше только интереснее

Мы подошли к тому, чтобы запустить наш Майнкрафт

Я НЕ буду углубляться в сам CC Tweaked, ровно как и в язык, на котором пишется код в нём - Lua.

Моя задача - лишь познакомить с ним и показать Вам, насколько просто использование HTTP через этот мод.

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

https://www.youtube.com/watch?v=7HrWg_P7uKk&list=PL3A9AC22762B7829E

Запускаем на версии Forge 1.20.1 (Как было обговорено раньше) и создаём карту.

Инвентарь в творческом режиме
Инвентарь в творческом режиме

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

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

Продвинутый компьютер в инвентаре
Продвинутый компьютер в инвентаре

Далее открываем его, после чего вводим команду edit (название файла).lua.
Запоминаем, что "edit" в Computer Craft - это открытие файла для изменения.

edit index.lua
edit index.lua

Нам открывается пустое окошко, которое является своего рода блокнотом, куда мы можем вводить наш код, а по нажатию ctrl, и выбрав "Run" стрелочками слева снизу, дополнительно нажав Enter, запустить (выйти из этого режима можно также нажав ctrl):

Блокнот CC Tweaked
Блокнот CC Tweaked

Переменные здесь создаются без объявления каких-либо типов или даже ключевых слов. Просто какому-то названию переменной присваиваем какое-то значение (Пайтонисты радуются).

Подобной переменной станет наш URL, который нам выплюнул ngrok. Просто выписываем как отдельную константу, чтобы затем воспользоваться:

NODE_ENPOINT
NODE_ENPOINT

Теперь мы даже можем обратиться через http и сделать какой-то демо-запрос.

Для этого у нас есть глобальный класс "http", через который мы можем создать условный GET-запрос.
Вывести результат в консоль мы можем с помощью функции print() (Пайтонисты радуются).

Наравне с GET мы также можем сделать POST-запрос. Хедеры для него указываются вторым аргументом после эндпоинта.

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

Полная таблица методов для http в CC Tweaked выглядит так:

Таблица методов http
Таблица методов http

Переходим к написанию кода на JavaScript

Суть программы

Суть нашей программы будет заключаться в том, что у каждой переписки есть свой id (так называемый "chat id"). Мы будем следить за его наличием в боте. Если таковой имеется, будем отправлять туда сообщения до тех пор, пока пользователь нажимает на кнопку, которая подведена редстоуном. Если новый пользователь введёт сообщение в него, то бот переключится на нового пользователя, и при "накликивании" в Майнкрафте сообщения будут идти на него. Так будет до тех пор, пока новый пользователь не заблокирует нашего бота. Если всё же произойдёт блок, но в Майнкрафте произошёл ещё один клик на кнопку, то мы шлём ошибку, а затем снова (с нуля всего цикла) запрашиваем написать боту сообщение.

Мне подсказали, что подобного бота стоит назвать "Бот-ждун".
Ради бога, думаю, правда можно назвать так)

Подключаемся к боту

Сначала, чтобы наш бот работал, нам нужен его HTTP API token, который так к стати нам дал BotFather ещё в самом начале.

Далее нам нужно инициализировать бота через класс из библиотеки, указав также polling (Грубо говоря, мы будем следить за тем, что пришло к нашему боту в реальном времени).

Давайте для теста также укажем боту, чтобы на сообщение он нам отвечал тем же сообщением (Подобные боты называются "Эхо-ботами").
Перед этим давайте выведем то, что бот даёт при ответе на то, что мы написали ему какое-то сообщение. Для этого воспользуемся методом ".on('message', () => {})" и вызовем console.log() с ответом на событие.

Ответ на событие отправки сообщение
Ответ на событие отправки сообщение

Видим, что ответом является большой объект, из которого нам на данный момент нужно вытянуть текст - ключ "text", а также айди чата (ключ "chat.id"), по которому нужно отправить сообщение.

Чтобы создать «эхо» просто воспользуемся методом ".sendMessage(msg.chat.id, msg.text)".

Результат работы
Результат работы

Как видим - всё работает!

Пишем Express.js

Далее мы напишем небольшое API.

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

Для начала создадим обращение к нашему Экспресс'у через GET-запрос.

app.get("/", function (request, response) { response.send("Welcome") })

"Welcome"
"Welcome"

На основе чего зададим следующую логику:

  1. На глобальном уровне пропишем мутируемую переменную chatId, которой будет являться чат, на который в данный момент будут слаться сообщения.

  2. В теле callback'а запроса будем смотреть, если наш чат не нулевой.
    Если такое условие соблюдается, то мы выполняем отправку сообщения боту, а также обрабатываем этот промис на наличие reject'а (Коим и является проверкой на блок у пользователя. Как раз таки в .catch() мы и будем его обнулять, ожидая нового айди).
    Если условие не соблюдается, то мы просто возвращаем, что хотим от пользователя ввести какое-то сообщение.

  3. Дополним наше ".on('message')" присваиванием нового значения для chatId, а также отправкой его через бота.

  4. Также пропишем функцию "sendResponse", которая будет возвращать void - отправку на запрос (грубо говоря, вывод в консоль в Майнкрафте) функции со строкой, которая разделяется с помощью "\n", а также трёх сплошных линий и принимать два аргумента: саму функцию для отправки и строчку, которую мы должны отправить.

  5. Важной частью всего колбэка является наличие хедера ngrok-skip-browser-warning .
    Видите ли, когда Вы переходите по URL, который Вам дал ngrok, то вначале мы видим приветственное "Хеллоу!" от нашего дорогого туннеля.

    You are about to visit ngrok
    You are about to visit ngrok

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

  6. Наш хедер, который мы поставим с помощью "response.set('ngrok-skip-browser-warning', 'skip-browser-warning')" будет важной частью правильной работы.

    Результат кода по итогу должен выглядеть так:

    Код проекта
    Код проекта

Или (для копирования):

import Express from 'express';
import TelegramBot from "node-telegram-bot-api";
const app = Express();
const TOKEN = "7876670462:AAHVTSEIoblDhauy1x0U2fQG9lT-LoqKLvI";

const bot = new TelegramBot(TOKEN, {polling: true});
let chatId = 0;
const sendResponse = (respFoo, str) => {
return respFoo.send(str + '\n ---');
}

app.get("/", function (request, response) {
response.set('ngrok-skip-browser-warning', 'skip-browser-warning');
if (chatId > 0) {
bot.sendMessage(chatId, `Minecraft program was launched. Message was sent on chat id - "${chatId}"`).then(() => {
sendResponse(response, `Message sent. Current chat id - "${chatId}"`);
}).catch(() => {
sendResponse(response, `Oops! Something went wrong with chat id "${chatId}". Check, if your bot is not blocked and type it any message to set new chat id!`);
chatId = 0;
})
} else {
        sendResponse(response, `You need to pass a chat id to the bot. Pass it first and come back to send a message!`);
}
});

bot.on('message', (msg) => {

bot.sendMessage(msg.chat.id, `Current chat id for sending messages is ${msg.chat.id}`);

chatId = msg.chat.id;
})
app.listen(3000);

Также необходимо указать ".readAll()" при GET в CC Tweaked. Он позволяет прочитать всё содержимое файла правильно и показать нам именно строчку, а не таблицу ("table").

readAll()
readAll()

Письмо счастья

И теперь, если при запуске программы (ctrl + Enter и выбрать "Run") Вам выдался текст в Майнкрафте, а при написании боту вернулся ответ "Message sent..." - поздравляю! Ваш код работает верно.

Для полноты картины давайте выведем наш результат в монитор

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

Давайте поставим справа четыре блока продвинутого монитора и выведем на него сообщение.

Вначале построим прямо справа от нашего компьютера 4 блока монитора (блоки расширяют друг друга, отчего превращаются в один большой монитор).

Затем нужно обратиться к монитору. Чтобы это сделать, нужно выйти из программы - (ctrl + Exit + Enter), после чего прописать ключевое слову "monitor", указать, в каком направлении от компьютера стоит монитор ("right" / "left" / "top" / т.д.) и написать название программы для вывода. Всё просто!

Прописываем обращение к монитору
Прописываем обращение к монитору
Наш рабочий монитор
Наш рабочий монитор

Осталось доделать совсем немного, чтобы назвать наши планы завершёнными ;)

Взаимодействие с кнопкой в Майнкрафте

Computer Craft из коробки позволяет взаимодействовать с редстоуном. Для этого у него на глобальном уровне прописан класс «rs» (Акроним — «RedStone»).

Красный камень должен взаимодействовать с компьютером. При подведении одного к другому, а также при последующем взаимодействии через команды «вкл.»/»выкл.» можно использовать большое количество методов.

Методы взаимодействия с красным камнем (Input / Output)
Методы взаимодействия с красным камнем (Input / Output)

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

В чём основная проблема?

".getInput()" НЕ слушает ввод через редстоун постоянно. Его главная задача заключается в том, чтобы лишь один раз посмотреть наличие и вывести какой-то ответ.
Поэтому было решено сделать троттлинг на прослушивание событий кнопки.


Что такое троттлинг?

Говоря грубо, мы раз в какое-то время будем смотреть изменение состояния чего-либо. В нашем случае, будет воспроизводиться функция "sleep()" с циклом while. Данная функция не даёт потоку программы двигаться, пока не пройдёт определённый промежуток времени, который был указан в параметры этой функции.

Давайте напишем этот цикл:

Троттлинг нажатия кнопки
Троттлинг нажатия кнопки
Компьютер с красным камнем
Компьютер с красным камнем

И не забываем подвести строго слева (как прописано в направлении - "left") кнопку с красным камнем.

Далее делаем всё ту же процедуру:

  1. "monitor right index.lua";

  2. Затем нажимаете на кнопочку;

  3. Пишет ответ - вам приходит в Telegram уведомление!

Эпилог

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

Попробуйте и Вы сделать что‑то интересное!

Буду очень рад узнать, что моё объяснение кому‑то хотя бы минимально в этом плане помогло).

Также буду очень рад услышать, что меня можно где‑то поправить.
Если есть какие‑то вопросы по коду, по содержанию — пишите, на всё постараюсь ответить.

Всем добра!

Комментарии (3)


  1. TheDiVaZo
    15.12.2024 20:29

    Теперь у меня есть причина играть в Майнкрафт


  1. Classic_Fungus
    15.12.2024 20:29

    Чёт перемудрёно как-то. Можно же за 10 минут накидать в eclipse мод, который шлёт любые запросы куда и когда надо, кинуть в папку с модами и всё. А дальше связывать что угодно с чем угодно. У меня так уровень света меняется в зависимости от освещения в игре


    1. kitten20 Автор
      15.12.2024 20:29

      Здравствуйте!

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

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

      Если напишете свой пост касательно http в Майнкрафте, то буду только рад поставить свой аппрув и опробовать у себя! :)