, , ,

Multiplayer Web-Game mit AWS | Schiffe versenken

Leon Obertopp, bg050

Projektidee:

Im Rahmen der Vorlesung “Software Development for Cloud Computing” sollen die Studierenden in Gruppen ein eigenes Projekt, mit Hilfe von in der Vorlesung gezeigten Cloud Technologien umsetzen. Wir hatten Anfangs Probleme ein geeignetes Thema zu finden, da unser Wissenstand im Thema Cloud nicht besonders groß war. Letztendlich haben wir uns dazu entschieden ein Multiplayer Spiel im Web umzusetzen, welches auf ein serverless Backend setzt, welches in der AWS-Cloud sitzt. Dabei wollten wir uns ein bisschen mehr herausfordern und ein Spiel als Vorlage nehmen, welches zwar einfach umzusetzen ist, aber auch seine Kniffe hat.
Letzen Endes fiel unsere Entscheidung darauf eine Version des bekannten Spiel “Schiffe versenken” umzusetzen. “Schiffe versenken” hat ein relativ einfaches Spielsystem und benötigt keine komplizierten Berechnungen und eignet sich daher gut für ein erstes Übungsprojekt im Thema Cloud. Wir hatten Anfangs die Sorge, dass der Projektscope zu klein ist und haben uns bereits Möglichkeiten überlegt, welche das Spielprinzip von “Schiffe versenken” interessanter machen, wie z.B. ein Multiplayer mit mehr als zwei Spielern, ein Matchmaking-System, Power-Ups, verschiedene Karten, und noch mehr. Dazu sind wir allerdings aufgrund der unterschätzten Komplexität nicht mehr gekommen.

Planung und erste Mockups:

Als erstes haben wir uns überlegt, was unsere Applikation im ersten Schritt können soll, dafür haben wir uns sowohl mit den Technologien als auch mit dem Prinzip des Spiels Schiffe versenken auseinander gesetzt und haben darauf hin einen ersten groben Projektplan entworfen:

  • 1. Schritt
    • Entwicklung eines Hauptmenüs, in welchem der Spieler ein neues Spiel erstellen oder einem bestehenden Spiel mit einem Lobby Code beitreten kann. Außerdem haben wir an dieser Stelle auch bereits eine Möglichkeit für Einstellungen und Matchmaking konzeptioniert auch wenn wir dazu nicht mehr gekommen sind.
  • 2. Schritt
    • Entwicklung einer Lobby, in welcher die Spieler ihre Spielfelder bauen und sich anschließend bereit setzen können, damit der Host anschließend das Spiel starten kann.
  • 3. Schritt
    • Entwicklung des Spielfelds und des Spiels selber, nach den klassischen Regeln von Schiffe versenken.
  • 4. Schritt
    • Entwicklung von unseren Erweiterungen des Spielprinzips von Schiffe versenken.

So ein Projektplan ist zwar ganz gut, aber wir mussten schnell feststellen, dass wir oft Dinge vergessen oder übersehen haben, sodass das Endprodukt durchaus anders war als ursprünglich gedacht.

Erste Mockups

Architektur und Technologien:

Als nächstes haben wir uns überlegt, welche Technologien wir für unser Projekt nutzen wollen und welche Lernerfahrungen wir damit machen wollen.

  • Frontend:
    • Next.js als Frontendframework
    • Node.js für den Build
    • Typescript / TSX
    • Erst Static Web Hosting mit AWS S3 später normales Hosting mit Vercel
  • Backend:
    • Node.js Runtime (erst 20.x später umgestellt auf 24.x, da der Support für 20.x im September von Amazon eingestellt wurde.)
    • AWS Lambda Funktionen
    • DynamoDb (NoSql Datenbank)
    • AWS Cloudwatch zum debuggen
    • Terraform Konfiguration für automatisches Deployment des gesamten Backends

Die Schnittstelle zwischen Frontend und den Lambda Funktionen bildet ein AWS ApiGateway, auf welchem unser WebSocket läuft. Wir haben uns überlegt, ob wir lieber mit einer REST Schnittstelle arbeiten wollen, haben uns aber dagegen entschieden, da wir beide bisher noch nicht mit Websockets gearbeitet haben und wir somit hier eine weitere Lernerfahrung einbauen konnten. Außerdem hat sich später herausgestellt, dass im Kontext eines 1 vs. 1 Game ein WebSocket die bessere Wahl war.

Als Architektur haben wir uns für eine “Serverless Web Application” Architektur in AWS entschieden. Wir haben uns nur kurz mit anderen Cloud Providern beschäftigt, da wir der Meinung waren, dass das umfassende Free Tier von AWS ein guter Einstiegspunkt in die Cloudwelt ist. Außerdem wurde im Kontext Serverless in der Vorlesung hauptsächlich AWS vorgeführt, weswegen uns die Entscheidung dahingehend relativ einfach viel.

Serverless bedeutet, dass wir keine Server verwalten müssen. AWS übernimmt Skalierung, Wartung und Abrechnung nach Nutzung. Das spart Zeit und Kosten, vor allem in der frühen Projektphase. Nachteil bei unserer Architektur mit AWS ist, dass wir uns damit stark von Amazon abhängig machen (Vendor Lock-in) und wir teilweise in der Ausführungszeit und der Konfiguration eingeschränkt sind. Bei großen Projekten mit komplexeren Funktionen eignet sich eine vollständig Serverless Applikation also eher nicht. Hier könnte man dann eher auf ein Backend in Container-Architekturen wie Kubernetes nutzen. Diese bieten mehr Kontrolle und Flexibilität, erfordern aber deutlich mehr Aufwand im Setup und Betrieb. Für unser kleines Projekt hat sich die Serverless Architektur als sehr praktisch erwiesen.

Projektvorstellung:

Im Nachfolgenden Abschnitt stellen wir kurz den Stand unseres Projektes zum Zeitpunkt der Abgabe vor. Sobald man die Domain aufruft, wird man automatisch in das Menü weitergeleitet. Dort muss man zuerst seine Schiffe platzieren. Wir haben uns gegen das platzieren in der Lobby entschieden, damit die Lobby nicht so lange aufrecht erhalten werden muss, dazu aber später mehr. Das kann man per Drag & Drop machen. Wählt man ein Schiff auf dem Spielfeld aus, wird es rot umrandet und man kann mit der R-Taste das Schiff um 90° drehen. Die ausgegrauten Buttons sind Features die wir zeitlich nicht mehr entwickeln konnten. Der Debug Button logt uns das Schiff Spielfeld in die Konsole, das war für das debuggen enorm wichtig, da das erstellen der Spielfelder nicht ganz so einfach war, wie anfangs gedacht.

Sofern ein Spieler Schiffe platziert hat, kann er über Spiel erstellen und Spiel beitreten. Hierbei öffnet sich ein Dialog Fenster in welchem der Spieler seinen Namen eingeben kann und wenn man einem Spiel beitritt wird auch noch ein Lobbycode benötigt, welcher initial vom Backend beim erstellen eines Spiels erstellt wird. Sofern alles richtig funktioniert hat, wird man in die Lobby weitergeleitet, in welcher sich beide Nutzer bereit setzen können. Anschließend kann der Host des Spiels das Spiel starten.

Sobald der Lobby-Ersteller das Spiel startet, werden beide Nutzer auf die Seite /game weitergeleitet und das Spiel beginnt, dabei ist der Host des Spiels immer als erster dran. Solange man einen Treffer erzielt, kann man weiterraten, ansonsten ist der Gegner dran. Das Spiel ist beendet, sobald alle Schiffe eines Spielers gefunden wurden. Dann kann man zum Hauptmenü zurückkehren und das Spiel von vorne spielen. Eine Regel des Spiels haben wir dabei nicht eingebaut, normalerweise muss zwischen Schiffen immer ein Feld Platz sein, da wir aber große Probleme mit dem erstellen der Spielfelder hatten, gerade was die Validierung dieser anging, haben wir diese Regel sowohl im Spiel als auch in der Erstellung nicht beachtet.

Frontend:

Wie bereits erwähnt, haben wir uns entschieden, das Frontend mit NextJS umzusetzen, da wir damit schon erste Erfahrungen hatten und man in NextJS mit dem neuen App-Router sehr einfach Single Page Applikationen umsetzen kann. Der ganze Code soll dabei lokal im Browser laufen. In NextJS benötigt man dafür client-Components. Diese werden zu Beginn jeder Komponente als solche deklariert. Diese client-Components werden einmal generiert und dann werden dieselben Dateien an die Benutzer geschickt. Bei Server-Components hingegen können die zurückgegebenen Dateien je nach Anfrage auch anders aussehen.

Zuerst war geplant, das NextJS-Projekt einmal zu bauen und dann in einen S3-Bucket hochzuladen. Dort gibt man die index.html-Datei an, auf die der Benutzer beim Besuchen der Seite geleitet wird. Dann hat man eine statische Website mit html-, CSS- und JavaScript-Dateien, die unverändert an den Browser geschickt werden. Das hat einige Vorteile und Nachteile: Die Dateien lassen sich gut in Content Delivery Networks (CDN) cachen, wodurch die Seite schnell geladen wird. Außerdem ist kein kompliziertes Server-Backend notwendig, sondern es müssen nur Dateien irgendwo hochgeladen und zur Verfügung gestellt werden. Das macht es sehr kostengünstig. Außerdem sind solche statischen Seiten sehr sicher, weil kein serverseitiger Code ausgeführt wird, der Schwachstellen haben könnte.

Allerdings bringt diese Methode auch einige Nachteile mit sich, die uns dazu gebracht haben, das ganze doch nicht so umzusetzen:

Zuallererst hat es nicht sofort ohne Probleme geklappt. Sobald man das NextJS-Projekt gebaut hat, sah die Seite die da heraus gekommen war sehr anders aus (siehe Abbildung). Offenbar konnten die CSS-Attribute für eine statische Seite so nicht genutzt werden. Das Frontend war allerdings schon ziemlich fortgeschritten, weshalb eine Änderung hier einen deutlichen Aufwand bedeutet hätte. Außerdem können Funktionen von NextJS bei den statischen Seiten nicht genutzt werden, wie z.B. der App-Router. Dieser bewirkt, dass sich die Ordnerstruktur in der URL widerspiegelt. Hat man z.B. den Ordner “menu”, dann erhält man beim Aufruf von “example.com/menu” die page.tsx-Datei aus diesem Ordner. Dieses Routing wird beim Bauen der Seite für eine statische Seite nicht übersetzt. Somit funktioniert das Routing nicht mehr. Auch das Auslesen von URL-Parametern (z.B. “example.com/lobby?gameId=U4FG56”) hätte Probleme bereitet. Somit haben wir uns gegen das Hosten eine statischen Seite entscheiden und haben die Seite auf Vercel, dem Entwickler hinter NextJS, gehostet. Trotzdem haben wir darauf geachtet, möglichst viele Client-Komponenten einzubauen, um die Interaktivität mit der Seite z.B. beim Drag and Drop der Schiffe oder dem Nutzen von Browser-APIs wie das Kopieren des Lobby-Codes über einen Knopf zu ermöglichen.

Statisch gebautes Hauptmenü (entstanden aus einer früheren Version des Hauptmenüs)

Automatisches Deployment:

Zur Entwicklungszeit war es sehr praktisch den lokalen NextJS-Entwicklungsserver zu nutzen. Gegen Ende haben wir uns aber für das Deployment eine GitLab-CICD-Pipeline gebaut. Diese baut bei jedem push auf GitLab das Projekt und lädt die gebauten Dateien auf Vercel hoch. Dabei sieht Vercel den Quellcode nicht, weil der Code vom GitLab-Server gebaut wird. Wird auf den main-Branch gepusht, ist die gebaute Seite auch gleich öffentlich aufrufbar. Bei anderen Branches wird ein Preview-Build gebaut, der nur mit Authentifizierung bei Vercel aufrufbar ist. Allerdings eignet sich das nicht für unser Projekt, da im free plan nur ein Nutzer Teil des Projekts sein kann.

Arbeiten mit Websockets:

Als das Frontend mit dem Backend verbunden wurde, musste ich erstmal herausfinden, wie eine Verbindung mit dem WebSocket möglich ist. Dafür habe ich zuerst fälschlicherweise die Bibliothek socket.io genutzt. Socket.io ist ein eigenes Protokoll, das über eine http-Verbindung anstatt direkt über WebSockets läuft. Mit dem nativen WebSocket-Protokoll konnte ich eine Verbindung mit dem WebSocket über das API-Gateway aufbauen, sobald die Seite erstmalig geladen wird. Da der WebSocket irgendwann timeoutet, wird mittlerweile die Verbindung erst aufgebaut, sobald man auf “Spiel erstellen” oder “Spiel beitreten” klickt.

Das Objekt, mit dem man die WebSocket-Verbindung nutzen kann, um z.B. Nachrichten zu senden oder zu empfangen, wird in der Root-Komponente “GameWrapper” erstellt und initialisiert und an die darunterliegenden Komponenten weitergegeben. Später hat es sich als nützlicher herausgestellt, alle WebSocket-Funktionen in der Komponente zu kapseln und nur noch Funktionen anzubieten, die das Objekt nutzt. So gibt es z.B. eine sendMessage-Funktion, die eine Nachricht bestehend aus Key-Value-Paaren an das Backend schickt. Ein Aufruf sieht z.B. so aus:

{"action": "setReady", "gameId":"...", "isReady": true}

Da jeder Aufruft und jede Antwort eine “action” enthält, ist dies ein extra Feld. Somit ist der erste String, den man der sendMessage-Funktion mitgibt, diese action (z.B. setReady, joinGame, createGame, playerJoinedResponse, …). Danach gibt man ein Objekt aus Key-Value-Paaren mit. Der Aufruf für die oben beschriebene Aktion sieht dann so aus:

sendMessage("setReady", {gameId: gameId, isReady: true})

Im GameWrapper werden außerdem alle Nachrichten empfangen und in einem Array “messages” gespeichert. Auf dieses Array können die child-Komponenten zugreifen und Listener einbauen, die auf bestimmte Befehle hören. Sobald z.B. die Nachricht “otherPlayerReadyStatus” empfangen wird, wird der Inhalt überprüft und dann im Frontend dargestellt (indem der Gegner z.B. grün angezeigt wird).

Dieses Senden und Empfangen der Nachrichten ermöglicht den Echtzeit-Betrieb der Applikation und bildet die Basis, durch die Informationen an das Backend geschickt und vom Backend empfangen werden.

Backend:

Unser Backend besteht im Grunde auf zwei Teilen. Unseren Lambda Handlern, welche wir mit der Node.js Runtime geschrieben haben und einer Terraform Konfiguration, welche alle unsere AWS Komponenten bei Amazon automatisch deployed. Dabei Nutzen wir für unser Backend einen AWS ApiGateway, welches als WebSocket läuft, DynamoDb als persistenten Speicher und AWS Lambda Funktionen für die Serverless Architektur. Ursprünglich haben wir überlegt unser Backend mit dem Serverless Framework umzusetzen. Uns wurde allerdings empfohlen, dies aufgrund von Einschränkungen in Verbindung mit den anderen Cloud Komponenten nicht zu nutzen. Diese Einschränkungen haben sich nach etwas Recherche nicht wirklich bestätigt, allerdings haben wir parallel zu dieser Vorlesung noch die Vorlesung “Software definied Infrastructure” besucht, in welcher der Umgang mit Terraform gelehrt wird. Daher war es naheliegend für unser Projekt auch Terraform zu nutzen und unser Wissen dahingehend zu vertiefen.

