The gothello-server is responsible for implementing rules and communicating with clients. It is made of two APIs one working over a web-socket and one over HTTP. The web-socket API is used for real-time game communication while the HTTP api is used for match making.
We are using Spring as our web-server since its easy-ish to use https://spring.io/guides/gs/rest-service/
We use maven to manage our dependencies, tests, and builds
- Install maven. On Ubuntu this can be done with:
sudo apt install maven
- Compile & Run. This should install all dependencies, compile, then run.
cd gothello-server
mvn spring-boot:run
You should now be able to connect to the server now. http://localhost:8080/api/v0/game/new
/api/v0/game/${id}
- Get basic game meta-data/api/v0/game/${id}/state
- Get the game's state including the board/api/v0/game/new
- Create a new game/api/v0/game/join
- Get the id of an open game
state
- Informs the players of the game state including the boardgameOver
- Informs the players of the winnerstatus
- Informs the clients of an exceptionresign
- Informs the server a player has resignedpass
- Informs the server a player has passedplayStone
- Informs the server a player has played a stone
The HTTP API is made of only GET requests with URL parameters
Get details about a specific game
/api/v0/game/${id}
{
"messageType": "game",
"id": 293017601,
"gameType": "PUBLIC",
"gameFull": false,
"open": true
}
id
The game id is used to refer to the GamegameType
The type of game can bePUBLIC, PRIVATE, SINGLE_PLAYER
gameFull
Are both players in the gameopen
Can someone join the game
Get the games current state
/api/v0/game/${id}/state
{
"messageType": "state",
"yourTurn": false,
"yourStones": "WHITE",
"turn": "BLACK",
"board": [
["I","I","I","I","I","I","I","I"],
["I","W","B","I","I","I","I","I"],
["I","B","W","I","I","I","I","I"],
["I","I","I","I","I","I","I","I"],
["I","I","I","I","I","I","I","I"],
["I","I","I","I","I","B","W","I"],
["I","I","I","I","I","W","B","I"],
["I","I","I","I","I","I","I","I"]
],
"turnNumber": 0
}
This endpoint will create a new game and return its game ID. This lets a client create a game if no open game exists. If it is a public game then it can be connected to through /api/v0/games/join. If it is private the game link must be shared manually. This lets users play with who they want to by sharing a link to a private game.
/api/v0/game/new?type="public/private/single_player"
{
"messageType": "game",
"id": 293017601,
"gameType": "PUBLIC",
"gameFull": false,
"open": true
}
This endpoint will return a game that is open. This allows the client to connect with someone waiting for a game.
/api/v0/game/join
{
"messageType": "game",
"id": 293017601,
"gameType": "PUBLIC",
"gameFull": false,
"open": true
}
Normal REST style API is good for managing the games but it is not ideal for actually playing the game. This is because only the client is allowed to make requests of the server. The server cannot update the client if state changes. Web-sockets are bidirectional letting the server update the client when the other player makes a move. We send JSON through the web-sockets allowing us to easily decode the messages.
- Each game has its own path based on the game id.
- All messages relating to the game are sent through this web-socket to the server.
- The server is responsible for keeping both clients up to date with the game state.
Connecting to a game via a web-socket is easy. Use a web-socket client to connect to. It will wait for two clients to join before starting the game.
${ws|wss}://${endpoint}/api/v0/game/${game-id}/socket
# e.g
ws://localhost:8080/api/v0/game/1912937816/socket
We need a good way to communicate the boards state between the client and server. Each cell of the board can either be in one of the following states black, white or empty. However to keep the logic server-side we also want to be able to communicate which squares are legal moves and which are illegal moves. Since cells that are already occupied are always illegal we only need to split the empty cells into legal or illegal.
Each cell is one of the following:
B
- BlackW
- WhiteL
- Legal. A clear tile where the player could place a stoneI
- Illegal. A clear tile where a the player cannot place a stone
For example, below is an Othello board. It has been serialised using JSON into a 2D array of strings. It describes the legal moves for white.
[
["I","L","I", "I"],
["L","B","W", "I"],
["I","W","B", "L"],
["I","I","L", "I"]
]
By sharing legal and illegal moves we can offer visual feedback and validation, without having game logic in the client.
Messages sent from the server to the players/clients.
The most commonly used message will be the state message that sends the game state to the players. Each player will be sent a unique board state since it can only be one persons turn.
{
"messageType": "state",
"yourTurn": false,
"yourStones": "WHITE",
"turn": "BLACK",
"board": [
["I","I","I","I","I","I","I","I"],
["I","W","B","I","I","I","I","I"],
["I","B","W","I","I","I","I","I"],
["I","I","I","I","I","I","I","I"],
["I","I","I","I","I","I","I","I"],
["I","I","I","I","I","B","W","I"],
["I","I","I","I","I","W","B","I"],
["I","I","I","I","I","I","I","I"]
],
"turnNumber": 0
}
When the game is over the server informs the players who won as well as score info. The score is currently the number of stones in a colour.
{
"messageType": "gameOver",
"winner": "BLACK",
"scores": {
"black": 7,
"white": 4
},
"isDraw": false,
"isLoser": false,
"isWinner": false
}
If something goes wrong a message should be sent to help with debugging or to help the user. Exceptions come in multiple variants DEFAULT, SUCCESS, ERROR, WARNING, INFO
.
For example when a player unexpectedly leaves the game:
{
"messageType": "status",
"variant": "INFO",
"message": "White has lost connection"
}
Messages sent from the client/player to the server.
When someone is willing to give up they can resign and forfeit.
{"messageType":"resign"}
If it is not advantageous for a player to place a stone they may pass.
{"messageType":"pass"}
When a player plays a stone they need to tell the server. If it is not there turn an error is sent by the server in response.
{
"messageType": "playStone",
"row": 2,
"col": 3
}