Dokumentation

Version 0.1.3 - Genereret mandag den 11. marts 2024

Kontakt

Har du spørgsmål, fundet en fejl eller idéer til forbedringer? Så er du meget velkommen til at kontakte os:


+45 53 64 09 34
anders@immer.co

Terminologi

container/container-element
DOM-elementet det enkelte spil "lever" i.
Klient
Konteksten, spillet bliver indlejret i — dette kan f.eks. været et website eller mobil-applikation.
Indgående handlinger
Handlinger fra klienten, som sendes til spillet.
Udgående handlinger
Handlinger, sendt fra spillet, som klienten kan lytte til.
Udvekslings-ID
En unik nøgle for hver registreret bruger, der bruges til at udveksle indhold mellem Immer’s API og klienten uden at identificere den enkelte brugers oplysninger.
Sessions-ID
En unik nøgle tildelt af klienten, der kan bruges til at gemme spildata på brugere, som ikke er registreret endnu.
Klient API-nøgle
En API-nøgle, der bruges til at sende og modtage indhold fra Immer’s API, men med begrænset rettigheder. Denne nøgle bruges til at sende og hente data på de enkelte spil og den enkelte spillers statistik.
API-nøgle
En API-nøgle, der ikke eksponeres for klienten, med flere rettigheder. Denne nøgle kan f.eks. bruges til at hente data på tværs af spillere og spil, eller udtræk af events og lignende.

Hurtig start

Immer’s spil bliver aktiveret ved at tilføje et HTML-element og script-tag til siden eller web-viewet, det skal indlejres i.

Eksempel

I dette eksempel indlejre vi spillet Gitter på en almindelig HTML-side:

<html>
  <head></head>
  <body>
    <!-- HTML-elementet spillet skal indlejres i -->
    <div id="immer-gitter" data-key="DIN_KLIENT_API_NØGLE_HER"></div>

    <!-- Henvisning til JavaScript-koden der eksekverer spillet -->
    <script src="https://acme.immerspiele.com/games/gitter/gitter.js"></script>
  </body>
</html>

Opsætning

Et spil kan konfigureres med forskellige attributter. Disse kan sættes på spillets container-element eller via. en global JavaScript-funktion.

Opsætning på spillets container-element

<div id="immer-gitter" data-key="DIN_KLIENT_API_NØGLE_HER"></div>

Opsætning via. global JavaScript-funktion

Funktionen skal hedde immerSetup og returnere et objekt. Denne funktionen skal defineres inden selve scriptet til spillet bliver indlæst.

<script>
  function immerSetup() {
    return {
      key: 'DIN_KLIENT_API_NØGLE_HER',
    };
  }
</script>

<!-- Henvisning til JavaScript-koden der eksekverer spillet -->
<script src="https://acme.immerspiele.com/games/gitter/gitter.js"></script>

Egenskaber

Data-attributObject-nøgleBeskrivelse
data-keykeyKlient API-nøgle som bruges til at hente og sende data til Immer’s API.
data-login-urlloginUrlURL til login-side. Hvis denne ikke er sat, vil spillet sende en udgående handling med ID 1 når brugeren ønsker at logge ind.
data-proxyproxyURL til en server-side proxy, der kan bruges til at skjule klient API-nøglen og spillerens ID. Se Grundlæggende for mere information.
data-player-idplayerIdUdvekslings-ID for den aktuelle bruger. Denne bruges til at hente og sende data til Immer’s API.

Sikkerhed mellem klient og API

Når en bruger starter, spiller eller afslutter et spil skal informationen sendes til Immer’s API så sikkert og enkelt som muligt.

Grundlæggende

Implementeringer

Server-side “proxy” 🔒🔒🔒

Vi vurderer, at denne løsning er den mest sikre og holdbare da ingen oplysninger eksponeres i klienten.

