it's working chat
This commit is contained in:
parent
dcf885a7b0
commit
a1dbfadfb0
|
@ -20,6 +20,6 @@
|
||||||
"vite": "^6.0.0"
|
"vite": "^6.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"peerjs": "^1.5.4"
|
"uuid": "^11.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
peerjs:
|
uuid:
|
||||||
specifier: ^1.5.4
|
specifier: ^11.0.3
|
||||||
version: 1.5.4
|
version: 11.0.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@sveltejs/adapter-node':
|
'@sveltejs/adapter-node':
|
||||||
specifier: ^5.2.0
|
specifier: ^5.2.0
|
||||||
|
@ -205,10 +205,6 @@ packages:
|
||||||
'@jridgewell/trace-mapping@0.3.25':
|
'@jridgewell/trace-mapping@0.3.25':
|
||||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||||
|
|
||||||
'@msgpack/msgpack@2.8.0':
|
|
||||||
resolution: {integrity: sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==}
|
|
||||||
engines: {node: '>= 10'}
|
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.28':
|
'@polka/url@1.0.0-next.28':
|
||||||
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
|
resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==}
|
||||||
|
|
||||||
|
@ -446,9 +442,6 @@ packages:
|
||||||
estree-walker@2.0.2:
|
estree-walker@2.0.2:
|
||||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
|
||||||
eventemitter3@4.0.7:
|
|
||||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
|
||||||
|
|
||||||
fdir@6.4.2:
|
fdir@6.4.2:
|
||||||
resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
|
resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -520,14 +513,6 @@ packages:
|
||||||
path-parse@1.0.7:
|
path-parse@1.0.7:
|
||||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
|
||||||
peerjs-js-binarypack@2.1.0:
|
|
||||||
resolution: {integrity: sha512-YIwCC+pTzp3Bi8jPI9UFKO0t0SLo6xALnHkiNt/iUFmUUZG0fEEmEyFKvjsDKweiFitzHRyhuh6NvyJZ4nNxMg==}
|
|
||||||
engines: {node: '>= 14.0.0'}
|
|
||||||
|
|
||||||
peerjs@1.5.4:
|
|
||||||
resolution: {integrity: sha512-yFsoLMnurJKlQbx6kVSBpOp+AlNldY1JQS2BrSsHLKCZnq6t7saHleuHM5svuLNbQkUJXHLF3sKOJB1K0xulOw==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
|
|
||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
@ -556,9 +541,6 @@ packages:
|
||||||
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
|
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
sdp@3.2.0:
|
|
||||||
resolution: {integrity: sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==}
|
|
||||||
|
|
||||||
set-cookie-parser@2.7.1:
|
set-cookie-parser@2.7.1:
|
||||||
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
|
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
|
||||||
|
|
||||||
|
@ -598,6 +580,10 @@ packages:
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
uuid@11.0.3:
|
||||||
|
resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
vite@6.0.3:
|
vite@6.0.3:
|
||||||
resolution: {integrity: sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==}
|
resolution: {integrity: sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==}
|
||||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
@ -646,10 +632,6 @@ packages:
|
||||||
vite:
|
vite:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
webrtc-adapter@9.0.1:
|
|
||||||
resolution: {integrity: sha512-1AQO+d4ElfVSXyzNVTOewgGT/tAomwwztX/6e3totvyyzXPvXIIuUUjAmyZGbKBKbZOXauuJooZm3g6IuFuiNQ==}
|
|
||||||
engines: {node: '>=6.0.0', npm: '>=3.10.0'}
|
|
||||||
|
|
||||||
zimmerframe@1.1.2:
|
zimmerframe@1.1.2:
|
||||||
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
|
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
|
||||||
|
|
||||||
|
@ -749,8 +731,6 @@ snapshots:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.0
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
|
||||||
'@msgpack/msgpack@2.8.0': {}
|
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.28': {}
|
'@polka/url@1.0.0-next.28': {}
|
||||||
|
|
||||||
'@rollup/plugin-commonjs@28.0.2(rollup@4.28.1)':
|
'@rollup/plugin-commonjs@28.0.2(rollup@4.28.1)':
|
||||||
|
@ -966,8 +946,6 @@ snapshots:
|
||||||
|
|
||||||
estree-walker@2.0.2: {}
|
estree-walker@2.0.2: {}
|
||||||
|
|
||||||
eventemitter3@4.0.7: {}
|
|
||||||
|
|
||||||
fdir@6.4.2(picomatch@4.0.2):
|
fdir@6.4.2(picomatch@4.0.2):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.2
|
picomatch: 4.0.2
|
||||||
|
@ -1019,15 +997,6 @@ snapshots:
|
||||||
|
|
||||||
path-parse@1.0.7: {}
|
path-parse@1.0.7: {}
|
||||||
|
|
||||||
peerjs-js-binarypack@2.1.0: {}
|
|
||||||
|
|
||||||
peerjs@1.5.4:
|
|
||||||
dependencies:
|
|
||||||
'@msgpack/msgpack': 2.8.0
|
|
||||||
eventemitter3: 4.0.7
|
|
||||||
peerjs-js-binarypack: 2.1.0
|
|
||||||
webrtc-adapter: 9.0.1
|
|
||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
picomatch@4.0.2: {}
|
picomatch@4.0.2: {}
|
||||||
|
@ -1075,8 +1044,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
mri: 1.2.0
|
mri: 1.2.0
|
||||||
|
|
||||||
sdp@3.2.0: {}
|
|
||||||
|
|
||||||
set-cookie-parser@2.7.1: {}
|
set-cookie-parser@2.7.1: {}
|
||||||
|
|
||||||
sirv@3.0.0:
|
sirv@3.0.0:
|
||||||
|
@ -1126,6 +1093,8 @@ snapshots:
|
||||||
|
|
||||||
typescript@5.7.2: {}
|
typescript@5.7.2: {}
|
||||||
|
|
||||||
|
uuid@11.0.3: {}
|
||||||
|
|
||||||
vite@6.0.3:
|
vite@6.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.24.0
|
esbuild: 0.24.0
|
||||||
|
@ -1138,8 +1107,4 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 6.0.3
|
vite: 6.0.3
|
||||||
|
|
||||||
webrtc-adapter@9.0.1:
|
|
||||||
dependencies:
|
|
||||||
sdp: 3.2.0
|
|
||||||
|
|
||||||
zimmerframe@1.1.2: {}
|
zimmerframe@1.1.2: {}
|
||||||
|
|
34
src/lib/audio.svelte.ts
Normal file
34
src/lib/audio.svelte.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
export class AudioManager {
|
||||||
|
globalGain = $state(1.0);
|
||||||
|
useMusic: boolean;
|
||||||
|
ac: AudioContext;
|
||||||
|
dest: MediaStreamAudioDestinationNode;
|
||||||
|
gain: GainNode;
|
||||||
|
source: AudioNode;
|
||||||
|
public readonly output: MediaStream;
|
||||||
|
|
||||||
|
constructor(media: HTMLAudioElement | MediaStream) {
|
||||||
|
this.ac = new AudioContext();
|
||||||
|
this.dest = this.ac.createMediaStreamDestination();
|
||||||
|
this.gain = this.ac.createGain();
|
||||||
|
globalThis.audioPlay = this;
|
||||||
|
|
||||||
|
if (media instanceof HTMLAudioElement) {
|
||||||
|
this.source = this.ac.createMediaElementSource(media);
|
||||||
|
media.autoplay = true;
|
||||||
|
media.controls = true;
|
||||||
|
media.play();
|
||||||
|
this.useMusic = true;
|
||||||
|
} else {
|
||||||
|
this.source = this.ac.createMediaStreamSource(media);
|
||||||
|
this.useMusic = false;
|
||||||
|
}
|
||||||
|
// .connect(this.gain)
|
||||||
|
// this.source.connect(this.dest);
|
||||||
|
this.gain.gain.value /= 32;
|
||||||
|
this.source.connect(this.gain).connect(this.dest)
|
||||||
|
this.output = this.dest.stream;
|
||||||
|
// this.output = media;
|
||||||
|
console.log("epic", this.dest.stream, this.output);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,149 +1,127 @@
|
||||||
// import { BinaryStreamReader } from "./readerStream";
|
import { ReadMethod } from "./types";
|
||||||
// import { AsyncReadMethod, AsyncReadMethodReturnValue, ObjectValue, ObjectValueType, ReadMethod, ReadMethodReturnValue } from "./types";
|
|
||||||
|
|
||||||
// export class BinaryReader {
|
export class BinaryReader {
|
||||||
// protected index: number = 0;
|
protected index: number = 0;
|
||||||
|
|
||||||
// constructor(
|
constructor(
|
||||||
// protected readonly buffer: DataView,
|
protected readonly buffer: DataView,
|
||||||
// ) {}
|
) { }
|
||||||
|
|
||||||
// readBoolean(): boolean {
|
readBoolean(): boolean {
|
||||||
// return this.readUInt8() != 0;
|
return this.readUInt8() != 0;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readSInt8(): number {
|
readSInt8(): number {
|
||||||
// const value = this.buffer.getInt8(this.index);
|
const value = this.buffer.getInt8(this.index);
|
||||||
|
|
||||||
// this.index++;
|
this.index++;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readUInt8(): number {
|
readUInt8(): number {
|
||||||
// const value = this.buffer.getUint8(this.index);
|
const value = this.buffer.getUint8(this.index);
|
||||||
|
|
||||||
// this.index++;
|
this.index++;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readSInt16(): number {
|
readSInt16(): number {
|
||||||
// const value = this.buffer.getInt16(this.index, true);
|
const value = this.buffer.getInt16(this.index, true);
|
||||||
|
|
||||||
// this.index += 2;
|
this.index += 2;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readUInt16(): number {
|
readUInt16(): number {
|
||||||
// const value = this.buffer.getUint16(this.index, true);
|
const value = this.buffer.getUint16(this.index, true);
|
||||||
|
|
||||||
// this.index += 2;
|
this.index += 2;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readSInt32(): number {
|
readSInt32(): number {
|
||||||
// const value = this.buffer.getInt32(this.index, true);
|
const value = this.buffer.getInt32(this.index, true);
|
||||||
|
|
||||||
// this.index += 4;
|
this.index += 4;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readUInt32(): number {
|
readUInt32(): number {
|
||||||
// const value = this.buffer.getUint32(this.index, true);
|
const value = this.buffer.getUint32(this.index, true);
|
||||||
|
|
||||||
// this.index += 4;
|
this.index += 4;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readSInt64(): bigint {
|
readSInt64(): bigint {
|
||||||
// const value = this.buffer.getBigInt64(this.index, true);
|
const value = this.buffer.getBigInt64(this.index, true);
|
||||||
|
|
||||||
// this.index += 8;
|
this.index += 8;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readUInt64(): bigint {
|
readUInt64(): bigint {
|
||||||
// const value = this.buffer.getBigUint64(this.index, true);
|
const value = this.buffer.getBigUint64(this.index, true);
|
||||||
|
|
||||||
// this.index += 8;
|
this.index += 8;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readFloat32(): number {
|
readFloat32(): number {
|
||||||
// const value = this.buffer.getFloat32(this.index, true);
|
const value = this.buffer.getFloat32(this.index, true);
|
||||||
|
|
||||||
// this.index += 4;
|
this.index += 4;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readFloat64(): number {
|
readFloat64(): number {
|
||||||
// const value = this.buffer.getFloat64(this.index, true);
|
const value = this.buffer.getFloat64(this.index, true);
|
||||||
|
|
||||||
// this.index += 8;
|
this.index += 8;
|
||||||
|
|
||||||
// return value;
|
return value;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readString(length: number, encoding?: string): string {
|
readString(length: number, encoding?: string): string {
|
||||||
// const decoder = new TextDecoder(encoding);
|
const decoder = new TextDecoder(encoding);
|
||||||
|
|
||||||
// return decoder.decode(this.buffer.buffer.slice(this.index, this.index += length));
|
return decoder.decode(this.buffer.buffer.slice(this.index, this.index += length));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async readArray<T>(size: number, readMethod: ReadMethod<T> | AsyncReadMethod<T>): Promise<T[]> {
|
async readFixedString(length: number, encoding?: string): Promise<string> {
|
||||||
// const array = new Array<T>(size);
|
const decoder = new TextDecoder(encoding);
|
||||||
|
|
||||||
// for (let i = 0; i < size; i++) {
|
const slice = new Uint8Array(this.buffer.buffer.slice(this.index, this.index += length));
|
||||||
// array[i] = await this.read(readMethod);
|
const foundIndex = slice.findIndex(byte => byte == 0);
|
||||||
// }
|
const end = foundIndex === -1 ? this.buffer.byteLength : foundIndex
|
||||||
|
|
||||||
// return array;
|
return decoder.decode(slice.slice(0, end));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async readObject(): Promise<ObjectValue> {
|
async readArray<T>(size: number, readMethod: ReadMethod<T, BinaryReader>): Promise<T[]> {
|
||||||
// return await this.readMappedShortEnum({
|
const array = new Array<T>(size);
|
||||||
// [ObjectValueType.Object]: async (r: BinaryReader | BinaryStreamReader) => Object.fromEntries(await r.readArray(await r.readUInt32(), async r2 => [ await r2.readString(await r2.readUInt32()), await r2.readObject() ])),
|
|
||||||
// [ObjectValueType.Array]: async (r: BinaryReader | BinaryStreamReader) => await r.readArray(await r.readUInt32(), async (r2: BinaryReader | BinaryStreamReader) => await r2.readObject()),
|
|
||||||
// [ObjectValueType.Boolean]: async (r: BinaryReader | BinaryStreamReader) => await r.readBoolean(),
|
|
||||||
// [ObjectValueType.Null]: () => null,
|
|
||||||
// [ObjectValueType.Number]: async (r: BinaryReader | BinaryStreamReader) => await r.readFloat64(),
|
|
||||||
// [ObjectValueType.String]: async (r: BinaryReader | BinaryStreamReader) => await r.readString(await r.readUInt32()),
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async read<T>(readMethod: ReadMethod<T> | AsyncReadMethod<T>): Promise<T> {
|
for (let i = 0; i < size; i++) {
|
||||||
// if ("deserialize" in readMethod) {
|
array[i] = await this.read(readMethod);
|
||||||
// return await readMethod.deserialize(this);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// return await readMethod(this);
|
return array;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readShortEnum<T extends number>(): T {
|
async read<T>(readMethod: ReadMethod<T, BinaryReader>): Promise<T> {
|
||||||
// return this.readUInt8() as T;
|
if ("deserialize" in readMethod) {
|
||||||
// }
|
return await readMethod.deserialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
// readEnum<T extends number>(): T {
|
return await readMethod(this);
|
||||||
// return this.readUInt16() as T;
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// readDate(): Date {
|
|
||||||
// return new Date(Number(this.readUInt64()));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async readMappedEnum<T extends number, R extends Record<T, ReadMethod<any> | AsyncReadMethod<T>>>(mappedType: R): Promise<ReadMethodReturnValue<R[keyof R]> | AsyncReadMethodReturnValue<R[keyof R]>> {
|
|
||||||
// return await this.read(mappedType[this.readEnum<T>()]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async readMappedShortEnum<T extends number, R extends Record<T, ReadMethod<any> | AsyncReadMethod<T>>>(mappedType: R): Promise<ReadMethodReturnValue<R[keyof R]> | AsyncReadMethodReturnValue<R[keyof R]>> {
|
|
||||||
// return await this.read(mappedType[this.readShortEnum<T>()]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ export class BinaryStreamReader {
|
||||||
(async () => {
|
(async () => {
|
||||||
while (true) {
|
while (true) {
|
||||||
const data = await reader.read();
|
const data = await reader.read();
|
||||||
console.log("got data", data);
|
|
||||||
streamReader.push(data.value);
|
streamReader.push(data.value);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -94,35 +93,35 @@ export class BinaryStreamReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
async readSInt16(): Promise<number> {
|
async readSInt16(): Promise<number> {
|
||||||
return (await this.bytes(2)).getInt16(0);
|
return (await this.bytes(2)).getInt16(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readUInt16(): Promise<number> {
|
async readUInt16(): Promise<number> {
|
||||||
return (await this.bytes(2)).getUint16(0);
|
return (await this.bytes(2)).getUint16(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readSInt32(): Promise<number> {
|
async readSInt32(): Promise<number> {
|
||||||
return (await this.bytes(4)).getInt32(0);
|
return (await this.bytes(4)).getInt32(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readUInt32(): Promise<number> {
|
async readUInt32(): Promise<number> {
|
||||||
return (await this.bytes(4)).getUint32(0);
|
return (await this.bytes(4)).getUint32(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readSInt64(): Promise<bigint> {
|
async readSInt64(): Promise<bigint> {
|
||||||
return (await this.bytes(8)).getBigInt64(0);
|
return (await this.bytes(8)).getBigInt64(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readUInt64(): Promise<bigint> {
|
async readUInt64(): Promise<bigint> {
|
||||||
return (await this.bytes(8)).getBigUint64(0);
|
return (await this.bytes(8)).getBigUint64(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readFloat32(): Promise<number> {
|
async readFloat32(): Promise<number> {
|
||||||
return (await this.bytes(4)).getFloat32(0);
|
return (await this.bytes(4)).getFloat32(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readFloat64(): Promise<number> {
|
async readFloat64(): Promise<number> {
|
||||||
return (await this.bytes(8)).getFloat64(0);
|
return (await this.bytes(8)).getFloat64(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readString(length: number, encoding?: string): Promise<string> {
|
async readString(length: number, encoding?: string): Promise<string> {
|
||||||
|
@ -136,10 +135,10 @@ export class BinaryStreamReader {
|
||||||
|
|
||||||
const bytes = await this.bytes(length);
|
const bytes = await this.bytes(length);
|
||||||
const slice = new Uint8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
const slice = new Uint8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
||||||
const end = slice.findIndex(byte => byte == 0);
|
const foundIndex = slice.findIndex(byte => byte == 0);
|
||||||
console.log(bytes.byteOffset, bytes.byteOffset + end, end, bytes.byteLength);
|
const end = foundIndex === -1 ? slice.byteLength : foundIndex;
|
||||||
|
|
||||||
return decoder.decode(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + end));
|
return decoder.decode(slice.slice(0, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
async readArray<T>(size: number, readMethod: AsyncReadMethod<T, this>): Promise<T[]> {
|
async readArray<T>(size: number, readMethod: AsyncReadMethod<T, this>): Promise<T[]> {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
type Method<T, R, F extends string> = ((reader: R) => T) | { [key in F]: (reader: R) => T };
|
type Method<T, R, F extends string> = ((reader: R) => T) | { [key in F]: (reader: R) => T };
|
||||||
|
|
||||||
export type ReadMethod<T, R> = Method<T, R, "deserialize">;
|
export type ReadMethod<T, R> = Method<T, R, "deserialize">;
|
||||||
export type WriteMethod<W> = Method<void, W, "serialize">;
|
|
||||||
export type AsyncReadMethod<T, R> = Method<T | Promise<T>, R, "deserialize">;
|
export type AsyncReadMethod<T, R> = Method<T | Promise<T>, R, "deserialize">;
|
||||||
|
|
||||||
|
export type WriteMethod<T, W> = ((writer: W, value: T) => void) | { serialize: (writer: W, value: T) => void };
|
||||||
|
|
||||||
export type ReadMethodReturnValue<X> = X extends ReadMethod<infer Y, any> ? (Y extends Promise<infer Z> ? Z : Y) : never;
|
export type ReadMethodReturnValue<X> = X extends ReadMethod<infer Y, any> ? (Y extends Promise<infer Z> ? Z : Y) : never;
|
||||||
|
|
||||||
export enum ObjectValueType {
|
export enum ObjectValueType {
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class BinaryWriter {
|
||||||
writeSInt16(value: number): void {
|
writeSInt16(value: number): void {
|
||||||
this.request(2);
|
this.request(2);
|
||||||
|
|
||||||
this.buffer.setInt16(this.index, value);
|
this.buffer.setInt16(this.index, value, true);
|
||||||
|
|
||||||
this.index += 2;
|
this.index += 2;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export class BinaryWriter {
|
||||||
writeUInt16(value: number): void {
|
writeUInt16(value: number): void {
|
||||||
this.request(2);
|
this.request(2);
|
||||||
|
|
||||||
this.buffer.setUint16(this.index, value);
|
this.buffer.setUint16(this.index, value, true);
|
||||||
|
|
||||||
this.index += 2;
|
this.index += 2;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export class BinaryWriter {
|
||||||
writeSInt32(value: number): void {
|
writeSInt32(value: number): void {
|
||||||
this.request(4);
|
this.request(4);
|
||||||
|
|
||||||
this.buffer.setInt32(this.index, value);
|
this.buffer.setInt32(this.index, value, true);
|
||||||
|
|
||||||
this.index += 4;
|
this.index += 4;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export class BinaryWriter {
|
||||||
writeUInt32(value: number): void {
|
writeUInt32(value: number): void {
|
||||||
this.request(4);
|
this.request(4);
|
||||||
|
|
||||||
this.buffer.setUint32(this.index, value);
|
this.buffer.setUint32(this.index, value, true);
|
||||||
|
|
||||||
this.index += 4;
|
this.index += 4;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ export class BinaryWriter {
|
||||||
writeSInt64(value: bigint): void {
|
writeSInt64(value: bigint): void {
|
||||||
this.request(8);
|
this.request(8);
|
||||||
|
|
||||||
this.buffer.setBigInt64(this.index, value);
|
this.buffer.setBigInt64(this.index, value, true);
|
||||||
|
|
||||||
this.index += 8;
|
this.index += 8;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ export class BinaryWriter {
|
||||||
writeUInt64(value: bigint): void {
|
writeUInt64(value: bigint): void {
|
||||||
this.request(8);
|
this.request(8);
|
||||||
|
|
||||||
this.buffer.setBigUint64(this.index, value);
|
this.buffer.setBigUint64(this.index, value, true);
|
||||||
|
|
||||||
this.index += 8;
|
this.index += 8;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ export class BinaryWriter {
|
||||||
writeFloat32(value: number): void {
|
writeFloat32(value: number): void {
|
||||||
this.request(4);
|
this.request(4);
|
||||||
|
|
||||||
this.buffer.setFloat32(this.index, value);
|
this.buffer.setFloat32(this.index, value, true);
|
||||||
|
|
||||||
this.index += 4;
|
this.index += 4;
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ export class BinaryWriter {
|
||||||
writeFloat64(value: number): void {
|
writeFloat64(value: number): void {
|
||||||
this.request(8);
|
this.request(8);
|
||||||
|
|
||||||
this.buffer.setFloat64(this.index, value);
|
this.buffer.setFloat64(this.index, value, true);
|
||||||
|
|
||||||
this.index += 8;
|
this.index += 8;
|
||||||
}
|
}
|
||||||
|
@ -114,17 +114,41 @@ export class BinaryWriter {
|
||||||
this.index += encoded.byteLength;
|
this.index += encoded.byteLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeArray(array: WriteMethod<BinaryWriter>[]): void {
|
writePrefixedString(string: string): void {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encoded = encoder.encode(string);
|
||||||
|
this.writeUInt32(encoded.byteLength);
|
||||||
|
this.request(encoded.byteLength);
|
||||||
|
|
||||||
|
new Uint8Array(this.buffer.buffer).set(encoded, this.index);
|
||||||
|
|
||||||
|
this.index += encoded.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFixedString(size: number, string: string): void {
|
||||||
|
this.request(size);
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
encoder.encodeInto(string, new Uint8Array(this.buffer.buffer, this.index, size));
|
||||||
|
|
||||||
|
this.index += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeArray<T>(array: T[], method: WriteMethod<T, BinaryWriter>): void {
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
this.write(array[i]);
|
this.write(array[i], method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write(writeMethod: WriteMethod<BinaryWriter>): void {
|
write<T>(value: T, writeMethod: WriteMethod<T, BinaryWriter>): void {
|
||||||
if ("serialize" in writeMethod) {
|
if ("serialize" in writeMethod) {
|
||||||
writeMethod.serialize(this);
|
writeMethod.serialize(this, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeMethod(this);
|
writeMethod(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
toBytes(): Uint8Array {
|
||||||
|
return new Uint8Array(this.buffer.buffer.slice(this.buffer.byteOffset, this.buffer.byteOffset + this.index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,213 @@
|
||||||
import Peer from "peerjs";
|
import { v4 } from "uuid";
|
||||||
import { readHello } from "./protocol/hello";
|
import { readHello } from "./protocol/hello";
|
||||||
import { BinaryStreamReader } from "./binary/readerStream";
|
import { BinaryStreamReader } from "./binary/readerStream";
|
||||||
|
import { ReadKind, readEvent } from "./protocol/serverEvent";
|
||||||
|
import { AudioManager } from "./audio.svelte";
|
||||||
|
import { type WriteEvent, writeEvent, WriteKind } from "./protocol/clientEvent";
|
||||||
|
import { BinaryWriter } from "./binary/writer";
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
static async connect() {
|
static async connect(audioManager: AudioManager, outputAudio: HTMLDivElement) {
|
||||||
const transport = new WebTransport(`https://${location.hostname}:4433`, { allowPooling: false });
|
const transport = new WebTransport(`https://${location.hostname}:4433`, { allowPooling: false });
|
||||||
await transport.ready;
|
await transport.ready;
|
||||||
|
console.log("ready!");
|
||||||
const stream = await transport.createBidirectionalStream();
|
const stream = await transport.createBidirectionalStream();
|
||||||
|
|
||||||
const peer = new Peer();
|
const uuid = v4();
|
||||||
await new Promise((res) => {
|
const writer = stream.writable.getWriter();
|
||||||
peer.on("open", () => {
|
await writer.write(new TextEncoder().encode(uuid));
|
||||||
console.log("open!", peer.id)
|
|
||||||
res(void 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await stream.writable.getWriter().write(new TextEncoder().encode(peer.id));
|
|
||||||
const reader = BinaryStreamReader.fromReader(stream.readable.getReader());
|
const reader = BinaryStreamReader.fromReader(stream.readable.getReader());
|
||||||
const players = $state(await readHello(reader));
|
const { players, peers } = await readHello(reader);
|
||||||
|
console.log("done reading hello");
|
||||||
|
|
||||||
return new Client(players, peer, transport, stream);
|
const playersBind = $state(players);
|
||||||
|
const client = new Client(audioManager, outputAudio, playersBind, uuid, transport, stream, writer);
|
||||||
|
|
||||||
|
for (const id of peers) {
|
||||||
|
client.getPeer(id).offer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// peer.on("call", (conn) => {
|
||||||
|
// conn.answer(audioManager.output);
|
||||||
|
// client.peers[conn.connectionId] = new Peer(conn.connectionId, outputAudio, conn)
|
||||||
|
// });
|
||||||
|
|
||||||
|
console.log("done setup, calling handlers");
|
||||||
|
client.datagramEventHandler();
|
||||||
|
client.streamEventHandler(reader);
|
||||||
|
client.closeHandler();
|
||||||
|
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async closeHandler() {
|
||||||
|
await this.transport.closed;
|
||||||
|
console.error("closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async datagramEventHandler() {
|
||||||
|
const dgramReader = this.transport.datagrams.readable.getReader();
|
||||||
|
this.eventHandler(async () => {
|
||||||
|
const dgram = await dgramReader.read();
|
||||||
|
console.log(dgram);
|
||||||
|
throw dgram;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPeer(id: string) {
|
||||||
|
if (this.peers[id] !== undefined) {
|
||||||
|
return this.peers[id];
|
||||||
|
}
|
||||||
|
return this.peers[id] = new Peer(id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeEvent(event: WriteEvent) {
|
||||||
|
const binaryWriter = new BinaryWriter(0x100);
|
||||||
|
binaryWriter.write(event, writeEvent);
|
||||||
|
await this.writer.write(binaryWriter.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async streamEventHandler(reader: BinaryStreamReader) {
|
||||||
|
this.eventHandler(() => readEvent(reader))
|
||||||
|
}
|
||||||
|
private async eventHandler(eventSource: () => ReturnType<typeof readEvent>) {
|
||||||
|
while (true) {
|
||||||
|
const event = await eventSource();
|
||||||
|
console.log("got event", event);
|
||||||
|
switch (event.kind) {
|
||||||
|
case ReadKind.Connected: {
|
||||||
|
console.log("hello", event.name);
|
||||||
|
this.players[event.id] = new Player(event.name, event.id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.Disconnected: {
|
||||||
|
console.log("goodbye", event.id);
|
||||||
|
delete this.players[event.id];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.Moved: {
|
||||||
|
if (this.players[event.id] !== undefined)
|
||||||
|
this.players[event.id].position = event.position;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.StageChanged: {
|
||||||
|
console.log("goodbye", event.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.PeerConnectionChanged: {
|
||||||
|
console.log("peer changed", event.id, event.connected);
|
||||||
|
// if (event.connected) {
|
||||||
|
// this.getPeer(event.id)
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.Offer: {
|
||||||
|
this.getPeer(event.id).gotOffer(event.offerSdp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.Answer: {
|
||||||
|
this.getPeer(event.id).gotAnswer(event.answerSdp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReadKind.IceCandidate: {
|
||||||
|
this.getPeer(event.id).gotCandidate(event.candidate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private peers: Record<string, Peer> = $state({});
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
public players: Player[],
|
public audioManager: AudioManager,
|
||||||
private peer: Peer,
|
public outputAudio: HTMLDivElement,
|
||||||
private transport: WebTransport,
|
public players: Record<number, Player>,
|
||||||
private stream: WebTransportBidirectionalStream
|
public uuid: string,
|
||||||
|
public transport: WebTransport,
|
||||||
|
public stream: WebTransportBidirectionalStream,
|
||||||
|
public writer: WritableStreamDefaultWriter,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return this.peer.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closed() {
|
closed() {
|
||||||
// return transport.closed;
|
return this.transport.closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Player {
|
export class Player {
|
||||||
constructor(public name: string, public id: number, public position: number[], public stage: string) { }
|
constructor(public name: string, public id: number, public position: number[] = [0, 0, 0], public stage: string = "nostage" + Math.random()) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Peer {
|
||||||
|
private connection: RTCPeerConnection;
|
||||||
|
private audel: HTMLAudioElement;
|
||||||
|
private state: string = $state("not connected");
|
||||||
|
constructor(public peerId: string, public client: Client) {
|
||||||
|
this.connection = new RTCPeerConnection({
|
||||||
|
iceServers: [
|
||||||
|
{
|
||||||
|
urls: ["stun:stun.l.google.com:19302"],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
for (const track of this.client.audioManager.output.getAudioTracks()) {
|
||||||
|
this.connection.addTrack(track, this.client.audioManager.output);
|
||||||
|
}
|
||||||
|
this.connection.addEventListener("iceconnectionstatechange", (state) => {
|
||||||
|
console.log(this.connection.iceConnectionState);
|
||||||
|
this.state = this.connection.iceConnectionState;
|
||||||
|
});
|
||||||
|
this.connection.addEventListener("icecandidate", (event) => {
|
||||||
|
if (event.candidate)
|
||||||
|
this.client.writeEvent({ kind: WriteKind.IceCandidate, to: this.peerId, candidate: event.candidate });
|
||||||
|
else
|
||||||
|
console.log(this.peerId, "finished candidate gathering")
|
||||||
|
});
|
||||||
|
this.audel = document.createElement("audio");
|
||||||
|
this.connection.addEventListener("track", (ev) => {
|
||||||
|
this.audel.srcObject = ev.streams[0];
|
||||||
|
this.audel.autoplay = true;
|
||||||
|
this.audel.controls = true;
|
||||||
|
this.client.outputAudio.appendChild(this.audel);
|
||||||
|
console.log('got track :)');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async offer() {
|
||||||
|
console.log('offering!');
|
||||||
|
const offer = await this.connection.createOffer({ offerToReceiveAudio: true });
|
||||||
|
await this.connection.setLocalDescription(offer);
|
||||||
|
this.client.writeEvent({
|
||||||
|
kind: WriteKind.Offer,
|
||||||
|
to: this.peerId,
|
||||||
|
offerSdp: offer.sdp ?? throwExpr("it didn't create an offer???"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async gotOffer(offer: string) {
|
||||||
|
console.log('got offer!');
|
||||||
|
await this.connection.setRemoteDescription(new RTCSessionDescription({ type: "offer", sdp: offer }));
|
||||||
|
const answer = await this.connection.createAnswer({ offerToReceiveAudio: true });
|
||||||
|
await this.connection.setLocalDescription(answer);
|
||||||
|
|
||||||
|
this.client.writeEvent({
|
||||||
|
kind: WriteKind.Answer,
|
||||||
|
to: this.peerId,
|
||||||
|
answerSdp: answer.sdp ?? throwExpr("it didn't create an offer???"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async gotAnswer(answer: string) {
|
||||||
|
console.log('got answer!');
|
||||||
|
await this.connection.setRemoteDescription(new RTCSessionDescription({ type: "answer", sdp: answer }));
|
||||||
|
}
|
||||||
|
public async gotCandidate(candidate: RTCIceCandidateInit) {
|
||||||
|
console.log('got candidate...');
|
||||||
|
await this.connection.addIceCandidate(candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwExpr(...args: Parameters<ErrorConstructor>): never {
|
||||||
|
throw new Error(...args);
|
||||||
}
|
}
|
||||||
|
|
30
src/lib/protocol/clientEvent.ts
Normal file
30
src/lib/protocol/clientEvent.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { BinaryWriter } from "../binary/writer";
|
||||||
|
|
||||||
|
export enum WriteKind {
|
||||||
|
Offer,
|
||||||
|
Answer,
|
||||||
|
IceCandidate,
|
||||||
|
}
|
||||||
|
export type WriteEvent = {
|
||||||
|
kind: WriteKind.IceCandidate,
|
||||||
|
to: string,
|
||||||
|
candidate: RTCIceCandidate
|
||||||
|
} | {
|
||||||
|
kind: WriteKind.Offer,
|
||||||
|
to: string,
|
||||||
|
offerSdp: string
|
||||||
|
} | {
|
||||||
|
kind: WriteKind.Answer,
|
||||||
|
to: string,
|
||||||
|
answerSdp: string
|
||||||
|
}
|
||||||
|
export function writeEvent(writer: BinaryWriter, event: WriteEvent) {
|
||||||
|
writer.writeUInt8(event.kind);
|
||||||
|
writer.writeFixedString(36, event.to);
|
||||||
|
switch (event.kind) {
|
||||||
|
case WriteKind.Offer: writer.writePrefixedString(event.offerSdp); return;
|
||||||
|
case WriteKind.Answer: writer.writePrefixedString(event.answerSdp); return;
|
||||||
|
case WriteKind.IceCandidate: writer.writePrefixedString(JSON.stringify(event.candidate.toJSON())); return;
|
||||||
|
default: throw new Error("kind not good");
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,23 +2,24 @@ import { BinaryStreamReader } from "../binary/readerStream";
|
||||||
import { Player } from "../client.svelte";
|
import { Player } from "../client.svelte";
|
||||||
|
|
||||||
export async function readHello(reader: BinaryStreamReader) {
|
export async function readHello(reader: BinaryStreamReader) {
|
||||||
console.log("reading");
|
|
||||||
const count = await reader.readUInt8();
|
const count = await reader.readUInt8();
|
||||||
console.log("reading", count);
|
const players: Record<number, Player> = {};
|
||||||
const players = await reader.readArray(count, async (reader) => {
|
for (let i = 0; i < count; i++) {
|
||||||
const name = await reader.readFixedString(0x20);
|
|
||||||
console.log(name);
|
|
||||||
const id = await reader.readUInt32();
|
const id = await reader.readUInt32();
|
||||||
const position = await reader.readArray(3, async reader => await reader.readFloat32());
|
const name = await reader.readFixedString(0x20);
|
||||||
const stage = await reader.readFixedString(0x40);
|
const stage = await reader.readFixedString(0x40);
|
||||||
|
const position = await reader.readArray(3, async reader => await reader.readFloat32());
|
||||||
|
|
||||||
return new Player(
|
players[id] = new Player(
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
position,
|
position,
|
||||||
stage,
|
stage,
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
const peers = await reader.readArray(await reader.readUInt32(), async (reader) => {
|
||||||
|
return await reader.readFixedString(36);
|
||||||
});
|
});
|
||||||
|
|
||||||
return players;
|
return { players, peers };
|
||||||
}
|
}
|
||||||
|
|
59
src/lib/protocol/serverEvent.ts
Normal file
59
src/lib/protocol/serverEvent.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { BinaryStreamReader } from "../binary/readerStream";
|
||||||
|
|
||||||
|
export enum ReadKind {
|
||||||
|
Connected = 0,
|
||||||
|
Disconnected = 1,
|
||||||
|
Moved = 2,
|
||||||
|
StageChanged = 3,
|
||||||
|
PeerConnectionChanged = 4,
|
||||||
|
Offer = 5,
|
||||||
|
Answer = 6,
|
||||||
|
IceCandidate = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readEvent(reader: BinaryStreamReader) {
|
||||||
|
const kind = await reader.readUInt8();
|
||||||
|
switch (kind) {
|
||||||
|
case ReadKind.Connected: {
|
||||||
|
const id = await reader.readUInt32();
|
||||||
|
const name = await reader.readFixedString(0x20);
|
||||||
|
return { kind, id, name };
|
||||||
|
}
|
||||||
|
case ReadKind.Disconnected: {
|
||||||
|
const id = await reader.readUInt32();
|
||||||
|
return { kind, id };
|
||||||
|
}
|
||||||
|
case ReadKind.Moved: {
|
||||||
|
const id = await reader.readUInt32();
|
||||||
|
const position = await reader.readArray(3, async reader => reader.readFloat32());
|
||||||
|
return { kind, id, position };
|
||||||
|
}
|
||||||
|
case ReadKind.StageChanged: {
|
||||||
|
const id = await reader.readUInt32();
|
||||||
|
const stage = await reader.readFixedString(0x40);
|
||||||
|
return { kind, id, stage };
|
||||||
|
}
|
||||||
|
case ReadKind.PeerConnectionChanged: {
|
||||||
|
const id = await reader.readFixedString(36);
|
||||||
|
const connected = await reader.readBoolean();
|
||||||
|
return { kind, id, connected };
|
||||||
|
}
|
||||||
|
case ReadKind.Offer: {
|
||||||
|
const id = await reader.readFixedString(36);
|
||||||
|
const offerSdp = await reader.readString(await reader.readUInt32());
|
||||||
|
return { kind, id, offerSdp }
|
||||||
|
}
|
||||||
|
case ReadKind.Answer: {
|
||||||
|
const id = await reader.readFixedString(36);
|
||||||
|
const answerSdp = await reader.readString(await reader.readUInt32());
|
||||||
|
return { kind, id, answerSdp }
|
||||||
|
}
|
||||||
|
case ReadKind.IceCandidate: {
|
||||||
|
const id = await reader.readFixedString(36);
|
||||||
|
const candidate: RTCIceCandidateInit = JSON.parse(await reader.readString(await reader.readUInt32()));
|
||||||
|
return { kind, id, candidate }
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported kind ${kind}`)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +1,76 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { Client } from "$lib/client.svelte.ts";
|
import { Client } from "$lib/client.svelte.ts";
|
||||||
|
import { AudioManager } from "$lib/audio.svelte.ts";
|
||||||
import { BinaryStreamReader } from "$lib/binary/readerStream";
|
import { BinaryStreamReader } from "$lib/binary/readerStream";
|
||||||
|
|
||||||
let name = $state("none");
|
let name = $state("");
|
||||||
let client: Client = undefined as any;
|
let useMusic = $state(false);
|
||||||
let players = $derived(client?.players ?? []);
|
let client: Client = $state(undefined as any);
|
||||||
|
let players = $derived.by(() => {
|
||||||
|
if (client === undefined) return {};
|
||||||
|
return client.players;
|
||||||
|
});
|
||||||
|
let ac: AudioManager;
|
||||||
|
let wyrm: HTMLAudioElement;
|
||||||
|
let outputAudio: HTMLDivElement;
|
||||||
async function a() {
|
async function a() {
|
||||||
client = await Client.connect();
|
name = localStorage.getItem("name") ?? "";
|
||||||
|
useMusic = localStorage.getItem("useMusic") == "true";
|
||||||
|
}
|
||||||
|
async function audioPrep() {
|
||||||
|
let media = useMusic
|
||||||
|
? wyrm
|
||||||
|
: await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: true,
|
||||||
|
video: false,
|
||||||
|
});
|
||||||
|
ac = new AudioManager(media);
|
||||||
|
}
|
||||||
|
async function connect() {
|
||||||
|
await audioPrep();
|
||||||
|
client = await Client.connect(ac, outputAudio);
|
||||||
console.log(client);
|
console.log(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(a);
|
onMount(a);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={outputAudio} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
I am <input type="text" maxlength="32" list="connectedPlayers" />
|
I am <input
|
||||||
</div>
|
type="text"
|
||||||
<datalist>
|
maxlength="32"
|
||||||
{#each players as player}
|
list="connectedPlayers"
|
||||||
<option>{player.name}</option>
|
bind:value={name}
|
||||||
{/each}
|
onchange={() => localStorage.setItem("name", name)}
|
||||||
</datalist>
|
/>
|
||||||
<div>
|
<label for="useMusic">Use Music</label>
|
||||||
Connected players - {players.length}<br />
|
<input
|
||||||
{#each players as player}
|
id="useMusic"
|
||||||
<pre class="name">{player.name}</pre>
|
type="checkbox"
|
||||||
{/each}
|
bind:checked={useMusic}
|
||||||
|
onchange={() => localStorage.setItem("useMusic", useMusic)}
|
||||||
|
/>
|
||||||
|
<button onclick={() => connect()}>Connect</button>
|
||||||
|
<audio bind:this={wyrm} src="/wyrmjewelbox.wav" autoplay={false} controls
|
||||||
|
>audio of the wyrm's houseki box</audio
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
{#if client !== undefined}
|
||||||
|
<datalist>
|
||||||
|
{#each players as player}
|
||||||
|
<option>{player.name}</option>
|
||||||
|
{/each}
|
||||||
|
</datalist>
|
||||||
|
<div>
|
||||||
|
Connected players - {players.length}<br />
|
||||||
|
{#each players as player}
|
||||||
|
<pre class="name">{player.name}</pre>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.name {
|
.name {
|
||||||
|
|
BIN
static/wyrmjewelbox.wav
Executable file
BIN
static/wyrmjewelbox.wav
Executable file
Binary file not shown.
Loading…
Reference in a new issue