Quibbble K8s Controller
The project allows Quibbble games to be run in a K8s cluster. Games are spun up as individual pods with their entire lifecycle handled by the Quibbble controller. This allows Quibbble to take advantage of the power of K8s, primarily the ability to scale as well as to seperate and self heal in the event of any single game failure.
Setup
This controller currently requires the usage of NGINX to allow for dynamic routing of games. Meaning that if a game with key tictactoe
and id example
is created, then NGINX will handle the routing to that game over path /tictactoe/example
.
Install NGINX
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
Install Quibbble Controller
helm upgrade --install quibbble-controller quibbble-controller \
--repo https://quibbble.github.io/quibbble-controller \
--namespace quibbble --create-namespace
Supported Games
Architecture
There are three main processes in this system.
controller
- Processes game create and delete requests.
watcher
- Periodically searches for and cleans up stale games.
server
- Runs a game instance.
Flows
Game Creation
- Send
POST https://<host>/create
with some qqn
such as [key "tictactoe"][id "example"][teams "red, blue"]
.
controller
processes the request and, if valid, creates K8s ConfigMap, Pod, Service, and Ingress resources.
- Game can now be accessed at
https://<host>/tictactoe/example
.
Game Connection
- Join a game by connection to
wss://<host>/tictactoe/example/connect
with websockets.
- Connection should be open to a
server
instance and relevant game messages should be recieved.
Game Cleanup
watcher
will kick off every X
timeperiod.
- Job requests data from all live games by calling
GET https://<host>/<key>/<id>/active
for each game.
- If there are no connected players and no recent updates then all K8s related resources are deleted.
REST API
POST
/create
(create a game)
##### Parameters
> | name | type | data type | description |
> |-----------|-----------|------------------------------------|-------------------------------------------------------------------|
> | None | required | [QGN](/quibbble-controller/pkg/gamenotation/) | [QGN](/quibbble-controller/pkg/gamenotation/) descibing the game to create |
##### Responses
> | http code | content-type | response |
> |---------------|-----------------------------------|---------------------------------------------------------------------|
> | `201` | `text/plain;charset=UTF-8` | `Created` |
> | `400` | `text/plain;charset=UTF-8` | `Bad Request` |
> | `409` | `text/plain;charset=UTF-8` | `Conflict` |
> | `500` | `text/plain;charset=UTF-8` | `Internal Server Error` |
##### Example cURL
> ```javascript
> curl -X POST -H "Content-Type: application/qgn" --data @post.qgn https://api.quibbble.com/create
> ```
DELETE
/delete?key={key}&id={id}
(delete a game)
##### Parameters
> | name | type | data type | description |
> |-----------|-----------|------------------------------------|-------------------------------------------------------------------|
> | key | required | string | The name of the game i.e. `tictactoe` or `connect4` |
> | id | required | string | The unique id of the game instance to join |
##### Responses
> | http code | content-type | response |
> |---------------|-----------------------------------|---------------------------------------------------------------------|
> | `200` | `text/plain;charset=UTF-8` | `OK` |
> | `404` | `text/plain;charset=UTF-8` | `Not Found` |
> | `500` | `text/plain;charset=UTF-8` | `Internal Server Error` |
##### Example cURL
> ```javascript
> curl -X DELETE https://api.quibbble.com/delete?key={key}&id={id}
> ```
WEBSOCKET
/{key}/{id}/connect
(connect to a game)
##### Parameters
> | name | type | data type | description |
> |-----------|-----------|------------------------------------|-------------------------------------------------------------------|
> | key | required | string | The name of the game i.e. `tictactoe` or `connect4` |
> | id | required | string | The unique id of the game instance to join |
##### Responses
> None
##### Example wscat
> ```javascript
> wscat -c wss://api.quibbble.com/{key}/{id}/connect
> ```
GET
/{key}/{id}/snapshot?format={format}
(get game snapshot)
##### Parameters
> | name | type | data type | description |
> |-----------|-----------|------------------------------------|-------------------------------------------------------------------|
> | key | required | string | The name of the game i.e. `tictactoe` or `connect4` |
> | id | required | string | The unique id of the game instance to join |
> | format | required | one of `json` or `qgn` | The type of data to return |
##### Responses
> | http code | content-type | response |
> |---------------|-----------------------------------------|---------------------------------------------------------------------|
> | `200` | `application/json` or `application/qgn` | JSON or [QGN](/quibbble-controller/pkg/gamenotation/) |
> | `400` | `text/plain;charset=UTF-8` | `Bad Request` |
> | `404` | `text/plain;charset=UTF-8` | `Not Found` |
> | `500` | `text/plain;charset=UTF-8` | `Internal Server Error` |
##### Example cURL
> ```javascript
> curl -X GET https://api.quibbble.com/{key}/{id}/snapshot?format=json
> ```
GET
/stats
(get all game stats)
##### Parameters
> None
##### Responses
> | http code | content-type | response |
> |---------------|-----------------------------------------|---------------------------------------------------------------------|
> | `200` | `application/json` | Stats for all games |
> | `500` | `text/plain;charset=UTF-8` | `Internal Server Error` |
##### Example cURL
> ```javascript
> curl -X GET https://api.quibbble.com/stats
> ```
GET
/{key}/{id}/active
(get game activity)
##### Parameters
> | name | type | data type | description |
> |-----------|-----------|------------------------------------|-------------------------------------------------------------------|
> | key | required | string | The name of the game i.e. `tictactoe` or `connect4` |
> | id | required | string | The unique id of the game instance to join |
##### Responses
> | http code | content-type | response |
> |---------------|-----------------------------------------|---------------------------------------------------------------------|
> | `200` | `application/json` | JSON data describing player count and last update time |
> | `404` | `text/plain;charset=UTF-8` | `Not Found` |
##### Example cURL
> ```javascript
> curl -X GET https://api.quibbble.com/{key}/{id}/active
> ```
Websocket Messaging
Sendable Messages
join
(join a team)
##### Message
```json
{
"type": "join",
"details": "$TEAM"
}
```
action
(perform a game action)
##### Message
```json
{
"type": "$ACTION",
"details": {...}
}
```
ai
(ai plays for the current team)
##### Message
```json
{
"type": "ai",
}
```
chat
(send a chat message)
##### Message
```json
{
"type": "chat",
"details": "$MESSAGE"
}
```
Recievable Messages
snapshot
(retrieve a snapshot of the game)
##### Details
Message sent to all players on every game state change.
##### Message
```json
{
"type": "snapshot",
"details": {...}
}
```
connection
(retrieve player connection information)
##### Details
Message sent to all players on every player connection, drop, or team change.
##### Message
```json
{
"type": "snapshot",
"details": {
"uid": "$UID1",
"players": {
"$UID1": "$TEAM1",
"$UID2": "$TEAM2",
"$UID3": null
}
}
}
```
chat
(retrieve chat message)
##### Details
Message sent to all players on every sent chat message.
##### Message
```json
{
"type": "chat",
"details": {
"uid": "$UID",
"team": "$TEAM",
"message": "$MESSAGE",
}
}
```
error
(retrieve error message)
##### Details
Message sent to origin player on failed action message.
##### Message
```json
{
"type": "error",
"details": "$MESSAGE"
}
```