cajonzero 0.1.3 → 0.2.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/dist/bin/cj0.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from 'child_process';
3
- import { writeFileSync, mkdirSync, existsSync, rmSync, readdirSync, readFileSync, statSync, renameSync, cpSync } from 'fs';
3
+ import { existsSync, mkdirSync, renameSync, rmSync, cpSync, writeFileSync, readFileSync, mkdtempSync, statSync, readdirSync } from 'fs';
4
4
  import { createRequire } from 'module';
5
+ import { tmpdir } from 'os';
5
6
  import path4 from 'path';
6
7
  import { createInterface } from 'readline/promises';
7
- import { gzipSync } from 'zlib';
8
+ import { gunzipSync, gzipSync } from 'zlib';
8
9
  import { vec3, vec4, vec2, mat3 } from 'gl-matrix';
9
10
  import { fileURLToPath } from 'url';
10
- import { randomBytes, createHash } from 'crypto';
11
+ import { createHash, randomBytes } from 'crypto';
11
12
 
12
13
  // src/core/maps/catalog.ts
13
14
  var MAP_CATALOG_VERSION = 1;
@@ -65,30 +66,28 @@ function normalizeRootUrl(raw) {
65
66
  }
66
67
  return url.toString();
67
68
  }
68
- function assertRelativeMapPath(relPath) {
69
- if (relPath.length === 0 || relPath.includes("\0") || relPath.includes("\\")) {
70
- fail("INVALID_PATH", `invalid map path "${relPath}"`);
69
+ function normalizeRelativeMapRoot(raw) {
70
+ const trimmed = raw.trim();
71
+ if (!trimmed.startsWith("/")) {
72
+ fail("INVALID_ROOT", `map root must start with "/": ${raw}`);
73
+ }
74
+ if (trimmed.includes("\\") || trimmed.includes("\0") || trimmed.includes("?") || trimmed.includes("#")) {
75
+ fail("INVALID_ROOT", `map root contains an unsupported character: ${raw}`);
71
76
  }
72
- if (relPath.startsWith("/")) fail("INVALID_PATH", `absolute map path "${relPath}"`);
73
77
  const parts = [];
74
- for (const part of relPath.split("/")) {
78
+ for (const part of trimmed.split("/")) {
75
79
  if (part === "" || part === ".") continue;
76
- if (part === "..") fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
80
+ if (part === "..") fail("INVALID_ROOT", `map root escapes the site root: ${raw}`);
77
81
  parts.push(part);
78
82
  }
79
- if (parts.length === 0) fail("INVALID_PATH", "empty map path");
80
- return parts.join("/");
83
+ if (parts.length === 0) fail("INVALID_ROOT", "map root must not be the site root");
84
+ return `/${parts.join("/")}/`;
81
85
  }
82
- function mapFilePath(root, relPath) {
83
- const base = new URL(normalizeRootUrl(root));
84
- const relative = assertRelativeMapPath(relPath);
85
- const url = new URL(relative, base);
86
- if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
87
- fail("INVALID_PATH", `map path escapes the root: "${relPath}"`);
86
+ function normalizeMapRoot(raw) {
87
+ if (typeof raw !== "string" || raw.trim().length === 0) {
88
+ fail("INVALID_ROOT", "map root must be a non-empty URL or path");
88
89
  }
89
- if (base.search.length > 0) url.search = base.search;
90
- url.hash = "";
91
- return url.toString();
90
+ return raw.trim().startsWith("/") ? normalizeRelativeMapRoot(raw) : normalizeRootUrl(raw);
92
91
  }
93
92
  function optionalString(value, label, code = "INVALID_MAP") {
94
93
  if (value === void 0) return void 0;
@@ -107,7 +106,7 @@ function parseEntry(value, index) {
107
106
  assertMapSlug(slug);
108
107
  const rootRaw = optionalString(record.root, `maps[${index}].root`, "INVALID_ROOT");
109
108
  if (rootRaw === void 0) fail("INVALID_ROOT", `maps[${slug}].root is required`);
110
- const root = normalizeRootUrl(rootRaw);
109
+ const root = normalizeMapRoot(rootRaw);
111
110
  const worldId = optionalString(record.worldId, `maps[${index}].worldId`);
112
111
  const assetRoot = optionalString(record.assetRoot, `maps[${index}].assetRoot`, "INVALID_ROOT");
113
112
  const name = optionalString(record.name, `maps[${index}].name`);
@@ -115,6 +114,7 @@ function parseEntry(value, index) {
115
114
  const version = optionalString(record.version, `maps[${index}].version`);
116
115
  const etag = optionalString(record.etag, `maps[${index}].etag`);
117
116
  const cover = optionalString(record.cover, `maps[${index}].cover`);
117
+ const scriptsFile = optionalString(record.scriptsFile, `maps[${index}].scriptsFile`);
118
118
  return {
119
119
  slug,
120
120
  root,
@@ -124,7 +124,8 @@ function parseEntry(value, index) {
124
124
  ...version !== void 0 ? { version } : {},
125
125
  ...etag !== void 0 ? { etag } : {},
126
126
  ...cover !== void 0 ? { cover } : {},
127
- ...assetRoot !== void 0 ? { assetRoot: normalizeRootUrl(assetRoot) } : {}
127
+ ...assetRoot !== void 0 ? { assetRoot: normalizeMapRoot(assetRoot) } : {},
128
+ ...scriptsFile !== void 0 ? { scriptsFile } : {}
128
129
  };
129
130
  }
130
131
  function parseMapCatalog(input) {
@@ -151,6 +152,167 @@ function parseMapCatalog(input) {
151
152
  function findMap(catalog, slug) {
152
153
  return catalog.maps.find((entry) => entry.slug === slug);
153
154
  }
155
+ var BOXPLAY_FORMAT_VERSION = 1;
156
+ var BOXPLAY_BUNDLE_TYPE = "map-group-play-bundle";
157
+ var DEFAULT_BOXPLAY_LIMITS = {
158
+ maxExpandedBytes: 2 * 1024 * 1024 * 1024,
159
+ maxEntries: 2e5
160
+ };
161
+ var BoxplayError = class extends Error {
162
+ code;
163
+ constructor(code, message) {
164
+ super(message);
165
+ this.name = "BoxplayError";
166
+ this.code = code;
167
+ }
168
+ };
169
+ var decoder = new TextDecoder();
170
+ function fail2(code, message) {
171
+ throw new BoxplayError(code, message);
172
+ }
173
+ function cString(block, offset, length) {
174
+ let end = offset;
175
+ const limit = Math.min(offset + length, block.length);
176
+ while (end < limit && block[end] !== 0) end += 1;
177
+ return decoder.decode(block.subarray(offset, end));
178
+ }
179
+ function octal(block, offset, length) {
180
+ const value = parseInt(cString(block, offset, length).replace(/[\0 ]/g, ""), 8);
181
+ return Number.isNaN(value) ? 0 : value;
182
+ }
183
+ function isZero(block) {
184
+ for (const byte of block) if (byte !== 0) return false;
185
+ return true;
186
+ }
187
+ function normalizeArchivePath(raw) {
188
+ if (raw.includes("\0") || raw.includes("\\")) fail2("UNSAFE_PATH", `invalid path: ${raw}`);
189
+ if (raw.startsWith("/") || /^[a-zA-Z]:/.test(raw)) fail2("UNSAFE_PATH", `absolute path: ${raw}`);
190
+ const parts = [];
191
+ for (const part of raw.split("/")) {
192
+ if (part === "" || part === ".") continue;
193
+ if (part === "..") fail2("UNSAFE_PATH", `path escapes the archive: ${raw}`);
194
+ parts.push(part);
195
+ }
196
+ if (parts.length === 0) fail2("UNSAFE_PATH", "empty archive path");
197
+ return parts.join("/");
198
+ }
199
+ function* parseTar(tar, limits = DEFAULT_BOXPLAY_LIMITS) {
200
+ let offset = 0;
201
+ let count = 0;
202
+ let longName = null;
203
+ while (offset + 512 <= tar.length) {
204
+ const header = tar.subarray(offset, offset + 512);
205
+ if (isZero(header)) break;
206
+ const name = cString(header, 0, 100);
207
+ const size = octal(header, 124, 12);
208
+ const typeflag = String.fromCharCode(header[156] ?? 0);
209
+ const prefix = cString(header, 345, 155);
210
+ const dataStart = offset + 512;
211
+ const dataEnd = dataStart + size;
212
+ if (dataEnd > tar.length) fail2("CORRUPT", `entry overruns archive: ${name}`);
213
+ count += 1;
214
+ if (count > limits.maxEntries) {
215
+ fail2("TOO_MANY_FILES", `archive exceeds ${limits.maxEntries} entries`);
216
+ }
217
+ const data = tar.subarray(dataStart, dataEnd);
218
+ offset = dataStart + Math.ceil(size / 512) * 512;
219
+ if (typeflag === "L") {
220
+ longName = decoder.decode(data).replace(/\0+$/, "");
221
+ continue;
222
+ }
223
+ const resolved = longName ?? (prefix ? `${prefix}/${name}` : name);
224
+ longName = null;
225
+ if (typeflag === "5") {
226
+ yield { path: normalizeArchivePath(resolved), kind: "dir" };
227
+ } else if (typeflag === "0" || typeflag === "\0") {
228
+ yield { path: normalizeArchivePath(resolved), kind: "file", data };
229
+ } else {
230
+ fail2("UNSUPPORTED_ENTRY", `unsupported tar entry ${typeflag}: ${resolved}`);
231
+ }
232
+ }
233
+ }
234
+ function parseBoxplayBytes(input, limits = DEFAULT_BOXPLAY_LIMITS) {
235
+ if (input.length < 2 || input[0] !== 31 || input[1] !== 139) {
236
+ fail2("NOT_GZIP", "not a gzip stream");
237
+ }
238
+ let tar;
239
+ try {
240
+ tar = gunzipSync(input);
241
+ } catch (error) {
242
+ fail2("CORRUPT", `gzip failed: ${error.message}`);
243
+ }
244
+ if (tar.length > limits.maxExpandedBytes) {
245
+ fail2("TOO_LARGE", `archive expands to ${tar.length} bytes`);
246
+ }
247
+ const files = [];
248
+ const manifests = /* @__PURE__ */ new Map();
249
+ const dataFiles = /* @__PURE__ */ new Map();
250
+ let bundleBytes = null;
251
+ for (const entry of parseTar(tar, limits)) {
252
+ if (entry.kind === "dir") continue;
253
+ files.push({ path: entry.path, data: entry.data, size: entry.data.length });
254
+ if (entry.path === "bundle.json") {
255
+ bundleBytes = entry.data;
256
+ continue;
257
+ }
258
+ const manifest = /^worlds\/([^/]+)\/manifest\.json$/.exec(entry.path);
259
+ if (manifest) {
260
+ const worldId = manifest[1];
261
+ const parsed = JSON.parse(decoder.decode(entry.data));
262
+ if (typeof parsed !== "object" || parsed === null) {
263
+ fail2("MISSING_WORLD", `worlds/${worldId}/manifest.json is not an object`);
264
+ }
265
+ manifests.set(worldId, parsed);
266
+ continue;
267
+ }
268
+ const data = /^worlds\/([^/]+)\/data\/(.+)$/.exec(entry.path);
269
+ if (data) {
270
+ const worldId = data[1];
271
+ const list = dataFiles.get(worldId) ?? [];
272
+ list.push(entry.path);
273
+ dataFiles.set(worldId, list);
274
+ }
275
+ }
276
+ if (bundleBytes === null) fail2("MISSING_BUNDLE", "archive has no bundle.json");
277
+ let bundle;
278
+ try {
279
+ const raw = JSON.parse(decoder.decode(bundleBytes));
280
+ if (typeof raw !== "object" || raw === null) throw new Error("not an object");
281
+ const value = raw;
282
+ if (value.formatVersion !== BOXPLAY_FORMAT_VERSION) throw new Error("bad formatVersion");
283
+ if (value.type !== BOXPLAY_BUNDLE_TYPE) throw new Error("bad type");
284
+ bundle = {
285
+ ...value,
286
+ formatVersion: BOXPLAY_FORMAT_VERSION,
287
+ type: BOXPLAY_BUNDLE_TYPE,
288
+ mapGroupId: String(value.mapGroupId),
289
+ version: String(value.version),
290
+ members: Array.isArray(value.members) ? value.members : []
291
+ };
292
+ } catch (error) {
293
+ fail2("INVALID_BUNDLE", `invalid bundle.json: ${error.message}`);
294
+ }
295
+ if (manifests.size === 0) fail2("MISSING_WORLD", "archive has no world manifest");
296
+ return {
297
+ bundle,
298
+ worlds: [...manifests].map(([worldId, manifest]) => ({
299
+ worldId,
300
+ manifest,
301
+ dataFiles: (dataFiles.get(worldId) ?? []).sort()
302
+ })),
303
+ files,
304
+ expandedBytes: tar.length
305
+ };
306
+ }
307
+ function readBoxplayFile(file, limits = DEFAULT_BOXPLAY_LIMITS) {
308
+ let bytes;
309
+ try {
310
+ bytes = readFileSync(file);
311
+ } catch (error) {
312
+ fail2("CORRUPT", `cannot read ${file}: ${error.message}`);
313
+ }
314
+ return parseBoxplayBytes(bytes, limits);
315
+ }
154
316
 
155
317
  // src/vendor/mudb/schema/void.ts
156
318
  var MuVoid = class {
@@ -2339,631 +2501,975 @@ var MuDate = class {
2339
2501
  }
2340
2502
  };
2341
2503
 
2342
- // src/vendor/mudb/box-utils/id.ts
2343
- function compareId(a, b) {
2344
- return a.id - b.id;
2345
- }
2346
- function interpolate(objects, l, h, id) {
2347
- const a = objects[l].id;
2348
- const b = objects[h].id;
2349
- const t = (id - a) / (b - a);
2350
- return Math.max(l, Math.min(h, Math.round((1 - t) * l + t * h) | 0));
2351
- }
2352
- function indexEqRange(objects, id, _l, _h) {
2353
- let l = _l;
2354
- let h = _h;
2355
- while (l < h) {
2356
- const m = interpolate(objects, l, h, id);
2357
- const x = objects[m].id;
2358
- if (x === id) {
2359
- return m;
2360
- } else if (x < id) {
2361
- l = m + 1;
2362
- } else {
2363
- h = m - 1;
2364
- }
2504
+ // src/vendor/mudb/box-schema/content-server/index.ts
2505
+ var HashSchema = new MuASCII();
2506
+
2507
+ // src/vendor/mudb/box-schema/utils/ref-schema.ts
2508
+ var _noSerialize = () => {
2509
+ throw new Error("cannot serialize references");
2510
+ };
2511
+ var MuRefSchema = class {
2512
+ identity = null;
2513
+ muType = "ref";
2514
+ muData = { type: "ref" };
2515
+ json = { type: "ref" };
2516
+ constructor() {
2365
2517
  }
2366
- if (l === h && objects[l].id === id) {
2367
- return l;
2518
+ diff = _noSerialize;
2519
+ patch = _noSerialize;
2520
+ toJSON = _noSerialize;
2521
+ fromJSON = _noSerialize;
2522
+ alloc() {
2523
+ return null;
2368
2524
  }
2369
- return -1;
2370
- }
2371
- new MuFloat32();
2372
- var Vec2Schema = new MuVector(new MuFloat32(), 2);
2373
- var vec3Pool = [];
2374
- var Vec3Schema = {
2375
- muType: "vector",
2376
- identity: vec3.create(),
2377
- json: {
2378
- type: "vector",
2379
- data: [0, 0, 0]
2380
- },
2381
- alloc: () => {
2382
- return vec3Pool.pop() || vec3.create();
2383
- },
2384
- free: (v) => {
2385
- vec3Pool.push(v);
2386
- },
2387
- assign: (dst, src) => {
2388
- dst[0] = src[0];
2389
- dst[1] = src[1];
2390
- dst[2] = src[2];
2391
- return dst;
2392
- },
2393
- equal: (dst, src) => {
2394
- return dst[0] === src[0] && dst[1] === src[1] && dst[2] === src[2];
2395
- },
2396
- diff: (base, target, out) => {
2397
- const s0 = (base[0] !== target[0]) << 0;
2398
- const s1 = (base[1] !== target[1]) << 1;
2399
- const s2 = (base[2] !== target[2]) << 2;
2400
- const flags = s0 + s1 + s2;
2401
- if (flags === 0) {
2402
- return false;
2403
- }
2404
- out.grow(13);
2405
- out.writeUint8(flags);
2406
- if (s0) {
2407
- out.writeFloat32(target[0]);
2408
- }
2409
- if (s1) {
2410
- out.writeFloat32(target[1]);
2411
- }
2412
- if (s2) {
2413
- out.writeFloat32(target[2]);
2525
+ free() {
2526
+ }
2527
+ assign(dst, src) {
2528
+ return src;
2529
+ }
2530
+ clone(x) {
2531
+ return x;
2532
+ }
2533
+ equal(a, b) {
2534
+ return a === b;
2535
+ }
2536
+ };
2537
+
2538
+ // src/vendor/mudb/box-schema/utils/relative-array.ts
2539
+ var RelativeArray = class {
2540
+ identity;
2541
+ muType = "relative array";
2542
+ muData;
2543
+ json;
2544
+ arraySchema;
2545
+ alloc;
2546
+ free;
2547
+ clone;
2548
+ equal;
2549
+ assign;
2550
+ toJSON;
2551
+ fromJSON;
2552
+ constructor(schema, capacity, identity2) {
2553
+ const arraySchema = this.arraySchema = new MuArray(
2554
+ schema,
2555
+ capacity,
2556
+ identity2
2557
+ );
2558
+ this.json = {
2559
+ type: "relative array",
2560
+ value: schema.json
2561
+ };
2562
+ this.muData = schema;
2563
+ this.identity = arraySchema.identity;
2564
+ this.alloc = arraySchema.alloc;
2565
+ this.free = arraySchema.free;
2566
+ this.clone = arraySchema.clone;
2567
+ this.equal = arraySchema.equal;
2568
+ this.assign = arraySchema.assign;
2569
+ this.toJSON = arraySchema.toJSON;
2570
+ this.fromJSON = arraySchema.fromJSON;
2571
+ }
2572
+ diff(base, target, stream) {
2573
+ const prefixOffset = stream.offset;
2574
+ const targetLength = target.length;
2575
+ const numTrackers = Math.ceil(targetLength / 8);
2576
+ stream.grow(4 + numTrackers);
2577
+ stream.writeUint32(targetLength);
2578
+ let trackerOffset = stream.offset;
2579
+ stream.offset = trackerOffset + numTrackers;
2580
+ let tracker = 0;
2581
+ let numPatch = 0;
2582
+ const baseLength = base.length;
2583
+ const valueSchema = this.muData;
2584
+ let prev = base.length > 0 ? base[0] : valueSchema.identity;
2585
+ for (let i = 0; i < target.length; ++i) {
2586
+ if (valueSchema.diff(prev, target[i], stream)) {
2587
+ tracker |= 1 << (i & 7);
2588
+ ++numPatch;
2589
+ }
2590
+ if ((i & 7) === 7) {
2591
+ stream.writeUint8At(trackerOffset++, tracker);
2592
+ tracker = 0;
2593
+ }
2594
+ prev = target[i];
2414
2595
  }
2415
- return true;
2416
- },
2417
- patch: (base, inp) => {
2418
- const v = vec3Pool.pop() || vec3.create();
2419
- const flags = inp.readUint8();
2420
- v[0] = flags & 1 ? inp.readFloat32() : base[0];
2421
- v[1] = flags & 2 ? inp.readFloat32() : base[1];
2422
- v[2] = flags & 4 ? inp.readFloat32() : base[2];
2423
- return v;
2424
- },
2425
- clone: (v) => {
2426
- const x = vec3Pool.pop();
2427
- if (x) {
2428
- x[0] = v[0];
2429
- x[1] = v[1];
2430
- x[2] = v[2];
2431
- return x;
2596
+ if (targetLength & 7) {
2597
+ stream.writeUint8At(trackerOffset, tracker);
2432
2598
  }
2433
- return vec3.clone(v);
2434
- },
2435
- toJSON: (v) => [v[0], v[1], v[2]],
2436
- fromJSON: (json) => {
2437
- let v = vec3Pool.pop();
2438
- if (Array.isArray(json)) {
2439
- v = v || vec3.create();
2440
- v[0] = +json[0] || 0;
2441
- v[1] = +json[1] || 0;
2442
- v[2] = +json[2] || 0;
2443
- return v;
2444
- } else if (v) {
2445
- v[0] = v[1] = v[2] = 0;
2446
- return v;
2447
- } else {
2448
- return vec3.create();
2599
+ if (numPatch > 0 || baseLength !== targetLength) {
2600
+ return true;
2449
2601
  }
2602
+ stream.offset = prefixOffset;
2603
+ return false;
2450
2604
  }
2451
- };
2452
- var vec4Pool = [];
2453
- var Vec4Schema = {
2454
- muType: "vector",
2455
- identity: vec4.create(),
2456
- json: {
2457
- type: "vector",
2458
- data: [0, 0, 0, 0]
2459
- },
2460
- alloc: () => {
2461
- return vec4Pool.pop() || vec4.create();
2462
- },
2463
- free: (v) => {
2464
- vec4Pool.push(v);
2465
- },
2466
- assign: (dst, src) => {
2467
- dst[0] = src[0];
2468
- dst[1] = src[1];
2469
- dst[2] = src[2];
2470
- dst[3] = src[3];
2471
- return dst;
2472
- },
2473
- equal: (dst, src) => {
2474
- return dst[0] === src[0] && dst[1] === src[1] && dst[2] === src[2] && dst[3] === src[3];
2475
- },
2476
- diff: (base, target, out) => {
2477
- const s0 = (base[0] !== target[0]) << 0;
2478
- const s1 = (base[1] !== target[1]) << 1;
2479
- const s2 = (base[2] !== target[2]) << 2;
2480
- const s3 = (base[3] !== target[3]) << 3;
2481
- const flags = s0 + s1 + s2 + s3;
2482
- if (flags === 0) {
2483
- return false;
2484
- }
2485
- out.grow(17);
2486
- out.writeUint8(flags);
2487
- if (s0) {
2488
- out.writeFloat32(target[0]);
2489
- }
2490
- if (s1) {
2491
- out.writeFloat32(target[1]);
2492
- }
2493
- if (s2) {
2494
- out.writeFloat32(target[2]);
2495
- }
2496
- if (s3) {
2497
- out.writeFloat32(target[3]);
2498
- }
2499
- return true;
2500
- },
2501
- patch: (base, inp) => {
2502
- const v = vec4Pool.pop() || vec4.create();
2503
- const flags = inp.readUint8();
2504
- v[0] = flags & 1 ? inp.readFloat32() : base[0];
2505
- v[1] = flags & 2 ? inp.readFloat32() : base[1];
2506
- v[2] = flags & 4 ? inp.readFloat32() : base[2];
2507
- v[3] = flags & 8 ? inp.readFloat32() : base[3];
2508
- return v;
2509
- },
2510
- clone: (v) => {
2511
- const x = vec4Pool.pop();
2512
- if (x) {
2513
- x[0] = v[0];
2514
- x[1] = v[1];
2515
- x[2] = v[2];
2516
- x[3] = v[3];
2517
- return x;
2518
- }
2519
- return vec4.clone(v);
2520
- },
2521
- toJSON: (v) => [v[0], v[1], v[2], v[3]],
2522
- fromJSON: (json) => {
2523
- let v = vec4Pool.pop();
2524
- if (Array.isArray(json)) {
2525
- v = v || vec4.create();
2526
- v[0] = +json[0] || 0;
2527
- v[1] = +json[1] || 0;
2528
- v[2] = +json[2] || 0;
2529
- v[3] = +json[3] || 0;
2530
- return v;
2531
- } else if (v) {
2532
- v[0] = v[1] = v[2] = v[3] = 0;
2533
- return v;
2534
- } else {
2535
- return vec4.create();
2605
+ patch(base, stream) {
2606
+ const result = this.clone(base);
2607
+ const targetLength = stream.readUint32();
2608
+ result.length = targetLength;
2609
+ let trackerOffset = stream.offset;
2610
+ const numTrackers = Math.ceil(targetLength / 8);
2611
+ stream.offset = trackerOffset + numTrackers;
2612
+ let tracker = 0;
2613
+ const valueSchema = this.muData;
2614
+ let prev = base.length > 0 ? base[0] : valueSchema.identity;
2615
+ for (let i = 0; i < targetLength; ++i) {
2616
+ const mod8 = i & 7;
2617
+ if (!mod8) {
2618
+ tracker = stream.readUint8At(trackerOffset++);
2619
+ }
2620
+ if (1 << mod8 & tracker) {
2621
+ prev = result[i] = valueSchema.patch(prev, stream);
2622
+ } else {
2623
+ result[i] = valueSchema.clone(prev);
2624
+ }
2536
2625
  }
2626
+ return result;
2537
2627
  }
2538
2628
  };
2539
- var QuatSchema = Vec4Schema;
2540
- new MuVector(new MuFloat32(), 9);
2541
- var Mat4Schema = new MuVector(new MuFloat32(), 16);
2629
+
2630
+ // src/vendor/mudb/box-schema/voxel/block-schema.ts
2631
+ var BlockInfoSchema = new MuStruct({
2632
+ // voxel info
2633
+ ids: new RelativeArray(new MuRelativeVarint(), Infinity, [0]),
2634
+ names: new MuArray(new MuUTF8(), Infinity, ["air"]),
2635
+ // run length encode these arrays
2636
+ emissive: new RelativeArray(new MuVarint(), Infinity, [0]),
2637
+ texture: new RelativeArray(
2638
+ new MuRelativeVarint(),
2639
+ Infinity,
2640
+ [0, 0, 0, 0, 0, 0]
2641
+ ),
2642
+ animLength: new RelativeArray(new MuVarint(1), Infinity, [1, 1, 1, 1, 1, 1]),
2643
+ friction: new RelativeArray(new MuQuantizedFloat(1 / 256, 1), Infinity, [0]),
2644
+ restitution: new RelativeArray(new MuQuantizedFloat(1 / 256, 0), Infinity, [
2645
+ 0
2646
+ ]),
2647
+ velocity: new RelativeArray(new MuFloat32(0), Infinity, [0, 0, 0]),
2648
+ // fluids
2649
+ fluids: new MuArray(
2650
+ new MuStruct({
2651
+ id: new MuVarint(),
2652
+ info: new MuUint32(),
2653
+ mass: new MuFloat64(1)
2654
+ }),
2655
+ Infinity,
2656
+ [
2657
+ {
2658
+ id: 0,
2659
+ info: 0,
2660
+ mass: 0
2661
+ }
2662
+ ]
2663
+ ),
2664
+ // category list
2665
+ category: new MuDictionary(
2666
+ new RelativeArray(new MuRelativeVarint(), Infinity),
2667
+ Infinity,
2668
+ {
2669
+ default: [0]
2670
+ }
2671
+ ),
2672
+ // texture info
2673
+ atlasRadius: new MuVarint(16),
2674
+ blockBumpShift: new MuVarint(6),
2675
+ blockColorShift: new MuVarint(4),
2676
+ // texture atlases
2677
+ colorAtlas: new MuArray(HashSchema, Infinity),
2678
+ materialAtlas: new MuArray(HashSchema, Infinity),
2679
+ bumpAtlas: new MuArray(HashSchema, Infinity)
2680
+ });
2542
2681
  new MuStruct({
2543
- lo: Vec3Schema,
2544
- hi: Vec3Schema
2682
+ id: new MuDictionary(new MuVarint(), Infinity, { air: 0 }),
2683
+ valid: new MuArray(new MuBoolean(false), Infinity),
2684
+ name: new MuArray(new MuUTF8(), Infinity, ["air"]),
2685
+ emissive: new MuArray(new MuUint16(), Infinity, [0]),
2686
+ texture: new MuArray(new MuUint32(), Infinity, [0, 0, 0, 0, 0, 0]),
2687
+ mass: new MuArray(new MuFloat64(), Infinity, [0]),
2688
+ restitution: new MuArray(new MuFloat64(), Infinity, [0]),
2689
+ friction: new MuArray(new MuFloat64(), Infinity, [0]),
2690
+ fluid: new MuArray(new MuVarint(), Infinity, [1]),
2691
+ fluidInfo: new MuArray(new MuUint32(), Infinity, [0, 0]),
2692
+ velocity: new MuArray(new MuFloat32(0), Infinity, [0, 0, 0]),
2693
+ isFluid: new MuRefSchema()
2545
2694
  });
2546
-
2547
- // src/vendor/mudb/rda/constant.ts
2548
- var MuRDAConstantStore = class {
2549
- value;
2550
- constructor(initial) {
2551
- this.value = initial;
2552
- }
2553
- state(rda, out) {
2554
- return rda.stateSchema.assign(out, this.value);
2695
+ var decodeUTF8;
2696
+ {
2697
+ new TextEncoder();
2698
+ const decoder2 = new TextDecoder();
2699
+ decodeUTF8 = (bytes) => decoder2.decode(bytes);
2700
+ }
2701
+ var MuBuffer = class {
2702
+ buffer;
2703
+ dataView;
2704
+ uint8;
2705
+ constructor(buffer) {
2706
+ this.buffer = buffer;
2707
+ this.dataView = new DataView(buffer);
2708
+ this.uint8 = new Uint8Array(buffer);
2555
2709
  }
2556
- apply() {
2557
- return false;
2710
+ };
2711
+ var bufferPool = new Array(31);
2712
+ for (let i = 0; i < 31; ++i) {
2713
+ bufferPool[i] = [];
2714
+ }
2715
+ var LITTLE_ENDIAN = true;
2716
+ var MuReadStream = class {
2717
+ buffer;
2718
+ offset;
2719
+ length;
2720
+ constructor(data) {
2721
+ this.buffer = new MuBuffer(data.buffer);
2722
+ this.offset = data.byteOffset;
2723
+ this.length = data.byteLength + data.byteOffset;
2558
2724
  }
2559
- inverse() {
2725
+ bytes() {
2726
+ return this.buffer.uint8.subarray(this.offset, this.length);
2560
2727
  }
2561
- serialize(rda, out) {
2562
- return rda.storeSchema.assign(out, this.value);
2728
+ checkBounds() {
2729
+ if (this.offset > this.length) {
2730
+ throw new Error("out of bounds");
2731
+ }
2563
2732
  }
2564
- free(rda) {
2565
- rda.stateSchema.free(this.value);
2733
+ readInt8() {
2734
+ const offset = this.offset;
2735
+ this.offset += 1;
2736
+ this.checkBounds();
2737
+ return this.buffer.dataView.getInt8(offset);
2566
2738
  }
2567
- };
2568
- var MuRDAConstant = class {
2569
- stateSchema;
2570
- actionSchema = new MuVoid();
2571
- storeSchema;
2572
- actionMeta = {
2573
- type: "table",
2574
- table: {}
2575
- };
2576
- action = {};
2577
- emptyStore;
2578
- constructor(stateSchema) {
2579
- this.stateSchema = stateSchema;
2580
- this.storeSchema = stateSchema;
2581
- this.emptyStore = new MuRDAConstantStore(stateSchema.identity);
2739
+ readInt16() {
2740
+ const offset = this.offset;
2741
+ this.offset += 2;
2742
+ this.checkBounds();
2743
+ return this.buffer.dataView.getInt16(offset, LITTLE_ENDIAN);
2582
2744
  }
2583
- createStore(initialState) {
2584
- return new MuRDAConstantStore(this.stateSchema.clone(initialState));
2745
+ readInt32() {
2746
+ const offset = this.offset;
2747
+ this.offset += 4;
2748
+ this.checkBounds();
2749
+ return this.buffer.dataView.getInt32(offset, LITTLE_ENDIAN);
2585
2750
  }
2586
- parse(store) {
2587
- return new MuRDAConstantStore(this.stateSchema.clone(store));
2751
+ readUint8() {
2752
+ const offset = this.offset;
2753
+ this.offset += 1;
2754
+ this.checkBounds();
2755
+ return this.buffer.dataView.getUint8(offset);
2588
2756
  }
2589
- };
2590
-
2591
- // src/vendor/mudb/rda/register.ts
2592
- function identity(x) {
2593
- return x;
2594
- }
2595
- var MuRDARegisterStore = class {
2596
- value;
2597
- constructor(initial) {
2598
- this.value = initial;
2599
- }
2600
- state(rda, out) {
2601
- return rda.stateSchema.assign(out, this.value);
2757
+ readUint16() {
2758
+ const offset = this.offset;
2759
+ this.offset += 2;
2760
+ this.checkBounds();
2761
+ return this.buffer.dataView.getUint16(offset, LITTLE_ENDIAN);
2602
2762
  }
2603
- inverse(rda) {
2604
- return rda.actionSchema.clone(this.value);
2763
+ readUint32() {
2764
+ const offset = this.offset;
2765
+ this.offset += 4;
2766
+ this.checkBounds();
2767
+ return this.buffer.dataView.getUint32(offset, LITTLE_ENDIAN);
2605
2768
  }
2606
- free(rda) {
2607
- rda.stateSchema.free(this.value);
2769
+ readFloat32() {
2770
+ const offset = this.offset;
2771
+ this.offset += 4;
2772
+ this.checkBounds();
2773
+ return this.buffer.dataView.getFloat32(offset, LITTLE_ENDIAN);
2608
2774
  }
2609
- apply(rda, action) {
2610
- this.value = rda.stateSchema.assign(this.value, rda.constrain(action));
2611
- return true;
2775
+ readFloat64() {
2776
+ const offset = this.offset;
2777
+ this.offset += 8;
2778
+ this.checkBounds();
2779
+ return this.buffer.dataView.getFloat64(offset, LITTLE_ENDIAN);
2612
2780
  }
2613
- serialize(rda, out) {
2614
- return rda.storeSchema.assign(out, this.value);
2781
+ readVarint() {
2782
+ const bytes = this.buffer.uint8;
2783
+ let offset = this.offset;
2784
+ const x0 = bytes[offset++];
2785
+ if (x0 < 128) {
2786
+ this.offset = offset;
2787
+ this.checkBounds();
2788
+ return x0;
2789
+ }
2790
+ const x1 = bytes[offset++];
2791
+ if (x1 < 128) {
2792
+ this.offset = offset;
2793
+ this.checkBounds();
2794
+ return x0 & 127 | x1 << 7;
2795
+ }
2796
+ const x2 = bytes[offset++];
2797
+ if (x2 < 128) {
2798
+ this.offset = offset;
2799
+ this.checkBounds();
2800
+ return x0 & 127 | (x1 & 127) << 7 | x2 << 14;
2801
+ }
2802
+ const x3 = bytes[offset++];
2803
+ if (x3 < 128) {
2804
+ this.offset = offset;
2805
+ this.checkBounds();
2806
+ return x0 & 127 | (x1 & 127) << 7 | (x2 & 127) << 14 | x3 << 21;
2807
+ }
2808
+ const x4 = bytes[offset++];
2809
+ this.offset = offset;
2810
+ this.checkBounds();
2811
+ return (x0 & 127) + ((x1 & 127) << 7) + ((x2 & 127) << 14) + ((x3 & 127) << 21) + x4 * (1 << 28);
2615
2812
  }
2616
- };
2617
- var MuRDARegister = class {
2618
- stateSchema;
2619
- actionSchema;
2620
- storeSchema;
2621
- actionMeta = { type: "unit" };
2622
- action = (value) => {
2623
- return this.actionSchema.clone(this.constrain(value));
2624
- };
2625
- emptyStore;
2626
- constrain;
2627
- constructor(stateSchema, constrain) {
2628
- this.stateSchema = this.actionSchema = this.storeSchema = stateSchema;
2629
- this.constrain = constrain || identity;
2630
- this.emptyStore = new MuRDARegisterStore(stateSchema.identity);
2813
+ readASCII(length) {
2814
+ const head = this.offset;
2815
+ this.offset += length;
2816
+ this.checkBounds();
2817
+ let str = "";
2818
+ for (let i = head; i < this.offset; ++i) {
2819
+ str += String.fromCharCode(this.buffer.uint8[i]);
2820
+ }
2821
+ return str;
2631
2822
  }
2632
- createStore(initialState) {
2633
- return new MuRDARegisterStore(this.stateSchema.clone(initialState));
2823
+ readString() {
2824
+ const byteLength = this.readVarint();
2825
+ const head = this.offset;
2826
+ this.offset += byteLength;
2827
+ this.checkBounds();
2828
+ const bytes = this.buffer.uint8.subarray(head, this.offset);
2829
+ return decodeUTF8(bytes);
2634
2830
  }
2635
- parse(store) {
2636
- return new MuRDARegisterStore(this.stateSchema.clone(store));
2831
+ readUint8At(offset) {
2832
+ return this.buffer.dataView.getUint8(offset);
2637
2833
  }
2638
2834
  };
2639
2835
 
2640
- // src/vendor/mudb/rda/struct.ts
2641
- var MuRDAStructStore = class {
2642
- stores;
2643
- constructor(stores) {
2644
- this.stores = stores;
2836
+ // src/core/maps/block-info.ts
2837
+ var BLOCK_INFO_HASH = "QmW2E9E8ipYUifsCfkMj5SULYMYbFQvP6BQxo52atXw7Zj";
2838
+ async function readBlockInfoBytes(reader, worldId, hash = BLOCK_INFO_HASH) {
2839
+ try {
2840
+ const bytes = await reader.read(`worlds/${worldId}/data/blobs/${hash}`);
2841
+ if (bytes === null || bytes.length === 0) return null;
2842
+ return bytes;
2843
+ } catch {
2844
+ return null;
2645
2845
  }
2646
- state(rda, out) {
2647
- const ids = Object.keys(this.stores);
2648
- for (let i = 0; i < ids.length; ++i) {
2649
- const id = ids[i];
2650
- out[id] = this.stores[id].state(rda.rdas[id], out[id]);
2846
+ }
2847
+
2848
+ // src/vendor/mudb/box-utils/id.ts
2849
+ function compareId(a, b) {
2850
+ return a.id - b.id;
2851
+ }
2852
+ function interpolate(objects, l, h, id) {
2853
+ const a = objects[l].id;
2854
+ const b = objects[h].id;
2855
+ const t = (id - a) / (b - a);
2856
+ return Math.max(l, Math.min(h, Math.round((1 - t) * l + t * h) | 0));
2857
+ }
2858
+ function indexEqRange(objects, id, _l, _h) {
2859
+ let l = _l;
2860
+ let h = _h;
2861
+ while (l < h) {
2862
+ const m = interpolate(objects, l, h, id);
2863
+ const x = objects[m].id;
2864
+ if (x === id) {
2865
+ return m;
2866
+ } else if (x < id) {
2867
+ l = m + 1;
2868
+ } else {
2869
+ h = m - 1;
2651
2870
  }
2652
- return out;
2653
- }
2654
- apply(rda, action) {
2655
- return this.stores[action.type].apply(rda.rdas[action.type], action.data);
2656
2871
  }
2657
- inverse(rda, action) {
2658
- const result = rda.actionSchema.alloc();
2659
- result.type = action.type;
2660
- result.data = this.stores[action.type].inverse(
2661
- rda.rdas[action.type],
2662
- action.data
2663
- );
2664
- return result;
2872
+ if (l === h && objects[l].id === id) {
2873
+ return l;
2665
2874
  }
2666
- serialize(rda, out) {
2667
- const ids = Object.keys(this.stores);
2668
- for (let i = 0; i < ids.length; ++i) {
2669
- const id = ids[i];
2670
- out[id] = this.stores[id].serialize(rda.rdas[id], out[id]);
2875
+ return -1;
2876
+ }
2877
+ new MuFloat32();
2878
+ var Vec2Schema = new MuVector(new MuFloat32(), 2);
2879
+ var vec3Pool = [];
2880
+ var Vec3Schema = {
2881
+ muType: "vector",
2882
+ identity: vec3.create(),
2883
+ json: {
2884
+ type: "vector",
2885
+ data: [0, 0, 0]
2886
+ },
2887
+ alloc: () => {
2888
+ return vec3Pool.pop() || vec3.create();
2889
+ },
2890
+ free: (v) => {
2891
+ vec3Pool.push(v);
2892
+ },
2893
+ assign: (dst, src) => {
2894
+ dst[0] = src[0];
2895
+ dst[1] = src[1];
2896
+ dst[2] = src[2];
2897
+ return dst;
2898
+ },
2899
+ equal: (dst, src) => {
2900
+ return dst[0] === src[0] && dst[1] === src[1] && dst[2] === src[2];
2901
+ },
2902
+ diff: (base, target, out) => {
2903
+ const s0 = (base[0] !== target[0]) << 0;
2904
+ const s1 = (base[1] !== target[1]) << 1;
2905
+ const s2 = (base[2] !== target[2]) << 2;
2906
+ const flags = s0 + s1 + s2;
2907
+ if (flags === 0) {
2908
+ return false;
2671
2909
  }
2672
- return out;
2673
- }
2674
- free(rda) {
2675
- const ids = Object.keys(this.stores);
2676
- for (let i = 0; i < ids.length; ++i) {
2677
- const id = ids[i];
2678
- this.stores[id].free(rda.rdas[id]);
2910
+ out.grow(13);
2911
+ out.writeUint8(flags);
2912
+ if (s0) {
2913
+ out.writeFloat32(target[0]);
2679
2914
  }
2680
- }
2681
- };
2682
- var MuRDAStruct = class {
2683
- rdas;
2684
- stateSchema;
2685
- actionSchema;
2686
- storeSchema;
2687
- actionMeta;
2688
- action;
2689
- emptyStore;
2690
- _saveStore = null;
2691
- _wrapDispatch(id, rda) {
2692
- const self = this;
2693
- function wrapPartial(root, dispatch) {
2694
- const savedPartial = { data: null };
2695
- function wrapPartialRec(meta, index) {
2696
- if (meta.type === "unit") {
2697
- return new Function(
2698
- "rda",
2699
- "partial",
2700
- `/* ${id}:${index} */ return function() { var result = rda.actionSchema.alloc(); result.type = "${id}"; result.data = partial.data${index}.apply(null, arguments); return result; }`
2701
- )(self, savedPartial);
2702
- } else if (meta.type === "table") {
2703
- const result = {};
2704
- const keys = Object.keys(meta.table);
2705
- for (let i = 0; i < keys.length; ++i) {
2706
- const key = keys[i];
2707
- result[key] = wrapPartialRec(meta.table[key], `${index}["${key}"]`);
2708
- }
2709
- return result;
2710
- } else if (meta.type === "partial") {
2711
- return wrapPartial(
2712
- meta.action,
2713
- new Function(
2714
- "partial",
2715
- `/* ${id}:${index} */ return function () { return partial.data${index}.apply(null, arguments); }`
2716
- )(savedPartial)
2717
- );
2718
- }
2719
- return {};
2720
- }
2721
- return new Function(
2722
- "dispatch",
2723
- "partial",
2724
- "wrappedDispatch",
2725
- `/* ${id} */ return function () { partial.data = dispatch.apply(null, arguments); return wrappedDispatch; }`
2726
- )(dispatch, savedPartial, wrapPartialRec(root, ""));
2915
+ if (s1) {
2916
+ out.writeFloat32(target[1]);
2727
2917
  }
2728
- function wrapAction(meta, dispatch) {
2729
- if (meta.type === "unit") {
2730
- return function(...args) {
2731
- const result = self.actionSchema.alloc();
2732
- result.type = id;
2733
- result.data = dispatch.apply(null, args);
2734
- return result;
2735
- };
2736
- } else if (meta.type === "table") {
2737
- const result = {};
2738
- const ids = Object.keys(meta.table);
2739
- for (let i = 0; i < ids.length; ++i) {
2740
- const key = ids[i];
2741
- result[key] = wrapAction(meta.table[key], dispatch);
2742
- }
2743
- return result;
2744
- } else if (meta.type === "partial") {
2745
- return wrapPartial(meta.action, dispatch);
2746
- }
2747
- return {};
2918
+ if (s2) {
2919
+ out.writeFloat32(target[2]);
2748
2920
  }
2749
- function wrapStore(meta, index) {
2750
- if (meta.type === "unit") {
2751
- return new Function(
2752
- "rda",
2753
- "dispatch",
2754
- `/* ${id}:${index} */ return function() { var result = rda.actionSchema.alloc(); result.type = "${id}"; result.data = dispatch(rda._saveStore.stores["${id}"])${index}.apply(null, arguments); return result; }`
2755
- )(self, rda.action);
2756
- } else if (meta.type === "table") {
2757
- const result = {};
2758
- const ids = Object.keys(meta.table);
2759
- for (let i = 0; i < ids.length; ++i) {
2760
- const key = ids[i];
2761
- result[key] = wrapStore(meta.table[key], `${index}["${key}"]`);
2762
- }
2763
- return result;
2764
- } else if (meta.type === "partial") {
2765
- return wrapPartial(
2766
- meta.action,
2767
- new Function(
2768
- "rda",
2769
- "dispatch",
2770
- `/* ${id}:${index} */ return function() { return dispatch(rda._saveStore.stores["${id}"])${index}.apply(null, arguments); }`
2771
- )(self, rda.action)
2772
- );
2773
- }
2774
- return {};
2921
+ return true;
2922
+ },
2923
+ patch: (base, inp) => {
2924
+ const v = vec3Pool.pop() || vec3.create();
2925
+ const flags = inp.readUint8();
2926
+ v[0] = flags & 1 ? inp.readFloat32() : base[0];
2927
+ v[1] = flags & 2 ? inp.readFloat32() : base[1];
2928
+ v[2] = flags & 4 ? inp.readFloat32() : base[2];
2929
+ return v;
2930
+ },
2931
+ clone: (v) => {
2932
+ const x = vec3Pool.pop();
2933
+ if (x) {
2934
+ x[0] = v[0];
2935
+ x[1] = v[1];
2936
+ x[2] = v[2];
2937
+ return x;
2775
2938
  }
2776
- if (rda.actionMeta.type !== "store") {
2777
- this.actionMeta.action.table[id] = rda.actionMeta;
2778
- return wrapAction(rda.actionMeta, rda.action);
2939
+ return vec3.clone(v);
2940
+ },
2941
+ toJSON: (v) => [v[0], v[1], v[2]],
2942
+ fromJSON: (json) => {
2943
+ let v = vec3Pool.pop();
2944
+ if (Array.isArray(json)) {
2945
+ v = v || vec3.create();
2946
+ v[0] = +json[0] || 0;
2947
+ v[1] = +json[1] || 0;
2948
+ v[2] = +json[2] || 0;
2949
+ return v;
2950
+ } else if (v) {
2951
+ v[0] = v[1] = v[2] = 0;
2952
+ return v;
2779
2953
  } else {
2780
- this.actionMeta.action.table[id] = rda.actionMeta.action;
2781
- return wrapStore(rda.actionMeta.action, "");
2954
+ return vec3.create();
2782
2955
  }
2783
2956
  }
2784
- constructor(spec) {
2785
- this.rdas = spec;
2786
- const stateSpec = {};
2787
- const actionSpec = {};
2788
- const storeSpec = {};
2789
- const emptyStores = {};
2790
- const props = Object.keys(spec);
2791
- for (let i = 0; i < props.length; ++i) {
2792
- const prop = props[i];
2793
- const rda = spec[prop];
2794
- stateSpec[prop] = rda.stateSchema;
2795
- actionSpec[prop] = rda.actionSchema;
2796
- storeSpec[prop] = rda.storeSchema;
2797
- emptyStores[prop] = rda.emptyStore;
2957
+ };
2958
+ var vec4Pool = [];
2959
+ var Vec4Schema = {
2960
+ muType: "vector",
2961
+ identity: vec4.create(),
2962
+ json: {
2963
+ type: "vector",
2964
+ data: [0, 0, 0, 0]
2965
+ },
2966
+ alloc: () => {
2967
+ return vec4Pool.pop() || vec4.create();
2968
+ },
2969
+ free: (v) => {
2970
+ vec4Pool.push(v);
2971
+ },
2972
+ assign: (dst, src) => {
2973
+ dst[0] = src[0];
2974
+ dst[1] = src[1];
2975
+ dst[2] = src[2];
2976
+ dst[3] = src[3];
2977
+ return dst;
2978
+ },
2979
+ equal: (dst, src) => {
2980
+ return dst[0] === src[0] && dst[1] === src[1] && dst[2] === src[2] && dst[3] === src[3];
2981
+ },
2982
+ diff: (base, target, out) => {
2983
+ const s0 = (base[0] !== target[0]) << 0;
2984
+ const s1 = (base[1] !== target[1]) << 1;
2985
+ const s2 = (base[2] !== target[2]) << 2;
2986
+ const s3 = (base[3] !== target[3]) << 3;
2987
+ const flags = s0 + s1 + s2 + s3;
2988
+ if (flags === 0) {
2989
+ return false;
2798
2990
  }
2799
- this.stateSchema = new MuStruct(stateSpec);
2800
- this.actionSchema = new MuUnion(actionSpec);
2801
- this.storeSchema = new MuStruct(storeSpec);
2802
- this.emptyStore = new MuRDAStructStore(emptyStores);
2803
- this.actionMeta = {
2804
- type: "store",
2805
- action: {
2806
- type: "table",
2807
- table: {}
2808
- }
2809
- };
2810
- const storeDispatch = {};
2811
- for (let i = 0; i < props.length; ++i) {
2812
- const prop = props[i];
2813
- storeDispatch[prop] = this._wrapDispatch(prop, spec[prop]);
2991
+ out.grow(17);
2992
+ out.writeUint8(flags);
2993
+ if (s0) {
2994
+ out.writeFloat32(target[0]);
2814
2995
  }
2815
- this.action = ((store) => {
2816
- this._saveStore = store;
2817
- return storeDispatch;
2818
- });
2819
- }
2820
- createStore(state) {
2821
- const stores = {};
2822
- const ids = Object.keys(this.rdas);
2823
- for (let i = 0; i < ids.length; ++i) {
2824
- const id = ids[i];
2825
- stores[id] = this.rdas[id].createStore(state[id]);
2996
+ if (s1) {
2997
+ out.writeFloat32(target[1]);
2826
2998
  }
2827
- return new MuRDAStructStore(stores);
2828
- }
2829
- parse(store) {
2830
- const stores = {};
2831
- const ids = Object.keys(this.rdas);
2832
- for (let i = 0; i < ids.length; ++i) {
2833
- const id = ids[i];
2834
- stores[id] = this.rdas[id].parse(store[id]);
2999
+ if (s2) {
3000
+ out.writeFloat32(target[2]);
3001
+ }
3002
+ if (s3) {
3003
+ out.writeFloat32(target[3]);
3004
+ }
3005
+ return true;
3006
+ },
3007
+ patch: (base, inp) => {
3008
+ const v = vec4Pool.pop() || vec4.create();
3009
+ const flags = inp.readUint8();
3010
+ v[0] = flags & 1 ? inp.readFloat32() : base[0];
3011
+ v[1] = flags & 2 ? inp.readFloat32() : base[1];
3012
+ v[2] = flags & 4 ? inp.readFloat32() : base[2];
3013
+ v[3] = flags & 8 ? inp.readFloat32() : base[3];
3014
+ return v;
3015
+ },
3016
+ clone: (v) => {
3017
+ const x = vec4Pool.pop();
3018
+ if (x) {
3019
+ x[0] = v[0];
3020
+ x[1] = v[1];
3021
+ x[2] = v[2];
3022
+ x[3] = v[3];
3023
+ return x;
3024
+ }
3025
+ return vec4.clone(v);
3026
+ },
3027
+ toJSON: (v) => [v[0], v[1], v[2], v[3]],
3028
+ fromJSON: (json) => {
3029
+ let v = vec4Pool.pop();
3030
+ if (Array.isArray(json)) {
3031
+ v = v || vec4.create();
3032
+ v[0] = +json[0] || 0;
3033
+ v[1] = +json[1] || 0;
3034
+ v[2] = +json[2] || 0;
3035
+ v[3] = +json[3] || 0;
3036
+ return v;
3037
+ } else if (v) {
3038
+ v[0] = v[1] = v[2] = v[3] = 0;
3039
+ return v;
3040
+ } else {
3041
+ return vec4.create();
2835
3042
  }
2836
- return new MuRDAStructStore(stores);
2837
3043
  }
2838
3044
  };
3045
+ var QuatSchema = Vec4Schema;
3046
+ new MuVector(new MuFloat32(), 9);
3047
+ var Mat4Schema = new MuVector(new MuFloat32(), 16);
3048
+ new MuStruct({
3049
+ lo: Vec3Schema,
3050
+ hi: Vec3Schema
3051
+ });
2839
3052
 
2840
- // src/vendor/mudb/rda/map.ts
2841
- function compareKey(a, b) {
2842
- return a < b ? -1 : b < a ? 1 : 0;
2843
- }
2844
- function compareStoreElements(a, b) {
2845
- return a.sequence - b.sequence || a.id - b.id;
2846
- }
2847
- function compareNum(a, b) {
2848
- return a - b;
2849
- }
2850
- var MuRDAMapStoreElement = class {
2851
- constructor(id, sequence, deleted, key, value) {
2852
- this.id = id;
2853
- this.sequence = sequence;
2854
- this.deleted = deleted;
2855
- this.key = key;
2856
- this.value = value;
2857
- }
2858
- id;
2859
- sequence;
2860
- deleted;
2861
- key;
3053
+ // src/vendor/mudb/rda/constant.ts
3054
+ var MuRDAConstantStore = class {
2862
3055
  value;
2863
- // currently a linked list, though this should probably be replaced by a pairing heap to prevent adversarial attacks
2864
- next = null;
2865
- prev = null;
3056
+ constructor(initial) {
3057
+ this.value = initial;
3058
+ }
3059
+ state(rda, out) {
3060
+ return rda.stateSchema.assign(out, this.value);
3061
+ }
3062
+ apply() {
3063
+ return false;
3064
+ }
3065
+ inverse() {
3066
+ }
3067
+ serialize(rda, out) {
3068
+ return rda.storeSchema.assign(out, this.value);
3069
+ }
3070
+ free(rda) {
3071
+ rda.stateSchema.free(this.value);
3072
+ }
2866
3073
  };
2867
- var MuRDAMapStore = class {
2868
- keyIndex = {};
2869
- idIndex = {};
2870
- constructor(elements) {
2871
- const { idIndex } = this;
2872
- for (let i = 0; i < elements.length; ++i) {
2873
- const element = elements[i];
2874
- const id = element.id;
2875
- idIndex[id] = element;
2876
- this._insertElement(element);
2877
- }
3074
+ var MuRDAConstant = class {
3075
+ stateSchema;
3076
+ actionSchema = new MuVoid();
3077
+ storeSchema;
3078
+ actionMeta = {
3079
+ type: "table",
3080
+ table: {}
3081
+ };
3082
+ action = {};
3083
+ emptyStore;
3084
+ constructor(stateSchema) {
3085
+ this.stateSchema = stateSchema;
3086
+ this.storeSchema = stateSchema;
3087
+ this.emptyStore = new MuRDAConstantStore(stateSchema.identity);
3088
+ }
3089
+ createStore(initialState) {
3090
+ return new MuRDAConstantStore(this.stateSchema.clone(initialState));
3091
+ }
3092
+ parse(store) {
3093
+ return new MuRDAConstantStore(this.stateSchema.clone(store));
3094
+ }
3095
+ };
3096
+
3097
+ // src/vendor/mudb/rda/register.ts
3098
+ function identity(x) {
3099
+ return x;
3100
+ }
3101
+ var MuRDARegisterStore = class {
3102
+ value;
3103
+ constructor(initial) {
3104
+ this.value = initial;
2878
3105
  }
2879
3106
  state(rda, out) {
2880
- const keyIndex = this.keyIndex;
2881
- const outKeys = Object.keys(out);
2882
- for (let i = 0; i < outKeys.length; ++i) {
2883
- const key = outKeys[i];
2884
- const cur = keyIndex[key];
2885
- if (!cur || cur.deleted) {
2886
- rda.valueRDA.stateSchema.free(out[key]);
2887
- delete out[key];
2888
- }
2889
- }
2890
- const keys = Object.keys(keyIndex);
2891
- const valueRDA = rda.valueRDA;
2892
- const valueSchema = valueRDA.stateSchema;
2893
- for (let i = 0; i < keys.length; ++i) {
2894
- const key = keys[i];
2895
- const final = keyIndex[key];
2896
- if (!final.deleted) {
2897
- out[key] = final.value.state(valueRDA, out[key] || valueSchema.alloc());
2898
- }
3107
+ return rda.stateSchema.assign(out, this.value);
3108
+ }
3109
+ inverse(rda) {
3110
+ return rda.actionSchema.clone(this.value);
3111
+ }
3112
+ free(rda) {
3113
+ rda.stateSchema.free(this.value);
3114
+ }
3115
+ apply(rda, action) {
3116
+ this.value = rda.stateSchema.assign(this.value, rda.constrain(action));
3117
+ return true;
3118
+ }
3119
+ serialize(rda, out) {
3120
+ return rda.storeSchema.assign(out, this.value);
3121
+ }
3122
+ };
3123
+ var MuRDARegister = class {
3124
+ stateSchema;
3125
+ actionSchema;
3126
+ storeSchema;
3127
+ actionMeta = { type: "unit" };
3128
+ action = (value) => {
3129
+ return this.actionSchema.clone(this.constrain(value));
3130
+ };
3131
+ emptyStore;
3132
+ constrain;
3133
+ constructor(stateSchema, constrain) {
3134
+ this.stateSchema = this.actionSchema = this.storeSchema = stateSchema;
3135
+ this.constrain = constrain || identity;
3136
+ this.emptyStore = new MuRDARegisterStore(stateSchema.identity);
3137
+ }
3138
+ createStore(initialState) {
3139
+ return new MuRDARegisterStore(this.stateSchema.clone(initialState));
3140
+ }
3141
+ parse(store) {
3142
+ return new MuRDARegisterStore(this.stateSchema.clone(store));
3143
+ }
3144
+ };
3145
+
3146
+ // src/vendor/mudb/rda/struct.ts
3147
+ var MuRDAStructStore = class {
3148
+ stores;
3149
+ constructor(stores) {
3150
+ this.stores = stores;
3151
+ }
3152
+ state(rda, out) {
3153
+ const ids = Object.keys(this.stores);
3154
+ for (let i = 0; i < ids.length; ++i) {
3155
+ const id = ids[i];
3156
+ out[id] = this.stores[id].state(rda.rdas[id], out[id]);
2899
3157
  }
2900
3158
  return out;
2901
3159
  }
2902
- _moveElement(element, key) {
2903
- if (key === element.key) {
2904
- return;
2905
- }
2906
- const { next, prev, sequence } = element;
2907
- if (this.keyIndex[element.key] === element) {
2908
- if (next) {
2909
- this.keyIndex[element.key] = next;
2910
- if (next.sequence >= sequence) {
2911
- next.deleted = false;
2912
- }
2913
- } else {
2914
- delete this.keyIndex[element.key];
2915
- }
2916
- }
2917
- if (next) {
2918
- next.prev = prev;
3160
+ apply(rda, action) {
3161
+ return this.stores[action.type].apply(rda.rdas[action.type], action.data);
3162
+ }
3163
+ inverse(rda, action) {
3164
+ const result = rda.actionSchema.alloc();
3165
+ result.type = action.type;
3166
+ result.data = this.stores[action.type].inverse(
3167
+ rda.rdas[action.type],
3168
+ action.data
3169
+ );
3170
+ return result;
3171
+ }
3172
+ serialize(rda, out) {
3173
+ const ids = Object.keys(this.stores);
3174
+ for (let i = 0; i < ids.length; ++i) {
3175
+ const id = ids[i];
3176
+ out[id] = this.stores[id].serialize(rda.rdas[id], out[id]);
2919
3177
  }
2920
- if (prev) {
2921
- prev.next = next;
3178
+ return out;
3179
+ }
3180
+ free(rda) {
3181
+ const ids = Object.keys(this.stores);
3182
+ for (let i = 0; i < ids.length; ++i) {
3183
+ const id = ids[i];
3184
+ this.stores[id].free(rda.rdas[id]);
2922
3185
  }
2923
- element.next = element.prev = null;
2924
- element.key = key;
2925
- this._insertElement(element);
2926
3186
  }
2927
- _insertElement(element) {
2928
- const key = element.key;
2929
- const head = this.keyIndex[key];
2930
- if (head) {
2931
- if (compareStoreElements(head, element) < 0) {
2932
- element.next = head;
2933
- head.prev = element;
2934
- head.deleted = true;
2935
- this.keyIndex[key] = element;
2936
- } else {
2937
- let prev = head;
2938
- while (prev) {
2939
- const next = prev.next;
2940
- if (!next) {
2941
- prev.next = element;
2942
- element.prev = prev;
2943
- break;
2944
- } else if (compareStoreElements(next, element) < 0) {
2945
- prev.next = element;
2946
- next.prev = element;
2947
- element.next = next;
2948
- element.prev = prev;
2949
- break;
3187
+ };
3188
+ var MuRDAStruct = class {
3189
+ rdas;
3190
+ stateSchema;
3191
+ actionSchema;
3192
+ storeSchema;
3193
+ actionMeta;
3194
+ action;
3195
+ emptyStore;
3196
+ _saveStore = null;
3197
+ _wrapDispatch(id, rda) {
3198
+ const self = this;
3199
+ function wrapPartial(root, dispatch) {
3200
+ const savedPartial = { data: null };
3201
+ function wrapPartialRec(meta, index) {
3202
+ if (meta.type === "unit") {
3203
+ return new Function(
3204
+ "rda",
3205
+ "partial",
3206
+ `/* ${id}:${index} */ return function() { var result = rda.actionSchema.alloc(); result.type = "${id}"; result.data = partial.data${index}.apply(null, arguments); return result; }`
3207
+ )(self, savedPartial);
3208
+ } else if (meta.type === "table") {
3209
+ const result = {};
3210
+ const keys = Object.keys(meta.table);
3211
+ for (let i = 0; i < keys.length; ++i) {
3212
+ const key = keys[i];
3213
+ result[key] = wrapPartialRec(meta.table[key], `${index}["${key}"]`);
2950
3214
  }
2951
- prev = next;
3215
+ return result;
3216
+ } else if (meta.type === "partial") {
3217
+ return wrapPartial(
3218
+ meta.action,
3219
+ new Function(
3220
+ "partial",
3221
+ `/* ${id}:${index} */ return function () { return partial.data${index}.apply(null, arguments); }`
3222
+ )(savedPartial)
3223
+ );
2952
3224
  }
3225
+ return {};
2953
3226
  }
2954
- } else {
2955
- this.keyIndex[key] = element;
3227
+ return new Function(
3228
+ "dispatch",
3229
+ "partial",
3230
+ "wrappedDispatch",
3231
+ `/* ${id} */ return function () { partial.data = dispatch.apply(null, arguments); return wrappedDispatch; }`
3232
+ )(dispatch, savedPartial, wrapPartialRec(root, ""));
2956
3233
  }
2957
- }
2958
- _applyUpsert(valueRDA, upsertAction) {
2959
- const prev = this.idIndex[upsertAction.id];
2960
- if (prev) {
2961
- prev.id = upsertAction.id;
2962
- prev.deleted = upsertAction.deleted;
2963
- prev.sequence = upsertAction.sequence;
2964
- prev.value.free(valueRDA);
2965
- prev.value = valueRDA.parse(upsertAction.value);
2966
- this._moveElement(prev, upsertAction.key);
3234
+ function wrapAction(meta, dispatch) {
3235
+ if (meta.type === "unit") {
3236
+ return function(...args) {
3237
+ const result = self.actionSchema.alloc();
3238
+ result.type = id;
3239
+ result.data = dispatch.apply(null, args);
3240
+ return result;
3241
+ };
3242
+ } else if (meta.type === "table") {
3243
+ const result = {};
3244
+ const ids = Object.keys(meta.table);
3245
+ for (let i = 0; i < ids.length; ++i) {
3246
+ const key = ids[i];
3247
+ result[key] = wrapAction(meta.table[key], dispatch);
3248
+ }
3249
+ return result;
3250
+ } else if (meta.type === "partial") {
3251
+ return wrapPartial(meta.action, dispatch);
3252
+ }
3253
+ return {};
3254
+ }
3255
+ function wrapStore(meta, index) {
3256
+ if (meta.type === "unit") {
3257
+ return new Function(
3258
+ "rda",
3259
+ "dispatch",
3260
+ `/* ${id}:${index} */ return function() { var result = rda.actionSchema.alloc(); result.type = "${id}"; result.data = dispatch(rda._saveStore.stores["${id}"])${index}.apply(null, arguments); return result; }`
3261
+ )(self, rda.action);
3262
+ } else if (meta.type === "table") {
3263
+ const result = {};
3264
+ const ids = Object.keys(meta.table);
3265
+ for (let i = 0; i < ids.length; ++i) {
3266
+ const key = ids[i];
3267
+ result[key] = wrapStore(meta.table[key], `${index}["${key}"]`);
3268
+ }
3269
+ return result;
3270
+ } else if (meta.type === "partial") {
3271
+ return wrapPartial(
3272
+ meta.action,
3273
+ new Function(
3274
+ "rda",
3275
+ "dispatch",
3276
+ `/* ${id}:${index} */ return function() { return dispatch(rda._saveStore.stores["${id}"])${index}.apply(null, arguments); }`
3277
+ )(self, rda.action)
3278
+ );
3279
+ }
3280
+ return {};
3281
+ }
3282
+ if (rda.actionMeta.type !== "store") {
3283
+ this.actionMeta.action.table[id] = rda.actionMeta;
3284
+ return wrapAction(rda.actionMeta, rda.action);
3285
+ } else {
3286
+ this.actionMeta.action.table[id] = rda.actionMeta.action;
3287
+ return wrapStore(rda.actionMeta.action, "");
3288
+ }
3289
+ }
3290
+ constructor(spec) {
3291
+ this.rdas = spec;
3292
+ const stateSpec = {};
3293
+ const actionSpec = {};
3294
+ const storeSpec = {};
3295
+ const emptyStores = {};
3296
+ const props = Object.keys(spec);
3297
+ for (let i = 0; i < props.length; ++i) {
3298
+ const prop = props[i];
3299
+ const rda = spec[prop];
3300
+ stateSpec[prop] = rda.stateSchema;
3301
+ actionSpec[prop] = rda.actionSchema;
3302
+ storeSpec[prop] = rda.storeSchema;
3303
+ emptyStores[prop] = rda.emptyStore;
3304
+ }
3305
+ this.stateSchema = new MuStruct(stateSpec);
3306
+ this.actionSchema = new MuUnion(actionSpec);
3307
+ this.storeSchema = new MuStruct(storeSpec);
3308
+ this.emptyStore = new MuRDAStructStore(emptyStores);
3309
+ this.actionMeta = {
3310
+ type: "store",
3311
+ action: {
3312
+ type: "table",
3313
+ table: {}
3314
+ }
3315
+ };
3316
+ const storeDispatch = {};
3317
+ for (let i = 0; i < props.length; ++i) {
3318
+ const prop = props[i];
3319
+ storeDispatch[prop] = this._wrapDispatch(prop, spec[prop]);
3320
+ }
3321
+ this.action = ((store) => {
3322
+ this._saveStore = store;
3323
+ return storeDispatch;
3324
+ });
3325
+ }
3326
+ createStore(state) {
3327
+ const stores = {};
3328
+ const ids = Object.keys(this.rdas);
3329
+ for (let i = 0; i < ids.length; ++i) {
3330
+ const id = ids[i];
3331
+ stores[id] = this.rdas[id].createStore(state[id]);
3332
+ }
3333
+ return new MuRDAStructStore(stores);
3334
+ }
3335
+ parse(store) {
3336
+ const stores = {};
3337
+ const ids = Object.keys(this.rdas);
3338
+ for (let i = 0; i < ids.length; ++i) {
3339
+ const id = ids[i];
3340
+ stores[id] = this.rdas[id].parse(store[id]);
3341
+ }
3342
+ return new MuRDAStructStore(stores);
3343
+ }
3344
+ };
3345
+
3346
+ // src/vendor/mudb/rda/map.ts
3347
+ function compareKey(a, b) {
3348
+ return a < b ? -1 : b < a ? 1 : 0;
3349
+ }
3350
+ function compareStoreElements(a, b) {
3351
+ return a.sequence - b.sequence || a.id - b.id;
3352
+ }
3353
+ function compareNum(a, b) {
3354
+ return a - b;
3355
+ }
3356
+ var MuRDAMapStoreElement = class {
3357
+ constructor(id, sequence, deleted, key, value) {
3358
+ this.id = id;
3359
+ this.sequence = sequence;
3360
+ this.deleted = deleted;
3361
+ this.key = key;
3362
+ this.value = value;
3363
+ }
3364
+ id;
3365
+ sequence;
3366
+ deleted;
3367
+ key;
3368
+ value;
3369
+ // currently a linked list, though this should probably be replaced by a pairing heap to prevent adversarial attacks
3370
+ next = null;
3371
+ prev = null;
3372
+ };
3373
+ var MuRDAMapStore = class {
3374
+ keyIndex = {};
3375
+ idIndex = {};
3376
+ constructor(elements) {
3377
+ const { idIndex } = this;
3378
+ for (let i = 0; i < elements.length; ++i) {
3379
+ const element = elements[i];
3380
+ const id = element.id;
3381
+ idIndex[id] = element;
3382
+ this._insertElement(element);
3383
+ }
3384
+ }
3385
+ state(rda, out) {
3386
+ const keyIndex = this.keyIndex;
3387
+ const outKeys = Object.keys(out);
3388
+ for (let i = 0; i < outKeys.length; ++i) {
3389
+ const key = outKeys[i];
3390
+ const cur = keyIndex[key];
3391
+ if (!cur || cur.deleted) {
3392
+ rda.valueRDA.stateSchema.free(out[key]);
3393
+ delete out[key];
3394
+ }
3395
+ }
3396
+ const keys = Object.keys(keyIndex);
3397
+ const valueRDA = rda.valueRDA;
3398
+ const valueSchema = valueRDA.stateSchema;
3399
+ for (let i = 0; i < keys.length; ++i) {
3400
+ const key = keys[i];
3401
+ const final = keyIndex[key];
3402
+ if (!final.deleted) {
3403
+ out[key] = final.value.state(valueRDA, out[key] || valueSchema.alloc());
3404
+ }
3405
+ }
3406
+ return out;
3407
+ }
3408
+ _moveElement(element, key) {
3409
+ if (key === element.key) {
3410
+ return;
3411
+ }
3412
+ const { next, prev, sequence } = element;
3413
+ if (this.keyIndex[element.key] === element) {
3414
+ if (next) {
3415
+ this.keyIndex[element.key] = next;
3416
+ if (next.sequence >= sequence) {
3417
+ next.deleted = false;
3418
+ }
3419
+ } else {
3420
+ delete this.keyIndex[element.key];
3421
+ }
3422
+ }
3423
+ if (next) {
3424
+ next.prev = prev;
3425
+ }
3426
+ if (prev) {
3427
+ prev.next = next;
3428
+ }
3429
+ element.next = element.prev = null;
3430
+ element.key = key;
3431
+ this._insertElement(element);
3432
+ }
3433
+ _insertElement(element) {
3434
+ const key = element.key;
3435
+ const head = this.keyIndex[key];
3436
+ if (head) {
3437
+ if (compareStoreElements(head, element) < 0) {
3438
+ element.next = head;
3439
+ head.prev = element;
3440
+ head.deleted = true;
3441
+ this.keyIndex[key] = element;
3442
+ } else {
3443
+ let prev = head;
3444
+ while (prev) {
3445
+ const next = prev.next;
3446
+ if (!next) {
3447
+ prev.next = element;
3448
+ element.prev = prev;
3449
+ break;
3450
+ } else if (compareStoreElements(next, element) < 0) {
3451
+ prev.next = element;
3452
+ next.prev = element;
3453
+ element.next = next;
3454
+ element.prev = prev;
3455
+ break;
3456
+ }
3457
+ prev = next;
3458
+ }
3459
+ }
3460
+ } else {
3461
+ this.keyIndex[key] = element;
3462
+ }
3463
+ }
3464
+ _applyUpsert(valueRDA, upsertAction) {
3465
+ const prev = this.idIndex[upsertAction.id];
3466
+ if (prev) {
3467
+ prev.id = upsertAction.id;
3468
+ prev.deleted = upsertAction.deleted;
3469
+ prev.sequence = upsertAction.sequence;
3470
+ prev.value.free(valueRDA);
3471
+ prev.value = valueRDA.parse(upsertAction.value);
3472
+ this._moveElement(prev, upsertAction.key);
2967
3473
  } else {
2968
3474
  const element = new MuRDAMapStoreElement(
2969
3475
  upsertAction.id,
@@ -4240,9 +4746,6 @@ var MuRDAList = class {
4240
4746
  return new MuRDAListStore(nodes);
4241
4747
  }
4242
4748
  };
4243
-
4244
- // src/vendor/mudb/box-schema/content-server/index.ts
4245
- var HashSchema = new MuASCII();
4246
4749
  var SCHROEPPEL23 = 2863311530;
4247
4750
  function readShroeppel(stream) {
4248
4751
  const x = stream.readVarint();
@@ -8139,284 +8642,95 @@ var DamageEventSchema = new MuStruct({
8139
8642
  die: new MuArray(new MuVarint(), Infinity)
8140
8643
  });
8141
8644
 
8142
- // src/vendor/mudb/box-schema/events/script-events.ts
8143
- new MuStruct({
8144
- damage: DamageEventSchema
8145
- });
8146
-
8147
- // src/vendor/mudb/box-schema/physics/collision-filter-schema.ts
8148
- var CollisionGroupSchema = new MuSortedArray(
8149
- new MuVarint(),
8150
- Infinity,
8151
- (a, b) => a - b
8152
- );
8153
- var CollisionFilterSchema = new MuArray(
8154
- CollisionGroupSchema,
8155
- Infinity
8156
- );
8157
-
8158
- // src/vendor/mudb/box-schema/zone/zone-schema.ts
8159
- var ZoneSchema = new MuStruct({
8160
- id: new MuVarint(),
8161
- // bounds
8162
- minX: new MuQuantizedFloat(1 / 16, 0),
8163
- minY: new MuQuantizedFloat(1 / 16, 0),
8164
- minZ: new MuQuantizedFloat(1 / 16, 0),
8165
- maxX: new MuQuantizedFloat(1 / 16, 0),
8166
- maxY: new MuQuantizedFloat(1 / 16, 0),
8167
- maxZ: new MuQuantizedFloat(1 / 16, 0)
8168
- });
8169
- var ZoneSelectorGroupSchema = new MuStruct({
8170
- id: new MuVarint(),
8171
- groupBits: new MuSortedArray(new MuVarint(), Infinity, (a, b) => a - b)
8172
- });
8173
- var ZoneSelectorGroupSetSchema = new MuSortedArray(
8174
- ZoneSelectorGroupSchema,
8175
- Infinity,
8176
- compareId
8177
- );
8178
- var PhysicsZoneSchema = new MuStruct({
8179
- // selector group for this zone
8180
- selectorGroup: new MuVarint(0),
8181
- // physics effects
8182
- massScale: new MuQuantizedFloat(1 / 256, 0),
8183
- fx: new MuQuantizedFloat(1 / 1024, 0),
8184
- fy: new MuQuantizedFloat(1 / 1024, 0),
8185
- fz: new MuQuantizedFloat(1 / 1024, 0),
8186
- ...ZoneSchema.muData
8187
- });
8188
- var PhysicsZoneSetSchema = new MuSortedArray(
8189
- PhysicsZoneSchema,
8190
- Infinity,
8191
- compareId
8192
- );
8193
- var EnvironmentZoneSchema = new MuStruct({
8194
- // weather effects
8195
- overrideFog: new MuBoolean(false),
8196
- fog: FogRDA.stateSchema,
8197
- overrideSky: new MuBoolean(false),
8198
- sky: SkyRDA.stateSchema,
8199
- overrideSnow: new MuBoolean(false),
8200
- snow: SnowRDA.stateSchema,
8201
- overrideRain: new MuBoolean(false),
8202
- rain: RainRDA.stateSchema,
8203
- ...ZoneSchema.muData
8204
- });
8205
- var EnvironmentZoneSetSchema = new MuSortedArray(
8206
- EnvironmentZoneSchema,
8207
- Infinity,
8208
- compareId
8209
- );
8210
-
8211
- // src/vendor/mudb/box-schema/environment/environment-schema.ts
8212
- var EnvironmentParamSchema = EnvironmentRDA.stateSchema;
8213
- new MuStruct({
8214
- timeOfDay: new MuFloat64(0),
8215
- sunUp: new MuBoolean(false),
8216
- globalLight: new MuFloat64(0),
8217
- gamma: new MuFloat32(1.3),
8218
- drawDistance: new MuFloat32(512),
8219
- freezeDrawDistance: new MuBoolean(false),
8220
- sunDirection: Vec3Schema,
8221
- sunColor: Vec3Schema,
8222
- skyLeft: Vec3Schema,
8223
- skyRight: Vec3Schema,
8224
- skyTop: Vec3Schema,
8225
- skyBottom: Vec3Schema,
8226
- skyFront: Vec3Schema,
8227
- skyBack: Vec3Schema,
8228
- fogParams: Vec4Schema,
8229
- fogParams2: Vec4Schema,
8230
- fogParams3: Vec4Schema
8231
- });
8232
-
8233
- // src/vendor/mudb/box-schema/utils/ref-schema.ts
8234
- var _noSerialize = () => {
8235
- throw new Error("cannot serialize references");
8236
- };
8237
- var MuRefSchema = class {
8238
- identity = null;
8239
- muType = "ref";
8240
- muData = { type: "ref" };
8241
- json = { type: "ref" };
8242
- constructor() {
8243
- }
8244
- diff = _noSerialize;
8245
- patch = _noSerialize;
8246
- toJSON = _noSerialize;
8247
- fromJSON = _noSerialize;
8248
- alloc() {
8249
- return null;
8250
- }
8251
- free() {
8252
- }
8253
- assign(dst, src) {
8254
- return src;
8255
- }
8256
- clone(x) {
8257
- return x;
8258
- }
8259
- equal(a, b) {
8260
- return a === b;
8261
- }
8262
- };
8263
-
8264
- // src/vendor/mudb/box-schema/utils/relative-array.ts
8265
- var RelativeArray = class {
8266
- identity;
8267
- muType = "relative array";
8268
- muData;
8269
- json;
8270
- arraySchema;
8271
- alloc;
8272
- free;
8273
- clone;
8274
- equal;
8275
- assign;
8276
- toJSON;
8277
- fromJSON;
8278
- constructor(schema, capacity, identity2) {
8279
- const arraySchema = this.arraySchema = new MuArray(
8280
- schema,
8281
- capacity,
8282
- identity2
8283
- );
8284
- this.json = {
8285
- type: "relative array",
8286
- value: schema.json
8287
- };
8288
- this.muData = schema;
8289
- this.identity = arraySchema.identity;
8290
- this.alloc = arraySchema.alloc;
8291
- this.free = arraySchema.free;
8292
- this.clone = arraySchema.clone;
8293
- this.equal = arraySchema.equal;
8294
- this.assign = arraySchema.assign;
8295
- this.toJSON = arraySchema.toJSON;
8296
- this.fromJSON = arraySchema.fromJSON;
8297
- }
8298
- diff(base, target, stream) {
8299
- const prefixOffset = stream.offset;
8300
- const targetLength = target.length;
8301
- const numTrackers = Math.ceil(targetLength / 8);
8302
- stream.grow(4 + numTrackers);
8303
- stream.writeUint32(targetLength);
8304
- let trackerOffset = stream.offset;
8305
- stream.offset = trackerOffset + numTrackers;
8306
- let tracker = 0;
8307
- let numPatch = 0;
8308
- const baseLength = base.length;
8309
- const valueSchema = this.muData;
8310
- let prev = base.length > 0 ? base[0] : valueSchema.identity;
8311
- for (let i = 0; i < target.length; ++i) {
8312
- if (valueSchema.diff(prev, target[i], stream)) {
8313
- tracker |= 1 << (i & 7);
8314
- ++numPatch;
8315
- }
8316
- if ((i & 7) === 7) {
8317
- stream.writeUint8At(trackerOffset++, tracker);
8318
- tracker = 0;
8319
- }
8320
- prev = target[i];
8321
- }
8322
- if (targetLength & 7) {
8323
- stream.writeUint8At(trackerOffset, tracker);
8324
- }
8325
- if (numPatch > 0 || baseLength !== targetLength) {
8326
- return true;
8327
- }
8328
- stream.offset = prefixOffset;
8329
- return false;
8330
- }
8331
- patch(base, stream) {
8332
- const result = this.clone(base);
8333
- const targetLength = stream.readUint32();
8334
- result.length = targetLength;
8335
- let trackerOffset = stream.offset;
8336
- const numTrackers = Math.ceil(targetLength / 8);
8337
- stream.offset = trackerOffset + numTrackers;
8338
- let tracker = 0;
8339
- const valueSchema = this.muData;
8340
- let prev = base.length > 0 ? base[0] : valueSchema.identity;
8341
- for (let i = 0; i < targetLength; ++i) {
8342
- const mod8 = i & 7;
8343
- if (!mod8) {
8344
- tracker = stream.readUint8At(trackerOffset++);
8345
- }
8346
- if (1 << mod8 & tracker) {
8347
- prev = result[i] = valueSchema.patch(prev, stream);
8348
- } else {
8349
- result[i] = valueSchema.clone(prev);
8350
- }
8351
- }
8352
- return result;
8353
- }
8354
- };
8355
-
8356
- // src/vendor/mudb/box-schema/voxel/block-schema.ts
8357
- var BlockInfoSchema = new MuStruct({
8358
- // voxel info
8359
- ids: new RelativeArray(new MuRelativeVarint(), Infinity, [0]),
8360
- names: new MuArray(new MuUTF8(), Infinity, ["air"]),
8361
- // run length encode these arrays
8362
- emissive: new RelativeArray(new MuVarint(), Infinity, [0]),
8363
- texture: new RelativeArray(
8364
- new MuRelativeVarint(),
8365
- Infinity,
8366
- [0, 0, 0, 0, 0, 0]
8367
- ),
8368
- animLength: new RelativeArray(new MuVarint(1), Infinity, [1, 1, 1, 1, 1, 1]),
8369
- friction: new RelativeArray(new MuQuantizedFloat(1 / 256, 1), Infinity, [0]),
8370
- restitution: new RelativeArray(new MuQuantizedFloat(1 / 256, 0), Infinity, [
8371
- 0
8372
- ]),
8373
- velocity: new RelativeArray(new MuFloat32(0), Infinity, [0, 0, 0]),
8374
- // fluids
8375
- fluids: new MuArray(
8376
- new MuStruct({
8377
- id: new MuVarint(),
8378
- info: new MuUint32(),
8379
- mass: new MuFloat64(1)
8380
- }),
8381
- Infinity,
8382
- [
8383
- {
8384
- id: 0,
8385
- info: 0,
8386
- mass: 0
8387
- }
8388
- ]
8389
- ),
8390
- // category list
8391
- category: new MuDictionary(
8392
- new RelativeArray(new MuRelativeVarint(), Infinity),
8393
- Infinity,
8394
- {
8395
- default: [0]
8396
- }
8397
- ),
8398
- // texture info
8399
- atlasRadius: new MuVarint(16),
8400
- blockBumpShift: new MuVarint(6),
8401
- blockColorShift: new MuVarint(4),
8402
- // texture atlases
8403
- colorAtlas: new MuArray(HashSchema, Infinity),
8404
- materialAtlas: new MuArray(HashSchema, Infinity),
8405
- bumpAtlas: new MuArray(HashSchema, Infinity)
8645
+ // src/vendor/mudb/box-schema/events/script-events.ts
8646
+ new MuStruct({
8647
+ damage: DamageEventSchema
8648
+ });
8649
+
8650
+ // src/vendor/mudb/box-schema/physics/collision-filter-schema.ts
8651
+ var CollisionGroupSchema = new MuSortedArray(
8652
+ new MuVarint(),
8653
+ Infinity,
8654
+ (a, b) => a - b
8655
+ );
8656
+ var CollisionFilterSchema = new MuArray(
8657
+ CollisionGroupSchema,
8658
+ Infinity
8659
+ );
8660
+
8661
+ // src/vendor/mudb/box-schema/zone/zone-schema.ts
8662
+ var ZoneSchema = new MuStruct({
8663
+ id: new MuVarint(),
8664
+ // bounds
8665
+ minX: new MuQuantizedFloat(1 / 16, 0),
8666
+ minY: new MuQuantizedFloat(1 / 16, 0),
8667
+ minZ: new MuQuantizedFloat(1 / 16, 0),
8668
+ maxX: new MuQuantizedFloat(1 / 16, 0),
8669
+ maxY: new MuQuantizedFloat(1 / 16, 0),
8670
+ maxZ: new MuQuantizedFloat(1 / 16, 0)
8671
+ });
8672
+ var ZoneSelectorGroupSchema = new MuStruct({
8673
+ id: new MuVarint(),
8674
+ groupBits: new MuSortedArray(new MuVarint(), Infinity, (a, b) => a - b)
8675
+ });
8676
+ var ZoneSelectorGroupSetSchema = new MuSortedArray(
8677
+ ZoneSelectorGroupSchema,
8678
+ Infinity,
8679
+ compareId
8680
+ );
8681
+ var PhysicsZoneSchema = new MuStruct({
8682
+ // selector group for this zone
8683
+ selectorGroup: new MuVarint(0),
8684
+ // physics effects
8685
+ massScale: new MuQuantizedFloat(1 / 256, 0),
8686
+ fx: new MuQuantizedFloat(1 / 1024, 0),
8687
+ fy: new MuQuantizedFloat(1 / 1024, 0),
8688
+ fz: new MuQuantizedFloat(1 / 1024, 0),
8689
+ ...ZoneSchema.muData
8690
+ });
8691
+ var PhysicsZoneSetSchema = new MuSortedArray(
8692
+ PhysicsZoneSchema,
8693
+ Infinity,
8694
+ compareId
8695
+ );
8696
+ var EnvironmentZoneSchema = new MuStruct({
8697
+ // weather effects
8698
+ overrideFog: new MuBoolean(false),
8699
+ fog: FogRDA.stateSchema,
8700
+ overrideSky: new MuBoolean(false),
8701
+ sky: SkyRDA.stateSchema,
8702
+ overrideSnow: new MuBoolean(false),
8703
+ snow: SnowRDA.stateSchema,
8704
+ overrideRain: new MuBoolean(false),
8705
+ rain: RainRDA.stateSchema,
8706
+ ...ZoneSchema.muData
8406
8707
  });
8708
+ var EnvironmentZoneSetSchema = new MuSortedArray(
8709
+ EnvironmentZoneSchema,
8710
+ Infinity,
8711
+ compareId
8712
+ );
8713
+
8714
+ // src/vendor/mudb/box-schema/environment/environment-schema.ts
8715
+ var EnvironmentParamSchema = EnvironmentRDA.stateSchema;
8407
8716
  new MuStruct({
8408
- id: new MuDictionary(new MuVarint(), Infinity, { air: 0 }),
8409
- valid: new MuArray(new MuBoolean(false), Infinity),
8410
- name: new MuArray(new MuUTF8(), Infinity, ["air"]),
8411
- emissive: new MuArray(new MuUint16(), Infinity, [0]),
8412
- texture: new MuArray(new MuUint32(), Infinity, [0, 0, 0, 0, 0, 0]),
8413
- mass: new MuArray(new MuFloat64(), Infinity, [0]),
8414
- restitution: new MuArray(new MuFloat64(), Infinity, [0]),
8415
- friction: new MuArray(new MuFloat64(), Infinity, [0]),
8416
- fluid: new MuArray(new MuVarint(), Infinity, [1]),
8417
- fluidInfo: new MuArray(new MuUint32(), Infinity, [0, 0]),
8418
- velocity: new MuArray(new MuFloat32(0), Infinity, [0, 0, 0]),
8419
- isFluid: new MuRefSchema()
8717
+ timeOfDay: new MuFloat64(0),
8718
+ sunUp: new MuBoolean(false),
8719
+ globalLight: new MuFloat64(0),
8720
+ gamma: new MuFloat32(1.3),
8721
+ drawDistance: new MuFloat32(512),
8722
+ freezeDrawDistance: new MuBoolean(false),
8723
+ sunDirection: Vec3Schema,
8724
+ sunColor: Vec3Schema,
8725
+ skyLeft: Vec3Schema,
8726
+ skyRight: Vec3Schema,
8727
+ skyTop: Vec3Schema,
8728
+ skyBottom: Vec3Schema,
8729
+ skyFront: Vec3Schema,
8730
+ skyBack: Vec3Schema,
8731
+ fogParams: Vec4Schema,
8732
+ fogParams2: Vec4Schema,
8733
+ fogParams3: Vec4Schema
8420
8734
  });
8421
8735
 
8422
8736
  // src/vendor/mudb/box-schema/script/script-protocol.ts
@@ -12906,146 +13220,6 @@ new MuSortedArray(
12906
13220
  Infinity,
12907
13221
  compareNetPublic
12908
13222
  );
12909
- var decodeUTF8;
12910
- {
12911
- new TextEncoder();
12912
- const decoder = new TextDecoder();
12913
- decodeUTF8 = (bytes) => decoder.decode(bytes);
12914
- }
12915
- var MuBuffer = class {
12916
- buffer;
12917
- dataView;
12918
- uint8;
12919
- constructor(buffer) {
12920
- this.buffer = buffer;
12921
- this.dataView = new DataView(buffer);
12922
- this.uint8 = new Uint8Array(buffer);
12923
- }
12924
- };
12925
- var bufferPool = new Array(31);
12926
- for (let i = 0; i < 31; ++i) {
12927
- bufferPool[i] = [];
12928
- }
12929
- var LITTLE_ENDIAN = true;
12930
- var MuReadStream = class {
12931
- buffer;
12932
- offset;
12933
- length;
12934
- constructor(data) {
12935
- this.buffer = new MuBuffer(data.buffer);
12936
- this.offset = data.byteOffset;
12937
- this.length = data.byteLength + data.byteOffset;
12938
- }
12939
- bytes() {
12940
- return this.buffer.uint8.subarray(this.offset, this.length);
12941
- }
12942
- checkBounds() {
12943
- if (this.offset > this.length) {
12944
- throw new Error("out of bounds");
12945
- }
12946
- }
12947
- readInt8() {
12948
- const offset = this.offset;
12949
- this.offset += 1;
12950
- this.checkBounds();
12951
- return this.buffer.dataView.getInt8(offset);
12952
- }
12953
- readInt16() {
12954
- const offset = this.offset;
12955
- this.offset += 2;
12956
- this.checkBounds();
12957
- return this.buffer.dataView.getInt16(offset, LITTLE_ENDIAN);
12958
- }
12959
- readInt32() {
12960
- const offset = this.offset;
12961
- this.offset += 4;
12962
- this.checkBounds();
12963
- return this.buffer.dataView.getInt32(offset, LITTLE_ENDIAN);
12964
- }
12965
- readUint8() {
12966
- const offset = this.offset;
12967
- this.offset += 1;
12968
- this.checkBounds();
12969
- return this.buffer.dataView.getUint8(offset);
12970
- }
12971
- readUint16() {
12972
- const offset = this.offset;
12973
- this.offset += 2;
12974
- this.checkBounds();
12975
- return this.buffer.dataView.getUint16(offset, LITTLE_ENDIAN);
12976
- }
12977
- readUint32() {
12978
- const offset = this.offset;
12979
- this.offset += 4;
12980
- this.checkBounds();
12981
- return this.buffer.dataView.getUint32(offset, LITTLE_ENDIAN);
12982
- }
12983
- readFloat32() {
12984
- const offset = this.offset;
12985
- this.offset += 4;
12986
- this.checkBounds();
12987
- return this.buffer.dataView.getFloat32(offset, LITTLE_ENDIAN);
12988
- }
12989
- readFloat64() {
12990
- const offset = this.offset;
12991
- this.offset += 8;
12992
- this.checkBounds();
12993
- return this.buffer.dataView.getFloat64(offset, LITTLE_ENDIAN);
12994
- }
12995
- readVarint() {
12996
- const bytes = this.buffer.uint8;
12997
- let offset = this.offset;
12998
- const x0 = bytes[offset++];
12999
- if (x0 < 128) {
13000
- this.offset = offset;
13001
- this.checkBounds();
13002
- return x0;
13003
- }
13004
- const x1 = bytes[offset++];
13005
- if (x1 < 128) {
13006
- this.offset = offset;
13007
- this.checkBounds();
13008
- return x0 & 127 | x1 << 7;
13009
- }
13010
- const x2 = bytes[offset++];
13011
- if (x2 < 128) {
13012
- this.offset = offset;
13013
- this.checkBounds();
13014
- return x0 & 127 | (x1 & 127) << 7 | x2 << 14;
13015
- }
13016
- const x3 = bytes[offset++];
13017
- if (x3 < 128) {
13018
- this.offset = offset;
13019
- this.checkBounds();
13020
- return x0 & 127 | (x1 & 127) << 7 | (x2 & 127) << 14 | x3 << 21;
13021
- }
13022
- const x4 = bytes[offset++];
13023
- this.offset = offset;
13024
- this.checkBounds();
13025
- return (x0 & 127) + ((x1 & 127) << 7) + ((x2 & 127) << 14) + ((x3 & 127) << 21) + x4 * (1 << 28);
13026
- }
13027
- readASCII(length) {
13028
- const head = this.offset;
13029
- this.offset += length;
13030
- this.checkBounds();
13031
- let str = "";
13032
- for (let i = head; i < this.offset; ++i) {
13033
- str += String.fromCharCode(this.buffer.uint8[i]);
13034
- }
13035
- return str;
13036
- }
13037
- readString() {
13038
- const byteLength = this.readVarint();
13039
- const head = this.offset;
13040
- this.offset += byteLength;
13041
- this.checkBounds();
13042
- const bytes = this.buffer.uint8.subarray(head, this.offset);
13043
- return decodeUTF8(bytes);
13044
- }
13045
- readUint8At(offset) {
13046
- return this.buffer.dataView.getUint8(offset);
13047
- }
13048
- };
13049
13223
 
13050
13224
  // src/core/engine/world-seed.ts
13051
13225
  var WORLD_SEED_VERSION = 2;
@@ -13074,6 +13248,21 @@ function asQuat(value) {
13074
13248
  asNumber(w, 1)
13075
13249
  ];
13076
13250
  }
13251
+ function archiveToWorldSeedSource(archive) {
13252
+ const world = archive.worlds[0];
13253
+ if (!world) {
13254
+ throw new Error("cajonzero: boxplay archive contains no world to seed");
13255
+ }
13256
+ const files = /* @__PURE__ */ new Map();
13257
+ for (const file of archive.files) {
13258
+ files.set(file.path, file.data);
13259
+ }
13260
+ return {
13261
+ worldId: world.worldId,
13262
+ manifest: world.manifest,
13263
+ reader: { read: (relPath) => files.get(relPath) ?? null }
13264
+ };
13265
+ }
13077
13266
  async function readJsonFile(source, relPath) {
13078
13267
  const bytes = await source.reader.read(`worlds/${source.worldId}/${relPath}`);
13079
13268
  if (!bytes) return null;
@@ -13129,6 +13318,45 @@ function entityToSeedBody(entity) {
13129
13318
  const bounds = asVec3(value.bounds) ?? [1, 1, 1];
13130
13319
  const scale = asVec3(value.scale) ?? [1, 1, 1];
13131
13320
  const orientation = asQuat(value.orientation);
13321
+ const [qx, qy, qz, qw] = orientation;
13322
+ const x2 = qx + qx;
13323
+ const y2 = qy + qy;
13324
+ const z2 = qz + qz;
13325
+ const xx = qx * x2;
13326
+ const yx = qy * x2;
13327
+ const yy = qy * y2;
13328
+ const zx = qz * x2;
13329
+ const zy = qz * y2;
13330
+ const zz = qz * z2;
13331
+ const wx = qw * x2;
13332
+ const wy = qw * y2;
13333
+ const wz = qw * z2;
13334
+ const m0 = 1 - yy - zz;
13335
+ const m1 = yx + wz;
13336
+ const m2 = zx - wy;
13337
+ const m3 = yx - wz;
13338
+ const m4 = 1 - xx - zz;
13339
+ const m5 = zy + wx;
13340
+ const m6 = zx + wy;
13341
+ const m7 = zy - wx;
13342
+ const m8 = 1 - xx - yy;
13343
+ const sx = scale[0];
13344
+ const sy = scale[1];
13345
+ const sz = scale[2];
13346
+ const bx = bounds[0];
13347
+ const by = bounds[1];
13348
+ const bz = bounds[2];
13349
+ let rx = 0;
13350
+ let ry = 0;
13351
+ let rz = 0;
13352
+ for (let i = 0; i < 8; i += 1) {
13353
+ const x = 0.5 * (i & 1 ? sx : -sx) * bx;
13354
+ const y = 0.5 * (i & 2 ? sy : -sy) * by;
13355
+ const z = 0.5 * (i & 4 ? sz : -sz) * bz;
13356
+ rx = Math.max(rx, Math.abs(x * m0 + y * m3 + z * m6));
13357
+ ry = Math.max(ry, Math.abs(x * m1 + y * m4 + z * m7));
13358
+ rz = Math.max(rz, Math.abs(x * m2 + y * m5 + z * m8));
13359
+ }
13132
13360
  let flags = 0;
13133
13361
  if (value.collision === true) flags |= 2 /* COLLIDES */;
13134
13362
  if (value.gravity === true) flags |= 4 /* GRAVITY */;
@@ -13139,9 +13367,9 @@ function entityToSeedBody(entity) {
13139
13367
  px: position[0],
13140
13368
  py: position[1],
13141
13369
  pz: position[2],
13142
- rx: bounds[0] * scale[0],
13143
- ry: bounds[1] * scale[1],
13144
- rz: bounds[2] * scale[2],
13370
+ rx,
13371
+ ry,
13372
+ rz,
13145
13373
  qx: orientation[0],
13146
13374
  qy: orientation[1],
13147
13375
  qz: orientation[2],
@@ -13173,6 +13401,40 @@ async function mapLimit(items, limit, fn) {
13173
13401
  await Promise.all(workers);
13174
13402
  return results;
13175
13403
  }
13404
+ async function resolveScripts(source, head, warnings) {
13405
+ const assetsRef = await resolveRef(source, head.scriptAssets);
13406
+ if (!isRecord(assetsRef)) return void 0;
13407
+ const rawIndex = typeof head.scriptIndex === "string" ? head.scriptIndex : "";
13408
+ const modules = {};
13409
+ for (const [path6, raw] of Object.entries(assetsRef)) {
13410
+ if (!isRecord(raw)) continue;
13411
+ if (raw.isClient === true || raw.type === 7) continue;
13412
+ const hash = typeof raw.hash === "string" ? raw.hash : "";
13413
+ if (hash.length === 0) {
13414
+ warnings.push(`script module "${path6}" has no hash`);
13415
+ continue;
13416
+ }
13417
+ const bytes = await source.reader.read(
13418
+ `worlds/${source.worldId}/data/blobs/${hash}`
13419
+ );
13420
+ if (!bytes) {
13421
+ warnings.push(`script module "${path6}" is missing (${hash.slice(0, 12)}\u2026)`);
13422
+ continue;
13423
+ }
13424
+ modules[path6] = {
13425
+ hash,
13426
+ source: textDecoder.decode(bytes),
13427
+ order: asNumber(raw.order, 0),
13428
+ ...typeof raw.name === "string" && raw.name.length > 0 ? { name: raw.name } : {}
13429
+ };
13430
+ }
13431
+ if (Object.keys(modules).length === 0) return void 0;
13432
+ const index = /^_+migrated_index\.js$/.test(rawIndex) && modules["index.js"] !== void 0 ? "index.js" : rawIndex;
13433
+ if (index.length > 0 && modules[index] === void 0) {
13434
+ warnings.push(`scriptIndex "${index}" does not exist in scriptAssets`);
13435
+ }
13436
+ return { index, modules };
13437
+ }
13176
13438
  async function resolveRenderAssets(source, entities, projectState, warnings) {
13177
13439
  const extraProjectHash = typeof projectState.extraProjectHash === "string" && projectState.extraProjectHash.length > 0 ? projectState.extraProjectHash : void 0;
13178
13440
  if (extraProjectHash === void 0) {
@@ -13235,22 +13497,22 @@ async function resolveRenderAssets(source, entities, projectState, warnings) {
13235
13497
  }
13236
13498
  const meshes = [];
13237
13499
  const paths = [...referenced.keys()].sort();
13238
- await mapLimit(paths, 8, async (path5) => {
13239
- const asset = referenced.get(path5);
13500
+ await mapLimit(paths, 8, async (path6) => {
13501
+ const asset = referenced.get(path6);
13240
13502
  if (!asset) return;
13241
13503
  const metadataHash = typeof asset.hash === "string" ? asset.hash : "";
13242
13504
  if (metadataHash.length === 0) {
13243
- warnings.push(`mesh ${path5} has no metadata hash`);
13505
+ warnings.push(`mesh ${path6} has no metadata hash`);
13244
13506
  return;
13245
13507
  }
13246
13508
  const metadata = await readBlobJson(source, metadataHash);
13247
13509
  if (!isRecord(metadata)) {
13248
- warnings.push(`mesh ${path5} metadata ${metadataHash} is missing/undecodable`);
13510
+ warnings.push(`mesh ${path6} metadata ${metadataHash} is missing/undecodable`);
13249
13511
  return;
13250
13512
  }
13251
13513
  const dataHash = typeof metadata.dataHash === "string" ? metadata.dataHash : "";
13252
13514
  if (dataHash.length === 0) {
13253
- warnings.push(`mesh ${path5} metadata has no dataHash`);
13515
+ warnings.push(`mesh ${path6} metadata has no dataHash`);
13254
13516
  return;
13255
13517
  }
13256
13518
  const bounds = asVec3(metadata.bounds) ?? [1, 1, 1];
@@ -13261,13 +13523,14 @@ async function resolveRenderAssets(source, entities, projectState, warnings) {
13261
13523
  })) : [];
13262
13524
  const renderBoxOffset = asVec3(metadata.renderBoxOffset);
13263
13525
  meshes.push({
13264
- path: path5,
13526
+ path: path6,
13265
13527
  modelId: asNumber(asset.modelId, 0),
13266
13528
  metadataHash,
13267
13529
  dataHash,
13268
13530
  bounds,
13269
13531
  motionList,
13270
- ...renderBoxOffset ? { renderBoxOffset } : {}
13532
+ ...renderBoxOffset ? { renderBoxOffset } : {},
13533
+ ...typeof metadata.hashType === "string" ? { hashType: metadata.hashType } : typeof asset.hashType === "string" ? { hashType: asset.hashType } : {}
13271
13534
  });
13272
13535
  });
13273
13536
  meshes.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -13338,6 +13601,19 @@ async function resolveWorldSeed(source, options = {}) {
13338
13601
  for (const [reason, count] of skipReasons) {
13339
13602
  warnings.push(`skipped ${count} entit${count === 1 ? "y" : "ies"}: ${reason}`);
13340
13603
  }
13604
+ const seedInfo = [];
13605
+ for (const raw of entities) {
13606
+ if (!isRecord(raw)) continue;
13607
+ const value = isRecord(raw.value) ? raw.value : void 0;
13608
+ if (value === void 0) continue;
13609
+ const rawId = raw.id;
13610
+ const id = typeof rawId === "string" ? Number(rawId) : typeof rawId === "number" ? rawId : NaN;
13611
+ if (!Number.isFinite(id) || id <= 0) continue;
13612
+ const name = typeof value.name === "string" ? value.name : typeof raw.name === "string" ? raw.name : "";
13613
+ const tags = Array.isArray(value.tags) ? value.tags.filter((tag) => typeof tag === "string") : [];
13614
+ seedInfo.push({ id, name, tags });
13615
+ }
13616
+ seedInfo.sort((a, b) => a.id - b.id);
13341
13617
  const voxelsRef = await resolveRef(source, head.voxels);
13342
13618
  let voxels = null;
13343
13619
  if (isRecord(voxelsRef)) {
@@ -13370,6 +13646,15 @@ async function resolveWorldSeed(source, options = {}) {
13370
13646
  }
13371
13647
  }
13372
13648
  const render = await resolveRenderAssets(source, entities, stateRecord, warnings);
13649
+ const scripts = await resolveScripts(source, head, warnings);
13650
+ const audioRef = await resolveRef(source, head.audioAssets);
13651
+ const audioAssets = [];
13652
+ if (isRecord(audioRef)) {
13653
+ for (const [path6, raw] of Object.entries(audioRef)) {
13654
+ if (!isRecord(raw) || typeof raw.hash !== "string" || raw.hash.length === 0) continue;
13655
+ audioAssets.push({ path: path6, hash: raw.hash });
13656
+ }
13657
+ }
13373
13658
  return {
13374
13659
  version: WORLD_SEED_VERSION,
13375
13660
  mapId: source.worldId,
@@ -13382,30 +13667,14 @@ async function resolveWorldSeed(source, options = {}) {
13382
13667
  bodies,
13383
13668
  voxels,
13384
13669
  render,
13670
+ ...scripts !== void 0 ? { scripts } : {},
13671
+ ...seedInfo.length > 0 ? { seedInfo } : {},
13672
+ ...audioAssets.length > 0 ? { audioAssets } : {},
13385
13673
  entityCount: entities.length,
13386
13674
  warnings
13387
13675
  };
13388
13676
  }
13389
13677
 
13390
- // src/core/maps/block-info.ts
13391
- var BLOCK_INFO_HASH = "QmW2E9E8ipYUifsCfkMj5SULYMYbFQvP6BQxo52atXw7Zj";
13392
- function decodeBlockInfo(bytes) {
13393
- try {
13394
- return BlockInfoSchema.patch(BlockInfoSchema.identity, new MuReadStream(bytes));
13395
- } catch {
13396
- return null;
13397
- }
13398
- }
13399
- async function readBlockInfoBytes(reader, worldId, hash = BLOCK_INFO_HASH) {
13400
- try {
13401
- const bytes = await reader.read(`worlds/${worldId}/data/blobs/${hash}`);
13402
- if (bytes === null || bytes.length === 0) return null;
13403
- return bytes;
13404
- } catch {
13405
- return null;
13406
- }
13407
- }
13408
-
13409
13678
  // src/core/maps/seed-pack.ts
13410
13679
  var SEED_PACK_VERSION = 1;
13411
13680
  var SEED_PACK_FORMAT = "cajonzero-seed";
@@ -13417,14 +13686,35 @@ function bytesToBase64(bytes) {
13417
13686
  }
13418
13687
  return btoa(binary);
13419
13688
  }
13420
- function encodeSeedPack(seed, blockInfoBytes) {
13421
- const { blockInfo: _decodedPalette, ...jsonSeed } = seed;
13689
+ var SCRIPT_PACK_FORMAT = "cajonzero-scripts";
13690
+ var SCRIPT_PACK_VERSION = 1;
13691
+ function encodeScriptsPack(seed, slug) {
13692
+ const scripts = seed.scripts;
13693
+ if (scripts === void 0) throw new Error("seed has no scripts");
13694
+ const document = {
13695
+ format: SCRIPT_PACK_FORMAT,
13696
+ version: SCRIPT_PACK_VERSION,
13697
+ slug,
13698
+ mapId: seed.mapId,
13699
+ index: scripts.index,
13700
+ modules: scripts.modules,
13701
+ ...scripts.revision !== void 0 && scripts.revision.length > 0 ? { revision: scripts.revision } : {}
13702
+ };
13703
+ return new TextEncoder().encode(JSON.stringify(document));
13704
+ }
13705
+ function encodeSeedPack(seed, blockInfoBytes, options = {}) {
13706
+ const {
13707
+ blockInfo: _decodedPalette,
13708
+ scripts: _privateScripts,
13709
+ ...jsonSeed
13710
+ } = seed;
13422
13711
  const document = {
13423
13712
  format: SEED_PACK_FORMAT,
13424
13713
  version: SEED_PACK_VERSION,
13425
13714
  mapId: seed.mapId,
13426
13715
  seed: {
13427
13716
  ...jsonSeed,
13717
+ ...options.includeScripts === true && seed.scripts !== void 0 ? { scripts: seed.scripts } : {},
13428
13718
  voxels: seed.voxels === null || seed.voxels === void 0 ? null : {
13429
13719
  shape: seed.voxels.shape,
13430
13720
  chunkCount: seed.voxels.chunkCount,
@@ -13437,143 +13727,330 @@ function encodeSeedPack(seed, blockInfoBytes) {
13437
13727
  return new TextEncoder().encode(JSON.stringify(document));
13438
13728
  }
13439
13729
 
13440
- // src/core/maps/http.ts
13441
- var MapSourceError = class extends Error {
13730
+ // src/maps/import.ts
13731
+ var DEFAULT_MAPS_FILE = "cajonzero/maps.json";
13732
+ var DEFAULT_PUBLIC_DIR = "public";
13733
+ var DEFAULT_SCRIPTS_DIR = path4.join("cajonzero", "private");
13734
+ var MapImportError = class extends Error {
13442
13735
  code;
13443
13736
  constructor(code, message) {
13444
13737
  super(message);
13445
- this.name = "MapSourceError";
13738
+ this.name = "MapImportError";
13446
13739
  this.code = code;
13447
13740
  }
13448
13741
  };
13449
- var DEFAULT_TIMEOUT_MS = 15e3;
13450
- var DEFAULT_MAX_BYTES = 32 * 1024 * 1024;
13451
- var DEFAULT_CONCURRENCY = 8;
13452
- function fail2(code, message) {
13453
- throw new MapSourceError(code, message);
13454
- }
13455
- function describeError(error) {
13456
- return error instanceof Error ? error.message : String(error);
13742
+ function resolveOptions(options) {
13743
+ const cwd = options.cwd ?? process.cwd();
13744
+ return {
13745
+ cwd,
13746
+ mapsPath: path4.resolve(cwd, options.maps ?? DEFAULT_MAPS_FILE),
13747
+ publicDir: path4.resolve(cwd, options.publicDir ?? DEFAULT_PUBLIC_DIR),
13748
+ scriptsDir: path4.resolve(cwd, options.scriptsDir ?? DEFAULT_SCRIPTS_DIR),
13749
+ gzip: options.gzip ?? true,
13750
+ resetRoot: options.resetRoot ?? false
13751
+ };
13457
13752
  }
13458
- var HttpMapSource = class {
13459
- /** Normalized root URL. */
13460
- root;
13461
- fetchImpl;
13462
- timeoutMs;
13463
- maxBytes;
13464
- cache = /* @__PURE__ */ new Map();
13465
- inflight = /* @__PURE__ */ new Map();
13466
- constructor(root, options = {}) {
13467
- this.root = normalizeRootUrl(root);
13468
- this.fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
13469
- this.timeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
13470
- this.maxBytes = options.maxDownloadBytes ?? DEFAULT_MAX_BYTES;
13471
- }
13472
- /** Read one archive-relative file; `null` when it does not exist (404). */
13473
- async read(relPath) {
13474
- const relative = assertRelativeMapPath(relPath);
13475
- if (this.cache.has(relative)) return this.cache.get(relative) ?? null;
13476
- let pending = this.inflight.get(relative);
13477
- if (pending === void 0) {
13478
- pending = this.fetchFile(relative);
13479
- this.inflight.set(relative, pending);
13480
- }
13481
- let bytes;
13482
- try {
13483
- bytes = await pending;
13484
- } finally {
13485
- this.inflight.delete(relative);
13753
+ function readMapCatalogFile(mapsPath) {
13754
+ if (!existsSync(mapsPath)) return EMPTY_MAP_CATALOG;
13755
+ let parsed;
13756
+ try {
13757
+ parsed = JSON.parse(readFileSync(mapsPath, "utf8").replace(/^\uFEFF/, ""));
13758
+ } catch (error) {
13759
+ throw new MapImportError(
13760
+ "INVALID_CATALOG",
13761
+ `${mapsPath} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
13762
+ );
13763
+ }
13764
+ try {
13765
+ return parseMapCatalog(parsed);
13766
+ } catch (error) {
13767
+ if (error instanceof MapCatalogError) {
13768
+ throw new MapImportError("INVALID_CATALOG", `${mapsPath}: ${error.message}`);
13486
13769
  }
13487
- this.cache.set(relative, bytes);
13488
- return bytes;
13770
+ throw error;
13489
13771
  }
13490
- /** Read + parse a JSON file; `null` when absent or not JSON. */
13491
- async readJson(relPath) {
13492
- const bytes = await this.read(relPath);
13493
- if (bytes === null) return null;
13772
+ }
13773
+ function writeFileAtomic(filePath, data) {
13774
+ mkdirSync(path4.dirname(filePath), { recursive: true });
13775
+ const temp = `${filePath}.tmp-${process.pid}-${Date.now().toString(36)}`;
13776
+ writeFileSync(temp, data);
13777
+ renameSync(temp, filePath);
13778
+ }
13779
+ function writeCatalog(mapsPath, catalog) {
13780
+ const document = {
13781
+ version: catalog.version,
13782
+ maps: catalog.maps
13783
+ };
13784
+ writeFileAtomic(mapsPath, `${JSON.stringify(document, null, 2)}
13785
+ `);
13786
+ }
13787
+ function deriveMapSlug(archive, override) {
13788
+ if (override !== void 0) {
13494
13789
  try {
13495
- return JSON.parse(new TextDecoder().decode(bytes));
13790
+ return assertMapSlug(override);
13496
13791
  } catch {
13497
- return null;
13792
+ throw new MapImportError("INVALID_SLUG", `invalid map slug "${override}"`);
13498
13793
  }
13499
13794
  }
13500
- async fetchFile(relPath) {
13501
- let url;
13502
- try {
13503
- url = mapFilePath(this.root, relPath);
13504
- } catch (error) {
13505
- fail2("MAP_INVALID_FILE", `${relPath}: ${describeError(error)}`);
13506
- }
13507
- const controller = new AbortController();
13508
- const timer = setTimeout(() => controller.abort(), this.timeoutMs);
13509
- let response;
13510
- try {
13511
- response = await this.fetchImpl(url, {
13512
- method: "GET",
13513
- signal: controller.signal,
13514
- redirect: "follow",
13515
- headers: { accept: "application/octet-stream, application/json;q=0.9, */*;q=0.8" }
13516
- });
13517
- } catch (error) {
13518
- clearTimeout(timer);
13519
- const reason = controller.signal.aborted ? `timed out after ${this.timeoutMs}ms` : describeError(error);
13520
- fail2("MAP_FETCH_FAILED", `${relPath}: ${reason}`);
13521
- } finally {
13522
- clearTimeout(timer);
13523
- }
13524
- if (response.status === 404) return null;
13525
- if (!response.ok) {
13526
- fail2("MAP_FETCH_FAILED", `${relPath}: HTTP ${response.status}`);
13527
- }
13528
- const declared = Number(response.headers.get("content-length") ?? "");
13529
- if (Number.isFinite(declared) && declared > this.maxBytes) {
13530
- fail2("MAP_TOO_LARGE", `${relPath}: ${declared} bytes exceeds the ${this.maxBytes}-byte cap`);
13531
- }
13532
- const buffer = await response.arrayBuffer();
13533
- if (buffer.byteLength > this.maxBytes) {
13534
- fail2(
13535
- "MAP_TOO_LARGE",
13536
- `${relPath}: ${buffer.byteLength} bytes exceeds the ${this.maxBytes}-byte cap`
13795
+ const world = archive.worlds[0];
13796
+ const candidates = [
13797
+ archive.bundle.permanentMapGroupId,
13798
+ archive.bundle.mapGroupId,
13799
+ world?.worldId
13800
+ ];
13801
+ for (const candidate of candidates) {
13802
+ if (typeof candidate !== "string") continue;
13803
+ const slug = toMapSlug(candidate);
13804
+ if (slug.length > 0) return slug;
13805
+ }
13806
+ throw new MapImportError(
13807
+ "INVALID_SLUG",
13808
+ "cannot derive a slug from the archive; pass --slug <slug>"
13809
+ );
13810
+ }
13811
+ function toMapSlug(raw) {
13812
+ const slug = raw.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^[-_]+/, "").replace(/[-_]+$/, "").slice(0, 63);
13813
+ return isMapSlug(slug) ? slug : "";
13814
+ }
13815
+ function mapPublicDir(publicDir, slug) {
13816
+ return path4.join(publicDir, "maps", slug);
13817
+ }
13818
+ function seedPackName(gzip) {
13819
+ return gzip ? "seed.json.gz" : "seed.json";
13820
+ }
13821
+ function scriptsPackPath(scriptsDir, slug, gzip) {
13822
+ return path4.join(scriptsDir, `${slug}.json${gzip ? ".gz" : ""}`);
13823
+ }
13824
+ function publicRoot(slug) {
13825
+ return `/maps/${slug}/`;
13826
+ }
13827
+ function readArchive(archivePath) {
13828
+ if (!existsSync(archivePath) || !statSync(archivePath).isFile()) {
13829
+ throw new MapImportError("ARCHIVE_NOT_FOUND", `archive not found: ${archivePath}`);
13830
+ }
13831
+ try {
13832
+ return readBoxplayFile(archivePath);
13833
+ } catch (error) {
13834
+ throw new MapImportError(
13835
+ "INVALID_ARCHIVE",
13836
+ `cannot read ${path4.basename(archivePath)}: ${error instanceof Error ? error.message : String(error)}`
13837
+ );
13838
+ }
13839
+ }
13840
+ async function importMap(action, archivePath, options, slugOverride) {
13841
+ const resolved = resolveOptions(options);
13842
+ const archive = readArchive(path4.resolve(resolved.cwd, archivePath));
13843
+ const slug = deriveMapSlug(archive, slugOverride ?? options.slug);
13844
+ const catalog = readMapCatalogFile(resolved.mapsPath);
13845
+ const existing = findMap(catalog, slug);
13846
+ const targetDir = mapPublicDir(resolved.publicDir, slug);
13847
+ const targetExists = existsSync(targetDir);
13848
+ if (action === "add") {
13849
+ if (existing !== void 0 || targetExists) {
13850
+ throw new MapImportError(
13851
+ "SLUG_EXISTS",
13852
+ `map "${slug}" already exists; use: cj0 update ${slug} <file.boxplay>`
13537
13853
  );
13538
13854
  }
13539
- return new Uint8Array(buffer);
13855
+ } else if (existing === void 0) {
13856
+ throw new MapImportError(
13857
+ "MAP_NOT_FOUND",
13858
+ `map "${slug}" is not in ${path4.relative(resolved.cwd, resolved.mapsPath)}; use: cj0 add <file.boxplay> --slug ${slug}`
13859
+ );
13540
13860
  }
13541
- };
13542
- function isRecord2(value) {
13543
- return typeof value === "object" && value !== null && !Array.isArray(value);
13544
- }
13545
- async function resolveWorldId(source) {
13546
- const bundle = await source.readJson("bundle.json");
13547
- if (bundle === null) fail2("MAP_NO_WORLD", "bundle.json is missing or not JSON");
13548
- const members = Array.isArray(bundle.members) ? bundle.members : [];
13549
- const records = members.filter(isRecord2);
13550
- const main2 = records.find((member) => member.mapRole === "main") ?? records[0];
13551
- const candidate = (main2 !== void 0 && typeof main2.id === "string" ? main2.id : void 0) ?? (typeof bundle.mainProjectId === "string" ? bundle.mainProjectId : void 0);
13552
- if (candidate === void 0) {
13553
- fail2("MAP_NO_WORLD", "bundle.json does not name a world (no member id, no mainProjectId)");
13554
- }
13555
- return assertWorldId(candidate);
13556
- }
13557
- async function loadWorldSeedGraphFromHttp(entry, options = {}) {
13558
- const source = new HttpMapSource(entry.root, options);
13559
- const worldId = entry.worldId ?? await resolveWorldId(source);
13560
- const manifest = await source.readJson(
13561
- `worlds/${worldId}/manifest.json`
13562
- );
13563
- if (manifest === null) {
13564
- fail2("MAP_NO_WORLD", `worlds/${worldId}/manifest.json is missing or not JSON`);
13861
+ const world = archive.worlds[0];
13862
+ if (world === void 0) {
13863
+ throw new MapImportError("INVALID_ARCHIVE", "archive contains no world");
13864
+ }
13865
+ const worldId = world.worldId;
13866
+ const source = archiveToWorldSeedSource(archive);
13867
+ let seed;
13868
+ try {
13869
+ seed = await resolveWorldSeed(source);
13870
+ } catch (error) {
13871
+ throw new MapImportError(
13872
+ "INVALID_ARCHIVE",
13873
+ `cannot resolve the map seed: ${error instanceof Error ? error.message : String(error)}`
13874
+ );
13565
13875
  }
13566
- const seed = await resolveWorldSeed(
13567
- { worldId, manifest, reader: source },
13568
- { concurrency: options.concurrency ?? DEFAULT_CONCURRENCY }
13876
+ const blockInfoBytes = await readBlockInfoBytes(source.reader, seed.mapId) ?? void 0;
13877
+ const scriptBlobs = new Set(
13878
+ Object.values(seed.scripts?.modules ?? {}).map(
13879
+ (module) => `worlds/${seed.mapId}/data/blobs/${module.hash}`
13880
+ )
13569
13881
  );
13570
- const blockInfoBytes = await readBlockInfoBytes(source, worldId) ?? void 0;
13571
- const blockInfo = blockInfoBytes === void 0 ? null : decodeBlockInfo(blockInfoBytes);
13882
+ const seedPack = encodeSeedPack(seed, blockInfoBytes);
13883
+ const seedData = resolved.gzip ? gzipSync(seedPack, { level: 9 }) : seedPack;
13884
+ const scriptPack = seed.scripts !== void 0 ? (() => {
13885
+ seed.scripts.revision = versionOf(archive, existing) ?? "";
13886
+ const packed = encodeScriptsPack(seed, slug);
13887
+ return resolved.gzip ? gzipSync(packed, { level: 9 }) : packed;
13888
+ })() : void 0;
13889
+ const mapsDir = path4.join(resolved.publicDir, "maps");
13890
+ mkdirSync(mapsDir, { recursive: true });
13891
+ const tempDir = path4.join(mapsDir, `.tmp-${slug}-${process.pid}-${Date.now().toString(36)}`);
13892
+ let files = 0;
13893
+ let bytes = 0;
13894
+ try {
13895
+ mkdirSync(tempDir, { recursive: true });
13896
+ for (const file of archive.files) {
13897
+ if (scriptBlobs.has(file.path)) continue;
13898
+ const destination = path4.join(tempDir, ...file.path.split("/"));
13899
+ mkdirSync(path4.dirname(destination), { recursive: true });
13900
+ writeFileSync(destination, file.data);
13901
+ files += 1;
13902
+ bytes += file.data.length;
13903
+ }
13904
+ const seedName = seedPackName(resolved.gzip);
13905
+ writeFileSync(path4.join(tempDir, seedName), seedData);
13906
+ files += 1;
13907
+ bytes += seedData.length;
13908
+ installPublicDir(tempDir, targetDir);
13909
+ } catch (error) {
13910
+ rmSync(tempDir, { recursive: true, force: true });
13911
+ throw error;
13912
+ }
13913
+ const scriptsPath = scriptsPackPath(resolved.scriptsDir, slug, resolved.gzip);
13914
+ if (scriptPack !== void 0) {
13915
+ writeFileAtomic(scriptsPath, scriptPack);
13916
+ rmSync(scriptsPackPath(resolved.scriptsDir, slug, !resolved.gzip), { force: true });
13917
+ } else {
13918
+ rmSync(scriptsPackPath(resolved.scriptsDir, slug, true), { force: true });
13919
+ rmSync(scriptsPackPath(resolved.scriptsDir, slug, false), { force: true });
13920
+ }
13921
+ const name = seed.projectName ?? existing?.name;
13922
+ const author = seed.author ?? existing?.author;
13923
+ const version = versionOf(archive, existing);
13924
+ const root = action === "update" && !resolved.resetRoot && existing !== void 0 ? existing.root : publicRoot(slug);
13925
+ const entry = {
13926
+ slug,
13927
+ root,
13928
+ worldId,
13929
+ ...name !== void 0 ? { name } : {},
13930
+ ...author !== void 0 ? { author } : {},
13931
+ ...version !== void 0 ? { version } : {},
13932
+ ...action === "update" && !resolved.resetRoot && existing?.assetRoot !== void 0 ? { assetRoot: existing.assetRoot } : {},
13933
+ ...action === "update" && !resolved.resetRoot && existing?.cover !== void 0 ? { cover: existing.cover } : {},
13934
+ ...scriptPack !== void 0 ? { scriptsFile: projectRelative(resolved.cwd, scriptsPath) } : {}
13935
+ };
13936
+ const maps = [...catalog.maps.filter((map) => map.slug !== slug), entry];
13937
+ writeCatalog(resolved.mapsPath, { version: catalog.version, maps });
13938
+ return {
13939
+ action,
13940
+ slug,
13941
+ root,
13942
+ worldId,
13943
+ mapId: seed.mapId,
13944
+ ...name !== void 0 ? { name } : {},
13945
+ ...version !== void 0 ? { version } : {},
13946
+ publicDir: targetDir,
13947
+ seedPack: path4.join(targetDir, seedPackName(resolved.gzip)),
13948
+ ...scriptPack !== void 0 ? {
13949
+ scriptsPack: scriptsPath,
13950
+ scriptsFile: projectRelative(resolved.cwd, scriptsPath)
13951
+ } : {},
13952
+ files,
13953
+ bytes,
13954
+ seedPackBytes: seedData.length,
13955
+ scriptModules: Object.keys(seed.scripts?.modules ?? {}).length,
13956
+ warnings: seed.warnings
13957
+ };
13958
+ }
13959
+ function projectRelative(cwd, dir) {
13960
+ return path4.relative(cwd, dir).split(path4.sep).join("/") || ".";
13961
+ }
13962
+ function defaultScriptPackFile(entry, scriptsDir) {
13963
+ const gz = scriptsPackPath(scriptsDir, entry.slug, true);
13964
+ return existsSync(gz) ? gz : scriptsPackPath(scriptsDir, entry.slug, false);
13965
+ }
13966
+ function mapScriptsFile(entry, options = {}) {
13967
+ const resolved = resolveOptions(options);
13968
+ return entry.scriptsFile !== void 0 ? path4.resolve(resolved.cwd, entry.scriptsFile) : defaultScriptPackFile(entry, resolved.scriptsDir);
13969
+ }
13970
+ function listScriptPackLocations(options = {}) {
13971
+ return readMapCatalogFile(resolveOptions(options).mapsPath).maps.flatMap((entry) => {
13972
+ const file = mapScriptsFile(entry, options);
13973
+ return existsSync(file) ? [{ slug: entry.slug, file }] : [];
13974
+ });
13975
+ }
13976
+ function versionOf(archive, existing) {
13977
+ const value = archive.bundle.version;
13978
+ if (typeof value === "string" && value.length > 0) return value;
13979
+ return existing?.version;
13980
+ }
13981
+ function installPublicDir(tempDir, targetDir) {
13982
+ const backup = `${targetDir}.bak-${process.pid}-${Date.now().toString(36)}`;
13983
+ const hadTarget = existsSync(targetDir);
13984
+ if (hadTarget) renameSync(targetDir, backup);
13985
+ try {
13986
+ renameSync(tempDir, targetDir);
13987
+ if (hadTarget) rmSync(backup, { recursive: true, force: true });
13988
+ } catch (error) {
13989
+ rmSync(targetDir, { recursive: true, force: true });
13990
+ if (hadTarget && existsSync(backup)) renameSync(backup, targetDir);
13991
+ throw error;
13992
+ }
13993
+ }
13994
+ function addMap(archivePath, options = {}) {
13995
+ return importMap("add", archivePath, options);
13996
+ }
13997
+ function updateMap(slug, archivePath, options = {}) {
13998
+ return importMap("update", archivePath, options, slug);
13999
+ }
14000
+ function listMaps(options = {}) {
14001
+ const resolved = resolveOptions(options);
14002
+ const catalog = readMapCatalogFile(resolved.mapsPath);
14003
+ return catalog.maps.map((entry) => {
14004
+ const dir = mapPublicDir(resolved.publicDir, entry.slug);
14005
+ const imported = existsSync(dir);
14006
+ const seeded = existsSync(path4.join(dir, seedPackName(true))) || existsSync(path4.join(dir, seedPackName(false)));
14007
+ const hasScripts = existsSync(mapScriptsFile(entry, options));
14008
+ return {
14009
+ slug: entry.slug,
14010
+ root: entry.root,
14011
+ ...entry.worldId !== void 0 ? { worldId: entry.worldId } : {},
14012
+ ...entry.name !== void 0 ? { name: entry.name } : {},
14013
+ ...entry.version !== void 0 ? { version: entry.version } : {},
14014
+ imported,
14015
+ seeded,
14016
+ hasScripts
14017
+ };
14018
+ });
14019
+ }
14020
+ function removeMap(slug, options = {}) {
14021
+ const resolved = resolveOptions(options);
14022
+ const catalog = readMapCatalogFile(resolved.mapsPath);
14023
+ const existing = findMap(catalog, slug);
14024
+ if (existing === void 0) {
14025
+ throw new MapImportError("MAP_NOT_FOUND", `map "${slug}" is not in the catalog`);
14026
+ }
14027
+ const dir = mapPublicDir(resolved.publicDir, slug);
14028
+ const removedDir = existsSync(dir) ? dir : void 0;
14029
+ rmSync(dir, { recursive: true, force: true });
14030
+ const removedScripts = [];
14031
+ const scriptFiles = /* @__PURE__ */ new Set([
14032
+ mapScriptsFile(existing, options),
14033
+ scriptsPackPath(resolved.scriptsDir, slug, true),
14034
+ scriptsPackPath(resolved.scriptsDir, slug, false)
14035
+ ]);
14036
+ for (const file of scriptFiles) {
14037
+ if (!existsSync(file)) continue;
14038
+ rmSync(file, { force: true });
14039
+ removedScripts.push(file);
14040
+ }
14041
+ const maps = catalog.maps.filter((map) => map.slug !== slug);
14042
+ writeCatalog(resolved.mapsPath, { version: catalog.version, maps });
13572
14043
  return {
13573
- seed: blockInfo === null ? seed : { ...seed, blockInfo },
13574
- ...blockInfoBytes !== void 0 ? { blockInfoBytes } : {}
14044
+ slug,
14045
+ ...removedDir !== void 0 ? { removedDir } : {},
14046
+ removedScripts
13575
14047
  };
13576
14048
  }
14049
+ function readScriptsPackText(file) {
14050
+ if (!existsSync(file)) return void 0;
14051
+ const raw = readFileSync(file);
14052
+ return file.endsWith(".gz") ? gunzipSync(raw).toString("utf8") : raw.toString("utf8");
14053
+ }
13577
14054
 
13578
14055
  // src/scaffold/templates.ts
13579
14056
  var SCAFFOLD_ENTRY_PATH = "src/index.ts";
@@ -13643,7 +14120,7 @@ var SCAFFOLD_CONFIG_CONTENT = `${JSON.stringify(
13643
14120
  `;
13644
14121
  var SCAFFOLD_MAPS_CONTENT = `${JSON.stringify({ version: 1, maps: [] }, null, 2)}
13645
14122
  `;
13646
- var PREPARE_ASSETS = "cj0 worker prepare";
14123
+ var SCAFFOLD_PUBLIC_KEEP_PATH = "public/.gitkeep";
13647
14124
  function packageJson(options) {
13648
14125
  const manifest = {
13649
14126
  name: options.packageName,
@@ -13651,14 +14128,12 @@ function packageJson(options) {
13651
14128
  private: true,
13652
14129
  type: "module",
13653
14130
  scripts: {
13654
- dev: `${PREPARE_ASSETS} && wrangler dev`,
13655
- build: `${PREPARE_ASSETS} && cj0 worker build-seed && wrangler deploy --dry-run`,
13656
- // Deploy always rebuilds: map content can change without `version` being
13657
- // bumped, and the room prefers the pack over the live CDN graph.
13658
- deploy: `${PREPARE_ASSETS} && cj0 worker build-seed --force && wrangler deploy`,
13659
- // `prepare` also runs after `npm install`, so a plain install leaves a
13660
- // ready-to-deploy project. Seed packs are network work and stay out of it.
13661
- prepare: PREPARE_ASSETS
14131
+ dev: "wrangler dev",
14132
+ // `cj0 deploy` syncs the packaged UI, uploads the private map-script
14133
+ // packs, then runs `wrangler deploy` — one command, no build chain.
14134
+ deploy: "cj0 deploy",
14135
+ sync: "cj0 sync",
14136
+ maps: "cj0 list"
13662
14137
  },
13663
14138
  dependencies: {
13664
14139
  cajonzero: options.cajonzeroRange,
@@ -13683,11 +14158,16 @@ function wranglerConfig(options) {
13683
14158
  ` "compatibility_date": ${JSON.stringify(options.compatibilityDate)},`,
13684
14159
  " // Deliberately NO `nodejs_compat`: cajonzero's worker path is Node-free.",
13685
14160
  ' "compatibility_flags": [],',
13686
- " // Static UI (play lobby + owner admin console) served by the worker.",
13687
- " // cajonzero ships the built assets into this directory.",
14161
+ " // QuickJS is a build-time-compiled WASM module; workerd forbids runtime",
14162
+ " // WebAssembly codegen. Keep this rule when editing the config.",
14163
+ ' "rules": [',
14164
+ ' { "type": "CompiledWasm", "globs": ["**/*.wasm"], "fallthrough": true }',
14165
+ " ],",
14166
+ " // Static UI + imported maps (play lobby + owner admin console).",
14167
+ " // `cj0 sync` writes the packaged UI here; `cj0 add` writes public/maps/.",
13688
14168
  ' "assets": {',
13689
- ' "directory": "dist/ui",',
13690
- " // Binding so the room can read seed packs bundled at deploy time.",
14169
+ ' "directory": "public",',
14170
+ " // Binding so the room can read imported maps + seed packs at runtime.",
13691
14171
  ' "binding": "ASSETS",',
13692
14172
  " // History routing: deep links (/play/x, /admin/bans) boot index.html.",
13693
14173
  ' "not_found_handling": "single-page-application",',
@@ -13774,24 +14254,25 @@ function readme(options) {
13774
14254
  "",
13775
14255
  "## \u5730\u56FE",
13776
14256
  "",
13777
- "\u5730\u56FE\u4E0D\u968F worker \u4E0A\u4F20\u3002\u628A\u89E3\u5305\u540E\u7684 `.boxplay` \u76EE\u5F55\u4F20\u5230 OSS/S3/CDN\uFF08\u9700\u5141\u8BB8 CORS\uFF09\uFF0C",
13778
- "\u518D\u5728 `cajonzero/maps.json` \u767B\u8BB0\uFF1A",
14257
+ "\u5730\u56FE\u5728\u672C\u5730\u5BFC\u5165\uFF0C\u968F worker \u4E00\u6B21\u90E8\u7F72\uFF08\u9759\u6001\u8D44\u6E90 `public/`\uFF09\uFF1A",
13779
14258
  "",
13780
- "```json",
13781
- "{",
13782
- ' "version": 1,',
13783
- ' "maps": [',
13784
- " {",
13785
- ' "slug": "omega",',
13786
- ' "root": "https://cdn.example.com/cajonzero/omega/",',
13787
- ' "worldId": "project-omega",',
13788
- ' "name": "Omega City Reloaded"',
13789
- " }",
13790
- " ]",
13791
- "}",
14259
+ "```bash",
14260
+ "npx cj0 add ./race.boxplay # \u89E3\u5305\u5230 public/maps/race/ \u5E76\u751F\u6210 seed",
14261
+ "npx cj0 update race ./race-v2.boxplay # \u8986\u76D6\u5DF2\u6709 slug",
14262
+ "npx cj0 list # \u67E5\u770B catalog + \u672C\u5730\u5BFC\u5165\u72B6\u6001",
14263
+ "npx cj0 remove race # \u5220\u9664\u5730\u56FE\u3001\u79C1\u6709\u811A\u672C\u4E0E catalog \u6761\u76EE",
13792
14264
  "```",
13793
14265
  "",
13794
- "`root` \u5FC5\u987B\u4EE5 `/` \u7ED3\u5C3E\uFF1B\u66F4\u65B0\u5730\u56FE\u540E\u5237\u65B0 CDN \u7F13\u5B58\u6216\u6362\u8DEF\u5F84\u3002",
14266
+ "`add/update` \u4F1A\uFF1A",
14267
+ "",
14268
+ "1. \u628A\u516C\u5F00\u5730\u56FE\u8D44\u6E90\u89E3\u5305\u5230 `public/maps/<slug>/`\uFF1B",
14269
+ "2. \u5728\u672C\u5730\u89E3\u6790\u4E16\u754C\u56FE\u5E76\u5199\u5165 `public/maps/<slug>/seed.json.gz`\uFF1B",
14270
+ "3. \u628A**\u670D\u52A1\u7AEF\u811A\u672C**\u5199\u5230 `cajonzero/private/<slug>.json.gz`\uFF0C\u4E0D\u8FDB\u5165 `public/`\uFF1B",
14271
+ '4. \u5728 `cajonzero/maps.json` \u5199\u5165 `"root": "/maps/<slug>/"`\uFF0C\u5E76\u628A\u670D\u52A1\u7AEF\u811A\u672C\u6587\u4EF6\u8BB0\u5F55\u4E3A',
14272
+ ' `"scriptsFile": "cajonzero/private/<slug>.json.gz"`\uFF08\u8BE5\u76EE\u5F55\u4F1A\u63D0\u4EA4\uFF0CCF \u6784\u5EFA\u65F6 `cj0 deploy` \u53EF\u4E0A\u4F20\uFF09\u3002',
14273
+ "",
14274
+ "\u9700\u8981\u72EC\u7ACB CDN \u65F6\uFF0C\u624B\u52A8\u628A\u8BE5\u6761\u76EE\u7684 `root` \u6539\u6210 `https://cdn.example.com/.../` \u5373\u53EF\uFF1B",
14275
+ "`cj0 update` \u9ED8\u8BA4\u4FDD\u7559\u4F60\u6539\u8FC7\u7684 `root`\uFF08`--reset-root` \u53EF\u6062\u590D\uFF09\u3002",
13795
14276
  "",
13796
14277
  "## \u90E8\u7F72",
13797
14278
  "",
@@ -13799,6 +14280,9 @@ function readme(options) {
13799
14280
  "npm run deploy",
13800
14281
  "```",
13801
14282
  "",
14283
+ "`cj0 deploy` \u4F1A\u540C\u6B65\u6253\u5305\u7684 UI \u5230 `public/`\u3001\u4E0A\u4F20\u79C1\u6709\u811A\u672C\uFF0C\u7136\u540E\u6267\u884C",
14284
+ "`wrangler deploy`\u3002\u9996\u6B21\u90E8\u7F72\u524D\u5148\u8FD0\u884C `npx cj0 setup`\u3002",
14285
+ "",
13802
14286
  "\u5927\u5385\u5728 `/`\uFF0C\u7BA1\u7406\u540E\u53F0\u5728 `/admin`\u3002",
13803
14287
  ""
13804
14288
  ].join("\n");
@@ -13813,6 +14297,7 @@ function buildScaffoldFiles(options) {
13813
14297
  content: SCAFFOLD_CONFIG_CONTENT.replace("<project-name>", options.packageName)
13814
14298
  },
13815
14299
  { path: "cajonzero/maps.json", content: SCAFFOLD_MAPS_CONTENT },
14300
+ { path: SCAFFOLD_PUBLIC_KEEP_PATH, content: "" },
13816
14301
  { path: ".gitignore", content: gitignore() },
13817
14302
  { path: "README.md", content: readme(options) }
13818
14303
  ];
@@ -14254,7 +14739,8 @@ function applySetup(options) {
14254
14739
  return { configPath, devVarsPath, vars, secrets, ...kvId !== void 0 ? { kvId } : {} };
14255
14740
  }
14256
14741
  var ASSETS_STAMP_FILE = ".cajonzero-assets.json";
14257
- var ASSETS_SEED_DIR = "seed";
14742
+ var ASSETS_MAPS_DIR = "maps";
14743
+ var ASSETS_KEEP_FILE = ".gitkeep";
14258
14744
  var DEFAULT_CONFIG = "wrangler.jsonc";
14259
14745
  function resolvePackageRoot() {
14260
14746
  try {
@@ -14334,7 +14820,7 @@ function assertSafeTarget(targetDir, sourceDir, configDir) {
14334
14820
  throw new Error(`assets \u76EE\u5F55\u4E0D\u80FD\u5305\u542B\u9879\u76EE\u914D\u7F6E/cajonzero \u8D44\u6E90\u76EE\u5F55\uFF1A${targetDir}`);
14335
14821
  }
14336
14822
  }
14337
- async function prepareWorkerAssets(options = {}) {
14823
+ async function syncPublicAssets(options = {}) {
14338
14824
  const cwd = options.cwd ?? process.cwd();
14339
14825
  const configPath = path4.resolve(cwd, options.configPath ?? DEFAULT_CONFIG);
14340
14826
  const configDir = path4.dirname(configPath);
@@ -14365,17 +14851,25 @@ async function prepareWorkerAssets(options = {}) {
14365
14851
  bytes: source.bytes
14366
14852
  };
14367
14853
  }
14368
- const seedDir = path4.join(targetDir, ASSETS_SEED_DIR);
14369
- const seedBackup = `${targetDir}.${ASSETS_SEED_DIR}-backup`;
14370
- if (!existsSync(seedDir) && existsSync(seedBackup)) {
14371
- mkdirSync(path4.dirname(seedDir), { recursive: true });
14372
- renameSync(seedBackup, seedDir);
14373
- }
14374
- let hasSeed = false;
14375
- if (existsSync(seedDir)) {
14376
- rmSync(seedBackup, { recursive: true, force: true });
14377
- renameSync(seedDir, seedBackup);
14378
- hasSeed = true;
14854
+ const mapsDir = path4.join(targetDir, ASSETS_MAPS_DIR);
14855
+ const mapsBackup = path4.join(configDir, `.cajonzero-maps-backup-${process.pid}`);
14856
+ const keepFile = path4.join(targetDir, ASSETS_KEEP_FILE);
14857
+ const keepBackup = path4.join(configDir, `.cajonzero-keep-backup-${process.pid}`);
14858
+ if (!existsSync(mapsDir) && existsSync(mapsBackup)) {
14859
+ mkdirSync(path4.dirname(mapsDir), { recursive: true });
14860
+ renameSync(mapsBackup, mapsDir);
14861
+ }
14862
+ let hasMaps = false;
14863
+ if (existsSync(mapsDir)) {
14864
+ rmSync(mapsBackup, { recursive: true, force: true });
14865
+ renameSync(mapsDir, mapsBackup);
14866
+ hasMaps = true;
14867
+ }
14868
+ let hasKeep = false;
14869
+ if (existsSync(keepFile)) {
14870
+ rmSync(keepBackup, { force: true });
14871
+ renameSync(keepFile, keepBackup);
14872
+ hasKeep = true;
14379
14873
  }
14380
14874
  try {
14381
14875
  rmSync(targetDir, { recursive: true, force: true });
@@ -14386,10 +14880,15 @@ async function prepareWorkerAssets(options = {}) {
14386
14880
  filter: (from) => path4.basename(from) !== ASSETS_STAMP_FILE
14387
14881
  });
14388
14882
  } finally {
14389
- if (hasSeed && existsSync(seedBackup)) {
14883
+ if (hasMaps && existsSync(mapsBackup)) {
14884
+ mkdirSync(targetDir, { recursive: true });
14885
+ rmSync(mapsDir, { recursive: true, force: true });
14886
+ renameSync(mapsBackup, mapsDir);
14887
+ }
14888
+ if (hasKeep && existsSync(keepBackup)) {
14390
14889
  mkdirSync(targetDir, { recursive: true });
14391
- rmSync(seedDir, { recursive: true, force: true });
14392
- renameSync(seedBackup, seedDir);
14890
+ rmSync(keepFile, { force: true });
14891
+ renameSync(keepBackup, keepFile);
14393
14892
  }
14394
14893
  }
14395
14894
  const next = {
@@ -14416,41 +14915,37 @@ Usage:
14416
14915
  cj0 <command> [options]
14417
14916
 
14418
14917
  Commands:
14419
- create <folder> Scaffold a deployable project
14420
- setup [--config <file>] Configure ENV values + KV namespace
14421
- serve [--port <port>] [--host <host>] Run the cajonzero server locally (workerd)
14422
- worker prepare [--config <file>] Sync the packaged UI into assets.directory
14423
- maps pack <slug> Precompute a seed pack (1 request/room)
14424
- worker build-seed [slug...] Build seed packs into worker assets
14425
-
14426
- Worker prepare options:
14427
- --config <file> Wrangler config (default: wrangler.jsonc; JSON/JSONC)
14428
- --assets <dir> Override assets.directory (relative to the config file)
14429
- --force Re-copy even when the fingerprint already matches
14430
- -h, --help Show this help
14918
+ create <folder> Scaffold a deployable project
14919
+ setup [--config <file>] Configure ENV values + KV namespace
14920
+ add <file.boxplay> [--slug <slug>] Import a map (public/maps + seed)
14921
+ update <slug> <file.boxplay> Replace an existing map
14922
+ list [--json] Show the map catalog
14923
+ remove <slug> Delete a map from the catalog
14924
+ sync [--config <file>] Copy the packaged UI into public/
14925
+ deploy [--config <file>] Sync UI, upload scripts, wrangler deploy
14926
+ serve [--port <port>] [--host <host>] Run the cajonzero server locally (workerd)
14431
14927
 
14432
- Worker build-seed options:
14433
- --config <file> Wrangler config (default: wrangler.jsonc)
14434
- --maps <file> Catalog to read (default: cajonzero/maps.json)
14435
- --assets <dir> Override assets.directory (default: from the config)
14436
- --force Rebuild even when the pack is up to date
14437
- --no-gzip Write plain JSON (lower CPU, larger asset)
14438
- -h, --help Show this help
14928
+ Map options (add/update/list/remove):
14929
+ --maps <file> Catalog path (default: cajonzero/maps.json)
14930
+ --public <dir> Static-asset dir (default: public)
14931
+ --scripts <dir> Private scripts dir (default: cajonzero/private)
14932
+ --slug <slug> Override the slug (add only)
14933
+ --no-gzip Write plain JSON instead of gzip packs
14934
+ --reset-root update: reset a custom root back to /maps/<slug>/
14935
+ --json Machine-readable summary (add/update/list/remove)
14936
+ -h, --help Show this help
14439
14937
 
14440
- Maps pack options:
14441
- --maps <file> Catalog to read (default: cajonzero/maps.json)
14442
- --out <file> Output path (default: <slug>.seed.json[.gz])
14443
- --no-gzip Write plain JSON instead of gzip (lower CPU, larger file)
14444
- -h, --help Show this help
14938
+ Sync/deploy options:
14939
+ --config <file> Wrangler config (default: wrangler.jsonc)
14940
+ --no-ui deploy: do not copy the packaged UI
14941
+ --no-scripts deploy: do not upload private map scripts
14942
+ -h, --help Show this help
14445
14943
 
14446
14944
  Serve options:
14447
- --port <port> Listener port (default: 8787)
14448
- --host <host> Listener host (default: 127.0.0.1)
14449
- --config <file> Wrangler config (default: wrangler.jsonc)
14450
- -h, --help Show this help
14451
-
14452
- Maps: unpack the .boxplay, upload the directory to OSS/CDN, and list its root
14453
- in cajonzero/maps.json. The CLI does not import map archives.
14945
+ --port <port> Listener port (default: 8787)
14946
+ --host <host> Listener host (default: 127.0.0.1)
14947
+ --config <file> Wrangler config (default: wrangler.jsonc)
14948
+ -h, --help Show this help
14454
14949
  `;
14455
14950
  var DEFAULT_SERVE_PORT = 8787;
14456
14951
  var DEFAULT_SERVE_HOST = "127.0.0.1";
@@ -14466,33 +14961,389 @@ function localDevVars() {
14466
14961
  }
14467
14962
  return Object.keys(vars).length > 0 ? vars : void 0;
14468
14963
  }
14469
- function printUsage() {
14470
- process.stdout.write(USAGE);
14964
+ function printUsage() {
14965
+ process.stdout.write(USAGE);
14966
+ }
14967
+ function fail3(message) {
14968
+ process.stderr.write(`cajonzero: ${message}
14969
+ `);
14970
+ process.exitCode = 1;
14971
+ }
14972
+ function errorLabel(error) {
14973
+ if (error instanceof ScaffoldError) {
14974
+ return `${error.code}: ${error.message}`;
14975
+ }
14976
+ return error instanceof Error ? error.message : String(error);
14977
+ }
14978
+ function readFlagValue(args, index, flag) {
14979
+ const inline = args[index];
14980
+ if (inline !== void 0 && inline.startsWith(`${flag}=`)) {
14981
+ return inline.slice(flag.length + 1);
14982
+ }
14983
+ const value = args[index + 1];
14984
+ if (value === void 0 || value.startsWith("-")) {
14985
+ throw new Error(`${flag} requires a value`);
14986
+ }
14987
+ return value;
14988
+ }
14989
+ function formatBytes(bytes) {
14990
+ if (bytes < 1024) return `${bytes} B`;
14991
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
14992
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
14993
+ }
14994
+ async function runCreate(args) {
14995
+ const [folder] = args;
14996
+ if (folder === void 0) {
14997
+ fail3("create requires: cj0 create <folder>");
14998
+ return;
14999
+ }
15000
+ const result = createProject(folder);
15001
+ process.stdout.write(
15002
+ `created project in ${result.target} (${result.files.length} files)
15003
+ ${result.files.join(", ")}
15004
+ `
15005
+ );
15006
+ try {
15007
+ const sync = await syncPublicAssets({ cwd: result.target });
15008
+ process.stdout.write(
15009
+ ` UI: ${sync.files} assets (${formatBytes(sync.bytes)}) -> ${path4.relative(
15010
+ result.target,
15011
+ sync.targetDir
15012
+ ) || "."}
15013
+ `
15014
+ );
15015
+ } catch (error) {
15016
+ process.stdout.write(
15017
+ ` UI: \u672A\u540C\u6B65\uFF08${error instanceof Error ? error.message : String(error)}\uFF09\uFF1B\u7A0D\u540E\u8FD0\u884C cj0 sync
15018
+ `
15019
+ );
15020
+ }
15021
+ process.stdout.write(
15022
+ ` next: cd ${folder} && npm install && npx cj0 setup && npm run dev
15023
+ `
15024
+ );
15025
+ }
15026
+ function parseSyncFlags(args, allowDeployFlags) {
15027
+ let config = DEFAULT_CONFIG2;
15028
+ let noUi = false;
15029
+ let noScripts = false;
15030
+ for (let i = 0; i < args.length; i += 1) {
15031
+ const arg = args[i];
15032
+ if (arg === void 0) continue;
15033
+ if (arg === "--config" || arg.startsWith("--config=")) {
15034
+ config = readFlagValue(args, i, "--config");
15035
+ if (!arg.includes("=")) i += 1;
15036
+ } else if (allowDeployFlags && arg === "--no-ui") {
15037
+ noUi = true;
15038
+ } else if (allowDeployFlags && arg === "--no-scripts") {
15039
+ noScripts = true;
15040
+ } else {
15041
+ throw new Error(`unexpected ${allowDeployFlags ? "deploy" : "sync"} option "${arg}"`);
15042
+ }
15043
+ }
15044
+ return { config, noUi, noScripts };
15045
+ }
15046
+ async function runSync(args) {
15047
+ const flags = parseSyncFlags(args, false);
15048
+ const result = await syncPublicAssets({ configPath: flags.config });
15049
+ const target = path4.relative(process.cwd(), result.targetDir) || ".";
15050
+ process.stdout.write(
15051
+ `${result.copied ? "synced" : "up to date"}: ${result.files} assets, ${formatBytes(result.bytes)} -> ${target}
15052
+ `
15053
+ );
15054
+ }
15055
+ function parseMapFlags(args, command) {
15056
+ let maps = DEFAULT_MAPS_FILE;
15057
+ let publicDir = DEFAULT_PUBLIC_DIR;
15058
+ let scriptsDir = DEFAULT_SCRIPTS_DIR;
15059
+ let slug;
15060
+ let gzip = true;
15061
+ let resetRoot = false;
15062
+ let json = false;
15063
+ const positional = [];
15064
+ for (let i = 0; i < args.length; i += 1) {
15065
+ const arg = args[i];
15066
+ if (arg === void 0) continue;
15067
+ if (arg === "--maps" || arg.startsWith("--maps=")) {
15068
+ maps = readFlagValue(args, i, "--maps");
15069
+ if (!arg.includes("=")) i += 1;
15070
+ } else if (arg === "--public" || arg.startsWith("--public=")) {
15071
+ publicDir = readFlagValue(args, i, "--public");
15072
+ if (!arg.includes("=")) i += 1;
15073
+ } else if (arg === "--scripts" || arg.startsWith("--scripts=")) {
15074
+ scriptsDir = readFlagValue(args, i, "--scripts");
15075
+ if (!arg.includes("=")) i += 1;
15076
+ } else if (arg === "--slug" || arg.startsWith("--slug=")) {
15077
+ slug = readFlagValue(args, i, "--slug");
15078
+ if (!arg.includes("=")) i += 1;
15079
+ } else if (arg === "--no-gzip") {
15080
+ gzip = false;
15081
+ } else if (arg === "--reset-root") {
15082
+ resetRoot = true;
15083
+ } else if (arg === "--json") {
15084
+ json = true;
15085
+ } else if (arg.startsWith("-")) {
15086
+ throw new Error(`unexpected ${command} option "${arg}"`);
15087
+ } else {
15088
+ positional.push(arg);
15089
+ }
15090
+ }
15091
+ return {
15092
+ maps,
15093
+ publicDir,
15094
+ scriptsDir,
15095
+ ...slug !== void 0 ? { slug } : {},
15096
+ gzip,
15097
+ resetRoot,
15098
+ json,
15099
+ positional
15100
+ };
15101
+ }
15102
+ function mapOptions(flags) {
15103
+ return {
15104
+ maps: flags.maps,
15105
+ publicDir: flags.publicDir,
15106
+ scriptsDir: flags.scriptsDir,
15107
+ ...flags.slug !== void 0 ? { slug: flags.slug } : {},
15108
+ gzip: flags.gzip,
15109
+ resetRoot: flags.resetRoot
15110
+ };
15111
+ }
15112
+ function printImportResult(result) {
15113
+ process.stdout.write(
15114
+ `${result.action === "add" ? "added" : "updated"} "${result.slug}" (${result.worldId}) -> ${result.root}
15115
+ map: ${result.files} files, ${formatBytes(result.bytes)} (seed ${formatBytes(result.seedPackBytes)})
15116
+ seed: ${path4.relative(process.cwd(), result.seedPack)}
15117
+ `
15118
+ );
15119
+ if (result.scriptsPack !== void 0) {
15120
+ process.stdout.write(
15121
+ ` scripts: ${result.scriptModules} modules -> ${path4.relative(
15122
+ process.cwd(),
15123
+ result.scriptsPack
15124
+ )}
15125
+ `
15126
+ );
15127
+ }
15128
+ for (const warning of result.warnings) process.stdout.write(` warning: ${warning}
15129
+ `);
15130
+ }
15131
+ async function runAdd(args) {
15132
+ const flags = parseMapFlags(args, "add");
15133
+ const archive = flags.positional[0];
15134
+ if (archive === void 0) {
15135
+ throw new Error("add requires: cj0 add <file.boxplay> [--slug <slug>]");
15136
+ }
15137
+ if (flags.positional.length > 1) {
15138
+ throw new Error(`unexpected argument "${flags.positional[1]}"`);
15139
+ }
15140
+ const result = await addMap(archive, mapOptions(flags));
15141
+ if (flags.json) {
15142
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
15143
+ `);
15144
+ return;
15145
+ }
15146
+ printImportResult(result);
15147
+ }
15148
+ async function runUpdate(args) {
15149
+ const flags = parseMapFlags(args, "update");
15150
+ const [slug, archive] = flags.positional;
15151
+ if (slug === void 0 || archive === void 0) {
15152
+ throw new Error("update requires: cj0 update <slug> <file.boxplay>");
15153
+ }
15154
+ if (flags.positional.length > 2) {
15155
+ throw new Error(`unexpected argument "${flags.positional[2]}"`);
15156
+ }
15157
+ const result = await updateMap(slug, archive, mapOptions(flags));
15158
+ if (flags.json) {
15159
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
15160
+ `);
15161
+ return;
15162
+ }
15163
+ printImportResult(result);
15164
+ }
15165
+ function runList(args) {
15166
+ const flags = parseMapFlags(args, "list");
15167
+ if (flags.positional.length > 0) {
15168
+ throw new Error(`unexpected argument "${flags.positional[0]}"`);
15169
+ }
15170
+ const entries = listMaps({
15171
+ maps: flags.maps,
15172
+ publicDir: flags.publicDir,
15173
+ scriptsDir: flags.scriptsDir
15174
+ });
15175
+ if (flags.json) {
15176
+ process.stdout.write(`${JSON.stringify(entries, null, 2)}
15177
+ `);
15178
+ return;
15179
+ }
15180
+ if (entries.length === 0) {
15181
+ process.stdout.write("no maps; import one with: cj0 add <file.boxplay>\n");
15182
+ return;
15183
+ }
15184
+ for (const entry of entries) {
15185
+ const state = [
15186
+ entry.imported ? "imported" : "catalog-only",
15187
+ entry.seeded ? "seeded" : "no-seed",
15188
+ entry.hasScripts ? "scripts" : "no-scripts"
15189
+ ].join(", ");
15190
+ process.stdout.write(
15191
+ `${entry.slug.padEnd(16)} ${entry.root}
15192
+ ${entry.name ?? "(unnamed)"}${entry.version !== void 0 ? ` v${entry.version}` : ""} [${state}]
15193
+ `
15194
+ );
15195
+ }
14471
15196
  }
14472
- function fail3(message) {
14473
- process.stderr.write(`cajonzero: ${message}
15197
+ function runRemove(args) {
15198
+ const flags = parseMapFlags(args, "remove");
15199
+ const slug = flags.positional[0];
15200
+ if (slug === void 0) {
15201
+ throw new Error("remove requires: cj0 remove <slug>");
15202
+ }
15203
+ if (flags.positional.length > 1) {
15204
+ throw new Error(`unexpected argument "${flags.positional[1]}"`);
15205
+ }
15206
+ const result = removeMap(slug, {
15207
+ maps: flags.maps,
15208
+ publicDir: flags.publicDir,
15209
+ scriptsDir: flags.scriptsDir
15210
+ });
15211
+ if (flags.json) {
15212
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
14474
15213
  `);
14475
- process.exitCode = 1;
15214
+ return;
15215
+ }
15216
+ process.stdout.write(`removed "${result.slug}"
15217
+ `);
15218
+ if (result.removedDir !== void 0) {
15219
+ process.stdout.write(` dir: ${path4.relative(process.cwd(), result.removedDir)}
15220
+ `);
15221
+ }
15222
+ for (const file of result.removedScripts) {
15223
+ process.stdout.write(` scripts: ${path4.relative(process.cwd(), file)}
15224
+ `);
15225
+ }
14476
15226
  }
14477
- function errorLabel(error) {
14478
- if (error instanceof ScaffoldError) {
14479
- return `${error.code}: ${error.message}`;
15227
+ function resolveWranglerBin() {
15228
+ try {
15229
+ const require2 = createRequire(import.meta.url);
15230
+ const packagePath = require2.resolve("wrangler/package.json");
15231
+ const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
15232
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["wrangler"];
15233
+ return bin !== void 0 ? path4.resolve(path4.dirname(packagePath), bin) : void 0;
15234
+ } catch {
15235
+ return void 0;
14480
15236
  }
14481
- return error instanceof Error ? error.message : String(error);
14482
15237
  }
14483
- function runCreate(args) {
14484
- const [folder] = args;
14485
- if (folder === void 0) {
14486
- fail3("create requires: cj0 create <folder>");
14487
- return;
15238
+ function scriptsKvBinding(configPath) {
15239
+ if (!existsSync(configPath)) return void 0;
15240
+ const parsed = parseJsoncObject(readFileSync(configPath, "utf8"));
15241
+ const namespaces = Array.isArray(parsed["kv_namespaces"]) ? parsed["kv_namespaces"] : [];
15242
+ const bindings = namespaces.map(
15243
+ (entry) => typeof entry === "object" && entry !== null ? entry["binding"] : void 0
15244
+ ).filter((binding) => typeof binding === "string");
15245
+ return bindings.includes("CAJONZERO_SCRIPTS") ? "CAJONZERO_SCRIPTS" : bindings.includes("CAJONZERO_KV") ? "CAJONZERO_KV" : void 0;
15246
+ }
15247
+ function uploadScriptPacks(cwd, configPath, locations, binding, target) {
15248
+ const wrangler = resolveWranglerBin();
15249
+ if (wrangler === void 0) {
15250
+ throw new Error("wrangler is not installed; cannot upload map scripts");
15251
+ }
15252
+ if (locations.length === 0) return 0;
15253
+ const temp = mkdtempSync(path4.join(tmpdir(), "cajonzero-scripts-"));
15254
+ let uploaded = 0;
15255
+ try {
15256
+ for (const { slug, file: source } of locations) {
15257
+ const text = readScriptsPackText(source);
15258
+ if (text === void 0) continue;
15259
+ const parsed = JSON.parse(text);
15260
+ const revision = typeof parsed.revision === "string" && parsed.revision.length > 0 ? parsed.revision : void 0;
15261
+ const file = path4.join(temp, `${slug}.json`);
15262
+ writeFileSync(file, text, "utf8");
15263
+ const keys = [
15264
+ ...revision !== void 0 ? [`scripts:${slug}:${revision}`] : [],
15265
+ `scripts:${slug}`
15266
+ ];
15267
+ for (const key of keys) {
15268
+ const args = [
15269
+ wrangler,
15270
+ "kv",
15271
+ "key",
15272
+ "put",
15273
+ key,
15274
+ "--binding",
15275
+ binding,
15276
+ "--path",
15277
+ file,
15278
+ "--config",
15279
+ configPath
15280
+ ];
15281
+ if (target.remote) {
15282
+ args.push("--remote");
15283
+ } else {
15284
+ args.push("--local");
15285
+ if (target.persistTo !== void 0) args.push("--persist-to", target.persistTo);
15286
+ }
15287
+ const result = spawnSync(process.execPath, args, { cwd, stdio: "inherit" });
15288
+ if (result.error !== void 0 || result.status !== 0) {
15289
+ throw new Error(`failed to upload ${key}`);
15290
+ }
15291
+ uploaded += 1;
15292
+ }
15293
+ }
15294
+ } finally {
15295
+ rmSync(temp, { recursive: true, force: true });
14488
15296
  }
14489
- const result = createProject(folder);
14490
- process.stdout.write(
14491
- `created project in ${result.target} (${result.files.length} files)
14492
- ${result.files.join(", ")}
14493
- next: cd ${folder} && npm install && npx cj0 setup && npm run dev
15297
+ return uploaded;
15298
+ }
15299
+ async function runDeploy(args) {
15300
+ const flags = parseSyncFlags(args, true);
15301
+ const cwd = process.cwd();
15302
+ const configPath = path4.resolve(cwd, flags.config);
15303
+ if (!flags.noUi) {
15304
+ try {
15305
+ const sync = await syncPublicAssets({ cwd, configPath: flags.config });
15306
+ process.stdout.write(
15307
+ `UI ${sync.copied ? "synced" : "up to date"}: ${sync.files} assets
15308
+ `
15309
+ );
15310
+ } catch (error) {
15311
+ throw new Error(
15312
+ `cannot sync the packaged UI: ${error instanceof Error ? error.message : String(error)}`
15313
+ );
15314
+ }
15315
+ }
15316
+ if (!flags.noScripts) {
15317
+ const binding = scriptsKvBinding(configPath);
15318
+ const locations = listScriptPackLocations({ cwd, scriptsDir: DEFAULT_SCRIPTS_DIR });
15319
+ if (binding !== void 0) {
15320
+ const uploaded = uploadScriptPacks(cwd, configPath, locations, binding, {
15321
+ remote: true
15322
+ });
15323
+ if (uploaded > 0) {
15324
+ process.stdout.write(`map scripts: ${uploaded} KV key(s) -> ${binding}
15325
+ `);
15326
+ }
15327
+ } else if (locations.length > 0) {
15328
+ process.stdout.write(
15329
+ `map scripts: no CAJONZERO_SCRIPTS/CAJONZERO_KV binding in ${flags.config}; skipping upload
14494
15330
  `
15331
+ );
15332
+ }
15333
+ }
15334
+ const wrangler = resolveWranglerBin();
15335
+ if (wrangler === void 0) {
15336
+ throw new Error("wrangler is not installed; run npm install first");
15337
+ }
15338
+ const result = spawnSync(
15339
+ process.execPath,
15340
+ [wrangler, "deploy", "--config", flags.config],
15341
+ { cwd, stdio: "inherit" }
14495
15342
  );
15343
+ if (result.error !== void 0) throw result.error;
15344
+ if (result.status !== 0) {
15345
+ throw new Error(`wrangler deploy exited with status ${result.status ?? "unknown"}`);
15346
+ }
14496
15347
  }
14497
15348
  function parseSetupFlags(args) {
14498
15349
  let config = DEFAULT_CONFIG2;
@@ -14508,17 +15359,6 @@ function parseSetupFlags(args) {
14508
15359
  }
14509
15360
  return { config };
14510
15361
  }
14511
- function resolveWranglerBin() {
14512
- try {
14513
- const require2 = createRequire(import.meta.url);
14514
- const packagePath = require2.resolve("wrangler/package.json");
14515
- const pkg = JSON.parse(readFileSync(packagePath, "utf8"));
14516
- const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["wrangler"];
14517
- return bin !== void 0 ? path4.resolve(path4.dirname(packagePath), bin) : void 0;
14518
- } catch {
14519
- return void 0;
14520
- }
14521
- }
14522
15362
  function createKvNamespace(cwd, configPath) {
14523
15363
  const bin = resolveWranglerBin();
14524
15364
  if (bin === void 0) return void 0;
@@ -14636,17 +15476,6 @@ async function runSetup(args) {
14636
15476
  }
14637
15477
  }
14638
15478
  }
14639
- function readFlagValue(args, index, flag) {
14640
- const inline = args[index];
14641
- if (inline !== void 0 && inline.startsWith(`${flag}=`)) {
14642
- return inline.slice(flag.length + 1);
14643
- }
14644
- const value = args[index + 1];
14645
- if (value === void 0 || value.startsWith("-")) {
14646
- throw new Error(`${flag} requires a value`);
14647
- }
14648
- return value;
14649
- }
14650
15479
  function parseServeFlags(args) {
14651
15480
  let port = DEFAULT_SERVE_PORT;
14652
15481
  let host = DEFAULT_SERVE_HOST;
@@ -14674,286 +15503,6 @@ function parseServeFlags(args) {
14674
15503
  }
14675
15504
  return { port, host, config };
14676
15505
  }
14677
- function parseMapsPackFlags(args) {
14678
- let slug;
14679
- let maps = "cajonzero/maps.json";
14680
- let out;
14681
- let gzip = true;
14682
- for (let i = 0; i < args.length; i += 1) {
14683
- const arg = args[i];
14684
- if (arg === void 0) continue;
14685
- if (arg === "--maps" || arg.startsWith("--maps=")) {
14686
- maps = readFlagValue(args, i, "--maps");
14687
- if (!arg.includes("=")) i += 1;
14688
- } else if (arg === "--out" || arg.startsWith("--out=")) {
14689
- out = readFlagValue(args, i, "--out");
14690
- if (!arg.includes("=")) i += 1;
14691
- } else if (arg === "--no-gzip") {
14692
- gzip = false;
14693
- } else if (arg.startsWith("-")) {
14694
- throw new Error(`unexpected maps pack option "${arg}"`);
14695
- } else if (slug === void 0) {
14696
- slug = arg;
14697
- } else {
14698
- throw new Error(`unexpected argument "${arg}"`);
14699
- }
14700
- }
14701
- if (slug === void 0) throw new Error("maps pack requires a map slug");
14702
- return { slug, maps, ...out !== void 0 ? { out } : {}, gzip };
14703
- }
14704
- function readMapCatalogFile(mapsPath, label) {
14705
- if (!existsSync(mapsPath)) {
14706
- throw new Error(`\u627E\u4E0D\u5230\u5730\u56FE\u76EE\u5F55 ${label}\uFF0C\u8BF7\u5728\u9879\u76EE\u76EE\u5F55\u4E2D\u8FD0\u884C`);
14707
- }
14708
- let raw;
14709
- try {
14710
- raw = JSON.parse(readFileSync(mapsPath, "utf8").replace(/^\uFEFF/, ""));
14711
- } catch (error) {
14712
- throw new Error(
14713
- `${label} \u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${error instanceof Error ? error.message : String(error)}`
14714
- );
14715
- }
14716
- return parseMapCatalog(raw);
14717
- }
14718
- async function runMapsPack(args) {
14719
- const flags = parseMapsPackFlags(args);
14720
- const cwd = process.cwd();
14721
- const catalog = readMapCatalogFile(path4.resolve(cwd, flags.maps), flags.maps);
14722
- const entry = findMap(catalog, flags.slug);
14723
- if (entry === void 0) {
14724
- throw new Error(`${flags.maps} \u4E2D\u6CA1\u6709 slug "${flags.slug}"`);
14725
- }
14726
- process.stdout.write(`resolving ${entry.slug} from ${entry.root}
14727
- `);
14728
- const { seed, blockInfoBytes } = await loadWorldSeedGraphFromHttp(entry);
14729
- for (const warning of seed.warnings) process.stdout.write(` warning: ${warning}
14730
- `);
14731
- const pack = encodeSeedPack(seed, blockInfoBytes);
14732
- const data = flags.gzip ? gzipSync(pack, { level: 9 }) : pack;
14733
- const fileName = `seed.json${flags.gzip ? ".gz" : ""}`;
14734
- const outPath = path4.resolve(cwd, flags.out ?? `${entry.slug}.${fileName}`);
14735
- writeFileSync(outPath, data);
14736
- const chunks = seed.voxels?.chunkCount ?? 0;
14737
- process.stdout.write(
14738
- `packed ${seed.bodies.length} bodies, ${chunks} voxel chunks, ${seed.render?.meshes.length ?? 0} meshes
14739
- wrote ${path4.relative(cwd, outPath)} (${formatBytes(data.length)}${flags.gzip ? ", gzip" : ""})
14740
- upload it to: ${entry.root}${fileName}
14741
- room startup becomes 1 request instead of one per blob.
14742
- `
14743
- );
14744
- }
14745
- async function runMaps(args) {
14746
- const [subcommand, ...rest] = args;
14747
- if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
14748
- printUsage();
14749
- return;
14750
- }
14751
- if (subcommand === "pack") {
14752
- if (rest.includes("-h") || rest.includes("--help")) {
14753
- printUsage();
14754
- return;
14755
- }
14756
- await runMapsPack(rest);
14757
- return;
14758
- }
14759
- throw new Error(`unknown maps command "${subcommand}"`);
14760
- }
14761
- function parseBuildSeedFlags(args) {
14762
- const slugs = [];
14763
- let config = DEFAULT_CONFIG2;
14764
- let maps = "cajonzero/maps.json";
14765
- let assets;
14766
- let force = false;
14767
- let gzip = true;
14768
- for (let i = 0; i < args.length; i += 1) {
14769
- const arg = args[i];
14770
- if (arg === void 0) continue;
14771
- if (arg === "--config" || arg.startsWith("--config=")) {
14772
- config = readFlagValue(args, i, "--config");
14773
- if (!arg.includes("=")) i += 1;
14774
- } else if (arg === "--maps" || arg.startsWith("--maps=")) {
14775
- maps = readFlagValue(args, i, "--maps");
14776
- if (!arg.includes("=")) i += 1;
14777
- } else if (arg === "--assets" || arg.startsWith("--assets=")) {
14778
- assets = readFlagValue(args, i, "--assets");
14779
- if (!arg.includes("=")) i += 1;
14780
- } else if (arg === "--force") {
14781
- force = true;
14782
- } else if (arg === "--no-gzip") {
14783
- gzip = false;
14784
- } else if (arg.startsWith("-")) {
14785
- throw new Error(`unexpected worker build-seed option "${arg}"`);
14786
- } else {
14787
- slugs.push(arg);
14788
- }
14789
- }
14790
- return { slugs, config, maps, ...assets !== void 0 ? { assets } : {}, force, gzip };
14791
- }
14792
- function readSeedMeta(metaPath) {
14793
- try {
14794
- return JSON.parse(readFileSync(metaPath, "utf8"));
14795
- } catch {
14796
- return void 0;
14797
- }
14798
- }
14799
- async function runWorkerBuildSeed(args) {
14800
- const flags = parseBuildSeedFlags(args);
14801
- const cwd = process.cwd();
14802
- const configPath = path4.resolve(cwd, flags.config);
14803
- const configDir = path4.dirname(configPath);
14804
- const assetsRel = flags.assets ?? readWranglerAssetsDirectory(configPath);
14805
- if (assetsRel === void 0) {
14806
- throw new Error(
14807
- `${path4.basename(configPath)} \u672A\u914D\u7F6E assets.directory\uFF1B\u7528 --assets <dir> \u6307\u5B9A\u8F93\u51FA\u76EE\u5F55`
14808
- );
14809
- }
14810
- const assetsDir = path4.resolve(configDir, assetsRel);
14811
- const seedDir = path4.join(assetsDir, ASSETS_SEED_DIR);
14812
- mkdirSync(seedDir, { recursive: true });
14813
- const catalog = readMapCatalogFile(path4.resolve(cwd, flags.maps), flags.maps);
14814
- const selected = flags.slugs.length > 0 ? flags.slugs.map((slug) => {
14815
- const entry = findMap(catalog, slug);
14816
- if (entry === void 0) throw new Error(`${flags.maps} \u4E2D\u6CA1\u6709 slug "${slug}"`);
14817
- return entry;
14818
- }) : [...catalog.maps];
14819
- if (selected.length === 0) {
14820
- process.stdout.write(`no maps in ${flags.maps}; nothing to build
14821
- `);
14822
- return;
14823
- }
14824
- let failures = 0;
14825
- for (const entry of selected) {
14826
- const suffix = flags.gzip ? ".json.gz" : ".json";
14827
- const packPath = path4.join(seedDir, `${entry.slug}${suffix}`);
14828
- const staleSuffix = flags.gzip ? ".json" : ".json.gz";
14829
- const meta = {
14830
- root: entry.root,
14831
- ...entry.worldId !== void 0 ? { worldId: entry.worldId } : {},
14832
- ...entry.version !== void 0 ? { version: entry.version } : {},
14833
- ...entry.etag !== void 0 ? { etag: entry.etag } : {},
14834
- gzip: flags.gzip
14835
- };
14836
- if (!flags.force && existsSync(packPath)) {
14837
- const previous = readSeedMeta(path4.join(seedDir, `${entry.slug}.meta.json`));
14838
- if (previous !== void 0 && JSON.stringify(previous) === JSON.stringify(meta)) {
14839
- process.stdout.write(`up to date: ${path4.relative(cwd, packPath)}
14840
- `);
14841
- continue;
14842
- }
14843
- }
14844
- try {
14845
- process.stdout.write(`resolving ${entry.slug} from ${entry.root}
14846
- `);
14847
- const { seed, blockInfoBytes } = await loadWorldSeedGraphFromHttp(entry);
14848
- for (const warning of seed.warnings) process.stdout.write(` warning: ${warning}
14849
- `);
14850
- const pack = encodeSeedPack(seed, blockInfoBytes);
14851
- const data = flags.gzip ? gzipSync(pack, { level: 9 }) : pack;
14852
- writeFileSync(packPath, data);
14853
- writeFileSync(
14854
- path4.join(seedDir, `${entry.slug}.meta.json`),
14855
- `${JSON.stringify(meta, null, 2)}
14856
- `,
14857
- "utf8"
14858
- );
14859
- rmSync(path4.join(seedDir, `${entry.slug}${staleSuffix}`), { force: true });
14860
- process.stdout.write(
14861
- `seeded ${entry.slug}: ${seed.bodies.length} bodies, ${seed.voxels?.chunkCount ?? 0} voxel chunks, ${seed.render?.meshes.length ?? 0} meshes -> ${path4.relative(cwd, packPath)} (${formatBytes(data.length)})
14862
- `
14863
- );
14864
- } catch (error) {
14865
- failures += 1;
14866
- process.stderr.write(
14867
- `cajonzero: build-seed failed for "${entry.slug}": ${errorLabel(error)}
14868
- `
14869
- );
14870
- }
14871
- }
14872
- if (failures > 0) {
14873
- throw new Error(`${failures} map(s) failed to build; refusing to deploy without a seed`);
14874
- }
14875
- const known = new Set(catalog.maps.map((map) => map.slug));
14876
- let pruned = 0;
14877
- for (const name of readdirSync(seedDir)) {
14878
- const match = /^(.+?)(?:\.meta)?\.json(?:\.gz)?$/.exec(name);
14879
- const slug = match?.[1];
14880
- if (slug === void 0 || known.has(slug)) continue;
14881
- rmSync(path4.join(seedDir, name), { force: true });
14882
- pruned += 1;
14883
- }
14884
- if (pruned > 0) {
14885
- process.stdout.write(`pruned ${pruned} stale seed asset(s)
14886
- `);
14887
- }
14888
- process.stdout.write(
14889
- `seed assets: ${path4.relative(cwd, seedDir)} (served at /seed/<slug>.json[.gz])
14890
- `
14891
- );
14892
- }
14893
- function parseWorkerPrepareFlags(args) {
14894
- let config = DEFAULT_CONFIG2;
14895
- let assets;
14896
- let force = false;
14897
- for (let i = 0; i < args.length; i += 1) {
14898
- const arg = args[i];
14899
- if (arg === void 0) continue;
14900
- if (arg === "--config" || arg.startsWith("--config=")) {
14901
- config = readFlagValue(args, i, "--config");
14902
- if (!arg.includes("=")) i += 1;
14903
- } else if (arg === "--assets" || arg.startsWith("--assets=")) {
14904
- assets = readFlagValue(args, i, "--assets");
14905
- if (!arg.includes("=")) i += 1;
14906
- } else if (arg === "--force") {
14907
- force = true;
14908
- } else {
14909
- throw new Error(`unexpected worker prepare option "${arg}"`);
14910
- }
14911
- }
14912
- return { config, ...assets !== void 0 ? { assets } : {}, force };
14913
- }
14914
- function formatBytes(bytes) {
14915
- if (bytes < 1024) return `${bytes} B`;
14916
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
14917
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
14918
- }
14919
- async function runWorkerPrepare(args) {
14920
- const flags = parseWorkerPrepareFlags(args);
14921
- const result = await prepareWorkerAssets({
14922
- cwd: process.cwd(),
14923
- configPath: flags.config,
14924
- ...flags.assets !== void 0 ? { assetsDir: flags.assets } : {},
14925
- force: flags.force
14926
- });
14927
- const target = path4.relative(process.cwd(), result.targetDir) || ".";
14928
- process.stdout.write(
14929
- `${result.copied ? "synced" : "up to date"}: ${result.files} assets, ${formatBytes(result.bytes)} -> ${target}
14930
- `
14931
- );
14932
- }
14933
- async function runWorker(args) {
14934
- const [subcommand, ...rest] = args;
14935
- if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
14936
- printUsage();
14937
- return;
14938
- }
14939
- if (subcommand === "prepare") {
14940
- if (rest.includes("-h") || rest.includes("--help")) {
14941
- printUsage();
14942
- return;
14943
- }
14944
- await runWorkerPrepare(rest);
14945
- return;
14946
- }
14947
- if (subcommand === "build-seed") {
14948
- if (rest.includes("-h") || rest.includes("--help")) {
14949
- printUsage();
14950
- return;
14951
- }
14952
- await runWorkerBuildSeed(rest);
14953
- return;
14954
- }
14955
- throw new Error(`unknown worker command "${subcommand}"`);
14956
- }
14957
15506
  async function runServe(args) {
14958
15507
  const flags = parseServeFlags(args);
14959
15508
  const { unstable_dev, unstable_readConfig } = await import('wrangler');
@@ -14964,14 +15513,41 @@ async function runServe(args) {
14964
15513
  }
14965
15514
  const assetsDir = config.assets?.directory;
14966
15515
  if (typeof assetsDir === "string" && assetsDir.length > 0) {
14967
- await prepareWorkerAssets({ cwd: process.cwd(), configPath: flags.config, assetsDir });
15516
+ try {
15517
+ await syncPublicAssets({ cwd: process.cwd(), configPath: flags.config, assetsDir });
15518
+ } catch (error) {
15519
+ process.stdout.write(
15520
+ `cajonzero: UI sync skipped (${error instanceof Error ? error.message : String(error)})
15521
+ `
15522
+ );
15523
+ }
14968
15524
  }
14969
15525
  const secrets = localDevVars();
15526
+ const persistTo = path4.resolve(
15527
+ path4.dirname(path4.resolve(flags.config)),
15528
+ ".wrangler",
15529
+ "state"
15530
+ );
15531
+ const scriptsBinding = scriptsKvBinding(path4.resolve(flags.config));
15532
+ if (scriptsBinding !== void 0) {
15533
+ const uploaded = uploadScriptPacks(
15534
+ process.cwd(),
15535
+ flags.config,
15536
+ path4.resolve(process.cwd(), DEFAULT_SCRIPTS_DIR),
15537
+ scriptsBinding,
15538
+ { remote: false, persistTo }
15539
+ );
15540
+ if (uploaded > 0) {
15541
+ process.stdout.write(`map scripts: ${uploaded} local KV key(s) -> ${scriptsBinding}
15542
+ `);
15543
+ }
15544
+ }
14970
15545
  const worker = await unstable_dev(entry, {
14971
15546
  config: flags.config,
14972
15547
  ip: flags.host,
14973
15548
  port: flags.port,
14974
15549
  local: true,
15550
+ persistTo,
14975
15551
  logLevel: "info",
14976
15552
  // Inject local-dev secrets (from the environment) on top of the config
14977
15553
  // `vars` so auth/session work locally without `wrangler secret put`.
@@ -15015,22 +15591,38 @@ async function main(argv) {
15015
15591
  printUsage();
15016
15592
  return;
15017
15593
  }
15594
+ if (rest.includes("-h") || rest.includes("--help")) {
15595
+ printUsage();
15596
+ return;
15597
+ }
15018
15598
  try {
15019
15599
  switch (command) {
15020
15600
  case "serve":
15021
15601
  await runServe(rest);
15022
15602
  return;
15023
15603
  case "create":
15024
- runCreate(rest);
15604
+ await runCreate(rest);
15025
15605
  return;
15026
15606
  case "setup":
15027
15607
  await runSetup(rest);
15028
15608
  return;
15029
- case "worker":
15030
- await runWorker(rest);
15609
+ case "add":
15610
+ await runAdd(rest);
15611
+ return;
15612
+ case "update":
15613
+ await runUpdate(rest);
15614
+ return;
15615
+ case "list":
15616
+ runList(rest);
15617
+ return;
15618
+ case "remove":
15619
+ runRemove(rest);
15620
+ return;
15621
+ case "sync":
15622
+ await runSync(rest);
15031
15623
  return;
15032
- case "maps":
15033
- await runMaps(rest);
15624
+ case "deploy":
15625
+ await runDeploy(rest);
15034
15626
  return;
15035
15627
  default:
15036
15628
  process.stderr.write(`cajonzero: unknown command "${command}"