Skip to content
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

WIP Architecture #60

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
108 changes: 108 additions & 0 deletions doc/architecture/server-crc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# server architecture

## file descriptions

### `types/**/*.d.ts`

| Responsibilities | Collaborators |
|------------------|---------------------|
| - Define types | - `lobby.ts` |
| | - `socketServer.ts` |
| | - `events/*.ts` |
| | - `machines/*.ts` |




### `data/*.json`

| Responsibilities | Collaborators |
|---------------------|---------------|
| - Store static data | - `round.ts` |



### `events/serverbound.ts`


| Responsibilities | Collaborators |
|-------------------------------------|---------------------|
| - websocket server event listeners | - `socketServer.ts` |




### `events/clientbound.ts`


| Responsibilities | Collaborators |
|---------------------------------------------------|---------------------------|
| - server websocket events to be used in listeners | - `socketServer.ts` |
| | - `events/serverbound.ts` |




### `machines/lobby.ts`


| Responsibilities | Collaborators |
|-----------------------------|---------------------|
| - adds players to game | - `models/lobby.ts` |
| - removes players from game | |

### `machines/round.ts`


| Responsibilities | Collaborators |
|------------------------------------------|---------------------|
| - sets the question | - `models/round.ts` |
| - receives responses to the question | |
| - determines correctness of responses | |
| - determines when to end the round | |
| - determines points players win or loose | |


### `index.ts`


| Responsibilities | Collaborators |
|-------------------------------------------|---------------------|
| - instantiates the socket and HTTP server | - `socketServer.ts` |


### `models/lobby.ts`


| Responsibilities | Collaborators |
|--------------------------------------------------------------------------------|-----------------------|
| - interfaces between the client and serverbound events and lobby state machine | - `machines/lobby.ts` |
| | - `socketServer.ts` |
| | - `events/*.ts` |



### `models/round.ts`



| Responsibilities | Collaborators |
|--------------------------------------------------------------------------------|-----------------------|
| - interfaces between the client and serverbound events and round state machine | - `machines/round.ts` |
| | - `socketServer.ts` |
| | - `events/*.ts` |




### `socketServer.ts`

defines socket.io server-level events and sets up a few on initialisation



| Responsibilities | Collaborators |
|-------------------------------|-----------------|
| - instatiates models | - `index.ts` |
| - communicates between models | - `models/*.ts` |
| - emits events to client | - `events/*.ts` |
73 changes: 73 additions & 0 deletions doc/architecture/server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# server architecture

## file descriptions

### `types/**/*.d.ts`

type definitions

#### users

- `lobby.ts`
- `socketServer.ts`
- `events/*.ts`
- `machines/*.ts`

### `data/*.json`

static data

#### users

- `round.ts`

### `events/*.ts`

socket.io event listener/message method (`on`/`event`) argument builders

#### users

- `socketServer.ts`
- other files of the same category

### `machines/*.ts`

XState state machines

#### users

- `lobby.ts` + `round.ts`

### `index.ts`

app initialisation

### `lobby.ts` + `round.ts`

interface between socket.io events (`events/*.ts`) and XState state machines
(`machines/*.ts`). In Rails conceptual framework, these are like models

#### users

- `socketServer.ts`
- `events/*.ts`
- `machines/*.ts`

### `socketServer.ts`

defines socket.io server-level events and sets up a few on initialisation

#### users

- `index.ts`
- `events/*.ts`
- `lobby.ts` + `round.ts`

## flow (incomplete)

1. `index.ts` creates a HTTP server and an instance of our `SocketServer` class,
and starts the HTTP server
2. the `SockerServer` instance creates an instance of our `Lobby` class and an
instance of the socket.io `Server` class, then listens for new socket connections
3. when new sockets connect, the `SockerServer` instance adds listeners
4. the lobby starts up the lobby machine
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"lint:ts:fix": "bunx @biomejs/biome check --apply .",
"lint:css": "bunx stylelint \"client/assets/styles/*.css\"",
"lint:css:fix": "bunx stylelint \"client/assets/styles/*.css\" --fix",
"start": "bun run compile && bun server/index.ts --watch",
"start": "bun run compile && bun server/index.ts",
"start:watch": "bun run compile && bun --watch server/index.ts",
"typecheck": "bun run tsc",
"test:unit": "bun test server",
"test:integration:cli": "bunx playwright test",
Expand Down
4 changes: 2 additions & 2 deletions script/server
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
echo "==> Starting the server..."
echo "==> Starting the server in watch mode..."

