chore: run prettier
This commit is contained in:
parent
2a7c87605c
commit
a302992c6a
16 changed files with 397 additions and 265 deletions
|
@ -4,5 +4,6 @@
|
|||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"plugins": ["prettier-plugin-svelte"]
|
||||
}
|
||||
|
|
12
index.html
12
index.html
|
@ -3,11 +3,15 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Draw with friends">
|
||||
<meta name="description" content="Draw with friends" />
|
||||
<title>Doodly</title>
|
||||
<link rel="icon" href="/favicon.png">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
href="/apple-touch-icon.png"
|
||||
sizes="180x180"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"dev:server": "node --loader ts-node/esm src/server/main.ts",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write ."
|
||||
"format:check": "prettier --check . ./**/*.svelte",
|
||||
"format": "prettier --write . ./**/*.svelte"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.8.0",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
|
|
|
@ -6,6 +6,7 @@ specifiers:
|
|||
'@tsconfig/svelte': ^3.0.0
|
||||
bulma: ^0.9.4
|
||||
prettier: ^2.7.1
|
||||
prettier-plugin-svelte: ^2.7.0
|
||||
socket.io: ^4.5.1
|
||||
socket.io-client: ^4.5.1
|
||||
svelte: ^3.49.0
|
||||
|
@ -27,6 +28,7 @@ devDependencies:
|
|||
'@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.49.0+vite@3.0.6
|
||||
'@tsconfig/svelte': 3.0.0
|
||||
prettier: 2.7.1
|
||||
prettier-plugin-svelte: 2.7.0_o3ioganyptcsrh6x4hnxvjkpqi
|
||||
svelte: 3.49.0
|
||||
svelte-check: 2.8.0_svelte@3.49.0
|
||||
svelte-preprocess: 4.10.7_uslzfc62di2n2otc2tvfklnwji
|
||||
|
@ -2667,6 +2669,16 @@ packages:
|
|||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/prettier-plugin-svelte/2.7.0_o3ioganyptcsrh6x4hnxvjkpqi:
|
||||
resolution: {integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==}
|
||||
peerDependencies:
|
||||
prettier: ^1.16.4 || ^2.0.0
|
||||
svelte: ^3.2.0
|
||||
dependencies:
|
||||
prettier: 2.7.1
|
||||
svelte: 3.49.0
|
||||
dev: true
|
||||
|
||||
/prettier/2.7.1:
|
||||
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
<script lang="ts">
|
||||
import { io, type Socket } from "socket.io-client";
|
||||
import { onDestroy } from "svelte";
|
||||
import { io, type Socket } from 'socket.io-client';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
import WaitingLobby from "./client/WaitingLobby.svelte";
|
||||
import JoinPage from "./client/JoinPage.svelte";
|
||||
import type { ChatMessage, ClientToServerEvents, GameState, Player, ServerToClientEvents, GameOver as GameOverData } from "./lib/socket";
|
||||
import SelectWord from "./client/SelectWord.svelte";
|
||||
import Card from "./components/Card.svelte";
|
||||
import ChatWindow from "./client/ChatWindow.svelte";
|
||||
import GuessPage from "./client/GuessPage.svelte";
|
||||
import GameOver from "./client/GameOver.svelte";
|
||||
import WaitingLobby from './client/WaitingLobby.svelte';
|
||||
import JoinPage from './client/JoinPage.svelte';
|
||||
import type {
|
||||
ChatMessage,
|
||||
ClientToServerEvents,
|
||||
GameState,
|
||||
Player,
|
||||
ServerToClientEvents,
|
||||
GameOver as GameOverData,
|
||||
} from './lib/socket';
|
||||
import SelectWord from './client/SelectWord.svelte';
|
||||
import Card from './components/Card.svelte';
|
||||
import ChatWindow from './client/ChatWindow.svelte';
|
||||
import GuessPage from './client/GuessPage.svelte';
|
||||
import GameOver from './client/GameOver.svelte';
|
||||
|
||||
let socket: Socket<ServerToClientEvents, ClientToServerEvents> | null = null;
|
||||
let socket: Socket<ServerToClientEvents, ClientToServerEvents> | null =
|
||||
null;
|
||||
let connected = false;
|
||||
|
||||
let username = '';
|
||||
|
@ -31,7 +39,10 @@
|
|||
|
||||
function joinGame(uname: string, gameId: string) {
|
||||
players = [];
|
||||
socket = io(import.meta.env.GAMESERVER_URL || `http://${window.location.hostname}:3000/`);
|
||||
socket = io(
|
||||
import.meta.env.GAMESERVER_URL ||
|
||||
`http://${window.location.hostname}:3000/`
|
||||
);
|
||||
socket.on('connect', () => {
|
||||
socket.emit('joinGame', gameId, uname, (result) => {
|
||||
if (result.error === false) {
|
||||
|
@ -69,18 +80,18 @@
|
|||
});
|
||||
|
||||
socket.on('playerLeft', (username) => {
|
||||
players = players.filter(p => p.username !== username);
|
||||
players = players.filter((p) => p.username !== username);
|
||||
});
|
||||
|
||||
socket.on('becameHost', (newHost) => {
|
||||
if (newHost === username) {
|
||||
host = true;
|
||||
}
|
||||
players = players.map(player => {
|
||||
players = players.map((player) => {
|
||||
return {
|
||||
...player,
|
||||
isHost: player.username === newHost,
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -129,43 +140,51 @@
|
|||
|
||||
<main>
|
||||
{#if !connected}
|
||||
<JoinPage joinGame={joinGame} error={error} loading={socket !== null} on:dismiss-error={() => error = ''}></JoinPage>
|
||||
{:else}
|
||||
{#if gameState === 'waiting'}
|
||||
<WaitingLobby username={username} gameId={gameId} host={host} socket={socket} players={players}></WaitingLobby>
|
||||
<JoinPage
|
||||
{joinGame}
|
||||
{error}
|
||||
loading={socket !== null}
|
||||
on:dismiss-error={() => (error = '')}
|
||||
/>
|
||||
{:else if gameState === 'waiting'}
|
||||
<WaitingLobby {username} {gameId} {host} {socket} {players} />
|
||||
{:else if gameState === 'waitingForWord'}
|
||||
<Card>
|
||||
<p>
|
||||
Waiting for a word to be picked...
|
||||
</p>
|
||||
<p>Waiting for a word to be picked...</p>
|
||||
</Card>
|
||||
{:else if gameState === 'selectingWord'}
|
||||
<SelectWord words={wordOptions} on:pick-word={(event) => socket.emit('pickWord', event.detail)} />
|
||||
<SelectWord
|
||||
words={wordOptions}
|
||||
on:pick-word={(event) => socket.emit('pickWord', event.detail)}
|
||||
/>
|
||||
{:else if gameState === 'guessing'}
|
||||
<GuessPage on:guess={(e) => socket.emit('guessWord', e.detail, (correct) => {
|
||||
<GuessPage
|
||||
on:guess={(e) =>
|
||||
socket.emit('guessWord', e.detail, (correct) => {
|
||||
if (correct) {
|
||||
gameState = 'guessedCorrectly';
|
||||
}
|
||||
})} />
|
||||
})}
|
||||
/>
|
||||
<ChatWindow messages={chat} />
|
||||
{:else if gameState === 'guessedCorrectly'}
|
||||
<Card>
|
||||
<h1>Correct!</h1>
|
||||
<p>
|
||||
Waiting for the other players to finish...
|
||||
</p>
|
||||
<p>Waiting for the other players to finish...</p>
|
||||
</Card>
|
||||
<ChatWindow messages={chat} />
|
||||
{:else if gameState === 'drawing'}
|
||||
<Card>
|
||||
<h1>
|
||||
Start drawing!
|
||||
</h1>
|
||||
<h1>Start drawing!</h1>
|
||||
</Card>
|
||||
|
||||
<ChatWindow messages={chat} />
|
||||
{:else if gameState === 'gameOver'}
|
||||
<GameOver gameOver={gameOver} isHost={host} {username} on:play-again={startGame} />
|
||||
{/if}
|
||||
<GameOver
|
||||
{gameOver}
|
||||
isHost={host}
|
||||
{username}
|
||||
on:play-again={startGame}
|
||||
/>
|
||||
{/if}
|
||||
</main>
|
||||
|
|
|
@ -19,7 +19,13 @@ export interface GameOver {
|
|||
type Result<T> = { error: false; data: T } | { error: true; message: string };
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
joinGame: (id: string, username: string, callback: (result: Result<{id: string, isHost: boolean, players: Player[]}>) => void) => void;
|
||||
joinGame: (
|
||||
id: string,
|
||||
username: string,
|
||||
callback: (
|
||||
result: Result<{ id: string; isHost: boolean; players: Player[] }>
|
||||
) => void
|
||||
) => void;
|
||||
startGame: () => void;
|
||||
|
||||
pickWord: (word: string) => void;
|
||||
|
@ -35,7 +41,14 @@ export interface Player {
|
|||
isHost: boolean;
|
||||
}
|
||||
|
||||
export type GameState = 'waiting' | 'selectingWord' | 'waitingForWord' | 'drawing' | 'guessing' | 'guessedCorrectly' | 'gameOver';
|
||||
export type GameState =
|
||||
| 'waiting'
|
||||
| 'selectingWord'
|
||||
| 'waitingForWord'
|
||||
| 'drawing'
|
||||
| 'guessing'
|
||||
| 'guessedCorrectly'
|
||||
| 'gameOver';
|
||||
|
||||
export interface ChatMessage {
|
||||
author: string;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import type { Server, Socket } from "socket.io";
|
||||
import type { ClientToServerEvents, Player, ServerToClientEvents, SocketData } from "src/lib/socket";
|
||||
import { levenshteinDistance } from "../lib/util.js";
|
||||
import randomWords from "./words.js";
|
||||
import type { Server, Socket } from 'socket.io';
|
||||
import type {
|
||||
ClientToServerEvents,
|
||||
Player,
|
||||
ServerToClientEvents,
|
||||
SocketData,
|
||||
} from 'src/lib/socket';
|
||||
import { levenshteinDistance } from '../lib/util.js';
|
||||
import randomWords from './words.js';
|
||||
|
||||
export class Game {
|
||||
io: Server<ClientToServerEvents, ServerToClientEvents, {}, SocketData>;
|
||||
|
@ -14,13 +19,24 @@ export class Game {
|
|||
currentWord: string | null;
|
||||
successfulPlayers: [string, number][];
|
||||
|
||||
constructor(gameId: string, io: Server<ClientToServerEvents, ServerToClientEvents, {}, SocketData>) {
|
||||
constructor(
|
||||
gameId: string,
|
||||
io: Server<ClientToServerEvents, ServerToClientEvents, {}, SocketData>
|
||||
) {
|
||||
this.io = io;
|
||||
this.gameId = gameId;
|
||||
this.players = new Map();
|
||||
}
|
||||
|
||||
playerJoin(socket: Socket<ClientToServerEvents, ServerToClientEvents, {}, SocketData>, username: string): string | Player {
|
||||
playerJoin(
|
||||
socket: Socket<
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
{},
|
||||
SocketData
|
||||
>,
|
||||
username: string
|
||||
): string | Player {
|
||||
for (const player of this.players.values()) {
|
||||
if (player.username === username) {
|
||||
return 'username taken';
|
||||
|
@ -48,14 +64,22 @@ export class Game {
|
|||
* @param socket Socket that has just disconnected
|
||||
* @returns true if the game is empty, false otherwise
|
||||
*/
|
||||
playerLeft(socket: Socket<ClientToServerEvents, ServerToClientEvents, {}, SocketData>): boolean {
|
||||
playerLeft(
|
||||
socket: Socket<
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
{},
|
||||
SocketData
|
||||
>
|
||||
): boolean {
|
||||
const player = this.players.get(socket.id);
|
||||
this.players.delete(socket.id);
|
||||
if (this.players.size === 0) return true;
|
||||
if (player.isHost) {
|
||||
// we need to assign a new host for this room
|
||||
const playerIds = Array.from(this.players.keys());
|
||||
const newHostId = playerIds[Math.floor(Math.random() * playerIds.length)];
|
||||
const newHostId =
|
||||
playerIds[Math.floor(Math.random() * playerIds.length)];
|
||||
const newHost = this.players.get(newHostId);
|
||||
newHost.isHost = true;
|
||||
this.io.to(this.gameId).emit('becameHost', newHost.username);
|
||||
|
@ -70,7 +94,14 @@ export class Game {
|
|||
* Will pick a random player to choose a word, and then
|
||||
* broadcast the start to the other players.
|
||||
*/
|
||||
gameStart(socket: Socket<ClientToServerEvents, ServerToClientEvents, {}, SocketData>) {
|
||||
gameStart(
|
||||
socket: Socket<
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
{},
|
||||
SocketData
|
||||
>
|
||||
) {
|
||||
if (!this.players.get(socket.id).isHost) return;
|
||||
|
||||
const playerIds = Array.from(this.players.keys());
|
||||
|
@ -82,7 +113,15 @@ export class Game {
|
|||
this.io.to(player).emit('pickWord', words);
|
||||
}
|
||||
|
||||
pickWord(socket: Socket<ClientToServerEvents, ServerToClientEvents, {}, SocketData>, word: string) {
|
||||
pickWord(
|
||||
socket: Socket<
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
{},
|
||||
SocketData
|
||||
>,
|
||||
word: string
|
||||
) {
|
||||
if (socket.id !== this.currentPlayer) return;
|
||||
this.currentWord = word;
|
||||
this.io.to(this.gameId).except(socket.id).emit('startGuessing');
|
||||
|
@ -97,20 +136,52 @@ export class Game {
|
|||
* @param guess The word that the player guessed
|
||||
* @returns true if the word was correct, false otherwise
|
||||
*/
|
||||
guessWord(socket: Socket<ClientToServerEvents, ServerToClientEvents, {}, SocketData>, guess: string): boolean {
|
||||
if (!this.currentWord || this.successfulPlayers.find(([id, _]) => id === socket.id) !== undefined) return;
|
||||
guessWord(
|
||||
socket: Socket<
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
{},
|
||||
SocketData
|
||||
>,
|
||||
guess: string
|
||||
): boolean {
|
||||
if (
|
||||
!this.currentWord ||
|
||||
this.successfulPlayers.find(([id, _]) => id === socket.id) !==
|
||||
undefined
|
||||
)
|
||||
return;
|
||||
|
||||
const player = this.players.get(socket.id);
|
||||
|
||||
if (guess.toLocaleLowerCase() === this.currentWord.toLocaleLowerCase()) {
|
||||
if (
|
||||
guess.toLocaleLowerCase() === this.currentWord.toLocaleLowerCase()
|
||||
) {
|
||||
// Correct answer!
|
||||
this.io.to(this.gameId).except(socket.id).emit('chatMessage', 'game', `${player.username} guessed the correct word`);
|
||||
this.successfulPlayers.push([socket.id, new Date().getTime() - this.startTime]);
|
||||
this.io
|
||||
.to(this.gameId)
|
||||
.except(socket.id)
|
||||
.emit(
|
||||
'chatMessage',
|
||||
'game',
|
||||
`${player.username} guessed the correct word`
|
||||
);
|
||||
this.successfulPlayers.push([
|
||||
socket.id,
|
||||
new Date().getTime() - this.startTime,
|
||||
]);
|
||||
|
||||
return true;
|
||||
} else if (levenshteinDistance(guess.toLocaleLowerCase(), this.currentWord.toLocaleLowerCase()) <= 2) {
|
||||
} else if (
|
||||
levenshteinDistance(
|
||||
guess.toLocaleLowerCase(),
|
||||
this.currentWord.toLocaleLowerCase()
|
||||
) <= 2
|
||||
) {
|
||||
// close guess
|
||||
this.io.to(socket.id).emit('chatMessage', 'game', `${guess} is close!`);
|
||||
this.io
|
||||
.to(socket.id)
|
||||
.emit('chatMessage', 'game', `${guess} is close!`);
|
||||
}
|
||||
|
||||
// Broadcast incorrect guesses to all players
|
||||
|
@ -138,7 +209,10 @@ export class Game {
|
|||
|
||||
private isGameOver(): boolean {
|
||||
for (const id of this.players.keys()) {
|
||||
if (id !== this.currentPlayer && this.successfulPlayers.find(([i, _]) => id === i) === undefined) {
|
||||
if (
|
||||
id !== this.currentPlayer &&
|
||||
this.successfulPlayers.find(([i, _]) => id === i) === undefined
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,9 @@ io.on('connection', (socket) => {
|
|||
io.to(gameId).emit('playerJoined', username);
|
||||
|
||||
socket.data.gameId = gameId;
|
||||
socket.rooms.forEach(room => room !== socket.id && socket.leave(room));
|
||||
socket.rooms.forEach(
|
||||
(room) => room !== socket.id && socket.leave(room)
|
||||
);
|
||||
socket.join(gameId);
|
||||
|
||||
const players = Array.from(room.players.values());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import sveltePreprocess from 'svelte-preprocess'
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
|
||||
export default {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: sveltePreprocess()
|
||||
}
|
||||
preprocess: sveltePreprocess(),
|
||||
};
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
"checkJs": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"include": [
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.js",
|
||||
"src/**/*.svelte"
|
||||
],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import { defineConfig } from 'vite';
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
|
@ -18,7 +18,8 @@ export default defineConfig({
|
|||
src: 'favicon.png',
|
||||
sizes: '256x256',
|
||||
type: 'image/png',
|
||||
},{
|
||||
},
|
||||
{
|
||||
src: 'favicon-192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
|
|
Loading…
Reference in a new issue