The contact as displayed as code is the final say. If there is a discrepency on the image, go with what is written in these docs.
Make sure you have read the above!
Read websockets…

Flow of data

Flow of Data in the project

This is how data flows through out system from a technological point of view. We have a NextJS frontend, that communicate to AWS Lambda functions via AWS API Gateway. We are utilizing various other AWS services; such as AWS DynamoDB, AWS ElastiCache and AWS Cognito.

Error

Status Code: 4XX | 5XX

{
    statusCode: 4XX | 5XX,
    message: ErrorMessage
}

Create Game

Flow of Create Game

Request POST /createGame

{
    options: GameConfigurationOptions,
    description: string
}

Response 200 OK

{
    success: true,
    gameID: GameID,
    options: GameConfigurationOptions
}

Web Socket

Once a game has been created, the user will need to make a request to the WebSocket server that links the gameID to the user’s connection ID (provided by WebSockets, not part of the request body) and marks this user as the host.

//Make sure you match the role capitalization.
{
    action: "game",
    type: "createGame",
    gameID: GameID,
    role: Host //any role other than 'Host' will return 401 UNAUTHORIZED
}

Join Game

Flow of Join Game

Request that the students make to join in on a game.

Request POST /joinGame

{
    gameID: GameID,
    playerName: String
}

Response 200 OK

{
    gameID: GameID,
    playerID: PlayerID,
    data: PlayerData
}

Web Socket

If the request was successful, the client will need to make the following websocket request:

{
    action: "game",
    data: {
        type: "joinGame",
        gameID: GameID,
        playerID: PlayerID,
        role: Host | A | B | C
    }
}

This will then linked the player ID, socket ID, player role and game ID in the connections database.

Start Game

Flow of Start Game

The game host should make this web socket request in order to get the game started, it will be broadcasted to all players.

Web Socket

{
    action: "game",
    data: {
        type: "startGame",
        playerID: PlayerID,
        gameID: GameID
    }
}

This will then go through the connection table, getting all of the connection ID’s for the provided game ID. It will then broadcast a message out to all those clients saying that the game is about to start.

Start of Round

Flow of Start Round

The game will be managed by the host, so that means all actions will be initiated by the host. All starting round messages will be triggered by the host. The host as well as the web socket server should always remain in sync with the data, but the host is final source of data. That means the host will be the timer of the rounds, as well as triggering the respective messages.

There will need to be a timer running on the hosts client that keeps track of how long things are. We could even look at providing toast notifications about how much time is remaining, but that is a QOL for later.

The hosts client will be the initator of all the web sockets in relating to the rounds.

If the current round is greater than 1, it sound process the already existing games from the ElastiCache. It should process all of them and then do one large batch update to the DynamoDB. Have a look at Process Round.

Web Socket

A start round message will be sent to all players in the game. It will include the round number (the round that is starting), when the round is starting and the duration of the round (in seconds).

{
    action: "game",
    data: {
        type: "startRound",
        gameID: GameID,
        playerID: PlayerID,
        round: CurrentRoundNumber,
        startTime: StartTime,
        duration: seconds
    }
}

End of Round

Flow of End of Round

This request will be initated by the game host and broadcast to all players of the game that the round is over and that they should send their data to the server.

Web Socket to Client

Request initated by the game host, indicating that the round has ended. For this, there is X amount of time (represented in seconds), where the player - whatever their role - will do their actions relating to their role. Such as deciding the tax rate, reimbursing roles and handling audits. The player (role B and C) will also have a chance to be audited. That all gets dealt with during this time period.

{
    action: "game",
    data: {
        type: "endRound",
        gameID: GameID,
        playerID: PlayerID,
        round: CurrentRoundNumber,
        startTime: timestamp,
        duration: seconds
    }
}

Web Socket to Server

The client will be notified that the round has ended and needs to send the following data back.

{
    action: "game",
    data: {
        type: "roundEnd",
        round: CurrentRoundNumber,
        gameID: GameID,
        playerID: PlayerID,
        playerData: PlayerData
    }
}

When a player sends their data in, it is simply just written to an ElastiCache instance, where it will be processed later on by the Process Round RESTful route.

Finish Game

When the game is finished, players should be displayed information and statistics. More information to come.

Request

Response

Web Socket

Should broadcast to all connected clients that the game has been finished.

from client to server
{
    action: "game",
    data: {
        type: "finished",
        gameID: GameID,
    }
}

Process Round

Restful API route to process the previous round. Will fetch all player data from the ElasticCache, process it and then batch update the DynamoDB for the current.

Request GET /process/:gameID/:roundNumber

Response 200 OK

{
  // whatever it returns
}

Fetch player from Game

Restful API route to fetch a specific player for a game.

Request GET /games/:gameID/:playerID

Response

{
  "success": true,
  "player": {
    "gameID": "624764",
    "data": {
      "wealth": [],
      "income": [],
      "role": "",
      "planet": [],
      "playerCount": [],
      "declaredIncome": [],
      "redistributedWealth": [],
      "taxRate": [],
      "audit": [],
      "name": "Bob is your uncle!",
      "rank": [],
      "id": "f11c8ece-9df0-4db8-b91d-2c22d7872e39",
      "bankrupt": []
    },
    "playerID": "f11c8ece-9df0-4db8-b91d-2c22d7872e39"
  }
}

Errors

{ 
    success: false, 
    error: "Player does not exist." | "Player does not exist for the provided game." | "Could not fetch player from the provided game."
}

Games

