other
October 5, 2023

iddqd или Violence As A Service

Иногда разработчикам хочется сделать что-то просто так. Вот о таком небольшом "просто так" данная статья, а если точнее, то о запуске DOSBox в браузере.

Немного предыстории

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

Наша команда делала небольшое приложение для внутреннего использования - для помощи нашим сейлзам и поддержке. И, поскольку, проект внутренний решили сделать "пасхалку", но так, чтоб еще немного потренироваться :) Короче, теперь по супер-секретной горячей кнопке мы можем запустить Doom и пройти его - батарейки и сохранения включены!

Оригинальная обложка Doom (игра, 1993)

Теперь немного истории

Я, будучи лютым олдфагом старых игр, часто использую DOSBox для того, чтоб поностальгировать или даже поиграть со своими детьми (например, в Master of Orion 2), и когда на глаза попалась статья о запуске этого чудо-агрегата прямо в браузере, предложил товарищу затащить его к себе. В качестве игры выбрали культовый Doom (и не какой-то этот ваш, а настоящий! олдфаговский!). Сказано - сделано.

Реализация

Сначала надо выбрать реализацию Dosbox под JS и в нашем случае это будет caiiiycuk/js-dos:

DOSBox is an open source DOS emulator designed for running old games. Emscripten compiles C/C++ code to JavaScript. This is a version of DOSBox which can be compiled with Emscripten to run in a web browser. It allows running old DOS games and other DOS programs in a web browser.

Это JS-библиотека, которая содержит в себе всё что надо для запуска DOSBox и сильно упрощает этот процесс.

Далее нам потребуется:

  • работающее Rails-приложение (только Rails, только Ruby!);
  • подключить JS-библиотеку;
  • сделать Stimulus-контроллер для оживления процесса;
  • сделать вёрстку (в нашем случае модалку);
  • запустить, пройти и помериться поделиться рейтингом!

Подключаем JS-библиотеки:

diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ ... @@
     "bootstrap-icons": "^1.10.5",
     "esbuild": "^0.19.2",
+    "hotkeys-js": "^3.12.0",
+    "js-dos": "^7.5.0",
+    "url": "^0.11.2"
@@ ... @@

Тут есть небольшой нюанс: js-dos при установке подтягивает WASM досбокса, но "делает это без уважения" - поэтому мы просто руками скопировали в /public файлы wdosb‎ox.js‎, wdosbox.j‎s.symbols и wdosbo‎x.wasm‎.

Stimulus и оживление вёрстки

Теперь самое большое - сделать Stimulus-контроллер для вызова модалки и обработки горячей кнопки.

#app/javascript/controllers/dos_games_controller.js

import { Controller } from "@hotwired/stimulus"
// кривой импорт
import "js-dos"
import hotkeys from 'hotkeys-js';
import * as bootstrap from "bootstrap"

export default class extends Controller {
  
  static targets = ['dosContainer', 'closeButton'];

  connect() {
    this.modal = new bootstrap.Modal(this.element)
    this.element.addEventListener('shown.bs.modal', () => this.startPlayer())
    this.element.addEventListener('hide.bs.modal', () => this.stopPlayer())
    this.closeButtonTarget.addEventListener('click', () => this.modal.hide())
    hotkeys('ctrl+i+q', ()=>{
      this.modal.show();
    });
    hotkeys('ctrl+q', () => {
      this.modal.hide();
    })
  }
  
  startPlayer() {
    this.player ||= Dos(this.dosContainerTarget, {
      withNetworkingApi: false,
      noSocialLinks: true,
      clickToStart: true
    })
    this.player.run(this.gameLink);
  }

  stopPlayer() {
    if (!this.player) { return; }

    this.player.layers.save();
    this.player.stop();
  }

  get gameLink() {
    // return "/Wonderfield.jsdos";
    return "https://cdn.dos.zone/custom/dos/doom.jsdos";
    // return "https://cdn.dos.zone/custom/dos/homm_2.jsdos";
  }
}

Тут можно запускать не только Doom, но и не менее православные HoMM2 и другие любимые игры детства или даже сделать селектор с выпадашкой!

Собрать свои бандлы с играми можно тут.

HTML и модалочка

Делаем вёрстку модалочки (мы используем bootstrap и очень его любим) и подключаем к ней dos-games контроллер:

diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim
index 21dac7e..750a63e 100644
--- a/app/views/layouts/application.html.slim
+++ b/app/views/layouts/application.html.slim
@@ ... @@ 
     = javascript_include_tag 'application', 'data-turbo-track': 'reload'
 
   body.p-0.bg-light
+    .modal.fade data-controller="dos-games"
+      .modal-dialog style="min-width: 50%;"
+        .modal-content
+          .modal-header
+            h5 =  'GAAAME'
+            button.btn-close.modal-close data-dos-games-target="closeButton"
+          .modal-body style="min-height: 600px;"
+            div data-dos-games-target='dosContainer' style="width: 100%; height: 600px;"
     #mainapp.d-flex
       div.d-none data-controller="toastr" = flash_to_toastr(flash).to_json
       =render 'layouts/sidebar'

Всё!

Теперь можно посмотреть, что получилось:

Ссылки? Ссылки!

  • DOSBox - оригинальный проект;
  • JS DOSBox - перекомпилированный-перетранслированный проект из C/C++ в JS;
  • WASM - WebAssembly и что это такое;
  • Old Games - всё, во что вы хотели поиграть в детстве и не успели;