Skip to content

Latest commit

 

History

History
1870 lines (1537 loc) · 69 KB

communication-protocol.md

File metadata and controls

1870 lines (1537 loc) · 69 KB

Table of Contents

  1. Communication protocol documentation
    1. Common data structures
  2. NetEvents - Network level client-server connection management
    1. Connecting to the server
    2. Closing the connection
    3. ErrServerUnavailable
    4. Reconnection
    5. Heartbeat
    6. Errors
  3. Application level client-server connection management
    1. Choosing a nickname
    2. Choosing the number of players
    3. Quitting the game
    4. Reconnecting
  4. VCEvents - Player setup game phase
    1. Choosing leader cards
    2. Choosing starting resources
  5. VCEvents - Turns game phase
    1. Main actions
      1. Getting resources from the market
      2. Buying a development card
      3. Activating productions
      4. Ending the turn
    2. Secondary actions
      1. Swapping two shelves
      2. Leader Actions
        1. Activating a leader card
        2. Discarding a leader card
  6. MVEvents - State events
    1. UpdateActionToken
    2. UpdateActivateLeader
    3. UpdateCurrentPlayer
    4. UpdateDevCardGrid
    5. UpdateDevSlot
    6. UpdateFaithPoints
    7. UpdateGame
      1. Caching
      2. Parameters and indices
    8. UpdateGameEnd
    9. UpdateLastRound
    10. UpdateLeadersHand
    11. UpdateLeadersHandCount
    12. UpdateMarket
    13. UpdatePlayerStatus
    14. UpdateResourceContainer
    15. UpdateSetupDone
    16. UpdateVaticanSection
    17. UpdateVictoryPoints
    18. Shared Errors
      1. ErrAction
      2. ErrNoSuchEntity
      3. ErrObjectNotOwned
      4. ErrReplacedTransRecipe
      5. ErrInvalidResourceTransaction
      6. ErrResourceTransfer

Communication protocol documentation

This document describes the client-server communication protocol used by the implementation of the Masters of Renaissance game written by group AM49.

All messages are encoded using the GSON library and follow therefore the JSON specification, language-wise.
Every value shown in the messages is to be taken as an example, having been written only to show the messages' structure.

Important details about state update messages in MVEvents - State events.

UpdateAction messages confirm the success of the requested action and mark the end of the state update messages stream.

Error messages are unicast to the client that sent the illegal request.

Common data structures

Two data structures that are often used inside messages are:

  1. Resource maps - correlate a string, representing a resource type, with an integer, corresponding to its quantity
  2. Resource container maps - correlate a container ID (expressed as an integer) with a resource map, corresponding to the resource(s) to add to/remove from it

NetEvents - Network level client-server connection management

To make the protocol more resilient and situation-aware, NetEvents have been separated from application-level events.

The player, when starting the client, can choose whether to connect to the server (singleplayer and multiplayer playmodes) or playing locally (singleplayer only).
In both cases the messages sent are the same and happen in the exact same way: the network side of the project has been implemented to allow changing the transport layer transparently to both the client and the server.

Connecting to the server

Upon connection, a handshake takes place:

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │ ReqWelcome                     │
     ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
     │                                │
     │                     ResWelcome │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │

ReqWelcome (client)

{
  "type": "ReqWelcome"
}

ResWelcome (server)

{
  "type": "ResWelcome"
}

Closing the connection

The event of the connection closing can happen for three reasons:

  1. The player quits the game
  2. The client crashes
  3. The server crashes

Quitting the client sends one final application-level message to the server ( see Quitting the game), allowing the server to distinguish between the situations. After the server has executed the quit routine, the sockets can now be closed.
A ReqGoodbye event is sent by the client:

{ "type": "ReqGoodbye" }

A ResGoodbye message is sent by the server as an acknowledgement:

{
  "type": "ResGoodbye"
}

Only then the network sockets is closed by both parties.

ErrServerUnavailable

This message is used internally by the network layer to notify the application layer of the fact that the connection with the server was closed by the server itself.

ErrServerUnavailable

{ "type": "ErrServerUnavailable" }

Reconnection

The player is allowed to connect back to the server and join the game they were previously in.
The reconnection routine at the network level is exactly the same as a normal connection.

Heartbeat

