Teknologi

I Bouvet er vi flere hundre teknologer som brenner for å programmere og utforme gode, digitale løsninger. I denne bloggen utforsker vi teknologien og deler det vi finner med dere.

Bouvet Battle Royale – Rematch

Vår årlige fagdag ble i fjor kjørt som showdown mellom våre avdelinger som jobber med Java- og Microsoft-teknologi. Uten å rippe opp i det, ble det en knusende seier til Java. Første helga i november bar det tilbake til Oscarsborg for “Bouvet Battle Royale – Rematch”.

TL;DR

Bevæpnet med datamaskin, skarpe hjerner og en haug av javascript-rammerverk, lagde hvert lag klienten de skulle bruke for å gjennomføre et real-time, real-world, strategispill ute rundt Oscarsborg festning. Denne bloggposten beskriver opplegget og hvordan vi lagde spillmotoren som deltakerne bruke til å samle poeng ved å registrere poster, ha kontroll på lagkameraters posisjoner, og ikke minst sabotere ved å sprenge poster og legge ut feller for å sabotere for andre lag. Videre går vi inn på både design og teknisk løsning – også hostet vi opplegget i Microsoft Azure. Det blei en god del Code for Fun for både deltakere og arrangører.

bouvet_flagg

 

 

Easy to play, hard to master

Basert på erfaringer fra fjorårets opplegg hadde vi noen ønsker for hvordan vi ville kjøre årets ”code camp competition”. Oscarsborg er et utrolig kult sted med to øyer og mange spennende detaljer, som bunkere og kanoner, så vi måtte definitivt ha et utendørsopplegg.

Kanonopplegg

Postplakat på pipa

Dessuten ville vi ikke favorisere de lagene som var så heldige å ha en spreking på laget, så det skulle lønne seg å ha hele laget i gang ute. Pluss at vi ønsket oss et element av strategi. Egentlig ganske ambisiøst. Dette måtte også veies opp mot hvor mye en gruppe på tre-fire utviklere rekker å lage på en kveld (inkl. en bedre middag innimellom). Dessuten måtte vi prøve å begrense hvor mye tid vi som arrangører skulle legge ned i forberedelser. Kort sagt, så leita vi etter et konsept med enkle regler, men som kunne brukes avansert.

Ok - det lønte seg litt å løpe

Ok – det lønte seg litt å løpe

Postjakt, pluss pluss

Klient på Windows Phone

Klient på Windows Phone

I planleggingen var vi innom flere varianter ala “Capture the flag”, “Hold på basen”,
“Kontrollere områder” før vi endte opp med “Postjakt”. Hvis du har vært borti orientering eller multisport, kjenner du igjen konseptet hvor det er satt ut en del poster i terrenget, og hvor lagene får poeng etter postens definerte verdi. Originalt er det vanlig at laget skal ta seg rundt samlet, men i vårt tilfelle slapp vi opp på det – vi ønsket at lagene skulle koordinere seg og gjerne spre seg rundt på øya. Dessuten skulle det lønne seg å være det første laget som registrerte en post, så poengverdien skulle synke i takt med antall registreringer. For å kunne regissere spillet, la vi opp til at poster var satt opp med fra-og-til-tidspunkter som angav om de var synlige. Med dette kunne vi regissere spillet ved å “åpne opp” områder utover i konkurransen. Fysisk var de selvsagt synlige ute i terrenget, men lagene kunne ikke registrere dem. Registrering av en post ble gjort ved å sende inn en kode som hang på en plakat. Å bare sende inn postens koordinat funker ikke, den er for lett å simulere.

Action med effekter

Tvert i gang på båten utover

Tvert i gang på båten utover

For lage litt futt i feltet, fikk hvert lag to effekter: En som sprengte en post i lufta (på liksom), og dermed deaktiverte den i ett minutt. Dette ville forsinke lag som kommer rett bak. Eller de kunne sabotere for det neste laget som prøvde å registrere posten ved å rigge en “felle”. Dette laget fikk nemlig postens verdi i minuspoeng, pluss at posten ble deaktivert i ett minutt også i dette tilfellet.

Posisjoner og meldinger

I tillegg til kjernefunksjonaliteten rundt postregistreringen, la vi opp til to tilleggstjenester: Posisjoner og meldinger. Hver deltaker kunne sende inn sin posisjon og hente ut en liste med hvor de andre deltakerne befant seg. Dette ble også brukt til å vise en samlet oversikt over alle lag på et scoreboard.

Scoreboard midt i matchen. Grønn = synlig, blå = skjult, rød = sprengt

Scoreboard midt i matchen. Grønn post = synlig, blå = skjult, rød = sprengt

Det ble ikke lagt opp til noen begrensning i hvordan deltakerne skulle kommunisere. De måtte gjerne ringe hverandre, sende SMS eller for den saks skyld gå sammen i terrenget og snakke sammen(!). Likevel satte vi opp en enkel meldingstjeneste hvor deltakerne kunne sende inn en melding som kom fram til de andre på laget. Som arrangører lokket vi med at meldingstjenesten ville bli brukt til å publisere meldinger som ville gi fordeler under spillets gang. Vi fikk også god bruk for meldingstjenesten etter hvert.

Komponenter

Diagrammet under illustrerer hva som skulle lages:

BBR2015_bokser_og_pilerLagenes oppgave var å programmere en klient som skulle brukes i spillet. Kjerneinformasjon om poster med posisjon og verdi ble hentet fra Gamestate – en feed som også innehold hvilke effekter de hadde tilgjengelig, og lagets poengsum og plassering. Registrering av poster, samt sending og mottak av henholdsvis posisjoner og meldinger ble gjort mot egne tjenester med enkle grensesnitt. Vi som arrangører hadde satt opp et scoreboard som hadde tilgang på utvidet informasjon sånn at vi kunne se det totale bildet av deltakere, og se både aktive og skjulte poster.

På den tekniske siden

Siden det tekniske opplegget i fjor hakket litt, besluttet vi tidlig å gå for kjent teknologi: Datalagring på SQL Server og en enkel frontend basert på ASP.NET WebApi. Ikke noe fancy NO-SQL, køer eller bakgrunnsjobber som kunne stelle det til. Samtidig var det veldig viktig at løsningen fungerte stabilt, og ikke gav samtidighetsproblemer. Dette var spesielt viktig i forbindelse med postregistrering – det som faktisk gav poeng. Vi skulle støtte ca 40 samtidige brukere, så det var begrenset hvor heftig det var behov for å dra til. Samtidig er det lov å tenke og holde seg innenfor prinsipper som gjør at backend-delen av løsningen kan redesignes uten at API’et endres. For eksempel:

  • Skriveoperasjoner gir bare 200 OK tilbake til klienten (med mindre noe feilet ved mottak). Resultatet av operasjonen, må hentes ut en annen vei. F.eks. registrerer laget en post, men ny poengsum må hentes ut fra gamestate. Dette gir et asynkront design i meldingsflyten. Om vi prosesserer den synkront eller legger den på en kø og lar en bakgrunnsjobb ta det videre, påvirker ikke API’et.
  • Skriveoperasjoner skal være append-only. Dvs. at ingen skriveoperasjoner skal slåss om de samme recordene.
  • Lesing skal så langt som mulig jobbe mot cache.
Trafikk mot web-apiet under konkurransen

Trafikk mot web-apiet under konkurransen

Vi kan godt kalle det CQRS-isj slæsj Eventual Consistency. Vi hadde også delt opp tjenestene sånn at meldinger, posisjoner og spillet ikke var avhengige av hverandre. På den måten kunne vi satt bort utvikling av meldinger og posisjoner til ledige konsulenter som kunne løst oppgaven på ønsket teknologi og skyplattform. Spilltjenesten ville vi ha kontroll på sjøl.

Prinsipper i praksis

Implementasjonen av meldingstjenesten ligner litt på hvordan Event Streams fungerer i Microsoft Azure – eller hvordan streams fungerer i det hele tatt. Hver melding som sendes inn, stemples med et sekvensnummer (i praksis DateTime.Now.Ticks). Dette nummeret er med i responsen når klienter henter ut meldinger. Neste gang en klient spør, kan den be om meldinger med nyere sekvensnummer siden sist. Vi la ikke inn noen caching på meldinger – alle spørringer går helt ned i databasen, men for å unngå altfor heftig last, ble det lagt på en Throttling-begrensning slik at hver klient bare fikk lov til å spørre hvert 3. sekund.

