diff options
Diffstat (limited to 'modules/mono')
38 files changed, 362 insertions, 302 deletions
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 3e771e06f0..6057004166 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,6 +1,5 @@ import os import os.path -import sys import subprocess from SCons.Script import Dir, Environment @@ -397,9 +396,8 @@ def configure(env, env_mono): mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() if tools_enabled: + # Only supported for editor builds. copy_mono_root_files(env, mono_root) - else: - print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.") def make_template_dir(env, mono_root): diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index 3090a4759a..0ec7e2f433 100644 --- a/modules/mono/build_scripts/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py @@ -9,7 +9,7 @@ if os.name == "nt": def _reg_open_key(key, subkey): try: return winreg.OpenKey(key, subkey) - except (WindowsError, OSError): + except OSError: if platform.architecture()[0] == "32bit": bitness_sam = winreg.KEY_WOW64_64KEY else: @@ -37,7 +37,7 @@ def _find_mono_in_reg(subkey, bits): with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0] return value - except (WindowsError, OSError): + except OSError: return None @@ -48,7 +48,7 @@ def _find_mono_in_reg_old(subkey, bits): if default_clr: return _find_mono_in_reg(subkey + "\\" + default_clr, bits) return None - except (WindowsError, EnvironmentError): + except OSError: return None @@ -97,7 +97,7 @@ def find_msbuild_tools_path_reg(): raise ValueError("Cannot find `installationPath` entry") except ValueError as e: print("Error reading output from vswhere: " + e.message) - except WindowsError: + except OSError: pass # Fine, vswhere not found except (subprocess.CalledProcessError, OSError): pass @@ -109,5 +109,5 @@ def find_msbuild_tools_path_reg(): with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0] return value - except (WindowsError, OSError): + except OSError: return "" diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py index 03f4e57f02..6a621c3c8b 100644 --- a/modules/mono/build_scripts/solution_builder.py +++ b/modules/mono/build_scripts/solution_builder.py @@ -8,9 +8,6 @@ def find_dotnet_cli(): import os.path if os.name == "nt": - windows_exts = os.environ["PATHEXT"] - windows_exts = windows_exts.split(os.pathsep) if windows_exts else [] - for hint_dir in os.environ["PATH"].split(os.pathsep): hint_dir = hint_dir.strip('"') hint_path = os.path.join(hint_dir, "dotnet") diff --git a/modules/mono/config.py b/modules/mono/config.py index cd659057ef..d060ae9b28 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -23,18 +23,16 @@ def configure(env): envvars.Add( PathVariable( "mono_prefix", - "Path to the mono installation directory for the target platform and architecture", + "Path to the Mono installation directory for the target platform and architecture", "", PathVariable.PathAccept, ) ) - envvars.Add(BoolVariable("mono_static", "Statically link mono", default_mono_static)) - envvars.Add(BoolVariable("mono_glue", "Build with the mono glue sources", True)) + envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static)) + envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True)) envvars.Add(BoolVariable("build_cil", "Build C# solutions", True)) envvars.Add( - BoolVariable( - "copy_mono_root", "Make a copy of the mono installation directory to bundle with the editor", False - ) + BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True) ) # TODO: It would be great if this could be detected automatically instead diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index e1e9d1381f..45a6f991bf 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -8,7 +8,7 @@ See also [GodotSharp]. </description> <tutorials> - <link>https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> + <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> </tutorials> <methods> <method name="new" qualifiers="vararg"> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props index dfc59e6ccb..5febcf3175 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props @@ -14,15 +14,15 @@ <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir> <GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir> - <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.mono\temp\'. --> - <BaseOutputPath>$(GodotProjectDir).mono\temp\bin\</BaseOutputPath> - <OutputPath>$(GodotProjectDir).mono\temp\bin\$(Configuration)\</OutputPath> + <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. --> + <BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath> + <OutputPath>$(GodotProjectDir).godot\mono\temp\bin\$(Configuration)\</OutputPath> <!-- Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set. Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet. --> - <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\$(Configuration)\</IntermediateOutputPath> - <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\</BaseIntermediateOutputPath> + <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\$(Configuration)\</IntermediateOutputPath> + <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\</BaseIntermediateOutputPath> <!-- Do not append the target framework name to the output path. --> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> @@ -88,7 +88,7 @@ <PropertyGroup> <!-- ExportDebug also defines DEBUG like Debug does. --> <DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants> - <!-- Debug defines TOOLS to differenciate between Debug and ExportDebug configurations. --> + <!-- Debug defines TOOLS to differentiate between Debug and ExportDebug configurations. --> <DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants> <DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants> @@ -102,11 +102,11 @@ --> <Reference Include="GodotSharp"> <Private>false</Private> - <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath> + <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath> </Reference> <Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' "> <Private>false</Private> - <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath> + <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath> </Reference> </ItemGroup> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs index c2549b4ad5..5edf72d63e 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs @@ -70,13 +70,14 @@ namespace GodotTools.BuildLogger { string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}"; - if (e.ProjectFile.Length > 0) + if (!string.IsNullOrEmpty(e.ProjectFile)) line += $" [{e.ProjectFile}]"; WriteLine(line); string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," + - $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}"; + $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," + + $"{e.ProjectFile?.CsvEscape() ?? string.Empty}"; issuesStreamWriter.WriteLine(errorLine); } @@ -89,8 +90,9 @@ namespace GodotTools.BuildLogger WriteLine(line); - string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," + - $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}"; + string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," + + $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," + + $"{e.ProjectFile?.CsvEscape() ?? string.Empty}"; issuesStreamWriter.WriteLine(warningLine); } diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index 012b69032e..b217ae4bf7 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; namespace GodotTools.Core { @@ -14,14 +15,18 @@ namespace GodotTools.Core if (Path.DirectorySeparatorChar == '\\') dir = dir.Replace("/", "\\") + "\\"; - Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute); - Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute); + var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute); + var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute); - return relRoot.MakeRelativeUri(fullPath).ToString(); + // MakeRelativeUri converts spaces to %20, hence why we need UnescapeDataString + return Uri.UnescapeDataString(relRoot.MakeRelativeUri(fullPath).ToString()); } public static string NormalizePath(this string path) { + if (string.IsNullOrEmpty(path)) + return path; + bool rooted = path.IsAbsolutePath(); path = path.Replace('\\', '/'); @@ -31,7 +36,17 @@ namespace GodotTools.Core path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim(); - return rooted ? Path.DirectorySeparatorChar + path : path; + if (!rooted) + return path; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string maybeDrive = parts[0]; + if (maybeDrive.Length == 2 && maybeDrive[1] == ':') + return path; // Already has drive letter + } + + return Path.DirectorySeparatorChar + path; } private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory); @@ -43,9 +58,9 @@ namespace GodotTools.Core path.StartsWith(DriveRoot, StringComparison.Ordinal); } - public static string ToSafeDirName(this string dirName, bool allowDirSeparator) + public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false) { - var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" }; + var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"}; if (allowDirSeparator) { diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index 572c541412..0f50c90531 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -121,7 +121,7 @@ namespace GodotTools.IdeMessaging this.messageHandler = messageHandler; this.logger = logger; - string projectMetadataDir = Path.Combine(godotProjectDir, ".mono", "metadata"); + string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata"); MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs index 6f318aab4a..cc0da44a13 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -2,6 +2,7 @@ using GodotTools.Core; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; namespace GodotTools.ProjectEditor @@ -88,7 +89,7 @@ namespace GodotTools.ProjectEditor string solutionPath = Path.Combine(DirectoryPath, Name + ".sln"); string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg); - File.WriteAllText(solutionPath, content); + File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM } public DotNetSolution(string name) diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj index 9cb50014b0..e4d6b2e010 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj @@ -11,13 +11,21 @@ <ItemGroup> <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" /> </ItemGroup> + <!-- + The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described + here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486 + We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when + searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed. + --> <ItemGroup> - <!-- - The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described - here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486 - We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when - searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed. - --> <None Include="MSBuild.exe" CopyToOutputDirectory="Always" /> </ItemGroup> + <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) "> + <PropertyGroup> + <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath> + <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir> + </PropertyGroup> + <!-- Need to copy it here as well on Windows --> + <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" /> + </Target> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs index 5541876f9e..01d7c99662 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; @@ -41,7 +42,8 @@ namespace GodotTools.ProjectEditor var root = GenGameProject(name); - root.Save(path); + // Save (without BOM) + root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); return Guid.NewGuid().ToString().ToUpper(); } diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index 4041c56597..4e2c0f17cc 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -61,10 +61,9 @@ namespace GodotTools.ProjectEditor if (item.ItemType != itemType) continue; - string normalizedExclude = item.Exclude.NormalizePath(); - - var glob = MSBuildGlob.Parse(normalizedExclude); + string normalizedRemove = item.Remove.NormalizePath(); + var glob = MSBuildGlob.Parse(normalizedRemove); excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile))); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs index f36e581a5f..7bfba779fb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -161,8 +161,21 @@ namespace GodotTools.Build // Try to find 15.0 with vswhere - string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)"); - vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + var envNames = Internal.GodotIs32Bits() ? new[] { "ProgramFiles", "ProgramW6432" } : new[] { "ProgramFiles(x86)", "ProgramFiles" }; + + string vsWherePath = null; + foreach (var envName in envNames) + { + vsWherePath = Environment.GetEnvironmentVariable(envName); + if (!string.IsNullOrEmpty(vsWherePath)) + { + vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe"; + if (File.Exists(vsWherePath)) + break; + } + + vsWherePath = null; + } var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"}; diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs index a8afb38728..1d800b8151 100644 --- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs +++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs @@ -48,7 +48,7 @@ namespace GodotTools var firstMatch = classes.FirstOrDefault(classDecl => classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object. - classDecl.SearchName != searchName // Filter by the name we're looking for + classDecl.SearchName == searchName // Filter by the name we're looking for ); if (firstMatch == null) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index f60e469503..42ede3f3f3 100755 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -328,7 +328,7 @@ MONO_AOT_MODE_LAST = 1000, if (lipoExitCode != 0) throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}"); - // TODO: Add the AOT lib and interpreter libs as device only to supress warnings when targeting the simulator + // TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator // Add the fat AOT static library to the Xcode project exporter.AddIosProjectStaticLib(fatOutputFilePath); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 554763eecb..599ca94699 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -19,7 +19,7 @@ namespace GodotTools.Export public class ExportPlugin : EditorExportPlugin { [Flags] - enum I18NCodesets + enum I18NCodesets : long { None = 0, CJK = 1, @@ -430,7 +430,7 @@ namespace GodotTools.Export private static string DetermineDataDirNameForProject() { var appName = (string)ProjectSettings.GetSetting("application/config/name"); - string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false); + string appNameSafe = appName.ToSafeDirName(); return $"data_{appNameSafe}"; } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index e2f7e87388..2a450c5b87 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -31,6 +31,7 @@ namespace GodotTools private CheckBox aboutDialogCheckBox; private Button bottomPanelBtn; + private Button toolBarBuildButton; public GodotIdeManager GodotIdeManager { get; private set; } @@ -45,6 +46,7 @@ namespace GodotTools get { var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name"); + projectAssemblyName = projectAssemblyName.ToSafeDirName(); if (string.IsNullOrEmpty(projectAssemblyName)) projectAssemblyName = "UnnamedProject"; return projectAssemblyName; @@ -126,6 +128,7 @@ namespace GodotTools { menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln)); bottomPanelBtn.Show(); + toolBarBuildButton.Show(); } private void _ShowAboutDialog() @@ -467,6 +470,15 @@ namespace GodotTools aboutVBox.AddChild(aboutDialogCheckBox); } + toolBarBuildButton = new Button + { + Text = "Build", + HintTooltip = "Build solution", + FocusMode = Control.FocusModeEnum.None + }; + toolBarBuildButton.PressedSignal += _BuildSolutionPressed; + AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton); + if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath)) { ApplyNecessaryChangesToSolution(); @@ -474,20 +486,12 @@ namespace GodotTools else { bottomPanelBtn.Hide(); + toolBarBuildButton.Hide(); menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); } menuPopup.IdPressed += _MenuOptionPressed; - var buildButton = new Button - { - Text = "Build", - HintTooltip = "Build solution", - FocusMode = Control.FocusModeEnum.None - }; - buildButton.PressedSignal += _BuildSolutionPressed; - AddControlToContainer(CustomControlContainer.Toolbar, buildButton); - // External editor settings EditorDef("mono/editor/external_editor", ExternalEditorId.None); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index b15e9b060a..2edd8c87dc 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -43,6 +43,16 @@ namespace GodotSharpExport { +MonoAssemblyName *new_mono_assembly_name() { + // Mono has no public API to create an empty MonoAssemblyName and the struct is private. + // As such the only way to create it is with a stub name and then clear it. + + MonoAssemblyName *aname = mono_assembly_name_new("stub"); + CRASH_COND(aname == nullptr); + mono_assembly_name_free(aname); // Frees the string fields, not the struct + return aname; +} + struct AssemblyRefInfo { String name; uint16_t major; @@ -67,7 +77,7 @@ AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) { }; } -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) { MonoImage *image = p_assembly->get_image(); for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { @@ -79,26 +89,16 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> continue; } - GDMonoAssembly *ref_assembly = nullptr; - - { - MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A" - CRASH_COND(ref_aname == nullptr); - SCOPE_EXIT { - mono_assembly_name_free(ref_aname); - mono_free(ref_aname); - }; - - mono_assembly_get_assemblyref(image, i, ref_aname); + mono_assembly_get_assemblyref(image, i, reusable_aname); - if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) { - ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); - } - - r_assembly_dependencies[ref_name] = ref_assembly->get_path(); + GDMonoAssembly *ref_assembly = NULL; + if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) { + ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); } - Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies); + r_assembly_dependencies[ref_name] = ref_assembly->get_path(); + + Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'."); } @@ -130,7 +130,10 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies, ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'."); - Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies); + MonoAssemblyName *reusable_aname = new_mono_assembly_name(); + SCOPE_EXIT { mono_free(reusable_aname); }; + + Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies); if (err != OK) { return err; } diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 430c82953e..f7d6e7e302 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -151,7 +151,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { case '"': { bool verbatim = idx != 0 && code[idx - 1] == '@'; - CharType begin_str = code[idx]; + char32_t begin_str = code[idx]; idx++; String tk_string = String(); while (true) { @@ -170,13 +170,13 @@ ScriptClassParser::Token ScriptClassParser::get_token() { } else if (code[idx] == '\\' && !verbatim) { //escaped characters... idx++; - CharType next = code[idx]; + char32_t next = code[idx]; if (next == 0) { error_str = "Unterminated String"; error = true; return TK_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -234,7 +234,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { //a number - const CharType *rptr; + const char32_t *rptr; double number = String::to_float(&code[idx], &rptr); idx += (rptr - &code[idx]); value = number; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a963810d63..f77d3052f4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -44,6 +44,15 @@ namespace Godot.Collections Add(element); } + public Array(params object[] array) : this() + { + if (array == null) + { + throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); + } + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); + } + internal Array(ArraySafeHandle handle) { safeHandle = handle; @@ -72,6 +81,11 @@ namespace Godot.Collections return godot_icall_Array_Resize(GetPtr(), newSize); } + public static Array operator +(Array left, Array right) + { + return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr())); + } + // IDisposable public void Dispose() @@ -155,6 +169,9 @@ namespace Godot.Collections internal extern static IntPtr godot_icall_Array_Ctor(); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] @@ -176,6 +193,9 @@ namespace Godot.Collections internal extern static void godot_icall_Array_Clear(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] @@ -231,6 +251,15 @@ namespace Godot.Collections objectArray = new Array(collection); } + public Array(params T[] array) : this() + { + if (array == null) + { + throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); + } + objectArray = new Array(array); + } + public Array(Array array) { objectArray = array; @@ -266,6 +295,11 @@ namespace Godot.Collections return objectArray.Resize(newSize); } + public static Array<T> operator +(Array<T> left, Array<T> right) + { + return new Array<T>(left.objectArray + right.objectArray); + } + // IList<T> public T this[int index] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 5aba31c622..3f1120575f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -207,7 +207,7 @@ namespace Godot } } - internal Quat RotationQuat() + public Quat RotationQuat() { Basis orthonormalizedBasis = Orthonormalized(); real_t det = orthonormalizedBasis.Determinant(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index d851abc6d3..3700a6194f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -565,6 +565,9 @@ namespace Godot rgba = rgba.Substring(1); } + // If enabled, use 1 hex digit per channel instead of 2. + // Other sizes aren't in the HTML/CSS spec but we could add them if desired. + bool isShorthand = rgba.Length < 5; bool alpha; if (rgba.Length == 8) @@ -575,47 +578,60 @@ namespace Godot { alpha = false; } + else if (rgba.Length == 4) + { + alpha = true; + } + else if (rgba.Length == 3) + { + alpha = false; + } else { throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba); } - if (alpha) + a = 1.0f; + if (isShorthand) { - a = ParseCol8(rgba, 6) / 255f; - - if (a < 0) + r = ParseCol4(rgba, 0) / 15f; + g = ParseCol4(rgba, 1) / 15f; + b = ParseCol4(rgba, 2) / 15f; + if (alpha) { - throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + a = ParseCol4(rgba, 3) / 15f; } } else { - a = 1.0f; + r = ParseCol8(rgba, 0) / 255f; + g = ParseCol8(rgba, 2) / 255f; + b = ParseCol8(rgba, 4) / 255f; + if (alpha) + { + a = ParseCol8(rgba, 6) / 255f; + } } - int from = alpha ? 2 : 0; - - r = ParseCol8(rgba, 0) / 255f; - if (r < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba); } - g = ParseCol8(rgba, 2) / 255f; - if (g < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba); } - b = ParseCol8(rgba, 4) / 255f; - if (b < 0) { throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba); } + + if (a < 0) + { + throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + } } /// <summary> @@ -751,45 +767,28 @@ namespace Godot value = max; } - private static int ParseCol8(string str, int ofs) + private static int ParseCol4(string str, int ofs) { - int ig = 0; + char character = str[ofs]; - for (int i = 0; i < 2; i++) + if (character >= '0' && character <= '9') { - int c = str[i + ofs]; - int v; - - if (c >= '0' && c <= '9') - { - v = c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - v = c - 'a'; - v += 10; - } - else if (c >= 'A' && c <= 'F') - { - v = c - 'A'; - v += 10; - } - else - { - return -1; - } - - if (i == 0) - { - ig += v * 16; - } - else - { - ig += v; - } + return character - '0'; + } + else if (character >= 'a' && character <= 'f') + { + return character + (10 - 'a'); } + else if (character >= 'A' && character <= 'F') + { + return character + (10 - 'A'); + } + return -1; + } - return ig; + private static int ParseCol8(string str, int ofs) + { + return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1); } private String ToHex32(float val) @@ -828,46 +827,24 @@ namespace Godot if (color[0] == '#') { - color = color.Substring(1, color.Length - 1); + color = color.Substring(1); } - bool alpha; - - switch (color.Length) + // Check if the amount of hex digits is valid. + int len = color.Length; + if (!(len == 3 || len == 4 || len == 6 || len == 8)) { - case 8: - alpha = true; - break; - case 6: - alpha = false; - break; - default: - return false; + return false; } - if (alpha) - { - if (ParseCol8(color, 0) < 0) + // Check if each hex digit is valid. + for (int i = 0; i < len; i++) { + if (ParseCol4(color, i) == -1) { return false; } } - int from = alpha ? 2 : 0; - - if (ParseCol8(color, from + 0) < 0) - { - return false; - } - if (ParseCol8(color, from + 2) < 0) - { - return false; - } - if (ParseCol8(color, from + 4) < 0) - { - return false; - } - return true; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 41b4e9367f..bd1dbc1229 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -440,7 +440,12 @@ namespace Godot // </summary> public static bool IsAbsPath(this string instance) { - return System.IO.Path.IsPathRooted(instance); + if (string.IsNullOrEmpty(instance)) + return false; + else if (instance.Length > 1) + return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/") || instance.Contains(":\\"); + else + return instance[0] == '/' || instance[0] == '\\'; } // <summary> @@ -448,7 +453,7 @@ namespace Godot // </summary> public static bool IsRelPath(this string instance) { - return !System.IO.Path.IsPathRooted(instance); + return !IsAbsPath(instance); } // <summary> @@ -624,41 +629,46 @@ namespace Godot return instance.Length; } - // <summary> - // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. - // </summary> - public static bool ExprMatch(this string instance, string expr, bool caseSensitive) + /// <summary> + /// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. + /// </summary> + private static bool ExprMatch(this string instance, string expr, bool caseSensitive) { - if (expr.Length == 0 || instance.Length == 0) - return false; + // case '\0': + if (expr.Length == 0) + return instance.Length == 0; switch (expr[0]) { - case '\0': - return instance[0] == 0; case '*': - return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); + return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive)); case '?': - return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); + return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); default: - return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && - ExprMatch(expr + 1, instance + 1, caseSensitive); + if (instance.Length == 0) return false; + return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); } } - // <summary> - // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). - // </summary> + /// <summary> + /// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). + /// </summary> public static bool Match(this string instance, string expr, bool caseSensitive = true) { + if (instance.Length == 0 || expr.Length == 0) + return false; + return instance.ExprMatch(expr, caseSensitive); } - // <summary> - // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). - // </summary> + /// <summary> + /// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). + /// </summary> public static bool MatchN(this string instance, string expr) { + if (instance.Length == 0 || expr.Length == 0) + return false; + return instance.ExprMatch(expr, caseSensitive: false); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 26bd828a5b..3dff37279b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -670,41 +670,37 @@ namespace Godot public static bool operator <(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y < right.y; } - return left.x < right.x; } public static bool operator >(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y > right.y; } - return left.x > right.x; } public static bool operator <=(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y <= right.y; } - return left.x <= right.x; } public static bool operator >=(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y >= right.y; } - return left.x >= right.x; } @@ -714,7 +710,6 @@ namespace Godot { return Equals((Vector2)obj); } - return false; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index d9b16a6afd..4a4a2a43cd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -713,49 +713,53 @@ namespace Godot public static bool operator <(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z < right.z; + } return left.y < right.y; } - return left.x < right.x; } public static bool operator >(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z > right.z; + } return left.y > right.y; } - return left.x > right.x; } public static bool operator <=(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z <= right.z; + } return left.y < right.y; } - return left.x < right.x; } public static bool operator >=(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z >= right.z; + } return left.y > right.y; } - return left.x > right.x; } diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 766b00d612..3313e8cb77 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -104,10 +104,31 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { } } +Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) { + Array *godot_array = memnew(Array); + unsigned int count = mono_array_length(mono_array); + godot_array->resize(count); + for (unsigned int i = 0; i < count; i++) { + MonoObject *item = mono_array_get(mono_array, MonoObject *, i); + godot_icall_Array_SetAt(godot_array, i, item); + } + return godot_array; +} + Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) { return memnew(Array(ptr->duplicate(deep))); } +Array *godot_icall_Array_Concatenate(Array *left, Array *right) { + int count = left->size() + right->size(); + Array *new_array = memnew(Array(left->duplicate(false))); + new_array->resize(count); + for (unsigned int i = 0; i < (unsigned int)right->size(); i++) { + new_array->operator[](i + left->size()) = right->operator[](i); + } + return new_array; +} + int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); } @@ -284,6 +305,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic); @@ -291,6 +313,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate); diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 692da991c7..df31823deb 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -122,7 +122,7 @@ public: private: _GodotSharpDirs() { - res_data_dir = "res://.mono"; + res_data_dir = "res://.godot/mono"; res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 9dbeee57ce..6e351001d4 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -107,7 +107,7 @@ void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly)); #ifdef GD_MONO_HOT_RELOAD - const char *path = mono_image_get_filename(image); + String path = String::utf8(mono_image_get_filename(image)); if (FileAccess::exists(path)) { gdassembly->modified_time = FileAccess::get_modified_time(path); } @@ -464,7 +464,9 @@ GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_a if (!assembly) { assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs); - ERR_FAIL_NULL_V(assembly, nullptr); + if (!assembly) { + return nullptr; + } } GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); @@ -487,7 +489,9 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_ if (!assembly) { assembly = _real_load_assembly_from(p_path, p_refonly); - ERR_FAIL_NULL_V(assembly, nullptr); + if (!assembly) { + return nullptr; + } } GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index c5a988b8c3..b8ee0204c4 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -64,25 +64,32 @@ static int get_log_level_id(const char *p_log_level) { return -1; } +static String make_text(const char *log_domain, const char *log_level, const char *message) { + String text(message); + text += " (in domain "; + text += log_domain; + if (log_level) { + text += ", "; + text += log_level; + } + text += ")"; + return text; +} + void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) { FileAccess *f = GDMonoLog::get_singleton()->log_file; if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) { - String text(message); - text += " (in domain "; - text += log_domain; - if (log_level) { - text += ", "; - text += log_level; - } - text += ")\n"; + String text = make_text(log_domain, log_level, message); + text += "\n"; f->seek_end(); f->store_string(text); } if (fatal) { - ERR_PRINT("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); + String text = make_text(log_domain, log_level, message); + ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); // Make sure to flush before aborting f->flush(); f->close(); diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 6d7d5f76cd..c460e283ea 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -311,44 +311,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ return false; } -String mono_to_utf8_string(MonoString *p_mono_string) { - MonoError error; - char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); - - if (!mono_error_ok(&error)) { - ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&error) + "'."); - mono_error_cleanup(&error); - return String(); - } - - String ret = String::utf8(utf8); - - mono_free(utf8); - - return ret; -} - -String mono_to_utf16_string(MonoString *p_mono_string) { - int len = mono_string_length(p_mono_string); - String ret; - - if (len == 0) { - return ret; - } - - ret.resize(len + 1); - ret.set(len, 0); - - CharType *src = (CharType *)mono_string_chars(p_mono_string); - CharType *dst = ret.ptrw(); - - for (int i = 0; i < len; i++) { - dst[i] = src[i]; - } - - return ret; -} - MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 4ff330fd43..a1fd975916 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -69,15 +69,11 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ // String -String mono_to_utf8_string(MonoString *p_mono_string); -String mono_to_utf16_string(MonoString *p_mono_string); - _FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_to_utf16_string(p_mono_string); - } - - return mono_to_utf8_string(p_mono_string); + char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string); + String ret = String(utf32); + mono_free(utf32); + return ret; } _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { @@ -88,20 +84,8 @@ _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { return mono_string_to_godot_not_null(p_mono_string); } -_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) { - return mono_string_new(mono_domain_get(), p_string.utf8().get_data()); -} - -_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) { - return mono_string_from_utf16((mono_unichar2 *)p_string.c_str()); -} - _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_from_utf16_string(p_string); - } - - return mono_from_utf8_string(p_string); + return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data())); } // Variant diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 9db4a5f3f0..5958bf3cc1 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -44,7 +44,8 @@ if (unlikely(m_exc != nullptr)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ GD_UNREACHABLE(); \ - } + } else \ + ((void)0) namespace GDMonoUtils { @@ -162,20 +163,24 @@ StringName get_native_godot_class_name(GDMonoClass *p_class); #define GD_MONO_BEGIN_RUNTIME_INVOKE \ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \ - _runtime_invoke_count_ref += 1; + _runtime_invoke_count_ref += 1; \ + ((void)0) -#define GD_MONO_END_RUNTIME_INVOKE \ - _runtime_invoke_count_ref -= 1; +#define GD_MONO_END_RUNTIME_INVOKE \ + _runtime_invoke_count_ref -= 1; \ + ((void)0) #define GD_MONO_SCOPE_THREAD_ATTACH \ GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \ - (void)__gdmono__scope__thread__attach__; + (void)__gdmono__scope__thread__attach__; \ + ((void)0) #ifdef DEBUG_ENABLED -#define GD_MONO_ASSERT_THREAD_ATTACHED \ - { CRASH_COND(!GDMonoUtils::is_thread_attached()); } +#define GD_MONO_ASSERT_THREAD_ATTACHED \ + CRASH_COND(!GDMonoUtils::is_thread_attached()); \ + ((void)0) #else -#define GD_MONO_ASSERT_THREAD_ATTACHED +#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0) #endif #endif // GD_MONOUTILS_H diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h index 7fd0d24eb0..e30d9a8abd 100644 --- a/modules/mono/register_types.h +++ b/modules/mono/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef MONO_REGISTER_TYPES_H +#define MONO_REGISTER_TYPES_H + void register_mono_types(); void unregister_mono_types(); + +#endif // MONO_REGISTER_TYPES_H diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index dc542477f5..c76619cca4 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -46,7 +46,7 @@ #define GD_UNREACHABLE() \ CRASH_NOW(); \ do { \ - } while (true); + } while (true) #endif namespace gdmono { diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index e0cf916a01..a619f0b975 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -71,12 +71,12 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) buffer.resize(512); DWORD dwBufferSize = buffer.size(); - LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); if (res == ERROR_MORE_DATA) { // dwBufferSize now contains the actual size buffer.resize(dwBufferSize); - res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); } if (res == ERROR_SUCCESS) { @@ -90,7 +90,7 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) { HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; @@ -127,7 +127,7 @@ LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) { String default_clr; HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ccfaf5aba7..5d1abd0c09 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -54,12 +54,16 @@ String cwd() { #ifdef WINDOWS_ENABLED const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr); - String buffer; + Char16String buffer; buffer.resize((int)expected_size); - if (::GetCurrentDirectoryW(expected_size, buffer.ptrw()) == 0) + if (::GetCurrentDirectoryW(expected_size, (wchar_t *)buffer.ptrw()) == 0) return "."; - return buffer.simplify_path(); + String result; + if (result.parse_utf16(buffer.ptr())) { + return "."; + } + return result.simplify_path(); #else char buffer[PATH_MAX]; if (::getcwd(buffer, sizeof(buffer)) == nullptr) { @@ -86,7 +90,7 @@ String abspath(const String &p_path) { String realpath(const String &p_path) { #ifdef WINDOWS_ENABLED // Open file without read/write access - HANDLE hFile = ::CreateFileW(p_path.c_str(), 0, + HANDLE hFile = ::CreateFileW((LPCWSTR)(p_path.utf16().get_data()), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -100,12 +104,18 @@ String realpath(const String &p_path) { return p_path; } - String buffer; + Char16String buffer; buffer.resize((int)expected_size); - ::GetFinalPathNameByHandleW(hFile, buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED); + ::GetFinalPathNameByHandleW(hFile, (wchar_t *)buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED); ::CloseHandle(hFile); - return buffer.simplify_path(); + + String result; + if (result.parse_utf16(buffer.ptr())) { + return p_path; + } + + return result.simplify_path(); #elif UNIX_ENABLED char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr); @@ -130,7 +140,7 @@ String join(const String &p_a, const String &p_b) { return p_b; } - const CharType a_last = p_a[p_a.length() - 1]; + const char32_t a_last = p_a[p_a.length() - 1]; if ((a_last == '/' || a_last == '\\') || (p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) { return p_a + p_b; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index f8d9804de4..65da4328f6 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -49,7 +49,7 @@ int sfind(const String &p_text, int p_from) { return -1; } - const CharType *src = p_text.c_str(); + const char32_t *src = p_text.get_data(); for (int i = p_from; i <= (len - src_len); i++) { bool found = true; @@ -64,7 +64,7 @@ int sfind(const String &p_text, int p_from) { found = src[read_pos] == '%'; break; case 1: { - CharType c = src[read_pos]; + char32_t c = src[read_pos]; found = src[read_pos] == 's' || (c >= '0' && c <= '4'); break; } @@ -121,7 +121,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const int result = 0; while ((result = sfind(p_text, search_from)) >= 0) { - CharType c = p_text[result + 1]; + char32_t c = p_text[result + 1]; int req_index = (c == 's' ? findex++ : c - '0'); |