diff --git a/GodotDebugSession/GodotDebugSession.csproj b/GodotDebugSession/GodotDebugSession.csproj index 1275f3b..fa8b0fc 100644 --- a/GodotDebugSession/GodotDebugSession.csproj +++ b/GodotDebugSession/GodotDebugSession.csproj @@ -1,4 +1,4 @@ - + @@ -79,7 +79,7 @@ - + diff --git a/package.json b/package.json index e976497..d57fcca 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "C# Tools for Godot", "description": "Debugger and utilities for working with Godot C# projects", "icon": "icon.png", - "version": "0.2.1", + "version": "0.3.0", "publisher": "neikeq", "license": "MIT", "repository": { @@ -36,32 +36,34 @@ "webpack-watch": "webpack --mode development --watch" }, "dependencies": { - "async-file": "^2.0.2", - "chokidar": "^3.4.0", - "fs-extra": "^10.0.0", - "jsonc-parser": "^3.0.0", - "lookpath": "^1.2.1", - "promise-socket": "^6.0.3", - "vscode-debugprotocol": "^1.40.0" + "async-file": "2.0.2", + "chokidar": "3.4.0", + "fs-extra": "10.0.0", + "jsonc-parser": "3.0.0", + "lookpath": "1.2.1", + "promise-socket": "6.0.3", + "semver": "^7.3.8", + "vscode-debugprotocol": "1.40.0" }, "extensionDependencies": [ "ms-dotnettools.csharp", "ms-vscode.mono-debug" ], "devDependencies": { - "@types/fs-extra": "^9.0.12", - "@types/glob": "^7.1.1", - "@types/mocha": "^5.2.6", - "@types/node": "^16.4.1", - "@types/vscode": "^1.62.0", - "glob": "^7.1.4", - "mocha": "^6.1.4", - "ts-loader": "^7.0.5", - "tslint": "^5.12.1", - "typescript": "^3.3.1", - "vsce": "^1.20.0", - "webpack": "^5.70.0", - "webpack-cli": "^4.9.2" + "@types/fs-extra": "9.0.12", + "@types/glob": "7.1.1", + "@types/mocha": "5.2.6", + "@types/node": "16.4.1", + "@types/semver": "^7.3.13", + "@types/vscode": "1.62.0", + "glob": "7.1.4", + "mocha": "6.1.4", + "ts-loader": "7.0.5", + "tslint": "5.12.1", + "typescript": "3.9.10", + "vsce": "1.20.0", + "webpack": "5.70.0", + "webpack-cli": "4.9.2" }, "breakpoints": [ { @@ -78,7 +80,18 @@ "godot.csharp.executablePath": { "type": "string", "default": null, - "description": "Path to the Godot engine executable." + "description": "Path to the Godot 3 engine executable.", + "deprecationMessage": "Use godot3ExecutablePath instead." + }, + "godot.csharp.godot3ExecutablePath": { + "type": "string", + "default": null, + "description": "Path to the Godot 3 engine executable." + }, + "godot.csharp.godot4ExecutablePath": { + "type": "string", + "default": null, + "description": "Path to the Godot 4 engine executable." } } }, @@ -105,7 +118,7 @@ "debuggers": [ { "type": "godot-mono", - "label": "C# Godot", + "label": "C# Godot 3", "languages": [ "csharp", "fsharp" @@ -122,8 +135,8 @@ }, "configurationSnippets": [ { - "label": "C# Godot: Play in Editor Configuration", - "description": "Launch a C# Godot App from the open editor with a debugger.", + "label": "C# Godot 3: Play in Editor Configuration", + "description": "Launch a C# Godot 3 App from the open editor with a debugger.", "body": { "name": "Play in Editor", "type": "godot-mono", @@ -132,8 +145,8 @@ } }, { - "label": "C# Godot: Launch Configuration", - "description": "Launch a C# Godot App with a debugger.", + "label": "C# Godot 3: Launch Configuration", + "description": "Launch a C# Godot 3 App with a debugger.", "body": { "name": "Launch", "type": "godot-mono", @@ -148,8 +161,8 @@ } }, { - "label": "C# Godot: Launch Configuration (Select Scene)", - "description": "Launch a C# Godot App with a debugger.", + "label": "C# Godot 3: Launch Configuration (Select Scene)", + "description": "Launch a C# Godot 3 App with a debugger.", "body": { "name": "Launch (Select Scene)", "type": "godot-mono", @@ -165,7 +178,7 @@ } }, { - "label": "C# Godot: Attach Configuration", + "label": "C# Godot 3: Attach Configuration", "description": "Attach a debugger to a C# Godot App.", "body": { "name": "Attach", @@ -226,6 +239,62 @@ } } } + }, + { + "type": "godot-dotnet", + "label": "C# Godot 4", + "languages": [ + "csharp", + "fsharp" + ], + "configurationSnippets": [ + { + "label": "C# Godot 4: Launch Configuration", + "description": "Launch a C# Godot 4 App with a debugger.", + "body": { + "name": "Launch", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${1:}", + "args": [ + "--path", + "${workspaceRoot}" + ], + "cwd": "${workspaceRoot}", + "stopAtEntry": false, + "console": "internalConsole" + } + }, + { + "label": "C# Godot 4: Launch Configuration (Select Scene)", + "description": "Launch a C# Godot 4 App with a debugger.", + "body": { + "name": "Launch (Select Scene)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${1:}", + "args": [ + "--path", + "${workspaceRoot}", + "${command:godot.csharp.getLaunchScene}" + ], + "cwd": "${workspaceRoot}", + "stopAtEntry": false, + "console": "internalConsole" + } + }, + { + "label": "C# Godot 4: Attach Configuration", + "description": "Attach a debugger to a C# Godot 4 App.", + "body": { + "name": "Attach", + "type": "coreclr", + "request": "attach" + } + } + ] } ] } diff --git a/src/assets-generator/assets-generator.ts b/src/assets-generator/assets-generator.ts index 3ca02c1..ad32734 100644 --- a/src/assets-generator/assets-generator.ts +++ b/src/assets-generator/assets-generator.ts @@ -28,12 +28,12 @@ export class AssetsGenerator { return new AssetsGenerator(vscodeFolder); } - public async addTasksJsonIfNecessary(): Promise { - return addTasksJsonIfNecessary(this.tasksJsonPath); + public async addTasksJsonIfNecessary(godotVersion: string): Promise { + return addTasksJsonIfNecessary(this.tasksJsonPath, godotVersion); } - public async addLaunchJsonIfNecessary(): Promise { - return addLaunchJsonIfNecessary(this.launchJsonPath); + public async addLaunchJsonIfNecessary(godotVersion: string): Promise { + return addLaunchJsonIfNecessary(this.launchJsonPath, godotVersion); } public async hasExistingAssets(): Promise { diff --git a/src/assets-generator/debug.ts b/src/assets-generator/debug.ts index bcae4aa..135f715 100644 --- a/src/assets-generator/debug.ts +++ b/src/assets-generator/debug.ts @@ -1,21 +1,22 @@ import * as vscode from 'vscode'; import * as jsonc from 'jsonc-parser'; import * as fs from 'fs-extra'; +import * as semver from 'semver'; import {getFormattingOptions, replaceCommentPropertiesWithComments, updateJsonWithComments} from '../json-utils'; -import {findGodotExecutablePath} from '../godot-utils'; +import {findGodotExecutablePath, GODOT_VERSION_3, GODOT_VERSION_4} from '../godot-utils'; -export function createLaunchConfiguration(godotExecutablePath: string | undefined): +export function createLaunchConfiguration(godotExecutablePath: string | undefined, godotVersion: string): {version: string, configurations: vscode.DebugConfiguration[]} { return { version: '2.0.0', - configurations: _createDebugConfigurations(godotExecutablePath), + configurations: _createDebugConfigurations(godotExecutablePath, godotVersion), }; } -export function createDebugConfigurationsArray(godotExecutablePath: string | undefined): vscode.DebugConfiguration[] +export function createDebugConfigurationsArray(godotExecutablePath: string | undefined, godotVersion: string): vscode.DebugConfiguration[] { - const configurations = _createDebugConfigurations(godotExecutablePath); + const configurations = _createDebugConfigurations(godotExecutablePath, godotVersion); // Remove comments configurations.forEach(configuration => { @@ -34,17 +35,27 @@ export function createDebugConfigurationsArray(godotExecutablePath: string | und return configurations; } -function _createDebugConfigurations(godotExecutablePath: string | undefined): vscode.DebugConfiguration[] -{ - return [ - createPlayInEditorDebugConfiguration(), - createLaunchDebugConfiguration(godotExecutablePath), - createLaunchDebugConfiguration(godotExecutablePath, true), - createAttachDebugConfiguration(), - ]; +function _createDebugConfigurations(godotExecutablePath: string | undefined, godotVersion: string): vscode.DebugConfiguration[] { + if (semver.intersects(godotVersion, GODOT_VERSION_3)) { + return [ + createPlayInEditorDebugConfigurationForGodot3(), + createLaunchDebugConfigurationForGodot3(godotExecutablePath), + createLaunchDebugConfigurationForGodot3(godotExecutablePath, true), + createAttachDebugConfigurationForGodot3(), + ]; + } else if (semver.intersects(godotVersion, GODOT_VERSION_4)) { + return [ + createLaunchDebugConfigurationForGodot4(godotExecutablePath), + createLaunchDebugConfigurationForGodot4(godotExecutablePath, true), + createAttachDebugConfigurationForGodot4(), + ]; + } else { + vscode.window.showErrorMessage('Cannot create C# Godot debug configurations. Godot version is unknown or unsupported.'); + return []; + } } -export function createPlayInEditorDebugConfiguration(): vscode.DebugConfiguration +export function createPlayInEditorDebugConfigurationForGodot3(): vscode.DebugConfiguration { return { name: 'Play in Editor', @@ -54,7 +65,7 @@ export function createPlayInEditorDebugConfiguration(): vscode.DebugConfiguratio }; } -export function createLaunchDebugConfiguration(godotExecutablePath: string | undefined, canSelectScene: boolean = false): vscode.DebugConfiguration +export function createLaunchDebugConfigurationForGodot3(godotExecutablePath: string | undefined, canSelectScene: boolean = false): vscode.DebugConfiguration { godotExecutablePath = godotExecutablePath ?? ''; return { @@ -67,14 +78,36 @@ export function createLaunchDebugConfiguration(godotExecutablePath: string | und 'OS-COMMENT1': 'See which arguments are available here:', 'OS-COMMENT2': 'https://docs.godotengine.org/en/stable/getting_started/editor/command_line_tutorial.html', executableArguments: [ + '--path', + '${workspaceRoot}', + ...(canSelectScene ? ['${command:godot.csharp.getLaunchScene}'] : []), + ], + }; +} + +export function createLaunchDebugConfigurationForGodot4(godotExecutablePath: string | undefined, canSelectScene: boolean = false): vscode.DebugConfiguration +{ + godotExecutablePath = godotExecutablePath ?? ''; + return { + name: `Launch${canSelectScene ? ' (Select Scene)' : ''}`, + type: 'coreclr', + request: 'launch', + preLaunchTask: 'build', + program: godotExecutablePath, + 'OS-COMMENT1': 'See which arguments are available here:', + 'OS-COMMENT2': 'https://docs.godotengine.org/en/stable/getting_started/editor/command_line_tutorial.html', + args: [ '--path', '${workspaceRoot}', ...(canSelectScene ? ['${command:SelectLaunchScene}'] : []), ], + cwd: '${workspaceRoot}', + stopAtEntry: false, + console: 'internalConsole', }; } -export function createAttachDebugConfiguration() +export function createAttachDebugConfigurationForGodot3() { return { name: 'Attach', @@ -85,10 +118,19 @@ export function createAttachDebugConfiguration() }; } -export async function addLaunchJsonIfNecessary(launchJsonPath: string): Promise +export function createAttachDebugConfigurationForGodot4() +{ + return { + name: 'Attach', + type: 'coreclr', + request: 'attach', + }; +} + +export async function addLaunchJsonIfNecessary(launchJsonPath: string, godotVersion: string): Promise { - const godotExecutablePath = await findGodotExecutablePath(); - const launchConfiguration = createLaunchConfiguration(godotExecutablePath); + const godotExecutablePath = await findGodotExecutablePath(godotVersion); + const launchConfiguration = createLaunchConfiguration(godotExecutablePath, godotVersion); const formattingOptions = getFormattingOptions(); diff --git a/src/assets-generator/tasks.ts b/src/assets-generator/tasks.ts index 617ad49..5fbb5b8 100644 --- a/src/assets-generator/tasks.ts +++ b/src/assets-generator/tasks.ts @@ -1,33 +1,62 @@ +import * as vscode from 'vscode'; import * as tasks from 'vscode-tasks'; import * as jsonc from 'jsonc-parser'; import * as fs from 'fs-extra'; +import * as semver from 'semver'; import {getFormattingOptions, replaceCommentPropertiesWithComments, updateJsonWithComments} from '../json-utils'; -import {findGodotExecutablePath} from '../godot-utils'; +import {findGodotExecutablePath, GODOT_VERSION_3, GODOT_VERSION_4} from '../godot-utils'; -export function createTasksConfiguration(godotExecutablePath: string | undefined): tasks.TaskConfiguration +export function createTasksConfiguration(godotExecutablePath: string | undefined, godotVersion: string): tasks.TaskConfiguration { return { version: '2.0.0', - tasks: [createBuildTaskDescription(godotExecutablePath)], + tasks: _createTasksConfiguration(godotExecutablePath, godotVersion), }; } -export function createBuildTaskDescription(godotExecutablePath: string | undefined): tasks.TaskDescription +function _createTasksConfiguration(godotExecutablePath: string | undefined, godotVersion: string) : tasks.TaskDescription[] { + if (semver.intersects(godotVersion, GODOT_VERSION_3)) { + return [ + createBuildTaskDescriptionForGodot3(godotExecutablePath), + ]; + } else if (semver.intersects(godotVersion, GODOT_VERSION_4)) { + return [ + createBuildTaskDescriptionForGodot4(godotExecutablePath), + ]; + } else { + vscode.window.showErrorMessage('Cannot create C# Godot tasks configurations. Godot version is unknown or unsupported.'); + return []; + } +} + +export function createBuildTaskDescriptionForGodot3(godotExecutablePath: string | undefined): tasks.TaskDescription +{ + godotExecutablePath = godotExecutablePath ?? ''; + return { + label: 'build', + command: godotExecutablePath, + type: 'process', + args: ['--build-solutions', '--path', '${workspaceRoot}', '--no-window', '--quit'], + problemMatcher: '$msCompile', + }; +} + +export function createBuildTaskDescriptionForGodot4(godotExecutablePath: string | undefined): tasks.TaskDescription { godotExecutablePath = godotExecutablePath ?? ''; return { label: 'build', command: godotExecutablePath, type: 'process', - args: ['--build-solutions', '--path', '${workspaceRoot}', '--no-window', '-q'], + args: ['--build-solutions', '--path', '${workspaceRoot}', '--headless', '--quit'], problemMatcher: '$msCompile', }; } -export async function addTasksJsonIfNecessary(tasksJsonPath: string): Promise +export async function addTasksJsonIfNecessary(tasksJsonPath: string, godotVersion: string): Promise { - const godotExecutablePath = await findGodotExecutablePath(); - const tasksConfiguration = createTasksConfiguration(godotExecutablePath); + const godotExecutablePath = await findGodotExecutablePath(godotVersion); + const tasksConfiguration = createTasksConfiguration(godotExecutablePath, godotVersion); const formattingOptions = getFormattingOptions(); diff --git a/src/assets-provider.ts b/src/assets-provider.ts index 408df10..41600e5 100644 --- a/src/assets-provider.ts +++ b/src/assets-provider.ts @@ -2,8 +2,9 @@ import * as vscode from 'vscode'; import * as fs from 'fs-extra'; import {getVscodeFolder} from './vscode-utils'; import {AssetsGenerator} from './assets-generator'; +import {determineGodotVersion} from './godot-utils'; -export async function addAssets(): Promise +export async function addAssets(godotProjectDir: string | undefined = undefined): Promise { const vscodeFolder = getVscodeFolder(); if (!vscodeFolder) @@ -12,6 +13,12 @@ export async function addAssets(): Promise return; } + const godotVersion = await determineGodotVersion(godotProjectDir); + if (!godotVersion) { + vscode.window.showErrorMessage('Cannot create C# Godot debug configurations. Godot version is unknown or unsupported.'); + return; + } + const generator = AssetsGenerator.Create(vscodeFolder); const doGenerateAssets = await shouldGenerateAssets(generator); @@ -24,8 +31,8 @@ export async function addAssets(): Promise await fs.ensureDir(vscodeFolder); const promises = [ - generator.addTasksJsonIfNecessary(), - generator.addLaunchJsonIfNecessary(), + generator.addTasksJsonIfNecessary(godotVersion), + generator.addLaunchJsonIfNecessary(godotVersion), ]; await Promise.all(promises); diff --git a/src/configuration.ts b/src/configuration.ts index 5a48cc7..feb08f4 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -21,7 +21,8 @@ const DEFAULT_EXCEPTIONS: ExceptionConfigurations = { export class Configuration { public static Value: Configuration = new Configuration(); - public godotExecutablePath: string | undefined; + public godot3ExecutablePath: string | undefined; + public godot4ExecutablePath: string | undefined; public exceptionOptions: ExceptionConfigurations = DEFAULT_EXCEPTIONS; @@ -45,7 +46,8 @@ export class Configuration { // Too lazy so we're re-using mono-debug extension settings for now... const monoConfiguration = vscode.workspace.getConfiguration('mono-debug'); - this.godotExecutablePath = godotConfiguration.get('executablePath'); + this.godot3ExecutablePath = godotConfiguration.get('godot3ExecutablePath') || godotConfiguration.get('executablePath'); + this.godot4ExecutablePath = godotConfiguration.get('godot4ExecutablePath'); this.exceptionOptions = monoConfiguration.get('exceptionOptions', DEFAULT_EXCEPTIONS); } diff --git a/src/debug-provider.ts b/src/debug-provider.ts index 0cb8820..f8f05f2 100644 --- a/src/debug-provider.ts +++ b/src/debug-provider.ts @@ -3,7 +3,7 @@ import * as fs from 'fs-extra'; import {getVscodeFolder} from './vscode-utils'; import {Configuration} from './configuration'; import {AssetsGenerator, createDebugConfigurationsArray} from './assets-generator'; -import {findGodotExecutablePath} from './godot-utils'; +import {findGodotExecutablePath, determineGodotVersion} from './godot-utils'; export class GodotMonoDebugConfigProvider implements vscode.DebugConfigurationProvider { private godotProjectPath: string; @@ -24,16 +24,22 @@ export class GodotMonoDebugConfigProvider implements vscode.DebugConfigurationPr return []; } + const godotVersion = await determineGodotVersion(folder); + if (!godotVersion) { + vscode.window.showErrorMessage('Cannot create C# Godot debug configurations. Godot version is unknown or unsupported.'); + return []; + } + const generator = AssetsGenerator.Create(vscodeFolder); // Make sure .vscode folder exists, addTasksJsonIfNecessary will fail to create tasks.json if the folder does not exist. await fs.ensureDir(vscodeFolder); // Add a tasks.json - await generator.addTasksJsonIfNecessary(); + await generator.addTasksJsonIfNecessary(godotVersion); - const godotPath = await findGodotExecutablePath(); - return createDebugConfigurationsArray(godotPath); + const godotPath = await findGodotExecutablePath(godotVersion); + return createDebugConfigurationsArray(godotPath, godotVersion); } public async resolveDebugConfiguration( diff --git a/src/extension.ts b/src/extension.ts index cca9916..644a911 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -4,7 +4,7 @@ import * as completion_provider from './completion-provider'; import * as debug_provider from './debug-provider'; import * as assets_provider from './assets-provider'; import { getWorkspaceScenes } from './workspace-utils'; -import { fixPathForGodot } from './godot-utils'; +import { fixPathForGodot, determineGodotVersion, GODOT_VERSION_3 } from './godot-utils'; import { findProjectFiles, ProjectLocation, promptForProject } from './project-select'; export let client: Client; @@ -106,25 +106,25 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand(commandId, async () => { const project = await promptForProject(); // project.godot if (project !== undefined) { - setupProject(project, context); + await setupProject(project, context); } })); // One project.godot files found. Use it. if (foundProjects.length === 1) { - setupProject(foundProjects[0], context); + await setupProject(foundProjects[0], context); } // Multiple project.godot files found. Prompt the user for which one they want to use. else { const project = await promptForProject(); if (project !== undefined) { - setupProject(project, context); + await setupProject(project, context); } } // Setup generate assets command const generateAssetsCommand = vscode.commands.registerCommand('godot.csharp.generateAssets', async () => { - await assets_provider.addAssets(); + await assets_provider.addAssets(client.getGodotProjectDir()); }); context.subscriptions.push(generateAssetsCommand); @@ -135,16 +135,22 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(getLaunchSceneCommand); } -function setupProject(project: ProjectLocation, context: vscode.ExtensionContext) { +async function setupProject(project: ProjectLocation, context: vscode.ExtensionContext) { const statusBarPath: string = project.relativeProjectPath === '.' ? './' : project.relativeProjectPath; statusBarItem.text = `$(folder) Godot Project: ${statusBarPath}`; // Setup client if (client !== undefined) { client.dispose(); } + let godotVersion = await determineGodotVersion(project.absoluteProjectPath); + if (!godotVersion) { + // Fallback to Godot 3 + godotVersion = GODOT_VERSION_3; + } client = new Client( 'VisualStudioCode', fixPathForGodot(project.absoluteProjectPath), + godotVersion, new MessageHandler(), new Logger(), ); diff --git a/src/godot-tools-messaging/client.ts b/src/godot-tools-messaging/client.ts index 62e6f09..8b73512 100644 --- a/src/godot-tools-messaging/client.ts +++ b/src/godot-tools-messaging/client.ts @@ -2,8 +2,10 @@ import * as net from 'net'; import * as fs from 'fs'; import * as path from 'path'; import * as chokidar from 'chokidar'; +import * as semver from 'semver'; import PromiseSocket from 'promise-socket'; import { Disposable } from 'vscode'; +import {GODOT_VERSION_3, GODOT_VERSION_4} from '../godot-utils'; async function timeout(ms: number) { return new Promise(resolve => { @@ -207,6 +209,7 @@ export class Client implements Disposable { identity: string; projectDir: string; projectMetadataDir: string; + godotVersion: string; metaFilePath: string; messageHandler: IMessageHandler; logger: ILogger; @@ -219,13 +222,18 @@ export class Client implements Disposable { peer?: Peer; - constructor(identity: string, godotProjectDir: string, messageHandler: IMessageHandler, logger: ILogger) { + constructor(identity: string, godotProjectDir: string, godotVersion: string, messageHandler: IMessageHandler, logger: ILogger) { this.identity = identity; this.messageHandler = messageHandler; this.logger = logger; this.projectDir = godotProjectDir; - this.projectMetadataDir = path.join(godotProjectDir, '.mono', 'metadata'); + this.godotVersion = godotVersion; + if (semver.intersects(godotVersion, GODOT_VERSION_3)) { + this.projectMetadataDir = path.join(godotProjectDir, '.mono', 'metadata'); + } else { // GODOT_VERSION_4+ + this.projectMetadataDir = path.join(godotProjectDir, '.godot', 'mono', 'metadata'); + } this.metaFilePath = path.join(this.projectMetadataDir, GodotIdeMetadata.defaultFileName); } diff --git a/src/godot-utils.ts b/src/godot-utils.ts index 5ff5937..d38458e 100644 --- a/src/godot-utils.ts +++ b/src/godot-utils.ts @@ -1,6 +1,69 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as readline from 'readline'; +import * as path from 'path'; +import * as semver from 'semver'; import {lookpath} from 'lookpath'; import {Configuration} from './configuration'; import {client} from './extension'; +import {findProjectFiles} from './project-select'; + +export const GODOT_VERSION_3 = '3.*.*'; +export const GODOT_VERSION_4 = '4.*.*'; + +export async function determineGodotVersion(folder: vscode.WorkspaceFolder | string | undefined): Promise { + if (folder) { + // Try to determine Godot version from the project folder + const folderPath = typeof folder === 'string' ? folder : folder.uri.fsPath; + const projectFile = path.join(folderPath, 'project.godot'); + const godotVersion = await determineGodotVersionFromProjectFile(projectFile); + if (godotVersion) { + return godotVersion; + } + } + + // A project folder was not provided or we couldn't determine the version from it. + // Try to find all the project.godot files in the workspace. + const projectFiles = await findProjectFiles(); + if (projectFiles.length === 1) { + // We found exactly one project.godot file so we can assume this is the user's project + const godotVersion = await determineGodotVersionFromProjectFile(projectFiles[0].absoluteFilePath); + if (godotVersion) { + return godotVersion; + } + } + + // We could not determine the Godot version + return undefined; +} + +async function determineGodotVersionFromProjectFile(projectFilePath: string): Promise { + if (!fs.existsSync(projectFilePath)) { + return undefined; + } + + const projectFileStream = await fs.createReadStream(projectFilePath); + const rl = readline.createInterface({ + input: projectFileStream, + crlfDelay: Infinity, + }); + + for await (const line of rl) { + if (line.startsWith('config_version=')) { + const configVersion = line.substr('config_version='.length); + switch (configVersion) { + case '4': + return GODOT_VERSION_3; + case '5': + return GODOT_VERSION_4; + default: + return undefined; + } + } + } + + return undefined; +} export function fixPathForGodot(path: string): string { if (process.platform === "win32") { @@ -17,12 +80,15 @@ export function fixPathForGodot(path: string): string { return path; } -export async function findGodotExecutablePath(): Promise -{ +export async function findGodotExecutablePath(godotVersion: string): Promise { let path: string | undefined; // If the user has set the path in the settings, use that value - path = Configuration.Value.godotExecutablePath; + if (semver.intersects(godotVersion, GODOT_VERSION_3)) { + path = Configuration.Value.godot3ExecutablePath; + } else if (semver.intersects(godotVersion, GODOT_VERSION_4)) { + path = Configuration.Value.godot4ExecutablePath; + } if (path) { return path; }