Wir haben in der Entwicklung des Backends von Anfang an auf die Automatisierung des Deployment auf AWS gesetzt. Wir haben also kaum die Web Oberfläche von AWS genutzt sondern alles direkt mit Terraform gemacht. Dies hat zu relativ großen Problemen geführt, da wir Anfangs die einzelnen Komponenten nicht zum laufen bekommen haben. Das lag an zwei größeren Problemen. Wir haben uns initial an einem Tutorial für die Entwicklung von Lambda Funktionen zusammen mit DynamoDb orientiert. Das Tutorial war allerdings schon etwas älter, und nutzte noch eine alte Package Struktur von AWS, bedeutet unser Code wurde von AWS gar nicht gebaut, da die alten Packete gar nicht mehr von der Runtime unterstützt wurden. Herausgefunden haben wir das, indem wir auf der Weboberfläche das Testing genutzt haben, wodurch man direkt logs bekommen hat. Das war allerdings ziemlich umständlich, da jedes mal wenn man die Terraform-Config neu deployed hat diese Tests gelöscht wurden.

Arbeiten mit Cloudwatch:

Wir hatten aber noch ein weiteres Problem. Die Kommunikation zwischen den Lambda Funktionen und DynamoDb hat immer zu Fehlern geführt. In dem Testing Environment konnte man nicht wirklich sehen warum. Man muss initial wenn man diese Komponenten mit Terraform deployed eine IAM Policy erstellen, welche bestimmt worauf die Lambda Handler zugreifen dürfen. Ursprünglich dachten wir aufgrund des Tutorials das Cloudwatch kostenpflichtig ist. Daher haben wir dieses Feature nicht genutzt. Nachdem sich allerdings herausgestellt hat, dass dies nicht so ist, konnten wir mit Cloudwatch schnell die Probleme lösen, da die Fehlermeldungen hier viel ausführlicher dargestellt wurden. Mit Cloudwatch konnten wir feststellen das wir die Rechte für Scan und Query vergessen haben und dadurch konnten unsere Lambda Funktionen ihre Aufgabe nicht erfüllen. Es gab immer wieder Probleme mit den Rechten, sodass wir am Ende eine recht umfangreiche Policy erstellt haben.

Cloudwatch hat uns auch im verlaufe des Projektes sehr viel geholfen, wenn uns JS irgendwelche “undefined” Errors etc. geliefert hat.

WebSocket und Lambda Funktionen:

Unsere WebSocket Konfiguration haben wir relativ einfach gehalten. Wir haben nur die Standardrouten umgesetzt, welche unsere Lambda Funktionen ausführen, dabei unterscheiden wir zwischen der Connect, Game und Disconnect Route.

  • Connect
    • Der Connect Handler kümmert sich um die Verbindung eines Clients zum WebSocket. Außerdem speichert der Handler die connectionId des Nutzer in der DynamoDb Tabelle “Connections”. Da wir keine Authentifizierung in unserem Projekt eingebaut haben, dient uns diese ID als Identifizierung eines Nutzers.
  • Disconnect
    • Des Disconnect Handler löst die Verbindung eines Clients zum WebSocket auf. Außerdem wird geprüft, ob es ein Spiel gibt mit welchem der Nutzer assoziiert wird. In diesem Fall wird auf Basis des Game Status geprüft ob das Spiel beendet wird und anschließend gelöscht wird, damit wir keine unnötigen Daten auf der Datenbank haben.
  • Game
    • Der Game Handler ist das Herzstück unseres Backends. Hier ist die gesamte Spiellogik enthalten. Der Handler selber entspricht der Default Route eines Websockets, bedeutet alles was nicht Connect oder Disconnect ist wird an diese Route weitergeleitet. Die Entscheidung, welcher Teil vom Code ausgeführt wird, wird von einem Switch Case am Anfang des Handlers entschieden, welcher basierend auf der “action”-Property des Body entscheidet was passiert.

Zusammen mit mehreren Hilfsfunktionen bildet dies dann unsere Spiellogik.

DynamoDb:

