Skip to content

Enhancement: Player team and role selection #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 60 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
9b38a06
Move game_data_channel specs into folder, shorten filenames
whois-jon-peterson Aug 12, 2019
1c7251c
Backfill tests for player unsubscribing
whois-jon-peterson Aug 12, 2019
2811ce2
Backfill test players can't join game which is over
whois-jon-peterson Aug 12, 2019
df8efe7
Add test that player can select a team
whois-jon-peterson Aug 12, 2019
facb226
Scaffold pending tests
whois-jon-peterson Aug 12, 2019
050d06f
Add LobbyActions module to GameDataChannel
whois-jon-peterson Aug 12, 2019
0dcf3ab
Add method to select team
whois-jon-peterson Aug 12, 2019
b342ff3
Add can_join_team method to Player
whois-jon-peterson Aug 12, 2019
2359a8d
Add is_blue_team? method to Player
whois-jon-peterson Aug 12, 2019
b902b0f
Add is_intel? method to Player
whois-jon-peterson Aug 12, 2019
5ceccc8
Update current_player on successful selection
whois-jon-peterson Aug 12, 2019
cf92d0a
Fix test accessing role
whois-jon-peterson Aug 12, 2019
6ec66a0
Add test that player can select a role
whois-jon-peterson Aug 12, 2019
4e0e9a8
Add select_role method to LobbyActions
whois-jon-peterson Aug 12, 2019
4bdd261
Add can_join_role? method to Player
whois-jon-peterson Aug 12, 2019
1935596
Add test that player cannot select full team
whois-jon-peterson Aug 12, 2019
5ed47eb
Add test that player cannot select full role
whois-jon-peterson Aug 12, 2019
11b39e1
Expect boolean and message returns for selection validators
whois-jon-peterson Aug 12, 2019
bb1d319
Add messages to validator returns
whois-jon-peterson Aug 12, 2019
35af76e
Correct typo
whois-jon-peterson Aug 12, 2019
ba56aa6
Add deny_selection method to LobbyActions
whois-jon-peterson Aug 12, 2019
5bcd342
Add correct error messaging for spy vs intel role.
whois-jon-peterson Aug 12, 2019
3b98e43
Add tests that player cannot choose role if full for team
whois-jon-peterson Aug 12, 2019
cbbd57f
Refactor counting team/role collisions to private method
whois-jon-peterson Aug 12, 2019
45017bb
Use collision_count method
whois-jon-peterson Aug 12, 2019
fa486c8
Change error messaging to match expectation
whois-jon-peterson Aug 12, 2019
792fc8f
Add test that player cannot join as spy if team spies full
whois-jon-peterson Aug 12, 2019
7aa4c2e
Add test that player cannot join as intel if team intel is full
whois-jon-peterson Aug 12, 2019
e777d2d
Add test that player cannot change team/role after game start
whois-jon-peterson Aug 12, 2019
30b68f0
Move error messages to method
whois-jon-peterson Aug 12, 2019
11ba9c8
Use err method
whois-jon-peterson Aug 12, 2019
3b64229
Reject if game started
whois-jon-peterson Aug 12, 2019
d75f63d
Add method to game to check if started
whois-jon-peterson Aug 12, 2019
df6bea4
Ensure players subscribed for test
whois-jon-peterson Aug 12, 2019
6f826f8
Add tests that player can still change if current selection is full
whois-jon-peterson Aug 12, 2019
6c73ddd
Add expectation that team/role appears in player-joined message
whois-jon-peterson Aug 12, 2019
1b0b6f5
Unify compose_players/compose_roster behavior
whois-jon-peterson Aug 12, 2019
736ad03
Use unified compose_players method instead of compose_roster
whois-jon-peterson Aug 12, 2019
ed48c10
Add isBlueTeam and isIntel to player-joined message
whois-jon-peterson Aug 12, 2019
8f35630
Update player-joined documentation
whois-jon-peterson Aug 12, 2019
08d36d6
Add Select Team documentation
whois-jon-peterson Aug 12, 2019
c80051b
Add Select Role documentation
whois-jon-peterson Aug 12, 2019
40b63dd
Add player-update documentation
whois-jon-peterson Aug 12, 2019
cf15e18
Add category to illegal-action documentation
whois-jon-peterson Aug 12, 2019
998dc65
Add error messages for player team/role selection to documentation
whois-jon-peterson Aug 12, 2019
5a78379
Update messaging table in README
whois-jon-peterson Aug 12, 2019
0ee1fed
Left align 'keys' in all tables
whois-jon-peterson Aug 12, 2019
66232e5
Separate game-setup message from start_game method
whois-jon-peterson Aug 13, 2019
fc0195b
Separate hint-provided message from send_hint method
whois-jon-peterson Aug 13, 2019
18ce6fc
Add expectation that game doesn't start immediately
whois-jon-peterson Aug 13, 2019
0bcd6d9
Remove call to start_game from welcome_player
whois-jon-peterson Aug 13, 2019
90e20f5
Mark test for previous behavior as pending
whois-jon-peterson Aug 13, 2019
e4cff6b
Manually establish game in Guess tests
whois-jon-peterson Aug 13, 2019
d7722b4
Manually establish game in Player Selection tests
whois-jon-peterson Aug 13, 2019
ec995d0
Manually establish game in Hint tests
whois-jon-peterson Aug 13, 2019
b50076d
Add test for manual start_game action
whois-jon-peterson Aug 13, 2019
ba0e205
Remove defunct test for old behavior
whois-jon-peterson Aug 13, 2019
76a60a1
Update documentation to reflect start_game behavior
whois-jon-peterson Aug 13, 2019
bc30f15
Add db-query-matchers gem
whois-jon-peterson Aug 13, 2019
ccb7fa3
Add test to ensure player selections are respected
whois-jon-peterson Aug 13, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ group :development, :test do
gem 'awesome_print'
gem 'orderly'
gem 'action-cable-testing'
gem 'db-query-matchers'
end