Posisjoner var litt artige. Der lot vi i utgangspunktet alt være in-memory, hvor deltakerens siste posisjon ble overskrevet ved hver oppdatering. For å kunne tåle en restart av serveren, og for å kunne gjøre replay av bevegelser i etterkant, lagret vi en ny (tidsstemplet) posisjon til databasen hvis det hadde gått minst 10 sek siden sist hvis deltakeren hadde flyttet seg minst 10 meter. Kort sagt: Vi trengte ikke 15 records i databasen for å vite at deltakeren står og klør seg i hodet.

Replay med tracks i Google Earth

Replay med KML-fil i Google Earth (link her) med noen unøyaktige posisjoner uti fjorden

Konseptet for spilltjenesten virket også veldig enkelt. Iallfall i utgangspunktet. Lagene hadde hver sin tilstand (gamestate) som angav synlige poster med posisjon og verdi, lagets poengsum, ranking og tilgjengelige effekter. Dette påvirkes i utgangspunktet bare av at lagene registrerer nye poster. Dvs. at når et lag gjør en postregistrering, så må gamestate for alle lag (og scoreboard) kalkuleres på nytt.

Det viktigste med en postregistrering, er at en ikke gjør flere samtidig. To lag skal ikke kunne stemple først på en post og begge få 100p. En ordentlig løsning på dette hadde vært å prosessert postregistreringer fra en kø, men vi tillot oss å beskytte logikken av en synkron låsemekanisme. PS: Dette er ikke i nærheten av å funke hvis du vil kjøre med mer enn 1 server. Selve kalkuleringen av gamestate er en eneste stor fest av LINQ-statements for å beregne diverse aspekter. Det blir for smått å gå i detalj her, men et viktig poeng var å skru av Lazy Loading i Entity Framework og unngå tjattring mot databasen. Selve gamestaten var ett objekt hvor den nye bare overskrev den forrige.

SQL-instansen var ikke spesielt svett

SQL-instansen var ikke spesielt svett – totalt 21 MB…

Den enkle og rene spilltjenesten hang ikke helt med da vi skulle implementere effekter som gjorde at poster forsvant og dukket opp igjen. Ettersom det bare var postregistreringer som påvirket gamestate, var det ingenting som kunne gjøre at en skjult post, ville dukke opp igjen etter ett minutt. Her måtte vi introdusere utløpsdato for gamestate og rekalkulere. Det interessante var å finne denne utløpsdatoen, nemlig det nærmeste tidspunktet frem i tid som endrer på gamestate.

Hvis du har fulgt linkene til kildekoden hittil, ser du at det til dels er litt knotete logikk, og noe er klart modent for refactoring. Her er det viktig å si at logikken er dekket av automatiske tester som er lagt inn på nivået rett under WebApi-Controllere. Det er lov å si at de også kunne vært lettere å lese, dog de har ganske god dekning.

Vi endte opp med å implementere alle tjenestene i samme ASP.NET WebAPI-prosjekt, men teknisk sett er det fullt mulig å dele opp ettersom klienter uansett adresserer dem med ulike url’er.

For asynkront?

Vi i arrangørgjengen reiste ut til Oscarsborg på morran og skulle gjøre de siste testene og justeringene. En litt kinkig sak dukket opp i lønsjen da scoreboard-ansvarlige lurte på om det var noen måte han kunne hente ut om noen hadde plantet en felle, eller scoret poeng – dvs. en slags Event Feed? Dette hadde vi i utgangspunktet ikke, og vi hadde egentlig det samme problemet i forhold til å gi deltakere beskjed om at de ikke fikk poeng ved registrering fordi posten var skjult, eller at de hadde gått i en felle og mistet masse poeng. Men vi hadde jo akkurat lagt inn mulighet for at vi som arrangører kunne sende ut meldinger med viktig informasjon til alle lag – det var jo bare å bruke meldingsfunksjonen! Så en halvtime etterpå hadde vi full info på scoreboard, pluss feedback til lagene via meldinger om ting som hadde skjedd i spillet.

Fancy kart - og meldinger til høyre

En løsning med fancy kart – og meldinger til høyre

Teknologivalg

Vi gjør unna dette som en punktliste –  ferdig med det:

Backend

Scoreboard

All kildekode ligger på: https://github.com/bouvet/BBR2015/

Gjennomføring

Det var hektisk aktivitet helt fra lagene fikk utdelt oppgaven da båten la ut fra Aker Brygge mot Oscarsborg fredag kl 1500. Noen lag hadde gjort lignende løsninger før og hadde nødvendigvis kommet lenger enn andre på kortere tid, men alle hadde en fungerende løsning da konkurransen skulle starte lørdag formiddag.

En utskrift fra posisjonsloggen viser at det var greit trykk fram til kl 0330 på natta:

Antall posisjoner/10 min

Antall rapporterte posisjoner per 10 min

De fleste lagene hadde enten basert seg på Google Maps eller Open Street Map – med relativt lite detaljer ute på Oscarsborg. Ett lag hadde derimot klart å dra inn Norge-serien fra Statens Kartverk og kunne navigere med veldig godt kartgrunnlag.

Hardcore curl action

Hardcore Curl Action

Den mest oppsiktsvekkende løsningen, var et Java-lag som hadde satt én mann i auditoriet til å følge med på scoreboard. Han sendte koordinater ut til lagkameratene i felt, som igjen løp ut og sendte bilde av postkoder tilbake til “backendmannen”. Disse registrerte han så i spillets API ved hjelp av en curl-commando. Fullt funksjonelt! Men de vant ikke.

Konkurranseløsningen blei faktisk også hacket av to lag… Som vi skrev tidligere, var det ganske god testdekning på registreringslogikken. Vi hadde selvsagt sjekket at antall effekter gikk ned når en brukte dem. Det gikk derimot helt fint å bruke en effekt som du ikke hadde i beholdningen. Java 2 la ut mange feller.

Alle API-metoder som scoreboard-løsningen brukte, var beskyttet av en hemmelig API-nøkkel. Det vi glemte litt, var at scoreboardet ble presentert ved oppstart fredag ettermiddag, og det var selvsagt ett lag (Microsoft 2) som noterte url’en og fikk tak i denne hemmeligheten. Laget implementerte funksjonalitet i klienten som viste alle både aktive og skjulte poster, inkl visualisering av data som vanlige klienter ikke fikk ut i gamestate (som hvor det var feller). Vi får ta selvkritikk for dårlig sikkerhet, men det mest oppsiktsvekkende var i grunn at laget ikke vant.

"Black Hat"-klienten med oversikt over alle posisjoner

«Black Hat»-klienten med oversikt over alle posisjoner

Event-feeden funka også. Her er sitat fra en periode i spillet:

  • Microsoft 2 registrerte en post og fikk 70 poeng.
  • Java 3 registrerte en post og fikk 70 poeng.
  • Java 3 sprengte en post i lufta.
  • Java 2 registrerte en post og fikk 60 poeng.
  • Java 2 rigget en felle på en post.
  • Microsoft 1 utløste en felle rigget av Java 2 og mistet 50 poeng.

Ettersom felle-bonanzaen fra Java 2 rota til resultatlista ganske kraftig, måtte vi rekalkulere litt i full fart. Det var fremdeles Java 2 som vant konkurransen (gratulerer), men det ble litt jevnere bakover i lista. Martin Gravråk (Microsoft 2) ble Most-Valuable-Player.

Videre muligheter

Dagens serverløsning er designet til å bli kodet av 2-3 personer på minst mulig tid, og å støtte spill med ca 40 personer. Den er ikke “overdesignet” til å utnytte mulighetene til en N-Tier+ arkitektur og skalere ut i bredden til å hoste en “e-sport event” med tusenvis av deltakere og noen millioner seere som følger med på hvert sitt scoreboard. Det hadde derimot vært en artig øvelse å gjøre – iallfall på tegnebrettet. Det er mange problemstillinger som fort baller på seg når en vil skalere ut over en enkelt maskin. Vi får ordne en fagkveld rundt dette en gang snart.

