@nodecraft/rendermite 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/atlas/registry.d.ts +3 -3
- package/dist/atlas/sources.d.ts +1 -1
- package/dist/{blend-Qvrn6YgK.d.ts → blend-CyDIxCne.d.ts} +1 -1
- package/dist/cache-Cr2ON3RQ.js +66 -0
- package/dist/cli.js +12 -6
- package/dist/compose-CQCb2LVv.js +104 -0
- package/dist/descriptor-DWebtkI1.js +26 -0
- package/dist/{discovery-D89pVVJA.d.ts → discovery-wiJBGryo.d.ts} +1 -1
- package/dist/dynamic/generators.d.ts +1 -1
- package/dist/dynamic/generators.js +1 -1
- package/dist/{encode-3u49i-eD.js → encode-D6dQu2dJ.js} +16 -4
- package/dist/{encode-C5kdAm45.d.ts → encode-DaNQF28M.d.ts} +1 -1
- package/dist/{entity-fallback-DfL0_eyf.js → entity-fallback-BRCeGKBT.js} +1 -1
- package/dist/{face-CpVdnf-w.d.ts → face-BIYg14TE.d.ts} +10 -4
- package/dist/{face-D-N1amV4.js → face-DmCiHUd5.js} +23 -10
- package/dist/{generators-CcVRG30y.js → generators-BlG4KmtP.js} +1 -1
- package/dist/{generators-CCB8h0mP.d.ts → generators-DhRov4RB.d.ts} +1 -1
- package/dist/{geo-Dq2zve09.js → geo-Diky41Tw.js} +6 -3
- package/dist/index.d.ts +14 -14
- package/dist/index.js +11 -11
- package/dist/{item-styles-DJ3LWZS2.js → item-styles-upgEO1yP.js} +1 -1
- package/dist/items/discovery.d.ts +1 -1
- package/dist/{jar-6T3xdgaL.d.ts → jar-CFkA-VgT.d.ts} +2 -1
- package/dist/{jar-Bt3i0ls4.js → jar-D9z-X7tA.js} +12 -12
- package/dist/{legacy-reshape-CewShfF3.js → legacy-reshape-C05SfAPR.js} +1 -1
- package/dist/{loader-A-BZa8J4.js → loader-B8NJO7pR.js} +12 -3
- package/dist/{loader-CBzn3Koc.js → loader-CfXMxcEV.js} +37 -7
- package/dist/{loader-C-6QN3xv.d.ts → loader-D22zZME7.d.ts} +2 -2
- package/dist/{loader-TFFNV4Jl.d.ts → loader-Myy22TDL.d.ts} +8 -2
- package/dist/{manager-dfI7b9N4.js → manager-CkletR5K.js} +29 -2
- package/dist/{manager-CoZaeQTu.d.ts → manager-Cv1NeFyF.d.ts} +28 -1
- package/dist/models/loader.d.ts +1 -1
- package/dist/models/loader.js +1 -1
- package/dist/mods/prepare.d.ts +1 -1
- package/dist/mods/prepare.js +1 -1
- package/dist/mods/synthesize.d.ts +2 -2
- package/dist/mods/synthesize.js +1 -1
- package/dist/mods/tints.d.ts +2 -2
- package/dist/mods/tints.js +1 -1
- package/dist/output/encode.d.ts +1 -1
- package/dist/output/encode.js +1 -1
- package/dist/output/item-styles.js +1 -1
- package/dist/pipeline-BIk1RRIp.js +221 -0
- package/dist/pipeline-zq-yU3Te.d.ts +58 -0
- package/dist/pool-CKGp1qup.js +131 -0
- package/dist/{prepare-BeUpXwaP.js → prepare-BESRrh6K.js} +4 -4
- package/dist/{prepare-gfR1aIjt.d.ts → prepare-CPxO0kKz.d.ts} +1 -1
- package/dist/{prepare-C8wRjAXP.js → prepare-Cc8QZ3j0.js} +4 -4
- package/dist/{prepare-wfWtaz_X.d.ts → prepare-Cs6QDqie.d.ts} +1 -1
- package/dist/{rasterizer-BHWDrmGz.js → rasterizer-76qO8TDh.js} +42 -4
- package/dist/{rasterizer-Dh35qXbn.d.ts → rasterizer-yqJkicm1.d.ts} +21 -2
- package/dist/render/blend.d.ts +1 -1
- package/dist/render/compose.d.ts +2 -2
- package/dist/render/compose.js +1 -1
- package/dist/render/entity-fallback.d.ts +6 -6
- package/dist/render/entity-fallback.js +1 -1
- package/dist/render/face.d.ts +2 -2
- package/dist/render/face.js +1 -1
- package/dist/render/fluid-bucket.d.ts +2 -2
- package/dist/render/geo.d.ts +4 -4
- package/dist/render/geo.js +1 -1
- package/dist/render/legacy-tints.d.ts +2 -2
- package/dist/render/obj.d.ts +5 -5
- package/dist/render/obj.js +1 -1
- package/dist/render/pipeline.d.ts +2 -0
- package/dist/render/pipeline.js +2 -0
- package/dist/render/rasterizer.d.ts +2 -2
- package/dist/render/rasterizer.js +2 -2
- package/dist/render/renderer.d.ts +2 -2
- package/dist/render/renderer.js +1 -1
- package/dist/render/tints.d.ts +1 -1
- package/dist/{renderer-LDhBNHcl.js → renderer-CjT19tHd.js} +130 -53
- package/dist/{renderer-nbMCw1Pl.d.ts → renderer-DVsZHAvf.d.ts} +23 -6
- package/dist/resources/cache.d.ts +30 -0
- package/dist/resources/cache.js +2 -0
- package/dist/resources/descriptor.d.ts +2 -0
- package/dist/resources/descriptor.js +2 -0
- package/dist/resources/jar.d.ts +1 -1
- package/dist/resources/jar.js +1 -1
- package/dist/resources/manager.d.ts +1 -1
- package/dist/resources/manager.js +1 -1
- package/dist/resources/synthetic.d.ts +1 -1
- package/dist/resources/synthetic.js +1 -1
- package/dist/resources/zip-reader.d.ts +5 -0
- package/dist/resources/zip-reader.js +2 -0
- package/dist/run-BnICOF4I.js +209 -0
- package/dist/{run-BlSZw9Qk.d.ts → run-CKUtynTy.d.ts} +12 -2
- package/dist/run.d.ts +1 -1
- package/dist/run.js +1 -1
- package/dist/{sources-DH9dk3KF.d.ts → sources-B9X9w3qP.d.ts} +1 -1
- package/dist/{synthesize-CRgSEmEp.js → synthesize-3Q0aaOnK.js} +1 -1
- package/dist/{synthetic-CDsj--li.d.ts → synthetic-Qk-BSUXO.d.ts} +5 -1
- package/dist/{synthetic-DC2bSnGe.js → synthetic-ZAUjXT7E.js} +0 -0
- package/dist/textures/loader.d.ts +1 -1
- package/dist/textures/loader.js +1 -1
- package/dist/{tints-CuG6GwQJ.js → tints-B8UJmZMu.js} +2 -2
- package/dist/{tints-txt-8xI5.d.ts → tints-DLBExVWN.d.ts} +2 -2
- package/dist/versions/legacy-reshape.d.ts +1 -1
- package/dist/versions/legacy-reshape.js +1 -1
- package/dist/versions/prepare.d.ts +1 -1
- package/dist/versions/prepare.js +1 -1
- package/dist/workers/pool.d.ts +36 -5
- package/dist/workers/pool.js +2 -67
- package/dist/workers/render-worker.d.ts +15 -0
- package/dist/workers/render-worker.js +40 -0
- package/dist/zip-reader-BSbJKUT1.js +156 -0
- package/package.json +4 -2
- package/dist/compose-qjhyBvnS.js +0 -41
- package/dist/run-CfnWIRY4.js +0 -316
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
2
|
-
import { n as MapResourceManager } from "./synthetic-
|
|
2
|
+
import { n as MapResourceManager } from "./synthetic-ZAUjXT7E.js";
|
|
3
3
|
//#region src/mods/synthesize.ts
|
|
4
4
|
/**
|
|
5
5
|
* Model synthesis for mods that ship textures without item models, ported from
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ResourceLocator } from "./locator-jDbdUeTd.js";
|
|
2
|
-
import { a as ResourceManager, i as ResourceKindName } from "./manager-
|
|
2
|
+
import { a as ResourceManager, i as ResourceKindName, s as ResourceDescriptor } from "./manager-Cv1NeFyF.js";
|
|
3
3
|
//#region src/resources/synthetic.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* In-memory resources, used for era shims (synthesized `models/item/generated.json`,
|
|
@@ -7,6 +7,8 @@ import { a as ResourceManager, i as ResourceKindName } from "./manager-CoZaeQTu.
|
|
|
7
7
|
* mutating asset trees on disk like the old pipeline did.
|
|
8
8
|
*/
|
|
9
9
|
declare class MapResourceManager implements ResourceManager {
|
|
10
|
+
/** Rebuild from a describe() payload (worker side). */
|
|
11
|
+
static fromEntries(entries: Iterable<[string, Uint8Array]>): MapResourceManager;
|
|
10
12
|
private readonly entries;
|
|
11
13
|
set(kind: ResourceKindName, locator: string | ResourceLocator, content: Buffer | string): this;
|
|
12
14
|
getResource(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer | null>;
|
|
@@ -14,6 +16,7 @@ declare class MapResourceManager implements ResourceManager {
|
|
|
14
16
|
getAllResources(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer[]>;
|
|
15
17
|
listResources(kind: ResourceKindName, namespace: string): Promise<ResourceLocator[]>;
|
|
16
18
|
listNamespaces(): Promise<string[]>;
|
|
19
|
+
describe(): ResourceDescriptor;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* Restricts a manager to specific resource kinds — donor versions may only
|
|
@@ -29,6 +32,7 @@ declare class KindFilteredResourceManager implements ResourceManager {
|
|
|
29
32
|
getAllResources(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer[]>;
|
|
30
33
|
listResources(kind: ResourceKindName, namespace: string): Promise<ResourceLocator[]>;
|
|
31
34
|
listNamespaces(): Promise<string[]>;
|
|
35
|
+
describe(): ResourceDescriptor | null;
|
|
32
36
|
}
|
|
33
37
|
//#endregion
|
|
34
38
|
export { MapResourceManager as n, KindFilteredResourceManager as t };
|
|
Binary file
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TextureMeta, i as TextureLoader, n as AtlasResolverLike, o as TextureResolutionError, r as Texture, t as AnimatedTexture } from "../loader-
|
|
1
|
+
import { a as TextureMeta, i as TextureLoader, n as AtlasResolverLike, o as TextureResolutionError, r as Texture, t as AnimatedTexture } from "../loader-Myy22TDL.js";
|
|
2
2
|
export { AnimatedTexture, AtlasResolverLike, Texture, TextureLoader, TextureMeta, TextureResolutionError };
|
package/dist/textures/loader.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as TextureLoader, r as TextureResolutionError, t as AnimatedTexture } from "../loader-
|
|
1
|
+
import { n as TextureLoader, r as TextureResolutionError, t as AnimatedTexture } from "../loader-CfXMxcEV.js";
|
|
2
2
|
export { AnimatedTexture, TextureLoader, TextureResolutionError };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as modTintPackPath } from "./builtin-caYjESh0.js";
|
|
2
2
|
import { a as defaultProperties, o as evaluateItemModel } from "./definition-3MBm8ar_.js";
|
|
3
3
|
import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
|
|
4
|
-
import { t as FolderResourceManager } from "./manager-
|
|
5
|
-
import { n as MapResourceManager } from "./synthetic-
|
|
4
|
+
import { t as FolderResourceManager } from "./manager-CkletR5K.js";
|
|
5
|
+
import { n as MapResourceManager } from "./synthetic-ZAUjXT7E.js";
|
|
6
6
|
//#region src/mods/tints.ts
|
|
7
7
|
/**
|
|
8
8
|
* Tints for mod items whose colors live in client code rather than asset JSON.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as TextureLoader } from "./loader-
|
|
2
|
-
import {
|
|
1
|
+
import { i as TextureLoader } from "./loader-Myy22TDL.js";
|
|
2
|
+
import { r as TintColor } from "./face-BIYg14TE.js";
|
|
3
3
|
import { o as TintDefinition } from "./definition-BwX6Xxv0.js";
|
|
4
4
|
//#region src/render/tints.d.ts
|
|
5
5
|
declare class InvalidTinterDefinitionError extends Error {}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as reshapeLegacyAssets } from "../legacy-reshape-
|
|
1
|
+
import { t as reshapeLegacyAssets } from "../legacy-reshape-C05SfAPR.js";
|
|
2
2
|
export { reshapeLegacyAssets };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as compareVersions, i as PrepareOptions, n as EraName, o as eraForVersion, r as GEOMETRY_DONOR_VERSIONS, s as prepareVersionResources, t as Era } from "../prepare-
|
|
1
|
+
import { a as compareVersions, i as PrepareOptions, n as EraName, o as eraForVersion, r as GEOMETRY_DONOR_VERSIONS, s as prepareVersionResources, t as Era } from "../prepare-Cs6QDqie.js";
|
|
2
2
|
export { Era, EraName, GEOMETRY_DONOR_VERSIONS, PrepareOptions, compareVersions, eraForVersion, prepareVersionResources };
|
package/dist/versions/prepare.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS, r as compareVersions, t as Era } from "../prepare-
|
|
1
|
+
import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS, r as compareVersions, t as Era } from "../prepare-Cc8QZ3j0.js";
|
|
2
2
|
export { Era, GEOMETRY_DONOR_VERSIONS, compareVersions, eraForVersion, prepareVersionResources };
|
package/dist/workers/pool.d.ts
CHANGED
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Worker-thread pool for parallel item rendering. Design goals born from the
|
|
4
4
|
* PHP tool's failure modes (docs/research/mod-failure-taxonomy.md): errors
|
|
5
|
-
* cross the worker boundary as structured data, a dead worker's
|
|
6
|
-
*
|
|
5
|
+
* cross the worker boundary as structured data, a dead worker's in-flight items
|
|
6
|
+
* are reported rather than orphaned, and results stream back so output lands
|
|
7
|
+
* incrementally.
|
|
8
|
+
*
|
|
9
|
+
* Workers spawn as soon as the pool is constructed, so their asset indexing
|
|
10
|
+
* overlaps the caller's own discovery pass; each holds several tasks at once so
|
|
11
|
+
* native encoding overlaps the next item's rasterization; and the queue drains
|
|
12
|
+
* through a cursor, because shift() on a 400k-item array is quadratic.
|
|
7
13
|
*/
|
|
8
14
|
interface WorkerTask {
|
|
9
15
|
id: string;
|
|
@@ -16,8 +22,33 @@ interface WorkerResult<T> {
|
|
|
16
22
|
interface PoolOptions {
|
|
17
23
|
workerUrl: URL;
|
|
18
24
|
workerData: unknown;
|
|
19
|
-
|
|
25
|
+
/** Worker threads to spawn. */
|
|
26
|
+
workers: number;
|
|
27
|
+
/** Tasks held by each worker at once (default 2). */
|
|
28
|
+
inFlight?: number;
|
|
29
|
+
}
|
|
30
|
+
declare class WorkerRenderPool<T> {
|
|
31
|
+
private readonly options;
|
|
32
|
+
private readonly inFlightLimit;
|
|
33
|
+
private readonly workers;
|
|
34
|
+
private tasks;
|
|
35
|
+
private cursor;
|
|
36
|
+
private started;
|
|
37
|
+
private onResult;
|
|
38
|
+
private settle;
|
|
39
|
+
private settled;
|
|
40
|
+
/** Workers dying before completing anything mean a bad config, not bad luck. */
|
|
41
|
+
private startupFailures;
|
|
42
|
+
constructor(options: PoolOptions);
|
|
43
|
+
private spawn;
|
|
44
|
+
private pump;
|
|
45
|
+
private retire;
|
|
46
|
+
private fail;
|
|
47
|
+
private finish;
|
|
48
|
+
private abort;
|
|
49
|
+
/** Run every task, calling back as results arrive. Terminates the pool after. */
|
|
50
|
+
run(tasks: WorkerTask[], onResult: (result: WorkerResult<T>) => void): Promise<void>;
|
|
51
|
+
close(): Promise<void>;
|
|
20
52
|
}
|
|
21
|
-
declare function runPool<T>(tasks: WorkerTask[], options: PoolOptions, onResult: (result: WorkerResult<T>) => void): Promise<void>;
|
|
22
53
|
//#endregion
|
|
23
|
-
export { PoolOptions, WorkerResult, WorkerTask
|
|
54
|
+
export { PoolOptions, WorkerRenderPool, WorkerResult, WorkerTask };
|
package/dist/workers/pool.js
CHANGED
|
@@ -1,67 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Worker-thread pool for parallel item rendering. Design goals born from the
|
|
5
|
-
* PHP tool's failure modes (docs/research/mod-failure-taxonomy.md): errors
|
|
6
|
-
* cross the worker boundary as structured data, a dead worker's queue items are
|
|
7
|
-
* requeued not orphaned, and results stream back so output lands incrementally.
|
|
8
|
-
*/
|
|
9
|
-
async function runPool(tasks, options, onResult) {
|
|
10
|
-
const queue = [...tasks];
|
|
11
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
12
|
-
await new Promise((resolve) => {
|
|
13
|
-
let active = 0;
|
|
14
|
-
const spawn = () => {
|
|
15
|
-
const worker = new Worker(options.workerUrl, { workerData: options.workerData });
|
|
16
|
-
active++;
|
|
17
|
-
const next = () => {
|
|
18
|
-
const task = queue.shift();
|
|
19
|
-
if (task === void 0) {
|
|
20
|
-
worker.terminate().catch(() => {});
|
|
21
|
-
active--;
|
|
22
|
-
if (active === 0) resolve();
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
inFlight.set(worker, task);
|
|
26
|
-
worker.postMessage(task);
|
|
27
|
-
};
|
|
28
|
-
worker.on("message", (message) => {
|
|
29
|
-
inFlight.delete(worker);
|
|
30
|
-
onResult(message);
|
|
31
|
-
next();
|
|
32
|
-
});
|
|
33
|
-
worker.on("error", (err) => {
|
|
34
|
-
const task = inFlight.get(worker);
|
|
35
|
-
inFlight.delete(worker);
|
|
36
|
-
if (task) onResult({
|
|
37
|
-
id: task.id,
|
|
38
|
-
error: `Worker crashed: ${err.message}`
|
|
39
|
-
});
|
|
40
|
-
active--;
|
|
41
|
-
if (queue.length > 0) spawn();
|
|
42
|
-
else if (active === 0) resolve();
|
|
43
|
-
});
|
|
44
|
-
worker.on("exit", (code) => {
|
|
45
|
-
const task = inFlight.get(worker);
|
|
46
|
-
if (task && code !== 0) {
|
|
47
|
-
inFlight.delete(worker);
|
|
48
|
-
onResult({
|
|
49
|
-
id: task.id,
|
|
50
|
-
error: `Worker exited with code ${code}`
|
|
51
|
-
});
|
|
52
|
-
active--;
|
|
53
|
-
if (queue.length > 0) spawn();
|
|
54
|
-
else if (active === 0) resolve();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
next();
|
|
58
|
-
};
|
|
59
|
-
if (queue.length === 0) {
|
|
60
|
-
resolve();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
for (let i = 0; i < Math.min(options.concurrency, queue.length); i++) spawn();
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
//#endregion
|
|
67
|
-
export { runPool };
|
|
1
|
+
import { t as WorkerRenderPool } from "../pool-CKGp1qup.js";
|
|
2
|
+
export { WorkerRenderPool };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { s as ResourceDescriptor } from "../manager-Cv1NeFyF.js";
|
|
2
|
+
import { n as ItemRenderOutcome, r as PipelineSettings } from "../pipeline-zq-yU3Te.js";
|
|
3
|
+
//#region src/workers/render-worker.d.ts
|
|
4
|
+
interface RenderWorkerData {
|
|
5
|
+
resources: ResourceDescriptor;
|
|
6
|
+
settings: PipelineSettings;
|
|
7
|
+
/** libvips threads per worker; the pool already saturates the CPU. */
|
|
8
|
+
sharpConcurrency: number;
|
|
9
|
+
}
|
|
10
|
+
interface RenderWorkerTask {
|
|
11
|
+
id: string;
|
|
12
|
+
}
|
|
13
|
+
type RenderWorkerOutcome = ItemRenderOutcome;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { RenderWorkerData, RenderWorkerOutcome, RenderWorkerTask };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { t as ItemPipeline } from "../pipeline-BIk1RRIp.js";
|
|
2
|
+
import { n as restoreResources } from "../descriptor-DWebtkI1.js";
|
|
3
|
+
import sharp from "sharp";
|
|
4
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
5
|
+
//#region src/workers/render-worker.ts
|
|
6
|
+
/**
|
|
7
|
+
* Worker entry: rebuilds the run's resource tree from its descriptor, then
|
|
8
|
+
* renders, encodes and writes items on demand. Items are written here rather
|
|
9
|
+
* than shipped back as buffers — the main thread only needs the outcome, and
|
|
10
|
+
* the file write is the run's success signal either way.
|
|
11
|
+
*
|
|
12
|
+
* Messages in are `{ id }`; messages out are `{ id, result }` or `{ id, error }`.
|
|
13
|
+
* Errors cross as strings because Error instances lose their type across the
|
|
14
|
+
* structured-clone boundary.
|
|
15
|
+
*/
|
|
16
|
+
if (!parentPort) throw new Error("render-worker must be started as a worker thread");
|
|
17
|
+
const port = parentPort;
|
|
18
|
+
const data = workerData;
|
|
19
|
+
sharp.concurrency(data.sharpConcurrency);
|
|
20
|
+
const pipeline = await ItemPipeline.create(restoreResources(data.resources), data.settings);
|
|
21
|
+
async function handle(task) {
|
|
22
|
+
try {
|
|
23
|
+
const result = await pipeline.renderItem(task.id);
|
|
24
|
+
port.postMessage({
|
|
25
|
+
id: task.id,
|
|
26
|
+
result
|
|
27
|
+
});
|
|
28
|
+
} catch (err) {
|
|
29
|
+
port.postMessage({
|
|
30
|
+
id: task.id,
|
|
31
|
+
error: err.message
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
port.on("message", (task) => {
|
|
36
|
+
handle(task).catch(() => {});
|
|
37
|
+
});
|
|
38
|
+
port.postMessage({ ready: true });
|
|
39
|
+
//#endregion
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { close, createReadStream, fstat, open, read } from "node:fs";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
import { RandomAccessReader, fromRandomAccessReader, open as open$1 } from "yauzl";
|
|
4
|
+
//#region src/resources/zip-reader.ts
|
|
5
|
+
/**
|
|
6
|
+
* Opens a zip with its central directory held in memory.
|
|
7
|
+
*
|
|
8
|
+
* yauzl reads each central-directory record with its own small read, so a
|
|
9
|
+
* 26,000-entry client jar costs ~26,000 syscalls and well over a second before
|
|
10
|
+
* the first icon renders. The directory is one contiguous region, so reading it
|
|
11
|
+
* whole and serving those reads from the buffer makes indexing ~6× faster while
|
|
12
|
+
* keeping entry data itself on disk — only the directory stays resident.
|
|
13
|
+
*
|
|
14
|
+
* Anything unexpected (zip64, a truncated archive, no end-of-directory record)
|
|
15
|
+
* falls back to yauzl's own open, which is correct for every archive.
|
|
16
|
+
*/
|
|
17
|
+
const EOCD_SIGNATURE = 101010256;
|
|
18
|
+
/** End-of-directory record plus the largest possible archive comment. */
|
|
19
|
+
const EOCD_SEARCH_BYTES = 65557;
|
|
20
|
+
const ZIP64_SENTINEL = 4294967295;
|
|
21
|
+
/** Above this, a range streams from its own descriptor instead of one buffer. */
|
|
22
|
+
const MAX_BUFFERED_READ_BYTES = 33554432;
|
|
23
|
+
/** Locate the central directory from the end-of-directory record. */
|
|
24
|
+
function findCentralDirectory(tail, tailStart) {
|
|
25
|
+
for (let index = tail.length - 22; index >= 0; index--) {
|
|
26
|
+
if (tail.readUInt32LE(index) !== EOCD_SIGNATURE) continue;
|
|
27
|
+
const size = tail.readUInt32LE(index + 12);
|
|
28
|
+
const offset = tail.readUInt32LE(index + 16);
|
|
29
|
+
if (size === ZIP64_SENTINEL || offset === ZIP64_SENTINEL) return null;
|
|
30
|
+
if (offset + size > tailStart + index) return null;
|
|
31
|
+
return {
|
|
32
|
+
offset,
|
|
33
|
+
size
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** Promisified positional read; a raw descriptor avoids FileHandle's close-on-GC. */
|
|
39
|
+
async function readAt(fd, buffer, offset, length, position) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
read(fd, buffer, offset, length, position, (err, bytesRead) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
resolve(bytesRead);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
var BufferedDirectoryReader = class extends RandomAccessReader {
|
|
51
|
+
path;
|
|
52
|
+
fd;
|
|
53
|
+
directory;
|
|
54
|
+
directoryStart;
|
|
55
|
+
constructor(path, fd, directory, directoryStart) {
|
|
56
|
+
super();
|
|
57
|
+
this.path = path;
|
|
58
|
+
this.fd = fd;
|
|
59
|
+
this.directory = directory;
|
|
60
|
+
this.directoryStart = directoryStart;
|
|
61
|
+
}
|
|
62
|
+
_readStreamForRange(start, end) {
|
|
63
|
+
const directoryEnd = this.directoryStart + this.directory.length;
|
|
64
|
+
if (start >= this.directoryStart && end <= directoryEnd) return Readable.from([this.directory.subarray(start - this.directoryStart, end - this.directoryStart)]);
|
|
65
|
+
const length = end - start;
|
|
66
|
+
if (length > MAX_BUFFERED_READ_BYTES) return createReadStream(this.path, {
|
|
67
|
+
start,
|
|
68
|
+
end: end - 1
|
|
69
|
+
});
|
|
70
|
+
const { fd } = this;
|
|
71
|
+
return Readable.from((async function* read() {
|
|
72
|
+
const buffer = Buffer.alloc(length);
|
|
73
|
+
let filled = 0;
|
|
74
|
+
while (filled < length) {
|
|
75
|
+
const bytesRead = await readAt(fd, buffer, filled, length - filled, start + filled);
|
|
76
|
+
if (bytesRead === 0) break;
|
|
77
|
+
filled += bytesRead;
|
|
78
|
+
}
|
|
79
|
+
yield filled === length ? buffer : buffer.subarray(0, filled);
|
|
80
|
+
})());
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* yauzl reads each directory record through here. Copying from the buffer
|
|
84
|
+
* skips the per-read stream pipeline entirely, which is most of the cost of
|
|
85
|
+
* indexing tens of thousands of entries.
|
|
86
|
+
*/
|
|
87
|
+
read(buffer, offset, length, position, callback) {
|
|
88
|
+
const directoryEnd = this.directoryStart + this.directory.length;
|
|
89
|
+
if (position >= this.directoryStart && position + length <= directoryEnd) {
|
|
90
|
+
const from = position - this.directoryStart;
|
|
91
|
+
this.directory.copy(buffer, offset, from, from + length);
|
|
92
|
+
queueMicrotask(() => callback(null));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
readAt(this.fd, buffer, offset, length, position).then(() => callback(null)).catch(callback);
|
|
96
|
+
}
|
|
97
|
+
/** yauzl's close hook, reached when the ZipFile releases its last reference. */
|
|
98
|
+
close(callback) {
|
|
99
|
+
close(this.fd, (err) => callback(err ?? null));
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
async function openZip(zipPath) {
|
|
103
|
+
const fallback = async () => new Promise((resolve, reject) => {
|
|
104
|
+
open$1(zipPath, {
|
|
105
|
+
lazyEntries: true,
|
|
106
|
+
autoClose: false
|
|
107
|
+
}, (err, zip) => {
|
|
108
|
+
if (err) {
|
|
109
|
+
reject(err);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
resolve(zip);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
let fd;
|
|
116
|
+
try {
|
|
117
|
+
fd = await new Promise((resolve, reject) => {
|
|
118
|
+
open(zipPath, "r", (err, handle) => err ? reject(err) : resolve(handle));
|
|
119
|
+
});
|
|
120
|
+
} catch {
|
|
121
|
+
return fallback();
|
|
122
|
+
}
|
|
123
|
+
const abandon = async () => {
|
|
124
|
+
close(fd, () => {});
|
|
125
|
+
return fallback();
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
const size = await new Promise((resolve, reject) => {
|
|
129
|
+
fstat(fd, (err, stats) => err ? reject(err) : resolve(stats.size));
|
|
130
|
+
});
|
|
131
|
+
const tailStart = Math.max(0, size - EOCD_SEARCH_BYTES);
|
|
132
|
+
const tail = Buffer.alloc(size - tailStart);
|
|
133
|
+
await readAt(fd, tail, 0, tail.length, tailStart);
|
|
134
|
+
const central = findCentralDirectory(tail, tailStart);
|
|
135
|
+
if (!central) return await abandon();
|
|
136
|
+
const directory = Buffer.alloc(central.size);
|
|
137
|
+
await readAt(fd, directory, 0, central.size, central.offset);
|
|
138
|
+
const reader = new BufferedDirectoryReader(zipPath, fd, directory, central.offset);
|
|
139
|
+
return await new Promise((resolve, reject) => {
|
|
140
|
+
fromRandomAccessReader(reader, size, {
|
|
141
|
+
lazyEntries: true,
|
|
142
|
+
autoClose: false
|
|
143
|
+
}, (err, zip) => {
|
|
144
|
+
if (err) {
|
|
145
|
+
reject(err);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
resolve(zip);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
return abandon();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
export { openZip as t };
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodecraft/rendermite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Minecraft item icon renderer: renders inventory-identical item icons from vanilla and modded assets across game versions, outputting optimized PNG/WebP/AVIF plus CSS and a JSON item library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author": "
|
|
7
|
+
"author": "Nodecraft",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/nodecraft/rendermite.git"
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"lint": "eslint . --fix",
|
|
43
43
|
"lint:ci": "eslint .",
|
|
44
44
|
"test": "vitest run",
|
|
45
|
+
"bench": "vitest bench --config vitest.bench.config.ts --run",
|
|
46
|
+
"bench:report": "vitest bench --config vitest.bench.config.ts --run --outputJson bench-results.json && node scripts/bench-report.ts",
|
|
45
47
|
"test:watch": "vitest",
|
|
46
48
|
"typecheck": "tsc --noEmit",
|
|
47
49
|
"semantic-release": "semantic-release"
|
package/dist/compose-qjhyBvnS.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { t as blendPixel } from "./blend-CHP2-j9U.js";
|
|
2
|
-
//#region src/render/compose.ts
|
|
3
|
-
/**
|
|
4
|
-
* Per-pixel painter's compositing, mirroring upstream Model::combineFaceImages:
|
|
5
|
-
* at each pixel, faces sorted ascending by that pixel's depth value (0 where the
|
|
6
|
-
* rasterizer never wrote depth — the overdraw band relies on sorting beneath),
|
|
7
|
-
* then source-over blended in that order when alpha > 0.
|
|
8
|
-
*/
|
|
9
|
-
function combineFaceBuffers(width, height, faces) {
|
|
10
|
-
const out = new Float64Array(width * height * 4);
|
|
11
|
-
const order = [...faces];
|
|
12
|
-
for (let pixel = 0; pixel < width * height; pixel++) {
|
|
13
|
-
const x = pixel % width;
|
|
14
|
-
const y = Math.floor(pixel / width);
|
|
15
|
-
const localPixel = (face) => {
|
|
16
|
-
const localX = x - face.x;
|
|
17
|
-
const localY = y - face.y;
|
|
18
|
-
return localX >= 0 && localX < face.width && localY >= 0 && localY < face.height ? localY * face.width + localX : null;
|
|
19
|
-
};
|
|
20
|
-
order.sort((a, b) => {
|
|
21
|
-
const aPixel = localPixel(a);
|
|
22
|
-
const bPixel = localPixel(b);
|
|
23
|
-
return (aPixel === null ? 0 : a.depths[aPixel]) - (bPixel === null ? 0 : b.depths[bPixel]);
|
|
24
|
-
});
|
|
25
|
-
const offset = pixel * 4;
|
|
26
|
-
for (const face of order) {
|
|
27
|
-
const facePixel = localPixel(face);
|
|
28
|
-
if (facePixel === null) continue;
|
|
29
|
-
const faceOffset = facePixel * 4;
|
|
30
|
-
const alpha = face.colors[faceOffset + 3];
|
|
31
|
-
if (alpha > 0) blendPixel(out, offset, face.colors[faceOffset], face.colors[faceOffset + 1], face.colors[faceOffset + 2], alpha);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return {
|
|
35
|
-
width,
|
|
36
|
-
height,
|
|
37
|
-
data: out
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
export { combineFaceBuffers as t };
|