The server checks whether the clients are still alive using both normal messages and heartbeat messages (the timeout resets with any kind of message).
Heartbeat messages are sent at regular intervals.

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │                   ReqHeartbeat │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │
     │ ResHeartbeat                   │
     ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
     │                                │

ReqHeartbeat (server)

{
  "type": "ReqHeartbeat"
}

ResHeartbeat (client)

{
  "type": "ResHeartbeat"
}

Errors

Broken (unparsable) messages are signaled with a ErrProtocol messsage containing information about the error itself:

{
  "type": "ErrProtocol",
  "msg": "Error parsing the message: invalid token 'a'"
}

Application level client-server connection management

A summary of the requirements highlighting the relevant parts is reported below:

  • On player connection: the player is automatically added to the game currently being filled. If there is no game in its starting phase, a new one is created.
  • The player starting a new game chooses how many players have to join before the game can start.
  • The game starts as soon as the specified number of players (given by the first player to join) is reached.
  • The server manages the players' turns as per the game's rules. The server must handle a player disconnecting or leaving the game; if there are no players left the game will terminate and all players have to be notified.

The following specification for the additional feature "Multiple Games" is taken into account in the communication protocol's design:

  • Only one waiting room is used to manage the players who join the server.
  • Players who disconnect can successively reconnect to continue the game. While a player is not connected, the game continues skipping the player's turns.

Given those requirements, the communication at connection time has been modeled the following way.

Choosing a nickname

After establishing a connection with the server, the client will ask the player to input a nickname of their choice. The entry is sent to the server, and, if acceptable (unique among the registered nicknames, not empty, not already set), is accepted. Else, the server will signal the error, restarting the process.

Information about the player being the first of the game is included in the response sent to the client. This is necessary to handle the choice of the number of players.

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │ ReqJoin                        │
     ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
     │                                │ ╭───────────────────────╮
     │                                ├─┤ check nickname unique │
     │              UpdateBookedSeats │ ╰───────────────────────╯
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │
     │                    ErrNickname │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │

ReqJoin (client)

{
  "type": "ReqJoin",
  "nickname": "NicknameA"
}

UpdateBookedSeats (server)

{
  "type": "UpdateBookedSeats",
  "bookedSeats":1,
  "canPrepareNewGame":"NicknameA"
}

ErrNickname (server)

{
  "type": "ErrNickname",
  "reason": "TAKEN"
}

