Skip to content

Commit 3d65bf8

Browse files
authored
Merge pull request #20 from NoTaskStudios/feature/clone-from-template
Feature/clone from template
2 parents a43c943 + 6c75338 commit 3d65bf8

File tree

6 files changed

+323
-89
lines changed

6 files changed

+323
-89
lines changed

src/configs/unityConfig.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import os from "os";
2+
import path from "path";
3+
4+
/**
5+
* Interface for Unity Hub paths
6+
*/
7+
interface UnityHubPaths {
8+
/** Path to the Unity Hub executable */
9+
hubDir: string;
10+
11+
/** Path to the Unity Hub projects list file */
12+
projects: string;
13+
14+
/** Path to the Unity Hub default projects directory */
15+
projectDir: string;
16+
}
17+
18+
/**
19+
* Interface for Unity Editor paths
20+
*/
21+
interface UnityEditorPaths {
22+
/** Base path to the Unity Editor installation */
23+
base: string;
24+
25+
/** Path to the Unity Editor executable */
26+
executable: string;
27+
28+
/** Path to the Unity Editor project templates */
29+
projectTemplates: string;
30+
}
31+
32+
interface UnityTemplatePaths {
33+
/** Path to the Unity project templates */
34+
projectTemplates: string;
35+
}
36+
37+
/**
38+
* Interface for platform-specific Unity configuration
39+
*/
40+
interface PlatformConfig {
41+
/** The path to the Unity Editor executable */
42+
editor: UnityEditorPaths;
43+
44+
/** The path to the Unity Hub executable */
45+
hub: UnityHubPaths;
46+
47+
/** The path to the Unity project templates */
48+
templates: UnityTemplatePaths;
49+
50+
/** The platform name (e.g., "win32", "darwin", "linux") */
51+
platform: string;
52+
53+
/** CPU architecture of the Unity editor (e.g., "x86_64") */
54+
architecture: string;
55+
}
56+
57+
/**
58+
* Platform-specific paths for Unity Hub components
59+
*/
60+
const UNITY_HUB_PATHS: Record<string, UnityHubPaths> = {
61+
win32: {
62+
hubDir: "C:\\Program Files\\Unity Hub\\Unity Hub.exe",
63+
projects: path.join(os.homedir(), "AppData", "Roaming", "UnityHub", "projects-v1.json"),
64+
projectDir: path.join(os.homedir(), "AppData", "Roaming", "UnityHub", "projectDir.json"),
65+
},
66+
darwin: {
67+
hubDir: "/Applications/Unity Hub.app/Contents/MacOS/Unity Hub",
68+
projects: path.join(os.homedir(), "Library", "Application Support", "UnityHub", "projects-v1.json"),
69+
projectDir: path.join(os.homedir(), "Library", "Application Support", "UnityHub", "projectDir.json"),
70+
},
71+
linux: {
72+
hubDir: "/opt/UnityHub/UnityHub",
73+
projects: path.join(os.homedir(), ".config", "UnityHub", "projects-v1.json"),
74+
projectDir: path.join(os.homedir(), ".config", "UnityHub", "projectDir.json"),
75+
},
76+
};
77+
78+
/**
79+
* Configuration paths for Unity Editor executables across different operating systems.
80+
* The structure provides base installation directories and relative paths to the
81+
* executable for each supported platform (Windows, macOS, Linux).
82+
*/
83+
const UNITY_EDITOR_PATHS: Record<string, UnityEditorPaths> = {
84+
win32: {
85+
base: "C:/Program Files/Unity/Hub/Editor",
86+
executable: "Editor/Unity.exe",
87+
projectTemplates: "Editor/Data/Resources/PackageManager/ProjectTemplates",
88+
},
89+
darwin: {
90+
base: "/Applications/Unity/Hub/Editor",
91+
executable: "Unity.app/Contents/MacOS/Unity",
92+
projectTemplates: "Unity.app/Contents/Resources/PackageManager/ProjectTemplates",
93+
},
94+
linux: {
95+
base: "/opt/unity/editor",
96+
executable: "Editor/Unity",
97+
projectTemplates: "Editor/Data/Resources/PackageManager/ProjectTemplates",
98+
},
99+
};
100+
101+
class UnityConfig {
102+
/**
103+
* Gets the current platform configuration
104+
* @returns The platform-specific Unity paths configuration
105+
*/
106+
public static getPlatformConfig(): PlatformConfig {
107+
const platform = process.platform;
108+
const unityHubPaths = UNITY_HUB_PATHS[platform];
109+
const unityEditorPaths = UNITY_EDITOR_PATHS[platform];
110+
111+
const settings: PlatformConfig = {
112+
editor: {
113+
base: environment.unityEditorPath ?? unityEditorPaths.base,
114+
executable: unityEditorPaths.executable,
115+
projectTemplates: unityEditorPaths.projectTemplates,
116+
},
117+
hub: {
118+
hubDir: environment.unityHubPath ?? unityHubPaths.hubDir,
119+
projects: unityHubPaths.projects,
120+
projectDir: unityHubPaths.projectDir,
121+
},
122+
templates: {
123+
projectTemplates: environment.unityProjectTemplatePath ?? unityEditorPaths.projectTemplates,
124+
},
125+
platform,
126+
architecture: os.arch(),
127+
};
128+
129+
return settings;
130+
}
131+
}
132+
133+
const environment = {
134+
unityHubPath: process.env.UNITY_HUB_PATH,
135+
unityEditorPath: process.env.UNITY_EDITOR_PATH,
136+
unityProjectPath: process.env.UNITY_PROJECT_PATH,
137+
unityProjectTemplatePath: process.env.UNITY_PROJECT_TEMPLATE_PATH,
138+
};
139+
140+
export { UnityConfig };
141+
export type { UnityHubPaths, UnityEditorPaths, UnityTemplatePaths, PlatformConfig };