Alle forespørgsler, der sendes til Immer’s API, kører igennem en server-side “proxy”. Denne proxy beriger alle forespørgsler med en API-nøgle og et udvekslings-ID. På denne måde behøver vi ikke afsløre følsomme oplysninger i klienten. Dette giver - potentielt - også en lille hastighedsforøgelse da browseren ikke behøver at sende et ekstra preflight-request pga. CORS.

Eksempel

Når Wørdle starter, laver spillet selv et kald til Immer’s API for at indlæse dagens ord. Hvis brugeren allerede har eller er i gang med at spille, skal dette “state” også genskabes via. API-kaldet.

Eksempel på første forespørgelse i Wørdle hvis brugeren er logget ind:

GET https://acme.immerspiele.com/api/woerdle/latest

# Headers der — som minimum — skal bruges:
Authorization: Bearer tqrnlFGwtXBFRaOALLQiVKVCpi0DCTtyrem8UJ7XuEdUx
X-Immer-Player-ID: 6ac1d26b284a25f733135a5e0eed542b3f6261ec

For at skjule følsomme oplysninger, kan spillet konfigureres til, at kører igennem en proxy:

<div
  id="immer-woerdle"
  data-proxy="https://www.acme.dk/spil-proxy-endpoint"
>
</div>

<!-- Eller, hvis proxy-adressen er på samme host: -->
<div
  id="immer-woerdle"
  data-proxy="/spil-proxy-endpoint"
>
</div>

<!-- Alternativt, kan spillet opsættes med en global JavaScript-funktion: -->
<script>
  function immerSetup() {
    return {
      proxy: '/spil-proxy-endpoint',
    };
  }
</script>

Første forespørgelse i Wørdle vil nu se sådan ud:

GET https://www.acme.dk/spil-proxy-endpoint/woerdle/latest

På server-siden viderestilles forespørgslen til Immer’s API inklusiv API-nøgle og udvekslings-ID (som givetvis findes eller kan fortolkes i et sessions-lag eller via. en cookie):

GET https://www.acme.dk/spil-proxy-endpoint/woerdle/latest

# Headers tilføjet af proxy
Authorization: Bearer tqrnlFGwtXBFRaOALLQiVKVCpi0DCTtyrem8UJ7XuEdUx
X-Immer-Player-ID: 6ac1d26b284a25f733135a5e0eed542b3f6261ec

Andre relevante headers som X-Request-ID og X-Forwarded-For kan også medtages ifht. logning og fejlfinding.

Signeret API-nøgle 🔒🔒

For at begrænse eksponering af klient API-nøglen, kan nøglen signeres server-side, så den er unik pr. udvekslings-ID eller sessions-ID og spil.

Der laves en signeret HMAC-værdi med SHA-256 og følgende værdier:

{navn på spil}+{udvekslings-ID eller sessions-ID} med API-nøglen som den “hemmelige nøgle”. Resultatet præfikses med sign_.

Eksempel
hashed = hmac(
  message: 'wørdle+6ac1d26b284a25f733135a5e0eed542b3f6261ec',
  secret: 'tqrnlFGwtXBFRaOALLQiVKVCpi0DCTtyrem8UJ7XuEdUx'
)

=> hashed: 'e9fa3cb0b12770c744976738b55c99732c507f4fa64e5b895c2a47ce75b49c9e'

key = 'sign_' + hashed

=> key: 'sign_e9fa3cb0b12770c744976738b55c99732c507f4fa64e5b895c2a47ce75b49c9e'

Ved opsætning kan værdien nu bruges som API-nøgle:

<div
  id="immer-woerdle"
  data-key="sign_e9fa3cb0b12770c744976738b55c99732c507f4fa64e5b895c2a47ce75b49c9e"
  data-player-id="6ac1d26b284a25f733135a5e0eed542b3f6261ec"
>
</div>

Klient API-nøgle 🔒

Integrationen til Immer’s API kan også ske via. en klient API-nøgle. Denne løsning er mindre sikker, da API-nøglen og udvekslings-ID’et vil være eksponeret for klienten.

Eksempel

Ved opsætning kan spillet konfigureres med en API-nøgle og et udvekslings-ID (hvis brugeren er logget ind).

