summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--modules/gdscript/gdscript_cache.cpp4
-rw-r--r--modules/gltf/gltf_document.cpp2
-rw-r--r--modules/gltf/register_types.cpp5
-rw-r--r--modules/mono/csharp_script.cpp89
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs18
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs125
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp34
-rw-r--r--modules/mono/mono_gd/gd_mono.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h8
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp13
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp3
-rw-r--r--modules/svg/image_loader_svg.cpp14
-rw-r--r--modules/svg/image_loader_svg.h4
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml39
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml10
-rw-r--r--modules/webrtc/library_godot_webrtc.js196
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp13
-rw-r--r--modules/webrtc/webrtc_peer_connection.h19
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp2
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h2
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp23
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h14
29 files changed, 418 insertions, 252 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 4a38caea52..4f325fcf52 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -35,14 +35,14 @@
<description>
Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
[b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
- The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
+ The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. It must be a static string, so format strings can't be used. You can use this to provide additional details about why the assertion failed.
[codeblock]
# Imagine we always want speed to be between 0 and 20.
var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
- assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
+ assert(speed &lt; 20, "the speed limit is 20") # Show a message
[/codeblock]
</description>
</method>
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 10babad378..1cff2181af 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1077,10 +1077,12 @@ Error GDScript::load_source_code(const String &p_path) {
}
source = s;
+ path = p_path;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
-#endif
- path = p_path;
+ set_edited(false);
+ set_last_modified_time(FileAccess::get_modified_time(path));
+#endif // TOOLS_ENABLED
return OK;
}
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 48d5fbc569..c25f5b58d5 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -146,9 +146,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
Vector<uint8_t> source_file;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (err) {
- ERR_FAIL_COND_V(err, "");
- }
+ ERR_FAIL_COND_V(err, "");
uint64_t len = f->get_length();
source_file.resize(len + 1);
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 6cb398b5f8..f5730e7137 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -2673,7 +2673,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
} else if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true);
- ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(joints_0.size() != joints_1.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
Vector<int> joints;
joints.resize(vertex_num * weight_8_count);
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index dbbccc9bcc..6e7f7d6fed 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -142,6 +142,11 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
+ // Can't (a priori) run external app on these platforms.
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.web", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.web", false);
ClassDB::set_current_api(prev_api);
EditorNode::add_init_callback(_editor_init);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 990a95821e..8fd3626a20 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2035,6 +2035,52 @@ void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values,
}
#endif
+void GD_CLR_STDCALL CSharpScript::_add_property_info_list_callback(CSharpScript *p_script, const String *p_current_class_name, void *p_props, int32_t p_count) {
+ GDMonoCache::godotsharp_property_info *props = (GDMonoCache::godotsharp_property_info *)p_props;
+
+#ifdef TOOLS_ENABLED
+ p_script->exported_members_cache.push_back(PropertyInfo(
+ Variant::NIL, *p_current_class_name, PROPERTY_HINT_NONE,
+ p_script->get_path(), PROPERTY_USAGE_CATEGORY));
+#endif
+
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_info &prop = props[i];
+
+ StringName name = *reinterpret_cast<const StringName *>(&prop.name);
+ String hint_string = *reinterpret_cast<const String *>(&prop.hint_string);
+
+ PropertyInfo pinfo(prop.type, name, prop.hint, hint_string, prop.usage);
+
+ p_script->member_info[name] = pinfo;
+
+ if (prop.exported) {
+#ifdef TOOLS_ENABLED
+ p_script->exported_members_cache.push_back(pinfo);
+#endif
+
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ p_script->exported_members_names.insert(name);
+#endif
+ }
+ }
+}
+
+#ifdef TOOLS_ENABLED
+void GD_CLR_STDCALL CSharpScript::_add_property_default_values_callback(CSharpScript *p_script, void *p_def_vals, int32_t p_count) {
+ GDMonoCache::godotsharp_property_def_val_pair *def_vals = (GDMonoCache::godotsharp_property_def_val_pair *)p_def_vals;
+
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_def_val_pair &def_val_pair = def_vals[i];
+
+ StringName name = *reinterpret_cast<const StringName *>(&def_val_pair.name);
+ Variant value = *reinterpret_cast<const Variant *>(&def_val_pair.value);
+
+ p_script->exported_members_defval_cache[name] = value;
+ }
+}
+#endif
+
bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
bool is_editor = Engine::get_singleton()->is_editor_hint();
@@ -2066,49 +2112,10 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
#endif
if (GDMonoCache::godot_api_cache_updated) {
- GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyInfoList(this,
- [](CSharpScript *p_script, const String *p_current_class_name, GDMonoCache::godotsharp_property_info *p_props, int32_t p_count) {
-#ifdef TOOLS_ENABLED
- p_script->exported_members_cache.push_back(PropertyInfo(
- Variant::NIL, *p_current_class_name, PROPERTY_HINT_NONE,
- p_script->get_path(), PROPERTY_USAGE_CATEGORY));
-#endif
-
- for (int i = 0; i < p_count; i++) {
- const GDMonoCache::godotsharp_property_info &prop = p_props[i];
-
- StringName name = *reinterpret_cast<const StringName *>(&prop.name);
- String hint_string = *reinterpret_cast<const String *>(&prop.hint_string);
-
- PropertyInfo pinfo(prop.type, name, prop.hint, hint_string, prop.usage);
-
- p_script->member_info[name] = pinfo;
-
- if (prop.exported) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyInfoList(this, &_add_property_info_list_callback);
#ifdef TOOLS_ENABLED
- p_script->exported_members_cache.push_back(pinfo);
-#endif
-
-#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- p_script->exported_members_names.insert(name);
-#endif
- }
- }
- });
-
-#ifdef TOOLS_ENABLED
- GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyDefaultValues(this,
- [](CSharpScript *p_script, GDMonoCache::godotsharp_property_def_val_pair *p_def_vals, int32_t p_count) {
- for (int i = 0; i < p_count; i++) {
- const GDMonoCache::godotsharp_property_def_val_pair &def_val_pair = p_def_vals[i];
-
- StringName name = *reinterpret_cast<const StringName *>(&def_val_pair.name);
- Variant value = *reinterpret_cast<const Variant *>(&def_val_pair.value);
-
- p_script->exported_members_defval_cache[name] = value;
- }
- });
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyDefaultValues(this, &_add_property_default_values_callback);
#endif
}
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f2844a051d..d469c28d4a 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -133,6 +133,10 @@ class CSharpScript : public Script {
void _clear();
+ static void GD_CLR_STDCALL _add_property_info_list_callback(CSharpScript *p_script, const String *p_current_class_name, void *p_props, int32_t p_count);
+#ifdef TOOLS_ENABLED
+ static void GD_CLR_STDCALL _add_property_default_values_callback(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
+#endif
bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
index bff9760b32..859ea52c93 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -17,7 +17,7 @@
<!-- C# source generators -->
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
- <PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
+ <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
</ItemGroup>
<!-- Godot API references -->
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
index 4baae77b34..37bd4a0be0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -21,10 +21,13 @@
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
<PropertyGroup>
<GenerateGodotNupkgsVersionsCode><![CDATA[
-namespace $(RootNamespace) {
- public class GeneratedGodotNupkgsVersions {
+namespace $(RootNamespace)
+{
+ public class GeneratedGodotNupkgsVersions
+ {
public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
+ public const string GodotSharp = "$(PackageVersion_GodotSharp)"%3b
}
}
]]></GenerateGodotNupkgsVersionsCode>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
index 7bce53308c..b437c7e742 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Runtime.InteropServices;
using JetBrains.Annotations;
using OS = GodotTools.Utils.OS;
@@ -16,6 +17,23 @@ namespace GodotTools.Build
// In the future, this method may do more than just search in PATH. We could look in
// known locations or use Godot's linked nethost to search from the hostfxr location.
+ if (OS.IsMacOS)
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.X64)
+ {
+ string dotnet_x64 = "/usr/local/share/dotnet/x64/dotnet"; // Look for x64 version, when running under Rosetta 2.
+ if (File.Exists(dotnet_x64))
+ {
+ return dotnet_x64;
+ }
+ }
+ string dotnet = "/usr/local/share/dotnet/dotnet"; // Look for native version.
+ if (File.Exists(dotnet))
+ {
+ return dotnet;
+ }
+ }
+
return OS.PathWhich("dotnet");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 4041026426..237ac85267 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -122,7 +122,7 @@ namespace GodotTools.Build
{
base._Ready();
- CustomMinimumSize = new Vector2(0, 228) * EditorScale;
+ CustomMinimumSize = new Vector2i(0, (int)(228 * EditorScale));
SizeFlagsVertical = (int)SizeFlags.ExpandFill;
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index d2e0e128b5..fe309b8102 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -22,71 +22,13 @@ namespace GodotTools.Build
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 FormatException("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.
+ /// Returns all the paths where the Godot.Offline.Config files can be found.
/// Does not determine whether the returned files exist or not.
/// </summary>
- private static string[] GetAllUserNuGetConfigFilePaths()
+ private static string[] GetAllGodotNuGetConfigFilePaths()
{
- // Where to find 'NuGet/NuGet.Config':
+ // Where to find 'NuGet/config/Godot.Offline.Config':
//
// - Mono/.NETFramework (standalone NuGet):
// Uses Environment.SpecialFolder.ApplicationData
@@ -98,10 +40,12 @@ namespace GodotTools.Build
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ const string configFileName = "Godot.Offline.Config";
+
if (Utils.OS.IsWindows)
{
// %APPDATA% for both
- return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
+ return new[] { Path.Combine(applicationData, "NuGet", "config", configFileName) };
}
var paths = new string[2];
@@ -111,20 +55,20 @@ namespace GodotTools.Build
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
if (!string.IsNullOrEmpty(dotnetCliHome))
{
- paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "config", configFileName);
}
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");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "config", configFileName);
}
// Mono/.NETFramework (standalone NuGet)
// ApplicationData is $HOME/.config on Linux/macOS
- paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+ paths[1] = Path.Combine(applicationData, "NuGet", "config", configFileName);
return paths;
}
@@ -141,28 +85,26 @@ namespace GodotTools.Build
// 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,
+ /// Adds the specified fallback folder to the Godot.Offline.Config files,
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
/// </summary>
- public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ public static void AddFallbackFolderToGodotNuGetConfigs(string name, string path)
{
- foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ // Make sure the fallback folder exists to avoid error:
+ // MSB4018: The "ResolvePackageAssets" task failed unexpectedly.
+ System.IO.Directory.CreateDirectory(path);
+
+ foreach (string nuGetConfigPath in GetAllGodotNuGetConfigFilePaths())
{
- 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""?>
+ 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>
+ <fallbackPackageFolders>
+ <add key=""{name}"" value=""{path}"" />
+ </fallbackPackageFolders>
</configuration>
";
- System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
- System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
- }
-
- AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
}
}
@@ -189,6 +131,7 @@ namespace GodotTools.Build
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+ string nupkgMetadataDestPath = Path.Combine(destDir, ".nupkg.metadata");
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
return; // Already added (for speed we don't check if every file is properly extracted)
@@ -197,12 +140,18 @@ namespace GodotTools.Build
// Generate .nupkg.sha512 file
- using (var alg = SHA512.Create())
- {
- alg.ComputeHash(File.ReadAllBytes(nupkgPath));
- string base64Hash = Convert.ToBase64String(alg.Hash);
- File.WriteAllText(nupkgSha512DestPath, base64Hash);
- }
+ byte[] hash = SHA512.HashData(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+
+ // Generate .nupkg.metadata file
+ // Spec: https://github.com/NuGet/Home/wiki/Nupkg-Metadata-File
+
+ File.WriteAllText(nupkgMetadataDestPath, @$"{{
+ ""version"": 2,
+ ""contentHash"": ""{base64Hash}"",
+ ""source"": null
+}}");
// Extract nupkg
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
@@ -251,7 +200,7 @@ namespace GodotTools.Build
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"))
+ !entryFullName.Contains('/') && entryFullName.EndsWith(".nuspec"))
{
continue;
}
@@ -297,6 +246,8 @@ namespace GodotTools.Build
{
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
+ ("GodotSharp", GeneratedGodotNupkgsVersions.GodotSharp),
+ ("GodotSharpEditor", GeneratedGodotNupkgsVersions.GodotSharp),
};
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 1cfaea3ec9..89364d1c02 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -123,7 +123,7 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
@@ -497,7 +497,7 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index c27bb959fe..ce4ac9b796 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2229,6 +2229,26 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate signal
{
+ if (p_isignal.is_deprecated) {
+ if (p_isignal.deprecation_message.is_empty()) {
+ WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+ }
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_isignal.deprecation_message);
+ p_output.append("\")]");
+ }
+
+ String delegate_name = p_isignal.proxy_name;
+ delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler
+
+ // Generate delegate
+ p_output.append(MEMBER_BEGIN "public delegate void ");
+ p_output.append(delegate_name);
+ p_output.append("(");
+ p_output.append(arguments_sig);
+ p_output.append(");\n");
+
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -2247,25 +2267,11 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.is_empty()) {
- WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
- }
-
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
p_output.append(p_isignal.deprecation_message);
p_output.append("\")]");
}
- String delegate_name = p_isignal.proxy_name;
- delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler
-
- // Generate delegate
- p_output.append(MEMBER_BEGIN "public delegate void ");
- p_output.append(delegate_name);
- p_output.append("(");
- p_output.append(arguments_sig);
- p_output.append(");\n");
-
// TODO:
// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 43811a4325..21252a5dca 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -35,11 +35,13 @@
#include "../godotsharp_defs.h"
+#ifndef GD_CLR_STDCALL
#ifdef WIN32
#define GD_CLR_STDCALL __stdcall
#else
#define GD_CLR_STDCALL
#endif
+#endif
namespace gdmono {
@@ -56,8 +58,6 @@ struct PluginCallbacks {
} // namespace gdmono
-#undef GD_CLR_STDCALL
-
class GDMono {
bool runtime_initialized;
bool finalizing_scripts_domain;
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index ca3a6c95a7..13b599fe55 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -47,11 +47,13 @@ class CSharpScript;
namespace GDMonoCache {
+#ifndef GD_CLR_STDCALL
#ifdef WIN32
#define GD_CLR_STDCALL __stdcall
#else
#define GD_CLR_STDCALL
#endif
+#endif
struct godotsharp_property_info {
godot_string_name name; // Not owned
@@ -68,8 +70,8 @@ struct godotsharp_property_def_val_pair {
};
struct ManagedCallbacks {
- using Callback_ScriptManagerBridge_GetPropertyInfoList_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, const String *, godotsharp_property_info *p_props, int32_t p_count);
- using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, godotsharp_property_def_val_pair *p_def_vals, int32_t p_count);
+ using Callback_ScriptManagerBridge_GetPropertyInfoList_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, const String *, void *p_props, int32_t p_count);
+ using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *);
using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, uint32_t, const Variant *);
@@ -145,6 +147,4 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks);
} // namespace GDMonoCache
-#undef GD_CLR_STDCALL
-
#endif // GD_MONO_CACHE_H
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 6f60318b3b..d46972ffb6 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -91,9 +91,6 @@ void MultiplayerSpawner::add_spawnable_scene(const String &p_path) {
sc.path = p_path;
if (Engine::get_singleton()->is_editor_hint()) {
ERR_FAIL_COND(!FileAccess::exists(p_path));
- } else {
- sc.cache = ResourceLoader::load(p_path);
- ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path);
}
spawnable_scenes.push_back(sc);
}
@@ -101,6 +98,7 @@ int MultiplayerSpawner::get_spawnable_scene_count() const {
return spawnable_scenes.size();
}
String MultiplayerSpawner::get_spawnable_scene(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, (int)spawnable_scenes.size(), "");
return spawnable_scenes[p_idx].path;
}
void MultiplayerSpawner::clear_spawnable_scenes() {
@@ -270,9 +268,12 @@ const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const
Node *MultiplayerSpawner::instantiate_scene(int p_id) {
ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr);
- Ref<PackedScene> scene = spawnable_scenes[p_id].cache;
- ERR_FAIL_COND_V(scene.is_null(), nullptr);
- return scene->instantiate();
+ SpawnableScene &sc = spawnable_scenes[p_id];
+ if (sc.cache.is_null()) {
+ sc.cache = ResourceLoader::load(sc.path);
+ }
+ ERR_FAIL_COND_V_MSG(sc.cache.is_null(), nullptr, "Invalid spawnable scene: " + sc.path);
+ return sc.cache->instantiate();
}
Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index cfb8e0cd42..f989fc45a5 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -209,6 +209,9 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
for (uint32_t shape_owner : shape_owners) {
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
for (int i = 0; i < shape_count; i++) {
+ if (static_body->is_shape_owner_disabled(i)) {
+ continue;
+ }
Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i);
if (s.is_null()) {
continue;
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 5f839bd979..cd6081f91b 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -35,6 +35,12 @@
#include <thorvg.h>
+HashMap<Color, Color> ImageLoaderSVG::forced_color_map = HashMap<Color, Color>();
+
+void ImageLoaderSVG::set_forced_color_map(const HashMap<Color, Color> &p_color_map) {
+ forced_color_map = p_color_map;
+}
+
void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string) {
// Replace colors in the SVG based on what is passed in `p_color_map`.
// Used to change the colors of editor icons based on the used theme.
@@ -138,7 +144,13 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const
Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) {
String svg = p_fileaccess->get_as_utf8_string();
- create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>());
+
+ if (p_flags & FLAG_CONVERT_COLORS) {
+ create_image_from_string(p_image, svg, p_scale, false, forced_color_map);
+ } else {
+ create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>());
+ }
+
ERR_FAIL_COND_V(p_image->is_empty(), FAILED);
if (p_flags & FLAG_FORCE_LINEAR) {
p_image->srgb_to_linear();
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index fc89b63edb..e6f73ab18f 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -34,9 +34,13 @@
#include "core/io/image_loader.h"
class ImageLoaderSVG : public ImageFormatLoader {
+ static HashMap<Color, Color> forced_color_map;
+
void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
public:
+ static void set_forced_color_map(const HashMap<Color, Color> &p_color_map);
+
void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags, float p_scale) override;
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index e99aeb4f51..4ecc71ddbb 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -67,6 +67,18 @@
Returns the connection state. See [enum ConnectionState].
</description>
</method>
+ <method name="get_gathering_state" qualifiers="const">
+ <return type="int" enum="WebRTCPeerConnection.GatheringState" />
+ <description>
+ Returns the ICE [enum GatheringState] of the connection. This lets you detect, for example, when collection of ICE candidates has finished.
+ </description>
+ </method>
+ <method name="get_signaling_state" qualifiers="const">
+ <return type="int" enum="WebRTCPeerConnection.SignalingState" />
+ <description>
+ Returns the [enum SignalingState] on the local end of the connection while connecting or reconnecting to another peer.
+ </description>
+ </method>
<method name="initialize">
<return type="int" enum="Error" />
<param index="0" name="configuration" type="Dictionary" default="{}" />
@@ -165,5 +177,32 @@
<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
The peer connection is closed (after calling [method close] for example).
</constant>
+ <constant name="GATHERING_STATE_NEW" value="0" enum="GatheringState">
+ The peer connection was just created and hasn't done any networking yet.
+ </constant>
+ <constant name="GATHERING_STATE_GATHERING" value="1" enum="GatheringState">
+ The ICE agent is in the process of gathering candidates for the connection.
+ </constant>
+ <constant name="GATHERING_STATE_COMPLETE" value="2" enum="GatheringState">
+ The ICE agent has finished gathering candidates. If something happens that requires collecting new candidates, such as a new interface being added or the addition of a new ICE server, the state will revert to gathering to gather those candidates.
+ </constant>
+ <constant name="SIGNALING_STATE_STABLE" value="0" enum="SignalingState">
+ There is no ongoing exchange of offer and answer underway. This may mean that the [WebRTCPeerConnection] is new ([constant STATE_NEW]) or that negotiation is complete and a connection has been established ([constant STATE_CONNECTED]).
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_LOCAL_OFFER" value="1" enum="SignalingState">
+ The local peer has called [method set_local_description], passing in SDP representing an offer (usually created by calling [method create_offer]), and the offer has been applied successfully.
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_REMOTE_OFFER" value="2" enum="SignalingState">
+ The remote peer has created an offer and used the signaling server to deliver it to the local peer, which has set the offer as the remote description by calling [method set_remote_description].
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_LOCAL_PRANSWER" value="3" enum="SignalingState">
+ The offer sent by the remote peer has been applied and an answer has been created and applied by calling [method set_local_description]. This provisional answer describes the supported media formats and so forth, but may not have a complete set of ICE candidates included. Further candidates will be delivered separately later.
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_REMOTE_PRANSWER" value="4" enum="SignalingState">
+ A provisional answer has been received and successfully applied in response to an offer previously sent and established by calling [method set_local_description].
+ </constant>
+ <constant name="SIGNALING_STATE_CLOSED" value="5" enum="SignalingState">
+ The [WebRTCPeerConnection] has been closed.
+ </constant>
</constants>
</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
index 3c4bf18a76..474d2f6a89 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -37,6 +37,16 @@
<description>
</description>
</method>
+ <method name="_get_gathering_state" qualifiers="virtual const">
+ <return type="int" enum="WebRTCPeerConnection.GatheringState" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_signaling_state" qualifiers="virtual const">
+ <return type="int" enum="WebRTCPeerConnection.SignalingState" />
+ <description>
+ </description>
+ </method>
<method name="_initialize" qualifiers="virtual">
<return type="int" enum="Error" />
<param index="0" name="p_config" type="Dictionary" />
diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js
index e57e4299e0..e6604eecd7 100644
--- a/modules/webrtc/library_godot_webrtc.js
+++ b/modules/webrtc/library_godot_webrtc.js
@@ -220,64 +220,123 @@ mergeInto(LibraryManager.library, GodotRTCDataChannel);
const GodotRTCPeerConnection = {
$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
$GodotRTCPeerConnection: {
- onstatechange: function (p_id, p_conn, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
- return;
- }
- let state;
- switch (p_conn.iceConnectionState) {
- case 'new':
- state = 0;
- break;
- case 'checking':
- state = 1;
- break;
- case 'connected':
- case 'completed':
- state = 2;
- break;
- case 'disconnected':
- state = 3;
- break;
- case 'failed':
- state = 4;
- break;
- case 'closed':
- default:
- state = 5;
- break;
- }
- callback(state);
+ // Enums
+ ConnectionState: {
+ 'new': 0,
+ 'connecting': 1,
+ 'connected': 2,
+ 'disconnected': 3,
+ 'failed': 4,
+ 'closed': 5,
},
- onicecandidate: function (p_id, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref || !event.candidate) {
- return;
+ ConnectionStateCompat: {
+ // Using values from IceConnectionState for browsers that do not support ConnectionState (notably Firefox).
+ 'new': 0,
+ 'checking': 1,
+ 'connected': 2,
+ 'completed': 2,
+ 'disconnected': 3,
+ 'failed': 4,
+ 'closed': 5,
+ },
+
+ IceGatheringState: {
+ 'new': 0,
+ 'gathering': 1,
+ 'complete': 2,
+ },
+
+ SignalingState: {
+ 'stable': 0,
+ 'have-local-offer': 1,
+ 'have-remote-offer': 2,
+ 'have-local-pranswer': 3,
+ 'have-remote-pranswer': 4,
+ 'closed': 5,
+ },
+
+ // Callbacks
+ create: function (config, onConnectionChange, onSignalingChange, onIceGatheringChange, onIceCandidate, onDataChannel) {
+ let conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ GodotRuntime.error(e);
+ return 0;
}
- const c = event.candidate;
- const candidate_str = GodotRuntime.allocString(c.candidate);
- const mid_str = GodotRuntime.allocString(c.sdpMid);
- callback(mid_str, c.sdpMLineIndex, candidate_str);
- GodotRuntime.free(candidate_str);
- GodotRuntime.free(mid_str);
+ const id = IDHandler.add(conn);
+
+ if ('connectionState' in conn && conn['connectionState'] !== undefined) {
+ // Use "connectionState" if supported
+ conn.onconnectionstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onConnectionChange(GodotRTCPeerConnection.ConnectionState[conn.connectionState] || 0);
+ };
+ } else {
+ // Fall back to using "iceConnectionState" when "connectionState" is not supported (notably Firefox).
+ conn.oniceconnectionstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onConnectionChange(GodotRTCPeerConnection.ConnectionStateCompat[conn.iceConnectionState] || 0);
+ };
+ }
+ conn.onicegatheringstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onIceGatheringChange(GodotRTCPeerConnection.IceGatheringState[conn.iceGatheringState] || 0);
+ };
+ conn.onsignalingstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onSignalingChange(GodotRTCPeerConnection.SignalingState[conn.signalingState] || 0);
+ };
+ conn.onicecandidate = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ const c = event.candidate;
+ if (!c || !c.candidate) {
+ return;
+ }
+ const candidate_str = GodotRuntime.allocString(c.candidate);
+ const mid_str = GodotRuntime.allocString(c.sdpMid);
+ onIceCandidate(mid_str, c.sdpMLineIndex, candidate_str);
+ GodotRuntime.free(candidate_str);
+ GodotRuntime.free(mid_str);
+ };
+ conn.ondatachannel = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ const cid = IDHandler.add(event.channel);
+ onDataChannel(cid);
+ };
+ return id;
},
- ondatachannel: function (p_id, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
+ destroy: function (p_id) {
+ const conn = IDHandler.get(p_id);
+ if (!conn) {
return;
}
-
- const cid = IDHandler.add(event.channel);
- callback(cid);
+ conn.onconnectionstatechange = null;
+ conn.oniceconnectionstatechange = null;
+ conn.onicegatheringstatechange = null;
+ conn.onsignalingstatechange = null;
+ conn.onicecandidate = null;
+ conn.ondatachannel = null;
+ IDHandler.remove(p_id);
},
onsession: function (p_id, callback, session) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
+ if (!IDHandler.get(p_id)) {
return;
}
const type_str = GodotRuntime.allocString(session.type);
@@ -297,27 +356,19 @@ const GodotRTCPeerConnection = {
},
},
- godot_js_rtc_pc_create__sig: 'iiiiii',
- godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
- const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
- const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
- const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
-
- const config = JSON.parse(GodotRuntime.parseString(p_config));
- let conn = null;
- try {
- conn = new RTCPeerConnection(config);
- } catch (e) {
- GodotRuntime.error(e);
- return 0;
- }
-
- const base = GodotRTCPeerConnection;
- const id = IDHandler.add(conn);
- conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
- conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
- conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
- return id;
+ godot_js_rtc_pc_create__sig: 'iiiiiiii',
+ godot_js_rtc_pc_create: function (p_config, p_ref, p_on_connection_state_change, p_on_ice_gathering_state_change, p_on_signaling_state_change, p_on_ice_candidate, p_on_datachannel) {
+ const wrap = function (p_func) {
+ return GodotRuntime.get_func(p_func).bind(null, p_ref);
+ };
+ return GodotRTCPeerConnection.create(
+ JSON.parse(GodotRuntime.parseString(p_config)),
+ wrap(p_on_connection_state_change),
+ wrap(p_on_signaling_state_change),
+ wrap(p_on_ice_gathering_state_change),
+ wrap(p_on_ice_candidate),
+ wrap(p_on_datachannel)
+ );
},
godot_js_rtc_pc_close__sig: 'vi',
@@ -331,14 +382,7 @@ const GodotRTCPeerConnection = {
godot_js_rtc_pc_destroy__sig: 'vi',
godot_js_rtc_pc_destroy: function (p_id) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
- return;
- }
- ref.oniceconnectionstatechange = null;
- ref.onicecandidate = null;
- ref.ondatachannel = null;
- IDHandler.remove(p_id);
+ GodotRTCPeerConnection.destroy(p_id);
},
godot_js_rtc_pc_offer_create__sig: 'viiii',
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index d885b9262b..5aa891d35c 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -69,6 +69,8 @@ void WebRTCPeerConnection::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
+ ClassDB::bind_method(D_METHOD("get_gathering_state"), &WebRTCPeerConnection::get_gathering_state);
+ ClassDB::bind_method(D_METHOD("get_signaling_state"), &WebRTCPeerConnection::get_signaling_state);
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
@@ -80,6 +82,17 @@ void WebRTCPeerConnection::_bind_methods() {
BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
BIND_ENUM_CONSTANT(STATE_FAILED);
BIND_ENUM_CONSTANT(STATE_CLOSED);
+
+ BIND_ENUM_CONSTANT(GATHERING_STATE_NEW);
+ BIND_ENUM_CONSTANT(GATHERING_STATE_GATHERING);
+ BIND_ENUM_CONSTANT(GATHERING_STATE_COMPLETE);
+
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_STABLE);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_OFFER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_OFFER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_PRANSWER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_PRANSWER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_CLOSED);
}
WebRTCPeerConnection::WebRTCPeerConnection() {
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
index 122ea3d00f..76f29f9d68 100644
--- a/modules/webrtc/webrtc_peer_connection.h
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -47,6 +47,21 @@ public:
STATE_CLOSED
};
+ enum GatheringState {
+ GATHERING_STATE_NEW,
+ GATHERING_STATE_GATHERING,
+ GATHERING_STATE_COMPLETE,
+ };
+
+ enum SignalingState {
+ SIGNALING_STATE_STABLE,
+ SIGNALING_STATE_HAVE_LOCAL_OFFER,
+ SIGNALING_STATE_HAVE_REMOTE_OFFER,
+ SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
+ SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
+ SIGNALING_STATE_CLOSED,
+ };
+
private:
static StringName default_extension;
@@ -57,6 +72,8 @@ public:
static void set_default_extension(const StringName &p_name);
virtual ConnectionState get_connection_state() const = 0;
+ virtual GatheringState get_gathering_state() const = 0;
+ virtual SignalingState get_signaling_state() const = 0;
virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
@@ -74,5 +91,7 @@ public:
};
VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::GatheringState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::SignalingState);
#endif // WEBRTC_PEER_CONNECTION_H
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
index 54143e4b79..592a1f8a97 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.cpp
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -32,6 +32,8 @@
void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_connection_state);
+ GDVIRTUAL_BIND(_get_gathering_state);
+ GDVIRTUAL_BIND(_get_signaling_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
GDVIRTUAL_BIND(_create_offer);
diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h
index 0c324ca45f..085069debb 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -53,6 +53,8 @@ public:
/** GDExtension **/
EXBIND0RC(ConnectionState, get_connection_state);
+ EXBIND0RC(GatheringState, get_gathering_state);
+ EXBIND0RC(SignalingState, get_signaling_state);
EXBIND1R(Error, initialize, Dictionary);
EXBIND0R(Error, create_offer);
EXBIND2R(Error, set_remote_description, String, String);
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index f48705253b..a371312ae9 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -51,6 +51,16 @@ void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_sta
peer->_conn_state = (ConnectionState)p_state;
}
+void WebRTCPeerConnectionJS::_on_gathering_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_gathering_state = (GatheringState)p_state;
+}
+
+void WebRTCPeerConnectionJS::_on_signaling_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_signaling_state = (SignalingState)p_state;
+}
+
void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
ERR_PRINT("RTCPeerConnection error!");
}
@@ -100,7 +110,7 @@ Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
_conn_state = STATE_NEW;
String config = Variant(p_config).to_json_string();
- _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_ice_candidate, &_on_data_channel);
+ _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_gathering_state_changed, &_on_signaling_state_changed, &_on_ice_candidate, &_on_data_channel);
return _js_id ? OK : FAILED;
}
@@ -117,14 +127,19 @@ Error WebRTCPeerConnectionJS::poll() {
return OK;
}
+WebRTCPeerConnection::GatheringState WebRTCPeerConnectionJS::get_gathering_state() const {
+ return _gathering_state;
+}
+
+WebRTCPeerConnection::SignalingState WebRTCPeerConnectionJS::get_signaling_state() const {
+ return _signaling_state;
+}
+
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
return _conn_state;
}
WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
- _conn_state = STATE_NEW;
- _js_id = 0;
-
Dictionary config;
initialize(config);
}
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index 50266129e4..e62ad6af28 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -37,11 +37,13 @@
extern "C" {
typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnIceGatheringStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnSignalingStateChange)(void *p_obj, int p_state);
typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
typedef void (*RTCOnError)(void *p_obj);
-extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
+extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_connection_state_change, RTCOnIceGatheringStateChange p_on_gathering_state_change, RTCOnSignalingStateChange p_on_signaling_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
extern void godot_js_rtc_pc_close(int p_id);
extern void godot_js_rtc_pc_destroy(int p_id);
extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
@@ -55,10 +57,14 @@ class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
private:
- int _js_id;
- ConnectionState _conn_state;
+ int _js_id = 0;
+ ConnectionState _conn_state = STATE_NEW;
+ GatheringState _gathering_state = GATHERING_STATE_NEW;
+ SignalingState _signaling_state = SIGNALING_STATE_STABLE;
static void _on_connection_state_changed(void *p_obj, int p_state);
+ static void _on_gathering_state_changed(void *p_obj, int p_state);
+ static void _on_signaling_state_changed(void *p_obj, int p_state);
static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
static void _on_data_channel(void *p_obj, int p_channel);
static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
@@ -66,6 +72,8 @@ private:
public:
virtual ConnectionState get_connection_state() const override;
+ virtual GatheringState get_gathering_state() const override;
+ virtual SignalingState get_signaling_state() const override;
virtual Error initialize(Dictionary configuration = Dictionary()) override;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()) override;