group :development do
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ GEM
concurrent-ruby (1.1.5)
crass (1.0.4)
database_cleaner (1.7.0)
db-query-matchers (0.9.0)
activesupport (>= 4.0, <= 6.0)
rspec (~> 3.0)
diff-lcs (1.3)
docile (1.3.2)
erubi (1.8.0)
Expand Down Expand Up @@ -254,6 +257,7 @@ DEPENDENCIES
byebug
capybara
database_cleaner
db-query-matchers
jbuilder (~> 2.5)
launchy
listen (>= 3.0.5, < 3.2)
Expand Down
140 changes: 123 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ At this point, we have everything we need for our Rails environment. If you wish

## Websockets Message Events

|From Server |From Client |
|:---: |:---: |
|[Player Joined](#player-joined) | |
|[Game Started](#game-started) | |
|[Hint Provided](#hint-provided) |[Hint Sent](#hint-sent) |
|[Board Update](#board-update) |[Guess Sent](#guess-sent)|
|[Game Over](#game-over) | |
|[Illegal Action](#illegal-action)| |
|From Server |From Client |
|:---: |:---: |
|[Player Joined](#player-joined) |[Select Team](#select-team)|
|[Player Update](#player-update) |[Select Role](#select-role)|
|[Game Started](#game-started) |[Start Game](#start-game) |
|[Hint Provided](#hint-provided) |[Hint Sent](#hint-sent) |
|[Board Update](#board-update) |[Guess Sent](#guess-sent) |
|[Game Over](#game-over) | |
|[Illegal Action](#illegal-action)| |

---

Expand All @@ -85,7 +86,7 @@ POST /api/v1/games
}
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`name`|String: The username that the requesting user would like to use during the game|

##### Successful Response
Expand All @@ -101,7 +102,7 @@ HTTP/1.1 201 Created
}
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`invite_code`|String: A code which can be shared with other players. They will use this code to join the game.|
|`id` |Integer: The unique id for the player.|
|`name` |String: A confirmation that the requested name was indeed assigned to the player.|
Expand Down Expand Up @@ -138,7 +139,7 @@ POST /api/v1/games/:invite_code/players
}
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`:invite_code`|String: (Within URI) The invite code provided by the person inviting the requesting user to their existing game.|
|`name` |String: The username that the requesting user would like to use during the game|

Expand All @@ -154,7 +155,7 @@ HTTP/1.1 200 OK
}
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`id` |Integer: The unique id for the player.|
|`name` |String: A confirmation that the requested name was indeed assigned to the player.|
|`token`|String: A token unique to the current player, which can be used to identify them in future requests to the server.|
Expand Down Expand Up @@ -218,7 +219,7 @@ Request the Intel data for a game, allowing the player to see which cards belong
GET /api/v1/intel?token=<player_token>
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`token`|String: A valid token belonging to a Player with the Intel role.|

##### Successful Response
Expand All @@ -237,7 +238,7 @@ HTTP/1.1 200 OK
}
```
|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|description|
|:---: |:--- |
|:--- |:--- |
|`cards` |Array: An **ordered** collection of `card` objects which are part of the game. These cards go onto the board left-to-right, top-to-bottom.|
|`-->card.id` |Integer: The unique identifier for the card.|
|`-->card.word`|String: The word for the card.|
Expand Down Expand Up @@ -284,7 +285,7 @@ HTTP/1.1 401 Unauthorized

### Player Joined

This message is broadcast to the game channel whenever a player joins the game. It contains the name and ID of the player who joined, as well as a roster of all players currently in the game.
This message is broadcast to the game channel whenever a player joins the game. It contains the name, ID, team, and role of the player who joined, as well as a roster of all players currently in the game.

##### Payload

Expand All @@ -294,10 +295,14 @@ This message is broadcast to the game channel whenever a player joins the game.
data: {
id: 0,
name: "name",
isBlueTeam: true,
isIntel: true,
playerRoster: [
{
id: 0,
name: "name"
name: "name",
isBlueTeam: true,
isIntel: true,
},
...
]
Expand All @@ -311,15 +316,112 @@ This message is broadcast to the game channel whenever a player joins the game.
|`data` |Object: The data payload of the message.|
|`data.id` |Integer: The unique id of the player who joined.|
|`data.name` |String: The name of the player who joined.|
|`data.isBlueTeam` |Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.|
|`data.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.|
|`data.playerRoster`|Array: A collection of `player` objects for all players currently in the game, **ordered by** the time they joined the lobby.|
|`-->player.id` |Integer: The unique id of the given player.|
|`-->player.name` |String: The name of the given player.|
|`-->player.isBlueTeam`|Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.|
|`-->player.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.|

---

### Select Team

This message is sent from the game client to the server by any player before the game has started. The payload contains the team that the player would like to join.

##### Call

```js
cable.selectTeam({
team: "red"
})
```

|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|Description|
|:--- |:--- |
|`team`|String: The team the player would like to play on. "red" or "blue"|

---

### Select Role

This message is sent from the game client to the server by any player before the game has started. The payload contains the role that the player would like to play.

##### Call

```js
cable.selectRole({
role: "intel"
})
```

|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|Description|
|:--- |:--- |
|`role`|String: The role the player would like to have. "intel" or "spy"|

---

### Player Update

This message is broadcast to the game channel whenever a player makes a team or role selection. It contains the name, ID, team, and role of the player who changed, as well as a roster of all players currently in the game.

##### Payload

```js
{
type: "player-update",
data: {
id: 0,
name: "name",
isBlueTeam: true,
isIntel: true,
playerRoster: [
{
id: 0,
name: "name",
isBlueTeam: true,
isIntel: true,
},
...
]
}
}
```

|key&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|Description|
|:--- |:--- |
|`type` |String: The type of message being broadcast.|
|`data` |Object: The data payload of the message.|
|`data.id` |Integer: The unique id of the player who joined.|
|`data.name` |String: The name of the player who joined.|
|`data.isBlueTeam` |Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.|
|`data.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.|
|`data.playerRoster` |Array: A collection of `player` objects for all players currently in the game, **ordered by** the time they joined the lobby.|
|`-->player.id` |Integer: The unique id of the given player.|
|`-->player.name` |String: The name of the given player.|
|`-->player.isBlueTeam`|Boolean: `null` if the player has not been assigned a team, `true` if the player is on the blue team, `false` if they're on the red team.|
|`-->player.isIntel` |Boolean: `null` if the player has not been assigned a role, `true` if the player has the Intel role, `false` if they have the Spy role.|

---

### Start Game

This message is sent from the game client to the server by any player after all players have joined, while the game is still on the lobby screen.

##### Call

```js
cable.startGame()
```

No payload should be provided with this message.

---

### Game Started

This message is broadcast to the game channel once the final player has joined the lobby. It is broadcast _after_ the [player joined](#player-joined) message generated by that player.
This message is broadcast to the game channel after all players have joined the lobby and any player sends the [`start_game`](#start-game) message. It is broadcast _after_ the [player joined](#player-joined) message generated by that player.

##### Payload

Expand Down Expand Up @@ -515,6 +617,7 @@ This message is broadcast to all players after any illegal action is performed b
type: 'illegal-action',
data: {
error: "<descriptive message>",
category: "personal",
byPlayerId: 1
}
}
Expand All @@ -525,6 +628,7 @@ This message is broadcast to all players after any illegal action is performed b
|`type` |String: The type of message being broadcast.|
|`data` |Object: The data payload of the message.|
|`data.error` |String: The descriptive error message.|
|`data.category` |String: The category of error. `personal` for messages that should only be displayed to the affected player, `public` for messages that should be broadcast to the lobby, or `info` for messages that should only output to the browser console with `console.warn()`.|
|`data.byPlayerId`|Integer: The ID of the player who performed the illegal action.|

<details><summary>The potential illegal actions that are anticipated and caught are:</summary>
Expand All @@ -537,5 +641,7 @@ This message is broadcast to all players after any illegal action is performed b
- "\<player name\> attempted to submit an invalid hint"
- A Spy player submits a guess with a card ID not present in this game
- "\<player name\> attempted to submit a guess for a card not in this game"
- A Player attempts to select a role or team that is full.
- Various: see [`Player#err`](app/models/player.rb)

</details>
Loading