The UpdateBookedSeats message gives the client information about who is the first in the waiting list (and therefore can choose the new game's player count) and the quantity of players in the waiting list. When the game is waiting for players to join before its start, sending notifications allows the players who already joined to know how many empty seats are left, therefore getting a sense for how much waiting time there is left.

Choosing the number of players

When a player is assigned by the server as the first of a new game, they have to decide the number of players required to start it.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │
╰────────────╯ │ ReqNewGame                     │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭─────────────╮
               │                                ├─┤ try setting │
               │                 UpdateJoinGame │ ╰─────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqNewGame (client)

{
  "type": "ReqNewGame",
  "playersCount": 3
}

UpdateJoinGame (server)

{
  "type": "UpdateJoinGame",
  "playersCount": 3
}

ErrNewGame (server)

{
  "type": "ErrNewGame",
  "isInvalidPlayersCount": false
}

The UpdateJoinGame message signals to the first playersCount waiting clients that a new game is undergoing creation and the number of players included. Any other client that joins the server and is assigned to the starting game will also receive the message.
If a ReqNewGame is received from a client that is not allowed to request a new game, ErrNewGame.isInvalidPlayersCount is set to false.

See the Game start section for information on how the protocol defines the initial data transfer after joining a game.

Quitting the game

When the player quits the game, the client will send a ReqQuit message:

ReqQuit (client)

{
  "type":"ReqQuit"
}

The server will then execute an internal routine that will allow the player to reconnect at a later time and join back the game they were in, notifying at the same time the other players of the game of the event ( see UpdatePlayerStatus).
A ResQuit message is sent to the client to notify it about the closing routine being completed.

ResQuit (server)

{
  "type":"ResQuit"
}

The network-level closing routine will then start (see Closing the connection).

Reconnecting

A player can reconnect to a game they left, if it's still ongoing.
This is done at connection time by choosing the same nickname as they previously had.

When reconnecting, the server will send all the necessary game data for the client to cache ( see UpdateGame). The client will therefore be able to judge what phase of the game is currently in place (setup, turns, who the current player is, etc.).

VCEvents - Player setup game phase

When the game starts, the server instantiates its internal model.
It will then send the game data to the clients (see UpdateGame), and the player setup phase will start.

During this phase the players will have to choose the quantity of leader cards and resources specified in the configuration file.

Choosing leader cards

As per the game's rules, the players have to decide manually what leader cards they want to keep for the game's duration.

The client is sent the IDs of the leader cards they can choose from, and will send back a subset of them.

Errors related to this action are:

  1. ErrAction message, with reason LATE_SETUP_ACTION - the request message is sent too late (the setup phase is already concluded)
  2. ErrObjectNotOwned - the request message contains IDs that are not in the player's card list
  3. ErrInitialChoice - the request message contains too few IDs or the leader cards have alerady been chosen by the player
           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
╭────────────╮ │                                │
│ user input ├─┤                                │
╰────────────╯ │ ReqChooseLeaders               │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │               ErrInitialChoice │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqChooseLeaders (client)

{
  "type": "ReqChooseLeaders",
  "leaders": [ 3, 15 ]
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "CHOOSE_LEADERS",
  "player": "NicknameA"
}

ErrInitialChoice (server)

{
  "type": "ErrInitialChoice",
  "isLeadersChoice": true,
  "missingLeadersCount": 2
}

Choosing starting resources

After choosing the leader cards the players is prompted to choose their starting resources, following the configuration file's settings.

The client is sent the quantity of resources and the resource types the player has to choose from ( see UpdateGame).
Every player will have to choose the resources before the game's turns can start, thereby concluding the setup phase.

The shelves field is a standard resource container map

Error messages are fired in these situations:

  1. ErrAction message, with reason LATE_SETUP_ACTION - the request message is sent too late (the setup phase is already concluded)
  2. ErrObjectNotOwned - the request message contains resource container IDs that are not owned the player
  3. ErrNoSuchEntity - a resource type that does not exist is specified
  4. ErrInitialChoice - the resurces were already chosen
  5. ErrResourceTransfer - a number of resources exceeding the shelf's capacity is requested to be added to a shelf
           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
╭────────────╮ │                                │
│ user input ├─┤                                │
╰────────────╯ │ ReqChooseResources             │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                ErrNoSuchEntity │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │               ErrInitialChoice │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrResourceTransfer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqChooseResources (client)

{
  "type": "ReqChooseResources",
  "shelves": [
    { "1": { "Coin": 1 } },
    { "0": { "Shield": 1 } }
  ]
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "CHOOSE_RESOURCES",
  "player": "NicknameA"
}

ErrAction (server)

{
  "type": "ErrChooseResources",
  "msg": "Cannot choose 2 starting resources, only 1 available."
}

ErrInitialChoice (server)

{
  "type": "ErrInitialChoice",
  "isLeadersChoice": false,
  "missingLeadersCount": 0
}

VCEvents - Turns game phase

After all players have gone through the setup phase, the server will start the turn loop.

The messages in this section can be differentiated into:

  1. Main actions, of which the player has to make only one during the turn
  2. Secondary actions, which can be repeated within the player's turn
  3. State messages, which update the local caches' state to game the server's

Main actions

During their turn, the player has to choose which action to take among these:

  1. Getting resources from the market
  2. Buying a development card
  3. Activating the production

Since the player may want to make a secondary move after the main action, the server waits for a TurnEnd message before switching to the next player.

Getting resources from the market

The following needs to be specified:

  1. Which row/column the player wants to take the resources from
  2. For each replaceable resource, which resource type to convert it to (if applicable)
  3. For each resource (its type considered after the leaders' processing), which shelf to use to store it
  4. What resources, among the ones taken from the market, to discard

Discarding is simply handled by specifying a lower quantity of resources to add to a shelf. This also easily games the rule for which only the resources given by the market can be discarded.

The replacements field specifies how the resource conversion should be handled. Since the player knows what type of resource the leader converts to, they can easily select them by specifying, for each type of resource they want as output, how many replaceable resources (of the available ones) to use.

The replacement field is a standard resource map The shelves field is a standard resource container map

Errors may arise from fitting the resources in the shelves, either by specifying the wrong shelf or by not discarding enough resources. See Shared Errors for more details on the errors that may be generated by the request.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqTakeFromMarket              │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                ErrNoSuchEntity │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrResourceReplacement │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrReplacedTransRecipe │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrResourceTransfer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqTakeFromMarket (client)

{
  "type": "ReqTakeFromMarket",
  "isRow": true,
  "index": 0,
  "replacements": { "Coin": 2 },
  "shelves": [
    { "1": { "Coin": 2 } },
    { "3": { "Shield": 1 } }
  ]
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "TAKE_MARKET_RESOURCES",
  "player": "NicknameA"
}

Buying a development card

The following information is needed when buying a development card:

  1. The row and column of the card to identify it in the grid
  2. The slot to place the card into
  3. For each resource that has to be paid, the shelf (or strongbox) to take it from

The resContainers field is a standard resource container map

An ErrBuyDevCard message signals:

  1. the selected color/level refer to an empty deck of cards (isStackEmpty is set to true)
  2. the card's level does not allow the card to be placed in the selected slot (isStackEmpty is set to false)

An ErrCardRequirements message signals which cost requirements the player is missing.

The missingResources field is a standard resource map

Other possible errors include not identifying a valid card/slot, not satisfying placement requirements (the card's level is not one above the level of the card it is being placed onto) and not specifying correctly the resource transaction's details.
See Shared Errors for more details on the errors that may be generated by the request.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqBuyDevCard                  │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                ErrNoSuchEntity │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrResourceReplacement │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrReplacedTransRecipe │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrResourceTransfer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                  ErrBuyDevCard │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrCardRequirements │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqBuyDevCard (client)

{
  "type": "ReqBuyDevCard",
  "level": 1,
  "color": "Blue",
  "devSlot": 2,
  "resContainers": [
    { "1": { "Coin": 2 } },
    { "3": { "Shield": 1 } }
  ]
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "BUY_DEVELOPMENT_CARD",
  "player": "NicknameA"
}

ErrBuyDevCard (server)

{
  "type": "ErrBuyDevCard",
  "isStackEmpty": true
}

ErrCardRequirements (server)

{
  "type": "ErrCardRequirements",
  "missingDevCards": null,
  "missingResources": { "Coin": 1, "Shield": 2 }
}

Activating productions

The following information is needed when activating productions:

  1. Which productions to activate
  2. For each production, a
  3. Each non-storable resource chosen as a replacement in the input
  4. The storable and non-storable output replacements

The inputContainers field is a standard resource container map detailing all (default and replaced) resources in input. The non-storable replacements have to be specified by themselves in the inputNonStorableRep field (which is a standard resource map). The outputRep field specifies the replacements pertaining to the output side of the productions (it's a standard resource map since all output goes to the player's strongbox).

Errors may originate from issues with referencing inexistent/not owned objects, resource replacements and resource transfers.
See Shared Errors for more details on a specific error.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqActivateProductions         │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                ErrNoSuchEntity │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrResourceReplacement │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │         ErrReplacedTransRecipe │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrResourceTransfer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqActivateProductions (client)

{
  "type": "ReqActivateProductions",
  "prodRequests": [
    {
      "id": 0,
      "outputRep": { "Faith": 1 },
      "inputContainers": [
        { "1": { "Coin": 1 } },
        { "2": { "Coin": 2 } },
        { "0": { "Shield": 2 } }
      ]
    }, {
      "id": 3,
      "outputRep": { "Faith": 1 },
      "inputContainers": [
        { "0": { "Stone": 2 } }
      ],
      "inputNonStorableRep": { "Faith": 1 }
    }
  ]
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "ACTIVATE_PRODUCTION",
  "player": "NicknameA"
}

Ending the turn

Since the server cannot at any point assume that the player has finished choosing their moves ( see secondary actions), an explicit message has to be sent.

If a player ends their turn early (without having done a mandatory action) an ErrAction with "reason": "EARLY_TURN_END" is generated.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqEndTurn                     │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────╮
               │                                ├─┤ exec │
               │        *state update messages* │ ╰──────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqEndTurn (client)

{ "type": "ReqEndTurn" }

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "END_TURN",
  "player": "NicknameA"
}

Secondary actions

Secondary moves can be performed as often as the player wants and at any point of the turn. They are:

  1. Swapping the content of the player's shelves
  2. Activating/discarding a leader card

Swapping two shelves

During their turn, the player can decide to reorder the content of their warehouse's shelves and leader depots.

This is technically only useful when taking resources from the market, as no other action refills the shelves, but it was left as an always-possible operation to improve the gameplay experience.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqSwapShelves                 │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrResourceTransfer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqSwapShelves (client)

{
  "type": "ReqSwapShelves",
  "shelf1": 0,
  "shelf2": 3
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "SWAP_SHELVES",
  "player": "NicknameA"
}

Leader actions

During their turn, in addition to one of the main three actions, a player can choose to discard or activate their leader cards.

To activate or discard a leader the server needs to know which card the player wants to act on and which action to perform.

Activating a leader card

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqLeaderAction                │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │            ErrCardRequirements │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqLeaderAction (client)

{
  "type": "ReqLeaderAction",
  "leader": 0,
  "isActivate": true
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "ACTIVATE_LEADER",
  "player": "NicknameA"
}

ErrCardRequirements (server)

{
  "type": "ErrCardRequirements",
  "missingDevCards": [
    {
      "color": "Blue",
      "level": 1,
      "quantity": 2
    }
  ],
  "missingResources": null
}

If a leader is activated while already active no error is raised, since it's not a critical event.

Discarding a leader card

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
╭────────────╮ │                                │
│ user input ├─┤                                │ 
╰────────────╯ │ ReqLeaderAction                │
               ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━►│
               │                                │ ╭──────────────────╮
               │                                ├─┤ try exec / check │
               │        *state update messages* │ ╰──────────────────╯
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                   UpdateAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │                      ErrAction │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │              ErrObjectNotOwned │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │
               │       ErrActiveLeaderDiscarded │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

ReqLeaderAction (client)

{
  "type": "ReqLeaderAction",
  "leader": 0,
  "isActivate": false
}

UpdateAction (server)

{
  "type": "UpdateAction",
  "action": "DISCARD_LEADER",
  "player": "NicknameA"
}

ErrActiveLeaderDiscarded (server)

{
  "type": "ErrActiveLeaderDiscarded"
}

The ErrActiveLeaderDiscarded error message is sent by the server when a player tries to discard an active leader card.

MVEvents - State events

These messages are used to update the clients' caches so that the data is synchronized with the server's.

State update messages are sent autonomously by the updated entities. Clients cannot assume that a state update message will be sent or the timing it will be sent with (ordering with respect to other messages, etc.).
State update messages are broadcast unless specified.

IDs reference the data given in game start.

UpdateActionToken

Notifies clients of the activation of an action token, sending its ID.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │              UpdateActionToken │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateActionToken (server)

{
  "type": "UpdateActionToken",
  "actionToken": 6
}

UpdateActivateLeader

Notifies clients when a leader card is activated.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │           UpdateActivateLeader │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateActivateLeader (server)

{
  "type": "UpdateActivateLeader",
  "leader": 1
}

UpdateCurrentPlayer

Notifies clients of who the current player is from the moment the message is sent.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │            UpdateCurrentPlayer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateCurrentPlayer (server)

{
  "type": "UpdateCurrentPlayer",
  "nickname": "NicknameA"
}

UpdateDevCardGrid

Notifies the clients of an update in the development card grid's state.

The list of IDs are the respective top cards of each deck, the level of which is the card's position in the list and the color of which is specified in the enclosing map.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │              UpdateDevCardGrid │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateDevCardGrid (server)

{
  "type": "UpdateDevCardGrid",
  "topCards": {
    "levelsCount": 3,
    "colorsCount": 4,
    "grid": {
      "Purple": [
        [ 2, 0, 1, 3 ]
      ],
      "Green": [
        [ 8, 7, 6, 4 ]
      ]
    }
  }
}

UpdateDevSlot

Notifies the clients of cards being in a player's board's development card slot.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │              UpdateDevSlot │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateDevSlot (server)

{
  "type": "UpdateDevSlot",
  "player": "NicknameA",
  "slot": 0,
  "cards": [ 7, 9 ]
}

UpdateFaithPoints

Notifies clients of a player progressing on the faith track.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │               UpdateFaithTrack │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateFaithTrack (server)

{
  "type": "UpdateFaithTrack",
  "isBlackCross": false,
  "faithPoints": 14,
  "player": "NicknameA"
}

UpdateGame

As the game starts, the server notifies all players of the event.

Caching

The server sends the game's state to be cached by the clients. Caching parts of the game's state allows the clients to answer requests without the server's intervention.
Caching allows partial checks to be preemptively (but not exclusively) done client side: if the player specifies an index that's out of bounds, the client is able to catch the error before sending the request to the server, reducing network and server loads and improving the game's responsiveness.

Parameters and indices

The game's model has been parameterized to allow for flexibility. The parameters are set via a configuration file, which also contains serialized game data (e.g. cards, resources, etc).

Clients have a default configuration file embedded to allow for local single player games. Both clients and server also support loading custom configuration files.

Since the file on a server may be different from the one embedded in a client, all game elements need to be sent at the start of a game, ensuring proper synchronization between the clients and the server, both in terms of IDs and actual game data.

Since the connection and reconnection phases are very delicate, a modular approach was discarded in favor of a monolithic message.
If a modular approach were to be chosen, the clients' state-switching logic would become unmanageable, needing to manage asynchronous and independent state messages to handle transitions that depend on the presence of the data itself (some of these transitions would in fact be impossible to model if the data was not sent all in the same message).

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │                     UpdateGame │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │

UpdateGame (server)

{
  "type": "UpdateGame",
  "players": [
    {
      "nickname": "NicknameA",
      "baseProduction": 0,
      "warehouseShelves": [ 0, 1, 2 ],
      "strongbox": 3,
      "setup": {
        "chosenLeadersCount": 2,
        "initialResources": 1,
        "initialExcludedResources": [ "Faith" ],
        "hasChosenLeaders": false,
        "hasChosenResources": false
      },
      "leadersHand": [ 3, 5, 7, 15 ],
      "leadersHandCount": 4,
      "devSlots": [ [], [], [] ],
      "faithPoints": 0,
      "victoryPoints": 0,
      "active":true
    }
  ],
  "devCardColors": [
    {
      "name": "Blue",
      "ansiColor": "\u001b[34m"
    },
    {
      "name": "Green",
      "ansiColor": "\u001b[32m"
    }
  ],
  "resourceTypes": [
    {
      "name": "Servant",
      "ansiColor": "\u001B[95m",
      "isStorable": true,
      "isGiveableToPlayer": true,
      "isTakeableFromPlayer": true
    }, {
      "name": "Zero",
      "ansiColor": "\u001B[37m",
      "isStorable": false,
      "isGiveableToPlayer": false,
      "isTakeableFromPlayer": false
    }
  ],
  "leaderCards": [
    {
      "resourceType": "Servant",
      "leaderType": "DiscountLeader",
      "devCardRequirement": {
        "entries": [
          { "color": "Yellow", "quantity": 1, "level": 2 }
        ]
      },
      "isActive": false,
      "containerId": -1,
      "discount": 1,
      "id": 0,
      "victoryPoints": 2,
      "production": -1
    }
  ],
  "developmentCards": [
    {
      "color": "Green",
      "cost": {
        "requirements": { "Shield": 1 }
      },
      "level": 1,
      "id": 0,
      "victoryPoints": 1,
      "production": 5
    }
  ],
  "resContainers": [
    {
      "id": 4,
      "content": {},
      "boundedResType": "Stone",
      "size": 2
    }, {
      "id": 3,
      "content": {},
      "size": -1
    }
  ],
  "productions": [
    {
      "id": 0,
      "input": {},
      "inputBlanks": 2,
      "inputBlanksExclusions": [],
      "output": {},
      "outputBlanks": 1,
      "outputBlanksExclusions": [ "Faith" ],
      "discardableOutput": false
    }
  ],
  "actionTokens": [
    {
      "id": 0,
      "kind": "ActionTokenBlackMoveOneShuffle"
    }, {
      "id": 3,
      "kind": "ActionTokenDiscardTwo",
      "discardedDevCardColor": "Blue"
    }
  ],
  "faithTrack": {
    "vaticanSections": {
      "16": {
        "id": 2,
        "faithPointsBeginning": 12,
        "faithPointsEnd": 16,
        "victoryPoints": 3,
        "activated": false,
        "bonusGivenPlayers": []
      }
    },
    "yellowTiles": [
      {
        "faithPoints": 12,
        "victoryPoints": 6
      }
    ]
  },
  "market": {
    "grid": [
      [ "Stone", "Shield", "Zero", "Faith" ],
      [ "Zero", "Servant", "Zero", "Zero" ]
    ],
    "replaceableResType": "Zero",
    "slide": "Servant"
  },
  "devCardGrid": {
    "levelsCount": 3,
    "colorsCount": 4,
    "topCards": {
      "Yellow": [ null, 11, 31, 39 ],
      "Purple": [ null, 5, 29, 33 ]
    }
  },
  "isSetupDone": false,
  "devSlotsCount": 3,
  "currentPlayer": "NicknameA",
  "inkwellPlayer": "NicknameA",
  "blackPoints": 0,
  "lastRound": false,
  "ended": false,
  "isMandatoryActionDone": false
}

UpdateGameEnd

Notifies the clients of the end of the game, detailing the winner player.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │                  UpdateGameEnd │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateGameEnd (server)

{
  "type": "UpdateGameEnd",
  "winner": "NicknameA"
}

UpdateLastRound

Notifies the clients of the current round being the last.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │                UpdateLastRound │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateLastRound (server)

{
  "type": "UpdateLastRound"
}

UpdateLeadersHand

A player's leader cards are hidden from the other players until activated. This message is therefore sent only to the owner of the cards, whom can see their IDs.
The other players will receive a UpdateLeadersHandCount event, which only contains the number of cards owned by the other player.
Upon receiving a UpdateLeadersHand event, the non-owner players will know that the currently-playing player has activated a card and its ID. They can therefore add the ID to the current player's leader cards hand.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │              UpdateLeadersHand │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateLeadersHand (server)

{
  "type": "UpdateLeadersHand",
  "player": "NicknameA",
  "leaders": [ 3, 5 ]
}

UpdateLeadersHandCount

Given the explanation of UpdateLeadersHand, this message contains the number of leader card a player is holding.
This allows for the cards' IDs to remain hidden while informing clients of events such as a card being discarded.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │         UpdateLeadersHandCount │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateLeadersHandCount (server)

{
  "type": "UpdateLeadersHandCount",
  "player": "NicknameA",
  "leadersHandCount": 1
}

UpdateMarket

This message holds the current state of the game's market.

It specifies what resource type is to be accounted for as replaceable, since the configuration file allows for it to be changed.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │                   UpdateMarket │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateMarket (server)

{
  "type": "UpdateMarket",
  "market": {
    "grid": [
      [ "Coin", "Servant", "Stone", "Shield" ],
      [ "Shield", "Faith", "Zero", "Zero" ]
    ],
    "replaceableResType": "Zero",
    "slide": "Stone"
  }
}

UpdatePlayerStatus

Notifications about players connecting/disconnecting from a game are sent via this message.

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │   UpdatePlayerStatus │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │

UpdatePlayerStatus (server)

{
  "type": "UpdatePlayerStatus",
  "nickname": "NicknameA",
  "isActive": true
}

UpdateResourceContainer

This message details the current state of the resource container with the specified ID.

Strongboxes have size set to -1.
If there is no binding resource the boundedResType field is not specified.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │        UpdateResourceContainer │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateResourceContainer (server)

{
  "type": "UpdateResourceContainer",
  "resContainer": {
    "id": 3,
    "content": {
      "Coin": 3
    },
    "boundedResType": "Coin",
    "size": 1
  }
}

UpdateSetupDone

When every player has chosen their leader cards and starting resources the conclusion of the setup phase is notified to the clients.

 ┌────────┒                      ┌────────┒ 
 │ Client ┃                      │ Server ┃
 ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
     │                                │
     │                UpdateSetupDone │
     │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
     │                                │

UpdateSetupDone (server)

{
  "type": "UpdateSetupDone"
}

UpdateVaticanSection

This message contains the list of players who benefit from the vatican section's bonus points.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │           UpdateVaticanSection │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateVaticanSection (server)

{
  "type": "UpdateVaticanSection",
  "id": 60,
  "bonusGivenPlayers": "NicknameA"
}

UpdateVictoryPoints

This message contains the current quantity of victory points for the specified player.
The victory points' quantity is updated in real time.

           ┌────────┒                      ┌────────┒ 
           │ Client ┃                      │ Server ┃
           ┕━━━┯━━━━┛                      ┕━━━━┯━━━┛
               │                                │
               │            UpdateVictoryPoints │
               │◄━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
               │                                │

UpdateVictoryPoints (server)

{
  "type": "UpdateVictoryPoints",
  "player": "NicknameA",
  "victoryPoints": 20
}

Shared Errors

Error messages that are sent in multiple occasions are reported here.
Any successive mentions of these messages refer to this section for syntax and examples.

ErrAction

This message signals to the client that the action is being requested at the wrong time.
The reason field offers a more detailed explanation:

  1. LATE_SETUP_ACTION - a setup request is sent after the setup phase is concluded
  2. EARLY_MANDATORY_ACTION - an action request (non-setup) is sent during the setup phase
  3. LATE_MANDATORY_ACTION - a action request (non-setup) is sent for the second time during a player's turn
  4. EARLY_TURN_END - a request to end the player's turn is sent before a mandatory action request
  5. GAME_ENDED - an action request is sent after the game's end
  6. NOT_CURRENT_PLAYER - the player requesting the action is not the current player

ErrAction (server)

{
  "type": "ErrAction",
  "reason": "LATE_SETUP_ACTION"
}

ErrNoSuchEntity

This message signals the absence of an entity to game an ID with.

The originalEntity field describes the kind of entity the request pertained to, which include:

  1. MARKET_INDEX - index of a market's row/column does not exist
  2. LEADER - leader card with referenced ID does not exist
  3. DEVCARD - development card with referenced ID does not exist
  4. COLOR - referenced color does not exist
  5. RESOURCE - referenced resource type does not exist

The message also reports the id or the code string of the missing object.

ErrNoSuchEntity (server)

{
  "type": "ErrNoSuchEntity",
  "originalEntity": "LEADER",
  "id": 50,
  "code": null
}

ErrObjectNotOwned

When a request message from a client references the ID of an object that is not owned by the player, an ErrObjectNotOwned message is sent by the server, detailing the erroneus ID and the object's kind.

ErrObjectNotOwned (server)

{
  "type": "ErrObjectNotOwned",
  "id": "NicknameA",
  "objectType": "LeaderCard"
}

ErrReplacedTransRecipe

This message signals a discrepancy between the available and specified numbers of resources to be put in a container.

The isInput field indicates where in the recipe (input/output) the error happened. The replacedCount field details the number of available resources in the transaction after the replacements have been factored in.
The shelvesChoiceResCount field details the number of resources requested to be put in the container.
The isIllegalDiscardedOut field specifies whether the discrepancy is to be reconduced to illegally discarded resources in output.

ErrReplacedTransRecipe (server)

{
  "type": "ErrReplacedTransRecipe",
  "isInput": true,
  "resType": "Coin",
  "replacedCount": 3,
  "shelvesChoiceResCount": 4,
  "isIllegalDiscardedOut": false
}

ErrInvalidResourceTransaction

This message signals an error when validating a resource transaction request.
The issue might lie in either of the shelf maps or the replacement maps.

The isInput field distinguishes between the transaction's input and output resources, while the isReplacement field distinguishes between replacements and shelf maps.
The reason field details the reason for which the request was denied, and can be one of:

  1. NEGATIVE_VALUES - maps contain negative resource quantities
  2. ILLEGAL_STORABLE - a storable resource is specified when non-storable are allowed only
  3. ILLEGAL_NON_STORABLE - same as above, but inverted
  4. EXCLUDED - a forbidden resource is used as a replacement

ErrResourceReplacement (server)

{
  "type": "ErrResourceReplacement",
  "isInput": true,
  "isReplacement": false,
  "reason": "ILLEGAL_NON_STORABLE"
}

ErrResourceTransfer

This error signals an error with a resource transfer request.

Reasons included in the message are:

  1. BOUNDED_RESTYPE_DIFFER - a resource is trying to be added/removed to a shelf that's bound to another resource type
  2. NON_STORABLE - a non-storable resource is trying to be added/removed to a resource container
  3. CAPACITY_REACHED - the resource transfer requests that the number of resulting resources in the container is either less than zero or greater than the container's capacity
  4. DUPLICATE_BOUNDED_RESOURCE - a resource is trying to be added to a shelf while there is another shelf bound to the same resource type

ErrResourceTransfer (server)

{
  "type": "ErrResourceTransfer",
  "resType": "Coin",
  "isAdded": true,
  "reason": "CAPACITY_REACHED"
}