initial commit
This commit is contained in:
commit
dcf885a7b0
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Output
|
||||||
|
.output
|
||||||
|
.vercel
|
||||||
|
.netlify
|
||||||
|
.wrangler
|
||||||
|
/.svelte-kit
|
||||||
|
/build
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.test
|
||||||
|
|
||||||
|
# Vite
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
|
*.pem
|
||||||
|
jack/
|
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"files.autoSave": "onFocusChange"
|
||||||
|
}
|
38
README.md
Normal file
38
README.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# sv
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||||
|
|
||||||
|
## 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
|
||||||
|
npx sv create
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npx sv create 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://svelte.dev/docs/kit/adapters) for your target environment.
|
25
package.json
Normal file
25
package.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "proximity-chat",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-node": "^5.2.0",
|
||||||
|
"@sveltejs/kit": "^2.9.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@vitejs/plugin-basic-ssl": "^1.2.0",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^6.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"peerjs": "^1.5.4"
|
||||||
|
}
|
||||||
|
}
|
1145
pnpm-lock.yaml
Normal file
1145
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
13
src/app.d.ts
vendored
Normal file
13
src/app.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
|
// for information about these interfaces
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
// interface Error {}
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface PageState {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
12
src/app.html
Normal file
12
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, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
149
src/lib/binary/reader.ts
Normal file
149
src/lib/binary/reader.ts
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
// import { BinaryStreamReader } from "./readerStream";
|
||||||
|
// import { AsyncReadMethod, AsyncReadMethodReturnValue, ObjectValue, ObjectValueType, ReadMethod, ReadMethodReturnValue } from "./types";
|
||||||
|
|
||||||
|
// export class BinaryReader {
|
||||||
|
// protected index: number = 0;
|
||||||
|
|
||||||
|
// constructor(
|
||||||
|
// protected readonly buffer: DataView,
|
||||||
|
// ) {}
|
||||||
|
|
||||||
|
// readBoolean(): boolean {
|
||||||
|
// return this.readUInt8() != 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readSInt8(): number {
|
||||||
|
// const value = this.buffer.getInt8(this.index);
|
||||||
|
|
||||||
|
// this.index++;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readUInt8(): number {
|
||||||
|
// const value = this.buffer.getUint8(this.index);
|
||||||
|
|
||||||
|
// this.index++;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readSInt16(): number {
|
||||||
|
// const value = this.buffer.getInt16(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 2;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readUInt16(): number {
|
||||||
|
// const value = this.buffer.getUint16(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 2;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readSInt32(): number {
|
||||||
|
// const value = this.buffer.getInt32(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 4;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readUInt32(): number {
|
||||||
|
// const value = this.buffer.getUint32(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 4;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readSInt64(): bigint {
|
||||||
|
// const value = this.buffer.getBigInt64(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 8;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readUInt64(): bigint {
|
||||||
|
// const value = this.buffer.getBigUint64(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 8;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readFloat32(): number {
|
||||||
|
// const value = this.buffer.getFloat32(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 4;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readFloat64(): number {
|
||||||
|
// const value = this.buffer.getFloat64(this.index, true);
|
||||||
|
|
||||||
|
// this.index += 8;
|
||||||
|
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readString(length: number, encoding?: string): string {
|
||||||
|
// const decoder = new TextDecoder(encoding);
|
||||||
|
|
||||||
|
// return decoder.decode(this.buffer.buffer.slice(this.index, this.index += length));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async readArray<T>(size: number, readMethod: ReadMethod<T> | AsyncReadMethod<T>): Promise<T[]> {
|
||||||
|
// const array = new Array<T>(size);
|
||||||
|
|
||||||
|
// for (let i = 0; i < size; i++) {
|
||||||
|
// array[i] = await this.read(readMethod);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return array;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async readObject(): Promise<ObjectValue> {
|
||||||
|
// return await this.readMappedShortEnum({
|
||||||
|
// [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> {
|
||||||
|
// if ("deserialize" in readMethod) {
|
||||||
|
// return await readMethod.deserialize(this);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return await readMethod(this);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readShortEnum<T extends number>(): T {
|
||||||
|
// return this.readUInt8() as T;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// readEnum<T extends number>(): T {
|
||||||
|
// 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>()]);
|
||||||
|
// }
|
||||||
|
// }
|
166
src/lib/binary/readerStream.ts
Normal file
166
src/lib/binary/readerStream.ts
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
import type { AsyncReadMethod } from "./types";
|
||||||
|
|
||||||
|
export class BinaryStreamReader {
|
||||||
|
chunks: { view: ArrayBuffer, index: number }[] = [];
|
||||||
|
readStack: { size: number, resolve: (value: DataView | PromiseLike<DataView>) => void, reject: (reason?: any) => void }[] = [];
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
push(view: ArrayBuffer): void {
|
||||||
|
this.chunks.push({ view, index: 0 });
|
||||||
|
|
||||||
|
let next = this.readStack[0];
|
||||||
|
|
||||||
|
while (next && this.hasFree(next.size)) {
|
||||||
|
this.readStack.shift()!.resolve(this.get(next.size));
|
||||||
|
|
||||||
|
next = this.readStack[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromReader(reader: ReadableStreamDefaultReader): BinaryStreamReader {
|
||||||
|
const streamReader = new BinaryStreamReader();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
while (true) {
|
||||||
|
const data = await reader.read();
|
||||||
|
console.log("got data", data);
|
||||||
|
streamReader.push(data.value);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return streamReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get(size: number): DataView {
|
||||||
|
const b = new Uint8Array(size);
|
||||||
|
let bHead: number = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.chunks.length; i++) {
|
||||||
|
const buf = this.chunks[i];
|
||||||
|
|
||||||
|
let wantedBytes = (size - bHead)
|
||||||
|
let availableBytes = (buf.view.byteLength - buf.index);
|
||||||
|
|
||||||
|
if (wantedBytes < availableBytes) {
|
||||||
|
b.set(new Uint8Array(buf.view.slice(buf.index, buf.index += wantedBytes)), bHead);
|
||||||
|
bHead += wantedBytes;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
b.set(new Uint8Array(buf.view.slice(buf.index, buf.index += availableBytes)), bHead);
|
||||||
|
bHead += availableBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this.chunks[0] && this.chunks[0].view.byteLength == this.chunks[0].index) {
|
||||||
|
this.chunks.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataView(b.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasFree(size: number) {
|
||||||
|
let accumulated: number = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.chunks.length; i++) {
|
||||||
|
accumulated += this.chunks[i].view.byteLength - this.chunks[i].index;
|
||||||
|
|
||||||
|
if (accumulated >= size) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async bytes(size: number): Promise<DataView> {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
if (this.hasFree(size)) {
|
||||||
|
return res(this.get(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readStack.push({ size, resolve: res, reject: rej });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async readBoolean(): Promise<boolean> {
|
||||||
|
return await this.readUInt8() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async readSInt8(): Promise<number> {
|
||||||
|
return (await this.bytes(1)).getInt8(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readUInt8(): Promise<number> {
|
||||||
|
return (await this.bytes(1)).getUint8(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readSInt16(): Promise<number> {
|
||||||
|
return (await this.bytes(2)).getInt16(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readUInt16(): Promise<number> {
|
||||||
|
return (await this.bytes(2)).getUint16(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readSInt32(): Promise<number> {
|
||||||
|
return (await this.bytes(4)).getInt32(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readUInt32(): Promise<number> {
|
||||||
|
return (await this.bytes(4)).getUint32(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readSInt64(): Promise<bigint> {
|
||||||
|
return (await this.bytes(8)).getBigInt64(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readUInt64(): Promise<bigint> {
|
||||||
|
return (await this.bytes(8)).getBigUint64(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFloat32(): Promise<number> {
|
||||||
|
return (await this.bytes(4)).getFloat32(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFloat64(): Promise<number> {
|
||||||
|
return (await this.bytes(8)).getFloat64(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readString(length: number, encoding?: string): Promise<string> {
|
||||||
|
const decoder = new TextDecoder(encoding);
|
||||||
|
|
||||||
|
return decoder.decode(await this.bytes(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFixedString(length: number, encoding?: string): Promise<string> {
|
||||||
|
const decoder = new TextDecoder(encoding);
|
||||||
|
|
||||||
|
const bytes = await this.bytes(length);
|
||||||
|
const slice = new Uint8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
||||||
|
const end = slice.findIndex(byte => byte == 0);
|
||||||
|
console.log(bytes.byteOffset, bytes.byteOffset + end, end, bytes.byteLength);
|
||||||
|
|
||||||
|
return decoder.decode(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + end));
|
||||||
|
}
|
||||||
|
|
||||||
|
async readArray<T>(size: number, readMethod: AsyncReadMethod<T, this>): Promise<T[]> {
|
||||||
|
const array = new Array<T>(size);
|
||||||
|
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
array[i] = await this.read(readMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
async read<T>(readMethod: AsyncReadMethod<T, this>): Promise<T> {
|
||||||
|
if ("deserialize" in readMethod) {
|
||||||
|
return await readMethod.deserialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await readMethod(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readDate(): Promise<Date> {
|
||||||
|
return new Date(Number(await this.readUInt64()));
|
||||||
|
}
|
||||||
|
}
|
18
src/lib/binary/types.ts
Normal file
18
src/lib/binary/types.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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 WriteMethod<W> = Method<void, W, "serialize">;
|
||||||
|
export type AsyncReadMethod<T, R> = Method<T | Promise<T>, R, "deserialize">;
|
||||||
|
|
||||||
|
export type ReadMethodReturnValue<X> = X extends ReadMethod<infer Y, any> ? (Y extends Promise<infer Z> ? Z : Y) : never;
|
||||||
|
|
||||||
|
export enum ObjectValueType {
|
||||||
|
Object,
|
||||||
|
Array,
|
||||||
|
String,
|
||||||
|
Number,
|
||||||
|
Boolean,
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObjectValue = { [key: string]: ObjectValue } | ObjectValue[] | null | string | number | boolean
|
130
src/lib/binary/writer.ts
Normal file
130
src/lib/binary/writer.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import type { ObjectValue, ObjectValueType, WriteMethod } from "./types";
|
||||||
|
|
||||||
|
export class BinaryWriter {
|
||||||
|
protected index: number = 0;
|
||||||
|
protected buffer: DataView;
|
||||||
|
|
||||||
|
constructor(baseSize?: number) {
|
||||||
|
this.buffer = new DataView(new ArrayBuffer(baseSize ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected request(size: number) {
|
||||||
|
let needSize = size - (this.buffer.byteLength - this.index);
|
||||||
|
|
||||||
|
if (needSize > 0) {
|
||||||
|
let newBuffer = new Uint8Array(this.buffer.byteLength + needSize);
|
||||||
|
|
||||||
|
newBuffer.set(new Uint8Array(this.buffer.buffer), 0);
|
||||||
|
|
||||||
|
this.buffer = new DataView(newBuffer.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBoolean(value: boolean): void {
|
||||||
|
this.writeUInt8(value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSInt8(value: number): void {
|
||||||
|
this.request(1);
|
||||||
|
|
||||||
|
this.buffer.setInt8(this.index, value);
|
||||||
|
|
||||||
|
this.index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt8(value: number): void {
|
||||||
|
this.request(1);
|
||||||
|
|
||||||
|
this.buffer.setUint8(this.index, value);
|
||||||
|
|
||||||
|
this.index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSInt16(value: number): void {
|
||||||
|
this.request(2);
|
||||||
|
|
||||||
|
this.buffer.setInt16(this.index, value);
|
||||||
|
|
||||||
|
this.index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt16(value: number): void {
|
||||||
|
this.request(2);
|
||||||
|
|
||||||
|
this.buffer.setUint16(this.index, value);
|
||||||
|
|
||||||
|
this.index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSInt32(value: number): void {
|
||||||
|
this.request(4);
|
||||||
|
|
||||||
|
this.buffer.setInt32(this.index, value);
|
||||||
|
|
||||||
|
this.index += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt32(value: number): void {
|
||||||
|
this.request(4);
|
||||||
|
|
||||||
|
this.buffer.setUint32(this.index, value);
|
||||||
|
|
||||||
|
this.index += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSInt64(value: bigint): void {
|
||||||
|
this.request(8);
|
||||||
|
|
||||||
|
this.buffer.setBigInt64(this.index, value);
|
||||||
|
|
||||||
|
this.index += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt64(value: bigint): void {
|
||||||
|
this.request(8);
|
||||||
|
|
||||||
|
this.buffer.setBigUint64(this.index, value);
|
||||||
|
|
||||||
|
this.index += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFloat32(value: number): void {
|
||||||
|
this.request(4);
|
||||||
|
|
||||||
|
this.buffer.setFloat32(this.index, value);
|
||||||
|
|
||||||
|
this.index += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFloat64(value: number): void {
|
||||||
|
this.request(8);
|
||||||
|
|
||||||
|
this.buffer.setFloat64(this.index, value);
|
||||||
|
|
||||||
|
this.index += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeString(string: string): void {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encoded = encoder.encode(string);
|
||||||
|
this.request(encoded.byteLength);
|
||||||
|
|
||||||
|
new Uint8Array(this.buffer.buffer).set(encoded, this.index);
|
||||||
|
|
||||||
|
this.index += encoded.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeArray(array: WriteMethod<BinaryWriter>[]): void {
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
this.write(array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write(writeMethod: WriteMethod<BinaryWriter>): void {
|
||||||
|
if ("serialize" in writeMethod) {
|
||||||
|
writeMethod.serialize(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeMethod(this);
|
||||||
|
}
|
||||||
|
}
|
49
src/lib/client.svelte.ts
Normal file
49
src/lib/client.svelte.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import Peer from "peerjs";
|
||||||
|
import { readHello } from "./protocol/hello";
|
||||||
|
import { BinaryStreamReader } from "./binary/readerStream";
|
||||||
|
|
||||||
|
export class Client {
|
||||||
|
static async connect() {
|
||||||
|
const transport = new WebTransport(`https://${location.hostname}:4433`, { allowPooling: false });
|
||||||
|
await transport.ready;
|
||||||
|
const stream = await transport.createBidirectionalStream();
|
||||||
|
|
||||||
|
const peer = new Peer();
|
||||||
|
await new Promise((res) => {
|
||||||
|
peer.on("open", () => {
|
||||||
|
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 players = $state(await readHello(reader));
|
||||||
|
|
||||||
|
return new Client(players, peer, transport, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
public players: Player[],
|
||||||
|
private peer: Peer,
|
||||||
|
private transport: WebTransport,
|
||||||
|
private stream: WebTransportBidirectionalStream
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this.peer.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
closed() {
|
||||||
|
// return transport.closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Player {
|
||||||
|
constructor(public name: string, public id: number, public position: number[], public stage: string) { }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
24
src/lib/protocol/hello.ts
Normal file
24
src/lib/protocol/hello.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { BinaryStreamReader } from "../binary/readerStream";
|
||||||
|
import { Player } from "../client.svelte";
|
||||||
|
|
||||||
|
export async function readHello(reader: BinaryStreamReader) {
|
||||||
|
console.log("reading");
|
||||||
|
const count = await reader.readUInt8();
|
||||||
|
console.log("reading", count);
|
||||||
|
const players = await reader.readArray(count, async (reader) => {
|
||||||
|
const name = await reader.readFixedString(0x20);
|
||||||
|
console.log(name);
|
||||||
|
const id = await reader.readUInt32();
|
||||||
|
const position = await reader.readArray(3, async reader => await reader.readFloat32());
|
||||||
|
const stage = await reader.readFixedString(0x40);
|
||||||
|
|
||||||
|
return new Player(
|
||||||
|
name,
|
||||||
|
id,
|
||||||
|
position,
|
||||||
|
stage,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return players;
|
||||||
|
}
|
1
src/lib/protocol/index.ts
Normal file
1
src/lib/protocol/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { } from "./hello";
|
36
src/routes/+page.svelte
Normal file
36
src/routes/+page.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { Client } from "$lib/client.svelte.ts";
|
||||||
|
import { BinaryStreamReader } from "$lib/binary/readerStream";
|
||||||
|
|
||||||
|
let name = $state("none");
|
||||||
|
let client: Client = undefined as any;
|
||||||
|
let players = $derived(client?.players ?? []);
|
||||||
|
async function a() {
|
||||||
|
client = await Client.connect();
|
||||||
|
console.log(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(a);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
I am <input type="text" maxlength="32" list="connectedPlayers" />
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.name {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
static/favicon.png
Normal file
BIN
static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
18
svelte.config.js
Normal file
18
svelte.config.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import adapter from '@sveltejs/adapter-node';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
// Consult https://svelte.dev/docs/kit/integrations
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
|
||||||
|
kit: {
|
||||||
|
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||||
|
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||||
|
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||||
|
adapter: adapter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./webtransport.d.ts"
|
||||||
|
]
|
||||||
|
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||||
|
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||||
|
//
|
||||||
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
|
}
|
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
server: {
|
||||||
|
https: {
|
||||||
|
key: readFileSync('key.pem'),
|
||||||
|
cert: readFileSync('cert.pem'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
sveltekit(),
|
||||||
|
]
|
||||||
|
});
|
29392
webtransport.d.ts
vendored
Normal file
29392
webtransport.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue