Un giorno di 3 anni fa nel team The Codest abbiamo preparato un grande gioco di Cody per programmatori Ruby. Nell'articolo di oggi, vorrei descrivere come si è svolto il lavoro su questo progetto e soprattutto mostrarvi il codice del progetto, che da oggi è disponibile pubblicamente sul nostro github.
Design del gioco
Quando abbiamo progettato il gioco, il nostro obiettivo principale era quello di preparare un intrattenimento divertente per i programmatori, oltre a fare qualcosa di interessante come parte del lavoro nella nostra azienda. Finora non avevamo avuto alcuna competenza nella creazione di giochi, per cui la sfida è stata notevole. In primo luogo, ci siamo concentrati su cosa fosse realmente questo gioco. Dopo aver elaborato il piano iniziale, ci siamo messi all'opera.
Nell'ambito del lavoro sul gioco, abbiamo deciso di partecipare a un hackathon e di dividerci in gruppi che svolgessero compiti specifici. Con questa divisione di 8 ore di lavoro, siamo riusciti a realizzare l'aspetto degli avversari nel gioco, l'intero layout e le fondamenta dei compiti e delle API dell'intero sistema. Nella fase successiva, ci siamo riuniti per incontri di 4 ore una volta al mese, grazie ai quali siamo riusciti a completare il gioco in 3 incontri.
Attuazione
Poiché siamo specializzati in RubyOnRails, abbiamo scelto questa tecnologia come leader. Tuttavia, il gioco non doveva essere testuale e quindi l'approccio ad esso si rifletteva in un'applicazione di tipo SPA. Come parte del compito, abbiamo lavorato su una ben nota pipeline di asset da rails (nel 2016 non c'era niente di meglio in linea di principio) e l'intera javascript basato su un sistema di proprietà codice con l'aiuto di TypeScript. Nell'applicazione c'era una divisione standard delle responsabilità: Rails come risorsa e fonte di API, javascript e affini come interazione con l'utente. In questo caso, tuttavia, si è operato come un ibrido e alcune viste sono state semplicemente renderizzate da Rails, mentre altre - da JS.
Dattiloscritto
Era il nostro primo esperimento in questo campo. Erano tempi in cui si credeva nel successo di CoffeScript. L'uso di TypeScript richiedeva l'introduzione di una gemma typescript-rails. Sfortunatamente, questa non era la soluzione definitiva, perché typescript, essendo un linguaggio tipizzato staticamente, richiedeva anche questo dalle librerie allegate di default a rails.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/jquery.d.ts (soprattutto quando si utilizza il sistema di gestione degli asset incorporato in rails).
Cody come gioco richiedeva molte dinamiche sul lato del browser, oltre alla modifica dell'albero del DOM. L'uso di TypeScript al posto di javascript vanilla ha rappresentato un enorme salto di qualità del codice; la presenza stessa di classi e incapsulamento era molto allettante per noi.
API e SPA
Nel 2019, le applicazioni SPA sono gestite utilizzando i magnifici React o Vue biblioteche. Tuttavia, nel 2015, lo abbiamo fatto in modo diverso. Il già citato typescript è stato utile per l'implementazione del gioco, mentre jQuery ha revocato tutto il lavoro relativo alla richiesta xml http. Ora possiamo usare fetch, mentre a quei tempi `$ .ajax` era tutto ciò che serviva per il lavoro. Date un'occhiata alla nostra API client!
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/services/api_client.js.ts
Se si trattava di un'API, bisognava risolvere il problema dell'autenticazione in qualche modo, no? Beh, è vero. Ma in questo caso, abbiamo seguito (è possibile scrivere qui - abbiamo usato la banda?!) la banda e nella sessione di rails abbiamo creato cookie_key e poi l'abbiamo salvata nel database. Quindi sapevamo che tutto era più che a posto.
https://github.com/codesthq/cody_the_game/search?q=cookie_key&unscoped_q=cookie_key
Lo stato del gioco era memorizzato nel database e le informazioni su quanti utenti avevano punti provenivano dal database (è lo stesso database? Possiamo cambiarlo con un pronome?). L'ACID è sempre utile quando non c'è cache sul lato del sistema;)
Nel caso della spa, è la soluzione migliore senza ricaricare la pagina. Abbiamo risolto il problema in modo classico e l'ancoraggio html era la soluzione migliore senza espandere le dipendenze inutili. Perché chi userebbe i turbolink?
SnapSVG
Se progettiamo un gioco, questo deve essere rilasciato solo con grafica e animazioni eccezionali. All'epoca passavamo molte ore a chiederci come soddisfare queste esigenze nella nostra applicazione. Da un lato, il canvas può fare miracoli, dall'altro, in un html pulito è molto più facile da raggiungere e tutti lo sanno. Dopo una minuziosa ricerca della soluzione migliore, abbiamo capito che la combinazione di queste due soluzioni è svg. Permette di presentare facilmente la grafica in un vettore, è scritto in linguaggio markup e, cosa più importante, può essere modificato al volo. È importante notare che esiste una libreria per i file svg che funziona in modo simile a jQuery e consente di effettuare operazioni sull'immagine in modo unificato. Questo è: http://snapsvg.io, abbiamo un bel ricordo dell'utilizzo di quella particolare soluzione.
Un esempio di utilizzo di snap.svg è riportato di seguito:
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/intro.js.ts
Il file haml stesso con lo scheletro grafico:
https://github.com/codesthq/cody_the_game/blob/master/app/views/game/show.html.haml
Come si può vedere, è quasi come un normale albero DOM e una normale applicazione rails!
TrustedSandbox
Bene, finalmente abbiamo API, grafica e SPA. Ma che dire dell'implementazione delle soluzioni inviate dagli utenti?
La prima cosa che viene in mente è il metodo eval, ma non siamo pazzi;) Nel 2016, docker era in ascesa, quindi ci è sembrata una scelta naturale. I container stessi non garantivano un isolamento e una protezione completi, per questo motivo abbiamo utilizzato una soluzione già pronta in Ruby chiamata https://github.com/vaharoni/trusted-sandbox. Questa soluzione ha permesso di proteggere meglio il codice prima di lasciare la sandbox e di configurare in modo standardizzato i requisiti del sistema operativo. Era molto importante limitare adeguatamente il tempo di esecuzione del codice, la memoria necessaria per operare e i cicli della CPU. La nostra configurazione è disponibile di seguito
https://github.com/codesthq/cody_the_game/blob/master/config/trusted_sandbox.yml.example
Naturalmente, la stessa sandbox di fiducia non garantiva nulla, ed è per questo che abbiamo ideato un sito web speciale per eseguire il codice.
https://github.com/codesthq/cody_the_game/blob/master/app/services/task_runner/base_task.rb
Ciascuno dei compiti aveva un proprio caso di test, che ci ha permesso di verificare la correttezza della soluzione implementata. Questo è stato fatto iniettando il codice utente nel caso di test, in modo che tutto fosse eseguito in isolamento.
https://github.com/codesthq/cody_the_game/blob/master/app/challenges/challenge/case.rb
Naturalmente, questa azione costava parecchio tempo e, mentre raccoglievamo le risposte, non potevamo permetterci di eseguire la sandbox, quindi ci siamo limitati a salvare il codice nel database, creando una submission e poi, utilizzando il long pooling, abbiamo chiesto all'endpoint di ottenere lo stato del codice. Questo ci ha permesso di alleggerire l'application server e di verificare i dati in modo appropriato. Naturalmente, dovevamo anche proteggerci dal "crash dello script" e quindi abbiamo limitato il numero di interrogazioni al server utilizzando la variabile ttl, che si può vedere qui sotto.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/level_controller.js.ts#L92
Sintesi della competizione
Fino a settembre 2011, le statistiche di gioco erano le seguenti:
- numero di sessioni: 1945 - compiti inviati: 4476 - ha inviato le risposte corrette: 1624 - ha terminato la partita: 31
Come si può vedere, le scale più grandi sono iniziate nel task # 2, perché non si trattava più di un normale esempio di hello world.
Leggi anche:
Una rapida immersione in Ruby 2.6. Cosa c'è di nuovo?
Scrivere la documentazione è diventato facile grazie a VuePress
Tutorial sulle basi di Vue.js. Come iniziare con questo framework?