diff --git a/msagent.js/core/package.json b/msagent.js/core/package.json index 405542b..469b4b7 100644 --- a/msagent.js/core/package.json +++ b/msagent.js/core/package.json @@ -1,5 +1,6 @@ { "name": "@msagent.js/core", + "description": "msagent.js core. Provides structures, parsing, and bitmap utilties for higher level usage", "version": "0.1.0", "packageManager": "yarn@4.2.2", "devDependencies": { diff --git a/msagent.js/core/src/decompress.ts b/msagent.js/core/src/decompress.ts index f02f49b..449adcd 100644 --- a/msagent.js/core/src/decompress.ts +++ b/msagent.js/core/src/decompress.ts @@ -1,21 +1,19 @@ -// Please note that the "meaningless" shifts of 0 are to force -// the value to be a 32-bit integer. Do not remove them. +import { WasmModule, WebassemblyOwnMemoryExports } from './wasm_module'; -let compressWasm: WebAssembly.WebAssemblyInstantiatedSource; - -interface CompressWasmExports { - memory: WebAssembly.Memory; - agentDecompressWASM: any; +// WASM exports for the decompression module. +interface CompressWasmExports extends WebassemblyOwnMemoryExports { + agentDecompressWASM: (pSource: number, sourceLen: number, pDest: number, destLen: number) => number; } +let compressWasm = new WasmModule(new URL('decompress.wasm', import.meta.url)); + // Initalize the decompression module export async function compressInit() { - let url = new URL('decompress.wasm', import.meta.url); - compressWasm = await WebAssembly.instantiateStreaming(fetch(url)); + await compressWasm.Initalize(); } function compressWasmGetExports() { - return compressWasm.instance.exports as any as CompressWasmExports; + return compressWasm.Exports; } function compressWASMGetMemory(): WebAssembly.Memory { @@ -32,14 +30,9 @@ function compressWASMGetMemory(): WebAssembly.Memory { export function compressDecompress(src: Uint8Array, dest: Uint8Array) { // Grow the WASM heap if needed. Funnily enough, this code is never hit in most // ACSes, so IDK if it's even needed - let memory = compressWASMGetMemory(); - if (memory.buffer.byteLength < src.length + dest.length) { - // A WebAssembly page is 64kb, so we need to grow at least that much - let npages = Math.floor((src.length + dest.length) / 65535) + 1; - console.log('Need to grow WASM heap', npages, 'pages', '(current byteLength is', memory.buffer.byteLength, ', we need', src.length + dest.length, ')'); - memory.grow(npages); - } + compressWasm.growHeapTo(src.length + dest.length); + let memory = compressWASMGetMemory(); let copyBuffer = new Uint8Array(memory.buffer); // Copy source to memory[0]. This will make things a bit simpler @@ -50,6 +43,7 @@ export function compressDecompress(src: Uint8Array, dest: Uint8Array) { if (nrBytesDecompressed != dest.length) throw new Error(`decompression failed: ${nrBytesDecompressed} != ${dest.length}`); - // Dest will be memory[src.length..dest.length] + // The uncompressed data is located at memory[src.length..dest.length]. + // Copy it into the destination buffer. dest.set(copyBuffer.slice(src.length, src.length + dest.length), 0); } diff --git a/msagent.js/core/src/image.ts b/msagent.js/core/src/image.ts index 7e1e02d..5f3cda6 100644 --- a/msagent.js/core/src/image.ts +++ b/msagent.js/core/src/image.ts @@ -38,7 +38,8 @@ export function imageDrawToBuffer(imageEntry: AcsImageEntry, palette: RGBAColor[ bufStream.seek(dwAlign(bufStream.tell()), SeekDir.BEG); } - // Next, draw the rows converted to RGBA, top down (so it's drawn as you'd expect) + // Next, draw the rows converted to RGBA, top down (so it's drawn correctly, + // and in the RGBA format we want to return) for (let y = 0; y < imageEntry.image.height - 1; ++y) { let row = rows[y]; for (let x = 0; x < imageEntry.image.width; ++x) { diff --git a/msagent.js/core/src/wasm_module.ts b/msagent.js/core/src/wasm_module.ts new file mode 100644 index 0000000..53438f3 --- /dev/null +++ b/msagent.js/core/src/wasm_module.ts @@ -0,0 +1,44 @@ +export interface WebassemblyOwnMemoryExports extends WebAssembly.Exports { + memory: WebAssembly.Memory; +} + +// A helper for WASM modules. +// This currently only works for modules that export their own memory, +// and do not import it. +// +// This covers all of the modules in the core so far, so +// this is perfectly fine, but it's still worth noting. +export class WasmModule { + private module: WebAssembly.WebAssemblyInstantiatedSource | null = null; + private url: URL; + + constructor(url: URL) { + this.url = url; + } + + async Initalize(): Promise { + this.module = await WebAssembly.instantiateStreaming(fetch(this.url)); + } + + get Module() { + if (this.module == null) throw new Error('WasmModule has not been initalized'); + return this.module; + } + + get Exports() { + let module = this.Module; + return module.instance.exports as TExports; + } + + // Grows the WebAssembly heap if required. + growHeapTo(newSize: number) { + let exports = this.Exports; + let memory: WebAssembly.Memory = exports.memory; + if (memory.buffer.byteLength < newSize) { + // A WebAssembly page is 64kb, so we need to grow at least a single page, + // even if it would be relatively wasteful to do so. + let npages = Math.floor(newSize / 65535) + 1; + memory.grow(npages); + } + } +} diff --git a/msagent.js/web/src/character.ts b/msagent.js/web/src/character.ts index a089783..1d5cb99 100644 --- a/msagent.js/web/src/character.ts +++ b/msagent.js/web/src/character.ts @@ -5,6 +5,11 @@ import { Agent } from './agent.js'; let acsDataCache = new Map(); // Purges the ACS cache. +// +// FIXME: A smarter way to do this would probably be instead reference count +// AcsData instances, then when an agent is disposed decrement reference count +// (or leave it at 0). Once it's 0 then a globally running interval can remove +// all keys that have no refcount. export function agentPurgeACSCache() { acsDataCache.clear(); } diff --git a/yarn.lock b/yarn.lock index 43eed7e..8863838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3619,11 +3619,11 @@ __metadata: linkType: hard "node-abi@npm:^3.3.0": - version: 3.65.0 - resolution: "node-abi@npm:3.65.0" + version: 3.67.0 + resolution: "node-abi@npm:3.67.0" dependencies: semver: "npm:^7.3.5" - checksum: 10c0/112672015d8f27d6be2f18d64569f28f5d6a15a94cc510da513c69c3e3ab5df6dac196ef13ff115a8fadb69b554974c47ef89b4f6350a2b02de2bca5c23db1e5 + checksum: 10c0/72ce2edbdfb84745bc201a4e48aa7146fd88a0d2c80046b6b17f28439c9a7683eab846f40f1e819349c31f7d9331ed5c50d1e741208d938dd5f38b29cab2275e languageName: node linkType: hard @@ -4246,7 +4246,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.4, semver@npm:^7.6.0": +"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.2 resolution: "semver@npm:7.6.2" bin: @@ -4255,6 +4255,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.8": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + languageName: node + linkType: hard + "seq-queue@npm:^0.0.5": version: 0.0.5 resolution: "seq-queue@npm:0.0.5"