_______ .---. .---. ____ _______ ___ _ ,---. .--.
/ __ \ | | |_ _| .' __ `. / __ \ .' | | || \ | |
| ,_/ \__) | | ( ' ) / ' \ \ | ,_/ \__) | .' | || , \ | |
,-./ ) | '-(_{;}_)|___| / |,-./ ) .' '_ | || |\_ \| |
\ '_ '`) | (_,_) _.-` |\ '_ '`) ' ( \.-.|| _( )_\ |
> (_) ) __ | _ _--. | .' _ | > (_) ) __ ' (`. _` /|| (_ o _) |
( . .-'_/ )|( ' ) | | | _( )_ |( . .-'_/ )| (_ (_) _)| (_,_)\ |
`-'`-' / (_{;}_)| | \ (_ o _) / `-'`-' / \ / . \ /| | | |
`._____.' '(_,_) '---' '.(_,_).' `._____.' ``-'`-'' '--' '--'
ChaCuN, which stands for 'Chasseurs et Cueilleurs au Néolithique', is an electronic version of the board game 'Chasseurs et Cueilleurs', derived from the famous 'Carcassonne'. It is the project of first-year students enrolled in the summer 2024 EPFL Computer Science bachelor semester.
ChaCuN is designed to be played by 2 to 5 players, each aiming to build a prehistoric landscape by placing square tiles side by side. The different parts of the landscape, for example, forests, rivers, etc., can be occupied by hunters, gatherers, or fishermen to earn points.
The multiplayer version of ChaCuN is available at https://github.com/simon-valerio-epfl/ChaCuN. Try it with your friends ;)
This WebSocket server allows up to 5 players per ChaCuN game to play together (and remotely!). Originally, this was not possible, as the game was designed to be played on a single computer.
- Instantaneous Responses: The server automatically transmits game actions, which saves time manually updating the game state. This also ensures that the gameplay is not disrupted.
- Game Chat Support: Players can communicate with each other in real-time using the chat feature, even after a game has ended.
The server ensures that players cannot cheat by sending invalid moves or skipping a player's turn.
Players can choose their own username and the name of the game they are playing. The seed of the game is automatically generated by the server.
The integration of a WebSocket server is the backbone of these enhancements. The server handles real-time communication between clients, managing connections, message broadcasting, and synchronization of game states.
The server architecture splits the implementation into three main packages:
ch.epfl.chacun.server
: Handles all the technical aspects of the server, such as the WebSocket server, the connection handlers, and the message handlers.ch.epfl.chacun.game
: Implements the game logic, such as the game state, the game actions, and the game rules.ch.epfl.chacun.logic
: Defines how the server should handle the messages received from the clients. This package is responsible for creating a game lobby, starting a game, and handling game actions, etc.
The server follows the RFC-6455 WebSocket standard. All messages after connection are therefore decoded and encoded in the following format:
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
* |I|S|S|S| (4) |A| (7) | (16/64) |
* |N|V|V|V| |S| | (if payload len==126/127) |
* | |1|2|3| |K| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | |Masking-key, if MASK set to 1 |
* +-------------------------------+-------------------------------+
* | Masking-key (continued) | Payload Data |
* +-------------------------------- - - - - - - - - - - - - - - - +
* : Payload Data continued ... :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued ... |
* +---------------------------------------------------------------+
The RFC6455
utility class is used to parse a payload into a PayloadData
record. With the extracted data of the
payload, we can then use the OpCode
to determine the type of the message received:
TEXT
for a text messageBINARY
for a binary message
Control frames are used to communicate state about the WebSocket.
CLOSE
for a close messagePING
for a ping messagePONG
for a pong message
A client-to-server message is encoded using a masking key:
The masking key is a 32-bit value chosen at random by the client. The unpredictability of the masking key is essential
to prevent authors of malicious applications from selecting the bytes that appear on the wire.
The server can decode the payload data by re-applying the mask key on the data.
Contrary to the client-to-server message, the server-to-client message does not have a masking key. The server can directly encode the payload data and send it to the client.
The server uses the java.nio
package to create an asynchronous server AsyncWebSocketServer
, listening for incoming
connections. When a client tries to connect, a new ChannelConnectionHandler
is created to handle the connection. If
the connection is accepted, a new ChannelReadHandler
is created to process any incoming message.
The abstract AsyncWebSocketServer
extends the WebSocketBroadcaster
abstract class which provides methods to
broadcast messages to all connected clients of a game. Each client can subscribe to a specific game to receive messages
and then unsubscribe.
A WebSocketBroadcaster
extends the WebSocketEventListener
abstract class which provides protected methods to handle
events such as a new connection, a disconnection, or a message received.
This allows the server to define default behavior for these events, such as logging the event but also provides an easy way for the actual server implementation to override these methods and provide custom behavior.
The AsyncWebSocketServer
class has a TimeoutWatcher
that checks for timeouts on the server. If a client has not sent
a message within a certain time frame, the server will close the connection.
Each AsynchronousSocketChannel
is wrapped on connection in a WebSocketChannel
class, which provides a lot of methods
to read and write messages following the RFC-6455 standard. This also allows attaching a context object to the channel,
which can be used to store information on the player (e.g., username).
The GameLogic
class handles all actions related to the server. It is responsible for creating a game lobby, starting a
game, handling game actions, broadcasting messages, etc. This class has a parseAndApplyWebSocketAction
method that
parses the message received from the client and applies the corresponding action. It returns a GameAction
object,
containing all the information needed for the server to send back an action if needed.
If a player wants to create a game, the server will create a new game lobby and add the player to it. If the first player to join the game lobby places a tile, the server will automatically start the game.
In a lobby, players can chat with each other and freely join/leave.
When a game is started, the server will instantiate a new OnGoingGame
.
The OnGoingGame
class is responsible for managing the game state, the game actions, and the game rules.
If a player leaves in the middle of the game, the server will cancel the game.
When a game ends, it will automatically be downgraded to a lobby.
The WebSocketServer
extends the AsyncWebSocketServer
. It overrides the WebSocketEventListener
methods to provide
custom behavior for to match the game logic. For exemple, when a client sends a message, it will ask the GameLogic
to
parse and apply the action and send back a new action to the client, and broadcast it if needed.
If you want to use this server to add multiplayer support to your game, please read the documentation available at https://github.com/polysource-projects/cs108-chacun-multiplayer-docs.