window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster już istnieje') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() thecodest, Author at The Codest - Page 15 of 18

Projektowanie gier

Projektując grę naszym głównym celem było przygotowanie fajnej rozrywki dla programistów, a także zrobienie czegoś ciekawego w ramach pracy w naszej firmie. Do tej pory nie mieliśmy żadnych kompetencji w tworzeniu gier, dlatego było to dla nas spore wyzwanie. W pierwszej kolejności skupiliśmy się na tym, czym ta gra tak naprawdę ma być. Po opracowaniu wstępnego planu przystąpiliśmy do działania.

W ramach prac nad grą zdecydowaliśmy się na hackathon i podział na grupy wykonujące konkretne zadania. Dzięki takiemu 8-godzinnemu podziałowi pracy udało nam się zrealizować wygląd przeciwników w grze, całą szatę graficzną oraz podwaliny zarówno zadań, jak i API całego systemu. W kolejnym etapie zbieraliśmy się na 4-godzinne spotkania raz w miesiącu, dzięki czemu udało nam się ukończyć grę w 3 spotkania.

Wdrożenie

Jako że specjalizujemy się w RubyOnRails, wybraliśmy tę technologię jako wiodącą. Gra nie miała być jednak tekstowa, dlatego podejście do niej znalazło odzwierciedlenie w aplikacji typu SPA. W ramach zadania pracowaliśmy nad dobrze znanym potokiem assetów z railsów (w 2016 roku w zasadzie nie było nic lepszego) oraz całą aplikacją. javascript w oparciu o nasze zastrzeżone kod z pomocą TypeScript. W aplikacji nastąpił standardowy podział obowiązków: Railsy jako źródło assetów i API, javascript i related jako interakcja z użytkownikiem. Tutaj jednak funkcjonowało to jako hybryda i część widoków była po prostu renderowana z Railsów, a część z JS.

Maszynopis

Był to nasz pierwszy eksperyment w tej dziedzinie. Były to czasy, kiedy ludzie wierzyli w sukces CoffeScript. Użycie TypeScript wymagało wprowadzenia gema typescript-rails. Niestety nie był to finał, gdyż typescript, jako język statycznie typowany, wymagał tego również od bibliotek domyślnie dołączonych do railsów.

https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/jquery.d.ts (szczególnie w przypadku korzystania z wbudowanego systemu zarządzania zasobami z szynami).

Cody jako gra wymagała sporej dynamiki po stronie przeglądarki, a także modyfikacji drzewa DOM. Użycie TypeScript zamiast vanilla javascript było ogromnym skokiem w jakości kodu, sama obecność klas i enkapsulacji była dla nas bardzo kusząca.

API i SPA

W 2019 r. aplikacje SPA są zarządzane za pomocą wspaniałych urządzeń React lub Vue biblioteki. Jednak w 2015 roku zrobiliśmy to w inny sposób. Wspomniany wcześniej typescript był pomocny w implementacji gry, podczas gdy jQuery cofnęło całą pracę związaną z żądaniem xml http. Teraz możemy używać fetch, podczas gdy w tamtych czasach `$ .ajax` był wszystkim, co było potrzebne do pracy. Spójrz na nasze api klienta!

https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/services/api_client.js.ts

Jeśli było to api, to trzeba było jakoś rozwiązać problem uwierzytelniania, prawda? No właśnie. Ale w tym przypadku poszliśmy za (czy można tu napisać - użyliśmy zespołu?!) zespołem i w sesji rails stworzyliśmy cookie_key, a następnie zapisaliśmy go w bazie danych. Stąd wiedzieliśmy, że wszystko jest więcej niż w porządku.

https://github.com/codesthq/cody_the_game/search?q=cookie_key&unscoped_q=cookie_key

Stan gry był przechowywany w bazie danych, a informacje o tym, ilu użytkowników ma punkty, pochodziły z bazy danych (czy to ta sama baza danych? Czy możemy po prostu zmienić to zaimkiem?). ACID zawsze się przydaje, gdy nie ma buforowania po stronie systemu;)

W przypadku spa jest to najlepsze rozwiązanie bez przeładowywania strony. Rozwiązaliśmy to klasycznie i anchor html był najlepszym rozwiązaniem bez rozszerzania niepotrzebnych zależności. Bo kto by używał turbolinków?

SnapSVG

