summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/math/random_number_generator.h2
-rw-r--r--editor/editor_inspector.cpp4
-rw-r--r--editor/scene_tree_dock.cpp5
-rw-r--r--modules/mono/SCsub5
-rw-r--r--modules/mono/build_scripts/godot_net_sdk_build.py45
-rw-r--r--modules/mono/csharp_script.cpp15
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs11
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs296
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs27
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs13
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp2
-rw-r--r--thirdparty/vulkan/loader/loader.c2
-rw-r--r--thirdparty/vulkan/loader/loader.h3
-rw-r--r--thirdparty/vulkan/loader/vk_loader_platform.h16
-rw-r--r--thirdparty/vulkan/patches/Vulkan-Loader-revert-pr260.patch57
25 files changed, 575 insertions, 28 deletions
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
index 920308e597..416e52ea87 100644
--- a/core/math/random_number_generator.h
+++ b/core/math/random_number_generator.h
@@ -37,9 +37,9 @@
class RandomNumberGenerator : public Reference {
GDCLASS(RandomNumberGenerator, Reference);
+protected:
RandomPCG randbase;
-protected:
static void _bind_methods();
public:
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 601f0ad735..1837b23a0b 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -414,9 +414,9 @@ bool EditorPropertyRevert::get_instanced_node_original_property(Node *p_node, co
node = node->get_owner();
}
- if (!found && node) {
+ if (!found && p_node) {
//if not found, try default class value
- Variant attempt = ClassDB::class_get_default_property_value(node->get_class_name(), p_prop);
+ Variant attempt = ClassDB::class_get_default_property_value(p_node->get_class_name(), p_prop);
if (attempt.get_type() != Variant::NIL) {
found = true;
value = attempt;
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index c4e90ca3ff..46e9cd4676 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1762,6 +1762,8 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
void SceneTreeDock::_script_creation_closed() {
script_create_dialog->disconnect("script_created", callable_mp(this, &SceneTreeDock::_script_created));
+ script_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed));
+ script_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
}
void SceneTreeDock::_toggle_editable_children_from_selection() {
@@ -2651,7 +2653,8 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) {
}
script_create_dialog->connect("script_created", callable_mp(this, &SceneTreeDock::_script_created));
- script_create_dialog->connect("popup_hide", callable_mp(this, &SceneTreeDock::_script_creation_closed), varray(), CONNECT_ONESHOT);
+ script_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed));
+ script_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed));
script_create_dialog->set_inheritance_base_type("Node");
script_create_dialog->config(inherits, path);
script_create_dialog->popup_centered();
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index e8f3174a0a..3b94949470 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -40,6 +40,11 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
godot_tools_build.build(env_mono, api_sln_cmd)
+ # Build Godot.NET.Sdk
+ import build_scripts.godot_net_sdk_build as godot_net_sdk_build
+
+ godot_net_sdk_build.build(env_mono)
+
# Add sources
env_mono.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py
new file mode 100644
index 0000000000..3bfba0f0f6
--- /dev/null
+++ b/modules/mono/build_scripts/godot_net_sdk_build.py
@@ -0,0 +1,45 @@
+# Build Godot.NET.Sdk solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_godot_net_sdk(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env["module_dir"]
+
+ solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
+ build_config = "Release"
+
+ from .solution_builder import build_solution
+
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
+ # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
+
+
+def build(env_mono):
+ assert env_mono["tools"]
+
+ output_dir = Dir("#bin").abspath
+ editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
+ nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
+
+ module_dir = os.getcwd()
+
+ package_version_file = os.path.join(
+ module_dir, "editor", "Godot.NET.Sdk", "Godot.NET.Sdk", "Godot.NET.Sdk_PackageVersion.txt"
+ )
+
+ with open(package_version_file, mode="r") as f:
+ version = f.read().strip()
+
+ target_filenames = ["Godot.NET.Sdk.%s.nupkg" % version]
+
+ targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
+ env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 70b731d611..8928b6e5e3 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -940,8 +940,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Use a placeholder for now to avoid losing the state when saving a scene
- obj->set_script(scr);
-
PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj);
obj->set_script_instance(placeholder);
@@ -968,12 +966,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> script = E->get();
- if (!script->get_path().empty()) {
#ifdef TOOLS_ENABLED
- script->exports_invalidated = true;
+ script->exports_invalidated = true;
#endif
- script->signals_invalidated = true;
+ script->signals_invalidated = true;
+ if (!script->get_path().empty()) {
script->reload(p_soft_reload);
if (!script->valid) {
@@ -1949,6 +1947,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
}
void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+ // Must make sure event signals are not left dangling
disconnect_event_signals();
#ifdef DEBUG_ENABLED
@@ -1964,6 +1963,9 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
CRASH_COND(gchandle.is_released());
#endif
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
r_remove_script_instance = false;
if (_unreference_owner_unsafe()) {
@@ -2223,6 +2225,9 @@ CSharpInstance::~CSharpInstance() {
destructing_script_instance = true;
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
if (!gchandle.is_released()) {
if (!predelete_notified && !ref_dying) {
// This destructor is not called from the owners destructor.
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index cfe070a371..316fd78f2a 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -288,7 +288,7 @@ public:
void mono_object_disposed(MonoObject *p_obj);
/*
- * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
+ * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index 86a0a4393e..8304d9e321 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Description>MSBuild .NET Sdk for Godot projects.</Description>
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.NET.Sdk</PackageId>
<Version>4.0.0</Version>
- <PackageVersion>4.0.0-dev2</PackageVersion>
<PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
<PackageType>MSBuildSdk</PackageType>
<PackageTags>MSBuildSdk</PackageTags>
@@ -19,7 +19,13 @@
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
</PropertyGroup>
- <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
+ <Target Name="ReadGodotNETSdkVersion" BeforeTargets="BeforeBuild;BeforeRebuild;CoreCompile">
+ <PropertyGroup>
+ <PackageVersion>$([System.IO.File]::ReadAllText('$(ProjectDir)Godot.NET.Sdk_PackageVersion.txt').Trim())</PackageVersion>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') " DependsOnTargets="ReadGodotNETSdkVersion">
<PropertyGroup>
<NuspecProperties>
id=$(PackageId);
@@ -32,4 +38,13 @@
</NuspecProperties>
</PropertyGroup>
</Target>
+
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
+ </PropertyGroup>
+ <Copy SourceFiles="$(OutputPath)$(PackageId).$(PackageVersion).nupkg"
+ DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ </Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt
new file mode 100644
index 0000000000..34749489b9
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt
@@ -0,0 +1 @@
+4.0.0-dev3
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index e4d6b2e010..37123ba2b2 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -10,6 +10,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
<!--
The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 01d7c99662..7d49d251dd 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -3,14 +3,13 @@ using System.IO;
using System.Text;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
+using GodotTools.Shared;
namespace GodotTools.ProjectEditor
{
public static class ProjectGenerator
{
- public const string GodotSdkVersionToUse = "4.0.0-dev2";
-
- public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
+ public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
public static ProjectRootElement GenGameProject(string name)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
new file mode 100644
index 0000000000..1d382dcb43
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -0,0 +1,36 @@
+<Project>
+ <!-- Generate C# file with the version of all the nupkgs bundled with Godot -->
+
+ <Target Name="SetPropertiesForGenerateGodotNupkgsVersions">
+ <PropertyGroup>
+ <GodotNETSdkPackageVersionFile>$(SolutionDir)..\Godot.NET.Sdk\Godot.NET.Sdk\Godot.NET.Sdk_PackageVersion.txt</GodotNETSdkPackageVersionFile>
+ <GeneratedGodotNupkgsVersionsFile>$(IntermediateOutputPath)GodotNupkgsVersions.g.cs</GeneratedGodotNupkgsVersionsFile>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="BeforeCompile;CoreCompile">
+ <ItemGroup>
+ <Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ <FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ </ItemGroup>
+ </Target>
+ <Target Name="_GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="SetPropertiesForGenerateGodotNupkgsVersions"
+ Inputs="$(MSBuildProjectFile);@(GodotNETSdkPackageVersionFile)"
+ Outputs="$(GeneratedGodotNupkgsVersionsFile)">
+ <PropertyGroup>
+ <GenerateGodotNupkgsVersionsCode><![CDATA[
+namespace $(RootNamespace) {
+ public class GeneratedGodotNupkgsVersions {
+ public const string GodotNETSdk = "$([System.IO.File]::ReadAllText('$(GodotNETSdkPackageVersionFile)').Trim())"%3b
+ }
+}
+]]></GenerateGodotNupkgsVersionsCode>
+ </PropertyGroup>
+ <WriteLinesToFile Lines="$(GenerateGodotNupkgsVersionsCode)"
+ File="$(GeneratedGodotNupkgsVersionsFile)"
+ Overwrite="True" WriteOnlyWhenDifferent="True" />
+ </Target>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
new file mode 100644
index 0000000000..3bc1698c15
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+ <Import Project="GenerateGodotNupkgsVersions.targets" />
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index ba5379e562..d3107a69db 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,5 +45,9 @@ Global
{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
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 9b512d90f5..b96b0c8175 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -7,7 +7,6 @@ using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
-using Path = System.IO.Path;
namespace GodotTools.Build
{
@@ -209,6 +208,16 @@ namespace GodotTools.Build
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
GenerateEditorScriptMetadata();
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index c22dce005c..708ec73454 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -33,6 +33,16 @@ namespace GodotTools.Build
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return; // No solution to build
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
BuildManager.GenerateEditorScriptMetadata();
if (!BuildManager.BuildProjectBlocking("Debug"))
@@ -54,6 +64,16 @@ namespace GodotTools.Build
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return; // No solution to build
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
BuildManager.GenerateEditorScriptMetadata();
if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"}))
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
new file mode 100644
index 0000000000..793ef7fd71
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -0,0 +1,296 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+using Godot;
+using GodotTools.Internals;
+using GodotTools.Shared;
+using Directory = GodotTools.Utils.Directory;
+using Environment = System.Environment;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public static class NuGetUtils
+ {
+ public const string GodotFallbackFolderName = "Godot Offline Packages";
+
+ public static string GodotFallbackFolderPath
+ => Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
+
+ private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(nuGetConfigPath);
+
+ const string nuGetConfigRootName = "configuration";
+
+ var rootNode = xmlDoc.DocumentElement;
+
+ if (rootNode == null)
+ {
+ // No root node, create it
+ rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
+ xmlDoc.AppendChild(rootNode);
+
+ // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
+ XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
+ rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
+ }
+ else
+ {
+ // Check that the root node is the expected one
+ if (rootNode.Name != nuGetConfigRootName)
+ throw new Exception("Invalid root Xml node for NuGet.Config. " +
+ $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
+ }
+
+ var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
+ rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
+
+ // Check if it already has our fallback package folder
+ for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
+ {
+ if (xmlNode.NodeType != XmlNodeType.Element)
+ continue;
+
+ var xmlElement = (XmlElement)xmlNode;
+ if (xmlElement.Name == "add" &&
+ xmlElement.Attributes["key"]?.Value == name &&
+ xmlElement.Attributes["value"]?.Value == path)
+ {
+ return;
+ }
+ }
+
+ XmlElement newEntry = xmlDoc.CreateElement("add");
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
+
+ fallbackFoldersNode.AppendChild(newEntry);
+
+ xmlDoc.Save(nuGetConfigPath);
+ }
+
+ /// <summary>
+ /// Returns all the paths where the user NuGet.Config files can be found.
+ /// Does not determine whether the returned files exist or not.
+ /// </summary>
+ private static string[] GetAllUserNuGetConfigFilePaths()
+ {
+ // Where to find 'NuGet/NuGet.Config':
+ //
+ // - Mono/.NETFramework (standalone NuGet):
+ // Uses Environment.SpecialFolder.ApplicationData
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$HOME/.config'
+ // - CoreCLR (dotnet CLI NuGet):
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$DOTNET_CLI_HOME/.nuget' otherwise '$HOME/.nuget'
+
+ string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+
+ if (Utils.OS.IsWindows)
+ {
+ // %APPDATA% for both
+ return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
+ }
+
+ var paths = new string[2];
+
+ // CoreCLR (dotnet CLI NuGet)
+
+ string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
+ if (!string.IsNullOrEmpty(dotnetCliHome))
+ {
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ }
+ else
+ {
+ string home = Environment.GetEnvironmentVariable("HOME");
+ if (string.IsNullOrEmpty(home))
+ throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
+ }
+
+ // Mono/.NETFramework (standalone NuGet)
+
+ // ApplicationData is $HOME/.config on Linux/macOS
+ paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+
+ return paths;
+ }
+
+ // nupkg extraction
+ //
+ // Exclude: (NuGet.Client -> NuGet.Packaging.PackageHelper.ExcludePaths)
+ // package/
+ // _rels/
+ // [Content_Types].xml
+ //
+ // Don't ignore files that begin with a dot (.)
+ //
+ // The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
+
+ /// <summary>
+ /// Adds the specified fallback folder to the user NuGet.Config files,
+ /// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
+ /// </summary>
+ public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ {
+ foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ {
+ if (!System.IO.File.Exists(nuGetConfigPath))
+ {
+ // It doesn't exist, so we create a default one
+ const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
+<configuration>
+ <packageSources>
+ <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
+ </packageSources>
+</configuration>
+";
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
+ }
+
+ AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ }
+ }
+
+ private static void AddPackageToFallbackFolder(string fallbackFolder,
+ string nupkgPath, string packageId, string packageVersion)
+ {
+ // dotnet CLI provides no command for this, but we can do it manually.
+ //
+ // - The expected structure is as follows:
+ // fallback_folder/
+ // <package.name>/<version>/
+ // <package.name>.<version>.nupkg
+ // <package.name>.<version>.nupkg.sha512
+ // <package.name>.nuspec
+ // ... extracted nupkg files (check code for excluded files) ...
+ //
+ // - <package.name> and <version> must be in lower case.
+ // - The sha512 of the nupkg is base64 encoded.
+ // - We can get the nuspec from the nupkg which is a Zip file.
+
+ string packageIdLower = packageId.ToLower();
+ string packageVersionLower = packageVersion.ToLower();
+
+ string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
+ string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
+ string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+
+ if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
+ return; // Already added (for speed we don't check if every file is properly extracted)
+
+ Directory.CreateDirectory(destDir);
+
+ // Generate .nupkg.sha512 file
+
+ using (var alg = SHA512.Create())
+ {
+ alg.ComputeHash(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(alg.Hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+ }
+
+ // Extract nupkg
+ ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
+
+ // Copy .nupkg
+ File.Copy(nupkgPath, nupkgDestPath);
+ }
+
+ private static readonly string[] NupkgExcludePaths =
+ {
+ "_rels/",
+ "package/",
+ "[Content_Types].xml"
+ };
+
+ private static void ExtractNupkg(string destDir, string nupkgPath, string packageId, string packageVersion)
+ {
+ // NOTE: Must use SimplifyGodotPath to make sure we don't extract files outside the destination directory.
+
+ using (var archive = ZipFile.OpenRead(nupkgPath))
+ {
+ // Extract .nuspec manually as it needs to be in lower case
+
+ var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
+
+ if (nuspecEntry == null)
+ throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+
+ // Extract the other package files
+
+ foreach (var entry in archive.Entries)
+ {
+ // NOTE: SimplifyGodotPath() removes trailing slash and backslash,
+ // so we can't use the result to check if the entry is a directory.
+
+ string entryFullName = entry.FullName.Replace('\\', '/');
+
+ // Check if the file must be ignored
+ if ( // Excluded files.
+ NupkgExcludePaths.Any(e => entryFullName.StartsWith(e, StringComparison.OrdinalIgnoreCase)) ||
+ // Nupkg hash files and nupkg metadata files on all directory.
+ entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
+ entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
+ // Nuspec at root level. We already extracted it previously but in lower case.
+ entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
+ {
+ continue;
+ }
+
+ string entryFullNameSimplified = entryFullName.SimplifyGodotPath();
+ string destFilePath = Path.Combine(destDir, entryFullNameSimplified);
+ bool isDir = entryFullName.EndsWith("/");
+
+ if (isDir)
+ {
+ Directory.CreateDirectory(destFilePath);
+ }
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
+ entry.ExtractToFile(destFilePath, overwrite: true);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies and extracts all the Godot bundled packages to the Godot NuGet fallback folder.
+ /// Does nothing if the packages were already copied.
+ /// </summary>
+ public static void AddBundledPackagesToFallbackFolder(string fallbackFolder)
+ {
+ GD.Print("Copying Godot Offline Packages...");
+
+ string nupkgsLocation = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "nupkgs");
+
+ void AddPackage(string packageId, string packageVersion)
+ {
+ string nupkgPath = Path.Combine(nupkgsLocation, $"{packageId}.{packageVersion}.nupkg");
+ AddPackageToFallbackFolder(fallbackFolder, nupkgPath, packageId, packageVersion);
+ }
+
+ foreach (var (packageId, packageVersion) in PackagesToAdd)
+ AddPackage(packageId, packageVersion);
+ }
+
+ private static readonly (string packageId, string packageVersion)[] PackagesToAdd =
+ {
+ ("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk)
+ };
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index f5c99e87ef..58561c5097 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -147,6 +147,21 @@ namespace GodotTools
case MenuOptions.AboutCSharp:
_ShowAboutDialog();
break;
+ case MenuOptions.SetupGodotNugetFallbackFolder:
+ {
+ try
+ {
+ string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
+ }
+ catch (Exception e)
+ {
+ ShowErrorDialog("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ break;
+ }
default:
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
}
@@ -183,6 +198,7 @@ namespace GodotTools
{
CreateSln,
AboutCSharp,
+ SetupGodotNugetFallbackFolder,
}
public void ShowErrorDialog(string message, string title = "Error")
@@ -426,6 +442,7 @@ namespace GodotTools
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
{
menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp);
+ menuPopup.AddItem("Setup Godot NuGet Offline Packages".TTR(), (int)MenuOptions.SetupGodotNugetFallbackFolder);
aboutDialog = new AcceptDialog();
editorBaseControl.AddChild(aboutDialog);
aboutDialog.Title = "Important: C# support is not feature-complete";
@@ -535,6 +552,16 @@ namespace GodotTools
exportPlugin.RegisterExportSettings();
exportPluginWeak = WeakRef(exportPlugin);
+ try
+ {
+ // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to add Godot NuGet Offline Packages to NuGet.Config: " + e.Message);
+ }
+
BuildManager.Initialize();
RiderPathManager.Initialize();
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a17c371117..4c137a2ba4 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -97,7 +97,7 @@
#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(12)
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
@@ -1368,7 +1368,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.append("." + ctor_method);
- output.append("(this);\n" CLOSE_BLOCK_L2);
+ output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
output.append(MEMBER_BEGIN "internal ");
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 42610c5ef7..48582d5ad8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -15,14 +15,13 @@ namespace Godot
public Object() : this(false)
{
if (ptr == IntPtr.Zero)
- {
ptr = godot_icall_Object_Ctor(this);
- }
- else
- {
- // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
- godot_icall_Object_ConnectEventSignals(ptr);
- }
+ _InitializeGodotScriptInstanceInternals();
+ }
+
+ internal void _InitializeGodotScriptInstanceInternals()
+ {
+ godot_icall_Object_ConnectEventSignals(ptr);
}
internal Object(bool memoryOwn)
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index fe1c2d28dd..0ed9e441ef 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -109,8 +109,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script, csharp_instance);
-
- csharp_instance->connect_event_signals();
}
void unhandled_exception(MonoException *p_exc) {
diff --git a/thirdparty/vulkan/loader/loader.c b/thirdparty/vulkan/loader/loader.c
index 418b88e30a..a6b5522cd4 100644
--- a/thirdparty/vulkan/loader/loader.c
+++ b/thirdparty/vulkan/loader/loader.c
@@ -7849,7 +7849,7 @@ out:
return result;
}
-#if defined(_WIN32)
+#if defined(_WIN32) && defined(LOADER_DYNAMIC_LIB)
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
diff --git a/thirdparty/vulkan/loader/loader.h b/thirdparty/vulkan/loader/loader.h
index 621eb0be3b..a3c1f6b635 100644
--- a/thirdparty/vulkan/loader/loader.h
+++ b/thirdparty/vulkan/loader/loader.h
@@ -428,6 +428,9 @@ static inline void loader_init_dispatch(void *obj, const void *data) {
// Global variables used across files
extern struct loader_struct loader;
extern THREAD_LOCAL_DECL struct loader_instance *tls_instance;
+#if defined(_WIN32) && !defined(LOADER_DYNAMIC_LIB)
+extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_init);
+#endif
extern loader_platform_thread_mutex loader_lock;
extern loader_platform_thread_mutex loader_json_lock;
extern loader_platform_thread_mutex loader_preload_icd_lock;
diff --git a/thirdparty/vulkan/loader/vk_loader_platform.h b/thirdparty/vulkan/loader/vk_loader_platform.h
index e227bf07b2..b7664f50a1 100644
--- a/thirdparty/vulkan/loader/vk_loader_platform.h
+++ b/thirdparty/vulkan/loader/vk_loader_platform.h
@@ -368,9 +368,25 @@ typedef HANDLE loader_platform_thread;
// The once init functionality is not used when building a DLL on Windows. This is because there is no way to clean up the
// resources allocated by anything allocated by once init. This isn't a problem for static libraries, but it is for dynamic
// ones. When building a DLL, we use DllMain() instead to allow properly cleaning up resources.
+#if defined(LOADER_DYNAMIC_LIB)
#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
#define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var)
#define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
+#else
+#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) INIT_ONCE var = INIT_ONCE_STATIC_INIT;
+#define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var) INIT_ONCE var;
+#define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func)
+static BOOL CALLBACK InitFuncWrapper(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) {
+ void (*func)(void) = (void (*)(void))Parameter;
+ func();
+ return TRUE;
+}
+static void loader_platform_thread_once_fn(void *ctl, void (*func)(void)) {
+ assert(func != NULL);
+ assert(ctl != NULL);
+ InitOnceExecuteOnce((PINIT_ONCE)ctl, InitFuncWrapper, (void *)func, NULL);
+}
+#endif
// Thread IDs:
typedef DWORD loader_platform_thread_id;
diff --git a/thirdparty/vulkan/patches/Vulkan-Loader-revert-pr260.patch b/thirdparty/vulkan/patches/Vulkan-Loader-revert-pr260.patch
new file mode 100644
index 0000000000..1267e560df
--- /dev/null
+++ b/thirdparty/vulkan/patches/Vulkan-Loader-revert-pr260.patch
@@ -0,0 +1,57 @@
+diff --git a/thirdparty/vulkan/loader/loader.c b/thirdparty/vulkan/loader/loader.c
+index 87d08d5116..c7cdb47122 100644
+--- a/thirdparty/vulkan/loader/loader.c
++++ b/thirdparty/vulkan/loader/loader.c
+@@ -7330,7 +7330,7 @@ out:
+ return result;
+ }
+
+-#if defined(_WIN32)
++#if defined(_WIN32) && defined(LOADER_DYNAMIC_LIB)
+ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+diff --git a/thirdparty/vulkan/loader/loader.h b/thirdparty/vulkan/loader/loader.h
+index 5e9495521b..56745a968d 100644
+--- a/thirdparty/vulkan/loader/loader.h
++++ b/thirdparty/vulkan/loader/loader.h
+@@ -420,6 +420,9 @@ static inline void loader_init_dispatch(void *obj, const void *data) {
+ // Global variables used across files
+ extern struct loader_struct loader;
+ extern THREAD_LOCAL_DECL struct loader_instance *tls_instance;
++#if defined(_WIN32) && !defined(LOADER_DYNAMIC_LIB)
++extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_init);
++#endif
+ extern loader_platform_thread_mutex loader_lock;
+ extern loader_platform_thread_mutex loader_json_lock;
+
+diff --git a/thirdparty/vulkan/loader/vk_loader_platform.h b/thirdparty/vulkan/loader/vk_loader_platform.h
+index 7824e35d6b..62e8e3ae09 100644
+--- a/thirdparty/vulkan/loader/vk_loader_platform.h
++++ b/thirdparty/vulkan/loader/vk_loader_platform.h
+@@ -330,9 +330,25 @@ typedef HANDLE loader_platform_thread;
+ // The once init functionality is not used when building a DLL on Windows. This is because there is no way to clean up the
+ // resources allocated by anything allocated by once init. This isn't a problem for static libraries, but it is for dynamic
+ // ones. When building a DLL, we use DllMain() instead to allow properly cleaning up resources.
++#if defined(LOADER_DYNAMIC_LIB)
+ #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
+ #define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var)
+ #define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
++#else
++#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) INIT_ONCE var = INIT_ONCE_STATIC_INIT;
++#define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var) INIT_ONCE var;
++#define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func)
++static BOOL CALLBACK InitFuncWrapper(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) {
++ void (*func)(void) = (void (*)(void))Parameter;
++ func();
++ return TRUE;
++}
++static void loader_platform_thread_once_fn(void *ctl, void (*func)(void)) {
++ assert(func != NULL);
++ assert(ctl != NULL);
++ InitOnceExecuteOnce((PINIT_ONCE)ctl, InitFuncWrapper, (void *)func, NULL);
++}
++#endif
+
+ // Thread IDs:
+ typedef DWORD loader_platform_thread_id;