> ## Documentation Index
> Fetch the complete documentation index at: https://docs.michaelrosstarr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Socket Requests

> Specification of the data that needs to be sent to the WebSocket connection to execute a given function. For each function, you can find the data structure and an example of a working command.

## WebSocket Documentation

* Values determined by the Server Stack are referred to as `[from METHOD /endpoint]`
* Values determined by the player are referred to as `[from client]`
* [Send requests](https://www.npmjs.com/package/ws?ref=blog.krum.io) to: `wss://w1ws57hjh0.execute-api.eu-central-1.amazonaws.com/prod/`

<Note>If at any point you need to see what a given player's object looks like, you can always send this query: `{"action":"check","type":"redis","method":"player","pid":"00001","gid":"18062024-2210"}` where pid is playerID and gid is gameID.</Note>

## createGame

createGame creates an object in the elasticache-tax-game cache, with the key `${gameID}`. It also creates the first empty planet in the planets array (in the elasticache-tax-game cache with the key `planets`).

### Required data

```javascript
{
    action: string, //MUST BE "game"
    type: string, //MUST BE "createGame"
    gameID: string, //[from RestAPI]
    stageTime: number, //in seconds (eg: 20 = 20s | so a round will be 1m 20s)
    auditChance: number, //decimal (eg: 20% = 0.20)
    finePercent: number, //decimal (eg: 20% = 0.20)
    planetCollectionMultiplier: number, //decimal (eg: 1.7) | planet collection multiplier
    taxRate: number, //decimal (eg: 20% = 0.20)
    declaredIncomePercent: number,
    incomeMax: number,
    incomeMin: number,
    bankruptcyThreshold: number,
    generateNewIncomeEachRound: boolean
}
```

example: `{"action":"game","type":"createGame","gameID":"18062024-1845","stageTime":20,"auditChance":0.25,"finePercent":0.15,"planetCollectionMultiplier":1.7,"taxRate":0.30, "declaredIncomePercent": 0.40, "incomeMax":1200, "incomeMin":600, "bankruptcyThreshold": 0, "generateNewIncomeEachRound": false}`

### Elasticache

```javascript
`${gameID}`: {
    hostID: string,
    gameID: string,
    gameConfiguration: {
        stageTime: number,
        auditChance: number,
        finePercent: number,
        pcm: number,
        defaultRate: number,
        decIP: number,
        incomeMax: number,
        incomeMin: number,
        bankruptcyThreshold: number,
        gNIEC: boolean
      }
}

`${planetID}-${gameID}`: {
    planetID: "planet1",
    planetName: `${AI generated}`,
    taxRate: data.taxRate,
    players: [{
        connectionID: string, //player's connection id
        playerID: string,
        playerName: string,
        role: string // one of ["A" || "B" || "C"]
    }],
  };

`planets-${gameID}` : "1";
```

### Returns

```javascript
    statusCode: 200,
    body: `{"type":"createGame","message":["OK","OK","OK","OK","OK","OK"]}`
```

## joinGame

joinGame creates an object in the elasticache-tax-game cache, with the key `${gameID}---${playerID}`. It will return a generated role and planet to each connection (once the planet is established), and update the host with the new player (playerID and name).

<Note> When WebSockets receives the request, it scans `DynamoDBStack-WebsocketConnections87C62C57-1H8H09K1YWXHY` table. If there is a record in the table with the same connectionId & gameID, it gets that object from Elasticache and returns the full item to the user.</Note>

### Required data

```javascript
action: string, //MUST BE "game"
type: string, //MUST BE "joinGame"
gameID: string,
playerID: string, //from RestAPI [ POST /joinGame]
name: string //from client
```

example: `{"action":"game","type":"joinGame","gameID":"game-c3b4i7ybc","playerID":"001a","name":"Emma"}`

### New Connection

#### Elasticache: Role A

```javascript
`${gameID}---${playerID}`: {
    connectionId: string,
    playerID: string,
    name: string,
    role: string, // "A"
    wealth: Array<number>, //starts as [0]
    rank: Array<number>, //starts as [0]
    planet: string, //`${planetID},${planetName}`,
    taxRate: Array<number>, // ONLY A | starts as [defaultRate]
    redistributedWealth: Array<number>, // ONLY A | amount they redistributed to B and C players in a round
    playerCount: Array<number>, // ONLY A | number of players on the planet they are currently on
    bankrupt: Array<boolean>, // - true if they went bankrupt in a round
}
```

#### Role B

```javascript
`${gameID}---${playerID}`: {
    connectionId: string,
    playerID: string,
    name: string,
    role: string, // "B"
    wealth: Array<number>, //starts as [0]
    rank: Array<number>, //starts as [0]
    planet: `${planetID},${planetName}`,
    income: Array<number>, // - income they recieve in a round
    declaredIncome: Array<number>, // - income they declared in a round
    audit: Array<number>, // - amount they were fined in an audit in a round
    bankrupt: Array<boolean>, // - true if their planet went bankrupt in a round
}
```

#### Role C

```javascript
${gameID}---${playerID}: {
    connectionId: string,
    playerID: string,
    name: string,
    role: string, // "C"
    wealth: Array<number>,
    rank: Array<number>,
    planet: Array<string>, //only an array for role C [`${planetID},${planetName}`]
    income: Array<number>, // - income they recieve in a round
    declaredIncome: Array<number>, // - income they declared in a round
    audit: Array<number>, // - amount they were fined in an audit in a round
    bankrupt: Array<boolean>, // - true if they went bankrupt in a round
}
```

#### Returns

```javascript
    statusCode: 200,
    body: `{"type":"joinGame","message":{"options":{"stageTime":20,"auditChance":0.1,"finePercent":0.5,"planetCollectionMultiplier":1.7,"taxRate":0.3,"declaredIncomePercent":0,"incomeMax":1200,"incomeMin":600,"bankruptcyThreshold":0,"generateNewIncomeEachRound":false}}}`
```

> Each time a player sends a joinGame request, the host will receive the following string: `{"type":"startGame","message":[{"planetID":"planet1","planetName":"Terralus","players":[{"connectionID":"ZqGUhc8pliACFpw=","playerID":"00001","playerName":"Emma","role":"A"},{"connectionID":"ZqGWHc1-FiACETQ=","playerID":"00002","playerName":"Jules","role":"B"},{"connectionID":"ZqGXndKyFiACEXA=","playerID":"00003","playerName":"Stevie","role":"C"}]}]}`

### ~~Existing Connection~~ DEPRECIATED

```javascript
    statusCode: 200,
    body: "{\"connectionId\":\"Zg2C4e7uliACEsg= \",\"playerID\":\"00001 \",\"name\":\"Emma \",\"role\":\"A\",\"wealth\":[0],\"rank\":[0],\"planet\":\"planet1,Quintaris\",\"taxRate\":[],\"redistributedWealth\":[],\"playerCount\":[],\"bankrupt\":[]}"
```

## startGame

startGame goes through all of the planets in the elasticache-tax-game cache, ensures that there are no planets with an awkward amount of players (checks the last planet), and then sends each player their planet (name and ID) and role.

* If there are two or less players, startGame reassigns each of them to an existing planet with the "B" role
  * The existing planets are determined by the player's position in the player array: `planetsObject[newestPlanet.players.indexOf(player)].players.push`

### Required data

```javascript
action: string, //MUST BE "game"
type: string, //MUST BE "startGame"
gameID: string
```

### Each player receives:

The players array **includes** this player
`{"type":"startGame","message":{{"planetID":"planet1","planetName":"Soralia","playerRole":"C","players":["Emma","Jules","Steve","Thomas","Kelvin","Dana"]}}`

## startRound

It should be sent by the **Host**. Creates an array of items in `DynamoDBStack-WebsocketConnections87C62C57-1H8H09K1YWXHY` with the given gameID, and then sends each connection the response message.

### Required data

```javascript
action: string, //MUST BE "game",
type: string, //MUST BE "startRound"
gameID: string,
round: number
```

### Returns

#### To Host

```javascript
    statusCode: 200,
    body: `{"type":"startRound","message":{{"round":1,"stage":1,"stageStartTime":"2024-06-17T18:17:49.544Z","stageDuration":20}}`
```

#### To Players

```javascript
    statusCode: 200,
    body: `{"type":"startRound","message":{"round":1,"stage":1,"stageStartTime":"2024-06-17T17:48:58.380Z","stageDuration":20}}`
```

## stageChanges, handleStage, & startStage

* `stageChanges` is sent by a player that needed to make an action during that stage:
  * At the end of stage 1, all "A" players should send a `stageChanges` request
  * At the end of stage 2, all "B" and "C" players should send a `stageChanges` request
  * At the end of stage 3, **all** players should send a `stageChanges` request
  * At the end of stage 4, "C" players should send a `stageChanges` request
* `handleStage` should be sent by the **Host** when the stage is up (stageDuration has elapsed). All players will be alerted that they must send their `stageChanges` request
  * This way, only one system is managing the time, and the WebSockets aren't using up a massive amount of processing power while each connection waits for stageDuration to elapse before sending a response.

<Warning>The WebSockets don't check if the stage duration is up. The response to `stageChanges` is sent in `handleStage`.</Warning>

<Warning>If players don't make a change, the request they send **needs** to have the default values in it. WebSockets will apply the logic to what is sent (so if the player sends 0, it will use 0 as the value), or send a `401 BAD REQUEST` if it's missing</Warning>

### stageChanges

Response: `{"type":"stageChanges","message":"OK"}`

#### Stage 1

```javascript
//ONLY SENT BY ROLE A PLAYERS
action: string, //MUST BE "game",
type: string, //MUST BE "stageChanges",
gameID: string,
playerID: string
round: number,
stage: number, //MUST BE 1
taxRate: number,
planetID: string
```

#### Stage 2

```javascript
//ONLY SENT BY ROLE B/C PLAYERS
action: string, //MUST BE "game",
type: string, //MUST BE "stageChanges",
gameID: string,
playerID: string
round: number,
stage: number, //MUST BE 2
income: number,
declaredIncome: number
```

#### Stage 3

```javascript
action: string, //MUST BE "game",
type: string, //MUST BE "stageChanges",
gameID: string,
playerID: string
round: number,
stage: number, //MUST BE 3
role: string, //VERY IMPORTANT!!!!!!!!!
redistributed: number, //ONLY PLAYER A: the amount they chose to redistribute
remainder: number //ONLY PLAYER A: redistributable-redistributed
audit: number //ONLY B+C PLAYERS
```

#### Stage 4

```javascript
action: string, //MUST BE "game",
type: string, //MUST BE "stageChanges",
gameID: string,
playerID: string
round: number,
stage: number, //MUST BE 4
newPlanetID: string,
newPlanetName: string,
```

eg : `{"action":"game","type":"stageChanges","gameID":"18062024-2210","round":1,"stage":1,"playerID":"00001","planetID":"planet1","taxRate":0.7}`

## handleStage

### Required Data

```javascript
action: "game",
type: "handleStage",
gameID: string,
```

### Returns to Host

```javascript
statusCode: 200,
body: `{"type":"handleStage","message":{"value":"OK"}}`
```

### Returns to Client

`{"type":"handleStage","message":{"value":"TIME UP"}}`

### startStage

#### Required Data

```javascript
action: "game",
type: "startStage",
gameID: string,
round: number,
stage: number //this should be the current stage | if handleStage() is being sent after stage 1, it should be 1
```

#### To Host

```javascript
    statusCode: 200,
    body: `{"type":"startStage","message":{"round":1,"stage":2,"stageStartTime":"19/06/2024, 12:11:11","stageDuration":20}}`
```

#### After the last stage of the round (automatically calls endRound)

```javascript
    statusCode: 200,
     body: `{
       "type":"endRound",
       "message":{
       "A":[
          {"connectionId":"Zru4medkliACEXA=","playerID":"00001","name":"Emma","role":"A","wealth":[0],"rank":[0],"planet":"planet1,Xerion","taxRate":[0.3],"redistributedWealth":[0],"playerCount":[5],"bankrupt":[false]}
        ],
          "B":[
            {"connectionId":"Zru5EcnpFiACHEQ=","playerID":"00002","name":"Jules","role":"B","wealth":[0],"rank":[0],"planet":"planet1,Xerion","income":[0],"declaredIncome":[0],"audit":[0],"bankrupt":[false]}
        ],
        "C":[
          {"connectionId":"Zru5Ve4rliACEeg=","playerID":"00003","name":"Stevie","role":"C","wealth":[0],"rank":[0],"planet":["planet1,Xerion"],"income":[0],"declaredIncome":[0],"audit":[0],"bankrupt":[false]}
        ]
      }
    }`
```

#### To Players - after stage 1

`{"type":"startStage","message":{"round":1,"stage":2,"stageStartTime":"19/06/2024, 12:11:11","stageDuration":20,"taxRate":0.15}}`

#### To Players - after stage 2

`{"type":"startStage","message":{"round":1,"stage":3,"stageStartTime":"19/06/2024, 16:21:12","stageDuration":20,"bankrupt":false,"declared":[800,1100,600,600],"redistributable":3689}}`

**A Players | Bankrupt**
`{"type":"startStage","message":{"round":1,"stage":3,"stageStartTime":"19/06/2024, 16:21:12","stageDuration":20,"bankrupt":true}}`

**Other Players**
`{"type":"startStage","message":{"round":1,"stage":3,"stageStartTime":"19/06/2024, 16:21:12","stageDuration":20}}`

#### To Players - after stage 3

**A Players**
`{"type":"startStage","message":{"round":1,"stage":4,"stageStartTime":"20/06/2024, 12:04:48","stageDuration":20,"updatedWealth":3850,"auditPool":850}}`

**B+C Players**
`{"type":"startStage","message":{"round":1,"stage":4,"stageStartTime":"20/06/2024, 12:04:48","stageDuration":20,"updatedWealth":137.8,"rebate":137.8}}`

#### To Players - after stage 4

````
{"type":"endRound",
"message":{
  "playerData":{
    "connectionId":"Zru5Ve4rliACEeg=",
    "playerID":"00003",
    "name":"Stevie",
    "role":"C",
    "wealth":[674.5],
    "rank":[0],
    "planet":["planet1,Zoroxia"],
    "income":[1100],
    "declaredIncome":[1100],
    "audit":[250],
    "bankrupt":[false]
  },
  "planetData":{
    "planetID":"planet1",
    "planetName":"Zoroxia",
    "taxRate":0.7,
    "bankruptThisRound":false,
    "players":[
      {"connectionID":"Zru4medkliACEXA=",
      "playerID":"00001",
      "playerName":"Emma",
      "role":"A"},
      {"connectionID":"Zru5EcnpFiACHEQ=",
      "playerID":"00002",
      "playerName":"Jules",
      "role":"B"},
      {"connectionID":"Zru5Ve4rliACEeg=",
      "playerID":"00003",
      "playerName":"Stevie",
      "role":"C"}
      ]
    }
  }
}```
````