src/events/hubEventEmitter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@ export class UnityHubInstallerEvent extends EventEmitter implements InstallerEmi
2424

2525
public get completed(): Promise<InstallerEvent[]> {
2626
return new Promise((resolve, reject) => {
27-
const onComplete = (events: InstallerEvent[]) => {
27+
const onComplete = (events: InstallerEvent[]): void => {
2828
cleanup();
2929
resolve(events);
3030
};
31-
const onError = (error: any) => {
31+
const onError = (error: any): void => {
3232
cleanup();
3333
reject(error);
3434
};
35-
const onCancel = () => {
35+
const onCancel = (): void => {
3636
cleanup();
3737
reject(new Error("Cancelled"));
3838
};
3939

40-
const cleanup = () => {
40+
const cleanup = (): void => {
4141
this.off(InstallerEventType.Completed, onComplete);
4242
this.off(InstallerEventType.Error, onError);
4343
this.off(InstallerEventType.Cancelled, onCancel);

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
// Exporting Classes
12
export { default as UnityHub } from "./unityHub.ts";
23
export { default as UnityEditor } from "./unityEditor.ts";
4+
export { default as UnityTemplates } from "./unityTemplates.ts";
5+
6+
// Exporting Events & Emitters
37
export { UnityHubInstallerEvent } from "./events/hubEventEmitter.ts";
48

9+
// Exporting Configurations
10+
export { UnityConfig } from "./configs/unityConfig.ts";
11+
12+
// Exporting Types
513
export * from "./types/unity.ts";

src/unityEditor.ts

Lines changed: 100 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import os from "os";
21
import fs from "fs-extra";
32
import path from "path";
43
import { ProjectInfo, TestMode, UnityBuildTarget, UnityEditorInfo } from "./types/unity.js";
54
import { CommandOptions, CommandResult, executeCommand } from "./utils/commandExecutor.js";
65
import { redactSensitiveArgs } from "./utils/security.js";
6+
import { UnityConfig, UnityEditorPaths } from "./configs/unityConfig.ts";
77

88
/**
99
* UnityEditor class provides a comprehensive interface for interacting with the Unity game engine editor
@@ -14,30 +14,10 @@ import { redactSensitiveArgs } from "./utils/security.js";
1414
*/
1515
class UnityEditor {
1616
/**
17-
* Configuration paths for Unity Editor executables across different operating systems.
18-
* The structure provides base installation directories and relative paths to the
19-
* executable for each supported platform (Windows, macOS, Linux).
20-
*
21-
* @private
22-
* @static
23-
* @type {Object<string, {base: string, executable: string}>}
24-
*
17+
* Platform-specific configuration for Unity Editor paths.
2518
* @internal
2619
*/
27-
private static UNITY_PATHS = {
28-
win32: {
29-
base: "C:/Program Files/Unity/Hub/Editor",
30-
executable: "Editor/Unity.exe",
31-
},
32-
darwin: {
33-
base: "/Applications/Unity/Hub/Editor",
34-
executable: "Unity.app/Contents/MacOS/Unity",
35-
},
36-
linux: {
37-
base: "/opt/unity/editor",
38-
executable: "Editor/Unity",
39-
},
40-
};
20+
private static unityConfig: UnityEditorPaths = UnityConfig.getPlatformConfig().editor;
4121

4222
/**
4323
* Resolves the platform-specific path to the Unity executable for a given version.
@@ -54,10 +34,23 @@ class UnityEditor {
5434
* const unityPath = UnityEditor.getUnityExecutablePath("2022.3.15f1");
5535
*/
5636
public static getUnityExecutablePath(version: string): string {
57-
const platform = os.platform() as keyof typeof UnityEditor.UNITY_PATHS;
58-
const unityConfig = UnityEditor.UNITY_PATHS[platform];
37+
const unityPath = path.join(this.unityConfig.base, version, this.unityConfig.executable);
38+
return unityPath;
39+
}
5940

60-
const unityPath = path.join(unityConfig.base, version, unityConfig.executable);
41+
/**
42+
* Resolves the platform-specific path to the Unity templates directory for a given version.
43+
* This function detects the current operating system and combines the appropriate
44+
* base path with the version-specific subdirectory and templates location.
45+
*
46+
* @public
47+
* @static
48+
* @param {string} version - Unity editor version in the format "YYYY.N.XfN" (e.g., "2023.3.0f1")
49+
* @returns {string} Absolute path to the Unity templates directory for the specified version
50+
* @throws {Error} If the current platform is not supported (not win32, darwin, or linux)
51+
*/
52+
public static getUnityTemplatesPath(version: string): string {
53+
const unityPath = path.join(this.unityConfig.base, version, this.unityConfig.projectTemplates);
6154
return unityPath;
6255
}
6356

@@ -518,8 +511,9 @@ class UnityEditor {
518511
* @param {ProjectInfo} projectInfo - Information about the project to create, including:
519512
* - path: Where to create the project
520513
* - editorVersion: Which Unity version to use
521-
* @param {boolean} [waitForExit=true] - Whether to wait for Unity to exit after creating the project
514+
* @param {boolean} [quit=false] - Whether to wait for Unity to exit after creating the project
522515
* Set to false to keep Unity open after project creation
516+
* @param {boolean} [useHub=true] - Whether to use Unity Hub for creating the project
523517
* @returns {Promise<boolean>} - Promise resolving to true if project creation was successful, false otherwise
524518
* @example
525519
* // Create a new project using Unity 2022.3.15f1
@@ -536,18 +530,21 @@ class UnityEditor {
536530
* console.error("Project creation failed");
537531
* }
538532
*/
539-
public static async createProject(projectInfo: ProjectInfo, waitForExit: boolean = true): Promise<boolean> {
533+
public static async createProject(
534+
projectInfo: ProjectInfo,
535+
quit: boolean = false,
536+
useHub: boolean = true
537+
): Promise<boolean> {
540538
try {
541539
console.debug(`Creating new project at ${projectInfo.projectPath}`);
542540

543541
const parentDir = path.dirname(projectInfo.projectPath);
544542
await fs.ensureDir(parentDir);
545543

546544
const args = ["-createProject", projectInfo.projectPath];
545+
if (useHub) args.push("-useHub", "-hubIPC");
547546

548-
if (waitForExit) {
549-
args.push("-quit");
550-
}
547+
if (quit) args.push("-quit");
551548

552549
const editorInfo = { version: projectInfo.editorVersion };
553550
const { stdout, stderr } = await this.execUnityEditorCommand(editorInfo, args, {
@@ -570,6 +567,75 @@ class UnityEditor {
570567
}
571568
}
572569

570+
/**
571+
* Creates a new Unity project from a specified template.
572+
* This function initializes a Unity project using an existing template,
573+
* allowing for rapid project setup with predefined assets and settings.
574+
*
575+
* @public
576+
* @static
577+
* @param {ProjectInfo} projectInfo - Information about the project to create, including:
578+
* - path: Where to create the project
579+
* - editorVersion: Which Unity version to use
580+
* @param {string} templatePath - The path to the template to use for the project, you can get templates path with unityTemplates.ts
581+
* @param {boolean} [quit=false] - Whether to wait for Unity to exit after creating the project
582+
* Set to false to keep Unity open after project creation
583+
* @param {boolean} [useHub=true] - Whether to use Unity Hub for creating the project
584+
* @returns {Promise<boolean>} - Promise resolving to true if project creation was successful, false otherwise
585+
* @example
586+
*
587+
* const success = await UnityEditor.createProjectFromTemplate(
588+
* {
589+
* path: "/path/to/new/MyAwesomeGame",
590+
* editorVersion: "2022.3.15f1"
591+
* },
592+
* "/path/to/template"
593+
* );
594+
*
595+
* if (success) {
596+
* console.log("Project created from template successfully");
597+
* } else {
598+
* console.error("Project creation from template failed");
599+
* }
600+
*/
601+
public static async createProjectFromTemplate(
602+
projectInfo: ProjectInfo,
603+
templatePath: string,
604+
quit: boolean = false,
605+
useHub: boolean = true
606+
): Promise<boolean> {
607+
try {
608+
console.debug(`Creating new project from template at ${projectInfo.projectPath}`);
609+
610+
const parentDir = path.dirname(projectInfo.projectPath);
611+
await fs.ensureDir(parentDir);
612+
613+
const args = ["-createProject", projectInfo.projectPath, "-cloneFromTemplate", templatePath];
614+
615+
if (quit) args.push("-quit");
616+
if (useHub) args.push("-useHub", "-hubIPC");
617+
618+
const editorInfo = { version: projectInfo.editorVersion };
619+
const { stdout, stderr } = await this.execUnityEditorCommand(editorInfo, args, {
620+
reject: false,
621+
});
622+
623+
const creationSuccessful =
624+
!stdout.includes("Failed to create project") && !stderr.includes("Failed to create project");
625+
626+
if (creationSuccessful) {
627+
console.debug(`Successfully created project from template at ${projectInfo.projectPath}`);
628+
return true;
629+
} else {
630+
console.error(`Failed to create project from template: ${stderr || stdout}`);
631+
return false;
632+
}
633+
} catch (error) {
634+
console.error("Error creating project from template:", error);
635+
return false;
636+
}
637+
}
638+
573639
/**
574640
* Opens an existing Unity project with the specified editor version.
575641
* This function can launch Unity with various options, either in batch mode
@@ -610,17 +676,9 @@ class UnityEditor {
610676

611677
const args = ["-projectPath", projectInfo.projectPath];
612678

613-
if (waitForExit) {
614-
args.push("-quit");
615-
}
616-
617-
if (batchmode) {
618-
args.push("-batchmode");
619-
}
620-
621-
if (useHub) {
622-
args.push(...["-useHub", "-hubIPC"]);
623-
}
679+
if (waitForExit) args.push("-quit");
680+
if (batchmode) args.push("-batchmode");
681+
if (useHub) args.push(...["-useHub", "-hubIPC"]);
624682

625683
const editorInfo = { version: projectInfo.editorVersion };
626684
const options = { reject: false };

0 commit comments

Comments
 (0)