En dag for 3 år siden i The Codest-teamet forberedte vi et flott Cody-spill for Ruby-programmerere. I dagens artikkel vil jeg beskrive hvordan arbeidet med dette prosjektet så ut og fremfor alt vise deg koden til prosjektet, som fra nå av er offentlig tilgjengelig på vår github.
Spilldesign
Da vi utviklet spillet, var hovedmålet vårt å lage morsom underholdning for programmerere, i tillegg til å gjøre noe interessant som en del av arbeidet i selskapet vårt. Så langt hadde vi ikke hatt noen kompetanse på å lage spill, og derfor var det en stor utfordring for oss. I første omgang fokuserte vi på hva dette spillet egentlig var. Etter å ha kommet opp med den første planen, gikk vi i gang.
Som en del av arbeidet med spillet bestemte vi oss for å ta et hackathon og dele oss inn i grupper som utførte spesifikke oppgaver. Med en slik 8-timers arbeidsdeling klarte vi å realisere utseendet til motstanderne i spillet, hele oppsettet og grunnlaget for både oppgaver og API-er i hele systemet. I neste fase samlet vi oss til 4-timers møter en gang i måneden, noe som gjorde at vi klarte å fullføre spillet på tre møter.
Implementering
Siden vi er spesialister på RubyOnRails, valgte vi denne teknologien som den ledende. Spillet var imidlertid ikke ment å være tekstlig, og derfor ble tilnærmingen til det gjenspeilet i SPA-applikasjonen. Som en del av oppgaven jobbet vi med en velkjent pipeline av eiendeler fra rails (i 2016 var det i prinsippet ikke noe bedre) og hele javascript basert på vår egenutviklede kode ved hjelp av TypeScript. I applikasjonen var det en standard ansvarsfordeling: Rails som ressurs og API-kilde, javascript og relatert som interaksjon med brukeren. Her fungerte det imidlertid som en hybrid, og noen visninger ble ganske enkelt gjengitt fra Rails, mens noen av de andre - fra JS.
Maskinskrevet manuskript
Det var vårt første eksperiment på dette feltet. Dette var tider da folk trodde på CoffeScripts suksess. Bruk av TypeScript krevde innføring av en typescript-rails-perle. Dessverre var dette ikke det endelige, ettersom typescript, som er et statisk typet språk, også krevde dette fra bibliotekene som var knyttet som standard til rails.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/jquery.d.ts (spesielt når du bruker det innebygde kapitalforvaltningssystemet med rails).
Cody som et spill krevde mye dynamikk på nettlesersiden, samt modifisering av DOM-treet. Å bruke TypeScript i stedet for vaniljejavascript var et stort sprang i kvaliteten på koden, og selve tilstedeværelsen av klasser og innkapsling var veldig fristende for oss.
API og SPA
I 2019 administreres SPA-applikasjoner ved hjelp av de fantastiske React- eller Vue biblioteker. I 2015 gjorde vi det imidlertid på en annen måte. Det tidligere nevnte typescriptet var nyttig i implementeringen av spillet, mens jQuery opphevet alt arbeidet knyttet til xml http-forespørselen. Nå kan vi bruke fetch, mens på den tiden var `$ .ajax` alt som trengtes for jobben. Ta en titt på vår klient api!
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/services/api_client.js.ts
Hvis det var api, måtte du løse autentiseringsproblemet på en eller annen måte, ikke sant? Jo, det er riktig. Men i så fall gikk vi etter (er det mulig å skrive her - vi brukte bandet?!) bandet, og i rails-sesjonen opprettet vi cookie_key og lagret den etterpå i databasen. Dermed visste vi at alt var mer enn i orden.
https://github.com/codesthq/cody_the_game/search?q=cookie_key&unscoped_q=cookie_key
Spillstatusen ble lagret i databasen, og informasjon om hvor mange brukere som hadde poeng kom fra databasen (er det den samme databasen? Kan vi bare endre det med et pronomen?). ACID kommer alltid godt med når det ikke er noen caching på systemsiden ;)
Når det gjelder spaet, er det best uten å laste inn siden på nytt. Vi har løst det på klassisk vis, og html-ankeret var den beste løsningen uten å utvide unødvendige avhengigheter. For hvem vil bruke turbolinks?
SnapSVG
Hvis vi designer et spill, må det bare utgis med flott grafikk og animasjoner. Den gang brukte vi mange timer på å lure på hvordan vi skulle oppfylle disse kravene i applikasjonen vår. På den ene siden kan lerretet gjøre mirakler, på den andre siden er det mye lettere å ta igjen i en ren html, og alle vet det. Etter et møysommelig søk etter den beste løsningen, fant vi ut at kombinasjonen av disse to løsningene er svg. Det lar deg enkelt presentere grafikk i en vektor, den er skrevet på markeringsspråket, og det som er viktigst, det kan endres på farten. Det er viktig at det finnes et bibliotek for svg-filer som fungerer på samme måte som jQuery og tillater operasjoner på bildet på en enhetlig måte. Dette er: http://snapsvg.io, Vi har veldig gode minner fra den spesielle bruken av løsningen.
Et eksempel på hvordan vi brukte snap.svg finner du nedenfor:
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/intro.js.ts
Selve haml-filen med det grafiske skjelettet:
https://github.com/codesthq/cody_the_game/blob/master/app/views/game/show.html.haml
Som du kan se, er det nesten som et vanlig DOM-tre og en vanlig rails-app!
TrustedSandbox
Vel, endelig hadde vi API, grafikk og SPA. Men hva med implementeringen av løsninger som brukerne sender inn?
Det første man tenker på er eval-metoden, men vi er ikke gale ;) Tilbake i 2016 var docker på fremmarsj, så det føltes som et naturlig valg. Containerne i seg selv garanterte ikke fullstendig isolasjon og beskyttelse, og derfor brukte vi en klar løsning i Ruby kalt https://github.com/vaharoni/trusted-sandbox. Det gjorde det mulig å beskytte koden bedre før den forlot sandkassen og konfigurere operativsystemkravene på en standardisert måte. Det var svært viktig å begrense tiden koden kunne kjøres, minnet som trengtes for å fungere og CPU-syklusene. Konfigurasjonen vår er tilgjengelig nedenfor
https://github.com/codesthq/cody_the_game/blob/master/config/trusted_sandbox.yml.example
Den samme pålitelige sandkassen garanterte selvfølgelig ikke noe som helst, og derfor utviklet vi et spesielt nettsted for å kjøre koden.
https://github.com/codesthq/cody_the_game/blob/master/app/services/task_runner/base_task.rb
Hver av oppgavene hadde sitt eget testtilfelle, slik at vi kunne verifisere at den implementerte løsningen var korrekt. Dette ble gjort ved å injisere brukerkoden i testtilfellet, slik at alt ble kjørt isolert.
https://github.com/codesthq/cody_the_game/blob/master/app/challenges/challenge/case.rb
Selvfølgelig kostet denne handlingen ganske mye tid, og mens vi samlet inn svarene, hadde vi ikke råd til å kjøre sandkassen, så vi lagret bare koden i databasen, opprettet en innsending og deretter, ved hjelp av lang pooling, spurte vi endepunktet for å få kodestatus. På denne måten kunne vi avlaste applikasjonsserveren og verifisere dataene på riktig måte. Selvfølgelig måtte vi også beskytte oss mot å "krasje skriptet", og derfor begrenset vi antall serverforespørsler ved hjelp av ttl-variabelen, som kan sees nedenfor.
https://github.com/codesthq/cody_the_game/blob/master/app/assets/javascripts/level_controller.js.ts#L92
Sammendrag av konkurransen
Frem til september 2011 var kampstatistikken som følger:
- antall økter: 1945 - sendte oppgaver: 4476 - sendte riktige svar: 1624 - avsluttet kampen: 31
Som du kan se, startet den største trappen i oppgave # 2 fordi det ikke lenger var et vanlig hello world-eksempel.
Les også:
Et raskt dykk inn i Ruby 2.6. Hva er nytt?
Det har blitt enkelt å skrive dokumentasjon takket være VuePress
Vue.js grunnleggende opplæring. Hvordan starte med dette rammeverket?