@uzuhq/code-cli 0.6.0 → 0.6.2
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/cli.js +88 -86
- package/dist/dev-server/sdk-server-shim.js +244 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command, Option } from "commander";
|
|
5
5
|
import { execSync } from "child_process";
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { dirname as dirname5, resolve as
|
|
6
|
+
import { readFileSync as readFileSync6, existsSync as existsSync4, unlinkSync, createWriteStream } from "fs";
|
|
7
|
+
import { dirname as dirname5, resolve as resolve6 } from "path";
|
|
8
8
|
import { ZipArchive } from "archiver";
|
|
9
9
|
import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL2 } from "url";
|
|
10
10
|
|
|
@@ -418,8 +418,30 @@ var registerRevision = async (params) => {
|
|
|
418
418
|
|
|
419
419
|
// src/build-server-logic.ts
|
|
420
420
|
import { build } from "esbuild";
|
|
421
|
-
|
|
422
|
-
|
|
421
|
+
|
|
422
|
+
// src/sdk-server-stub.ts
|
|
423
|
+
import { existsSync } from "fs";
|
|
424
|
+
import { dirname, resolve } from "path";
|
|
425
|
+
import { fileURLToPath } from "url";
|
|
426
|
+
var SDK_SPECIFIER = /^(@uzuhq\/code-sdk|@uzupj\/uzu-sdk)$/;
|
|
427
|
+
function resolveSdkShimPath() {
|
|
428
|
+
const here = fileURLToPath(import.meta.url);
|
|
429
|
+
const ext = here.endsWith(".ts") ? "ts" : "js";
|
|
430
|
+
const shim = resolve(dirname(here), "dev-server", `sdk-server-shim.${ext}`);
|
|
431
|
+
if (!existsSync(shim)) {
|
|
432
|
+
throw new Error(`sdk-server-shim \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${shim}`);
|
|
433
|
+
}
|
|
434
|
+
return shim;
|
|
435
|
+
}
|
|
436
|
+
var uzuSdkServerStub = {
|
|
437
|
+
name: "uzu-sdk-server-stub",
|
|
438
|
+
setup(build3) {
|
|
439
|
+
const shimPath = resolveSdkShimPath();
|
|
440
|
+
build3.onResolve({ filter: SDK_SPECIFIER }, () => ({ path: shimPath }));
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/build-server-logic.ts
|
|
423
445
|
var buildServerLogic = async (logicPath, outPath) => {
|
|
424
446
|
await build({
|
|
425
447
|
entryPoints: [logicPath],
|
|
@@ -428,29 +450,23 @@ var buildServerLogic = async (logicPath, outPath) => {
|
|
|
428
450
|
format: "esm",
|
|
429
451
|
target: "es2022",
|
|
430
452
|
platform: "neutral",
|
|
431
|
-
// SDK
|
|
432
|
-
//
|
|
433
|
-
|
|
453
|
+
// SDK は engine-core への素通し shim へ差し替えて bundle する。`uzu dev` と
|
|
454
|
+
// 同じ plugin を通すので、harness で動いたものが publish でも同じ形になる。
|
|
455
|
+
//
|
|
456
|
+
// external は 1 つも置かない。Worker Loader が facet へ渡すのは index.js /
|
|
457
|
+
// game-facet.js / logic.js の 3 つだけで npm の解決先が無いため、成果物に bare
|
|
458
|
+
// import が残ると部屋の起動時に落ちる。external を置かなければ、解決できない
|
|
459
|
+
// import は esbuild がここでエラーにするので、壊れた成果物は publish されない。
|
|
460
|
+
plugins: [uzuSdkServerStub]
|
|
434
461
|
});
|
|
435
|
-
assertNoSdkRuntimeImport(outPath);
|
|
436
462
|
console.log(`Built server logic: ${logicPath} \u2192 ${outPath}`);
|
|
437
463
|
};
|
|
438
|
-
var assertNoSdkRuntimeImport = (outPath) => {
|
|
439
|
-
const built = readFileSync2(outPath, "utf-8");
|
|
440
|
-
const found = SDK_PACKAGES.find((pkg) => built.includes(pkg));
|
|
441
|
-
if (!found) return;
|
|
442
|
-
throw new Error(
|
|
443
|
-
`logic \u304C ${found} \u304B\u3089\u5024\u3092 import \u3057\u3066\u3044\u307E\u3059\u3002
|
|
444
|
-
logic \u304B\u3089 SDK \u3078\u5411\u3051\u3066\u3088\u3044\u306E\u306F\u578B\u3060\u3051\u3067\u3059 (import type)\u3002
|
|
445
|
-
\u5B9F\u884C\u6642\u306B\u5FC5\u8981\u306A\u3082\u306E (serverOnly / SERVER_TIME \u7B49) \u306F @uzuhq/code-engine-core \u304B\u3089 import \u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
446
|
-
);
|
|
447
|
-
};
|
|
448
464
|
|
|
449
465
|
// src/create-2d-game.ts
|
|
450
|
-
import { mkdirSync, readFileSync as
|
|
451
|
-
import { resolve, dirname, join as join3 } from "path";
|
|
452
|
-
import { fileURLToPath } from "url";
|
|
453
|
-
var __dirname =
|
|
466
|
+
import { mkdirSync, readFileSync as readFileSync2, writeFileSync, readdirSync, statSync } from "fs";
|
|
467
|
+
import { resolve as resolve2, dirname as dirname2, join as join3 } from "path";
|
|
468
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
469
|
+
var __dirname = dirname2(fileURLToPath2(import.meta.url));
|
|
454
470
|
var copyDir = (src, dest, replacements) => {
|
|
455
471
|
mkdirSync(dest, { recursive: true });
|
|
456
472
|
for (const entry of readdirSync(src)) {
|
|
@@ -461,13 +477,13 @@ var copyDir = (src, dest, replacements) => {
|
|
|
461
477
|
continue;
|
|
462
478
|
}
|
|
463
479
|
if (entry.endsWith(".tpl")) {
|
|
464
|
-
let content =
|
|
480
|
+
let content = readFileSync2(srcPath, "utf-8");
|
|
465
481
|
for (const [key, value] of Object.entries(replacements)) {
|
|
466
482
|
content = content.replaceAll(`{{${key}}}`, value);
|
|
467
483
|
}
|
|
468
484
|
writeFileSync(join3(dest, entry.replace(/\.tpl$/, "")), content);
|
|
469
485
|
} else {
|
|
470
|
-
const content =
|
|
486
|
+
const content = readFileSync2(srcPath, "utf-8");
|
|
471
487
|
let output = content;
|
|
472
488
|
for (const [key, value] of Object.entries(replacements)) {
|
|
473
489
|
output = output.replaceAll(`{{${key}}}`, value);
|
|
@@ -478,8 +494,8 @@ var copyDir = (src, dest, replacements) => {
|
|
|
478
494
|
};
|
|
479
495
|
var toTitle = (name) => name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
480
496
|
var create2dGame = (name) => {
|
|
481
|
-
const dest =
|
|
482
|
-
const templateDir =
|
|
497
|
+
const dest = resolve2(process.cwd(), name);
|
|
498
|
+
const templateDir = resolve2(__dirname, "..", "game-2d-template");
|
|
483
499
|
const title = toTitle(name);
|
|
484
500
|
const replacements = { name, title };
|
|
485
501
|
console.log(`Creating 2D game project: ${name}`);
|
|
@@ -494,7 +510,7 @@ Next steps:`);
|
|
|
494
510
|
};
|
|
495
511
|
|
|
496
512
|
// src/cf-images-upload.ts
|
|
497
|
-
import { readFileSync as
|
|
513
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
498
514
|
import { basename as basename2 } from "path";
|
|
499
515
|
var parseUploadedImageId = (v) => {
|
|
500
516
|
if (typeof v === "object" && v !== null && "success" in v && v.success === true && "result" in v && typeof v.result === "object" && v.result !== null && "id" in v.result && typeof v.result.id === "string" && v.result.id !== "") {
|
|
@@ -513,7 +529,7 @@ var uploadIconsToCfImages = async (env, token, filePaths) => {
|
|
|
513
529
|
filePaths.map(async (filePath, i) => {
|
|
514
530
|
const image = session.images[i];
|
|
515
531
|
const formData = new FormData();
|
|
516
|
-
formData.append("file", new Blob([
|
|
532
|
+
formData.append("file", new Blob([readFileSync3(filePath)]), basename2(filePath));
|
|
517
533
|
const res = await fetch(image.uploadURL, { method: "POST", body: formData });
|
|
518
534
|
if (!res.ok) {
|
|
519
535
|
const detail = (await res.text()).trim();
|
|
@@ -530,8 +546,8 @@ var uploadIconsToCfImages = async (env, token, filePaths) => {
|
|
|
530
546
|
|
|
531
547
|
// src/dev.ts
|
|
532
548
|
import { spawn } from "child_process";
|
|
533
|
-
import { readFileSync as
|
|
534
|
-
import { dirname as dirname3, resolve as
|
|
549
|
+
import { readFileSync as readFileSync4, existsSync as existsSync2 } from "fs";
|
|
550
|
+
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
535
551
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
536
552
|
import { createInterface } from "readline";
|
|
537
553
|
|
|
@@ -549,8 +565,10 @@ import { WebSocketServer } from "ws";
|
|
|
549
565
|
import { randomUUID } from "crypto";
|
|
550
566
|
|
|
551
567
|
// ../engine-core/src/game-time.ts
|
|
552
|
-
var
|
|
568
|
+
var toMs = (t) => t;
|
|
569
|
+
var plus = (t, d) => toMs(t) + d;
|
|
553
570
|
var asGameTime = (ms) => ms;
|
|
571
|
+
var GAME_START = asGameTime(0);
|
|
554
572
|
|
|
555
573
|
// ../engine-core/src/game-clock.ts
|
|
556
574
|
var NO_PLAYERS_GRACE_MS = 5 * 6e4;
|
|
@@ -584,7 +602,7 @@ var GameClock = class {
|
|
|
584
602
|
}
|
|
585
603
|
/** ゲーム内時刻を、alarm / setTimeout が使う実時刻へ直す。 */
|
|
586
604
|
toWall(at) {
|
|
587
|
-
return at + this.startedAtWall + this.pausedTotalMs;
|
|
605
|
+
return toMs(at) + this.startedAtWall + this.pausedTotalMs;
|
|
588
606
|
}
|
|
589
607
|
/** 実際に理由を足したら true。呼び出し側が永続化や tick 停止の要否に使う。 */
|
|
590
608
|
freeze(reason) {
|
|
@@ -628,9 +646,10 @@ var GameClock = class {
|
|
|
628
646
|
function withGameClock(time, fn) {
|
|
629
647
|
const realNow = Date.now;
|
|
630
648
|
const RealDate = Date;
|
|
631
|
-
|
|
649
|
+
const ms = toMs(time);
|
|
650
|
+
Date.now = () => ms;
|
|
632
651
|
globalThis.Date = new Proxy(RealDate, {
|
|
633
|
-
construct: (target, args) => Reflect.construct(target, args.length === 0 ? [
|
|
652
|
+
construct: (target, args) => Reflect.construct(target, args.length === 0 ? [ms] : args)
|
|
634
653
|
});
|
|
635
654
|
try {
|
|
636
655
|
return fn();
|
|
@@ -1860,7 +1879,7 @@ function startHarnessServer(opts) {
|
|
|
1860
1879
|
});
|
|
1861
1880
|
httpServer.listen(opts.port, opts.host ?? "127.0.0.1");
|
|
1862
1881
|
return {
|
|
1863
|
-
stop: () => new Promise((
|
|
1882
|
+
stop: () => new Promise((resolve7) => {
|
|
1864
1883
|
for (const ws of wss.clients) {
|
|
1865
1884
|
try {
|
|
1866
1885
|
ws.close();
|
|
@@ -1868,7 +1887,7 @@ function startHarnessServer(opts) {
|
|
|
1868
1887
|
}
|
|
1869
1888
|
}
|
|
1870
1889
|
wss.close(() => {
|
|
1871
|
-
httpServer.close(() =>
|
|
1890
|
+
httpServer.close(() => resolve7());
|
|
1872
1891
|
});
|
|
1873
1892
|
})
|
|
1874
1893
|
};
|
|
@@ -2084,29 +2103,12 @@ function proxyUpgradeToScenario(req, socket, head, scenarioUrl) {
|
|
|
2084
2103
|
|
|
2085
2104
|
// src/dev-server/load-logic.ts
|
|
2086
2105
|
import { build as build2 } from "esbuild";
|
|
2087
|
-
import {
|
|
2106
|
+
import { mkdtempSync, rmSync } from "fs";
|
|
2088
2107
|
import { tmpdir } from "os";
|
|
2089
|
-
import {
|
|
2090
|
-
import {
|
|
2091
|
-
function resolveSdkShimPath() {
|
|
2092
|
-
const here = fileURLToPath2(import.meta.url);
|
|
2093
|
-
const shim = here.endsWith(".ts") ? resolve2(dirname2(here), "sdk-server-shim.ts") : resolve2(dirname2(here), "dev-server", "sdk-server-shim.js");
|
|
2094
|
-
if (!existsSync(shim)) {
|
|
2095
|
-
throw new Error(`sdk-server-shim \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${shim}`);
|
|
2096
|
-
}
|
|
2097
|
-
return shim;
|
|
2098
|
-
}
|
|
2099
|
-
var uzuSdkServerStub = {
|
|
2100
|
-
name: "uzu-sdk-server-stub",
|
|
2101
|
-
setup(build3) {
|
|
2102
|
-
const shimPath = resolveSdkShimPath();
|
|
2103
|
-
build3.onResolve({ filter: /^(@uzuhq\/code-sdk|@uzupj\/uzu-sdk)$/ }, () => ({
|
|
2104
|
-
path: shimPath
|
|
2105
|
-
}));
|
|
2106
|
-
}
|
|
2107
|
-
};
|
|
2108
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
2109
|
+
import { pathToFileURL } from "url";
|
|
2108
2110
|
async function loadLogicFromPath(logicPath) {
|
|
2109
|
-
const absPath =
|
|
2111
|
+
const absPath = resolve3(logicPath);
|
|
2110
2112
|
const tempDir = mkdtempSync(join4(tmpdir(), "uzu-dev-logic-"));
|
|
2111
2113
|
const outPath = join4(tempDir, "logic.mjs");
|
|
2112
2114
|
await build2({
|
|
@@ -2183,12 +2185,12 @@ var DEFAULT_READY_PATTERN = "Local:\\s+(https?://[^\\s]+)";
|
|
|
2183
2185
|
var DEFAULT_MIN_IFRAME_SHORT_EDGE = 360;
|
|
2184
2186
|
async function runDevCommand() {
|
|
2185
2187
|
const cwd = process.cwd();
|
|
2186
|
-
const manifestPath =
|
|
2188
|
+
const manifestPath = resolve4(cwd, "manifest.json");
|
|
2187
2189
|
if (!existsSync2(manifestPath)) {
|
|
2188
2190
|
console.error("manifest.json \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002 scenario \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
2189
2191
|
process.exit(1);
|
|
2190
2192
|
}
|
|
2191
|
-
const manifest = JSON.parse(
|
|
2193
|
+
const manifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
|
|
2192
2194
|
const playerCount = manifest.characters?.length ?? manifest.playerCount ?? 2;
|
|
2193
2195
|
const orientation = manifest.orientation ?? "portrait";
|
|
2194
2196
|
const devCommand = manifest.dev?.command ?? DEFAULT_DEV_COMMAND;
|
|
@@ -2196,13 +2198,13 @@ async function runDevCommand() {
|
|
|
2196
2198
|
let loaded = null;
|
|
2197
2199
|
if (manifest.serverActionLogicPath) {
|
|
2198
2200
|
console.log(`[uzu dev] Building server logic from ${manifest.serverActionLogicPath}...`);
|
|
2199
|
-
const logicPath =
|
|
2201
|
+
const logicPath = resolve4(cwd, manifest.serverActionLogicPath);
|
|
2200
2202
|
loaded = await loadLogicFromPath(logicPath);
|
|
2201
2203
|
console.log("[uzu dev] Server logic ready");
|
|
2202
2204
|
}
|
|
2203
2205
|
const thisDir = dirname3(fileURLToPath3(import.meta.url));
|
|
2204
|
-
const clientEntryJs =
|
|
2205
|
-
const clientEntryTs =
|
|
2206
|
+
const clientEntryJs = resolve4(thisDir, "harness", "client-entry.js");
|
|
2207
|
+
const clientEntryTs = resolve4(thisDir, "harness", "client-entry.ts");
|
|
2206
2208
|
const clientEntry = existsSync2(clientEntryJs) ? clientEntryJs : existsSync2(clientEntryTs) ? clientEntryTs : null;
|
|
2207
2209
|
if (!clientEntry) {
|
|
2208
2210
|
console.error(`harness client entry \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${clientEntryJs}`);
|
|
@@ -2299,7 +2301,7 @@ function stripAnsi(s) {
|
|
|
2299
2301
|
return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
2300
2302
|
}
|
|
2301
2303
|
function watchForReady(child, pattern) {
|
|
2302
|
-
return new Promise((
|
|
2304
|
+
return new Promise((resolve7, reject) => {
|
|
2303
2305
|
const timeout = setTimeout(() => {
|
|
2304
2306
|
reject(new Error("Timed out waiting for scenario dev server to become ready (30s)"));
|
|
2305
2307
|
}, 3e4);
|
|
@@ -2315,7 +2317,7 @@ function watchForReady(child, pattern) {
|
|
|
2315
2317
|
const url = /^https?:\/\//i.test(captured) ? captured : `http://localhost:${captured}`;
|
|
2316
2318
|
resolved = true;
|
|
2317
2319
|
clearTimeout(timeout);
|
|
2318
|
-
|
|
2320
|
+
resolve7(url.replace(/\/$/, ""));
|
|
2319
2321
|
};
|
|
2320
2322
|
if (child.stdout) {
|
|
2321
2323
|
const rl = createInterface({ input: child.stdout });
|
|
@@ -2342,14 +2344,14 @@ var MAX_HARNESS_PORT_ATTEMPTS = 100;
|
|
|
2342
2344
|
var HARNESS_HOST = "0.0.0.0";
|
|
2343
2345
|
async function findFreePort() {
|
|
2344
2346
|
const net = await import("net");
|
|
2345
|
-
const tryPort = (port) => new Promise((
|
|
2347
|
+
const tryPort = (port) => new Promise((resolve7) => {
|
|
2346
2348
|
const server = net.createServer();
|
|
2347
2349
|
server.unref();
|
|
2348
|
-
server.once("error", () =>
|
|
2350
|
+
server.once("error", () => resolve7(null));
|
|
2349
2351
|
server.listen(port, HARNESS_HOST, () => {
|
|
2350
2352
|
const address = server.address();
|
|
2351
2353
|
const assigned = typeof address === "object" && address !== null ? address.port : port;
|
|
2352
|
-
server.close(() =>
|
|
2354
|
+
server.close(() => resolve7(assigned));
|
|
2353
2355
|
});
|
|
2354
2356
|
});
|
|
2355
2357
|
for (let i = 0; i < MAX_HARNESS_PORT_ATTEMPTS; i++) {
|
|
@@ -2363,8 +2365,8 @@ async function findFreePort() {
|
|
|
2363
2365
|
}
|
|
2364
2366
|
|
|
2365
2367
|
// src/sdk-version.ts
|
|
2366
|
-
import { existsSync as existsSync3, readFileSync as
|
|
2367
|
-
import { dirname as dirname4, resolve as
|
|
2368
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
2369
|
+
import { dirname as dirname4, resolve as resolve5 } from "path";
|
|
2368
2370
|
var SDK_PACKAGE_PATHS = [
|
|
2369
2371
|
["@uzuhq", "code-sdk"],
|
|
2370
2372
|
["@uzupj", "uzu-sdk"]
|
|
@@ -2376,17 +2378,17 @@ var readInstalledSdkVersion = (cwd) => {
|
|
|
2376
2378
|
"@uzuhq/code-sdk \u304C node_modules \u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u4F9D\u5B58\u3092 install \u3057\u3066\u304B\u3089 publish \u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2377
2379
|
);
|
|
2378
2380
|
}
|
|
2379
|
-
const version = JSON.parse(
|
|
2381
|
+
const version = JSON.parse(readFileSync5(pkgPath, "utf-8")).version;
|
|
2380
2382
|
if (!version) {
|
|
2381
2383
|
throw new Error(`${pkgPath} \u306B version \u304C\u3042\u308A\u307E\u305B\u3093\u3002`);
|
|
2382
2384
|
}
|
|
2383
2385
|
return version;
|
|
2384
2386
|
};
|
|
2385
2387
|
var findSdkPackageJson = (cwd) => {
|
|
2386
|
-
let dir =
|
|
2388
|
+
let dir = resolve5(cwd);
|
|
2387
2389
|
for (; ; ) {
|
|
2388
2390
|
const found = SDK_PACKAGE_PATHS.map(
|
|
2389
|
-
(segments) =>
|
|
2391
|
+
(segments) => resolve5(dir, "node_modules", ...segments, "package.json")
|
|
2390
2392
|
).find(existsSync3);
|
|
2391
2393
|
if (found) {
|
|
2392
2394
|
return found;
|
|
@@ -2448,7 +2450,7 @@ var failureHtml = (msg) => {
|
|
|
2448
2450
|
</body>`;
|
|
2449
2451
|
};
|
|
2450
2452
|
var startLoopbackServer = async () => {
|
|
2451
|
-
let
|
|
2453
|
+
let resolve7 = null;
|
|
2452
2454
|
let reject = null;
|
|
2453
2455
|
let delivered = false;
|
|
2454
2456
|
let pending = null;
|
|
@@ -2485,7 +2487,7 @@ var startLoopbackServer = async () => {
|
|
|
2485
2487
|
}
|
|
2486
2488
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2487
2489
|
res.end(SUCCESS_HTML);
|
|
2488
|
-
if (
|
|
2490
|
+
if (resolve7) resolve7(code);
|
|
2489
2491
|
else pending = { code };
|
|
2490
2492
|
});
|
|
2491
2493
|
await new Promise((res, rej) => {
|
|
@@ -2507,7 +2509,7 @@ var startLoopbackServer = async () => {
|
|
|
2507
2509
|
() => rej(new Error(`login timed out after ${timeoutMs}ms`)),
|
|
2508
2510
|
timeoutMs
|
|
2509
2511
|
);
|
|
2510
|
-
|
|
2512
|
+
resolve7 = (code) => {
|
|
2511
2513
|
clearTimeout(t);
|
|
2512
2514
|
res(code);
|
|
2513
2515
|
};
|
|
@@ -2555,8 +2557,8 @@ var runLoginFlow = async (env, onURL) => {
|
|
|
2555
2557
|
// src/cli.ts
|
|
2556
2558
|
var envOption = () => new Option("--env <env>", "\u63A5\u7D9A\u5148\u74B0\u5883 (dev | stg | prd)").default(DEFAULT_ENV).hideHelp();
|
|
2557
2559
|
var readOwnCliVersion = () => {
|
|
2558
|
-
const pkgPath =
|
|
2559
|
-
const version = JSON.parse(
|
|
2560
|
+
const pkgPath = resolve6(dirname5(fileURLToPath4(import.meta.url)), "..", "package.json");
|
|
2561
|
+
const version = JSON.parse(readFileSync6(pkgPath, "utf-8")).version;
|
|
2560
2562
|
if (!version) {
|
|
2561
2563
|
throw new Error("uzu-cli \u306E package.json \u306B version \u304C\u3042\u308A\u307E\u305B\u3093\u3002");
|
|
2562
2564
|
}
|
|
@@ -2581,12 +2583,12 @@ program.command("dev").description(
|
|
|
2581
2583
|
program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30C9\u3057\u3066 R2 \u306B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9 \u2192 \u767B\u9332").option("--change-notes <msg>", "\u30EA\u30D3\u30B8\u30E7\u30F3\u306E\u5909\u66F4\u30E1\u30E2", "").addOption(envOption()).action(async (options) => {
|
|
2582
2584
|
const env = resolveEnv(options.env);
|
|
2583
2585
|
const cwd = process.cwd();
|
|
2584
|
-
const manifestPath =
|
|
2586
|
+
const manifestPath = resolve6(cwd, "manifest.json");
|
|
2585
2587
|
if (!existsSync4(manifestPath)) {
|
|
2586
2588
|
console.error("manifest.json \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30B2\u30FC\u30E0\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
2587
2589
|
process.exit(1);
|
|
2588
2590
|
}
|
|
2589
|
-
const manifest = JSON.parse(
|
|
2591
|
+
const manifest = JSON.parse(readFileSync6(manifestPath, "utf-8"));
|
|
2590
2592
|
if (typeof manifest !== "object" || manifest === null) {
|
|
2591
2593
|
console.error("manifest.json \u304C\u4E0D\u6B63\u306A\u5F62\u5F0F\u3067\u3059\u3002");
|
|
2592
2594
|
process.exit(1);
|
|
@@ -2640,8 +2642,8 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
|
|
|
2640
2642
|
execSync(buildCommand, { stdio: "inherit", cwd });
|
|
2641
2643
|
}
|
|
2642
2644
|
console.log("Creating ZIP...");
|
|
2643
|
-
const absoluteOutputDir =
|
|
2644
|
-
const zipPath =
|
|
2645
|
+
const absoluteOutputDir = resolve6(cwd, outputDir);
|
|
2646
|
+
const zipPath = resolve6(cwd, "__zip__.zip");
|
|
2645
2647
|
await new Promise((res, reject) => {
|
|
2646
2648
|
const output = createWriteStream(zipPath);
|
|
2647
2649
|
const archive = new ZipArchive({ zlib: { level: 9 } });
|
|
@@ -2656,8 +2658,8 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
|
|
|
2656
2658
|
const { resourceId, revisionId, token } = await uploadGameToR2(env, zipPath, absoluteOutputDir);
|
|
2657
2659
|
console.log(`Uploaded to R2. revisionId: ${revisionId}, resourceId: ${resourceId}`);
|
|
2658
2660
|
if (manifest.serverActionLogicPath) {
|
|
2659
|
-
const logicEntryPoint =
|
|
2660
|
-
const logicOutPath =
|
|
2661
|
+
const logicEntryPoint = resolve6(cwd, manifest.serverActionLogicPath);
|
|
2662
|
+
const logicOutPath = resolve6(cwd, "__logic__.js");
|
|
2661
2663
|
try {
|
|
2662
2664
|
console.log("Building server logic...");
|
|
2663
2665
|
await buildServerLogic(logicEntryPoint, logicOutPath);
|
|
@@ -2672,7 +2674,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
|
|
|
2672
2674
|
process.exit(1);
|
|
2673
2675
|
}
|
|
2674
2676
|
console.log("Uploading logic.js to R2...");
|
|
2675
|
-
const logicJsContent =
|
|
2677
|
+
const logicJsContent = readFileSync6(logicOutPath, "utf-8");
|
|
2676
2678
|
await uploadLogicToR2(env, token, logicJsContent, { wireVersion: WIRE_VERSION });
|
|
2677
2679
|
} finally {
|
|
2678
2680
|
if (existsSync4(logicOutPath)) unlinkSync(logicOutPath);
|
|
@@ -2683,7 +2685,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
|
|
|
2683
2685
|
console.log("Resolving character icons...");
|
|
2684
2686
|
const isLocalIcon = (char) => !!char.icon && !char.icon.startsWith("http://") && !char.icon.startsWith("https://");
|
|
2685
2687
|
const localIconPaths = manifestCharacters.filter(isLocalIcon).map((char) => {
|
|
2686
|
-
const iconAbsPath =
|
|
2688
|
+
const iconAbsPath = resolve6(cwd, char.icon);
|
|
2687
2689
|
if (!existsSync4(iconAbsPath)) {
|
|
2688
2690
|
console.error(`\u30A2\u30A4\u30B3\u30F3\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${char.icon}`);
|
|
2689
2691
|
process.exit(1);
|
|
@@ -2693,7 +2695,7 @@ program.command("publish").description("\u30B2\u30FC\u30E0\u3092\u30D3\u30EB\u30
|
|
|
2693
2695
|
const iconURLs = await uploadIconsToCfImages(env, token, localIconPaths);
|
|
2694
2696
|
resolvedCharacters = manifestCharacters.map((char) => {
|
|
2695
2697
|
if (!isLocalIcon(char)) return char;
|
|
2696
|
-
const cfImagesUrl = iconURLs.get(
|
|
2698
|
+
const cfImagesUrl = iconURLs.get(resolve6(cwd, char.icon));
|
|
2697
2699
|
if (!cfImagesUrl) {
|
|
2698
2700
|
console.error(`\u30A2\u30A4\u30B3\u30F3\u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u7D50\u679C\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${char.icon}`);
|
|
2699
2701
|
process.exit(1);
|
|
@@ -6,8 +6,214 @@ var DEFAULT_ICON_URLS = [
|
|
|
6
6
|
"https://imagedelivery.net/htp-D7B2hJT5XtdWYN9e7Q/43f45d11-da38-4d6e-637d-3df78e583500/original"
|
|
7
7
|
];
|
|
8
8
|
|
|
9
|
+
// ../engine-core/src/game-time.ts
|
|
10
|
+
var toMs = (t) => t;
|
|
11
|
+
var plus = (t, d) => toMs(t) + d;
|
|
12
|
+
var sub = (t, d) => toMs(t) - d;
|
|
13
|
+
var minus = (a, b) => toMs(a) - toMs(b);
|
|
14
|
+
var asGameTime = (ms) => ms;
|
|
15
|
+
var GAME_START = asGameTime(0);
|
|
16
|
+
|
|
9
17
|
// ../engine-core/src/game-clock.ts
|
|
10
18
|
var NO_PLAYERS_GRACE_MS = 5 * 6e4;
|
|
19
|
+
var GameClock = class {
|
|
20
|
+
startedAtWall = 0;
|
|
21
|
+
pausedTotalMs = 0;
|
|
22
|
+
pausedAtWall = null;
|
|
23
|
+
frozenBy = /* @__PURE__ */ new Set();
|
|
24
|
+
/** 時計を 0 から始め直す。`setup()` を呼ぶ直前に通す。 */
|
|
25
|
+
restart() {
|
|
26
|
+
this.startedAtWall = Date.now();
|
|
27
|
+
this.pausedTotalMs = 0;
|
|
28
|
+
this.pausedAtWall = null;
|
|
29
|
+
this.frozenBy.clear();
|
|
30
|
+
}
|
|
31
|
+
get frozen() {
|
|
32
|
+
return this.frozenBy.size > 0;
|
|
33
|
+
}
|
|
34
|
+
isFrozenBy(reason) {
|
|
35
|
+
return this.frozenBy.has(reason);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 現在のゲーム内時刻。停止中は `pausedAtWall` で凍るので同じ値を返し続ける。
|
|
39
|
+
*
|
|
40
|
+
* IMPORTANT: [withGameClock] の中で呼んではいけない。実装が `Date.now()` を読むので、
|
|
41
|
+
* 差し替え済みの時刻を実時刻として引き算し、大きく負の値になる。
|
|
42
|
+
* ハンドラへ渡す時刻は必ず外で 1 回取ってから渡すこと。
|
|
43
|
+
*/
|
|
44
|
+
now() {
|
|
45
|
+
return asGameTime((this.pausedAtWall ?? Date.now()) - this.startedAtWall - this.pausedTotalMs);
|
|
46
|
+
}
|
|
47
|
+
/** ゲーム内時刻を、alarm / setTimeout が使う実時刻へ直す。 */
|
|
48
|
+
toWall(at) {
|
|
49
|
+
return toMs(at) + this.startedAtWall + this.pausedTotalMs;
|
|
50
|
+
}
|
|
51
|
+
/** 実際に理由を足したら true。呼び出し側が永続化や tick 停止の要否に使う。 */
|
|
52
|
+
freeze(reason) {
|
|
53
|
+
if (this.frozenBy.has(reason)) return false;
|
|
54
|
+
const wasFrozen = this.frozen;
|
|
55
|
+
this.frozenBy.add(reason);
|
|
56
|
+
if (!wasFrozen) this.pausedAtWall = Date.now();
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/** 実際に理由を外したら true。 */
|
|
60
|
+
unfreeze(reason) {
|
|
61
|
+
if (!this.frozenBy.delete(reason)) return false;
|
|
62
|
+
if (!this.frozen && this.pausedAtWall !== null) {
|
|
63
|
+
this.pausedTotalMs += Date.now() - this.pausedAtWall;
|
|
64
|
+
this.pausedAtWall = null;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
snapshot() {
|
|
69
|
+
return {
|
|
70
|
+
startedAtWall: this.startedAtWall,
|
|
71
|
+
pausedTotalMs: this.pausedTotalMs,
|
|
72
|
+
pausedAtWall: this.pausedAtWall,
|
|
73
|
+
frozenBy: [...this.frozenBy]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 永続化した内容から復元する。欠損は「未停止」に倒す。
|
|
78
|
+
*
|
|
79
|
+
* 読めなかったせいで世界が止まったままになる方が、動き出すより悪い。
|
|
80
|
+
*/
|
|
81
|
+
restore(saved) {
|
|
82
|
+
this.startedAtWall = saved.startedAtWall ?? 0;
|
|
83
|
+
this.pausedTotalMs = saved.pausedTotalMs ?? 0;
|
|
84
|
+
this.pausedAtWall = saved.pausedAtWall ?? null;
|
|
85
|
+
this.frozenBy.clear();
|
|
86
|
+
for (const reason of saved.frozenBy ?? []) this.frozenBy.add(reason);
|
|
87
|
+
if (this.frozen && this.pausedAtWall === null) this.frozenBy.clear();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
function withGameClock(time, fn) {
|
|
91
|
+
const realNow = Date.now;
|
|
92
|
+
const RealDate = Date;
|
|
93
|
+
const ms = toMs(time);
|
|
94
|
+
Date.now = () => ms;
|
|
95
|
+
globalThis.Date = new Proxy(RealDate, {
|
|
96
|
+
construct: (target, args) => Reflect.construct(target, args.length === 0 ? [ms] : args)
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
return fn();
|
|
100
|
+
} finally {
|
|
101
|
+
globalThis.Date = RealDate;
|
|
102
|
+
Date.now = realNow;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function resolveDeadline(at, key, warned, report) {
|
|
106
|
+
if (at === null || at === void 0) return null;
|
|
107
|
+
if (typeof at === "number" && Number.isFinite(at)) return asGameTime(at);
|
|
108
|
+
if (!warned.has(key)) {
|
|
109
|
+
warned.add(key);
|
|
110
|
+
report(
|
|
111
|
+
`deadline "${key}" \u306E at() \u304C\u7DE0\u5207\u306B\u4F7F\u3048\u306A\u3044\u5024\u3092\u8FD4\u3057\u305F: ${String(at)}\u3002\u3053\u306E\u7DE0\u5207\u306F\u4E8C\u5EA6\u3068\u767A\u706B\u3057\u306A\u3044\u3002\u5EC3\u6B62\u3055\u308C\u305F ctx.now \u3092\u8AAD\u3093\u3067\u3044\u306A\u3044\u304B\u78BA\u8A8D\u3059\u308B\u3053\u3068`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ../engine-core/src/json-patch.ts
|
|
118
|
+
function escapePointer(key) {
|
|
119
|
+
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
120
|
+
}
|
|
121
|
+
function unescapePointer(token) {
|
|
122
|
+
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
123
|
+
}
|
|
124
|
+
function compare(oldObj, newObj, basePath = "") {
|
|
125
|
+
if (oldObj === newObj) return [];
|
|
126
|
+
if (oldObj === null || newObj === null || typeof oldObj !== "object" || typeof newObj !== "object") {
|
|
127
|
+
return [{ op: "replace", path: basePath || "/", value: newObj }];
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(oldObj) || Array.isArray(newObj)) {
|
|
130
|
+
if (JSON.stringify(oldObj) === JSON.stringify(newObj)) return [];
|
|
131
|
+
return [{ op: "replace", path: basePath || "/", value: newObj }];
|
|
132
|
+
}
|
|
133
|
+
const ops = [];
|
|
134
|
+
const oldKeys = Object.keys(oldObj);
|
|
135
|
+
const newKeys = Object.keys(newObj);
|
|
136
|
+
for (const key of oldKeys) {
|
|
137
|
+
if (!(key in newObj)) {
|
|
138
|
+
ops.push({ op: "remove", path: `${basePath}/${escapePointer(key)}` });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const key of newKeys) {
|
|
142
|
+
const childPath = `${basePath}/${escapePointer(key)}`;
|
|
143
|
+
if (!(key in oldObj)) {
|
|
144
|
+
ops.push({ op: "add", path: childPath, value: newObj[key] });
|
|
145
|
+
} else {
|
|
146
|
+
const childOps = compare(oldObj[key], newObj[key], childPath);
|
|
147
|
+
ops.push(...childOps);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return ops;
|
|
151
|
+
}
|
|
152
|
+
function applyPatch(doc, ops) {
|
|
153
|
+
for (const op of ops) {
|
|
154
|
+
const tokens = op.path.split("/").slice(1).map(unescapePointer);
|
|
155
|
+
if (tokens.length === 0) return false;
|
|
156
|
+
if (op.op === "replace" || op.op === "add") {
|
|
157
|
+
let target = doc;
|
|
158
|
+
for (let i = 0; i < tokens.length - 1; i++) {
|
|
159
|
+
target = target?.[tokens[i]];
|
|
160
|
+
if (target === void 0 || target === null) return false;
|
|
161
|
+
}
|
|
162
|
+
const lastKey = tokens[tokens.length - 1];
|
|
163
|
+
target[lastKey] = op.value;
|
|
164
|
+
} else if (op.op === "remove") {
|
|
165
|
+
let target = doc;
|
|
166
|
+
for (let i = 0; i < tokens.length - 1; i++) {
|
|
167
|
+
target = target?.[tokens[i]];
|
|
168
|
+
if (target === void 0 || target === null) return false;
|
|
169
|
+
}
|
|
170
|
+
const lastKey = tokens[tokens.length - 1];
|
|
171
|
+
if (Array.isArray(target)) {
|
|
172
|
+
target.splice(Number(lastKey), 1);
|
|
173
|
+
} else {
|
|
174
|
+
delete target[lastKey];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ../engine-core/src/random.ts
|
|
182
|
+
var SeededRandomImpl = class _SeededRandomImpl {
|
|
183
|
+
_state;
|
|
184
|
+
constructor(seed) {
|
|
185
|
+
this._state = seed | 0;
|
|
186
|
+
}
|
|
187
|
+
get state() {
|
|
188
|
+
return this._state;
|
|
189
|
+
}
|
|
190
|
+
static fromState(state) {
|
|
191
|
+
const r = new _SeededRandomImpl(0);
|
|
192
|
+
r._state = state;
|
|
193
|
+
return r;
|
|
194
|
+
}
|
|
195
|
+
float() {
|
|
196
|
+
this._state |= 0;
|
|
197
|
+
this._state = this._state + 1831565813 | 0;
|
|
198
|
+
let t = Math.imul(this._state ^ this._state >>> 15, 1 | this._state);
|
|
199
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
200
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
201
|
+
}
|
|
202
|
+
int(max) {
|
|
203
|
+
return Math.floor(this.float() * max);
|
|
204
|
+
}
|
|
205
|
+
pick(array) {
|
|
206
|
+
return array[this.int(array.length)];
|
|
207
|
+
}
|
|
208
|
+
shuffle(array) {
|
|
209
|
+
const a = [...array];
|
|
210
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
211
|
+
const j = this.int(i + 1);
|
|
212
|
+
[a[i], a[j]] = [a[j], a[i]];
|
|
213
|
+
}
|
|
214
|
+
return a;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
11
217
|
|
|
12
218
|
// ../engine-core/src/server-only.ts
|
|
13
219
|
function serverOnly(handler) {
|
|
@@ -16,9 +222,46 @@ function serverOnly(handler) {
|
|
|
16
222
|
function isServerOnlyAction(handler) {
|
|
17
223
|
return typeof handler === "function" && "__serverOnly" in handler && handler.__serverOnly === true;
|
|
18
224
|
}
|
|
225
|
+
|
|
226
|
+
// ../engine-core/src/roster.ts
|
|
227
|
+
function parseRoster(rosterParam) {
|
|
228
|
+
if (!rosterParam) return null;
|
|
229
|
+
let raw;
|
|
230
|
+
try {
|
|
231
|
+
raw = JSON.parse(rosterParam);
|
|
232
|
+
} catch {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
if (!Array.isArray(raw)) return null;
|
|
236
|
+
return raw.filter((p) => (p.kind ?? "player") === "player").map((p) => ({
|
|
237
|
+
id: p.id,
|
|
238
|
+
nickname: p.name ?? "Guest",
|
|
239
|
+
iconUrl: p.iconUrl ?? "",
|
|
240
|
+
characterId: p.characterId
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ../engine-core/src/versions.ts
|
|
245
|
+
var BRIDGE_VERSION = 2;
|
|
246
|
+
var WIRE_VERSION = 2;
|
|
19
247
|
export {
|
|
248
|
+
BRIDGE_VERSION,
|
|
20
249
|
DEFAULT_ICON_URLS,
|
|
250
|
+
GAME_START,
|
|
251
|
+
GameClock,
|
|
252
|
+
NO_PLAYERS_GRACE_MS,
|
|
21
253
|
SERVER_TIME,
|
|
254
|
+
SeededRandomImpl,
|
|
255
|
+
WIRE_VERSION,
|
|
256
|
+
applyPatch,
|
|
257
|
+
asGameTime,
|
|
258
|
+
compare,
|
|
22
259
|
isServerOnlyAction,
|
|
23
|
-
|
|
260
|
+
minus,
|
|
261
|
+
parseRoster,
|
|
262
|
+
plus,
|
|
263
|
+
resolveDeadline,
|
|
264
|
+
serverOnly,
|
|
265
|
+
sub,
|
|
266
|
+
withGameClock
|
|
24
267
|
};
|