Eines Tages vor 3 Jahren haben wir im The Codest-Team ein tolles Cody-Spiel für Ruby-Programmierer vorbereitet. Im heutigen Artikel möchte ich beschreiben, wie die Arbeit an diesem Projekt aussah und vor allem den Code des Projekts zeigen, der ab jetzt auf unserem Github öffentlich verfügbar ist.
Spiel-Design
Als wir das Spiel entwarfen, war unser Hauptziel, eine unterhaltsame Unterhaltung für Programmierer zu schaffen und etwas Interessantes als Teil der Arbeit in unserem Unternehmen zu tun. Bislang hatten wir keine Kompetenzen in der Entwicklung von Spielen, weshalb es für uns eine große Herausforderung war. Zunächst einmal haben wir uns Gedanken darüber gemacht, was dieses Spiel eigentlich sein sollte. Nachdem wir den ersten Plan ausgearbeitet hatten, machten wir uns an die Arbeit.
Im Rahmen der Arbeit am Spiel beschlossen wir, einen Hackathon zu veranstalten und uns in Gruppen aufzuteilen, die bestimmte Aufgaben erledigten. Mit einer solchen 8-stündigen Arbeitsteilung konnten wir das Aussehen der Gegner im Spiel, das gesamte Layout und die Grundlage sowohl der Aufgaben als auch der APIs des gesamten Systems realisieren. In der nächsten Phase trafen wir uns einmal im Monat zu 4-stündigen Sitzungen, wodurch wir das Spiel in 3 Sitzungen fertigstellen konnten.
Umsetzung
Da wir uns auf RubyOnRails spezialisiert haben, wählten wir diese Technologie als die führende. Allerdings sollte das Spiel nicht textuell sein und daher wurde der Ansatz dazu in der SPA-Typ-Anwendung reflektiert. Als Teil der Aufgabe arbeiteten wir an einer bekannten Pipeline von Assets aus Rails (im Jahr 2016 gab es im Prinzip nichts Besseres) und die gesamte javascript basierend auf unserem proprietären Code mit Hilfe von TypeScript. In der Anwendung, gab es eine Standard-Aufteilung der Zuständigkeiten: Rails als Asset und API-Quelle, Javascript und Co. als Interaktion mit dem Benutzer. Hier jedoch funktionierte es als ein Hybrid und einige Ansichten wurden einfach von Schienen gerendert, während einige der anderen - von JS.
Typoskript
Es war unser erstes Experiment in diesem Bereich. Das waren noch Zeiten, als man an den Erfolg von CoffeScript glaubte. Die Verwendung von TypeScript erforderte die Einführung eines typescript-rails-Gems. Leider war dies nicht die endgültige Lösung, da Typescript als statisch typisierte Sprache dies auch von den Bibliotheken verlangte, die standardmäßig mit Rails verbunden sind.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/jquery.d.ts (insbesondere bei Verwendung des eingebetteten Asset-Management-Systems mit Rails).
Cody als Spiel erforderte eine Menge Dynamik auf der Browserseite sowie die Änderung des DOM-Baums. Die Verwendung von TypeScript anstelle von Vanilla-Javascript war ein großer Sprung in der Qualität des Codes, das Vorhandensein von Klassen und Kapselung war sehr verlockend für uns.
API und SPA
Im Jahr 2019 werden die SPA-Anwendungen mit dem großartigen React oder Vue Bibliotheken. Im Jahr 2015 haben wir es jedoch anders gemacht. Das zuvor erwähnte Typescript war bei der Implementierung des Spiels hilfreich, während jQuery die gesamte Arbeit im Zusammenhang mit der xml http-Anfrage zurücknahm. Jetzt können wir fetch verwenden, während damals `$ .ajax` alles war, was für den Job benötigt wurde. Werfen Sie einen Blick auf unsere Client-Api!
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/services/api_client.js.ts
Wenn es sich um eine API handelte, mussten Sie das Authentifizierungsproblem irgendwie lösen, nicht wahr? Nun, das ist richtig. Aber in diesem Fall gingen wir nach (ist es möglich, hier zu schreiben - wir benutzten das Band?!) dem Band und in der Rails-Sitzung erstellten wir cookie_key und speicherten es anschließend in der Datenbank. Daher wussten wir, dass alles mehr als in Ordnung war.
https://github.com/codesthq/cody_the_game/search?q=cookie_key&unscoped_q=cookie_key
Der Spielstatus wurde in der Datenbank gespeichert, und die Informationen darüber, wie viele Benutzer Punkte hatten, kamen aus der Datenbank (ist es dieselbe Datenbank? Können wir sie einfach durch ein Pronomen ersetzen?). ACID ist immer dann nützlich, wenn es kein Caching auf der Systemseite gibt;)
Im Falle des Spas ist es das Beste, ohne die Seite neu zu laden. Wir haben es klassisch gelöst und der html-Anker war die beste Lösung, ohne unnötige Abhängigkeiten zu erweitern. Denn wer würde schon Turbolinks verwenden?
SnapSVG
Wenn wir ein Spiel entwerfen, muss es nur mit tollen Grafiken und Animationen veröffentlicht werden. Damals haben wir viele Stunden damit verbracht, uns zu fragen, wie wir diese Anforderungen in unserer Anwendung erfüllen können. Auf der einen Seite kann die Leinwand Wunder bewirken, auf der anderen Seite ist eine saubere Html-Datei viel einfacher einzuholen und jeder weiß das. Nach einer mühsamen Suche nach der besten Lösung haben wir herausgefunden, dass die Kombination dieser beiden Lösungen svg ist. Es ermöglicht die einfache Darstellung von Vektorgrafiken, ist in der Markup-Sprache geschrieben und, was das Wichtigste ist, es kann im laufenden Betrieb geändert werden. Wichtig ist, dass es eine Bibliothek für svg-Dateien gibt, die ähnlich wie jQuery funktioniert und Operationen mit dem Bild auf einheitliche Weise ermöglicht. Dies ist: http://snapsvg.io, wir haben sehr schöne Erinnerungen an diese besondere Lösung.
Ein Beispiel dafür, wie wir snap.svg verwendet haben, finden Sie unten:
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/intro.js.ts
Die haml-Datei selbst mit dem grafischen Gerüst:
https://github.com/codesthq/cody_the_game/blob/master/app/views/game/show.html.haml
Wie Sie sehen können, ist es fast wie ein normaler DOM-Baum und eine normale Rails-App!
TrustedSandbox
Nun, schließlich hatten wir API, Grafiken, SPA. Aber was ist mit der Umsetzung von Lösungen, die von den Nutzern geschickt werden?
Das erste, was uns in den Sinn kommt, ist die Eval-Methode, aber wir sind nicht verrückt;) Damals im Jahr 2016 war Docker auf dem Vormarsch, so dass es sich wie eine natürliche Wahl anfühlte. Die Container selbst garantierten keine vollständige Isolierung und Schutz, weshalb wir eine fertige Lösung in Ruby namens https://github.com/vaharoni/trusted-sandbox verwendeten. Sie ermöglichte es, den Code besser zu schützen, bevor er die Sandbox verlässt, und auf standardisierte Weise die Anforderungen des Betriebssystems zu konfigurieren. Es war sehr wichtig, die Ausführungszeit des Codes, den benötigten Arbeitsspeicher und die CPU-Zyklen richtig zu begrenzen. Unsere Konfiguration ist unten verfügbar
https://github.com/codesthq/cody_the_game/blob/master/config/trusted_sandbox.yml.example
Natürlich war die vertrauenswürdige Sandbox keine Garantie für irgendetwas, weshalb wir eine spezielle Website entwickelt haben, um den Code auszuführen.
https://github.com/codesthq/cody_the_game/blob/master/app/services/task_runner/base_task.rb
Für jede der Aufgaben gab es einen eigenen Testfall, mit dem wir die Korrektheit der implementierten Lösung überprüfen konnten. Dazu wurde der Anwendercode in den Testfall injiziert, so dass alles isoliert ausgeführt wurde.
https://github.com/codesthq/cody_the_game/blob/master/app/challenges/challenge/case.rb
Natürlich kostete diese Aktion viel Zeit, und während wir die Antworten sammelten, konnten wir es uns nicht leisten, die Sandbox laufen zu lassen, also haben wir den Code nur in der Datenbank gespeichert, eine Übermittlung erstellt und dann mit Hilfe von Long Pooling den Endpunkt gebeten, den Codestatus zu erhalten. So konnten wir den Anwendungsserver entlasten und die Daten entsprechend verifizieren. Natürlich mussten wir uns auch vor einem "Absturz des Skripts" schützen und haben daher die Anzahl der Serverabfragen mit Hilfe der ttl-Variable begrenzt, die Sie unten sehen können.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/level_controller.js.ts#L92
Zusammenfassung des Wettbewerbs
Bis September 2011 sahen die Spielstatistiken wie folgt aus:
- Anzahl der Sitzungen: 1945 - gesendete Aufgaben: 4476 - richtige Antworten gesendet: 1624 - beendete das Spiel: 31
Wie Sie sehen können, begann die größte Treppe in der Aufgabe # 2, weil es sich nicht mehr um ein gewöhnliches Hallo-Welt-Beispiel handelte.
Lesen Sie auch:
Ein kurzer Einblick in Ruby 2.6. Was ist neu?
Das Schreiben von Dokumentationen ist dank VuePress einfach geworden
Vue.js Grundlagen-Tutorial. Wie beginnt man mit diesem Framework?