Fetches all games

Request GET /games

Response

{
  "success": "true",
  "games": [
    {
      "description": "This is game for IMY310",
      "gameID": "624764",
      "options": {
        "toggleAlianInvasion": true
      }
    },
    {
      "description": "This is game for IMY310",
      "gameID": "692203",
      "options": {
        "toggleAlianInvasion": true
      }
    },
    {
      "description": "This is game for IMY310",
      "gameID": "652866",
      "options": {
        "toggleAlianInvasion": true
      }
    }
  ]
}

Fetch a game

Fetch a specific game

Request GET /games/:gameID

Response

{
  "success": "true",
  "game": {
    "description": "This is game for IMY310",
    "gameID": "624764",
    "options": {
      "toggleAlianInvasion": true
    }
  },
  "players": [
    {
      "gameID": "624764",
      "data": {
        "wealth": [],
        "income": [],
        "role": "",
        "planet": [],
        "playerCount": [],
        "declaredIncome": [],
        "redistributedWealth": [],
        "taxRate": [],
        "audit": [],
        "name": "Uncle Fred",
        "rank": [],
        "id": "c09fbeb1-1b2c-4146-a21d-ba048702cb8e",
        "bankrupt": []
      },
      "playerID": "c09fbeb1-1b2c-4146-a21d-ba048702cb8e"
    },
    {
      "gameID": "624764",
      "data": {
        "wealth": [],
        "income": [],
        "role": "",
        "planet": [],
        "playerCount": [],
        "declaredIncome": [],
        "redistributedWealth": [],
        "taxRate": [],
        "audit": [],
        "name": "Bob is your uncle!",
        "rank": [],
        "id": "f11c8ece-9df0-4db8-b91d-2c22d7872e39",
        "bankrupt": []
      },
      "playerID": "f11c8ece-9df0-4db8-b91d-2c22d7872e39"
    }
  ]
}

Fetch Consent from

Fetches the consent form.

Request GET /consent

Response

{
  markdown: "## That's what she said"
}

Update Consent Form

Updates the consent form.

Request POST /consent

{
  markdown: "## That's what she said"
}

*Response

{ 
  success: true, 
  message: "Successfully updated the consent form." 
}

CSV

Returns the game object as CSV

Request GET /games/:gameID/csv

Response

The game object as CSV with the “Content-Type” as “text/csv”.

"gameID","data","playerID"
"624764","{""wealth"":[],""income"":[],""role"":"""",""planet"":[],""playerCount"":[],""declaredIncome"":[],""redistributedWealth"":[],""taxRate"":[],""audit"":[],""name"":""Uncle Fred"",""rank"":[],""id"":""c09fbeb1-1b2c-4146-a21d-ba048702cb8e"",""bankrupt"":[]}","c09fbeb1-1b2c-4146-a21d-ba048702cb8e"
"624764","{""wealth"":[],""income"":[],""role"":"""",""planet"":[],""playerCount"":[],""declaredIncome"":[],""redistributedWealth"":[],""taxRate"":[],""audit"":[],""name"":""Bob is your uncle!"",""rank"":[],""id"":""f11c8ece-9df0-4db8-b91d-2c22d7872e39"",""bankrupt"":[]}","f11c8ece-9df0-4db8-b91d-2c22d7872e39"

XLSX

Getting the data in a way that frontend can generate it into an XLSX.

Request GET /games/:gameID/xlsx

Response

{
  xlsx: Player[],
  settings: {
    fileName: "Spreadsheet for Game(XXXXXX)"
  }
}

Latest Game

Fetch the latest game and its config.

Request GET /games/latest

Response

{
  "success": true,
  "game": {
    "description": "This is a test game",
    "gameID": "683687",
    "options": {
      "thats-what-she-said": true
    }
  }
}

Game Configs

Everything relating to the game Configs

Fetch all Configs

Fetch all game Configs

Request GET /games/configs

Response

{
  "success": true,
  "configs": [
    {
      "id": "bde6fc94-495b-4c45-9496-812f5f4594a0",
      "config": {
        "aliens": true,
        "bloodyWar": true,
        "what-she-said": "confrimed"
      }
    }
  ]
}

Fetch specific config

Fetch a specific config

Request GET /games/configs/:id

Response

{
  "success": true,
  "config": {
    "id": "bde6fc94-495b-4c45-9496-812f5f4594a0",
    "config": {
      "aliens": true,
      "bloodyWar": true,
      "what-she-said": "confrimed"
    }
  }
}

Add a new config

Create a new config based on the body you pass in.

Request POST /games/configs

{
  ...GameConfigurationOptions
}

Response

{
  "success": true
}

Get latest config

Fetches the latest config that was craeted.

Request GET /games/configs/latest

Response

{
  "success": true,
  "config": {
    "id": "484c6459-08bd-47f3-81aa-6724d875f2a4",
    "config": {
      "description": "3rd time is the charm - Test game using the config route now - delete this later",
      "options": {
        "auditChance": 0.6,
        "planetCollectionMultiplier": 1.7,
        "taxRate": 0.4,
        "finePercent": 2,
        "declaredIncomePercent": 1,
        "stageTime": 60,
        "bankruptcyThreshold": 200,
        "incomeMin": 600,
        "generateNewIncomeEachRound": true,
        "incomeMax": 1400
      }
    }
  }
}

Update player

Update a player in the redis

Request POST /update/player/:gameID

{
  playerID: PlayerID,
  data: PlayerData
}

Response

{
  success: true
}