Jeśli projektujemy grę, to musi być ona wydana tylko ze świetną grafiką i animacjami. Wtedy spędzaliśmy wiele godzin zastanawiając się jak sprostać tym wymaganiom w naszej aplikacji. Z jednej strony canvas potrafi zdziałać cuda, z drugiej w czystym htmlu dużo łatwiej się połapać i każdy o tym wie. Po żmudnych poszukiwaniach najlepszego rozwiązania doszliśmy do wniosku, że połączeniem tych dwóch rozwiązań jest svg. Pozwala on w łatwy sposób prezentować grafikę w wektorze, jest napisany w języku znaczników i co najważniejsze można go modyfikować w locie. Co ważne, istnieje biblioteka dla plików svg, która działa podobnie do jQuery i pozwala na operacje na obrazie w zunifikowany sposób. Jest to: http://snapsvg.io, Mamy bardzo miłe wspomnienia związane z tym konkretnym rozwiązaniem.

Przykład użycia snap.svg można znaleźć poniżej:

https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/intro.js.ts

Sam plik haml ze szkieletem graficznym:

https://github.com/codesthq/cody_the_game/blob/master/app/views/game/show.html.haml

Jak widać, jest to prawie jak normalne drzewo DOM i zwykła aplikacja railsowa!

TrustedSandbox

Cóż, w końcu mieliśmy API, Grafikę, SPA. Ale co z implementacją rozwiązań przesłanych przez użytkowników?

Pierwszą rzeczą, która przychodzi na myśl, jest metoda eval, ale nie jesteśmy szaleni;) W 2016 roku docker był na fali wznoszącej, więc wydawał się naturalnym wyborem. Same kontenery nie gwarantowały pełnej izolacji i ochrony, dlatego skorzystaliśmy z gotowego rozwiązania w Ruby o nazwie https://github.com/vaharoni/trusted-sandbox. Pozwoliło ono lepiej zabezpieczyć kod przed opuszczeniem piaskownicy i w ustandaryzowany sposób skonfigurować wymagania systemu operacyjnego. Bardzo ważne było odpowiednie ograniczenie czasu wykonywania kodu, pamięci potrzebnej do działania oraz cykli procesora. Nasza konfiguracja dostępna jest poniżej

https://github.com/codesthq/cody_the_game/blob/master/config/trusted_sandbox.yml.example

Oczywiście sama zaufana piaskownica niczego nie gwarantowała, dlatego wymyśliliśmy specjalną stronę internetową do uruchamiania kodu.

https://github.com/codesthq/cody_the_game/blob/master/app/services/task_runner/base_task.rb

Każde z zadań miało swój własny przypadek testowy, co pozwoliło nam zweryfikować poprawność zaimplementowanego rozwiązania. Dokonano tego poprzez wstrzyknięcie kodu użytkownika do przypadku testowego, tak aby wszystko było uruchamiane w izolacji.

https://github.com/codesthq/cody_the_game/blob/master/app/challenges/challenge/case.rb

Oczywiście czynność ta kosztowała sporo czasu, a podczas zbierania odpowiedzi nie mogliśmy sobie pozwolić na uruchomienie sandboxa, więc jedynie zapisywaliśmy kod w bazie danych, tworząc zgłoszenie, a następnie, korzystając z long poolingu, odpytywaliśmy endpoint w celu uzyskania statusu kodu. To pozwoliło nam odciążyć serwer aplikacji i odpowiednio zweryfikować dane. Oczywiście musieliśmy też zabezpieczyć się przed "zawaleniem skryptu", dlatego ograniczyliśmy liczbę zapytań do serwera za pomocą zmiennej ttl, co widać poniżej.

https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/level_controller.js.ts#L92

Podsumowanie konkursu

Do września 2011 roku statystyki gry wyglądały następująco:

- liczba sesji: 1945 - wysłane zadania: 4476 - wysłał prawidłowe odpowiedzi: 1624 - zakończył grę: 31

Jak widać, największe schody zaczęły się w zadaniu # 2, ponieważ nie był to już zwykły przykład hello world.

Czytaj także:

Szybkie zapoznanie się z Ruby 2.6. Co nowego?

Pisanie dokumentacji stało się łatwe dzięki VuePress

 Samouczek dotyczący podstaw Vue.js. Jak zacząć pracę z tym frameworkiem?

pl_PLPolish