chore: run prettier

This commit is contained in:
Ashhhleyyy 2022-08-13 16:17:26 +01:00
parent 2a7c87605c
commit a302992c6a
Signed by: ash
GPG key ID: 83B789081A0878FB
16 changed files with 397 additions and 265 deletions

View file

@ -4,5 +4,6 @@
"tabWidth": 4,
"useTabs": false,
"trailingComma": "es5",
"singleQuote": true
"singleQuote": true,
"plugins": ["prettier-plugin-svelte"]
}

View file

@ -1,3 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode"]
"recommendations": ["svelte.svelte-vscode"]
}

View file

@ -1,16 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View file

@ -1,34 +1,35 @@
{
"name": "doodly",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "vite build",
"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 ."
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tsconfig/svelte": "^3.0.0",
"prettier": "^2.7.1",
"svelte": "^3.49.0",
"svelte-check": "^2.8.0",
"svelte-preprocess": "^4.10.7",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^3.0.6",
"vite-plugin-pwa": "^0.12.3"
},
"dependencies": {
"@fontsource/noto-sans": "^4.5.11",
"bulma": "^0.9.4",
"socket.io": "^4.5.1",
"socket.io-client": "^4.5.1"
}
"name": "doodly",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.json",
"dev:server": "node --loader ts-node/esm src/server/main.ts",
"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",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
"typescript": "^4.6.4",
"vite": "^3.0.6",
"vite-plugin-pwa": "^0.12.3"
},
"dependencies": {
"@fontsource/noto-sans": "^4.5.11",
"bulma": "^0.9.4",
"socket.io": "^4.5.1",
"socket.io-client": "^4.5.1"
}
}

View file

@ -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'}

View file

@ -1,171 +1,190 @@
<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 connected = false;
let socket: Socket<ServerToClientEvents, ClientToServerEvents> | null =
null;
let connected = false;
let username = '';
let gameId = '';
let username = '';
let gameId = '';
let error = '';
let error = '';
let host = false;
let host = false;
let players: Player[] = [];
let players: Player[] = [];
let gameState: GameState = 'waiting';
let wordOptions: string[] = null;
let gameState: GameState = 'waiting';
let wordOptions: string[] = null;
let chat: ChatMessage[] = [];
let gameOver: GameOverData = null;
let chat: ChatMessage[] = [];
let gameOver: GameOverData = null;
function joinGame(uname: string, gameId: string) {
players = [];
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) {
const { id, isHost, players: p } = result.data;
players = p;
host = isHost;
connected = true;
username = uname;
gameState = 'waiting';
chat = [];
gameOver = null;
} else {
console.log('failed to join', result.message);
socket.disconnect();
socket = null;
connected = false;
error = result.message;
}
function joinGame(uname: string, gameId: string) {
players = [];
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) {
const { id, isHost, players: p } = result.data;
players = p;
host = isHost;
connected = true;
username = uname;
gameState = 'waiting';
chat = [];
gameOver = null;
} else {
console.log('failed to join', result.message);
socket.disconnect();
socket = null;
connected = false;
error = result.message;
}
});
});
});
socket.on('disconnect', (reason) => {
connected = false;
error = 'lost connection: ' + reason;
socket.disconnect();
socket = null;
});
socket.on('disconnect', (reason) => {
connected = false;
error = 'lost connection: ' + reason;
socket.disconnect();
socket = null;
});
socket.on('playerJoined', (username) => {
players.push({
username,
isHost: false,
});
players = players;
});
socket.on('playerJoined', (username) => {
players.push({
username,
isHost: false,
});
players = players;
});
socket.on('playerLeft', (username) => {
players = players.filter(p => p.username !== username);
});
socket.on('playerLeft', (username) => {
players = players.filter((p) => p.username !== username);
});
socket.on('becameHost', (newHost) => {
if (newHost === username) {
host = true;
}
players = players.map(player => {
return {
...player,
isHost: player.username === newHost,
}
});
});
socket.on('becameHost', (newHost) => {
if (newHost === username) {
host = true;
}
players = players.map((player) => {
return {
...player,
isHost: player.username === newHost,
};
});
});
socket.on('waitForWordPick', () => {
gameState = 'waitingForWord';
});
socket.on('waitForWordPick', () => {
gameState = 'waitingForWord';
});
socket.on('pickWord', (words) => {
console.log('pickWord', gameState, words);
gameState = 'selectingWord';
wordOptions = words;
});
socket.on('pickWord', (words) => {
console.log('pickWord', gameState, words);
gameState = 'selectingWord';
wordOptions = words;
});
socket.on('startDrawing', () => {
gameState = 'drawing';
});
socket.on('startDrawing', () => {
gameState = 'drawing';
});
socket.on('startGuessing', () => {
gameState = 'guessing';
});
socket.on('startGuessing', () => {
gameState = 'guessing';
});
socket.on('chatMessage', (author, message) => {
chat.reverse();
chat.push({ author, message });
chat.reverse();
chat = chat;
});
socket.on('chatMessage', (author, message) => {
chat.reverse();
chat.push({ author, message });
chat.reverse();
chat = chat;
});
socket.on('gameOver', (result) => {
gameState = 'gameOver';
gameOver = result;
});
}
onDestroy(() => {
if (socket) {
socket.disconnect();
socket.on('gameOver', (result) => {
gameState = 'gameOver';
gameOver = result;
});
}
});
function startGame() {
socket.emit('startGame');
chat = [];
}
onDestroy(() => {
if (socket) {
socket.disconnect();
}
});
function startGame() {
socket.emit('startGame');
chat = [];
}
</script>
<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>
{#if !connected}
<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>
</Card>
<Card>
<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) => {
if (correct) {
gameState = 'guessedCorrectly';
}
})} />
<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>
<h1>Correct!</h1>
<p>Waiting for the other players to finish...</p>
</Card>
<ChatWindow messages={chat} />
{:else if gameState === 'drawing'}
<Card>
<h1>
Start drawing!
</h1>
</Card>
<Card>
<h1>Start drawing!</h1>
</Card>
<ChatWindow messages={chat} />
<ChatWindow messages={chat} />
{:else if gameState === 'gameOver'}
<GameOver gameOver={gameOver} isHost={host} {username} on:play-again={startGame} />
<GameOver
{gameOver}
isHost={host}
{username}
on:play-again={startGame}
/>
{/if}
{/if}
</main>

