/GamblingKingsBackend

Attempt to build a serverless backend for a Mahjong Application

Primary LanguageTypeScriptApache License 2.0Apache-2.0

Mahjong Application Backend

Last Commit CI CD codecov code style: prettier License

Local development

Prerequisite

  • Node.Js
  • Typescript
  • Serverless
  • Java Runtime Engine (JRE) version 6.x or newer
  • (Important) Add a config file under .aws folder (C:\Users\USERNAME\.aws\config for Windows and ~/.aws/config for Unix-based systems)

Start local development

To start local dev, simply run:

yarn run start_local

OR

Follow the steps below for detailed step breakdown:

  1. Install dependencies
# Cleanup auto-generated folders
yarn run dev:cleanup

# Install dependencies
yarn run install_dep
  1. Remember to uncomment the code for local dev in serverless.yml

  2. (Important) Increase max space size for Node (otherwise, webpack may not work in your local machine)

export NODE_OPTIONS="--max-old-space-size=8192"
  1. Install dynamodb local (this will create a folder called .dynamodb in the project root directory) and start serverless and dynamodb locally
yarn run dev:run
  1. Test Connection here by entering the websocket url (e.g. ws://localhost:3001)

For more details on local dev, see the following links

  1. To invoke lambda function locally see invoke-local

Deploy to AWS account

  1. Add profile and credentials to .aws/credentials and ./aws/config file OR use aws-vault (Recommended):
aws-vault add gamblingkings-sls
  1. Deploy or remove AWS resources
    Note: --no-session flag seems to be required。 See this bug for more details

To deploy:

aws-vault exec <PROFILE_NAME> --no-session -- sls deploy

To remove:

aws-vault exec <PROFILE_NAME> --no-session -- sls remove

To start a production build and deploy to AWS:

yarn start

Websocket Test Data

SET_USERNAME

{
  "action": "SET_USERNAME",
  "payload": {
    "username": "new user"
  }
}

GET_ALL_USERS

{
  "action": "GET_ALL_USERS"
}

CREATE_GAME

{
  "action": "CREATE_GAME",
  "payload": {
    "game": {
      "gameName": "Chow Yun-fat,the Mahjong King",
      "gameType": "Mahjong",
      "gameVersion": "Japanese"
    }
  }
}

GET_ALL_GAMES

{
  "action": "GET_ALL_GAMES"
}

SEND_MESSAGE

{
  "action": "SEND_MESSAGE",
  "payload": {
    "username": "test user",
    "message": "custom message to all users"
  }
}

JOIN_GAME

{
  "action": "JOIN_GAME",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c"
  }
}

LEAVE_GAME

{
  "action": "LEAVE_GAME",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c"
  }
}

START_GAME

{
  "action": "START_GAME",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c"
  }
}

GAME_PAGE_LOAD

{
  "action": "GAME_PAGE_LOAD",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c"
  }
}

DRAW_TILE

{
  "action": "DRAW_TILE",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c"
  }
}

PLAY_TILE

{
  "action": "PLAY_TILE",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c",
    "tile": "1_DOT"
  }
}

PLAYED_TILE_INTERACTION

{
  "action": "PLAYED_TILE_INTERACTION",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c",
    "playedTiles": ["1_DOT", "1_DOT", "1_DOT"],
    "meldType": "TRIPLET",
    "skipInteraction": false
  }
}

WIN_ROUND

{
  "action": "WIN_ROUND",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c",
    "handPointResults": {
      "totalPoints": 3,
      "handPoints": 3,
      "extraPoints": 0,
      "windPoints": 0,
      "dragonPoints": 0,
      "flowerPoints": 0,
      "hands": [
        {
          "points": 3,
          "name": "ALL_TRIPLET"
        }
      ],
      "tiles": [
        {
          "type": "DOT",
          "value": 1
        },
        {
          "type": "DOT",
          "value": 1
        },
        {
          "type": "DOT",
          "value": 2
        },
        {
          "type": "DOT",
          "value": 2
        },
        {
          "type": "DOT",
          "value": 2
        },
        {
          "type": "DOT",
          "value": 3
        },
        {
          "type": "DOT",
          "value": 3
        },
        {
          "type": "DOT",
          "value": 3
        },
        {
          "type": "DOT",
          "value": 4
        },
        {
          "type": "DOT",
          "value": 4
        },
        {
          "type": "DOT",
          "value": 4
        },
        {
          "type": "CHARACTER",
          "value": 7
        },
        {
          "type": "CHARACTER",
          "value": 7
        },
        {
          "type": "CHARACTER",
          "value": 7
        }
      ],
      "wind": "EAST",
      "bonusTiles": [],
      "flower": 1,
      "concealedPoint": 0
    }
  }
}

SELF_PLAY_TILE

{
  "action": "SELF_PLAY_TILE",
  "payload": {
    "gameId": "5938902b-2e2c-4da8-b900-5cdfbba8f10c",
    "playedTile": "1_Dot",
    "isQuad": false,
    "alreadyMeld": false
  }
}

General user flow

  1. connect x 4: Four users connect to websocket
  2. CREATE_GAME: One user creates a game
  3. JOIN_GAME x 3: Three other users join the game created by the host
  4. START_GAME: Starts the game if there are four users in the game
  5. GAME_PAGE_LOAD x 4: Wait until assets are loaded on the frontend for all four users
  6. GAME_START: Officially starts the game if gameLoadedCount for the game in the Games table is 4
  7. DRAW_TILE: Draw one tile and send it to a user in the game
  8. LEAVE_GAME: To remove user from the Games table if user disconnects or manually leave a game. Note: if the user leaving the game is the game host, the game will be deleted
  9. PLAY_TILE: Send a played tile to all users in the game
  10. PLAYED_TILE_INTERACTION * 3: Interact with a played tile to make a meld (Consecutive, Triplet, or Quad), and the backend will decide who can take this tile based on meld priority. If all three other users decide not to take the tile, the tile can be skipped and not taken by anyone
  11. SELF_PLAY_TILE: To play bonus tiles automatically at game start or during the game
  12. WIN_ROUND: To win a game, update wind/dealer, and start a new game and distribute tiles to each user

Testing

Configuration files

Test folder

TODOs on Optimization and Future Refactoring

  • Refactor and optimize addUserToGame call to DynamoDB
  • Refactor and optimize removeUserFromGame call to DynamoDB

Build Optimization

See this article to know more about how to optimize build time using typescript, serverless, webpack, and eslint