summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml6
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp86
-rw-r--r--modules/gdscript/gdscript_cache.cpp4
-rw-r--r--modules/gdscript/gdscript_parser.cpp27
-rw-r--r--modules/gltf/SCsub2
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml3
-rw-r--r--modules/gltf/extensions/SCsub9
-rw-r--r--modules/gltf/gltf_defines.h2
-rw-r--r--modules/gltf/gltf_document.cpp55
-rw-r--r--modules/gltf/gltf_document.h2
-rw-r--r--modules/gltf/register_types.cpp5
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs8
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs2
-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/NuGetUtils.cs125
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp43
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp2
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp33
-rw-r--r--modules/multiplayer/multiplayer_spawner.h2
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp10
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.h2
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp3
-rw-r--r--modules/openxr/openxr_api.cpp2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp4
-rw-r--r--modules/text_server_fb/text_server_fb.cpp4
-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
40 files changed, 530 insertions, 272 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 4a38caea52..6cf8c1a30e 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>
@@ -555,7 +555,7 @@
<annotation name="@onready">
<return type="void" />
<description>
- Mark the following property as assigned on [Node]'s ready state change. Values for these properties are no assigned immediately upon the node's creation, and instead are computed and stored right before [method Node._ready].
+ Mark the following property as assigned on [Node]'s ready state change. Values for these properties are not assigned immediately upon the node's creation, and instead are computed and stored right before [method Node._ready].
[codeblock]
@onready var character_name: Label = $Label
[/codeblock]
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_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index e37ac1dc3b..32d9aec84f 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -484,12 +484,23 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (parser->script_path == ScriptServer::get_global_class_path(first)) {
result = parser->head->get_datatype();
} else {
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
- if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
- return GDScriptParser::DataType();
+ String path = ScriptServer::get_global_class_path(first);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
+ return GDScriptParser::DataType();
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ result.native_type = ScriptServer::get_global_class_native_base(first);
+ result.script_type = ResourceLoader::load(path, "Script");
+ result.script_path = path;
+ result.is_constant = true;
+ result.is_meta_type = false;
}
- result = ref->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
@@ -540,12 +551,13 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = ref->get_parser()->head->get_datatype();
result.is_meta_type = false;
} else {
- Ref<GDScript> script = member.constant->initializer->reduced_value;
+ Ref<Script> script = member.constant->initializer->reduced_value;
result.kind = GDScriptParser::DataType::SCRIPT;
result.builtin_type = Variant::OBJECT;
result.script_type = script;
result.script_path = script->get_path();
result.native_type = script->get_instance_base_type();
+ result.is_meta_type = false;
}
break;
}
@@ -2676,31 +2688,45 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType type;
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- if (ref.is_null()) {
- push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
+ String path = ScriptServer::get_global_class_path(p_class_name);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::CLASS;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
+ type.class_type = ref->get_parser()->head;
+ type.script_path = ref->get_parser()->script_path;
+ type.is_constant = true;
+ type.is_meta_type = true;
return type;
- }
-
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- if (err) {
- push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::SCRIPT;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
+ type.script_type = ResourceLoader::load(path, "Script");
+ type.script_path = path;
+ type.is_constant = true;
+ type.is_meta_type = true;
return type;
}
-
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::CLASS;
- type.builtin_type = Variant::OBJECT;
- type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
- type.class_type = ref->get_parser()->head;
- type.script_path = ref->get_parser()->script_path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
}
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
@@ -3189,7 +3215,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path);
}
p_preload->resolved_path = p_preload->resolved_path.simplify_path();
- if (!FileAccess::exists(p_preload->resolved_path)) {
+ if (!ResourceLoader::exists(p_preload->resolved_path)) {
push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
} else {
// TODO: Don't load if validating: use completion cache.
@@ -3808,7 +3834,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
Ref<Script> base_script = p_base_type.script_type;
- while (base_script.is_valid() && base_script->is_valid()) {
+ while (base_script.is_valid() && base_script->has_method(function_name)) {
MethodInfo info = base_script->get_method_info(function_name);
if (!(info == MethodInfo())) {
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/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 888cd782fb..d8a06a8663 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3758,6 +3758,33 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
return false;
}
break;
+ case GDScriptParser::DataType::CLASS:
+ // Can assume type is a global GDScript class.
+ if (!ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
+ push_error(R"(Exported script type must extend Resource.)");
+ return false;
+ }
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = export_type.class_type->identifier->name;
+ break;
+ case GDScriptParser::DataType::SCRIPT: {
+ StringName class_name;
+ if (export_type.script_type != nullptr && export_type.script_type.is_valid()) {
+ class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path());
+ }
+ if (class_name == StringName()) {
+ Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
+ if (script.is_valid()) {
+ class_name = script->get_language()->get_global_class_name(export_type.script_path);
+ }
+ }
+ if (class_name != StringName() && ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), SNAME("Resource"))) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = class_name;
+ }
+ } break;
case GDScriptParser::DataType::ENUM: {
variable->export_info.type = Variant::INT;
variable->export_info.hint = PROPERTY_HINT_ENUM;
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
index 71f3ba58d9..5f111165fd 100644
--- a/modules/gltf/SCsub
+++ b/modules/gltf/SCsub
@@ -7,7 +7,7 @@ env_gltf = env_modules.Clone()
# Godot source files
env_gltf.add_source_files(env.modules_sources, "*.cpp")
-env_gltf.add_source_files(env.modules_sources, "extensions/*.cpp")
env_gltf.add_source_files(env.modules_sources, "structures/*.cpp")
+SConscript("extensions/SCsub")
if env["tools"]:
env_gltf.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index d2a9022445..205f6c0f8f 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFDocumentExtension" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ [GLTFDocument] extension class.
</brief_description>
<description>
+ Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export.
</description>
<tutorials>
</tutorials>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index e933e6046a..4d1aa89ac9 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFNode" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ GLTF node class.
</brief_description>
<description>
+ Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes).
</description>
<tutorials>
+ <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
</tutorials>
<members>
<member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
diff --git a/modules/gltf/extensions/SCsub b/modules/gltf/extensions/SCsub
new file mode 100644
index 0000000000..ad214bb79c
--- /dev/null
+++ b/modules/gltf/extensions/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_gltf = env_modules.Clone()
+
+# Godot source files
+env_gltf.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
index c20c87f798..9ee2397968 100644
--- a/modules/gltf/gltf_defines.h
+++ b/modules/gltf/gltf_defines.h
@@ -66,9 +66,9 @@ using GLTFBufferIndex = int;
using GLTFBufferViewIndex = int;
using GLTFCameraIndex = int;
using GLTFImageIndex = int;
+using GLTFLightIndex = int;
using GLTFMaterialIndex = int;
using GLTFMeshIndex = int;
-using GLTFLightIndex = int;
using GLTFNodeIndex = int;
using GLTFSkeletonIndex = int;
using GLTFSkinIndex = int;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 6cb398b5f8..53cf7285f9 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -191,7 +191,7 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
return Error::FAILED;
}
- /* STEP SERIALIZE SCENE */
+ /* STEP SERIALIZE LIGHTS */
err = _serialize_lights(state);
if (err != OK) {
return Error::FAILED;
@@ -401,47 +401,47 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
Array nodes;
for (int i = 0; i < state->nodes.size(); i++) {
Dictionary node;
- Ref<GLTFNode> n = state->nodes[i];
+ Ref<GLTFNode> gltf_node = state->nodes[i];
Dictionary extensions;
node["extensions"] = extensions;
- if (!n->get_name().is_empty()) {
- node["name"] = n->get_name();
+ if (!gltf_node->get_name().is_empty()) {
+ node["name"] = gltf_node->get_name();
}
- if (n->camera != -1) {
- node["camera"] = n->camera;
+ if (gltf_node->camera != -1) {
+ node["camera"] = gltf_node->camera;
}
- if (n->light != -1) {
+ if (gltf_node->light != -1) {
Dictionary lights_punctual;
extensions["KHR_lights_punctual"] = lights_punctual;
- lights_punctual["light"] = n->light;
+ lights_punctual["light"] = gltf_node->light;
}
- if (n->mesh != -1) {
- node["mesh"] = n->mesh;
+ if (gltf_node->mesh != -1) {
+ node["mesh"] = gltf_node->mesh;
}
- if (n->skin != -1) {
- node["skin"] = n->skin;
+ if (gltf_node->skin != -1) {
+ node["skin"] = gltf_node->skin;
}
- if (n->skeleton != -1 && n->skin < 0) {
+ if (gltf_node->skeleton != -1 && gltf_node->skin < 0) {
}
- if (n->xform != Transform3D()) {
- node["matrix"] = _xform_to_array(n->xform);
+ if (gltf_node->xform != Transform3D()) {
+ node["matrix"] = _xform_to_array(gltf_node->xform);
}
- if (!n->rotation.is_equal_approx(Quaternion())) {
- node["rotation"] = _quaternion_to_array(n->rotation);
+ if (!gltf_node->rotation.is_equal_approx(Quaternion())) {
+ node["rotation"] = _quaternion_to_array(gltf_node->rotation);
}
- if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
- node["scale"] = _vec3_to_arr(n->scale);
+ if (!gltf_node->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
+ node["scale"] = _vec3_to_arr(gltf_node->scale);
}
- if (!n->position.is_zero_approx()) {
- node["translation"] = _vec3_to_arr(n->position);
+ if (!gltf_node->position.is_zero_approx()) {
+ node["translation"] = _vec3_to_arr(gltf_node->position);
}
- if (n->children.size()) {
+ if (gltf_node->children.size()) {
Array children;
- for (int j = 0; j < n->children.size(); j++) {
- children.push_back(n->children[j]);
+ for (int j = 0; j < gltf_node->children.size(); j++) {
+ children.push_back(gltf_node->children[j]);
}
node["children"] = children;
}
@@ -450,7 +450,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
ERR_CONTINUE(ext.is_null());
ERR_CONTINUE(!state->scene_nodes.find(i));
- Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]);
+ Error err = ext->export_node(state, gltf_node, node, state->scene_nodes[i]);
ERR_CONTINUE(err != OK);
}
@@ -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);
@@ -5046,7 +5046,7 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> sta
return mi;
}
-Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
+Light3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr);
@@ -5102,6 +5102,7 @@ Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, const GLTFNodeInde
return spatial;
}
+
void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
bool retflag = true;
_check_visibility(p_current, retflag);
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 36a2f94a4e..b3ed786a39 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -188,7 +188,7 @@ private:
const GLTFNodeIndex bone_index);
ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index);
Camera3D *_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index);
- Node3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index);
+ Light3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index);
Node3D *_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index);
void _assign_scene_names(Ref<GLTFState> state);
template <class T>
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/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
index 6f66ce9efa..d78a9c7db8 100755
--- a/modules/mono/build_scripts/build_assemblies.py
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -315,6 +315,8 @@ def main():
output_dir = os.path.abspath(args.godot_output_dir)
+ push_nupkgs_local = os.path.abspath(args.push_nupkgs_local) if args.push_nupkgs_local else None
+
msbuild_tool = find_any_msbuild_tool(args.mono_prefix)
if msbuild_tool is None:
@@ -327,7 +329,7 @@ def main():
output_dir,
args.godot_platform,
args.dev_debug,
- args.push_nupkgs_local,
+ push_nupkgs_local,
args.float,
)
sys.exit(exit_code)
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/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
index efdd50098e..f31ded4d77 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -156,6 +156,10 @@ namespace Godot.SourceGenerators
else if (typeKind == TypeKind.Array)
{
var arrayType = (IArrayTypeSymbol)type;
+
+ if (arrayType.Rank != 1)
+ return null;
+
var elementType = arrayType.ElementType;
switch (elementType.SpecialType)
@@ -177,8 +181,8 @@ namespace Godot.SourceGenerators
if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
return MarshalType.GodotObjectOrDerivedArray;
- if (elementType.ContainingAssembly.Name == "GodotSharp" &&
- elementType.ContainingNamespace.Name == "Godot")
+ if (elementType.ContainingAssembly?.Name == "GodotSharp" &&
+ elementType.ContainingNamespace?.Name == "Godot")
{
switch (elementType)
{
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 1df41a905b..eeda1042ca 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -235,6 +235,8 @@ namespace Godot.SourceGenerators
.Append(signalName)
.Append(";\n");
+ source.Append($" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedName()}\"/>\n");
+
source.Append(" public event ")
.Append(signalDelegate.DelegateSymbol.FullQualifiedName())
.Append(" ")
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/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..95a44d3b7e 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2229,6 +2229,35 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate signal
{
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
+ p_output.append(INDENT1 "/// ");
+ p_output.append("Represents the method that handles the ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>");
+ p_output.append(" event of a ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>");
+ p_output.append(" class.\n");
+ p_output.append(INDENT1 "/// </summary>");
+
+ 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 +2276,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/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 6f42ad6916..91392c8f79 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -152,7 +152,7 @@ bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line,
}
void godot_icall_Internal_EditorNodeShowScriptScreen() {
- EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
+ EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT);
}
void godot_icall_Internal_EditorRunPlay() {
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index 6f60318b3b..c68e2e5a99 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -86,23 +86,41 @@ void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
#endif
+
+TypedArray<String> MultiplayerSpawner::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (spawn_path.is_empty() || !has_node(spawn_path)) {
+ warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
+ }
+ bool has_scenes = get_spawnable_scene_count() > 0;
+ // Can't check if method is overriden in placeholder scripts.
+ bool has_placeholder_script = get_script_instance() && get_script_instance()->is_placeholder();
+ if (!has_scenes && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom) && !has_placeholder_script) {
+ warnings.push_back(RTR("A list of PackedScenes must be set in the \"Auto Spawn List\" property in order for MultiplayerSpawner to automatically spawn them remotely when added as child of \"spawn_path\"."));
+ warnings.push_back(RTR("Alternatively, a Script implementing the function \"_spawn_custom\" must be set for this MultiplayerSpawner, and \"spawn\" must be called explicitly in code."));
+ }
+ return warnings;
+}
+
void MultiplayerSpawner::add_spawnable_scene(const String &p_path) {
SpawnableScene sc;
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);
}
+
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() {
spawnable_scenes.clear();
}
@@ -270,9 +288,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/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
index fc3befc2d4..f038c3b2f9 100644
--- a/modules/multiplayer/multiplayer_spawner.h
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -91,6 +91,8 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
#endif
public:
+ TypedArray<String> get_configuration_warnings() const override;
+
Node *get_spawn_node() const {
return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr;
}
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index eee1495c14..01ecd1a7de 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -94,6 +94,16 @@ void MultiplayerSynchronizer::_update_process() {
}
}
+TypedArray<String> MultiplayerSynchronizer::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (root_path.is_empty() || !has_node(root_path)) {
+ warnings.push_back(RTR("A valid NodePath must be set in the \"Root Path\" property in order for MultiplayerSynchronizer to be able to synchronize properties."));
+ }
+
+ return warnings;
+}
+
Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
r_variant.resize(p_properties.size());
diff --git a/modules/multiplayer/multiplayer_synchronizer.h b/modules/multiplayer/multiplayer_synchronizer.h
index e84d41db86..9b9067a910 100644
--- a/modules/multiplayer/multiplayer_synchronizer.h
+++ b/modules/multiplayer/multiplayer_synchronizer.h
@@ -66,6 +66,8 @@ public:
static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs);
static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state);
+ TypedArray<String> get_configuration_warnings() const override;
+
void set_replication_interval(double p_interval);
double get_replication_interval() const;
uint64_t get_replication_interval_msec() const;
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/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 92d074cb75..94095eb61c 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1557,7 +1557,7 @@ void OpenXRAPI::end_frame() {
XrCompositionLayerProjection projection_layer = {
XR_TYPE_COMPOSITION_LAYER_PROJECTION, // type
nullptr, // next
- layers_list.size() > 1 ? XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT : XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, // layerFlags
+ layers_list.size() > 0 ? XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT : XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, // layerFlags
play_space, // space
view_count, // viewCount
projection_views, // views
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 7aebeafe70..d90870107b 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1940,12 +1940,12 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
face_count = tmp_face->num_faces;
+ FT_Done_Face(tmp_face);
}
- FT_Done_Face(tmp_face);
#endif
}
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 4d599dbcb5..23662bf2c4 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1039,12 +1039,12 @@ int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
face_count = tmp_face->num_faces;
+ FT_Done_Face(tmp_face);
}
- FT_Done_Face(tmp_face);
#endif
}
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;