Skip to content

Playtak Native Tournament API#7

Draft
nitzel wants to merge 24 commits intoUSTakAssociation:devfrom
nitzel:tournament
Draft

Playtak Native Tournament API#7
nitzel wants to merge 24 commits intoUSTakAssociation:devfrom
nitzel:tournament

Conversation

@nitzel
Copy link
Copy Markdown
Collaborator

@nitzel nitzel commented May 1, 2023

Goal

This PR is meant to give us a good start in interfacing with the playtak-server's new Seek-API and GameUpdate-Notifications (see USTakAssociation/tak-server#2) to get started on running tournaments natively on playtak.com

An initial rough idea of how the database tables and entities could look like was written down here.

An essential part of this PR are the database entities that will make up a tournament. They're not quite complete yet though.

The other essential part is, that Seeks can now be created through the playtak-api for tournament Games in the database, taking into account the GameRules configured in the database as well and the names of the players facing each other (see Matchup). Also, the corresponding Game is updated by the playtak-api when the playtak-server notifies it about game.started or game.ended events.

Please have a look at the Example Workflow at the bottom for a better explanation.

Todo

  • Re-organize URL structure
    • I don't like the separation of all the different entities. instead of querying the groups with v1/groups I'd prefer to query them with e.g. v1/tournaments/:tournamentId/:stageId/groups and it would only return the groups related to :stageId in :tournamentId
    • @invaderb any good ideas on how we could structure this best? Or just leave it as is (see swagger screenshot below)?
  • Add tests
  • Add endpoints for
    • Stage
    • Group
    • Tournament (GET all and GET by id are implemented, but there's nothing to create/edit)
    • Editing all tournament related entities (change a group name, game rules, set a game's result and playtak_id, ...)
    • possibly deleting some tournament related entities
    • Participant signup? Approve & reject?
  • Auth https://docs.nestjs.com/security/authentication
    • no one except the playtak server should be able to access /tournaments/game/update to send game updates
    • only tournament managers should be able to create the tournament/* entities
    • only the partaking players should be able to create a seek for a game in a matchup
  • Swagger: Improve annotations, especially regarding exceptions
  • improve logging, I'm not happy with the new lines when logging e.g. logger.log("hello", obj)
  • DB migration, how to handle tables that don't exist yet
  • Add options/queries to the get/get all endpoints
  • Improve returned values from endpoints - when should we return just the entity, and when should we include the referenced entities as well? And how deep? Just top->bottom? or the other way as well?

Prerequisites

  • Add Seek API and game update broadcasting tak-server#2 running
    • in properties.xml configuring <event-subscriber-url> to http://localhost:3004/v1/tournaments/game/update (depending on the playtak-api's port)
  • .env set PLAYTAK_API_URL=http://127.0.0.1:9998 (or whatever port the playtak server's HTTP server runs on)
  • playtak ui running

API Overview

(May be out of date)

image

Workflow Example

This is not quite up to date, because as of recently one needs to specify the parents of an inserted entity by their ID (e.g. to insert a matchup, you need to pass in the group's id as well. I haven't managed to update the examples yet. Nor have I actually tested the entire workflow (create tournament, rules, stages, groups, matchups, games, seeks) again yet).

To follow along, consider using this Postman configurationplaytak-api.postman_collection.json.txt (github doesn't allow .json files, so you may need to change the file extension)

Create a GameRules entity

PUT http://localhost:3004/v1/tournaments/game-rules

{
        "name": "6s 15+10",
        "timeContingent": 900,
        "timeIncrement": 10,
        "extraTimeTriggerMove": 0,
        "extraTimeAmount": 0,
        "komi": 2,
        "boardSize": 6,
        "capstones": 1,
        "pieces": 30
}
Create a Matchup

PUT http://localhost:3004/v1/tournaments/matchups

{
    "player1": "nitzel2",
    "player2": "kvothe",
    "group": 1 // groups not supported yet, choose any number
}
Create a Game in the Matchup with the GameRules

PUT http://localhost:3004/v1/tournaments/game

{
    "player1goesFirst": false, // if true, matchup.player1 is white, else it's matchup.player2
    "matchup": 1, // use the id that was returned by the create-matchup endpoint
    "rules": 1  // use the id that was returned by the create-game-rules endpoint
}
Get a list of all tournament Games

GET http://localhost:3004/v1/tournaments/game

[
    {
        "id": 5,
        "player1goesFirst": true,
        "playtakId": null,
        "result": null
    },
    // ...
]
Create a seek on the playtak server for one of the games listed above

Make sure that your player1 is logged in - otherwise this fails.

Here I chose to do it for the game with id=5

PUT http://localhost:3004/v1/tournaments/seeks/5

Internally this passes on the PNT Game's id as pntId for later reference. The seek is created and can be joined:

image

Join, play and finish the game

Once the game has been joined by the opponent, the game.playtakId is set. Once it is over, the result is set as well:

    {
        "id": 5,
        "player1goesFirst": true,
        "playtakId": 9,
        "result": "0-1"
    },

The logs of the playtak-api during this last step were:

     LOG [GameService] Game id=5 playtak_id=9 started: nitzel2 vs kvothe
     LOG [GameService] Game id=5 playtak_id=9 ended: nitzel2 vs kvothe
DEBUG [GameService] Marking game id=5 playtakId=9 nitzel2 vs kvothe as finished 0-1

Side effects

  • installed packages
    • class-validator, class-transformer for ValidationPipeline
    • @nestjs/axios to send http requests to the playtak-server
  • typeorm.synchronize = True if env.NODE_ENV == 'local' so that one can just amend the entities without having to worry about updating the table's columns manually. This must not be enabled in production, as mistakes can easily drop columns completely.
  • logging: for local development, everything >=debug is logged (so just verbose is excluded), otherwise only error, warning, log are logged and verbose,debug are omitted.

@nitzel
Copy link
Copy Markdown
Collaborator Author

nitzel commented May 1, 2023

Instead of having a UUID generated for a seek/game on the playtak server it'd be more robust to set that I'd to the PNT game id (think about multiple seeks created for a single PNT game. Only if the last one is accepted does my current implementation work)

Edit: Done in ae552cc

@invaderb
Copy link
Copy Markdown
Member

invaderb commented May 2, 2023

@nitzel

Instead of having a UUID generated for a seek/game on the playtak server it'd be more robust to set that I'd to the PNT game id (think about multiple seeks created for a single PNT game. Only if the last one is accepted does my current implementation work)

Edit: Done in ae552cc

So have a concern on this one is it going to effect the game id when it get's saved to the games.db?

@nitzel
Copy link
Copy Markdown
Collaborator Author

nitzel commented May 2, 2023

@nitzel

Instead of having a UUID generated for a seek/game on the playtak server it'd be more robust to set that I'd to the PNT game id (think about multiple seeks created for a single PNT game. Only if the last one is accepted does my current implementation work)
Edit: Done in ae552cc

So have a concern on this one is it going to effect the game id when it get's saved to the games.db?

Currently my changes to the playtak-server don't affect what's saved to the DB (no extra column added, none altered). It works like this: When creating the seek, you pass in the PNT Game Id (pntId). It's stored in memory on the seek. Once a game is created from the seek, the pntId is stored (in memory) on the Game as well. GameUpdate messages sent from the Playtak Server contain the playtakId as well as the pntId. The first one can later be used to find the corresponding game with all it's details in the playtak-server games table. The pntId is used to associate the GameUpdate messages with the right PNT Game and fill in the playtakId and the result columns,

We could consider adding the PNT Game Id to the playtak-server games table to more easily link from a PlaytakServer Game to a PNT Game, but I don't think that's necessary - we could just add an index on the game.playtakId column and then match them up with a join.

@nitzel nitzel changed the base branch from main to dev May 5, 2023 17:21
@invaderb invaderb deleted the branch USTakAssociation:dev May 5, 2023 17:53
@invaderb invaderb closed this May 5, 2023
@invaderb invaderb reopened this May 5, 2023
@invaderb invaderb deleted the branch USTakAssociation:dev May 5, 2023 18:09
@invaderb invaderb closed this May 5, 2023
@invaderb invaderb reopened this May 5, 2023

async getSeeks(): Promise<Array<SeekDto>> {

const response = await this.httpService.axiosRef.get<Array<SeekDto>>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally I prefer the try catch over the old school .catch I think it's cleaner but I'm not pushing a change maybe just considering consistency across the project

@invaderb
Copy link
Copy Markdown
Member

invaderb commented May 6, 2023

@nitzel per your tag
Re-organize URL structure I don't like the separation of all the different entities. instead of querying the groups with v1/groups I'd prefer to query them with e.g. v1/tournaments/:tournamentId/:stageId/groups and it would only return the groups related to :stageId in :tournamentId @invaderb any good ideas on how we could structure this best? Or just leave it as is (see swagger screenshot below)?

This is very GraphQLesk, should be possible with transactions is probably how I'd do it or would need to build a custom sql query and do a nested few joins (I'd have to play with some data to write the query out).

My only thought on this is you would have to know both the tournament id and stage id in order to pass in to the the groups and where would those come from?

@nitzel
Copy link
Copy Markdown
Collaborator Author

nitzel commented May 15, 2023

@nitzel per your tag Re-organize URL structure I don't like the separation of all the different entities. instead of querying the groups with v1/groups I'd prefer to query them with e.g. v1/tournaments/:tournamentId/:stageId/groups and it would only return the groups related to :stageId in :tournamentId @invaderb any good ideas on how we could structure this best? Or just leave it as is (see swagger screenshot below)?

@invaderb

This is very GraphQLesk, should be possible with transactions is probably how I'd do it or would need to build a custom sql query and do a nested few joins (I'd have to play with some data to write the query out).

My only thought on this is you would have to know both the tournament id and stage id in order to pass in to the the groups and where would those come from?

Thanks for your insight. I guess for now it's probably best to just keep it as it is: flat.

@nitzel nitzel mentioned this pull request Sep 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants