From a520d52ad3cc563be9fd8d0cfdb377d01c086f6d Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 31 Aug 2025 14:50:56 +1000 Subject: [PATCH] Preallocate download streams (#229) * feat: pre-allocate streams for high-latency downloads * fix: update drop-base * fix: remove debug latency * fix: lint --- drop-base | 2 +- nuxt.config.ts | 6 +++-- server/api/v2/client/chunk.post.ts | 39 ++++++++++++++++++------------ server/middleware/latency.ts | 2 +- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/drop-base b/drop-base index 4c42edf..06bea06 160000 --- a/drop-base +++ b/drop-base @@ -1 +1 @@ -Subproject commit 4c42edf5adfa755c33bc8ce7bf1ddec87a0963a8 +Subproject commit 06bea063633ed4bf7513e29ad7149bc067561199 diff --git a/nuxt.config.ts b/nuxt.config.ts index 49cc4e3..e88def9 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -74,7 +74,8 @@ export default defineNuxtConfig({ vite: { plugins: [ - tailwindcss(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tailwindcss() as any, // only used in dev server, not build because nitro sucks // see build hook below viteStaticCopy({ @@ -84,7 +85,8 @@ export default defineNuxtConfig({ dest: "twemoji", }, ], - }), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any, ], }, diff --git a/server/api/v2/client/chunk.post.ts b/server/api/v2/client/chunk.post.ts index 8402203..648961b 100644 --- a/server/api/v2/client/chunk.post.ts +++ b/server/api/v2/client/chunk.post.ts @@ -9,7 +9,10 @@ const GetChunk = type({ files: type({ filename: "string", chunkIndex: "number", - }).array(), + }) + .array() + .atLeastLength(1) + .atMostLength(256), }).configure(throwingArktype); export default defineEventHandler(async (h3) => { @@ -46,21 +49,27 @@ export default defineEventHandler(async (h3) => { streamFiles.map((e) => e.end - e.start).join(","), ); // Non-standard header, but we're cool like that 😎 - for (const file of streamFiles) { - const gameReadStream = await libraryManager.readFile( - context.libraryId, - context.libraryPath, - context.versionName, - file.filename, - { start: file.start, end: file.end }, - ); - if (!gameReadStream) - throw createError({ - statusCode: 500, - statusMessage: "Failed to create read stream", - }); + const streams = await Promise.all( + streamFiles.map(async (file) => { + const gameReadStream = await libraryManager.readFile( + context.libraryId, + context.libraryPath, + context.versionName, + file.filename, + { start: file.start, end: file.end }, + ); + if (!gameReadStream) + throw createError({ + statusCode: 500, + statusMessage: "Failed to create read stream", + }); + return { ...file, stream: gameReadStream }; + }), + ); + + for (const file of streams) { let length = 0; - await gameReadStream.pipeTo( + await file.stream.pipeTo( new WritableStream({ write(chunk) { h3.node.res.write(chunk); diff --git a/server/middleware/latency.ts b/server/middleware/latency.ts index cfa2508..14cf5f7 100644 --- a/server/middleware/latency.ts +++ b/server/middleware/latency.ts @@ -1,3 +1,3 @@ export default defineEventHandler(async () => { - // await new Promise((r) => setTimeout(r, 700)); + // await new Promise((r) => setTimeout(r, 1400)); });