<div
   id="immer-woerdle"
   data-key="dT5ElhVVRmbzPz4yO0rB5yhoPbqSF2fun6Y634iGAqIEBNlVZtj11FgH31nZ0iXB"
   data-player-id="6ac1d26b284a25f733135a5e0eed542b3f6261ec"
>
</div>

<!-- Alternativt, kan spillet opsættes med en global JavaScript-funktion: -->
<script>
  function immerSetup() {
    return {
      key: 'dT5ElhVVRmbzPz4yO0rB5yhoPbqSF2fun6Y634iGAqIEBNlVZtj11FgH31nZ0iXB',
      playerId: '6ac1d26b284a25f733135a5e0eed542b3f6261ec'
    };
  }
</script>

Åbn modal-vinduer programmatisk

Der er flere modal-vinduer i spillene, der kan åbnes programmatisk, så de kan integreres i forskellige kontekst - f.eks. en menu, en mobil-app osv.

Et modal-vindue åbnes ved at afvikle et CustomEvent med typen showModal på spillets container-element.

Eventets detail-egenskab skal indeholde et objekt med modal-vinduets ID.

// Opret event
const helpModalEvent = new CustomEvent('showModal', {
  detail: {
    id: 1,
  },
});

// Find container-elementet, som spillet vises indeni.
// I dette eksempel har elementet klassenavnet "game-container".
const container = document.querySelector('.game-container');

// Afvikl eventet på elementet
container.dispatchEvent(helpModalEvent);

Modal-ID'er

IDNavn
1Hjælp & regler
2Statistik
3Tidligere/gårsdagens løsning
4Ledetråde
5Rang
6Kontakt

Indgående handlinger

Udvalgte handlinger, der sker udenfor spillets område, kan sendes fra klienten til spillet. Dette kan f.eks. være, at fortælle spillet, at brugeren er logget ind.

Indgående handlinger kan sendes til spillet ved at bruge et CustomEvent med typen gameAction på spillets container-element.

Eventets detail-egenskab skal indeholde et objekt med handlingens ID.

// Opret event
const authenticateEvent = new CustomEvent('gameAction', {
  detail: {
    id: 1,
  },
});

// Find container-elementet, som spillet vises indeni.
// I dette eksempel har elementet klassenavnet "game-container".
const container = document.querySelector('.game-container');

// Afvikl eventet på elementet
container.dispatchEvent(authenticateEvent);

Handlings-ID'er

IDHandlingBeskrivelse
1Log indBrugeren er logget ind og dette skal reflekteres i spillet.

Udgående handlinger

Udvalgte handlinger, der foretages inde i spillet er tilgængelige for klienten, som spillet indlejres i.

Handlinger sendes fra spillet med et CustomEvent med typen gameEvent på spillets container-element.

Eventets detail-egenskab skal indeholde et objekt med handlingens ID.

// Find container-elementet, som spillet vises indeni.
// I dette eksempel har elementet klassenavnet "game-container".
const container = document.querySelector('.game-container');

// Tilføj event listener til container-elementet
container.addEventListener('gameEvent', (event) => {
  const id = event.detail.id;

  // Brug handlingens ID…
});

Handlings-ID'er

IDHandlingBeskrivelse
1Log indBrugeren ønsker at logge ind
2Opret brugerBruger ønsker at oprette en profil
3SkærmændringSpillet skifter skærm - f.eks. fra "velkomstskærm" til "spil".

Yderligere data på handlinger

Visse handlinger indeholder ekstra information.


IDHandlingStruktur
3Skærmændring
{
  // Handlings-ID
  id: number;
  // Skærm-ID
  screen: number;
}
Se skærm-ID'er

Skærme

Skærm-ID'er

IDNavnBeskrivelse
0IndlæsningVises når spillet indlæses.
1VelkomstVelkomstskærm.
2SpilSpilskærm.
3BreakerVises i spil, der understøtter "breaker"-skærme (f.eks. Satellit).
4Spil afsluttetVises når et spil afsluttes.