mirror of
https://github.com/danbulant/slightlyComplicatedTicTacToe
synced 2026-05-19 04:08:52 +00:00
initial commit
This commit is contained in:
parent
27c1df79d2
commit
7246ddea7b
26 changed files with 1946 additions and 0 deletions
12
.editorconfig
Normal file
12
.editorconfig
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
||||
|
|
@ -7,3 +7,6 @@ A multiplayer fight between dices.
|
|||
### Audio
|
||||
|
||||
reflectable【音楽素材MusMus】- Music by MusMus <https://musmus.main.jp/>
|
||||
<https://freesound.org/people/soundnimja/sounds/173326/>
|
||||
<https://freesound.org/people/LittleRobotSoundFactory/sounds/270325/>
|
||||
<https://www.dafont.com/squarefont.font>
|
||||
|
|
|
|||
BIN
assets/Square.ttf
Normal file
BIN
assets/Square.ttf
Normal file
Binary file not shown.
BIN
assets/Squareo.ttf
Normal file
BIN
assets/Squareo.ttf
Normal file
Binary file not shown.
BIN
assets/fall.wav
Normal file
BIN
assets/fall.wav
Normal file
Binary file not shown.
BIN
assets/jump.wav
Normal file
BIN
assets/jump.wav
Normal file
Binary file not shown.
8
client/.gitignore
vendored
Normal file
8
client/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
1
client/.npmrc
Normal file
1
client/.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
engine-strict=true
|
||||
13
client/.prettierignore
Normal file
13
client/.prettierignore
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
6
client/.prettierrc
Normal file
6
client/.prettierrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
}
|
||||
38
client/README.md
Normal file
38
client/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
28
client/package.json
Normal file
28
client/package.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "client",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"package": "svelte-kit package",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check --plugin-search-dir=. .",
|
||||
"format": "prettier --write --plugin-search-dir=. ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "next",
|
||||
"@sveltejs/kit": "next",
|
||||
"prettier": "^2.6.2",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
1342
client/pnpm-lock.yaml
Normal file
1342
client/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
11
client/src/app.d.ts
vendored
Normal file
11
client/src/app.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
// interface Locals {}
|
||||
// interface Platform {}
|
||||
// interface Session {}
|
||||
// interface Stuff {}
|
||||
}
|
||||
12
client/src/app.html
Normal file
12
client/src/app.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<div>%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
230
client/src/lib/Websocket.ts
Normal file
230
client/src/lib/Websocket.ts
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
import { writable, type Writable } from "svelte/store";
|
||||
|
||||
class FastEvent extends Event {
|
||||
data: any;
|
||||
constructor(name: string, data: any) {
|
||||
super(name);
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
const hosts: { urls: string, credential?: string, username?: string }[] = ("stun.ipfire.org:3478\n" +
|
||||
"stun.rolmail.net:3478\n" +
|
||||
"stun.steinbeis-smi.de:3478\n" +
|
||||
"stun.marcelproust.it:3478\n" +
|
||||
"stun3.3cx.com:3478\n" +
|
||||
"stun.voipraider.com:3478\n" +
|
||||
"stun.kore.com:3478\n" +
|
||||
"stun.voipstunt.com:3478\n" +
|
||||
"stun.fairytel.at:3478\n" +
|
||||
"stun.h4v.eu:3478\n" +
|
||||
"stun.peethultra.be:3478\n" +
|
||||
"stun.ortopediacoam.it:3478\n" +
|
||||
"stun.infra.net:3478\n" +
|
||||
"stun.vavadating.com:3478\n" +
|
||||
"stun.mixvoip.com:3478\n" +
|
||||
"stun.tele2.net:3478\n" +
|
||||
"stun2.3cx.com:3478\n" +
|
||||
"stun.myhowto.org:3478\n" +
|
||||
"stun.cellmail.com:3478\n" +
|
||||
"stun.poetamatusel.org:3478\n" +
|
||||
"stun.textz.com:3478\n" +
|
||||
"stun.romancecompass.com:3478\n" +
|
||||
"stun.ixc.ua:3478\n" +
|
||||
"stun.actionvoip.com:3478\n" +
|
||||
"stun.bethesda.net:3478\n" +
|
||||
"stun.parcodeinebrodi.it:3478\n" +
|
||||
"stun.jay.net:3478\n" +
|
||||
"stun.demos.ru:3478\n" +
|
||||
"stun.cloopen.com:3478\n" +
|
||||
"stun.crimeastar.net:3478\n" +
|
||||
"stun.vivox.com:3478\n" +
|
||||
"stun.openjobs.hu:3478\n" +
|
||||
"stun.kaznpu.kz:3478\n" +
|
||||
"stun.linphone.org:3478\n" +
|
||||
"stun.l.google.com:19302\n" +
|
||||
"stun.sonetel.net:3478").split("\n").map(t => ({ urls: "stun:" + t }));
|
||||
|
||||
hosts.push({
|
||||
urls: 'turn:relay.backups.cz',
|
||||
credential: 'webrtc',
|
||||
username: 'webrtc'
|
||||
},
|
||||
{
|
||||
urls: 'turn:relay.backups.cz?transport=tcp',
|
||||
credential: 'webrtc',
|
||||
username: 'webrtc'
|
||||
});
|
||||
|
||||
class ConnectedClient extends EventTarget {
|
||||
conn: RTCPeerConnection;
|
||||
sendChannel: RTCDataChannel;
|
||||
candidates: any[] = [];
|
||||
state: RTCDataChannelState | null = null;
|
||||
|
||||
constructor(public ws: WebsocketConnection, public name: string) {
|
||||
super();
|
||||
// @ts-ignore Initialized in the next function call
|
||||
this.conn = null;
|
||||
// @ts-ignore
|
||||
this.sendChannel = null;
|
||||
this.initializeConnection();
|
||||
}
|
||||
|
||||
initializeConnection() {
|
||||
console.log("Initializing connection");
|
||||
this.conn = new RTCPeerConnection({
|
||||
iceServers: hosts
|
||||
});
|
||||
|
||||
this.conn.onicecandidate = e => {
|
||||
console.log(e);
|
||||
if (!e.candidate) return;
|
||||
this.candidates.push(e.candidate);
|
||||
this.ws.send(JSON.stringify({ t: "cand", target: this.name, d: e.candidate }));
|
||||
};
|
||||
this.conn.onicecandidateerror = (e) => console.error(e);
|
||||
this.conn.ondatachannel = e => {
|
||||
this.sendChannel = e.channel;
|
||||
let timer: any;
|
||||
this.sendChannel.onclose = (e) => {
|
||||
clearInterval(timer);
|
||||
this.statusChanged();
|
||||
}
|
||||
this.sendChannel.onopen = (e) => {
|
||||
timer = setInterval(() => {
|
||||
this.send({ t: "p", d: Date.now() });
|
||||
}, 300);
|
||||
this.statusChanged();
|
||||
}
|
||||
this.statusChanged();
|
||||
this.sendChannel.onmessage = (e) => {
|
||||
const msg = JSON.parse(e.data);
|
||||
switch (msg.t) {
|
||||
default:
|
||||
console.log("MSG", msg);
|
||||
this.dispatchEvent(new FastEvent(msg.t, msg.d));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
send(data: any) {
|
||||
this.sendChannel.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
statusChanged() {
|
||||
if (this.sendChannel) {
|
||||
if (this.state !== this.sendChannel.readyState) {
|
||||
if (this.state === "open" && ["closing", "closed"].includes(this.sendChannel.readyState)) {
|
||||
this.initializeConnection();
|
||||
}
|
||||
this.state = this.sendChannel.readyState;
|
||||
console.log("state", this.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WebsocketConnection extends EventTarget {
|
||||
ws: WebSocket;
|
||||
fast: Map<string, ConnectedClient> = new Map();
|
||||
roomName: string | null = null;
|
||||
roomId: string | null = null;
|
||||
|
||||
constructor(public name: string) {
|
||||
super();
|
||||
// @ts-ignore Initialized in the next function call
|
||||
this.ws = null;
|
||||
this.connect();
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket("ws://" + location.hostname + ":8080");
|
||||
this.ws.addEventListener("open", () => {
|
||||
console.log("WS ready");
|
||||
});
|
||||
this.ws.addEventListener("message", (e) => {
|
||||
const msg = JSON.parse(e.data);
|
||||
console.log(msg);
|
||||
switch (msg.t) {
|
||||
case "cand": {
|
||||
const fast = this.fast.get(msg.source);
|
||||
if (!fast) return;
|
||||
if (fast.state === "open") return console.log("Already open");
|
||||
for (const candidate of msg.d) {
|
||||
fast.conn.addIceCandidate(candidate).then();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "desc": {
|
||||
const fast = this.fast.get(msg.source);
|
||||
if (!fast) return;
|
||||
if (fast.state === "open") return console.log("Already open");
|
||||
fast.conn.setRemoteDescription(msg.d)
|
||||
.then(() => fast.conn.createAnswer())
|
||||
.then(answer => fast.conn.setLocalDescription(answer))
|
||||
.then(() =>
|
||||
this.ws.send(JSON.stringify({ t: "desc", target: fast.name, d: fast.conn.localDescription }))
|
||||
)
|
||||
break;
|
||||
}
|
||||
case "join": {
|
||||
const fast = new ConnectedClient(this, msg.name);
|
||||
this.fast.set(msg.name, fast);
|
||||
if (fast.conn.localDescription) {
|
||||
this.ws.send(JSON.stringify({ t: "desc", target: msg.name, d: fast.conn.localDescription }))
|
||||
}
|
||||
if (fast.candidates) {
|
||||
this.ws.send(JSON.stringify({ t: "cand", target: msg.name, d: fast.candidates }));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "joined": {
|
||||
const clients = msg.clients;
|
||||
this.fast = new Map();
|
||||
for (const client of clients) {
|
||||
const fast = new ConnectedClient(this, client);
|
||||
if (fast.conn.localDescription) {
|
||||
this.ws.send(JSON.stringify({ t: "desc", target: msg.name, d: fast.conn.localDescription }))
|
||||
}
|
||||
if (fast.candidates) {
|
||||
this.ws.send(JSON.stringify({ t: "cand", target: msg.name, d: fast.candidates }));
|
||||
}
|
||||
this.fast.set(client, fast);
|
||||
}
|
||||
}
|
||||
case "leave": {
|
||||
const fast = this.fast.get(msg.name);
|
||||
if (!fast) return;
|
||||
fast.conn.close();
|
||||
this.fast.delete(msg.name);
|
||||
}
|
||||
case "left": {
|
||||
console.log("Left room successfully");
|
||||
this.roomName = null;
|
||||
this.roomId = null;
|
||||
this.fast.forEach(connection => connection.conn.close());
|
||||
this.fast = new Map();
|
||||
}
|
||||
case "list": {
|
||||
list.set(msg.rooms);
|
||||
}
|
||||
case "error": {
|
||||
console.error(msg.e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
refreshList() {
|
||||
this.ws.send(JSON.stringify({ t: "list" }));
|
||||
}
|
||||
|
||||
send(data: any) {
|
||||
this.ws.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
export const connection: Writable<WebsocketConnection|null> = writable(null);
|
||||
export const list: Writable<{ id: string, name: string, count: number }[]|null> = writable(null);
|
||||
2
client/src/routes/index.svelte
Normal file
2
client/src/routes/index.svelte
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||
BIN
client/static/favicon.png
Normal file
BIN
client/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
15
client/svelte.config.js
Normal file
15
client/svelte.config.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import preprocess from 'svelte-preprocess';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: preprocess(),
|
||||
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
13
client/tsconfig.json
Normal file
13
client/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
8
client/vite.config.js
Normal file
8
client/vite.config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
plugins: [sveltekit()]
|
||||
};
|
||||
|
||||
export default config;
|
||||
17
pnpm-lock.yaml
Normal file
17
pnpm-lock.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
lockfileVersion: 5.4
|
||||
|
||||
importers:
|
||||
|
||||
server:
|
||||
specifiers:
|
||||
uWebSockets.js: github:uNetworking/uWebSockets.js#v20.10.0
|
||||
dependencies:
|
||||
uWebSockets.js: github.com/uNetworking/uWebSockets.js/806df48c9da86af7b3341f3e443388c7cd15c3de
|
||||
|
||||
packages:
|
||||
|
||||
github.com/uNetworking/uWebSockets.js/806df48c9da86af7b3341f3e443388c7cd15c3de:
|
||||
resolution: {tarball: https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/806df48c9da86af7b3341f3e443388c7cd15c3de}
|
||||
name: uWebSockets.js
|
||||
version: 20.10.0
|
||||
dev: false
|
||||
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
packages:
|
||||
- client
|
||||
- server
|
||||
15
server/package.json
Normal file
15
server/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.10.0"
|
||||
}
|
||||
}
|
||||
168
server/src/index.js
Normal file
168
server/src/index.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
const decoder = new TextDecoder();
|
||||
const encoder = new TextEncoder();
|
||||
const PORT = 8080;
|
||||
|
||||
let i = 0;
|
||||
function uuid() {
|
||||
return (++i).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef Room
|
||||
* @property {string} id
|
||||
* @property {string} name
|
||||
* @property {Client} host
|
||||
* @property {Client[]} clients
|
||||
*/
|
||||
/**
|
||||
* @typedef Client
|
||||
* @property {string} name
|
||||
* @property {WebSocket} connection
|
||||
* @property {Room?} room
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Map<string, Room>}
|
||||
*/
|
||||
const rooms = new Map();
|
||||
/**
|
||||
* @type {Map<WebSocket, Client>}
|
||||
*/
|
||||
const clients = new Map();
|
||||
|
||||
require("uWebSockets.js")
|
||||
.App({})
|
||||
.ws("/", {
|
||||
idleTimeout: 30,
|
||||
maxPayloadLength: 16 * 1024 * 1024,
|
||||
upgrade: (res, req, context) => {
|
||||
console.log(
|
||||
`An Http connection wants to become WebSocket, URL: ${req.getUrl()}!`
|
||||
);
|
||||
|
||||
const url = req.getUrl();
|
||||
const parsed = new URL(url);
|
||||
let name = parsed.searchParams.get("name");
|
||||
if (!name || typeof name !== "string" || name.length < 2 || name.length > 64 || !name.trim()) return res.end("invalid_name");
|
||||
name = name.trim();
|
||||
if (clients.forEach(client => client.name === name)) return res.end("name_used");
|
||||
/* This immediately calls open handler, you must not use res after this call */
|
||||
res.upgrade(
|
||||
{
|
||||
url,
|
||||
name
|
||||
},
|
||||
/* Spell these correctly */
|
||||
req.getHeader("sec-websocket-key"),
|
||||
req.getHeader("sec-websocket-protocol"),
|
||||
req.getHeader("sec-websocket-extensions"),
|
||||
context
|
||||
);
|
||||
},
|
||||
open: (ws) => {
|
||||
console.log(
|
||||
"CON",
|
||||
ws.url,
|
||||
ws.name,
|
||||
decoder.decode(ws.getRemoteAddressAsText())
|
||||
);
|
||||
clients.set(ws, {
|
||||
connection: ws,
|
||||
name: ws.name,
|
||||
room: null
|
||||
});
|
||||
},
|
||||
message: (ws, message, isBinary) => {
|
||||
if (isBinary) return ws.end();
|
||||
try {
|
||||
const data = JSON.parse(decoder.decode(message));
|
||||
if(data.t === "ping") return ws.ping();
|
||||
switch(data.t) {
|
||||
case "ping": {
|
||||
return ws.ping();
|
||||
}
|
||||
case "create": {
|
||||
const client = clients.get(ws);
|
||||
if(client.room) return ws.send(JSON.stringify({t: "error", m: "already_in_room"}));
|
||||
const name = data.name.trim();
|
||||
if (!name || typeof name !== "string" || name.length < 2 || name.length > 64 || !name.trim()) return res.send(JSON.stringify({t: "error", m: "invalid_room_name"}));
|
||||
const room = {
|
||||
name: name,
|
||||
host: ws,
|
||||
clients: [client],
|
||||
id: uuid()
|
||||
};
|
||||
rooms.set(room.id, room);
|
||||
client.room = room;
|
||||
return ws.send(JSON.stringify({ t: "create", id: room.id, name: name }));
|
||||
}
|
||||
case "leave": {
|
||||
const client = clients.get(ws);
|
||||
const room = client.room;
|
||||
if (!room) return ws.send(JSON.stringify({ t: "error", m: "room_not_found" }));
|
||||
if (!room.clients.includes(client)) return ws.send(JSON.stringify({ t: "error", m: "not_in_room" }));
|
||||
room.clients.splice(room.clients.indexOf(client), 1);
|
||||
if (room.clients.length === 0) {
|
||||
rooms.delete(room.id);
|
||||
} else if(room.host == ws) {
|
||||
room.host = room.clients[0];
|
||||
room.clients.forEach(client => client.connection.send(JSON.stringify({ t: "host", host: client.name })));
|
||||
}
|
||||
client.room = null;
|
||||
room.clients.forEach(client => client.connection.send(JSON.stringify({ t: "leave", id: room.id, name: client.name })));
|
||||
ws.send(JSON.stringify({ t: "left", id: room.id, name: client.name }));
|
||||
break;
|
||||
}
|
||||
case "join": {
|
||||
const client = clients.get(ws);
|
||||
if (!client) return ws.end();
|
||||
const room = client.room;
|
||||
if (!room) return ws.send(JSON.stringify({ t: "error", e: "room_not_found" }));
|
||||
if (room.clients.includes(client)) return ws.send(JSON.stringify({ t: "error", e: "already_in_room" }));
|
||||
room.clients.push(ws);
|
||||
ws.room = room;
|
||||
room.clients.slice(0, -2).forEach(client => client.connection.send(JSON.stringify({ t: "join", id: room.id, client: client.name })));
|
||||
ws.send(JSON.stringify({ t: "joined", id: room.id, client: client.name, clients: room.clients.map(t => t.name) }));
|
||||
break;
|
||||
}
|
||||
case "cand":
|
||||
case "desc": {
|
||||
const client = clients.get(ws);
|
||||
if (!client) return ws.end();
|
||||
const room = client.room;
|
||||
if (!room) return ws.send(JSON.stringify({ t: "error", e: "room_not_found" }));
|
||||
if (!room.clients.includes(client)) return ws.send(JSON.stringify({ t: "error", e: "not_in_room" }));
|
||||
const targetClient = room.clients.find(t => t.name === msg.target);
|
||||
if(!targetClient) return ws.send(JSON.stringify({ t: "error", e: "target_not_found" }));
|
||||
if(!room.clients.includes(targetClient)) return ws.send(JSON.stringify({ t: "error", e: "target_not_in_room" }));
|
||||
targetClient.connection.send(JSON.stringify({ t: msg.t, id: room.id, source: client.name, d: msg.d }));
|
||||
}
|
||||
case "list": {
|
||||
ws.send(JSON.stringify({ t: "list", rooms: [...rooms.values()].map(t => ({ id: t.id, name: t.name, count: t.clients.length }))}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return ws.end();
|
||||
}
|
||||
},
|
||||
close: (ws, code, message) => {
|
||||
console.log("DIS", decoder.decode(ws.getRemoteAddressAsText()));
|
||||
if (clients.get(ws)) {
|
||||
let room = rooms.get(clients.get(ws));
|
||||
if (room) {
|
||||
room.clients.splice(room.clients.indexOf(ws), 1);
|
||||
if (room.clients.length === 0) {
|
||||
rooms.delete(room.id);
|
||||
} else if(room.host == ws) {
|
||||
room.host = room.clients[0];
|
||||
room.clients.forEach(client => client.connection.send(JSON.stringify({ t: "host", host: client.name })));
|
||||
}
|
||||
}
|
||||
}
|
||||
clients.delete(ws);
|
||||
},
|
||||
})
|
||||
.listen(PORT, () => {
|
||||
console.log(`Listening on port ${PORT}`);
|
||||
});
|
||||
Loading…
Reference in a new issue