Det er flere muligheter for å gjøre mindre refactoring-øvelser på løsningen ved å gå over til asynkron I/O (med async/await) for å skalere enda bedre i høyden. Det kunne også vært artig å implementere SignalR slik at klientene får pushet informasjon, kanskje ikke hele gamestate, men i det minste beskjed om at det er noe nytt å hente.

Postregistrering

Postregistrering

Det hadde også vært artig å dra inn noen fysiske sensorer i opplegget – f.eks. rundt postregistrering. En enkelt justering hadde vært å ha QR-koder hengende ute i terrenget som deltakerne kunne brukt til registrering. Alternativt kunne en hatt aktive skjermer ute, eller beacons som deltakernes klienter skulle hatt interaksjon med.

I selve spillet kunne en tenkt seg å implementere achievements for ulike ting lagene oppnår underveis, og flere effekter: f.eks. 2xscore eller en felle som bare går av hvis et lag fra motsatt leiren (Java/Microsoft) prøver å registrere posten. Også kunne en latt seg inspirere av skytespillsjangeren og latt lagene kjøre effekter utenfor postregistrering – som en “drone”-effekt som gjør at laget ser andre posisjoner, og riggede feller i en liten periode. Ev. gi en av deltakerne på laget spesielle egenskaper som å se feller, som må kommuniseres ut til de andre. Mye moro.

Ellers så skjønner en hvorfor populære storspill kjører betaperioder for å balansere spillmekanikken. Plassering av poster med høy verdi, samt hvor bratt poengverdien for en post sank etterhvert som lagene registrerte den, hadde stor effekt på resultatet. Det burde kanskje gitt bedre uttelling å være tålmodig å sanke poster selv om en ikke var først i løypa.

Den tekniske serverløsningen ble laget generelt og kan kjøres hvor som helst. Kanskje vi skal kjøre en mer urban runde i byen en gang?

Selv om det var ting å pusse på, og at løsningen blei “hacket” på to ulike måter, må vi si at det blei ei bra helg på Oscarsborg. Alle deltakerne gutset skikkelig og det var utrolig artig å se GPS-prikkene suse rundt på kartet etterhvert som poengene trillet inn. Vi lar en kollegas kommentar på tirsdag avslutte: “- Fy søren, jeg er støl ennå”.

Hjemover igjen

Hjemover igjen

rematch_logo2

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *

Magic Mirror – version 1

Introduction A while back I discovered the exciting world of “magic mirrors”. I don’t remember how or where it caught my attention, but..

DevOpsDays Oslo 2016

5.-6. september hadde eg gleden av å delta på den første norske DevOpsDays i Oslo. Her er en oppsummering av høydepunktene..

Bouvet at JavaZone 2016

This year JavaZone celebrated it’s 15th year with with 3000 attendees and over 170 sessions. As one of Norway’s premier Java..

IT years are like dogs years

One of the characteristics of the IT industry is that time works differently for us. This is challenging and fun,..

The Future of SharePoint

Den 4. mai holdt Microsoft en virtuell event om fremtiden til SharePoint, jeg fikk heldigvis anledning til å delta de..

SharePoint 2016 er på vei!

Som lovet var Microsoft ferdig med utviklingen av SharePoint Server 2016 (RTM – release to manufacturing) rett før påske, og..

En skybasert integrasjonsplattform

SAP HANA Cloud Integration SAP HANA Cloud Integration (HCI) er en skybasert integrasjonsplattform. Denne kan benyttes i stedet for SAP..

IoT Juleverksted

Tradisjonen tro var det i starten av desember tid for det årlige juleverkstedet hos Bouvet i Sandvika. Dette var tredje..

Key takeaways fra JavaOne 2015!

JavaOne er verdens største Java-konferanse og arrangeres hvert høst i San Francisco. Her kan du oppleve foredrag fra noen av de..

Teknologi

I Bouvet er vi flere hundre teknologer som brenner for å programmere og utforme gode, digitale løsninger. I denne bloggen utforsker vi teknologien og deler det vi finner med dere.