Skip to content

Commit

Permalink
docs: Connection sequence design (#19)
Browse files Browse the repository at this point in the history
Refs #17
  • Loading branch information
elementbound authored Nov 24, 2024
1 parent 83acc3d commit 6a6dbab
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Prettier messes up list indents
# See issue: https://github.com/prettier/prettier/issues/5019
# See ( unreleased ) fix: https://github.com/prettier/prettier/pull/15526
**/*.md
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ Dumb Adventures in Dumber Dungeons, right in your browser!

## Docs

Docs are served using [mkdocs]. From the repo root, install it and serve the site:
Docs are served using [mkdocs] and the [mkdocs_puml] addon. From the repo root,
install it and serve the site:

```sh
pip install mkdocs
pip install mkdocs mkdocs_puml
mkdocs serve
```

The site will be accessible at <http://127.0.0.1:8006/>.

[mkdocs]: https://www.mkdocs.org/
[mkdocs_puml]: https://mikhailkravets.github.io/mkdocs_puml/
190 changes: 190 additions & 0 deletions docs/devdocs/17-connection-sequence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Connection Sequence Design

As described in [#17].

## Overview

The feature can be broken down to the following major points:

1. Create a session
1. Participants join the session
1. Participant setup
1. Session start

## Session management

```puml
actor "Host Participant" as HP
boundary "Main Screen" as MS
entity "Server" as S
HP -> MS: Open in browser
MS -> S: Request game session
S -> MS: Respond with game session
MS -> HP: Redirect to session page
MS -> HP: Display instructions to join
```

When the host participant opens the game on their main screen, the application
creates a new game session and displays instructions on how to join.

---

Sessions are objects that have an ID and a list of participants that are
currently in the session.

Session IDs are randomly generated, to ensure all IDs are unique. This property
can be useful for scaling horizontally as well - instances can generate IDs for
a shared storage independently of each other.

Participants in this case represent the user *as part of the session*.
Participants do not exist outside of game sessions.

Participants also have a randomly generated ID, and in addition, a name and a
readiness flag.

For convenience, participants may have globally unique IDs, but they must
always belong to a game session.

### Displays

Aside from participants, displays may also connect to game sessions, e.g. the main
screen or TV the host is using. Displays don't interact with the game, they
just present its state to users.

Displays are participants themselves, marked with an `is_display` flag.

## Joining the session

When a participant opens the session - i.e. opens its link in a browser -, they
connect to the game through WebSocket. This will be used to send actions to the
server, and to receive state updates from it.

Initially, the server creates a Participant object, and sends its data to the
browser. The browser can then use this data to authenticate as the participant
itself.

On the server, the specified Session is validated ( i.e. does it exist? ), and
if so, a Participant is created and added to the list of players in the Session.
This event is broadcast to the Participants already in the session.

### Data storage

For starters, we can keep Session and Participant objects in memory, in a
simple dictionary and do lookups based on ID.

Later we can move on to keeping these objects in an actual database, so game
data is not tied to a single running instance, enabling horizontal scaling.

### WebSocket message routing

On the backend, [nest.js] has built-in support for WebSockets, through a
[socket.io] adapter - see [nest.js WS docs].

On the frontend, [socket.io] provides a high-level API similar to node's
EventEmitter.

## Participant setup

After successfully joining, participants are presented with the player setup
screen. For the scope of this epic, participants can edit their own names and
toggle their readiness.

```puml
@startsalt
{^"Participant setup"
Name | "Participant name"
[ Ready ]
}
@endsalt
```

During this phase, on the main screen, all Participants and their state is
displayed next to the join instructions. This is updated in real-time, as
participants join or update their settings.

```puml
@startsalt
{
{^"Join"
<img:https://upload.wikimedia.org/wikipedia/commons/thumb/d/d0/QR_code_for_mobile_English_Wikipedia.svg/296px-QR_code_for_mobile_English_Wikipedia.svg.png?20101130144655>
https://dumber.dungeons/game/XXXX
} |
{^"Participants"
Name | Ready?
Foo | <&circle-check>
Bar | <&circle-x>
Quix | <&circle-check>
Baz | <&circle-x>
}
}
@endsalt
```

Participant actions ( changing name or readiness ) are submitted through
WebSocket.

### Authentication

* WebSocket object
* Participants can be associated with a specific WebSocket connection object
* pros: Simple?
* cons: Not scalable, Potentially hacky
* Authentication tokens
* Aside from IDs, Participant objects also get an Auth token
* This auth token is only handed out to the Participant actor during creation
* The auth token can be used to perform actions on the Participant object
* pros: Relatively simple, Scalable
* cons: Extra network traffic

**Verdict:** Participants are associated with WebSocket connections. An initial
auth token is sent to the participant, but is not needed to be sent with every
message.

Authentication tokens are generated and set as http-only cookies on join. These
can be used on the server to determine if it's an existing participant
reconnecting to a game or a new one.

Note that reconnecting is out of scope for now, authentication tokens will be
useful later in development.

## Session start

During every readiness change, the server checks if all Participants are ready in
the affected session. If so, it broadcasts a session start event.

Clients lock the Participant settings and display a message that the session has
started.

## Data model

```puml
@startyaml
session:
id: 81DCIQL9NqPW
is_in_progress: false
participants:
- name: Foo
id: 841GIhHUw-iK
is_ready: false
is_display: false
auth_token: WMAou-ECTaxyl5IbLlIxLp8lnX_yyQ9Z
- name: Bar
id: teeDUHGoUTwy
is_ready: true
is_display: false
auth_token: _pQUp8F34t_HBjmp1jI6sRbOLSpv7AcA
- name: Foo's TV
id: acyLzh_Lab-F
is_ready: false
is_display: true
auth_token: Gu6A4KRqcIQXi8t5Xuyh6JP2YzqPFLSx
@endyaml
```

[#17]: https://github.com/foxssake/dumber-dungeons/issues/17
[nest.js]: https://nestjs.com/
[socket.io]: https://socket.io/
[nest.js WS docs]: https://docs.nestjs.com/websockets/gateways
8 changes: 8 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
site_name: 'Dumber Dungeons'
dev_addr: 'localhost:8006'
theme: readthedocs
plugins:
- plantuml:
puml_url: https://www.plantuml.com/plantuml/
num_workers: 8
theme:
enabled: false
interaction:
enabled: false

0 comments on commit 6a6dbab

Please sign in to comment.