[Мессенджеры, Программирование, Разработка игр, Логические игры] Настольная игра в Telegram с разоблачением

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
11-Июл-2021 23:30

при помощи Crystal, Lucky, Tourmaline и Telegram Bot Gaming PlatformФизикам можно сразу в репу.
Как известно(не многим), программист, хотя бы раз в жизни должен – поломать прод и выхватить за это, починить его, а на досуге построить баню и написать игру.
Успешно выполнив первые пункты пришла пора перейти к последнему из них, чтобы пить заслуженное пиво в бане, ни на что уже более не отвлекаясь.
Просто писать игру достаточно скучно и как и миллиард авторов до меня, я решил сделать такую игру – в которую интересно будет сыграть, хотя бы мне самому и не меньше двух, а то и трёх раз.
К моменту принятия решения, закончились новогодние выходные, друзья мои разъехались и я подумал:
  • для усложнения задачи неплохо было бы воплотить эту самую игру в телеге. 
  • игра должна многопользовательской - как минимум для пары человек.
  • и почему бы не нарды - мы с удовольствием в них рубимся время от времени?
Время на проект (для борьбы с прокрастинацией) выделил себе ровно месяц. Так появилась задача и сроки.
И если, для ее решения, мне нужны инструменты, то почему бы не взять какой нибудь новенький, сверкающий молоток моей мечты, который я давно хотел применить но не было повода взять его в руки?Итак – Crystal поскольку, я решил, что он прекрасен, удобен и быстр. (спойлер: да, так и есть)Копание в Telegram Bot Gaming Platform несколько разочаровало. Довольно унылые примеры с HTML+JS на тему: нажми кнопку быстро/вовремя или считай в уме быстрее всех, не вдохновляли. Копание в исходниках тележки дало некоторое понимание процесса взаимодействия ее с игрой. Документация на эту тему – довольно мутная. Сценарий взаимодействия получился такой – телеграмм кидает уведомление, о том что пользователь нажал  кнопку «Играть в ...» под сообщением с игрой. В ответ нужно отправить уникальную ссылку которую телеграмм откроет для пользователя – и да начнется битва)
Конечно я рассчитывал на что нибудь большее, вроде отправки сообщений через сервер tg для какой то коммуникации между участниками игры, но не случилось. Печально, зато можно поковырять websockets.Что понадобится?
  • endpoint который будет обрабатывать сообщения телеги
  • еще (как минимум один) который собственно и будет отдавать игровую доску
  • нечто, имеющее роутер для обработки запросов, средства собрать html страницу и возможность создавать/хранить/доставать активную игру в базе. Словом небольшой framework который избавит меня от рутины и освободит для высокого)
Выбор мой пал на Lucky (на тот момент версии 0.24) по следующим причинам: 
  • подход создателей и философия проекта
  • хорошая документация
  • механизм передачи параметров от контроллера к визуализации
  • наличие готовых json api контроллеров нужных для api телеги
  • хороший роутер и хелперы к нему
  • более менее приемлемый интерфейс для работы с БД
  • наличие готового деплоя на Heroku  
  • всё лишнее можно отключить
  • всё отсутствующее дописать