Lambda Funktionen sind stateless, um also Daten zu speichern und Spielstände zu prüfen, nutzen wir DynamoDb als Speicher. Anfangs war es etwas kompliziert sich an den NoSql Gedanken zu gewöhnen, aber man hatte den Dreh relativ schnell raus. Wir haben für unser Projekt zwei Tabellen genutzt.

  • Connections-Table
    • Diese Tabelle enthält alle aktiven WebSocket Verbindungen und die Information in welchem Spiel sich eine Verbindung im Moment befindet.
  • Games-Table
    • Diese Tabelle enthält unsere Spielstände. Die Spielstände sind dabei ein großes JSON Objekt, welches sowohl die Informationen zu beiden Spielern, als auch die Spielfelder enthält. Außerdem enthält das Objekt einen Schlüssel GameId, mit welchem wir ein Spiel klar identifizieren können. Eine lobbyCode, mit welcher ein Spiel über den lobbyCode Index identifiziert werden kann, und eine Information darüber welchen Status ein Spiel gerade hat.

Beispiel Spiel als JSON:

Während der Entwicklung haben wir oft viel Datenmüll angesammelt, wie bereits erwähnt haben wir daher mit Hilfe des Disconnect Handlers dafür gesorgt das alle Spielstände früher oder später gelöscht werden, je nachdem wie der Spielverlauf aussieht und ob ein Spieler die Verbindung verliert.

Tests:

Abgesehen von den manuellen Tests haben wir uns lange nicht um Tests gekümmert. Zum Ende hin haben wir für unser Backend noch Unit Tests geschrieben. Dabei haben wir ein Package von AWS genutzt, welches die DynamoDb mocken kann. Zusammen mit diesem Package haben wir alle Funktionen durchgetestet. Leider hatten wir keine Zeit mehr auch größere Integrationstests einzubauen. Sodass zum Beispiel die Verbindung mit dem WebSocket gar nicht getestet wird. Die Unit Tests werden automatisch bevor das Projekt via Vercel deployed wird mit der CI/CD Pipeline ausgeführt.

Weitere Herausforderungen:

Zeitmanagement:

Eines der größten Probleme die wir abseits der Rechte Probleme hatten, war unser Zeitmanagement. Wir hatten zwar relativ früh im Semester uns ein Thema herausgesucht, haben aber erst recht spät mit der eigentlichen Entwicklung angefangen, sodass wir zum Zeitpunkt der Endpräsentation nur einen MVP hatten, der in keiner Weise funktional war. Grund dafür war unter anderem das wir während der Vorlesungszeit so gut wie gar nicht daran gearbeitet hatten und dann während der Prüfungsphase mit anderen Projekten mit früherer Deadline beschäftigt waren. Wenn wir unser Zeitmanagement besser hinbekommen hätten, hätten wir am Ende nicht so einen Stress gehabt und hätten vielleicht auch unsere ursprünglich erdachten Extras einbauen können.

Unterschiede zwischen dem lokalen und dem Vercel Build:

Uns ist gegen Ende aufgefallen, dass man sich theoretisch selber blockieren kann, indem man anfängt selber im Web umher zu routen. Bedeutet entweder man springt manuell über die URL in Routen in die man eigentlich nicht soll, oder man nutzt f5 oder “vor/zurück”. Gerade wenn man hier in die Game Route springt kommt man aufgrund der Ladeanimation nicht mehr ohne weiteres aus der Route raus. Wir wollten daher ein automatisches Routing einbauen, welches verifiziert das ein Nutzer immer noch die Erlaubnis hat in einer Route zu sein. Dieses Problem ließ sich auch auf dem Lokalen Build relativ einfach lösen. Sofern keine GameId vorhanden ist oder keine WebSocket Verbindung existiert wird man zurück ins Hauptmenü geschmissen. Auch wenn man f5 drückt, soll man zurück ins Hauptmenü kommen, da wir die Nutzer ja über die ConnectionId identifizieren und man daher nach einem Verlust der Connection sowieso nicht mehr in das selbe Spiel zurück kann. Beides hat lokal funktioniert, komischerweise aber nicht auf Vercel. Wir konnten das Problem aufgrund der begrenzten debug Möglichkeiten auf Vercel leider nicht eingrenzen. Es scheint etwas mit dem prüfen auf die WebSocket Verbindung in einem Next.js Effect zu tun zu haben, aber wir konnten das Problem leider nicht lösen. Um seltsame Seiteneffekte zu vermeiden haben wir das Feature erstmal ausgebaut.