View file

@ -1,13 +1,13 @@
body {
font-family: 'Noto Sans', sans-serif;
/* background-color: #212;
font-family: 'Noto Sans', sans-serif;
/* background-color: #212;
color: #ded;
box-sizing: border-box; */
}
* {
box-sizing: inherit;
box-sizing: inherit;
}
/* input, button {

View file

@ -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;

View file

@ -4,7 +4,7 @@ import '@fontsource/noto-sans';
import 'bulma/css/bulma.css';
const app = new App({
target: document.getElementById('app'),
target: document.getElementById('app'),
});
export default app;

View file

@ -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;
}
}

View file

@ -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());

View file

@ -1,4 +1,4 @@
import words from '../assets/wordlist.json' assert {type: 'json'};
import words from '../assets/wordlist.json' assert { type: 'json' };
export default function randomWords(count = 3): string[] {
const randomWords: string[] = [];

View file

@ -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()
}
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: sveltePreprocess(),
};

View file

@ -1,21 +1,26 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"references": [{ "path": "./tsconfig.node.json" }]
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable checkJs if you'd like to use dynamic types in JS.
* Note that setting allowJs false does not prevent the use
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": [
"src/**/*.d.ts",
"src/**/*.ts",
"src/**/*.js",
"src/**/*.svelte"
],
"references": [{ "path": "./tsconfig.node.json" }]
}

View file

@ -1,8 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node"
},
"include": ["vite.config.ts"]
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node"
},
"include": ["vite.config.ts"]
}

View file

@ -1,30 +1,31 @@
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/
export default defineConfig({
plugins: [
svelte(),
VitePWA({
includeAssets: ['favicon.png', 'apple-touch-icon.png'],
manifest: {
name: 'Doodly',
short_name: 'Doodly',
description: 'Drawing game with friends',
theme_color: '#ffffff',
icons: [
{
src: 'favicon.png',
sizes: '256x256',
type: 'image/png',
},{
src: 'favicon-192.png',
sizes: '192x192',
type: 'image/png',
},
],
},
}),
],
plugins: [
svelte(),
VitePWA({
includeAssets: ['favicon.png', 'apple-touch-icon.png'],
manifest: {
name: 'Doodly',
short_name: 'Doodly',
description: 'Drawing game with friends',
theme_color: '#ffffff',
icons: [
{
src: 'favicon.png',
sizes: '256x256',
type: 'image/png',
},
{
src: 'favicon-192.png',
sizes: '192x192',
type: 'image/png',
},
],
},
}),
],
});