bun run start
bun run start:watch
22 changes: 17 additions & 5 deletions server/events/clientbound.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import type { Player, Question } from "../@types/models";
import type { Lobby } from "../lobby";
import type { Lobby } from "../models/lobby";

const clientboundEvents = {
getPlayers: (lobby: Lobby): [string, { players: Player["name"][] }] => {
getPlayers: (
lobby: Lobby,
): ClientboundSocketServerEvent<{ players: Player["name"][] }> => {
return ["players:get", { players: lobby.playerNames() }];
},
getQuestion: (question: Question): [string, { question: Question }] => {
getQuestion: (
question: Question,
): ClientboundSocketServerEvent<{ question: Question }> => {
return ["question:get", { question }];
},
setPlayer: (player: Player): [string, { player: Player }] => {
setPlayer: (
player: Player,
): ClientboundSocketServerEvent<{ player: Player }> => {
return ["player:set", { player }];
},
showStartButton: (): string => {
showStartButton: (): ClientboundSocketServerEvent => {
return "game:startable";
},
};

type Event = "game:startable" | "player:set" | "players:get" | "question:get";

type ClientboundSocketServerEvent<T = void> = T extends Record<string, unknown>
? [Event, T]
: Event;

export { clientboundEvents };
85 changes: 85 additions & 0 deletions server/events/serverbound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import type { Server, Socket } from "socket.io";

import { Colour, Player } from "../@types/models";
import type { Lobby } from "../models/lobby";
import type { Round } from "../models/round";
import type { SocketServer } from "../socketServer";
import { clientboundEvents } from "./clientbound";

const serverboundEvents = {
disconnect: (
lobby: Lobby,
socket: Socket, // this can just be the ID
server: Server,
): ServerboundSocketServerEvent<"disconnect"> => {
return [
"disconnect",
() => {
console.info(`disconnected: ${socket.id}`);
lobby.removePlayer(socket.id);
server.emit(...clientboundEvents.getPlayers(lobby));
},
];
},
postAnswers(
socket: Socket, // this can just be the ID
round?: Round,
): ServerboundSocketServerEvent<"answers:post"> {
return [
"answers:post",
(data: { colours: Colour[] }) =>
round?.addAnswer({ colours: data.colours, socketId: socket.id }),
];
},
postPlayers: (
lobby: Lobby,
socket: Socket,
server: Server,
): ServerboundSocketServerEvent<"players:post"> => {
return [
"players:post",
(data: { name: Player["name"] }) => {
const player = lobby.addPlayer(data.name, socket.id);
socket.emit(...clientboundEvents.setPlayer(player));
server.emit(...clientboundEvents.getPlayers(lobby));
},
];
},
// this one needs to use the new type format
startRound: (
server: SocketServer,
): ServerboundSocketServerEvent<"round:start"> => {
return [
"round:start",
() => {
server.onRoundStarted();
},
];
},
};

type Event = "answers:post" | "disconnect" | "players:post" | "round:start";

// type Payload = keyof Payloads;

interface Payloads {
colours: Colour[];
name: Player["name"];
socketId: Socket["id"];
}

type ServerboundSocketServerEvent<T extends Event> = T extends
| "disconnect"
| "round:start"
? [Event, () => void]
: // data: Payloads doesn't seem right - maybe Payloads[T] (but that doesn't work)?
[Event, (data: Payloads) => void];

// // Biome error - we could possibly ignore it
// type ServerboundSocketServerEvent<T extends Payload> =
// T extends void
// ? ["disconnect" | "round:start", () => void]
// : // data: Payloads doesn't seem right - maybe Payloads[T] (but that doesn't work)?
// [Event, (data: Payloads) => void];

export { serverboundEvents };
58 changes: 0 additions & 58 deletions server/events/severbound.ts

This file was deleted.

4 changes: 2 additions & 2 deletions server/machines/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const context = {

type Context = typeof context;

type Events =
type Event =
| {
player: Player;
type: "playerJoins";
Expand Down Expand Up @@ -39,7 +39,7 @@ const lobbyMachine = createMachine(
predictableActionArguments: true,
schema: {
context: {} as Context,
events: {} as Events,
events: {} as Event,
},
states: {
empty: {
Expand Down
Loading