launch-unity 0.13.0 → 0.14.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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/launch.d.ts +2 -0
- package/dist/launch.d.ts.map +1 -1
- package/dist/launch.js +118 -9
- package/dist/lib.d.ts +4 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +107 -8
- package/dist/unityHub.d.ts +1 -0
- package/dist/unityHub.d.ts.map +1 -1
- package/dist/unityHub.js +21 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Exports core functions for programmatic usage.
|
|
4
4
|
* Uses lib.ts which has no CLI side effects.
|
|
5
5
|
*/
|
|
6
|
-
export { LaunchOptions, LaunchResolvedOptions, UnityProcessInfo, parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, } from './lib.js';
|
|
6
|
+
export { LaunchOptions, LaunchResolvedOptions, UnityProcessInfo, parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAEhB,SAAS,EACT,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAEhB,SAAS,EACT,mBAAmB,EACnB,eAAe,EACf,MAAM,EACN,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export {
|
|
7
7
|
// Functions
|
|
8
|
-
parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, } from './lib.js';
|
|
8
|
+
parseArgs, findUnityProjectBfs, getUnityVersion, launch, findRunningUnityProcess, focusUnityProcess, killRunningUnity, quitRunningUnity, handleStaleLockfile, ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs, } from './lib.js';
|
package/dist/launch.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type LaunchOptions = {
|
|
|
6
6
|
unityArgs: string[];
|
|
7
7
|
searchMaxDepth: number;
|
|
8
8
|
restart: boolean;
|
|
9
|
+
quit: boolean;
|
|
9
10
|
addUnityHub: boolean;
|
|
10
11
|
favoriteUnityHub: boolean;
|
|
11
12
|
};
|
|
@@ -25,6 +26,7 @@ export declare function findRunningUnityProcess(projectPath: string): Promise<Un
|
|
|
25
26
|
export declare function focusUnityProcess(pid: number): Promise<void>;
|
|
26
27
|
export declare function handleStaleLockfile(projectPath: string): Promise<void>;
|
|
27
28
|
export declare function killRunningUnity(projectPath: string): Promise<void>;
|
|
29
|
+
export declare function quitRunningUnity(projectPath: string): Promise<void>;
|
|
28
30
|
export declare function findUnityProjectBfs(rootDir: string, maxDepth: number): string | undefined;
|
|
29
31
|
export declare function launch(opts: LaunchResolvedOptions): Promise<void>;
|
|
30
32
|
//# sourceMappingURL=launch.d.ts.map
|
package/dist/launch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":";AAeA,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAyJF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":";AAeA,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAyJF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2HvD;AA0CD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAe3D;AA2ND,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAIxG;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;AA4CD,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B5E;AAkCD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBzE;AA0CD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BzE;AAgDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqDzF;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvE"}
|
package/dist/launch.js
CHANGED
|
@@ -9,7 +9,7 @@ import { rm } from "node:fs/promises";
|
|
|
9
9
|
import { dirname, join, resolve } from "node:path";
|
|
10
10
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
|
-
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs } from "./unityHub.js";
|
|
12
|
+
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, groupCliArgs } from "./unityHub.js";
|
|
13
13
|
const execFileAsync = promisify(execFile);
|
|
14
14
|
const UNITY_EXECUTABLE_PATTERN_MAC = /Unity\.app\/Contents\/MacOS\/Unity/i;
|
|
15
15
|
const UNITY_EXECUTABLE_PATTERN_WINDOWS = /Unity\.exe/i;
|
|
@@ -144,6 +144,7 @@ export function parseArgs(argv) {
|
|
|
144
144
|
const positionals = [];
|
|
145
145
|
let maxDepth = 3; // default 3; -1 means unlimited
|
|
146
146
|
let restart = false;
|
|
147
|
+
let quit = false;
|
|
147
148
|
let addUnityHub = false;
|
|
148
149
|
let favoriteUnityHub = false;
|
|
149
150
|
let platform;
|
|
@@ -161,6 +162,10 @@ export function parseArgs(argv) {
|
|
|
161
162
|
restart = true;
|
|
162
163
|
continue;
|
|
163
164
|
}
|
|
165
|
+
if (arg === "-q" || arg === "--quit") {
|
|
166
|
+
quit = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
164
169
|
if (arg === "-u" ||
|
|
165
170
|
arg === "-a" ||
|
|
166
171
|
arg === "--unity-hub-entry" ||
|
|
@@ -226,6 +231,7 @@ export function parseArgs(argv) {
|
|
|
226
231
|
unityArgs,
|
|
227
232
|
searchMaxDepth: maxDepth,
|
|
228
233
|
restart,
|
|
234
|
+
quit,
|
|
229
235
|
addUnityHub,
|
|
230
236
|
favoriteUnityHub,
|
|
231
237
|
};
|
|
@@ -266,6 +272,7 @@ Options:
|
|
|
266
272
|
-h, --help Show this help message
|
|
267
273
|
-v, --version Show version number
|
|
268
274
|
-r, --restart Kill running Unity and restart
|
|
275
|
+
-q, --quit Quit running Unity gracefully (force-kill on timeout)
|
|
269
276
|
-p, --platform <P> Passed to Unity as -buildTarget (e.g., StandaloneOSX, Android, iOS)
|
|
270
277
|
--max-depth <N> Search depth when PROJECT_PATH is omitted (default 3, -1 unlimited)
|
|
271
278
|
-u, -a, --unity-hub-entry, --add-unity-hub
|
|
@@ -554,9 +561,11 @@ export async function handleStaleLockfile(projectPath) {
|
|
|
554
561
|
const message = error instanceof Error ? error.message : String(error);
|
|
555
562
|
console.warn(`Failed to delete UnityLockfile: ${message}`);
|
|
556
563
|
}
|
|
564
|
+
console.log();
|
|
557
565
|
}
|
|
558
566
|
const KILL_POLL_INTERVAL_MS = 100;
|
|
559
567
|
const KILL_TIMEOUT_MS = 10000;
|
|
568
|
+
const GRACEFUL_QUIT_TIMEOUT_MS = 10000;
|
|
560
569
|
function isProcessAlive(pid) {
|
|
561
570
|
try {
|
|
562
571
|
process.kill(pid, 0);
|
|
@@ -574,9 +583,9 @@ function killProcess(pid) {
|
|
|
574
583
|
// Process already exited
|
|
575
584
|
}
|
|
576
585
|
}
|
|
577
|
-
async function waitForProcessExit(pid) {
|
|
586
|
+
async function waitForProcessExit(pid, timeoutMs) {
|
|
578
587
|
const start = Date.now();
|
|
579
|
-
while (Date.now() - start <
|
|
588
|
+
while (Date.now() - start < timeoutMs) {
|
|
580
589
|
if (!isProcessAlive(pid)) {
|
|
581
590
|
return true;
|
|
582
591
|
}
|
|
@@ -588,17 +597,82 @@ export async function killRunningUnity(projectPath) {
|
|
|
588
597
|
const processInfo = await findRunningUnityProcess(projectPath);
|
|
589
598
|
if (!processInfo) {
|
|
590
599
|
console.log("No running Unity process found for this project.");
|
|
600
|
+
console.log();
|
|
591
601
|
return;
|
|
592
602
|
}
|
|
593
603
|
const pid = processInfo.pid;
|
|
594
604
|
console.log(`Killing Unity (PID: ${pid})...`);
|
|
595
605
|
killProcess(pid);
|
|
596
|
-
const exited = await waitForProcessExit(pid);
|
|
606
|
+
const exited = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
|
|
597
607
|
if (!exited) {
|
|
598
608
|
console.error(`Error: Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
|
|
599
609
|
process.exit(1);
|
|
600
610
|
}
|
|
601
611
|
console.log("Unity killed.");
|
|
612
|
+
console.log();
|
|
613
|
+
}
|
|
614
|
+
async function sendGracefulQuitMac(pid) {
|
|
615
|
+
// System Events quit and "tell application to quit" leave UnityLockfile behind,
|
|
616
|
+
// so we send Cmd+Q keystroke to trigger Unity's normal user-initiated shutdown flow
|
|
617
|
+
const script = [
|
|
618
|
+
'tell application "System Events"',
|
|
619
|
+
` set frontmost of (first process whose unix id is ${pid}) to true`,
|
|
620
|
+
' keystroke "q" using {command down}',
|
|
621
|
+
"end tell",
|
|
622
|
+
].join("\n");
|
|
623
|
+
try {
|
|
624
|
+
await execFileAsync("osascript", ["-e", script]);
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
// Process may have already exited
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async function sendGracefulQuitWindows(pid) {
|
|
631
|
+
// process.kill(pid, "SIGTERM") forcefully kills on Windows, so use CloseMainWindow() to send WM_CLOSE
|
|
632
|
+
const scriptLines = [
|
|
633
|
+
"$ErrorActionPreference = 'Stop'",
|
|
634
|
+
`try { $proc = Get-Process -Id ${pid} -ErrorAction Stop; $proc.CloseMainWindow() | Out-Null } catch { }`,
|
|
635
|
+
];
|
|
636
|
+
try {
|
|
637
|
+
await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
// Process may have already exited
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async function sendGracefulQuit(pid) {
|
|
644
|
+
if (process.platform === "darwin") {
|
|
645
|
+
await sendGracefulQuitMac(pid);
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (process.platform === "win32") {
|
|
649
|
+
await sendGracefulQuitWindows(pid);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
export async function quitRunningUnity(projectPath) {
|
|
654
|
+
const processInfo = await findRunningUnityProcess(projectPath);
|
|
655
|
+
if (!processInfo) {
|
|
656
|
+
console.log("No running Unity process found for this project.");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const pid = processInfo.pid;
|
|
660
|
+
console.log(`Quitting Unity (PID: ${pid})...`);
|
|
661
|
+
await sendGracefulQuit(pid);
|
|
662
|
+
console.log(`Sent graceful quit signal. Waiting up to ${GRACEFUL_QUIT_TIMEOUT_MS / 1000}s...`);
|
|
663
|
+
const exitedGracefully = await waitForProcessExit(pid, GRACEFUL_QUIT_TIMEOUT_MS);
|
|
664
|
+
if (exitedGracefully) {
|
|
665
|
+
console.log("Unity quit gracefully.");
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
console.log("Unity did not respond to graceful quit. Force killing...");
|
|
669
|
+
killProcess(pid);
|
|
670
|
+
const exitedAfterKill = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
|
|
671
|
+
if (!exitedAfterKill) {
|
|
672
|
+
console.error(`Error: Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
console.log("Unity force killed.");
|
|
602
676
|
}
|
|
603
677
|
function hasBuildTargetArg(unityArgs) {
|
|
604
678
|
for (const arg of unityArgs) {
|
|
@@ -698,7 +772,6 @@ export function findUnityProjectBfs(rootDir, maxDepth) {
|
|
|
698
772
|
export async function launch(opts) {
|
|
699
773
|
const { projectPath, platform, unityArgs, unityVersion } = opts;
|
|
700
774
|
const unityPath = getUnityPath(unityVersion);
|
|
701
|
-
console.log(`Detected Unity version: ${unityVersion}`);
|
|
702
775
|
if (!existsSync(unityPath)) {
|
|
703
776
|
console.error(`Error: Unity ${unityVersion} not found at ${unityPath}`);
|
|
704
777
|
console.error("Please install Unity through Unity Hub.");
|
|
@@ -706,6 +779,7 @@ export async function launch(opts) {
|
|
|
706
779
|
}
|
|
707
780
|
console.log("Opening Unity...");
|
|
708
781
|
console.log(`Project Path: ${projectPath}`);
|
|
782
|
+
console.log(`Detected Unity version: ${unityVersion}`);
|
|
709
783
|
const args = ["-projectPath", projectPath];
|
|
710
784
|
const unityArgsContainBuildTarget = hasBuildTargetArg(unityArgs);
|
|
711
785
|
if (platform && platform.length > 0 && !unityArgsContainBuildTarget) {
|
|
@@ -713,13 +787,40 @@ export async function launch(opts) {
|
|
|
713
787
|
}
|
|
714
788
|
const hubCliArgs = await getProjectCliArgs(projectPath);
|
|
715
789
|
if (hubCliArgs.length > 0) {
|
|
790
|
+
console.log("Unity Hub launch options:");
|
|
791
|
+
for (const line of groupCliArgs(hubCliArgs)) {
|
|
792
|
+
console.log(` ${line}`);
|
|
793
|
+
}
|
|
716
794
|
args.push(...hubCliArgs);
|
|
717
795
|
}
|
|
796
|
+
else {
|
|
797
|
+
console.log("Unity Hub launch options: none");
|
|
798
|
+
}
|
|
718
799
|
if (unityArgs.length > 0) {
|
|
719
800
|
args.push(...unityArgs);
|
|
720
801
|
}
|
|
721
|
-
|
|
722
|
-
|
|
802
|
+
return new Promise((resolve, reject) => {
|
|
803
|
+
const child = spawn(unityPath, args, {
|
|
804
|
+
stdio: "ignore",
|
|
805
|
+
detached: true,
|
|
806
|
+
// Git Bash (MSYS) がWindows パスをUnix 形式に自動変換するのを防ぐ
|
|
807
|
+
env: {
|
|
808
|
+
...process.env,
|
|
809
|
+
MSYS_NO_PATHCONV: "1",
|
|
810
|
+
},
|
|
811
|
+
});
|
|
812
|
+
const handleError = (error) => {
|
|
813
|
+
child.removeListener("spawn", handleSpawn);
|
|
814
|
+
reject(new Error(`Failed to launch Unity: ${error.message}`));
|
|
815
|
+
};
|
|
816
|
+
const handleSpawn = () => {
|
|
817
|
+
child.removeListener("error", handleError);
|
|
818
|
+
child.unref();
|
|
819
|
+
resolve();
|
|
820
|
+
};
|
|
821
|
+
child.once("error", handleError);
|
|
822
|
+
child.once("spawn", handleSpawn);
|
|
823
|
+
});
|
|
723
824
|
}
|
|
724
825
|
async function main() {
|
|
725
826
|
const options = parseArgs(process.argv);
|
|
@@ -731,14 +832,14 @@ async function main() {
|
|
|
731
832
|
if (!resolvedProjectPath) {
|
|
732
833
|
const searchRoot = process.cwd();
|
|
733
834
|
const depthInfo = options.searchMaxDepth === -1 ? "unlimited" : String(options.searchMaxDepth);
|
|
734
|
-
console.log(`
|
|
835
|
+
console.log(`Searching for Unity project under ${searchRoot} (max-depth: ${depthInfo})...`);
|
|
735
836
|
const found = findUnityProjectBfs(searchRoot, options.searchMaxDepth);
|
|
736
837
|
if (!found) {
|
|
737
838
|
console.error(`Error: Unity project not found under ${searchRoot}.`);
|
|
738
839
|
process.exit(1);
|
|
739
840
|
return;
|
|
740
841
|
}
|
|
741
|
-
console.log(
|
|
842
|
+
console.log();
|
|
742
843
|
resolvedProjectPath = found;
|
|
743
844
|
}
|
|
744
845
|
ensureProjectPath(resolvedProjectPath);
|
|
@@ -759,6 +860,14 @@ async function main() {
|
|
|
759
860
|
}
|
|
760
861
|
return;
|
|
761
862
|
}
|
|
863
|
+
if (options.quit && options.restart) {
|
|
864
|
+
console.error("Error: --quit and --restart cannot be used together.");
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
if (options.quit) {
|
|
868
|
+
await quitRunningUnity(resolvedProjectPath);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
762
871
|
if (options.restart) {
|
|
763
872
|
await killRunningUnity(resolvedProjectPath);
|
|
764
873
|
}
|
package/dist/lib.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* launch-unity core library functions.
|
|
3
3
|
* Pure library code without CLI entry point or side effects.
|
|
4
4
|
*/
|
|
5
|
-
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs } from "./unityHub.js";
|
|
5
|
+
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs } from "./unityHub.js";
|
|
6
6
|
export type LaunchOptions = {
|
|
7
7
|
subcommand?: "update";
|
|
8
8
|
projectPath?: string;
|
|
@@ -10,6 +10,7 @@ export type LaunchOptions = {
|
|
|
10
10
|
unityArgs: string[];
|
|
11
11
|
searchMaxDepth: number;
|
|
12
12
|
restart: boolean;
|
|
13
|
+
quit: boolean;
|
|
13
14
|
addUnityHub: boolean;
|
|
14
15
|
favoriteUnityHub: boolean;
|
|
15
16
|
};
|
|
@@ -29,7 +30,8 @@ export declare function findRunningUnityProcess(projectPath: string): Promise<Un
|
|
|
29
30
|
export declare function focusUnityProcess(pid: number): Promise<void>;
|
|
30
31
|
export declare function handleStaleLockfile(projectPath: string): Promise<void>;
|
|
31
32
|
export declare function killRunningUnity(projectPath: string): Promise<void>;
|
|
33
|
+
export declare function quitRunningUnity(projectPath: string): Promise<void>;
|
|
32
34
|
export declare function findUnityProjectBfs(rootDir: string, maxDepth: number): string | undefined;
|
|
33
35
|
export declare function launch(opts: LaunchResolvedOptions): Promise<void>;
|
|
34
|
-
export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs };
|
|
36
|
+
export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs };
|
|
35
37
|
//# sourceMappingURL=lib.d.ts.map
|
package/dist/lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvI,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAYF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CAyHvD;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAc3D;AAoND,wBAAsB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAIxG;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlE;AA4CD,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B5E;AAkCD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBzE;AA0CD,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BzE;AAgDD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqDzF;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DvE;AAGD,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/lib.js
CHANGED
|
@@ -7,7 +7,7 @@ import { existsSync, readFileSync, readdirSync, lstatSync, realpathSync } from "
|
|
|
7
7
|
import { rm } from "node:fs/promises";
|
|
8
8
|
import { join, resolve } from "node:path";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
|
-
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs } from "./unityHub.js";
|
|
10
|
+
import { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs } from "./unityHub.js";
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
const UNITY_EXECUTABLE_PATTERN_MAC = /Unity\.app\/Contents\/MacOS\/Unity/i;
|
|
13
13
|
const UNITY_EXECUTABLE_PATTERN_WINDOWS = /Unity\.exe/i;
|
|
@@ -31,6 +31,7 @@ export function parseArgs(argv) {
|
|
|
31
31
|
const positionals = [];
|
|
32
32
|
let maxDepth = 3; // default 3; -1 means unlimited
|
|
33
33
|
let restart = false;
|
|
34
|
+
let quit = false;
|
|
34
35
|
let addUnityHub = false;
|
|
35
36
|
let favoriteUnityHub = false;
|
|
36
37
|
let platform;
|
|
@@ -46,6 +47,10 @@ export function parseArgs(argv) {
|
|
|
46
47
|
restart = true;
|
|
47
48
|
continue;
|
|
48
49
|
}
|
|
50
|
+
if (arg === "-q" || arg === "--quit") {
|
|
51
|
+
quit = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
49
54
|
if (arg === "-u" ||
|
|
50
55
|
arg === "-a" ||
|
|
51
56
|
arg === "--unity-hub-entry" ||
|
|
@@ -111,6 +116,7 @@ export function parseArgs(argv) {
|
|
|
111
116
|
unityArgs,
|
|
112
117
|
searchMaxDepth: maxDepth,
|
|
113
118
|
restart,
|
|
119
|
+
quit,
|
|
114
120
|
addUnityHub,
|
|
115
121
|
favoriteUnityHub,
|
|
116
122
|
};
|
|
@@ -393,9 +399,11 @@ export async function handleStaleLockfile(projectPath) {
|
|
|
393
399
|
const message = error instanceof Error ? error.message : String(error);
|
|
394
400
|
console.warn(`Failed to delete UnityLockfile: ${message}`);
|
|
395
401
|
}
|
|
402
|
+
console.log();
|
|
396
403
|
}
|
|
397
404
|
const KILL_POLL_INTERVAL_MS = 100;
|
|
398
405
|
const KILL_TIMEOUT_MS = 10000;
|
|
406
|
+
const GRACEFUL_QUIT_TIMEOUT_MS = 10000;
|
|
399
407
|
function isProcessAlive(pid) {
|
|
400
408
|
try {
|
|
401
409
|
process.kill(pid, 0);
|
|
@@ -413,9 +421,9 @@ function killProcess(pid) {
|
|
|
413
421
|
// Process already exited
|
|
414
422
|
}
|
|
415
423
|
}
|
|
416
|
-
async function waitForProcessExit(pid) {
|
|
424
|
+
async function waitForProcessExit(pid, timeoutMs) {
|
|
417
425
|
const start = Date.now();
|
|
418
|
-
while (Date.now() - start <
|
|
426
|
+
while (Date.now() - start < timeoutMs) {
|
|
419
427
|
if (!isProcessAlive(pid)) {
|
|
420
428
|
return true;
|
|
421
429
|
}
|
|
@@ -427,16 +435,80 @@ export async function killRunningUnity(projectPath) {
|
|
|
427
435
|
const processInfo = await findRunningUnityProcess(projectPath);
|
|
428
436
|
if (!processInfo) {
|
|
429
437
|
console.log("No running Unity process found for this project.");
|
|
438
|
+
console.log();
|
|
430
439
|
return;
|
|
431
440
|
}
|
|
432
441
|
const pid = processInfo.pid;
|
|
433
442
|
console.log(`Killing Unity (PID: ${pid})...`);
|
|
434
443
|
killProcess(pid);
|
|
435
|
-
const exited = await waitForProcessExit(pid);
|
|
444
|
+
const exited = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
|
|
436
445
|
if (!exited) {
|
|
437
446
|
throw new Error(`Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
|
|
438
447
|
}
|
|
439
448
|
console.log("Unity killed.");
|
|
449
|
+
console.log();
|
|
450
|
+
}
|
|
451
|
+
async function sendGracefulQuitMac(pid) {
|
|
452
|
+
// System Events quit and "tell application to quit" leave UnityLockfile behind,
|
|
453
|
+
// so we send Cmd+Q keystroke to trigger Unity's normal user-initiated shutdown flow
|
|
454
|
+
const script = [
|
|
455
|
+
'tell application "System Events"',
|
|
456
|
+
` set frontmost of (first process whose unix id is ${pid}) to true`,
|
|
457
|
+
' keystroke "q" using {command down}',
|
|
458
|
+
"end tell",
|
|
459
|
+
].join("\n");
|
|
460
|
+
try {
|
|
461
|
+
await execFileAsync("osascript", ["-e", script]);
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
// Process may have already exited
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function sendGracefulQuitWindows(pid) {
|
|
468
|
+
// process.kill(pid, "SIGTERM") forcefully kills on Windows, so use CloseMainWindow() to send WM_CLOSE
|
|
469
|
+
const scriptLines = [
|
|
470
|
+
"$ErrorActionPreference = 'Stop'",
|
|
471
|
+
`try { $proc = Get-Process -Id ${pid} -ErrorAction Stop; $proc.CloseMainWindow() | Out-Null } catch { }`,
|
|
472
|
+
];
|
|
473
|
+
try {
|
|
474
|
+
await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// Process may have already exited
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
async function sendGracefulQuit(pid) {
|
|
481
|
+
if (process.platform === "darwin") {
|
|
482
|
+
await sendGracefulQuitMac(pid);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (process.platform === "win32") {
|
|
486
|
+
await sendGracefulQuitWindows(pid);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
export async function quitRunningUnity(projectPath) {
|
|
491
|
+
const processInfo = await findRunningUnityProcess(projectPath);
|
|
492
|
+
if (!processInfo) {
|
|
493
|
+
console.log("No running Unity process found for this project.");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const pid = processInfo.pid;
|
|
497
|
+
console.log(`Quitting Unity (PID: ${pid})...`);
|
|
498
|
+
await sendGracefulQuit(pid);
|
|
499
|
+
console.log(`Sent graceful quit signal. Waiting up to ${GRACEFUL_QUIT_TIMEOUT_MS / 1000}s...`);
|
|
500
|
+
const exitedGracefully = await waitForProcessExit(pid, GRACEFUL_QUIT_TIMEOUT_MS);
|
|
501
|
+
if (exitedGracefully) {
|
|
502
|
+
console.log("Unity quit gracefully.");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
console.log("Unity did not respond to graceful quit. Force killing...");
|
|
506
|
+
killProcess(pid);
|
|
507
|
+
const exitedAfterKill = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
|
|
508
|
+
if (!exitedAfterKill) {
|
|
509
|
+
throw new Error(`Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1000}s.`);
|
|
510
|
+
}
|
|
511
|
+
console.log("Unity force killed.");
|
|
440
512
|
}
|
|
441
513
|
function hasBuildTargetArg(unityArgs) {
|
|
442
514
|
for (const arg of unityArgs) {
|
|
@@ -536,12 +608,12 @@ export function findUnityProjectBfs(rootDir, maxDepth) {
|
|
|
536
608
|
export async function launch(opts) {
|
|
537
609
|
const { projectPath, platform, unityArgs, unityVersion } = opts;
|
|
538
610
|
const unityPath = getUnityPath(unityVersion);
|
|
539
|
-
console.log(`Detected Unity version: ${unityVersion}`);
|
|
540
611
|
if (!existsSync(unityPath)) {
|
|
541
612
|
throw new Error(`Unity ${unityVersion} not found at ${unityPath}. Please install Unity through Unity Hub.`);
|
|
542
613
|
}
|
|
543
614
|
console.log("Opening Unity...");
|
|
544
615
|
console.log(`Project Path: ${projectPath}`);
|
|
616
|
+
console.log(`Detected Unity version: ${unityVersion}`);
|
|
545
617
|
const args = ["-projectPath", projectPath];
|
|
546
618
|
const unityArgsContainBuildTarget = hasBuildTargetArg(unityArgs);
|
|
547
619
|
if (platform && platform.length > 0 && !unityArgsContainBuildTarget) {
|
|
@@ -549,13 +621,40 @@ export async function launch(opts) {
|
|
|
549
621
|
}
|
|
550
622
|
const hubCliArgs = await getProjectCliArgs(projectPath);
|
|
551
623
|
if (hubCliArgs.length > 0) {
|
|
624
|
+
console.log("Unity Hub launch options:");
|
|
625
|
+
for (const line of groupCliArgs(hubCliArgs)) {
|
|
626
|
+
console.log(` ${line}`);
|
|
627
|
+
}
|
|
552
628
|
args.push(...hubCliArgs);
|
|
553
629
|
}
|
|
630
|
+
else {
|
|
631
|
+
console.log("Unity Hub launch options: none");
|
|
632
|
+
}
|
|
554
633
|
if (unityArgs.length > 0) {
|
|
555
634
|
args.push(...unityArgs);
|
|
556
635
|
}
|
|
557
|
-
|
|
558
|
-
|
|
636
|
+
return new Promise((resolve, reject) => {
|
|
637
|
+
const child = spawn(unityPath, args, {
|
|
638
|
+
stdio: "ignore",
|
|
639
|
+
detached: true,
|
|
640
|
+
// Git Bash (MSYS) がWindows パスをUnix 形式に自動変換するのを防ぐ
|
|
641
|
+
env: {
|
|
642
|
+
...process.env,
|
|
643
|
+
MSYS_NO_PATHCONV: "1",
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
const handleError = (error) => {
|
|
647
|
+
child.removeListener("spawn", handleSpawn);
|
|
648
|
+
reject(new Error(`Failed to launch Unity: ${error.message}`));
|
|
649
|
+
};
|
|
650
|
+
const handleSpawn = () => {
|
|
651
|
+
child.removeListener("error", handleError);
|
|
652
|
+
child.unref();
|
|
653
|
+
resolve();
|
|
654
|
+
};
|
|
655
|
+
child.once("error", handleError);
|
|
656
|
+
child.once("spawn", handleSpawn);
|
|
657
|
+
});
|
|
559
658
|
}
|
|
560
659
|
// Re-export Unity Hub functions
|
|
561
|
-
export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs };
|
|
660
|
+
export { ensureProjectEntryAndUpdate, updateLastModifiedIfExists, getProjectCliArgs, parseCliArgs, groupCliArgs };
|
package/dist/unityHub.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const ensureProjectEntryAndUpdate: (projectPath: string, version: string, when: Date, setFavorite?: boolean) => Promise<void>;
|
|
2
2
|
export declare const updateLastModifiedIfExists: (projectPath: string, when: Date) => Promise<void>;
|
|
3
3
|
export declare const parseCliArgs: (cliArgsString: string) => string[];
|
|
4
|
+
export declare const groupCliArgs: (args: readonly string[]) => string[];
|
|
4
5
|
export declare const getProjectCliArgs: (projectPath: string) => Promise<string[]>;
|
|
5
6
|
//# sourceMappingURL=unityHub.d.ts.map
|
package/dist/unityHub.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unityHub.d.ts","sourceRoot":"","sources":["../src/unityHub.ts"],"names":[],"mappings":"AAwFA,eAAO,MAAM,2BAA2B,GACtC,aAAa,MAAM,EACnB,SAAS,MAAM,EACf,MAAM,IAAI,EACV,qBAAmB,KAClB,OAAO,CAAC,IAAI,CAgEd,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,aAAa,MAAM,EACnB,MAAM,IAAI,KACT,OAAO,CAAC,IAAI,CAuDd,CAAC;AAoBF,eAAO,MAAM,YAAY,GAAI,eAAe,MAAM,KAAG,MAAM,EA6C1D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CA8C7E,CAAC"}
|
|
1
|
+
{"version":3,"file":"unityHub.d.ts","sourceRoot":"","sources":["../src/unityHub.ts"],"names":[],"mappings":"AAwFA,eAAO,MAAM,2BAA2B,GACtC,aAAa,MAAM,EACnB,SAAS,MAAM,EACf,MAAM,IAAI,EACV,qBAAmB,KAClB,OAAO,CAAC,IAAI,CAgEd,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,aAAa,MAAM,EACnB,MAAM,IAAI,KACT,OAAO,CAAC,IAAI,CAuDd,CAAC;AAoBF,eAAO,MAAM,YAAY,GAAI,eAAe,MAAM,KAAG,MAAM,EA6C1D,CAAC;AAGF,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAAM,EAiB5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CA8C7E,CAAC"}
|
package/dist/unityHub.js
CHANGED
|
@@ -225,6 +225,27 @@ export const parseCliArgs = (cliArgsString) => {
|
|
|
225
225
|
}
|
|
226
226
|
return tokens;
|
|
227
227
|
};
|
|
228
|
+
// "-flag value" pairs are grouped into single strings for display (e.g. ["-foo", "bar", "-baz", "qux"] -> ["-foo bar", "-baz qux"])
|
|
229
|
+
export const groupCliArgs = (args) => {
|
|
230
|
+
const groups = [];
|
|
231
|
+
let current = "";
|
|
232
|
+
for (const arg of args) {
|
|
233
|
+
if (arg.startsWith("-") && current.length > 0) {
|
|
234
|
+
groups.push(current);
|
|
235
|
+
current = arg;
|
|
236
|
+
}
|
|
237
|
+
else if (current.length === 0) {
|
|
238
|
+
current = arg;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
current += ` ${arg}`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (current.length > 0) {
|
|
245
|
+
groups.push(current);
|
|
246
|
+
}
|
|
247
|
+
return groups;
|
|
248
|
+
};
|
|
228
249
|
export const getProjectCliArgs = async (projectPath) => {
|
|
229
250
|
assert(projectPath !== null && projectPath !== undefined, "projectPath must not be null");
|
|
230
251
|
const infoFilePath = resolveUnityHubProjectsInfoFile();
|