Security:

Nachdem wir unser Projekt automatisch auf Vercel deployt haben, haben wir uns auch Gedanken über die Sicherheit gemacht. Da wir keine personenbezogenen Daten verarbeiten, schien das Thema anfangs nicht wichtig. Später hatten wir jedoch die Sorge, dass unser öffentlicher WebSocket potenziell missbraucht werden könnte und dadurch hohe Kosten entstehen.

Als erste Maßnahme haben wir deshalb eine Origin-Prüfung eingebaut, sodass der WebSocket ausschließlich von localhost und unserer Vercel-Domain aus aufgerufen werden darf. Auch wenn das keine echte Sicherheit darstellt, erschwert es zumindest automatisierte Angriffe aus fremden Umgebungen. Außerdem hatten wir die Idee, zu Beginn jeder Session ein Token an die Nutzer auszugeben und dieses als Autorisierung für die WebSocket-Verbindung zu verwenden. Diese Lösung hätte einen deutlich besseren Schutz geboten, da so nur Clients mit gültigem Token eine Verbindung aufbauen könnten. Aufgrund der begrenzten Zeit im Projekt am Ende haben wir diesen Ansatz allerdings nicht mehr umgesetzt.

Learnings und Fazit:

Learnings:

  • AWS Grundlagen:
    • Zustandlose Serverless Funktionen mit AWS Lambdas, Amazon kümmert sich um die Skalierung und den Betrieb der Funktionen, wir müssen sie nur schreiben und deployen.
    • WebSocket API mit AWS API-Gateway: Verbindung zwischen Frontend und Backend mit Hilfe von Websockets. Der WebSocket wird bei Amazon deployed und verknüpft unsere Frontend Logik mit unseren Lambda Funktionen.
    • Zustandsspeicherung zwischen den einzelnen Funktionsaufrufen mit DynamoDb, einer NoSql Datenbank, welche sowohl unsere WebSocket Verbindungen als auch unsere Spielstände persistent speichert.
    • Auch wenn wir uns letztendlich gegen statischen Web Hosting in einem S3 Bucket entschieden haben, haben wir beim testen dieser Vorgehensweise dennoch viel neues gelernt.
    • Rechte Management (AWS IAM) sowohl bei den einzelnen Komponenten als auch bei den Nutzern.
    • Debuggen der Lambda Funktionen mit AWS Cloudwatch
  • Frontend Entwicklung:
    • Tiefere Einblicke in Next.js und der internen States des Frameworks
    • Dynamisches Routen mit Parametern in der URL
    • Nutzung einer externen WebSocket API in unserem Frontend Code
    • Automatisches Deployment der Webseite auf Vercel
  • Backend Entwicklung:
    • Automatisches Deployment des Backends mit Terraform
    • Umgang mit .env Dateien uns sensiblen Daten im Code
  • Sonstiges:
    • Planung und Zeitmanagement sollte frühzeitig gemacht werden (und sich auch dran gehalten werden)

Fazit:

Wir sind mit dem Ergebnis welches wir zum Zeitpunkt der Abgabe erreicht haben durchaus zufrieden. Wir haben es geschafft das Spiel in seinen Grundzügen umzusetzen und konnten unser neues Wissen bzgl. Cloud Komponenten gut einbinden. Nachdem wir unser Zeitmanagement unter Kontrolle bekommen haben und die größten Probleme mit den Rechten und dem Access der Cloud-Komponenten untereinander gelöst haben, haben wir schnell Fortschritt gemacht und am Ende eine spielbare Version unseres Projekts erreicht.

Auch wenn wir aufgrund der vielen Probleme am Ende unsere eigenen Ideen zur Erweiterung des Spiels nicht einbringen konnten, haben wir trotz des etwas kleineren Projektscopes viele neuen Erfahrungen gemacht, welche wir zukünftig weiter nutzen können. Daher können wir insgesamt mit dem Ergebnis des Projektes zufrieden sein.

Comments

Leave a Reply