summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py4
-rw-r--r--modules/mono/build_scripts/mono_configure.py11
-rw-r--r--modules/mono/build_scripts/solution_builder.py4
-rw-r--r--modules/mono/config.py1
-rw-r--r--modules/mono/doc_classes/@C#.xml13
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml5
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs48
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs270
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs15
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildTab.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs37
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs19
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs48
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj1
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp82
-rw-r--r--modules/mono/glue/scene_tree_glue.h50
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp4
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp1
38 files changed, 676 insertions, 106 deletions
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 7391e8790d..3bbbf29d3b 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -15,7 +15,9 @@ def build_godot_tools(source, target, env):
from .solution_builder import build_solution
- build_solution(env, solution_path, build_config)
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
# No need to copy targets. The GodotTools csproj takes care of copying them.
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 23f01b3cca..80e3b59325 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -191,17 +191,16 @@ def configure(env, env_mono):
env.Append(LIBS=["psapi"])
env.Append(LIBS=["version"])
else:
- mono_lib_name = find_name_in_dir_files(
- mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes
- )
+ mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
- if not mono_lib_name:
+ if not mono_lib_file:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + ".lib")
+ env.Append(LINKFLAGS=mono_lib_file)
else:
- env.Append(LIBS=[mono_lib_name])
+ mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
+ env.Append(LINKFLAGS=mono_lib_file_path)
mono_bin_path = os.path.join(mono_root, "bin")
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
index 371819fd72..03f4e57f02 100644
--- a/modules/mono/build_scripts/solution_builder.py
+++ b/modules/mono/build_scripts/solution_builder.py
@@ -142,9 +142,7 @@ def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
# Build solution
- targets = ["Restore", "Build"]
-
- msbuild_args += [solution_path, "/t:%s" % ",".join(targets), "/p:Configuration=" + build_config]
+ msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config]
msbuild_args += extra_msbuild_args
run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 7980a86cb3..cd659057ef 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -57,7 +57,6 @@ def configure(env):
def get_doc_classes():
return [
- "@C#",
"CSharpScript",
"GodotSharp",
]
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
deleted file mode 100644
index 83a7fbf02c..0000000000
--- a/modules/mono/doc_classes/@C#.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 1eb3404f9e..e1e9d1381f 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSharpScript" inherits="Script" version="4.0">
<brief_description>
+ A script implemented in the C# programming language (Mono-enabled builds only).
</brief_description>
<description>
+ This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds.
+ See also [GodotSharp].
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
<return type="Variant">
</return>
<description>
+ Returns a new instance of the script.
</description>
</method>
</methods>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 19a08d2036..417f8ac704 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GodotSharp" inherits="Object" version="4.0">
<brief_description>
+ Bridge between Godot and the Mono runtime (Mono-enabled builds only).
</brief_description>
<description>
+ This class is a bridge between Godot and the Mono runtime. It exposes several low-level operations and is only available in Mono-enabled Godot builds.
+ See also [CSharpScript].
</description>
<tutorials>
</tutorials>
@@ -11,26 +14,30 @@
<return type="void">
</return>
<description>
- Attaches the current thread to the mono runtime.
+ Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
<return type="void">
</return>
<description>
- Detaches the current thread from the mono runtime.
+ Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
<return type="int">
</return>
<description>
+ Returns the current MonoDomain ID.
+ [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="get_scripts_domain_id">
<return type="int">
</return>
<description>
+ Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
+ [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="is_domain_finalizing_for_unload">
@@ -39,26 +46,28 @@
<argument index="0" name="domain_id" type="int">
</argument>
<description>
- Returns whether the domain is being finalized.
+ Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_shutting_down">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
</description>
</method>
<method name="is_scripts_domain_loaded">
<return type="bool">
</return>
<description>
- Returns whether the scripts domain is loaded.
+ Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
</description>
</method>
</methods>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 7ab5c5fc59..012b69032e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -25,8 +25,9 @@ namespace GodotTools.Core
bool rooted = path.IsAbsolutePath();
path = path.Replace('\\', '/');
+ path = path[path.Length - 1] == '/' ? path.Substring(0, path.Length - 1) : path;
- string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index d069651dd3..572c541412 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -19,9 +19,12 @@ namespace GodotTools.IdeMessaging
private readonly string identity;
private string MetaFilePath { get; }
+ private DateTime? metaFileModifiedTime;
private GodotIdeMetadata godotIdeMetadata;
private readonly FileSystemWatcher fsWatcher;
+ public string GodotEditorExecutablePath => godotIdeMetadata.EditorExecutablePath;
+
private readonly IMessageHandler messageHandler;
private Peer peer;
@@ -123,7 +126,7 @@ namespace GodotTools.IdeMessaging
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
// FileSystemWatcher requires an existing directory
- if (!File.Exists(projectMetadataDir))
+ if (!Directory.Exists(projectMetadataDir))
Directory.CreateDirectory(projectMetadataDir);
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
@@ -142,6 +145,13 @@ namespace GodotTools.IdeMessaging
if (!File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null && metadata != godotIdeMetadata)
@@ -173,6 +183,13 @@ namespace GodotTools.IdeMessaging
if (IsConnected || !File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null)
@@ -185,7 +202,8 @@ namespace GodotTools.IdeMessaging
private GodotIdeMetadata? ReadMetadataFile()
{
- using (var reader = File.OpenText(MetaFilePath))
+ using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ using (var reader = new StreamReader(fileStream))
{
string portStr = reader.ReadLine();
@@ -272,6 +290,7 @@ namespace GodotTools.IdeMessaging
// ReSharper disable once UnusedMember.Global
public async void Start()
{
+ fsWatcher.Created += OnMetaFileChanged;
fsWatcher.Changed += OnMetaFileChanged;
fsWatcher.Deleted += OnMetaFileDeleted;
fsWatcher.EnableRaisingEvents = true;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index 67815959a6..dad6b9ae7a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.2</LangVersion>
<PackageId>GodotTools.IdeMessaging</PackageId>
- <Version>1.1.0</Version>
+ <Version>1.1.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Authors>Godot Engine contributors</Authors>
<Company />
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index a4e86d6177..10d7e1898e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -105,49 +105,45 @@ namespace GodotTools.IdeMessaging
try
{
- try
+ if (msg.Kind == MessageKind.Request)
{
- if (msg.Kind == MessageKind.Request)
- {
- var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
- await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
- }
- else if (msg.Kind == MessageKind.Response)
- {
- ResponseAwaiter responseAwaiter;
+ var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
+ await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
+ }
+ else if (msg.Kind == MessageKind.Response)
+ {
+ ResponseAwaiter responseAwaiter;
- using (await requestsSem.UseAsync())
+ using (await requestsSem.UseAsync())
+ {
+ if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
{
- if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
- {
- Logger.LogError($"Received unexpected response: {msg.Id}");
- return;
- }
-
- responseAwaiter = queue.Dequeue();
+ Logger.LogError($"Received unexpected response: {msg.Id}");
+ return;
}
- responseAwaiter.SetResult(msg.Content);
- }
- else
- {
- throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
+ responseAwaiter = queue.Dequeue();
}
+
+ responseAwaiter.SetResult(msg.Content);
}
- catch (Exception e)
+ else
{
- Logger.LogError($"Message handler for '{msg}' failed with exception", e);
+ throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
}
}
catch (Exception e)
{
- Logger.LogError($"Exception thrown from message handler. Message: {msg}", e);
+ Logger.LogError($"Message handler for '{msg}' failed with exception", e);
}
}
}
catch (Exception e)
{
- Logger.LogError("Unhandled exception in the peer loop", e);
+ if (!IsDisposed || !(e is SocketException || e.InnerException is SocketException))
+ {
+ Logger.LogError("Unhandled exception in the peer loop", e);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
index 1dd4f852e5..e93db9377b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
@@ -67,6 +67,19 @@ namespace GodotTools.IdeMessaging.Requests
{
}
+ public sealed class StopPlayRequest : Request
+ {
+ public new const string Id = "StopPlay";
+
+ public StopPlayRequest() : base(Id)
+ {
+ }
+ }
+
+ public sealed class StopPlayResponse : Response
+ {
+ }
+
public sealed class DebugPlayRequest : Request
{
public string DebuggerHost { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
new file mode 100644
index 0000000000..5b3ed0b1b7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net472</TargetFramework>
+ <LangVersion>7.2</LangVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="EnvDTE" Version="8.0.2" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
new file mode 100644
index 0000000000..affb2a47e7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text.RegularExpressions;
+using EnvDTE;
+
+namespace GodotTools.OpenVisualStudio
+{
+ internal static class Program
+ {
+ [DllImport("ole32.dll")]
+ private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);
+
+ [DllImport("ole32.dll")]
+ private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
+
+ [DllImport("user32.dll")]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ private static void ShowHelp()
+ {
+ Console.WriteLine("Opens the file(s) in a Visual Studio instance that is editing the specified solution.");
+ Console.WriteLine("If an existing instance for the solution is not found, a new one is created.");
+ Console.WriteLine();
+ Console.WriteLine("Usage:");
+ Console.WriteLine(@" GodotTools.OpenVisualStudio.exe solution [file[;line[;col]]...]");
+ Console.WriteLine();
+ Console.WriteLine("Lines and columns begin at one. Zero or lower will result in an error.");
+ Console.WriteLine("If a line is specified but a column is not, the line is selected in the text editor.");
+ }
+
+ // STAThread needed, otherwise CoRegisterMessageFilter may return CO_E_NOT_SUPPORTED.
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
+ {
+ ShowHelp();
+ return 0;
+ }
+
+ string solutionFile = NormalizePath(args[0]);
+
+ var dte = FindInstanceEditingSolution(solutionFile);
+
+ if (dte == null)
+ {
+ // Open a new instance
+
+ var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
+ dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ dte.UserControl = true;
+
+ try
+ {
+ dte.Solution.Open(solutionFile);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("Solution.Open: Invalid path or file not found");
+ return 1;
+ }
+
+ dte.MainWindow.Visible = true;
+ }
+
+ MessageFilter.Register();
+
+ try
+ {
+ // Open files
+
+ for (int i = 1; i < args.Length; i++)
+ {
+ // Both the line number and the column begin at one
+
+ string[] fileArgumentParts = args[i].Split(';');
+
+ string filePath = NormalizePath(fileArgumentParts[0]);
+
+ try
+ {
+ dte.ItemOperations.OpenFile(filePath);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("ItemOperations.OpenFile: Invalid path or file not found");
+ return 1;
+ }
+
+ if (fileArgumentParts.Length > 1)
+ {
+ if (int.TryParse(fileArgumentParts[1], out int line))
+ {
+ var textSelection = (TextSelection)dte.ActiveDocument.Selection;
+
+ if (fileArgumentParts.Length > 2)
+ {
+ if (int.TryParse(fileArgumentParts[2], out int column))
+ {
+ textSelection.MoveToLineAndOffset(line, column);
+ }
+ else
+ {
+ Console.Error.WriteLine("The column part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ else
+ {
+ textSelection.GotoLine(line, Select: true);
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("The line part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ }
+ }
+ finally
+ {
+ var mainWindow = dte.MainWindow;
+ mainWindow.Activate();
+ SetForegroundWindow(new IntPtr(mainWindow.HWnd));
+
+ MessageFilter.Revoke();
+ }
+
+ return 0;
+ }
+
+ private static DTE FindInstanceEditingSolution(string solutionPath)
+ {
+ if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
+ return null;
+
+ try
+ {
+ pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
+ ppenumMoniker.Reset();
+
+ var moniker = new IMoniker[1];
+
+ while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
+ {
+ string ppszDisplayName;
+
+ CreateBindCtx(0, out IBindCtx ppbc);
+
+ try
+ {
+ moniker[0].GetDisplayName(ppbc, null, out ppszDisplayName);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(ppbc);
+ }
+
+ if (ppszDisplayName == null)
+ continue;
+
+ // The digits after the colon are the process ID
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ continue;
+
+ if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
+ {
+ if (ppunkObject is DTE dte && dte.Solution.FullName.Length > 0)
+ {
+ if (NormalizePath(dte.Solution.FullName) == solutionPath)
+ return dte;
+ }
+ }
+ }
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(pprot);
+ }
+
+ return null;
+ }
+
+ static string NormalizePath(string path)
+ {
+ return new Uri(Path.GetFullPath(path)).LocalPath
+ .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ .ToUpperInvariant();
+ }
+
+ #region MessageFilter. See: http: //msdn.microsoft.com/en-us/library/ms228772.aspx
+
+ private class MessageFilter : IOleMessageFilter
+ {
+ // Class containing the IOleMessageFilter
+ // thread error-handling functions
+
+ private static IOleMessageFilter _oldFilter;
+
+ // Start the filter
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ int ret = CoRegisterMessageFilter(newFilter, out _oldFilter);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ // Done with the filter, close it
+ public static void Revoke()
+ {
+ int ret = CoRegisterMessageFilter(_oldFilter, out _);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ //
+ // IOleMessageFilter functions
+ // Handle incoming thread requests
+ int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
+ {
+ // Return the flag SERVERCALL_ISHANDLED
+ return 0;
+ }
+
+ // Thread call was rejected, so try again.
+ int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // flag = SERVERCALL_RETRYLATER
+ {
+ // Retry the thread call immediately if return >= 0 & < 100
+ return 99;
+ }
+
+ // Too busy; cancel call
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
+ {
+ // Return the flag PENDINGMSG_WAITDEFPROCESS
+ return 2;
+ }
+
+ // Implement the IOleMessageFilter interface
+ [DllImport("ole32.dll")]
+ private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index fb2beb6995..679d5bb444 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -12,6 +12,11 @@ namespace GodotTools.ProjectEditor
private const string CoreApiProjectName = "GodotSharp";
private const string EditorApiProjectName = "GodotSharpEditor";
+ public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
+ public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}";
+
+ public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}";
+
public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
{
string path = Path.Combine(dir, name + ".csproj");
@@ -19,6 +24,7 @@ namespace GodotTools.ProjectEditor
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(name, "Debug", out mainGroup);
+ mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 069a1edaa3..8774b4ee31 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -165,6 +165,21 @@ namespace GodotTools.ProjectEditor
return result.ToArray();
}
+ public static void EnsureHasProjectTypeGuids(MSBuildProject project)
+ {
+ var root = project.Root;
+
+ bool found = root.PropertyGroups.Any(pg =>
+ string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
+
+ if (found)
+ return;
+
+ root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
+
+ project.HasUnsavedChanges = true;
+ }
+
/// Simple function to make sure the Api assembly references are configured correctly
public static void FixApiHintPath(MSBuildProject project)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index f6147eb5bb..ba5379e562 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj", "{92600954-25F0-4291-8E11-1FEE9FC4BE20}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -37,5 +39,9 @@ Global
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index 3cf495f025..3de3d8d318 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -20,8 +20,8 @@ namespace GodotTools
private ItemList buildTabsList;
private TabContainer buildTabs;
- private ToolButton warningsBtn;
- private ToolButton errorsBtn;
+ private Button warningsBtn;
+ private Button errorsBtn;
private Button viewLogBtn;
private void _UpdateBuildTabsList()
@@ -285,7 +285,7 @@ namespace GodotTools
toolBarHBox.AddSpacer(begin: false);
- warningsBtn = new ToolButton
+ warningsBtn = new Button
{
Text = "Warnings".TTR(),
ToggleMode = true,
@@ -296,7 +296,7 @@ namespace GodotTools
warningsBtn.Toggled += _WarningsToggled;
toolBarHBox.AddChild(warningsBtn);
- errorsBtn = new ToolButton
+ errorsBtn = new Button
{
Text = "Errors".TTR(),
ToggleMode = true,
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index e55558c100..34e42489eb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -47,19 +47,14 @@ namespace GodotTools.Build
private static bool PrintBuildOutput =>
(bool)EditorSettings.GetSetting("mono/builds/print_build_output");
- private static Process LaunchBuild(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+ private static Process LaunchBuild(BuildInfo buildInfo)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
if (msbuildPath == null)
throw new FileNotFoundException("Cannot find the MSBuild executable.");
- var customPropertiesList = new List<string>();
-
- if (customProperties != null)
- customPropertiesList.AddRange(customProperties);
-
- string compilerArgs = BuildArguments(buildTool, solution, targets, config, loggerOutputDir, customPropertiesList);
+ string compilerArgs = BuildArguments(buildTool, buildInfo);
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
@@ -100,19 +95,7 @@ namespace GodotTools.Build
public static int Build(BuildInfo buildInfo)
{
- return Build(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
- buildInfo.LogsDirPath, buildInfo.CustomProperties);
- }
-
- public static Task<int> BuildAsync(BuildInfo buildInfo)
- {
- return BuildAsync(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
- buildInfo.LogsDirPath, buildInfo.CustomProperties);
- }
-
- public static int Build(string solution, string[] targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
- {
- using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
+ using (var process = LaunchBuild(buildInfo))
{
process.WaitForExit();
@@ -120,9 +103,9 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+ public static async Task<int> BuildAsync(BuildInfo buildInfo)
{
- using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
+ using (var process = LaunchBuild(buildInfo))
{
await process.WaitForExitAsync();
@@ -130,17 +113,18 @@ namespace GodotTools.Build
}
}
- private static string BuildArguments(BuildTool buildTool, string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties)
+ private static string BuildArguments(BuildTool buildTool, BuildInfo buildInfo)
{
string arguments = string.Empty;
if (buildTool == BuildTool.DotnetCli)
arguments += "msbuild "; // `dotnet msbuild` command
- arguments += $@"""{solution}"" /v:normal /t:{string.Join(",", targets)} ""/p:{"Configuration=" + config}"" " +
- $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
+ arguments += $@"""{buildInfo.Solution}"" /t:{string.Join(",", buildInfo.Targets)} " +
+ $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
+ $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
- foreach (string customProperty in customProperties)
+ foreach (string customProperty in buildInfo.CustomProperties)
{
arguments += " /p:" + customProperty;
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
index a1a69334e3..837c8adddb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
@@ -1,6 +1,6 @@
namespace GodotTools.Build
{
- public enum BuildTool
+ public enum BuildTool : long
{
MsBuildMono,
MsBuildVs,
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
index cca0983c01..ab090c46e7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
@@ -12,6 +12,7 @@ namespace GodotTools
public string Solution { get; }
public string[] Targets { get; }
public string Configuration { get; }
+ public bool Restore { get; }
public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
@@ -39,11 +40,12 @@ namespace GodotTools
{
}
- public BuildInfo(string solution, string[] targets, string configuration)
+ public BuildInfo(string solution, string[] targets, string configuration, bool restore)
{
Solution = solution;
Targets = targets;
Configuration = configuration;
+ Restore = restore;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index 598787ba03..0974d23176 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -175,7 +175,7 @@ namespace GodotTools
{
pr.Step("Building project solution", 0);
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Restore", "Build"}, config);
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
bool escapeNeedsDoubleBackslash = buildTool == BuildTool.MsBuildMono || buildTool == BuildTool.DotnetCli;
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
index 0106e1f1ac..8596cd24af 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
@@ -113,7 +113,7 @@ namespace GodotTools
throw new IndexOutOfRangeException("Item list index out of range");
// Get correct issue idx from issue list
- int issueIndex = (int)issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)(long)issuesList.GetItemMetadata(idx);
if (issueIndex < 0 || issueIndex >= issues.Count)
throw new IndexOutOfRangeException("Issue index out of range");
diff --git a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
index bb218c2f19..90d6eb960e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
@@ -1,6 +1,6 @@
namespace GodotTools
{
- public enum ExternalEditorId
+ public enum ExternalEditorId : long
{
None,
VisualStudio, // TODO (Windows-only)
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index c874025be0..f330f9ed2c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -29,7 +30,7 @@ namespace GodotTools
private AcceptDialog aboutDialog;
private CheckBox aboutDialogCheckBox;
- private ToolButton bottomPanelBtn;
+ private Button bottomPanelBtn;
public GodotIdeManager GodotIdeManager { get; private set; }
@@ -238,7 +239,31 @@ namespace GodotTools
// Not an error. Tells the caller to fallback to the global external editor settings or the built-in editor.
return Error.Unavailable;
case ExternalEditorId.VisualStudio:
- throw new NotSupportedException();
+ {
+ string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+ var args = new List<string>
+ {
+ GodotSharpDirs.ProjectSlnPath,
+ line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath
+ };
+
+ string command = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "GodotTools.OpenVisualStudio.exe");
+
+ try
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+
+ OS.RunProcess(command, args);
+ }
+ catch (Exception e)
+ {
+ GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ }
+
+ break;
+ }
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
case ExternalEditorId.Rider:
@@ -458,6 +483,9 @@ namespace GodotTools
// Apply the other fixes only after configurations have been migrated
+ // Make sure the existing project has the ProjectTypeGuids property (for VisualStudio)
+ ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject);
+
// Make sure the existing project has Api assembly references configured correctly
ProjectUtils.FixApiHintPath(msbuildProject);
@@ -485,7 +513,7 @@ namespace GodotTools
menuPopup.IdPressed += _MenuOptionPressed;
- var buildButton = new ToolButton
+ var buildButton = new Button
{
Text = "Build",
HintTooltip = "Build solution",
@@ -501,7 +529,8 @@ namespace GodotTools
if (OS.IsWindows)
{
- settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
+ settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
+ $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index ba527ca3b5..3f14629b11 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -33,5 +33,7 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <!-- Include it if this is an SCons build targeting Windows, or if it's not an SCons build but we're on Windows -->
+ <ProjectReference Include="..\GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) " />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index ae05710f4f..b30c857c64 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -10,7 +10,7 @@ namespace GodotTools
public override void _Notification(int what)
{
- if (what == Node.NotificationWmFocusIn)
+ if (what == Node.NotificationWmWindowFocusIn)
{
RestartTimer();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 32f264d100..17f3339560 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -12,6 +12,7 @@ using GodotTools.IdeMessaging;
using GodotTools.IdeMessaging.Requests;
using GodotTools.IdeMessaging.Utils;
using GodotTools.Internals;
+using GodotTools.Utils;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
using File = System.IO.File;
@@ -307,6 +308,11 @@ namespace GodotTools.Ides
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
return await HandleDebugPlay(request);
},
+ [StopPlayRequest.Id] = async (peer, content) =>
+ {
+ var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body);
+ return await HandleStopPlay(request);
+ },
[ReloadScriptsRequest.Id] = async (peer, content) =>
{
_ = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
@@ -343,6 +349,12 @@ namespace GodotTools.Ides
return Task.FromResult<Response>(new DebugPlayResponse());
}
+ private static Task<Response> HandleStopPlay(StopPlayRequest request)
+ {
+ DispatchToMainThread(Internal.EditorRunStop);
+ return Task.FromResult<Response>(new StopPlayResponse());
+ }
+
private static Task<Response> HandleReloadScripts()
{
DispatchToMainThread(Internal.ScriptEditorDebugger_ReloadScripts);
@@ -351,8 +363,13 @@ namespace GodotTools.Ides
private static async Task<Response> HandleCodeCompletionRequest(CodeCompletionRequest request)
{
+ // This is needed if the "resource path" part of the path is case insensitive.
+ // However, it doesn't fix resource loading if the rest of the path is also case insensitive.
+ string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
+
var response = new CodeCompletionResponse {Kind = request.Kind, ScriptFile = request.ScriptFile};
- response.Suggestions = await Task.Run(() => Internal.CodeCompletionRequest(response.Kind, response.ScriptFile));
+ response.Suggestions = await Task.Run(() =>
+ Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
return response;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
index 7fb087467f..569f27649f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
@@ -13,9 +13,9 @@ namespace GodotTools.Internals
public string Name { get; }
public string Namespace { get; }
public bool Nested { get; }
- public int BaseCount { get; }
+ public long BaseCount { get; }
- public ClassDecl(string name, string @namespace, bool nested, int baseCount)
+ public ClassDecl(string name, string @namespace, bool nested, long baseCount)
{
Name = name;
Namespace = @namespace;
@@ -45,7 +45,7 @@ namespace GodotTools.Internals
(string)classDeclDict["name"],
(string)classDeclDict["namespace"],
(bool)classDeclDict["nested"],
- (int)classDeclDict["base_count"]
+ (long)classDeclDict["base_count"]
));
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
new file mode 100644
index 0000000000..c6724ccaf7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+using Godot;
+using GodotTools.Core;
+using JetBrains.Annotations;
+
+namespace GodotTools.Utils
+{
+ public static class FsPathUtils
+ {
+ private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
+
+ private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
+ {
+ // This won't work for Linux/macOS case insensitive file systems, but it's enough for our current problems
+ bool caseSensitive = !OS.IsWindows;
+
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.StartsWith(parentPathNorm,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool PathStartsWith(this string childPath, string parentPath)
+ {
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
+ }
+
+ [CanBeNull]
+ public static string LocalizePathWithCaseChecked(string path)
+ {
+ string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
+ return null;
+
+ string result = "res://" + pathNorm.Substring(resourcePathNorm.Length);
+
+ // Remove the last separator we added
+ return result.Substring(0, result.Length - 1);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
new file mode 100644
index 0000000000..20b11a48dd
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+namespace Godot
+{
+ public partial class SceneTree
+ {
+ public Array<T> GetNodesInGroup<T>(StringName group) where T : class
+ {
+ return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T)));
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index b5ac124c9a..06ec2483c8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -53,6 +53,7 @@
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
+ <Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index ee99a300b9..f6999d01fb 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -35,6 +35,7 @@
#include "gd_glue.h"
#include "nodepath_glue.h"
#include "rid_glue.h"
+#include "scene_tree_glue.h"
#include "string_glue.h"
#include "string_name_glue.h"
@@ -50,6 +51,7 @@ void godot_register_glue_header_icalls() {
godot_register_object_icalls();
godot_register_rid_icalls();
godot_register_string_icalls();
+ godot_register_scene_tree_icalls();
}
// Used by the generated glue
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
new file mode 100644
index 0000000000..bea9544b08
--- /dev/null
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* scene_tree_glue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_tree_glue.h"
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/class_db.h"
+#include "modules/mono/csharp_script.h"
+#include "modules/mono/mono_gd/gd_mono_utils.h"
+#include "scene/main/node.h"
+
+Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype) {
+ List<Node *> nodes;
+ Array ret;
+
+ // Retrieve all the nodes in the group
+ ptr->get_nodes_in_group(*group, &nodes);
+
+ // No need to bother if the group is empty
+ if (!nodes.empty()) {
+ MonoType *elem_type = mono_reflection_type_get_type(refltype);
+ MonoClass *mono_class = mono_class_from_mono_type(elem_type);
+ GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
+
+ if (klass == GDMonoUtils::get_class_native_base(klass)) {
+ // If we're trying to get native objects, just check the inheritance list
+ StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass);
+ for (int i = 0; i < nodes.size(); ++i) {
+ if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name))
+ ret.push_back(nodes[i]);
+ }
+ } else {
+ // If we're trying to get csharpscript instances, get the mono object and compare the classes
+ for (int i = 0; i < nodes.size(); ++i) {
+ CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
+
+ if (si != nullptr) {
+ MonoObject *obj = si->get_mono_object();
+ if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
+ ret.push_back(nodes[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return memnew(Array(ret));
+}
+
+void godot_register_scene_tree_icalls() {
+ mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic);
+}
+
+#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/scene_tree_glue.h b/modules/mono/glue/scene_tree_glue.h
new file mode 100644
index 0000000000..e9af35a30b
--- /dev/null
+++ b/modules/mono/glue/scene_tree_glue.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* scene_tree_glue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_TREE_GLUE_H
+#define SCENE_TREE_GLUE_H
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/array.h"
+#include "core/string_name.h"
+#include "scene/main/scene_tree.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype);
+
+// Register internal calls
+
+void godot_register_scene_tree_icalls();
+
+#endif // MONO_GLUE_ENABLED
+
+#endif // SCENE_TREE_GLUE_H
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index 27e402d777..abb2761909 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -114,7 +114,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
}
void unhandled_exception(MonoException *p_exc) {
- mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+ mono_print_unhandled_exception((MonoObject *)p_exc);
if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 085062261d..158742846b 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -624,8 +624,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_BOOLEAN(val);
}
case Variant::INT: {
- int32_t val = p_var->operator signed int();
- return BOX_INT32(val);
+ int64_t val = p_var->operator int64_t();
+ return BOX_INT64(val);
}
case Variant::FLOAT: {
#ifdef REAL_T_IS_DOUBLE
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 1b4fe68582..e0cf916a01 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -75,7 +75,6 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
- Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}