Создание игровой доски в примитивном виде.
Выглядело примерно такДоска была нарисована в Inkscape и содержит несколько слоев. Сама доска, фишки, кости, слой сообщений. В процессе конечно были сделаны всевозможные ошибки относительно размещения начала системы координат, что важно для использования transform rotate преобразований в SVG. В приложении, доска представляет из себя SVG, – генерируемый кодом на Crystal при помощи Lucky конечно. Большую часть нудной работы сделал converting HTML to Lucky methods, (в него я загнал svg из Inkscape) остается только убрать повторения и разбить структуру svg на логические элементы с которыми будет удобно работать.
src/pages/active_games/game_page.crВ результате из 50kb SVG получилось 2-3 сотни строк на Crystal лежащие в соответствующей page. Время на вывод такой страницы без полезной нагрузки на моем буке измеряется µs. Я не стал разносить всё по компонентам исключительно экономя время. Многие возможности Crystal были просто не использованы. На тот момент степень понимания языка ещё не давала мне использовать все его преимущества. К концу проекта глядя на этот код я понимал, что многое можно сделать на много, на много красивее и удобнее. Логика игры, правила и ограничения.Теперь когда есть на чем играть, реализую логику – все правила и состояния игры, в одном классе Game, лежит в src/models/game.cr. Примерно 600 строк. Проследить боль и страдания можно в тестах к этому классу до теста 399 строки когда оно смогло играть с собой до победы. Правила брал здесьСовмещение логики и визуализации игрыДальше я начал натягивать сову на глобус. Совмещать логику и визуализацию, цель: отображать любое сохраненное состояние игры на доске. После победы(сова сопротивлялась как могла) я двинул дальше, Надо добавить управление. Детектировать, что ход сделан и отправлять его на сервер в соответствующий action.
Это удобнее было сделать на JS. Я не фанат JS, но когда надо – тогда надо.
Lucky уже настроен для использования всей этой лабуды с Webpack и.т.д. По умолчанию подключены небольшие и довольно полезные Turbolinks и rails-ujs. Выпиливать их не стал. JS в проекте Lucky лежит в src/js/app.js  Управление простое, выбираем кость и фишку которой ходим либо наоборот и отправляем ход на сервер, позднее добавил возможность ходить выбирая фишку и место назначения.Доверять полученному со стороны клиента (без должной проверки) нехорошо. Поэтому, работает только логика на сервере в классе Game. Состояние игры всегда корректно, можно спокойно выйти в другой чат и ответить на сообщение, и без проблем снова открыть игру на том же самом месте. Смычка города с селом, происходит в этом action. По сути это один большой case связывающий действия пользователя с состоянием игры. Если изменение было игрок увидит его после выполнения перенаправления на action отображения игровой доски.
На этом же этапе я подключил Turbolinks и rails-ujs для плавного апдейта страницы.Настало время подключать Telegram.Чтобы тестировать игру в связке с телеграмм нужен сервер с IP или доменом и соответствующим SSL сертификатом для подключения Webhooks через который телеграмм будет слать updates приложению. Сервис размещаю на Heroku. Процесс настройки деплоя сводится к нескольким тривиальным командам, после прочтения главы документации Lucky на эту тему. Heroku работает с телеграмм без возни с сертификатами сразу из коробки. Режим бота для доставки игры выбрал inline mode как наиболее прогрессивный и безопасный.Быстро набросал клиент для взаимодействия с api телеграмм, пара часов отладки и всё заработало. После чего, естественно, обнаружил прекрасный шард реализующий работу с api телеграмм для Crystal и имя ему Tourmaline. У него был всего один недостаток он не умел игры, я это поправил и автор оперативно принял изменения. Немного перетряхнул код и встроил бот уже через Tourmaline.Схема работы приложения с api телеграмм подробно:
  • создание игрового бота, всё стандартно через @BotFather
  • action в Lucky который будет принимать updates от телеграмм. 
  • прописать url этого action с помощью setwebhook в телеграмм api. (Tourmaline позволяет динамически устанавливать webhook, но я предпочел самостоятельно контролировать этот процесс)
  • при регистрации игры выдается ссылка вида game link (e.g., t.me/bot?game=game) в моем случае  http://t.me/tavla_best_bot?game=tavla. Для начала, достаточно этой ссылки чтобы поделится игрой. Клик на нее предложит выбрать чат, куда будет отправлено сообщение с игрой.
  • Под сообщением, по умолчанию будет одна обязательная кнопка «Играть в…». Клик пользователем по этой кнопке, отправляет на action (привязанный в пункте 3 к Webhook) структуру Update c вложенной в поле callback_query еще одной структурой с оригинальным названием CallbackQuery. Из нее берется поле game_short_name – имя вызываемой игры (если у нас ссылка вида http://t.me/tavla_best_bot?game=tavla  значение должно быть «tavla») и второе поле callback_query.id нужно передать обратно в answerCallbackQuery, чтобы телега понимала на что отвечаем.
    Tourmaline активно  использует аннотации, что очень удобно в написании бота, но не всегда удобно искать где отработает соответствующая функция. Из action апдейт прилетит сюда.

Структура UpdateВ соответствии с 5 пунктом соглашения при создании игрового бота не получится хранить куки и создавать сессии – поэтому все нужные параметры должны быть в url который генерирую в ответе. Фактически это идентификация игры и пользователя по динамическому url. Проверять такие url надо самостоятельно, поэтому использую helper и mixin.Если всё прошло успешно, телеграмм клиент открывает в браузере встроенном или внешнем переданный ему в answerCallbackQuery url. Параметры user сохраняются в базу при необходимости. Я использую в проекте Postgres скорее по привычке и для потестить ORM Avram, чем по необходимости, а вообще, вполне хватило бы Redis. Выглядит url так (разделение по двойному тире): /active_games/1eae03de19d1e909207c6192baff500771e0cbeb--AgzzzzzzzzzzzzzzUWfH7r74--321232123--5345353343 Первая часть подпись, генерируется из остальных параметров и некоего salt. Вторая часть inline_mesage_id. Третья и четвертая id пользователя телеграмм. Четвертая часть не обязательна, в этом случае за второго игрока станет действовать бот. Идентификаторinline_mesage_id однозначно определяет сообщение с игрой по которому сделан клик, user_idтого кто кликал.На этом работа телеги пока заканчивается и я возвращаюсь к игровой странице.Получив запрос get c url и проверив его валидность смотрю есть ли в базе такая игра. Если есть отображаю ее, если нет - сначала создаю.Общая логика такая (здесь) –  если два человека нажали в чате на одном сообщение кнопку играть, до того как игра начата одним из них, игра будет человек – человек, в противном случае человек – бот. Естественно схему можно менять как угодно, вплоть до рейтинга игроков и поиска активных в данный момент соперников не имеющих общих чатов.Последние штрихиДобавил в middleware Lucky websocket обработчик для доставки обновлений.  Когда противник сделал ход, игра обновит и доску второго игрока. На тот момент ws actions в Lucky был только в виде пары коммитов в экспериментальной ветке, но это всё равно было немного не то, что мне нужно. 
Отладка websocketТут пришлось повозится из за чудесатой поддержки websocket в Heroku, а точнее на бесплатном инстансе.
Он тупо рвет соединение при неактивности сокета. Пришлось добавить пинг от клиента через подобранный эмпирическим путем интервал и отправку мусорного сообщения в ответ. 
Вызов бота через inline запросДобавил боту возможность отправлять игру через inline запрос. Для этого в строке чата набираем имя бота @tavla_best_bot в ответ появится подсказка с игрой на которую нужно кликнуть.
Добавил меню c кнопками запуска игры, для комплекта если кто то запустит бот напрямую. 
Добавил отправку набранных игровых очков в телеграмм.   
На этом процесс можно было считать законченным. Оставшееся время я потратил на визуальщину и вкусовщину. Frontend не является моей специализацией, поэтому прошу отнестись снисходительно. Многое наверняка можно было сделать красивее и проще. Проблема оставшаяся нерешенной из скупердяйства принципиальных соображений – вертикальный режим в каких то (не всех) Iphone/Ipad, – ломает верстку. Я не пользуюсь этими девайсами, а реально бесплатных тестовых сред для разработчика я не нашел. Если кто то пофиксит буду рад.PsКод выложен в том виде к котором был на момент окончания времени проекта, он полностью рабочий, следуя инструкции в Readme можно поднять свой инстанс игры, естественно бот тоже нужен свой. Мест приложения рук, еще довольно много, например добавить боту игры ума. Возможно со временем я сделаю порт TDBackGammon и бот будет красавчик, но пока времени на это нет. Всего на проект я потратил почти 75 часов за отведенные 30 дней, в среднем примерно по 2.5 часа в день, самый длительный непрерывный интервал 9.5 часов.PpsЕще момент который касается бесплатных инстансов Heroku, они засыпают при неактивности, поэтому, отклик которого телеграмм ждет от приложения очень быстро, может не успеть с первого раза. Чтобы избежать подобной ситуации я дергаю инстанс снаружи HEAD запросом 1 раз в минуту, при таком подходе времени бесплатной активности вполне хватает на месяц для всех, кто сейчас играет в tavla. Надеюсь хабраэффекта не случится, и я и дальше смогу спокойно рубиться в нее за завтраком:)Моя искренняя благодарность всем, кто делает и помогает делать Crystal и его экосистему. Мне очень понравился Crystal. Я сделал для себя определённые выводы относительно будущего этого языка и постараюсь сделать на нём еще несколько проектов, не важно по работе или ради собственного удовольствия. Спасибо всем, кто это осилил, успехов и удачных проектов!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_messendzhery (Мессенджеры), #_programmirovanie (Программирование), #_razrabotka_igr (Разработка игр), #_logicheskie_igry (Логические игры), #_crystal, #_lucky, #_tourmaline, #_telegram, #_telegram_bot, #_nardy. (нарды.), #_tavla, #_messendzhery (
Мессенджеры
)
, #_programmirovanie (
Программирование
)
, #_razrabotka_igr (
Разработка игр
)
, #_logicheskie_igry (
Логические игры
)
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 11-Май 18:08
Часовой пояс: UTC + 5