@onimaxxing/worldgen 0.15.0
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 +442 -0
- package/editor_settings.d.ts +1034 -0
- package/index.d.ts +682 -0
- package/index.js +286 -0
- package/package.json +35 -0
- package/parallel/LICENSE +21 -0
- package/parallel/index.js +13 -0
- package/parallel/oni_wasm.d.ts +852 -0
- package/parallel/oni_wasm.js +2368 -0
- package/parallel/oni_wasm_bg.wasm +0 -0
- package/parallel/oni_wasm_bg.wasm.d.ts +99 -0
- package/parallel/snippets/wasm-bindgen-rayon-38edf6e439f6d70d/src/workerHelpers.js +107 -0
- package/serial/LICENSE +21 -0
- package/serial/oni_wasm.d.ts +346 -0
- package/serial/oni_wasm.js +1109 -0
- package/serial/oni_wasm_bg.wasm +0 -0
- package/serial/oni_wasm_bg.wasm.d.ts +47 -0
- package/yaml.d.ts +19 -0
- package/yaml.js +97 -0
package/README.md
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
# @onimaxxing/worldgen
|
|
2
|
+
|
|
3
|
+
A WASM build of the Oxygen Not Included worldgen engine. Returns the
|
|
4
|
+
same cluster the game would generate for a given coordinate.
|
|
5
|
+
|
|
6
|
+
## What it covers
|
|
7
|
+
|
|
8
|
+
- Cluster layout and starmap (world positions, POIs, rocket destinations).
|
|
9
|
+
- Per-world element grid, biome polygons, and world traits.
|
|
10
|
+
- Geyser, building, pickupable, and other entity spawn positions.
|
|
11
|
+
- Per-cell mass, temperature, and disease (raw after `worldgen.generate`; settled after `worldgen.advance`).
|
|
12
|
+
- Both basegame and Spaced Out clusters, including story traits, mixing codes, and DLC toggles.
|
|
13
|
+
|
|
14
|
+
Output is verified cell-by-cell against snapshots captured from the
|
|
15
|
+
running game, with a slight modification to force determinism during
|
|
16
|
+
the physics pass. The physics pass itself (temperatures, gas/liquid
|
|
17
|
+
displacement, critter and plant placement) is exposed as
|
|
18
|
+
`worldgen.advance`.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Browsers + ESM bundlers
|
|
24
|
+
npm install @onimaxxing/worldgen
|
|
25
|
+
|
|
26
|
+
# Node scripts, CLIs, SSR
|
|
27
|
+
npm install @onimaxxing/worldgen-node
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Same API on both. The web package ships both a serial build and a
|
|
31
|
+
parallel (rayon + `SharedArrayBuffer`) build; `init()` feature-detects
|
|
32
|
+
the host and picks one. The Node package is serial-only.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
**Browsers:**
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import init, { worldgen } from
|
|
40
|
+
'@onimaxxing/worldgen';
|
|
41
|
+
|
|
42
|
+
await init();
|
|
43
|
+
const map = worldgen.generate('V-SNDST-C-42-0-4A-MUWF1');
|
|
44
|
+
// map.worlds[0].element_idx element grid, width * height
|
|
45
|
+
// map.worlds[0].biome_cells overworld biome polygons
|
|
46
|
+
// map.worlds[0].geysers geyser spawn positions
|
|
47
|
+
// map.element_table element names by index
|
|
48
|
+
// map.starmap hex grid world locations
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Node:**
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// ESM
|
|
55
|
+
import { worldgen } from
|
|
56
|
+
'@onimaxxing/worldgen-node';
|
|
57
|
+
|
|
58
|
+
const map = worldgen.generate('V-SNDST-C-42-0-4A-MUWF1');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// CommonJS
|
|
63
|
+
const { worldgen } =
|
|
64
|
+
require('@onimaxxing/worldgen-node');
|
|
65
|
+
|
|
66
|
+
const map = worldgen.generate('V-SNDST-C-42-0-4A-MUWF1');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`init()` is a no-op on Node (WASM is loaded synchronously at import
|
|
70
|
+
time), so the same code runs on both targets.
|
|
71
|
+
|
|
72
|
+
Types ship in the package `.d.ts`:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import type { MapData, SettleSnapshot } from
|
|
76
|
+
'@onimaxxing/worldgen';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Parallel vs serial (web only)
|
|
80
|
+
|
|
81
|
+
The web package ships two complete wasm-bindgen builds under
|
|
82
|
+
`serial/` and `parallel/`. `init()` picks one at runtime:
|
|
83
|
+
|
|
84
|
+
- **Parallel:** loaded when `globalThis.crossOriginIsolated === true`
|
|
85
|
+
and `SharedArrayBuffer` is available. Runs the settle sim across a
|
|
86
|
+
rayon worker pool (`navigator.hardwareConcurrency` workers by
|
|
87
|
+
default; override with `init({ threads: N })`). Requires COOP/COEP
|
|
88
|
+
response headers on the page.
|
|
89
|
+
- **Serial:** loaded otherwise. No worker pool, no shared memory, no
|
|
90
|
+
headers required.
|
|
91
|
+
|
|
92
|
+
### Loading the WASM binary
|
|
93
|
+
|
|
94
|
+
`await init()` with no arguments resolves the `.wasm` via the
|
|
95
|
+
bundler-rewritten `new URL('oni_wasm_bg.wasm', import.meta.url)`
|
|
96
|
+
pattern inside the selected build. For hosts that don't rewrite
|
|
97
|
+
that pattern, pass the URL explicitly:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// Served from your own origin
|
|
101
|
+
await init({ module_or_path: '/wasm/oni_wasm_bg.wasm' });
|
|
102
|
+
|
|
103
|
+
// Pre-fetched bytes or a compiled WebAssembly.Module
|
|
104
|
+
await init({ module_or_path: await fetch(url) });
|
|
105
|
+
await init({ module_or_path: myWebAssemblyModule });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The URL is interpreted by the selected build; to pin a specific
|
|
109
|
+
`.wasm`, also pass `threads: 'serial'`.
|
|
110
|
+
|
|
111
|
+
### Coordinates
|
|
112
|
+
|
|
113
|
+
Standard game coords. Examples:
|
|
114
|
+
|
|
115
|
+
- `SNDST-A-42-0-0-0` basegame Sandstone, seed 42, no stories or mixing
|
|
116
|
+
- `V-SNDST-C-42-0-4A-MUWF1` Spaced Out Vanilla Sandstone, all stories and mixing
|
|
117
|
+
- `CER-C-100-0-4A-MUWF1` Spaced Out Ceres, seed 100
|
|
118
|
+
|
|
119
|
+
## API
|
|
120
|
+
|
|
121
|
+
The public surface is a default `init` export and a `worldgen`
|
|
122
|
+
singleton wrapping the one-slot cluster cache. `worldgen.generate(coord)`
|
|
123
|
+
populates the cache; every other method operates on whatever's
|
|
124
|
+
currently in it.
|
|
125
|
+
|
|
126
|
+
### Generation
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
worldgen.generate(coord: string): MapData
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Generate a cluster from a game coordinate and return the decoded
|
|
133
|
+
map (see [`MapData` shape](#mapdata-shape)). Caches the cluster
|
|
134
|
+
internally, replacing anything previously cached.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
worldgen.entities(): EntitySpawners
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Re-run ambient mob spawning against the cached cluster's current
|
|
141
|
+
cell state and return the refreshed entity lists. Call after
|
|
142
|
+
`worldgen.advance` chunks when settled liquid/gas flow may have
|
|
143
|
+
invalidated earlier mob placements. Requires a prior
|
|
144
|
+
`worldgen.generate`. Template-placed entities (geysers, oil wells,
|
|
145
|
+
props) persist across calls.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
worldgen.version(): string
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The ONI game build this WASM was baselined against, e.g. `"737195"`.
|
|
152
|
+
|
|
153
|
+
### Settle simulation
|
|
154
|
+
|
|
155
|
+
Worldgen takes hundreds of milliseconds; settling takes several
|
|
156
|
+
seconds. The API is split so you can show a preview first and fill
|
|
157
|
+
in settled data progressively.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
worldgen.advance(targetTick: number): SettleSnapshot
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Advance the cached cluster's settle sim to `targetTick` (in
|
|
164
|
+
`1..=499`) and return a decoded snapshot at that tick. Call
|
|
165
|
+
repeatedly with increasing ticks to paint intermediate frames.
|
|
166
|
+
The settle runs 499 ticks total; the last call with
|
|
167
|
+
`targetTick = 499` finalises the cluster.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const preview = worldgen.generate(coord);
|
|
171
|
+
renderPreview(preview);
|
|
172
|
+
|
|
173
|
+
for (let tick = 50; tick < 499; tick += 50) {
|
|
174
|
+
const snapshot = worldgen.advance(tick);
|
|
175
|
+
renderFrame(snapshot);
|
|
176
|
+
}
|
|
177
|
+
const final = worldgen.advance(499);
|
|
178
|
+
renderFrame(final);
|
|
179
|
+
|
|
180
|
+
worldgen.clear();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Throws if the cache is empty or `targetTick` is out of range.
|
|
184
|
+
|
|
185
|
+
### Editor
|
|
186
|
+
|
|
187
|
+
The active settings live in a single slot exposed as a `{ path → JSON-string }`
|
|
188
|
+
files map. The path keys are Klei's config paths (e.g.
|
|
189
|
+
`expansion1::worldgen/clusters/CGMCluster.yaml`); each value is the
|
|
190
|
+
JSON-encoded file body.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
const files = worldgen.snapshot.files();
|
|
194
|
+
const cluster = JSON.parse(files['expansion1::worldgen/clusters/CGMCluster.yaml']);
|
|
195
|
+
//...mutate cluster...
|
|
196
|
+
files['expansion1::worldgen/clusters/CGMCluster.yaml'] = JSON.stringify(cluster);
|
|
197
|
+
worldgen.snapshot.load(files);
|
|
198
|
+
// next worldgen.generate(...) uses the mutated settings
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Method | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `worldgen.snapshot.loadVanilla()` | Reset SETTINGS to the embedded baseline. Strict mode. |
|
|
204
|
+
| `worldgen.snapshot.loadVanillaExtensions()` | Same, Extensions mode. Accepts mod-defined enum extensions on subsequent `merge`s. |
|
|
205
|
+
| `worldgen.snapshot.load(files)` | Replace SETTINGS with `files`. Strict; atomic. Throws and leaves SETTINGS untouched on invalid input. |
|
|
206
|
+
| `worldgen.snapshot.loadExtensions(files)` | Extensions-mode counterpart of `load`. |
|
|
207
|
+
| `worldgen.snapshot.merge(files)` | Last-wins merge into the current SETTINGS. Auto-loads vanilla if empty. The typical mod-overlay entry point. |
|
|
208
|
+
| `worldgen.snapshot.remove(paths)` | Drop `paths` from the current SETTINGS. |
|
|
209
|
+
| `worldgen.snapshot.files()` | Fresh deep-copy of the current SETTINGS. `load(files())` is a no-op round-trip. |
|
|
210
|
+
| `worldgen.snapshot.checksum()` | Stable hex hash of the current snapshot. Same content → same hash regardless of load history. |
|
|
211
|
+
| `worldgen.snapshot.schemaVersion()` | The ONI game build (e.g. `"737195"`). Persist alongside snapshot bytes for migration checks. |
|
|
212
|
+
|
|
213
|
+
For ingesting mod YAML, the package also exports `parseKleiYaml`:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { parseKleiYaml } from '@onimaxxing/worldgen';
|
|
217
|
+
import { readFileSync } from 'node:fs';
|
|
218
|
+
|
|
219
|
+
const json = parseKleiYaml(readFileSync('mod/cluster.yaml', 'utf8'));
|
|
220
|
+
worldgen.snapshot.merge({
|
|
221
|
+
'expansion1::worldgen/clusters/MyMod.yaml': JSON.stringify(json),
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Any `load*` / `merge` / `remove` invalidates the cluster cache.
|
|
226
|
+
|
|
227
|
+
### Cache lifecycle
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
worldgen.clear(): void
|
|
231
|
+
worldgen.reset(): void
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- `clear()` drops the cached generated cluster; snapshot edits persist.
|
|
235
|
+
- `reset()` drops both the cluster cache and the snapshot; next
|
|
236
|
+
`generate()` reloads the embedded stock settings from scratch.
|
|
237
|
+
|
|
238
|
+
Cache semantics:
|
|
239
|
+
|
|
240
|
+
- **One slot** for the cluster cache. New `generate()` replaces it.
|
|
241
|
+
- **Per-worker.** Both caches are thread-local in WASM, so each Web
|
|
242
|
+
Worker (or Node worker thread) has its own independent state.
|
|
243
|
+
- **Snapshot edits evict the cluster cache.** Mutated settings make
|
|
244
|
+
the cached cluster stale.
|
|
245
|
+
|
|
246
|
+
### Running in a web worker
|
|
247
|
+
|
|
248
|
+
The WASM module is several megabytes and worldgen can block the
|
|
249
|
+
thread for hundreds of milliseconds; settling several seconds. Run
|
|
250
|
+
it in a worker to keep the main thread responsive.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
// worker.ts
|
|
254
|
+
import init, { worldgen } from
|
|
255
|
+
'@onimaxxing/worldgen';
|
|
256
|
+
|
|
257
|
+
await init();
|
|
258
|
+
|
|
259
|
+
self.onmessage = async ({ data: { coord } }) => {
|
|
260
|
+
const preview = worldgen.generate(coord);
|
|
261
|
+
self.postMessage({ kind: 'preview', data: preview });
|
|
262
|
+
|
|
263
|
+
for (let tick = 50; tick < 499; tick += 50) {
|
|
264
|
+
const snapshot = worldgen.advance(tick);
|
|
265
|
+
self.postMessage({ kind: 'frame', data: snapshot });
|
|
266
|
+
}
|
|
267
|
+
self.postMessage({ kind: 'frame', data: worldgen.advance(499) });
|
|
268
|
+
|
|
269
|
+
worldgen.clear();
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
// main thread
|
|
275
|
+
const worker = new Worker(new URL('./worker.ts', import.meta.url),
|
|
276
|
+
{ type: 'module' });
|
|
277
|
+
worker.onmessage = ({ data: { kind, data } }) => {
|
|
278
|
+
if (kind === 'preview') renderPreview(data);
|
|
279
|
+
if (kind === 'frame') renderFrame(data);
|
|
280
|
+
};
|
|
281
|
+
worker.postMessage({ coord: 'V-SNDST-C-42-0-4A-MUWF1' });
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### `MapData` shape
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
interface MapData {
|
|
288
|
+
coordinate: string;
|
|
289
|
+
seed: number;
|
|
290
|
+
cluster_id: string;
|
|
291
|
+
coordinate_prefix: string; // short tag e.g. "SNDST-C", "V-BAD-C", "VOLCA"
|
|
292
|
+
element_table: string[]; // element names indexed by element_idx
|
|
293
|
+
starmap: StarmapEntry[]; // Spaced Out hex grid world locations
|
|
294
|
+
starmap_pois: StarmapPoi[]; // Spaced Out non-asteroid hex POIs
|
|
295
|
+
vanilla_starmap: VanillaStarmapEntry[]; // basegame rocket destinations
|
|
296
|
+
worlds: WorldMapData[];
|
|
297
|
+
failure: WorldgenFailure | null; // populated on fatal worldgen error
|
|
298
|
+
telemetry: WorldgenEvent[]; // fail-slow warnings (empty on clean runs)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface WorldgenFailure {
|
|
302
|
+
stage: string; // pipeline stage that reported the error
|
|
303
|
+
world_index: number; // -1 for cluster-level failures
|
|
304
|
+
message: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface WorldgenEvent {
|
|
308
|
+
category: string; // e.g. "layout", "mob_spawning", "template_rules"
|
|
309
|
+
message: string; // per-world entries prefixed with "world[N]:"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
interface WorldMapData {
|
|
313
|
+
name: string; // world config path
|
|
314
|
+
width: number;
|
|
315
|
+
height: number;
|
|
316
|
+
is_starting: boolean;
|
|
317
|
+
world_traits: string[];
|
|
318
|
+
element_idx: Uint16Array; // u16 per cell, row-major (width * height)
|
|
319
|
+
mass: Float32Array; // f32 per cell
|
|
320
|
+
temperature: Float32Array; // f32 per cell
|
|
321
|
+
disease_idx: Uint8Array; // u8 per cell, 255 = none
|
|
322
|
+
disease_count: Int32Array; // i32 per cell
|
|
323
|
+
backwall_element_idx: Uint16Array; // U59 backwall layer; Vacuum-filled outside DLC5 aquatic biomes
|
|
324
|
+
backwall_mass: Float32Array; // f32 per cell, kg; 0 on Vacuum cells
|
|
325
|
+
backwall_temperature: Float32Array; // f32 per cell, K; 0 on Vacuum cells
|
|
326
|
+
biome_cells: BiomeCell[];
|
|
327
|
+
geysers: GeyserSpawn[];
|
|
328
|
+
buildings: EntitySpawn[];
|
|
329
|
+
pickupables: EntitySpawn[];
|
|
330
|
+
other_entities: EntitySpawn[];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
interface BiomeCell {
|
|
334
|
+
id: number;
|
|
335
|
+
type: string; // subworld type path
|
|
336
|
+
zone_type: ZoneType | null; // see ZoneType union exported from the package
|
|
337
|
+
x: number;
|
|
338
|
+
y: number;
|
|
339
|
+
poly: number[]; // flat [x0,y0,x1,y1,...]
|
|
340
|
+
tags: string[]; // layout tags ("AtSurface", "AtDepths", distance/zone tags, subworld tags)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
interface EntitySpawn {
|
|
344
|
+
tag: string; // game prefab name
|
|
345
|
+
cell: number; // grid cell index
|
|
346
|
+
x: number; // cell % width
|
|
347
|
+
y: number; // cell / width
|
|
348
|
+
connections?: number; // TemplatePrefab.connections bitmask (conduit-aware buildings only)
|
|
349
|
+
rotationOrientation?: string; // TemplatePrefab.rotationOrientation, e.g. "R90", "R270", "FlipH"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface GeyserSpawn extends EntitySpawn {
|
|
353
|
+
type: string; // resolved geyser template name
|
|
354
|
+
// Present when geyser stats were rolled:
|
|
355
|
+
scaled_rate?: number;
|
|
356
|
+
scaled_iter_len?: number;
|
|
357
|
+
scaled_iter_pct?: number;
|
|
358
|
+
scaled_year_len?: number;
|
|
359
|
+
scaled_year_pct?: number;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
interface StarmapEntry {
|
|
363
|
+
world_index: number;
|
|
364
|
+
q: number; r: number; // hex grid coords
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
interface StarmapPoi {
|
|
368
|
+
poi_type: string; // e.g. "HarvestableSpacePOI_*", "ArtifactSpacePOI"
|
|
369
|
+
q: number; r: number;
|
|
370
|
+
// Only on harvestable POIs:
|
|
371
|
+
capacity_roll?: number;
|
|
372
|
+
recharge_roll?: number;
|
|
373
|
+
total_capacity?: number;
|
|
374
|
+
recharge_time?: number;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Basegame rocket destinations. Empty on Spaced Out clusters.
|
|
378
|
+
interface VanillaStarmapEntry {
|
|
379
|
+
type: string; // destination type id
|
|
380
|
+
distance: number; // distance tier
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### `SettleSnapshot` shape
|
|
385
|
+
|
|
386
|
+
`worldgen.advance` returns typed-array views over cell data, not a
|
|
387
|
+
JSON array. One snapshot covers every world in the cluster.
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
interface SettleSnapshot {
|
|
391
|
+
tick: number; // 1..=499
|
|
392
|
+
worlds: SettleWorld[];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
interface SettleWorld {
|
|
396
|
+
width: number;
|
|
397
|
+
height: number;
|
|
398
|
+
element_idx: Uint16Array;
|
|
399
|
+
mass: Float32Array;
|
|
400
|
+
temperature: Float32Array;
|
|
401
|
+
disease_idx: Uint8Array; // 255 = none
|
|
402
|
+
disease_count: Int32Array;
|
|
403
|
+
backwall_element_idx: Uint16Array; // U59 backwall layer; Vacuum-filled outside DLC5 aquatic biomes
|
|
404
|
+
backwall_mass: Float32Array; // f32 per cell, kg; 0 on Vacuum cells
|
|
405
|
+
backwall_temperature: Float32Array; // f32 per cell, K; 0 on Vacuum cells
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Performance
|
|
410
|
+
|
|
411
|
+
Worldgen runtime, same machine both rows. Coordinate:
|
|
412
|
+
`V-SNDST-C-42-0-4A-MUWF1`, the Spaced Out Vanilla Sandstone cluster
|
|
413
|
+
at seed 42, with every story trait (`4A`) and every DLC mixing
|
|
414
|
+
option (`MUWF1`) enabled. 8 worlds, ~170,000 cells total.
|
|
415
|
+
|
|
416
|
+
**Time per seed:**
|
|
417
|
+
|
|
418
|
+
| Runtime | Time |
|
|
419
|
+
|---|---|
|
|
420
|
+
| In-game World Generation | 10.4 s |
|
|
421
|
+
| This package (Node 24) | 0.48 s |
|
|
422
|
+
|
|
423
|
+
WASM worldgen runs roughly 22x faster than the game's C# worldgen.
|
|
424
|
+
|
|
425
|
+
**Memory usage** (one worldgen at a time, measured on the same
|
|
426
|
+
cluster, resident memory only):
|
|
427
|
+
|
|
428
|
+
| Runtime | Working set |
|
|
429
|
+
|---|---|
|
|
430
|
+
| In-game World Generation | ~3.3 GB |
|
|
431
|
+
| This package (Node 24) | ~0.2 GB |
|
|
432
|
+
|
|
433
|
+
WASM worldgen uses roughly 15x less memory than the game's C# worldgen.
|
|
434
|
+
|
|
435
|
+
Package size is dominated by the two WASM modules; only one loads
|
|
436
|
+
at runtime. Load it in a web worker to keep download and instantiate
|
|
437
|
+
off the main thread.
|
|
438
|
+
|
|
439
|
+
## License
|
|
440
|
+
|
|
441
|
+
**MIT.**
|
|
442
|
+
|