diff options
Diffstat (limited to 'modules')
85 files changed, 1902 insertions, 923 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 < 20) # True, the program will continue assert(speed >= 0) # False, the program will stop assert(speed >= 0 and speed < 20) # You can also combine the two conditional statements in one check - assert(speed < 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details + assert(speed < 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_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e37ac1dc3b..b6e7dc397a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3189,7 +3189,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. 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_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 61e2c61abc..afebe3c149 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -2163,7 +2163,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE(OPCODE_AWAIT) { CHECK_SPACE(2); - // Do the oneshot connect. + // Do the one-shot connect. GET_INSTRUCTION_ARG(argobj, 0); Signal sig; @@ -2234,7 +2234,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a retvalue = gdfs; - Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback").bind(retvalue), Object::CONNECT_ONESHOT); + Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback").bind(retvalue), Object::CONNECT_ONE_SHOT); if (err != OK) { err_text = "Error connecting to signal: " + sig.get_name() + " during await."; OPCODE_BREAK; diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp index ab5a15c671..6923c765cb 100644 --- a/modules/gltf/extensions/gltf_light.cpp +++ b/modules/gltf/extensions/gltf_light.cpp @@ -109,6 +109,7 @@ void GLTFLight::set_outer_cone_angle(float p_outer_cone_angle) { Ref<GLTFLight> GLTFLight::from_node(const Light3D *p_light) { Ref<GLTFLight> l; l.instantiate(); + ERR_FAIL_COND_V_MSG(!p_light, l, "Tried to create a GLTFLight from a Light3D node, but the given node was null."); l->color = p_light->get_color(); if (cast_to<DirectionalLight3D>(p_light)) { l->light_type = "directional"; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 61af58bda7..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); @@ -5271,7 +5271,7 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(), p_grid_map->get_cell_scale(), p_grid_map->get_cell_scale())); - cell_xform.set_origin(p_grid_map->map_to_world( + cell_xform.set_origin(p_grid_map->map_to_local( Vector3(cell_location.x, cell_location.y, cell_location.z))); Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); 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/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp index 5069f39c4b..212b9b80c8 100644 --- a/modules/gltf/structures/gltf_camera.cpp +++ b/modules/gltf/structures/gltf_camera.cpp @@ -58,6 +58,7 @@ void GLTFCamera::_bind_methods() { Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) { Ref<GLTFCamera> c; c.instantiate(); + ERR_FAIL_COND_V_MSG(!p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null."); c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees. c->set_fov(Math::deg_to_rad(p_camera->get_fov())); diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 6717f23057..ed6cb8656a 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -84,7 +84,7 @@ <method name="get_meshes" qualifiers="const"> <return type="Array" /> <description> - Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space. + Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in local space. </description> </method> <method name="get_navigation_layer_value" qualifiers="const"> @@ -94,6 +94,13 @@ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> + <method name="get_navigation_map" qualifiers="const"> + <return type="RID" /> + <description> + Returns the [RID] of the navigation map this GridMap node uses for its cell baked navigation meshes. + This function returns always the map set on the GridMap node and not the map on the NavigationServer. If the map is changed directly with the NavigationServer API the GridMap node will not be aware of the map change. + </description> + </method> <method name="get_orthogonal_index_from_basis" qualifiers="const"> <return type="int" /> <param index="0" name="basis" type="Basis" /> @@ -114,6 +121,13 @@ Returns an array of all cells with the given item index specified in [code]item[/code]. </description> </method> + <method name="local_to_map" qualifiers="const"> + <return type="Vector3i" /> + <param index="0" name="local_position" type="Vector3" /> + <description> + Returns the map coordinates of the cell containing the given [param local_position]. If [param local_position] is in global coordinates, consider using [method Node3D.to_local] before passing it to this method. See also [method map_to_local]. + </description> + </method> <method name="make_baked_meshes"> <return type="void" /> <param index="0" name="gen_lightmap_uv" type="bool" default="false" /> @@ -121,11 +135,11 @@ <description> </description> </method> - <method name="map_to_world" qualifiers="const"> + <method name="map_to_local" qualifiers="const"> <return type="Vector3" /> <param index="0" name="map_position" type="Vector3i" /> <description> - Returns the position of a grid cell in the GridMap's local coordinate space. + Returns the position of a grid cell in the GridMap's local coordinate space. To convert the returned value into global coordinates, use [method Node3D.to_global]. See also [method map_to_local]. </description> </method> <method name="resource_changed"> @@ -169,12 +183,11 @@ Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="world_to_map" qualifiers="const"> - <return type="Vector3i" /> - <param index="0" name="world_position" type="Vector3" /> + <method name="set_navigation_map"> + <return type="void" /> + <param index="0" name="navigation_map" type="RID" /> <description> - Returns the coordinates of the grid cell containing the given point. - [code]pos[/code] should be in the GridMap's local coordinate space. + Sets the [RID] of the navigation map this GridMap node should use for its cell baked navigation meshes. </description> </method> </methods> diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 17f9832096..b5afd8507d 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -603,13 +603,13 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_pressed() || mb->is_shift_pressed())) { + if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_or_control_pressed() || mb->is_shift_pressed())) { if (mb->is_pressed()) { floor->set_value(floor->get_value() + mb->get_factor()); } return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten. - } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) { + } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && (mb->is_command_or_control_pressed() || mb->is_shift_pressed())) { if (mb->is_pressed()) { floor->set_value(floor->get_value() - mb->get_factor()); } @@ -629,7 +629,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D } else if (mb->is_shift_pressed() && can_edit) { input_action = INPUT_SELECT; last_selection = selection; - } else if (mb->is_command_pressed() && can_edit) { + } else if (mb->is_command_or_control_pressed() && can_edit) { input_action = INPUT_PICK; } else { input_action = INPUT_PAINT; @@ -746,7 +746,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { - if (pan_gesture->is_alt_pressed() && (pan_gesture->is_command_pressed() || pan_gesture->is_shift_pressed())) { + if (pan_gesture->is_alt_pressed() && (pan_gesture->is_command_or_control_pressed() || pan_gesture->is_shift_pressed())) { const real_t delta = pan_gesture->get_delta().y * 0.5; accumulated_floor_delta += delta; int step = 0; @@ -807,7 +807,7 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) { const Ref<InputEventMouseButton> mb = p_ie; // Zoom in/out using Ctrl + mouse wheel - if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed()) { + if (mb.is_valid() && mb->is_pressed() && mb->is_command_or_control_pressed()) { if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { size_slider->set_value(size_slider->get_value() + 0.2); } diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 8486e2c58b..466a2efd21 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -226,6 +226,27 @@ bool GridMap::is_baking_navigation() { return bake_navigation; } +void GridMap::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + for (const KeyValue<OctantKey, Octant *> &E : octant_map) { + Octant &g = *octant_map[E.key]; + for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) { + if (F.value.region.is_valid()) { + NavigationServer3D::get_singleton()->region_set_map(F.value.region, map_override); + } + } + } +} + +RID GridMap::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (is_inside_tree()) { + return get_world_3d()->get_navigation_map(); + } + return RID(); +} + void GridMap::set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; _recreate_octant_data(); @@ -497,18 +518,18 @@ int GridMap::get_orthogonal_index_from_basis(const Basis &p_basis) const { return 0; } -Vector3i GridMap::world_to_map(const Vector3 &p_world_position) const { +Vector3i GridMap::local_to_map(const Vector3 &p_world_position) const { Vector3 map_position = (p_world_position / cell_size).floor(); return Vector3i(map_position); } -Vector3 GridMap::map_to_world(const Vector3i &p_map_position) const { +Vector3 GridMap::map_to_local(const Vector3i &p_map_position) const { Vector3 offset = _get_offset(); - Vector3 world_pos( + Vector3 local_position( p_map_position.x * cell_size.x + offset.x, p_map_position.y * cell_size.y + offset.y, p_map_position.z * cell_size.z + offset.z); - return world_pos; + return local_position; } void GridMap::_octant_transform(const OctantKey &p_key) { @@ -639,10 +660,15 @@ bool GridMap::_octant_update(const OctantKey &p_key) { NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh); NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform); if (is_inside_tree()) { - NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->region_set_map(region, map_override); + } else { + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + } } nm.region = region; +#ifdef DEBUG_ENABLED // add navigation debugmesh visual instances if debug is enabled SceneTree *st = SceneTree::get_singleton(); if (st && st->is_debugging_navigation_hint()) { @@ -650,15 +676,14 @@ bool GridMap::_octant_update(const OctantKey &p_key) { RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid(); nm.navmesh_debug_instance = RS::get_singleton()->instance_create(); RS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid); - RS::get_singleton()->mesh_surface_set_material(navmesh_debug_rid, 0, st->get_debug_navigation_material()->get_rid()); } if (is_inside_tree()) { RS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world_3d()->get_scenario()); RS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform); } } +#endif // DEBUG_ENABLED } - g.navmesh_ids[E] = nm; } } @@ -757,7 +782,11 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) { NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers); NavigationServer3D::get_singleton()->region_set_navmesh(region, nm); NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F.value.xform); - NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + if (map_override.is_valid()) { + NavigationServer3D::get_singleton()->region_set_map(region, map_override); + } else { + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); + } F.value.region = region; } @@ -1022,6 +1051,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation); ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation); + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &GridMap::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &GridMap::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "layers"), &GridMap::set_navigation_layers); ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers); @@ -1047,8 +1079,8 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_basis_with_orthogonal_index", "index"), &GridMap::get_basis_with_orthogonal_index); ClassDB::bind_method(D_METHOD("get_orthogonal_index_from_basis", "basis"), &GridMap::get_orthogonal_index_from_basis); - ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &GridMap::world_to_map); - ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &GridMap::map_to_world); + ClassDB::bind_method(D_METHOD("local_to_map", "local_position"), &GridMap::local_to_map); + ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &GridMap::map_to_local); ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback); ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index f83ce68b09..6a53457d25 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -152,6 +152,7 @@ class GridMap : public Node3D { uint32_t collision_mask = 1; Ref<PhysicsMaterial> physics_material; bool bake_navigation = false; + RID map_override; uint32_t navigation_layers = 1; Transform3D last_transform; @@ -247,6 +248,9 @@ public: void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); + void set_navigation_map(RID p_navigation_map); + RID get_navigation_map() const; + void set_navigation_layers(uint32_t p_navigation_layers); uint32_t get_navigation_layers() const; @@ -276,8 +280,8 @@ public: Basis get_basis_with_orthogonal_index(int p_index) const; int get_orthogonal_index_from_basis(const Basis &p_basis) const; - Vector3i world_to_map(const Vector3 &p_world_position) const; - Vector3 map_to_world(const Vector3i &p_map_position) const; + Vector3i local_to_map(const Vector3 &p_local_position) const; + Vector3 map_to_local(const Vector3i &p_map_position) const; void set_cell_scale(float p_scale); float get_cell_scale() const; diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h index 5ba7e9cbf6..f129ef6f11 100644 --- a/modules/mbedtls/crypto_mbedtls.h +++ b/modules/mbedtls/crypto_mbedtls.h @@ -39,7 +39,7 @@ #include <mbedtls/ssl.h> class CryptoMbedTLS; -class SSLContextMbedTLS; +class TLSContextMbedTLS; class CryptoKeyMbedTLS : public CryptoKey { private: mbedtls_pk_context pkey; @@ -69,7 +69,7 @@ public: _FORCE_INLINE_ void unlock() { locks--; } friend class CryptoMbedTLS; - friend class SSLContextMbedTLS; + friend class TLSContextMbedTLS; }; class X509CertificateMbedTLS : public X509Certificate { @@ -98,7 +98,7 @@ public: _FORCE_INLINE_ void unlock() { locks--; } friend class CryptoMbedTLS; - friend class SSLContextMbedTLS; + friend class TLSContextMbedTLS; }; class HMACContextMbedTLS : public HMACContext { diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h index a6626c9f65..0c9f10b5ed 100644 --- a/modules/mbedtls/dtls_server_mbedtls.h +++ b/modules/mbedtls/dtls_server_mbedtls.h @@ -32,7 +32,7 @@ #define DTLS_SERVER_MBEDTLS_H #include "core/io/dtls_server.h" -#include "ssl_context_mbedtls.h" +#include "tls_context_mbedtls.h" class DTLSServerMbedTLS : public DTLSServer { private: diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp index 1296a4587c..e84d95773d 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.cpp +++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp @@ -32,7 +32,7 @@ #include "mbedtls/platform_util.h" #include "core/io/file_access.h" -#include "core/io/stream_peer_ssl.h" +#include "core/io/stream_peer_tls.h" int PacketPeerMbedDTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) { if (buf == nullptr || len == 0) { @@ -79,7 +79,7 @@ int PacketPeerMbedDTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) { } void PacketPeerMbedDTLS::_cleanup() { - ssl_ctx->clear(); + tls_ctx->clear(); base = Ref<PacketPeer>(); status = STATUS_DISCONNECTED; } @@ -91,16 +91,16 @@ int PacketPeerMbedDTLS::_set_cookie() { uint16_t port = base->get_packet_port(); memcpy(client_id, addr.get_ipv6(), 16); memcpy(&client_id[16], (uint8_t *)&port, 2); - return mbedtls_ssl_set_client_transport_id(ssl_ctx->get_context(), client_id, 18); + return mbedtls_ssl_set_client_transport_id(tls_ctx->get_context(), client_id, 18); } Error PacketPeerMbedDTLS::_do_handshake() { int ret = 0; - while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) { + while ((ret = mbedtls_ssl_handshake(tls_ctx->get_context())) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { if (ret != MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) { ERR_PRINT("TLS handshake error: " + itos(ret)); - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); } _cleanup(); status = STATUS_ERROR; @@ -121,12 +121,12 @@ Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_vali int ret = 0; int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; - Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, authmode, p_ca_certs); + Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, authmode, p_ca_certs); ERR_FAIL_COND_V(err != OK, err); - mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data()); - mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr); - mbedtls_ssl_set_timer_cb(ssl_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); + mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data()); + mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); + mbedtls_ssl_set_timer_cb(tls_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); status = STATUS_HANDSHAKING; @@ -139,13 +139,13 @@ Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_vali } Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain, Ref<CookieContextMbedTLS> p_cookies) { - Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert, p_cookies); + Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert, p_cookies); ERR_FAIL_COND_V(err != OK, err); base = p_base; base->set_blocking_mode(false); - mbedtls_ssl_session_reset(ssl_ctx->get_context()); + mbedtls_ssl_session_reset(tls_ctx->get_context()); int ret = _set_cookie(); if (ret != 0) { @@ -153,8 +153,8 @@ Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey> ERR_FAIL_V_MSG(FAILED, "Error setting DTLS client cookie"); } - mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr); - mbedtls_ssl_set_timer_cb(ssl_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); + mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); + mbedtls_ssl_set_timer_cb(tls_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay); status = STATUS_HANDSHAKING; @@ -173,11 +173,11 @@ Error PacketPeerMbedDTLS::put_packet(const uint8_t *p_buffer, int p_bytes) { return OK; } - int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_buffer, p_bytes); + int ret = mbedtls_ssl_write(tls_ctx->get_context(), p_buffer, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io } else if (ret <= 0) { - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); _cleanup(); return ERR_CONNECTION_ERROR; } @@ -190,7 +190,7 @@ Error PacketPeerMbedDTLS::get_packet(const uint8_t **r_buffer, int &r_bytes) { r_bytes = 0; - int ret = mbedtls_ssl_read(ssl_ctx->get_context(), packet_buffer, PACKET_BUFFER_SIZE); + int ret = mbedtls_ssl_read(tls_ctx->get_context(), packet_buffer, PACKET_BUFFER_SIZE); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io } else if (ret <= 0) { @@ -200,7 +200,7 @@ Error PacketPeerMbedDTLS::get_packet(const uint8_t **r_buffer, int &r_bytes) { } else { _cleanup(); status = STATUS_ERROR; - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); } return ERR_CONNECTION_ERROR; } @@ -220,7 +220,7 @@ void PacketPeerMbedDTLS::poll() { ERR_FAIL_COND(!base.is_valid()); - int ret = mbedtls_ssl_read(ssl_ctx->get_context(), nullptr, 0); + int ret = mbedtls_ssl_read(tls_ctx->get_context(), nullptr, 0); if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -229,7 +229,7 @@ void PacketPeerMbedDTLS::poll() { } else { _cleanup(); status = STATUS_ERROR; - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); } } } @@ -237,7 +237,7 @@ void PacketPeerMbedDTLS::poll() { int PacketPeerMbedDTLS::get_available_packet_count() const { ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0); - return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl)) > 0 ? 1 : 0; + return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls)) > 0 ? 1 : 0; } int PacketPeerMbedDTLS::get_max_packet_size() const { @@ -245,7 +245,7 @@ int PacketPeerMbedDTLS::get_max_packet_size() const { } PacketPeerMbedDTLS::PacketPeerMbedDTLS() { - ssl_ctx.instantiate(); + tls_ctx.instantiate(); } PacketPeerMbedDTLS::~PacketPeerMbedDTLS() { @@ -261,7 +261,7 @@ void PacketPeerMbedDTLS::disconnect_from_peer() { int ret = 0; // Send SSL close notification, blocking, but ignore other errors. do { - ret = mbedtls_ssl_close_notify(ssl_ctx->get_context()); + ret = mbedtls_ssl_close_notify(tls_ctx->get_context()); } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); } diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h index 5f2f42cd30..cc79057d67 100644 --- a/modules/mbedtls/packet_peer_mbed_dtls.h +++ b/modules/mbedtls/packet_peer_mbed_dtls.h @@ -32,7 +32,7 @@ #define PACKET_PEER_MBED_DTLS_H #include "core/io/packet_peer_dtls.h" -#include "ssl_context_mbedtls.h" +#include "tls_context_mbedtls.h" #include <mbedtls/timing.h> @@ -56,7 +56,7 @@ private: void _cleanup(); protected: - Ref<SSLContextMbedTLS> ssl_ctx; + Ref<TLSContextMbedTLS> tls_ctx; mbedtls_timing_delay_context timer; Error _do_handshake(); diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp index 2d4a18b3fc..675091b617 100644 --- a/modules/mbedtls/register_types.cpp +++ b/modules/mbedtls/register_types.cpp @@ -45,7 +45,7 @@ void initialize_mbedtls_module(ModuleInitializationLevel p_level) { } CryptoMbedTLS::initialize_crypto(); - StreamPeerMbedTLS::initialize_ssl(); + StreamPeerMbedTLS::initialize_tls(); PacketPeerMbedDTLS::initialize_dtls(); DTLSServerMbedTLS::initialize(); } @@ -57,6 +57,6 @@ void uninitialize_mbedtls_module(ModuleInitializationLevel p_level) { DTLSServerMbedTLS::finalize(); PacketPeerMbedDTLS::finalize_dtls(); - StreamPeerMbedTLS::finalize_ssl(); + StreamPeerMbedTLS::finalize_tls(); CryptoMbedTLS::finalize_crypto(); } diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp index 92590fbcf6..a97c6bd916 100644 --- a/modules/mbedtls/stream_peer_mbedtls.cpp +++ b/modules/mbedtls/stream_peer_mbedtls.cpp @@ -74,18 +74,18 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) { } void StreamPeerMbedTLS::_cleanup() { - ssl_ctx->clear(); + tls_ctx->clear(); base = Ref<StreamPeer>(); status = STATUS_DISCONNECTED; } Error StreamPeerMbedTLS::_do_handshake() { int ret = 0; - while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) { + while ((ret = mbedtls_ssl_handshake(tls_ctx->get_context())) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { // An error occurred. ERR_PRINT("TLS handshake error: " + itos(ret)); - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); disconnect_from_stream(); status = STATUS_ERROR; return FAILED; @@ -108,11 +108,11 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida base = p_base; int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE; - Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs); + Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs); ERR_FAIL_COND_V(err != OK, err); - mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data()); - mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr); + mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data()); + mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); status = STATUS_HANDSHAKING; @@ -127,12 +127,12 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) { ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER); - Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert); + Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert); ERR_FAIL_COND_V(err != OK, err); base = p_base; - mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr); + mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr); status = STATUS_HANDSHAKING; @@ -173,7 +173,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in return OK; } - int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_data, p_bytes); + int ret = mbedtls_ssl_write(tls_ctx->get_context(), p_data, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // Non blocking IO ret = 0; @@ -182,7 +182,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in disconnect_from_stream(); return ERR_FILE_EOF; } else if (ret <= 0) { - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); disconnect_from_stream(); return ERR_CONNECTION_ERROR; } @@ -216,7 +216,7 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r r_received = 0; - int ret = mbedtls_ssl_read(ssl_ctx->get_context(), p_buffer, p_bytes); + int ret = mbedtls_ssl_read(tls_ctx->get_context(), p_buffer, p_bytes); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = 0; // non blocking io } else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -224,7 +224,7 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r disconnect_from_stream(); return ERR_FILE_EOF; } else if (ret <= 0) { - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); disconnect_from_stream(); return ERR_CONNECTION_ERROR; } @@ -245,7 +245,7 @@ void StreamPeerMbedTLS::poll() { // We could pass nullptr as second parameter, but some behaviour sanitizers don't seem to like that. // Passing a 1 byte buffer to workaround it. uint8_t byte; - int ret = mbedtls_ssl_read(ssl_ctx->get_context(), &byte, 0); + int ret = mbedtls_ssl_read(tls_ctx->get_context(), &byte, 0); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { // Nothing to read/write (non blocking IO) @@ -254,7 +254,7 @@ void StreamPeerMbedTLS::poll() { disconnect_from_stream(); return; } else if (ret < 0) { - SSLContextMbedTLS::print_mbedtls_error(ret); + TLSContextMbedTLS::print_mbedtls_error(ret); disconnect_from_stream(); return; } @@ -269,11 +269,11 @@ void StreamPeerMbedTLS::poll() { int StreamPeerMbedTLS::get_available_bytes() const { ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0); - return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl)); + return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls)); } StreamPeerMbedTLS::StreamPeerMbedTLS() { - ssl_ctx.instantiate(); + tls_ctx.instantiate(); } StreamPeerMbedTLS::~StreamPeerMbedTLS() { @@ -288,7 +288,7 @@ void StreamPeerMbedTLS::disconnect_from_stream() { Ref<StreamPeerTCP> tcp = base; if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) { // We are still connected on the socket, try to send close notify. - mbedtls_ssl_close_notify(ssl_ctx->get_context()); + mbedtls_ssl_close_notify(tls_ctx->get_context()); } _cleanup(); @@ -302,16 +302,16 @@ Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const { return base; } -StreamPeerSSL *StreamPeerMbedTLS::_create_func() { +StreamPeerTLS *StreamPeerMbedTLS::_create_func() { return memnew(StreamPeerMbedTLS); } -void StreamPeerMbedTLS::initialize_ssl() { +void StreamPeerMbedTLS::initialize_tls() { _create = _create_func; available = true; } -void StreamPeerMbedTLS::finalize_ssl() { +void StreamPeerMbedTLS::finalize_tls() { available = false; _create = nullptr; } diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h index 68b07feea9..9219269539 100644 --- a/modules/mbedtls/stream_peer_mbedtls.h +++ b/modules/mbedtls/stream_peer_mbedtls.h @@ -31,24 +31,24 @@ #ifndef STREAM_PEER_MBEDTLS_H #define STREAM_PEER_MBEDTLS_H -#include "core/io/stream_peer_ssl.h" -#include "ssl_context_mbedtls.h" +#include "core/io/stream_peer_tls.h" +#include "tls_context_mbedtls.h" -class StreamPeerMbedTLS : public StreamPeerSSL { +class StreamPeerMbedTLS : public StreamPeerTLS { private: Status status = STATUS_DISCONNECTED; String hostname; Ref<StreamPeer> base; - static StreamPeerSSL *_create_func(); + static StreamPeerTLS *_create_func(); static int bio_recv(void *ctx, unsigned char *buf, size_t len); static int bio_send(void *ctx, const unsigned char *buf, size_t len); void _cleanup(); protected: - Ref<SSLContextMbedTLS> ssl_ctx; + Ref<TLSContextMbedTLS> tls_ctx; Error _do_handshake(); @@ -69,8 +69,8 @@ public: virtual int get_available_bytes() const; - static void initialize_ssl(); - static void finalize_ssl(); + static void initialize_tls(); + static void finalize_tls(); StreamPeerMbedTLS(); ~StreamPeerMbedTLS(); diff --git a/modules/mbedtls/ssl_context_mbedtls.cpp b/modules/mbedtls/tls_context_mbedtls.cpp index e2dad074cc..1ae7bc0436 100644 --- a/modules/mbedtls/ssl_context_mbedtls.cpp +++ b/modules/mbedtls/tls_context_mbedtls.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* ssl_context_mbedtls.cpp */ +/* tls_context_mbedtls.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "ssl_context_mbedtls.h" +#include "tls_context_mbedtls.h" static void my_debug(void *ctx, int level, const char *file, int line, @@ -37,7 +37,7 @@ static void my_debug(void *ctx, int level, fflush(stdout); } -void SSLContextMbedTLS::print_mbedtls_error(int p_ret) { +void TLSContextMbedTLS::print_mbedtls_error(int p_ret) { printf("mbedtls error: returned -0x%x\n\n", -p_ret); fflush(stdout); } @@ -82,12 +82,12 @@ CookieContextMbedTLS::~CookieContextMbedTLS() { clear(); } -/// SSLContextMbedTLS +/// TLSContextMbedTLS -Error SSLContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) { +Error TLSContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) { ERR_FAIL_COND_V_MSG(inited, ERR_ALREADY_IN_USE, "This SSL context is already active"); - mbedtls_ssl_init(&ssl); + mbedtls_ssl_init(&tls); mbedtls_ssl_config_init(&conf); mbedtls_ctr_drbg_init(&ctr_drbg); mbedtls_entropy_init(&entropy); @@ -110,7 +110,7 @@ Error SSLContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) return OK; } -Error SSLContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies) { +Error TLSContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies) { ERR_FAIL_COND_V(!p_pkey.is_valid(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(!p_cert.is_valid(), ERR_INVALID_PARAMETER); @@ -146,11 +146,11 @@ Error SSLContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<Crypto cookies = p_cookies; mbedtls_ssl_conf_dtls_cookies(&conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &(cookies->cookie_ctx)); } - mbedtls_ssl_setup(&ssl, &conf); + mbedtls_ssl_setup(&tls, &conf); return OK; } -Error SSLContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) { +Error TLSContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) { Error err = _setup(MBEDTLS_SSL_IS_CLIENT, p_transport, p_authmode); ERR_FAIL_COND_V(err != OK, err); @@ -172,15 +172,15 @@ Error SSLContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509Ce // Set valid CAs mbedtls_ssl_conf_ca_chain(&conf, &(cas->cert), nullptr); - mbedtls_ssl_setup(&ssl, &conf); + mbedtls_ssl_setup(&tls, &conf); return OK; } -void SSLContextMbedTLS::clear() { +void TLSContextMbedTLS::clear() { if (!inited) { return; } - mbedtls_ssl_free(&ssl); + mbedtls_ssl_free(&tls); mbedtls_ssl_config_free(&conf); mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); @@ -198,14 +198,14 @@ void SSLContextMbedTLS::clear() { inited = false; } -mbedtls_ssl_context *SSLContextMbedTLS::get_context() { +mbedtls_ssl_context *TLSContextMbedTLS::get_context() { ERR_FAIL_COND_V(!inited, nullptr); - return &ssl; + return &tls; } -SSLContextMbedTLS::SSLContextMbedTLS() { +TLSContextMbedTLS::TLSContextMbedTLS() { } -SSLContextMbedTLS::~SSLContextMbedTLS() { +TLSContextMbedTLS::~TLSContextMbedTLS() { clear(); } diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/tls_context_mbedtls.h index 5883388311..5e7b3dc46e 100644 --- a/modules/mbedtls/ssl_context_mbedtls.h +++ b/modules/mbedtls/tls_context_mbedtls.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* ssl_context_mbedtls.h */ +/* tls_context_mbedtls.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SSL_CONTEXT_MBEDTLS_H -#define SSL_CONTEXT_MBEDTLS_H +#ifndef TLS_CONTEXT_MBEDTLS_H +#define TLS_CONTEXT_MBEDTLS_H #include "crypto_mbedtls.h" @@ -44,10 +44,10 @@ #include <mbedtls/ssl.h> #include <mbedtls/ssl_cookie.h> -class SSLContextMbedTLS; +class TLSContextMbedTLS; class CookieContextMbedTLS : public RefCounted { - friend class SSLContextMbedTLS; + friend class TLSContextMbedTLS; protected: bool inited = false; @@ -63,7 +63,7 @@ public: ~CookieContextMbedTLS(); }; -class SSLContextMbedTLS : public RefCounted { +class TLSContextMbedTLS : public RefCounted { protected: bool inited = false; @@ -73,7 +73,7 @@ public: Ref<X509CertificateMbedTLS> certs; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ssl_context ssl; + mbedtls_ssl_context tls; mbedtls_ssl_config conf; Ref<CookieContextMbedTLS> cookies; @@ -86,8 +86,8 @@ public: mbedtls_ssl_context *get_context(); - SSLContextMbedTLS(); - ~SSLContextMbedTLS(); + TLSContextMbedTLS(); + ~TLSContextMbedTLS(); }; -#endif // SSL_CONTEXT_MBEDTLS_H +#endif // TLS_CONTEXT_MBEDTLS_H diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index c2d5452837..5d63773096 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -27,293 +27,3 @@ def configure(env, env_mono): if env["tools"]: env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"]) - - app_host_dir = find_dotnet_app_host_dir(env) - - def check_app_host_file_exists(file): - file_path = os.path.join(app_host_dir, file) - if not os.path.isfile(file_path): - raise RuntimeError("File not found: " + file_path) - - # TODO: - # All libnethost does for us is provide a function to find hostfxr. - # If we could handle that logic ourselves we could void linking it. - - # nethost file names: - # static: libnethost.a/lib - # shared: libnethost.a/dylib and nethost.dll - check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a") - check_app_host_file_exists("nethost.h") - check_app_host_file_exists("hostfxr.h") - check_app_host_file_exists("coreclr_delegates.h") - - env_mono.Prepend(CPPPATH=app_host_dir) - - env.Append(LIBPATH=[app_host_dir]) - - # Only the editor build links nethost, which is needed to find hostfxr. - # Exported games don't need this logic as hostfxr is bundled with them. - if tools_enabled: - libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a") - - if env["platform"] == "windows": - env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"]) - - if env.msvc: - env.Append(LINKFLAGS="libnethost.lib") - else: - env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) - else: - is_apple = env["platform"] in ["macos", "ios"] - # is_macos = is_apple and not is_ios - - # if is_ios and not is_ios_sim: - # env_mono.Append(CPPDEFINES=["IOS_DEVICE"]) - - if is_apple: - env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path]) - else: - env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"]) - - -def find_dotnet_app_host_dir(env): - dotnet_version = "6.0" - - dotnet_root = env["dotnet_root"] - - if not dotnet_root: - dotnet_cmd = find_dotnet_executable(env["arch"]) - if dotnet_cmd: - sdk_path = find_dotnet_sdk(dotnet_cmd, dotnet_version) - if sdk_path: - dotnet_root = os.path.abspath(os.path.join(sdk_path, os.pardir)) - - if not dotnet_root: - raise RuntimeError("Cannot find .NET Core Sdk") - - print("Found .NET Core Sdk root directory: " + dotnet_root) - - dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet") - - runtime_identifier = determine_runtime_identifier(env) - - # TODO: In the future, if it can't be found this way, we want to obtain it - # from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package. - app_host_version = find_app_host_version(dotnet_cmd, dotnet_version) - if not app_host_version: - raise RuntimeError("Cannot find .NET app host for version: " + dotnet_version) - - def get_runtime_path(): - return os.path.join( - dotnet_root, - "packs", - "Microsoft.NETCore.App.Host." + runtime_identifier, - app_host_version, - "runtimes", - runtime_identifier, - "native", - ) - - app_host_dir = get_runtime_path() - - # Some Linux distros use their distro name as the RID in these paths. - # If the initial generic path doesn't exist, try to get the RID from `dotnet --info`. - # The generic RID should still be the first choice. Some platforms like Windows 10 - # define the RID as `win10-x64` but still use the generic `win-x64` for directory names. - if not app_host_dir or not os.path.isdir(app_host_dir): - runtime_identifier = find_dotnet_cli_rid(dotnet_cmd) - app_host_dir = get_runtime_path() - - return app_host_dir - - -def determine_runtime_identifier(env): - # The keys are Godot's names, the values are the Microsoft's names. - # List: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog - names_map = { - "windows": "win", - "macos": "osx", - "linuxbsd": "linux", - } - arch_map = { - "x86_64": "x64", - "x86_32": "x86", - "arm64": "arm64", - "arm32": "arm", - } - platform = env["platform"] - if is_desktop(platform): - return "%s-%s" % (names_map[platform], arch_map[env["arch"]]) - else: - raise NotImplementedError() - - -def find_app_host_version(dotnet_cmd, search_version_str): - import subprocess - from distutils.version import LooseVersion - - search_version = LooseVersion(search_version_str) - found_match = False - - try: - env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US") - lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"], env=env).splitlines() - - for line_bytes in lines: - line = line_bytes.decode("utf-8") - if not line.startswith("Microsoft.NETCore.App "): - continue - - parts = line.split(" ", 2) - if len(parts) < 3: - continue - - version_str = parts[1] - - version = LooseVersion(version_str) - - if version >= search_version: - search_version = version - found_match = True - if found_match: - return str(search_version) - except (subprocess.CalledProcessError, OSError) as e: - import sys - - print(e, file=sys.stderr) - - return "" - - -def find_dotnet_arch(dotnet_cmd): - import subprocess - - try: - env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US") - lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines() - - for line_bytes in lines: - line = line_bytes.decode("utf-8") - - parts = line.split(":", 1) - if len(parts) < 2: - continue - - arch_str = parts[0].strip() - if arch_str != "Architecture": - continue - - arch_value = parts[1].strip() - arch_map = {"x64": "x86_64", "x86": "x86_32", "arm64": "arm64", "arm32": "arm32"} - return arch_map[arch_value] - except (subprocess.CalledProcessError, OSError) as e: - import sys - - print(e, file=sys.stderr) - - return "" - - -def find_dotnet_sdk(dotnet_cmd, search_version_str): - import subprocess - from distutils.version import LooseVersion - - search_version = LooseVersion(search_version_str) - - try: - env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US") - lines = subprocess.check_output([dotnet_cmd, "--list-sdks"], env=env).splitlines() - - for line_bytes in lines: - line = line_bytes.decode("utf-8") - - parts = line.split(" ", 1) - if len(parts) < 2: - continue - - version_str = parts[0] - - version = LooseVersion(version_str) - - if version < search_version: - continue - - path_part = parts[1] - return path_part[1 : path_part.find("]")] - except (subprocess.CalledProcessError, OSError) as e: - import sys - - print(e, file=sys.stderr) - - return "" - - -def find_dotnet_cli_rid(dotnet_cmd): - import subprocess - - try: - env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US") - lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines() - - for line_bytes in lines: - line = line_bytes.decode("utf-8") - if not line.startswith(" RID:"): - continue - - parts = line.split() - if len(parts) < 2: - continue - - return parts[1] - except (subprocess.CalledProcessError, OSError) as e: - import sys - - print(e, file=sys.stderr) - - return "" - - -ENV_PATH_SEP = ";" if os.name == "nt" else ":" - - -def find_dotnet_executable(arch): - is_windows = os.name == "nt" - windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None - path_dirs = os.environ["PATH"].split(ENV_PATH_SEP) - - search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list - - for dir in path_dirs: - search_dirs += [ - os.path.join(dir, "x64"), - os.path.join(dir, "x86"), - os.path.join(dir, "arm64"), - os.path.join(dir, "arm32"), - ] # search subfolders for cross compiling - - # `dotnet --info` may not specify architecture. In such cases, - # we fallback to the first one we find without architecture. - sdk_path_unknown_arch = "" - - for dir in search_dirs: - path = os.path.join(dir, "dotnet") - - if is_windows: - for extension in windows_exts: - path_with_ext = path + extension - - if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK): - sdk_arch = find_dotnet_arch(path_with_ext) - if sdk_arch == arch or arch == "": - return path_with_ext - elif sdk_arch == "": - sdk_path_unknown_arch = path_with_ext - else: - if os.path.isfile(path) and os.access(path, os.X_OK): - sdk_arch = find_dotnet_arch(path) - if sdk_arch == arch or arch == "": - return path - elif sdk_arch == "": - sdk_path_unknown_arch = path - - return sdk_path_unknown_arch diff --git a/modules/mono/config.py b/modules/mono/config.py index ad78c8c898..15fe79ef8c 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -4,23 +4,13 @@ supported_platforms = ["windows", "macos", "linuxbsd"] def can_build(env, platform): - return not env["arch"].startswith("rv") + if env["arch"].startswith("rv"): + return False + if env["tools"]: + env.module_add_dependencies("mono", ["regex"]) -def get_opts(platform): - from SCons.Variables import BoolVariable, PathVariable - - default_mono_static = platform in ["ios", "web"] - default_mono_bundles_zlib = platform in ["web"] - - return [ - PathVariable( - "dotnet_root", - "Path to the .NET Sdk installation directory for the target platform and architecture", - "", - PathVariable.PathAccept, - ), - ] + return True def configure(env): diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 64792a795f..8fd3626a20 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -784,6 +784,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (Object *obj : script->instances) { script->pending_reload_instances.insert(obj->get_instance_id()); + // Since this script instance wasn't a placeholder, add it to the list of placeholders + // that will have to be eventually replaced with a script instance in case it turns into one. + // This list is not cleared after the reload and the collected instances only leave + // the list if the script is instantiated or if it was a tool script but becomes a + // non-tool script in a rebuild. + script->pending_replace_placeholders.insert(obj->get_instance_id()); + RefCounted *rc = Object::cast_to<RefCounted>(obj); if (rc) { rc_instances.push_back(Ref<RefCounted>(rc)); @@ -836,6 +843,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload) } + script->was_tool_before_reload = script->tool; script->_clear(); } @@ -924,24 +932,34 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { ScriptInstance *si = obj->get_script_instance(); + // Check if the script must be instantiated or kept as a placeholder + // when the script may not be a tool (see #65266) + bool replace_placeholder = script->pending_replace_placeholders.has(obj->get_instance_id()); + if (!script->is_tool() && script->was_tool_before_reload) { + // The script was a tool before the rebuild so the removal was intentional. + replace_placeholder = false; + script->pending_replace_placeholders.erase(obj->get_instance_id()); + } + #ifdef TOOLS_ENABLED if (si) { // If the script instance is not null, then it must be a placeholder. // Non-placeholder script instances are removed in godot_icall_Object_Disposed. CRASH_COND(!si->is_placeholder()); - if (script->is_tool() || ScriptServer::is_scripting_enabled()) { - // Replace placeholder with a script instance + if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) { + // Replace placeholder with a script instance. CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id]; - // Backup placeholder script instance state before replacing it with a script instance + // Backup placeholder script instance state before replacing it with a script instance. si->get_property_state(state_backup.properties); ScriptInstance *script_instance = script->instance_create(obj); if (script_instance) { script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si)); + script->pending_replace_placeholders.erase(obj->get_instance_id()); obj->set_script_instance(script_instance); } } @@ -951,8 +969,24 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { #else CRASH_COND(si != nullptr); #endif - // Re-create script instance - obj->set_script(script); // will create the script instance as well + + // Re-create the script instance. + if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) { + // Create script instance or replace placeholder with a script instance. + ScriptInstance *script_instance = script->instance_create(obj); + + if (script_instance) { + script->pending_replace_placeholders.erase(obj->get_instance_id()); + obj->set_script_instance(script_instance); + continue; + } + } + // The script instance could not be instantiated or wasn't in the list of placeholders to replace. + obj->set_script(script); +#if DEBUG_ENABLED + // If we reached here, the instantiated script must be a placeholder. + CRASH_COND(!obj->get_script_instance()->is_placeholder()); +#endif } } @@ -2001,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(); @@ -2032,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) { - -#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 - } - } - }); + GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyInfoList(this, &_add_property_info_list_callback); #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 3509a5c87d..d469c28d4a 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -90,6 +90,9 @@ class CSharpScript : public Script { HashSet<ObjectID> pending_reload_instances; RBMap<ObjectID, StateBackup> pending_reload_state; + + bool was_tool_before_reload = false; + HashSet<ObjectID> pending_replace_placeholders; #endif String source; @@ -130,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/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs index 7aaadb27be..98ca534c66 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs @@ -39,6 +39,11 @@ namespace Godot.SourceGenerators for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++) { var typeSyntax = typeArgListSyntax.Arguments[i]; + + // Ignore omitted type arguments, e.g.: List<>, Dictionary<,>, etc + if (typeSyntax is OmittedTypeArgumentSyntax) + continue; + var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol; Debug.Assert(typeSymbol != null); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 5ac4f4a47e..1ee31eb1a9 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -133,7 +133,9 @@ namespace Godot.SourceGenerators .Distinct(new MethodOverloadEqualityComparer()) .ToArray(); - source.Append(" private partial class GodotInternal {\n"); + source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); + + source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedName()}.MethodName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -144,7 +146,7 @@ namespace Godot.SourceGenerators foreach (string methodName in distinctMethodNames) { - source.Append(" public static readonly StringName MethodName_"); + source.Append(" public new static readonly StringName "); source.Append(methodName); source.Append(" = \""); source.Append(methodName); @@ -157,8 +159,6 @@ namespace Godot.SourceGenerators if (godotClassMethods.Length > 0) { - source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>"; source.Append(" internal new static ") @@ -179,10 +179,10 @@ namespace Godot.SourceGenerators source.Append(" return methods;\n"); source.Append(" }\n"); - - source.Append("#pragma warning restore CS0109\n"); } + source.Append("#pragma warning restore CS0109\n"); + // Generate InvokeGodotClassMethod if (godotClassMethods.Length > 0) @@ -242,7 +242,7 @@ namespace Godot.SourceGenerators private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo) { - source.Append(" methods.Add(new(name: GodotInternal.MethodName_") + source.Append(" methods.Add(new(name: MethodName.") .Append(methodInfo.Name) .Append(", returnVal: "); @@ -350,7 +350,7 @@ namespace Godot.SourceGenerators source.Append(" "); if (!isFirstEntry) source.Append("else "); - source.Append("if (method == GodotInternal.MethodName_"); + source.Append("if (method == MethodName."); source.Append(methodName); source.Append(") {\n return true;\n }\n"); } @@ -362,7 +362,7 @@ namespace Godot.SourceGenerators { string methodName = method.Method.Name; - source.Append(" if (method == GodotInternal.MethodName_"); + source.Append(" if (method == MethodName."); source.Append(methodName); source.Append(" && argCount == "); source.Append(method.ParamTypes.Length); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index fc46d82dff..b331e2e794 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -122,14 +122,16 @@ namespace Godot.SourceGenerators var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray(); var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray(); - source.Append(" private partial class GodotInternal {\n"); + source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); + + source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedName()}.PropertyName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup foreach (var property in godotClassProperties) { string propertyName = property.PropertySymbol.Name; - source.Append(" public static readonly StringName PropName_"); + source.Append(" public new static readonly StringName "); source.Append(propertyName); source.Append(" = \""); source.Append(propertyName); @@ -139,7 +141,7 @@ namespace Godot.SourceGenerators foreach (var field in godotClassFields) { string fieldName = field.FieldSymbol.Name; - source.Append(" public static readonly StringName PropName_"); + source.Append(" public new static readonly StringName "); source.Append(fieldName); source.Append(" = \""); source.Append(fieldName); @@ -214,8 +216,6 @@ namespace Godot.SourceGenerators // Generate GetGodotPropertyList - source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - string dictionaryType = "System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>"; source.Append(" internal new static ") @@ -289,7 +289,7 @@ namespace Godot.SourceGenerators if (!isFirstEntry) source.Append("else "); - source.Append("if (name == GodotInternal.PropName_") + source.Append("if (name == PropertyName.") .Append(propertyMemberName) .Append(") {\n") .Append(" ") @@ -313,7 +313,7 @@ namespace Godot.SourceGenerators if (!isFirstEntry) source.Append("else "); - source.Append("if (name == GodotInternal.PropName_") + source.Append("if (name == PropertyName.") .Append(propertyMemberName) .Append(") {\n") .Append(" value = ") @@ -342,7 +342,7 @@ namespace Godot.SourceGenerators { source.Append(" properties.Add(new(type: (Godot.Variant.Type)") .Append((int)propertyInfo.Type) - .Append(", name: GodotInternal.PropName_") + .Append(", name: PropertyName.") .Append(propertyInfo.Name) .Append(", hint: (Godot.PropertyHint)") .Append((int)propertyInfo.Hint) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index c7745391d0..65dadcb801 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -243,7 +243,7 @@ namespace Godot.SourceGenerators source.Append(" = "); source.Append(exportedMember.Value ?? "default"); source.Append(";\n"); - source.Append(" values.Add(GodotInternal.PropName_"); + source.Append(" values.Add(PropertyName."); source.Append(exportedMember.Name); source.Append(", "); source.Append(defaultValueLocalName); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index 39a99ff8ba..a40220565f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -159,7 +159,7 @@ namespace Godot.SourceGenerators { string propertyName = property.PropertySymbol.Name; - source.Append(" info.AddProperty(GodotInternal.PropName_") + source.Append(" info.AddProperty(PropertyName.") .Append(propertyName) .Append(", ") .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type) @@ -172,7 +172,7 @@ namespace Godot.SourceGenerators { string fieldName = field.FieldSymbol.Name; - source.Append(" info.AddProperty(GodotInternal.PropName_") + source.Append(" info.AddProperty(PropertyName.") .Append(fieldName) .Append(", ") .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type) @@ -185,7 +185,7 @@ namespace Godot.SourceGenerators { string signalName = signalDelegate.Name; - source.Append(" info.AddSignalEventDelegate(GodotInternal.SignalName_") + source.Append(" info.AddSignalEventDelegate(SignalName.") .Append(signalName) .Append(", this.backing_") .Append(signalName) @@ -204,7 +204,7 @@ namespace Godot.SourceGenerators { string propertyName = property.PropertySymbol.Name; - source.Append(" if (info.TryGetProperty(GodotInternal.PropName_") + source.Append(" if (info.TryGetProperty(PropertyName.") .Append(propertyName) .Append(", out var _value_") .Append(propertyName) @@ -223,7 +223,7 @@ namespace Godot.SourceGenerators { string fieldName = field.FieldSymbol.Name; - source.Append(" if (info.TryGetProperty(GodotInternal.PropName_") + source.Append(" if (info.TryGetProperty(PropertyName.") .Append(fieldName) .Append(", out var _value_") .Append(fieldName) @@ -245,7 +245,7 @@ namespace Godot.SourceGenerators source.Append(" if (info.TryGetSignalEventDelegate<") .Append(signalDelegateQualifiedName) - .Append(">(GodotInternal.SignalName_") + .Append(">(SignalName.") .Append(signalName) .Append(", out var _value_") .Append(signalName) 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 6b06f10db1..1df41a905b 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -173,14 +173,16 @@ namespace Godot.SourceGenerators godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value)); } - source.Append(" private partial class GodotInternal {\n"); + source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); + + source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedName()}.SignalName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup foreach (var signalDelegate in godotSignalDelegates) { string signalName = signalDelegate.Name; - source.Append(" public static readonly StringName SignalName_"); + source.Append(" public new static readonly StringName "); source.Append(signalName); source.Append(" = \""); source.Append(signalName); @@ -193,8 +195,6 @@ namespace Godot.SourceGenerators if (godotSignalDelegates.Count > 0) { - source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>"; source.Append(" internal new static ") @@ -215,10 +215,10 @@ namespace Godot.SourceGenerators source.Append(" return signals;\n"); source.Append(" }\n"); - - source.Append("#pragma warning restore CS0109\n"); } + source.Append("#pragma warning restore CS0109\n"); + // Generate signal event foreach (var signalDelegate in godotSignalDelegates) @@ -291,7 +291,7 @@ namespace Godot.SourceGenerators private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo) { - source.Append(" signals.Add(new(name: GodotInternal.SignalName_") + source.Append(" signals.Add(new(name: SignalName.") .Append(methodInfo.Name) .Append(", returnVal: "); @@ -400,7 +400,7 @@ namespace Godot.SourceGenerators string signalName = signal.Name; var invokeMethodData = signal.InvokeMethodData; - source.Append(" if (signal == GodotInternal.SignalName_"); + source.Append(" if (signal == SignalName."); source.Append(signalName); source.Append(" && argCount == "); source.Append(invokeMethodData.ParamTypes.Length); 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 15a40c8ca5..ce4ac9b796 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -113,7 +113,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged" // Types that will be ignored by the generator and won't be available in C#. -const Vector<String> ignored_types = { "PhysicsServer3DExtension" }; +const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" }; void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) { // C interface for enums is the same as that of 'uint32_t'. Remember to apply @@ -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/editor/hostfxr_resolver.cpp b/modules/mono/editor/hostfxr_resolver.cpp new file mode 100644 index 0000000000..ea5978b2cd --- /dev/null +++ b/modules/mono/editor/hostfxr_resolver.cpp @@ -0,0 +1,335 @@ +/*************************************************************************/ +/* hostfxr_resolver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* +Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost +*/ + +/* +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "hostfxr_resolver.h" + +#include "core/config/engine.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/os/os.h" + +#ifdef WINDOWS_ENABLED +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#include "../utils/path_utils.h" +#include "semver.h" + +// We don't use libnethost as it gives us issues with some compilers. +// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the +// same function names for easier comparing in case we need to update this in the future. + +namespace { + +String get_hostfxr_file_name() { +#if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED) + return "hostfxr.dll"; +#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED) + return "libhostfxr.dylib"; +#else + return "libhostfxr.so"; +#endif +} + +bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) { + godotsharp::SemVerParser sem_ver_parser; + + bool found_ver = false; + godotsharp::SemVer latest_ver; + String latest_ver_str; + + Ref<DirAccess> da = DirAccess::open(fxr_root); + da->list_dir_begin(); + for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) { + if (!da->current_is_dir() || dir == "." || dir == "..") { + continue; + } + + String ver = dir.get_file(); + + godotsharp::SemVer fx_ver; + if (sem_ver_parser.parse(ver, fx_ver)) { + if (!found_ver || fx_ver > latest_ver) { + latest_ver = fx_ver; + latest_ver_str = ver; + found_ver = true; + } + } + } + + if (!found_ver) { + return false; + } + + String fxr_with_ver = path::join(fxr_root, latest_ver_str); + String hostfxr_file_path = path::join(fxr_with_ver, get_hostfxr_file_name()); + + ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver); + + r_fxr_path = hostfxr_file_path; + + return true; +} + +#ifdef WINDOWS_ENABLED +typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + +BOOL is_wow64() { + BOOL wow64 = FALSE; + + LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) { + wow64 = FALSE; + } + } + + return wow64; +} +#endif + +static const char *arch_name_map[][2] = { + { "arm32", "arm" }, + { "arm64", "arm64" }, + { "rv64", "riscv64" }, + { "x86_64", "x64" }, + { "x86_32", "x86" }, + { nullptr, nullptr } +}; + +String get_dotnet_arch() { + String arch = Engine::get_singleton()->get_architecture_name(); + + int idx = 0; + while (arch_name_map[idx][0] != nullptr) { + if (arch_name_map[idx][0] == arch) { + return arch_name_map[idx][1]; + } + idx++; + } + + return ""; +} + +bool get_default_installation_dir(String &r_dotnet_root) { +#if defined(WINDOWS_ENABLED) + String program_files_env; + if (is_wow64()) { + // Running x86 on x64, looking for x86 install + program_files_env = "ProgramFiles(x86)"; + } else { + program_files_env = "ProgramFiles"; + } + + String program_files_dir = OS::get_singleton()->get_environment(program_files_env); + + if (program_files_dir.is_empty()) { + return false; + } + +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) + // When emulating x64 on arm + String dotnet_root_emulated = path::join(program_files_dir, "dotnet", "x64"); + if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet.exe"))) { + r_dotnet_root = dotnet_root_emulated; + return true; + } +#endif + + r_dotnet_root = path::join(program_files_dir, "dotnet"); + return true; +#elif defined(MACOS_ENABLED) + r_dotnet_root = "/usr/local/share/dotnet"; + +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) + // When emulating x64 on arm + String dotnet_root_emulated = path::join(r_dotnet_root, "x64"); + if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet"))) { + r_dotnet_root = dotnet_root_emulated; + return true; + } +#endif + + return true; +#else + r_dotnet_root = "/usr/share/dotnet"; + return true; +#endif +} + +bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) { + Error err = OK; + Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err); + + if (f.is_null() || err != OK) { + return false; + } + + String line = f->get_line(); + + if (line.is_empty()) { + return false; + } + + r_dotnet_root = line; + return true; +} + +bool get_dotnet_self_registered_dir(String &r_dotnet_root) { +#if defined(WINDOWS_ENABLED) + String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch(); + Char16String value = String("InstallLocation").utf16(); + + HKEY hkey = NULL; + LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey); + if (result != ERROR_SUCCESS) { + return false; + } + + DWORD size = 0; + result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size); + if (result != ERROR_SUCCESS || size == 0) { + RegCloseKey(hkey); + return false; + } + + Vector<WCHAR> buffer; + buffer.resize(size / sizeof(WCHAR)); + result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size); + if (result != ERROR_SUCCESS) { + RegCloseKey(hkey); + return false; + } + + r_dotnet_root = String::utf16((const char16_t *)buffer.ptr()); + RegCloseKey(hkey); + return true; +#else + String install_location_file = path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower()); + if (get_install_location_from_file(install_location_file, r_dotnet_root)) { + return true; + } + + if (FileAccess::exists(install_location_file)) { + // Don't try with the legacy location, this will fall back to the hard-coded default install location + return false; + } + + String legacy_install_location_file = path::join("/etc/dotnet", "install_location"); + return get_install_location_from_file(legacy_install_location_file, r_dotnet_root); +#endif +} + +bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) { + String env_value = OS::get_singleton()->get_environment(p_env_key); + + if (!env_value.is_empty()) { + env_value = path::realpath(env_value); + + if (DirAccess::exists(env_value)) { + r_dotnet_root = env_value; + return true; + } + } + + return false; +} + +bool get_dotnet_root_from_env(String &r_dotnet_root) { + String dotnet_root_env = "DOTNET_ROOT"; + String arch_for_env = get_dotnet_arch(); + + if (!arch_for_env.is_empty()) { + // DOTNET_ROOT_<arch> + if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) { + return true; + } + } + +#ifdef WINDOWS_ENABLED + // WoW64-only: DOTNET_ROOT(x86) + if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) { + return true; + } +#endif + + // DOTNET_ROOT + return get_file_path_from_env(dotnet_root_env, r_dotnet_root); +} + +} //namespace + +bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) { + String fxr_dir = path::join(p_dotnet_root, "host", "fxr"); + ERR_FAIL_COND_V_MSG(!DirAccess::exists(fxr_dir), false, "The host fxr folder does not exist: " + fxr_dir); + return get_latest_fxr(fxr_dir, r_fxr_path); +} + +bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) { + if (!get_dotnet_root_from_env(r_dotnet_root) && + !get_dotnet_self_registered_dir(r_dotnet_root) && + !get_default_installation_dir(r_dotnet_root)) { + return false; + } + + return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path); +} diff --git a/modules/mono/editor/hostfxr_resolver.h b/modules/mono/editor/hostfxr_resolver.h new file mode 100644 index 0000000000..0f029ab7ae --- /dev/null +++ b/modules/mono/editor/hostfxr_resolver.h @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* hostfxr_resolver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef HOSTFXR_RESOLVER_H +#define HOSTFXR_RESOLVER_H + +#include "core/string/ustring.h" + +namespace godotsharp { +namespace hostfxr_resolver { + +bool try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_out_fxr_path); +bool try_get_path(String &r_out_dotnet_root, String &r_out_fxr_path); + +} //namespace hostfxr_resolver +} //namespace godotsharp + +#endif // HOSTFXR_RESOLVER_H diff --git a/modules/mono/editor/semver.cpp b/modules/mono/editor/semver.cpp new file mode 100644 index 0000000000..1656d0932f --- /dev/null +++ b/modules/mono/editor/semver.cpp @@ -0,0 +1,149 @@ +/*************************************************************************/ +/* semver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "semver.h" + +bool godotsharp::SemVer::parse_digit_only_field(const String &p_field, uint64_t &r_result) { + if (p_field.is_empty()) { + return false; + } + + int64_t integer = 0; + + for (int i = 0; i < p_field.length(); i++) { + char32_t c = p_field[i]; + if (is_digit(c)) { + bool overflow = ((uint64_t)integer > UINT64_MAX / 10) || ((uint64_t)integer == UINT64_MAX / 10 && c > '5'); + ERR_FAIL_COND_V_MSG(overflow, false, "Cannot represent '" + p_field + "' as a 64-bit unsigned integer, since the value is too large."); + integer *= 10; + integer += c - '0'; + } else { + return false; + } + } + + r_result = (uint64_t)integer; + return true; +} + +int godotsharp::SemVer::cmp(const godotsharp::SemVer &p_a, const godotsharp::SemVer &p_b) { + if (p_a.major != p_b.major) { + return p_a.major > p_b.major ? 1 : -1; + } + + if (p_a.minor != p_b.minor) { + return p_a.minor > p_b.minor ? 1 : -1; + } + + if (p_a.patch != p_b.patch) { + return p_a.patch > p_b.patch ? 1 : -1; + } + + if (p_a.prerelease.is_empty() && p_b.prerelease.is_empty()) { + return 0; + } + + if (p_a.prerelease.is_empty() || p_b.prerelease.is_empty()) { + return p_a.prerelease.is_empty() ? 1 : -1; + } + + if (p_a.prerelease != p_b.prerelease) { + // This could be optimized, but I'm too lazy + + Vector<String> a_field_set = p_a.prerelease.split("."); + Vector<String> b_field_set = p_b.prerelease.split("."); + + int a_field_count = a_field_set.size(); + int b_field_count = b_field_set.size(); + + int min_field_count = MIN(a_field_count, b_field_count); + + for (int i = 0; i < min_field_count; i++) { + const String &a_field = a_field_set[i]; + const String &b_field = b_field_set[i]; + + if (a_field == b_field) { + continue; + } + + uint64_t a_num; + bool a_is_digit_only = parse_digit_only_field(a_field, a_num); + + uint64_t b_num; + bool b_is_digit_only = parse_digit_only_field(b_field, b_num); + + if (a_is_digit_only && b_is_digit_only) { + // Identifiers consisting of only digits are compared numerically. + + if (a_num == b_num) { + continue; + } + + return a_num > b_num ? 1 : -1; + } + + if (a_is_digit_only || b_is_digit_only) { + // Numeric identifiers always have lower precedence than non-numeric identifiers. + return b_is_digit_only ? 1 : -1; + } + + // Identifiers with letters or hyphens are compared lexically in ASCII sort order. + return a_field > b_field ? 1 : -1; + } + + if (a_field_count != b_field_count) { + // A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal. + return a_field_count > b_field_count ? 1 : -1; + } + } + + return 0; +} + +bool godotsharp::SemVerParser::parse(const String &p_ver_text, godotsharp::SemVer &r_semver) { + if (!regex.is_valid() && regex.get_pattern().is_empty()) { + regex.compile("^(?P<major>0|[1-9]\\d*)\\.(?P<minor>0|[1-9]\\d*)\\.(?P<patch>0|[1-9]\\d*)(?:-(?P<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"); + ERR_FAIL_COND_V(!regex.is_valid(), false); + } + + Ref<RegExMatch> match = regex.search(p_ver_text); + + if (match.is_valid()) { + r_semver = SemVer( + match->get_string("major").to_int(), + match->get_string("minor").to_int(), + match->get_string("patch").to_int(), + match->get_string("prerelease"), + match->get_string("buildmetadata")); + return true; + } + + return false; +} diff --git a/modules/mono/editor/semver.h b/modules/mono/editor/semver.h new file mode 100644 index 0000000000..48ea8b043e --- /dev/null +++ b/modules/mono/editor/semver.h @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* semver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SEMVER_H +#define SEMVER_H + +#include "core/string/ustring.h" +#include "modules/regex/regex.h" + +// <sys/sysmacros.h> is included somewhere, which defines major(dev) to gnu_dev_major(dev) +#if defined(major) +#undef major +#endif +#if defined(minor) +#undef minor +#endif + +namespace godotsharp { + +struct SemVer { +private: + static bool parse_digit_only_field(const String &p_field, uint64_t &r_result); + + static int cmp(const SemVer &p_a, const SemVer &p_b); + +public: + int major = 0; + int minor = 0; + int patch = 0; + String prerelease; + String build_metadata; + + bool operator==(const SemVer &b) const { + return cmp(*this, b) == 0; + } + + bool operator!=(const SemVer &b) const { + return !operator==(b); + } + + bool operator<(const SemVer &b) const { + return cmp(*this, b) < 0; + } + + bool operator>(const SemVer &b) const { + return cmp(*this, b) > 0; + } + + bool operator<=(const SemVer &b) const { + return cmp(*this, b) <= 0; + } + + bool operator>=(const SemVer &b) const { + return cmp(*this, b) >= 0; + } + + SemVer() {} + + SemVer(int p_major, int p_minor, int p_patch, + const String &p_prerelease, const String &p_build_metadata) : + major(p_major), + minor(p_minor), + patch(p_patch), + prerelease(p_prerelease), + build_metadata(p_build_metadata) { + } +}; + +struct SemVerParser { +private: + RegEx regex; + +public: + bool parse(const String &p_ver_text, SemVer &r_semver); +}; + +} //namespace godotsharp + +#endif // SEMVER_H diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index 17f680361d..d8a6644a66 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -133,15 +133,6 @@ namespace Godot } /// <summary> - /// Returns the area of the <see cref="AABB"/>. - /// </summary> - /// <returns>The area.</returns> - public real_t GetArea() - { - return _size.x * _size.y * _size.z; - } - - /// <summary> /// Gets the position of one of the 8 endpoints of the <see cref="AABB"/>. /// </summary> /// <param name="idx">Which endpoint to get.</param> @@ -321,6 +312,15 @@ namespace Godot } /// <summary> + /// Returns the volume of the <see cref="AABB"/>. + /// </summary> + /// <returns>The volume.</returns> + public real_t GetVolume() + { + return _size.x * _size.y * _size.z; + } + + /// <summary> /// Returns a copy of the <see cref="AABB"/> grown a given amount of units towards all the sides. /// </summary> /// <param name="by">The amount to grow by.</param> @@ -340,30 +340,6 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> is flat or empty, - /// or <see langword="false"/> otherwise. - /// </summary> - /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area. - /// </returns> - public bool HasNoArea() - { - return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; - } - - /// <summary> - /// Returns <see langword="true"/> if the <see cref="AABB"/> has no surface (no size), - /// or <see langword="false"/> otherwise. - /// </summary> - /// <returns> - /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area. - /// </returns> - public bool HasNoSurface() - { - return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; - } - - /// <summary> /// Returns <see langword="true"/> if the <see cref="AABB"/> contains a point, /// or <see langword="false"/> otherwise. /// </summary> @@ -390,6 +366,34 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if the <see cref="AABB"/> + /// has a surface or a length, and <see langword="false"/> + /// if the <see cref="AABB"/> is empty (all components + /// of <see cref="Size"/> are zero or negative). + /// </summary> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has surface. + /// </returns> + public bool HasSurface() + { + return _size.x > 0.0f || _size.y > 0.0f || _size.z > 0.0f; + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="AABB"/> has + /// area, and <see langword="false"/> if the <see cref="AABB"/> + /// is linear, empty, or has a negative <see cref="Size"/>. + /// See also <see cref="GetVolume"/>. + /// </summary> + /// <returns> + /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has volume. + /// </returns> + public bool HasVolume() + { + return _size.x > 0.0f && _size.y > 0.0f && _size.z > 0.0f; + } + + /// <summary> /// Returns the intersection of this <see cref="AABB"/> and <paramref name="with"/>. /// </summary> /// <param name="with">The other <see cref="AABB"/>.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 3884781988..092724a6b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -130,7 +130,6 @@ namespace Godot.Bridge { // Performance is not critical here as this will be replaced with source generators. Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr); - var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); var ctor = scriptType .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) @@ -151,6 +150,8 @@ namespace Godot.Bridge } } + var obj = (Object)FormatterServices.GetUninitializedObject(scriptType); + var parameters = ctor.GetParameters(); int paramCount = parameters.Length; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index b30012d214..f2667c6807 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -634,7 +634,7 @@ namespace Godot /// <param name="outFrom">The start value for the output interpolation.</param> /// <param name="outTo">The destination value for the output interpolation.</param> /// <returns>The resulting mapped value mapped.</returns> - public static real_t RangeLerp(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo) + public static real_t Remap(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo) { return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value)); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index 0b475fec19..e80d75dacf 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -234,15 +234,17 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the <see cref="Rect2"/> is flat or empty, - /// or <see langword="false"/> otherwise. + /// Returns <see langword="true"/> if the <see cref="Rect2"/> has + /// area, and <see langword="false"/> if the <see cref="Rect2"/> + /// is linear, empty, or has a negative <see cref="Size"/>. + /// See also <see cref="GetArea"/>. /// </summary> /// <returns> /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area. /// </returns> - public bool HasNoArea() + public bool HasArea() { - return _size.x <= 0 || _size.y <= 0; + return _size.x > 0.0f && _size.y > 0.0f; } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index 8a2a98d6ee..b2768476cc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -236,15 +236,17 @@ namespace Godot } /// <summary> - /// Returns <see langword="true"/> if the <see cref="Rect2i"/> is flat or empty, - /// or <see langword="false"/> otherwise. + /// Returns <see langword="true"/> if the <see cref="Rect2i"/> has + /// area, and <see langword="false"/> if the <see cref="Rect2i"/> + /// is linear, empty, or has a negative <see cref="Size"/>. + /// See also <see cref="GetArea"/>. /// </summary> /// <returns> /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> has area. /// </returns> - public bool HasNoArea() + public bool HasArea() { - return _size.x <= 0 || _size.y <= 0; + return _size.x > 0 && _size.y > 0; } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs index d1962c68cf..e2da41ff47 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -187,7 +187,7 @@ namespace Godot ( Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight), Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight), - Mathf.CubicInterpolate(y, b.z, preA.z, postB.z, weight), + Mathf.CubicInterpolate(z, b.z, preA.z, postB.z, weight), Mathf.CubicInterpolate(w, b.w, preA.w, postB.w, weight) ); } @@ -212,7 +212,7 @@ namespace Godot ( Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT), Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT), - Mathf.CubicInterpolateInTime(y, b.z, preA.z, postB.z, weight, t, preAT, postBT), + Mathf.CubicInterpolateInTime(z, b.z, preA.z, postB.z, weight, t, preAT, postBT), Mathf.CubicInterpolateInTime(w, b.w, preA.w, postB.w, weight, t, preAT, postBT) ); } @@ -258,7 +258,7 @@ namespace Godot /// <returns>The dot product of the two vectors.</returns> public real_t Dot(Vector4 with) { - return (x * with.x) + (y * with.y) + (z * with.z) + (w + with.w); + return (x * with.x) + (y * with.y) + (z * with.z) + (w * with.w); } /// <summary> @@ -455,6 +455,7 @@ namespace Godot /// This can also be used to round to an arbitrary number of decimals. /// </summary> /// <param name="step">A vector value representing the step size to snap to.</param> + /// <returns>The snapped vector.</returns> public Vector4 Snapped(Vector4 step) { return new Vector4( @@ -632,6 +633,56 @@ namespace Godot } /// <summary> + /// Gets the remainder of each component of the <see cref="Vector4"/> + /// with the components of the given <see cref="real_t"/>. + /// This operation uses truncated division, which is often not desired + /// as it does not work well with negative numbers. + /// Consider using <see cref="PosMod(real_t)"/> instead + /// if you want to handle negative numbers. + /// </summary> + /// <example> + /// <code> + /// GD.Print(new Vector4(10, -20, 30, 40) % 7); // Prints "(3, -6, 2, 5)" + /// </code> + /// </example> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisor">The divisor value.</param> + /// <returns>The remainder vector.</returns> + public static Vector4 operator %(Vector4 vec, real_t divisor) + { + vec.x %= divisor; + vec.y %= divisor; + vec.z %= divisor; + vec.w %= divisor; + return vec; + } + + /// <summary> + /// Gets the remainder of each component of the <see cref="Vector4"/> + /// with the components of the given <see cref="Vector4"/>. + /// This operation uses truncated division, which is often not desired + /// as it does not work well with negative numbers. + /// Consider using <see cref="PosMod(Vector4)"/> instead + /// if you want to handle negative numbers. + /// </summary> + /// <example> + /// <code> + /// GD.Print(new Vector4(10, -20, 30, 10) % new Vector4(7, 8, 9, 10)); // Prints "(3, -4, 3, 0)" + /// </code> + /// </example> + /// <param name="vec">The dividend vector.</param> + /// <param name="divisorv">The divisor vector.</param> + /// <returns>The remainder vector.</returns> + public static Vector4 operator %(Vector4 vec, Vector4 divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + vec.z %= divisorv.z; + vec.w %= divisorv.w; + return vec; + } + + /// <summary> /// Returns <see langword="true"/> if the vectors are exactly equal. /// Note: Due to floating-point precision errors, consider using /// <see cref="IsEqualApprox"/> instead, which is more reliable. diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index e698e92d7a..3b87d9248a 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -43,12 +43,13 @@ #include "../utils/path_utils.h" #include "gd_mono_cache.h" +#include "../thirdparty/coreclr_delegates.h" +#include "../thirdparty/hostfxr.h" + #ifdef TOOLS_ENABLED -#include <nethost.h> +#include "../editor/hostfxr_resolver.h" #endif -#include <coreclr_delegates.h> -#include <hostfxr.h> #ifdef UNIX_ENABLED #include <dlfcn.h> #endif @@ -88,85 +89,43 @@ HostFxrCharString str_to_hostfxr(const String &p_str) { #endif } -#ifdef TOOLS_ENABLED -String str_from_hostfxr(const char_t *p_buffer) { -#ifdef _WIN32 - return String::utf16((const char16_t *)p_buffer); -#else - return String::utf8((const char *)p_buffer); -#endif -} -#endif - const char_t *get_data(const HostFxrCharString &p_char_str) { return (const char_t *)p_char_str.get_data(); } -#ifdef TOOLS_ENABLED -String find_hostfxr(size_t p_known_buffer_size, get_hostfxr_parameters *p_get_hostfxr_params) { - // Pre-allocate a large buffer for the path to hostfxr - Vector<char_t> buffer; - buffer.resize(p_known_buffer_size); - - int rc = get_hostfxr_path(buffer.ptrw(), &p_known_buffer_size, p_get_hostfxr_params); - - ERR_FAIL_COND_V_MSG(rc != 0, String(), "get_hostfxr_path failed with code: " + itos(rc)); - - return str_from_hostfxr(buffer.ptr()); -} -#endif - String find_hostfxr() { #ifdef TOOLS_ENABLED - const int CoreHostLibMissingFailure = 0x80008083; - const int HostApiBufferTooSmall = 0x80008098; - - size_t buffer_size = 0; - int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr); - - if (rc == HostApiBufferTooSmall) { - return find_hostfxr(buffer_size, nullptr); + String dotnet_root; + String fxr_path; + if (godotsharp::hostfxr_resolver::try_get_path(dotnet_root, fxr_path)) { + return fxr_path; } - if (rc == CoreHostLibMissingFailure) { - // Apparently `get_hostfxr_path` doesn't look for dotnet in `PATH`? (I suppose it needs the - // `DOTNET_ROOT` environment variable). If it fails, we try to find the dotnet executable - // in `PATH` ourselves and pass its location as `dotnet_root` to `get_hostfxr_path`. - String dotnet_exe = path::find_executable("dotnet"); - - if (!dotnet_exe.is_empty()) { - // The file found in PATH may be a symlink - dotnet_exe = path::abspath(path::realpath(dotnet_exe)); - - // TODO: - // Sometimes, the symlink may not point to the dotnet executable in the dotnet root. - // That's the case with snaps. The snap install should have been found with the - // previous `get_hostfxr_path`, but it would still be better to do this properly - // and use something like `dotnet --list-sdks/runtimes` to find the actual location. - // This way we could also check if the proper sdk or runtime is installed. This would - // allow us to fail gracefully and show some helpful information in the editor. - - HostFxrCharString dotnet_root = str_to_hostfxr(dotnet_exe.get_base_dir()); - - get_hostfxr_parameters get_hostfxr_parameters = { - sizeof(get_hostfxr_parameters), - nullptr, - get_data(dotnet_root) - }; - - buffer_size = 0; - rc = get_hostfxr_path(nullptr, &buffer_size, &get_hostfxr_parameters); - if (rc == HostApiBufferTooSmall) { - return find_hostfxr(buffer_size, &get_hostfxr_parameters); - } + // hostfxr_resolver doesn't look for dotnet in `PATH`. If it fails, we try to find the dotnet + // executable in `PATH` here and pass its location as `dotnet_root` to `get_hostfxr_path`. + String dotnet_exe = path::find_executable("dotnet"); + + if (!dotnet_exe.is_empty()) { + // The file found in PATH may be a symlink + dotnet_exe = path::abspath(path::realpath(dotnet_exe)); + + // TODO: + // Sometimes, the symlink may not point to the dotnet executable in the dotnet root. + // That's the case with snaps. The snap install should have been found with the + // previous `get_hostfxr_path`, but it would still be better to do this properly + // and use something like `dotnet --list-sdks/runtimes` to find the actual location. + // This way we could also check if the proper sdk or runtime is installed. This would + // allow us to fail gracefully and show some helpful information in the editor. + + dotnet_root = dotnet_exe.get_base_dir(); + if (godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(dotnet_root, fxr_path)) { + return fxr_path; } } - if (rc == CoreHostLibMissingFailure) { - ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " + - "Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " + - "libraries are not present in the expected locations."); - } + ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " + + "Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " + + "libraries are not present in the expected locations."); return String(); #else 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/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 55d2138674..66c9eca616 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -42,7 +42,7 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signa SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, awaiter_handle, p_signal)); Callable callable = Callable(awaiter_callable); - return p_source->connect(p_signal, callable, Object::CONNECT_ONESHOT); + return p_source->connect(p_signal, callable, Object::CONNECT_ONE_SHOT); } bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { diff --git a/modules/mono/thirdparty/coreclr_delegates.h b/modules/mono/thirdparty/coreclr_delegates.h new file mode 100644 index 0000000000..914ab592df --- /dev/null +++ b/modules/mono/thirdparty/coreclr_delegates.h @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __CORECLR_DELEGATES_H__ +#define __CORECLR_DELEGATES_H__ + +#include <stdint.h> + +#if defined(_WIN32) + #define CORECLR_DELEGATE_CALLTYPE __stdcall + #ifdef _WCHAR_T_DEFINED + typedef wchar_t char_t; + #else + typedef unsigned short char_t; + #endif +#else + #define CORECLR_DELEGATE_CALLTYPE + typedef char char_t; +#endif + +#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1) + +// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer +typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_and_get_function_pointer_fn)( + const char_t *assembly_path /* Fully qualified path to assembly */, + const char_t *type_name /* Assembly qualified type name */, + const char_t *method_name /* Public static method name compatible with delegateType */, + const char_t *delegate_type_name /* Assembly qualified delegate type name or null + or UNMANAGEDCALLERSONLY_METHOD if the method is marked with + the UnmanagedCallersOnlyAttribute. */, + void *reserved /* Extensibility parameter (currently unused and must be 0) */, + /*out*/ void **delegate /* Pointer where to store the function pointer result */); + +// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default) +typedef int (CORECLR_DELEGATE_CALLTYPE *component_entry_point_fn)(void *arg, int32_t arg_size_in_bytes); + +typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)( + const char_t *type_name /* Assembly qualified type name */, + const char_t *method_name /* Public static method name compatible with delegateType */, + const char_t *delegate_type_name /* Assembly qualified delegate type name or null, + or UNMANAGEDCALLERSONLY_METHOD if the method is marked with + the UnmanagedCallersOnlyAttribute. */, + void *load_context /* Extensibility parameter (currently unused and must be 0) */, + void *reserved /* Extensibility parameter (currently unused and must be 0) */, + /*out*/ void **delegate /* Pointer where to store the function pointer result */); + +#endif // __CORECLR_DELEGATES_H__ diff --git a/modules/mono/thirdparty/hostfxr.h b/modules/mono/thirdparty/hostfxr.h new file mode 100644 index 0000000000..591a8ebbea --- /dev/null +++ b/modules/mono/thirdparty/hostfxr.h @@ -0,0 +1,323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __HOSTFXR_H__ +#define __HOSTFXR_H__ + +#include <stddef.h> +#include <stdint.h> + +#if defined(_WIN32) + #define HOSTFXR_CALLTYPE __cdecl + #ifdef _WCHAR_T_DEFINED + typedef wchar_t char_t; + #else + typedef unsigned short char_t; + #endif +#else + #define HOSTFXR_CALLTYPE + typedef char char_t; +#endif + +enum hostfxr_delegate_type +{ + hdt_com_activation, + hdt_load_in_memory_assembly, + hdt_winrt_activation, + hdt_com_register, + hdt_com_unregister, + hdt_load_assembly_and_get_function_pointer, + hdt_get_function_pointer, +}; + +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv); +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_startupinfo_fn)( + const int argc, + const char_t **argv, + const char_t *host_path, + const char_t *dotnet_root, + const char_t *app_path); +typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)( + const int argc, + const char_t** argv, + const char_t* host_path, + const char_t* dotnet_root, + const char_t* app_path, + int64_t bundle_header_offset); + +typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message); + +// +// Sets a callback which is to be used to write errors to. +// +// Parameters: +// error_writer +// A callback function which will be invoked every time an error is to be reported. +// Or nullptr to unregister previously registered callback and return to the default behavior. +// Return value: +// The previously registered callback (which is now unregistered), or nullptr if no previous callback +// was registered +// +// The error writer is registered per-thread, so the registration is thread-local. On each thread +// only one callback can be registered. Subsequent registrations overwrite the previous ones. +// +// By default no callback is registered in which case the errors are written to stderr. +// +// Each call to the error writer is sort of like writing a single line (the EOL character is omitted). +// Multiple calls to the error writer may occur for one failure. +// +// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer +// will be propagated to hostpolicy for the duration of the call. This means that errors from +// both hostfxr and hostpolicy will be reporter through the same error writer. +// +typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE *hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer); + +typedef void* hostfxr_handle; +struct hostfxr_initialize_parameters +{ + size_t size; + const char_t *host_path; + const char_t *dotnet_root; +}; + +// +// Initializes the hosting components for a dotnet command line running an application +// +// Parameters: +// argc +// Number of argv arguments +// argv +// Command-line arguments for running an application (as if through the dotnet executable). +// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported. +// For example 'app.dll app_argument_1 app_argument_2`. +// parameters +// Optional. Additional parameters for initialization +// host_context_handle +// On success, this will be populated with an opaque value representing the initialized host context +// +// Return value: +// Success - Hosting components were successfully initialized +// HostInvalidState - Hosting components are already initialized +// +// This function parses the specified command-line arguments to determine the application to run. It will +// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and +// dependencies and prepare everything needed to load the runtime. +// +// This function only supports arguments for running an application. It does not support SDK commands. +// +// This function does not load the runtime. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)( + int argc, + const char_t **argv, + const struct hostfxr_initialize_parameters *parameters, + /*out*/ hostfxr_handle *host_context_handle); + +// +// Initializes the hosting components using a .runtimeconfig.json file +// +// Parameters: +// runtime_config_path +// Path to the .runtimeconfig.json file +// parameters +// Optional. Additional parameters for initialization +// host_context_handle +// On success, this will be populated with an opaque value representing the initialized host context +// +// Return value: +// Success - Hosting components were successfully initialized +// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components +// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components +// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components +// +// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed +// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that +// may be next to the .runtimeconfig.json). +// +// This function does not load the runtime. +// +// If called when the runtime has already been loaded, this function will check if the specified runtime +// config is compatible with the existing runtime. +// +// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful +// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that +// the difference in properties is acceptable. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_runtime_config_fn)( + const char_t *runtime_config_path, + const struct hostfxr_initialize_parameters *parameters, + /*out*/ hostfxr_handle *host_context_handle); + +// +// Gets the runtime property value for an initialized host context +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// name +// Runtime property name +// value +// Out parameter. Pointer to a buffer with the property value. +// +// Return value: +// The error code result. +// +// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only +// guaranteed until any of the below occur: +// - a 'run' method is called for the host context +// - properties are changed via hostfxr_set_runtime_property_value +// - the host context is closed via 'hostfxr_close' +// +// If host_context_handle is nullptr and an active host context exists, this function will get the +// property value for the active host context. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_property_value_fn)( + const hostfxr_handle host_context_handle, + const char_t *name, + /*out*/ const char_t **value); + +// +// Sets the value of a runtime property for an initialized host context +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// name +// Runtime property name +// value +// Value to set +// +// Return value: +// The error code result. +// +// Setting properties is only supported for the first host context, before the runtime has been loaded. +// +// If the property already exists in the host context, it will be overwritten. If value is nullptr, the +// property will be removed. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_set_runtime_property_value_fn)( + const hostfxr_handle host_context_handle, + const char_t *name, + const char_t *value); + +// +// Gets all the runtime properties for an initialized host context +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// count +// [in] Size of the keys and values buffers +// [out] Number of properties returned (size of keys/values buffers used). If the input value is too +// small or keys/values is nullptr, this is populated with the number of available properties +// keys +// Array of pointers to buffers with runtime property keys +// values +// Array of pointers to buffers with runtime property values +// +// Return value: +// The error code result. +// +// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only +// guaranteed until any of the below occur: +// - a 'run' method is called for the host context +// - properties are changed via hostfxr_set_runtime_property_value +// - the host context is closed via 'hostfxr_close' +// +// If host_context_handle is nullptr and an active host context exists, this function will get the +// properties for the active host context. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_properties_fn)( + const hostfxr_handle host_context_handle, + /*inout*/ size_t * count, + /*out*/ const char_t **keys, + /*out*/ const char_t **values); + +// +// Load CoreCLR and run the application for an initialized host context +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// +// Return value: +// If the app was successfully run, the exit code of the application. Otherwise, the error code result. +// +// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line. +// +// This function will not return until the managed application exits. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_run_app_fn)(const hostfxr_handle host_context_handle); + +// +// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one. +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// type +// Type of runtime delegate requested +// delegate +// An out parameter that will be assigned the delegate. +// +// Return value: +// The error code result. +// +// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config, +// then all delegate types are supported. +// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line, +// then only the following delegate types are currently supported: +// hdt_load_assembly_and_get_function_pointer +// hdt_get_function_pointer +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)( + const hostfxr_handle host_context_handle, + enum hostfxr_delegate_type type, + /*out*/ void **delegate); + +// +// Closes an initialized host context +// +// Parameters: +// host_context_handle +// Handle to the initialized host context +// +// Return value: +// The error code result. +// +typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle); + +struct hostfxr_dotnet_environment_sdk_info +{ + size_t size; + const char_t* version; + const char_t* path; +}; + +typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)( + const struct hostfxr_dotnet_environment_info* info, + void* result_context); + +struct hostfxr_dotnet_environment_framework_info +{ + size_t size; + const char_t* name; + const char_t* version; + const char_t* path; +}; + +struct hostfxr_dotnet_environment_info +{ + size_t size; + + const char_t* hostfxr_version; + const char_t* hostfxr_commit_hash; + + size_t sdk_count; + const struct hostfxr_dotnet_environment_sdk_info* sdks; + + size_t framework_count; + const struct hostfxr_dotnet_environment_framework_info* frameworks; +}; + +#endif //__HOSTFXR_H__ diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index e8f3aecc69..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() { @@ -230,8 +228,8 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s ObjectID oid = p_node->get_instance_id(); if (!tracked_nodes.has(oid)) { tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id); - p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONESHOT); - p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONESHOT); + p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); + p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT); } } @@ -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/multiplayer/scene_replication_state.cpp b/modules/multiplayer/scene_replication_state.cpp index 25442bb7fa..fbcf0acadb 100644 --- a/modules/multiplayer/scene_replication_state.cpp +++ b/modules/multiplayer/scene_replication_state.cpp @@ -39,7 +39,7 @@ SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID if (!tracked_nodes.has(p_id)) { tracked_nodes[p_id] = TrackedNode(p_id); Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); - node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack).bind(p_id), Node::CONNECT_ONESHOT); + node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack).bind(p_id), Node::CONNECT_ONE_SHOT); } return tracked_nodes[p_id]; } diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp index 5cdff7b52a..32abc52017 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp @@ -145,7 +145,7 @@ void NavigationMeshEditorPlugin::make_visible(bool p_visible) { NavigationMeshEditorPlugin::NavigationMeshEditorPlugin() { navigation_mesh_editor = memnew(NavigationMeshEditor); - EditorNode::get_singleton()->get_main_control()->add_child(navigation_mesh_editor); + EditorNode::get_singleton()->get_main_screen_control()->add_child(navigation_mesh_editor); add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox); navigation_mesh_editor->hide(); navigation_mesh_editor->bake_hbox->hide(); diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 848e554fb0..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; @@ -572,12 +575,8 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh( cfg.bmax[2] = bmax[2]; AABB baking_aabb = p_nav_mesh->get_filter_baking_aabb(); - - bool aabb_has_no_volume = baking_aabb.has_no_volume(); - - if (!aabb_has_no_volume) { + if (baking_aabb.has_volume()) { Vector3 baking_aabb_offset = p_nav_mesh->get_filter_baking_aabb_offset(); - cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x; cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y; cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z; diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index fcbe4d57f6..51c402d746 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -57,7 +57,7 @@ void OpenXRActionMapEditor::_notification(int p_what) { for (int i = 0; i < tabs->get_child_count(); i++) { Control *tab = static_cast<Control *>(tabs->get_child(i)); if (tab) { - tab->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + tab->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } } } break; @@ -113,7 +113,7 @@ OpenXRInteractionProfileEditorBase *OpenXRActionMapEditor::_add_interaction_prof // now add it in.. ERR_FAIL_NULL_V(new_profile_editor, nullptr); tabs->add_child(new_profile_editor); - new_profile_editor->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + new_profile_editor->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar"))); interaction_profiles.push_back(new_profile_editor); diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp index 80e58044d5..5f018291d5 100644 --- a/modules/openxr/editor/openxr_select_action_dialog.cpp +++ b/modules/openxr/editor/openxr_select_action_dialog.cpp @@ -39,7 +39,7 @@ void OpenXRSelectActionDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + scroll->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } break; } } diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp index 23b025db08..e92519aec2 100644 --- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp +++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp @@ -38,7 +38,7 @@ void OpenXRSelectInteractionProfileDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + scroll->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } break; } } 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; diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 7d73194ea9..1978d2e7c6 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -24,8 +24,8 @@ If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a multiplayer peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]). You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request. - [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's SSL certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the SSL certificate. - [b]Note:[/b] Specifying [code]custom_headers[/code] is not supported in Web exports due to browsers restrictions. + [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's TLS certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the TLS certificate. + [b]Note:[/b] Specifying [code]custom_headers[/code] is not supported in Web exports due to browsers' restrictions. </description> </method> <method name="disconnect_from_host"> @@ -50,12 +50,12 @@ </method> </methods> <members> - <member name="trusted_ssl_certificate" type="X509Certificate" setter="set_trusted_ssl_certificate" getter="get_trusted_ssl_certificate"> - If specified, this [X509Certificate] will be the only one accepted when connecting to an SSL host. Any other certificate provided by the server will be regarded as invalid. - [b]Note:[/b] Specifying a custom [code]trusted_ssl_certificate[/code] is not supported in Web exports due to browsers restrictions. + <member name="trusted_tls_certificate" type="X509Certificate" setter="set_trusted_tls_certificate" getter="get_trusted_tls_certificate"> + If specified, this [X509Certificate] will be the only one accepted when connecting to an TLS host. Any other certificate provided by the server will be regarded as invalid. + [b]Note:[/b] Specifying a custom [code]trusted_tls_certificate[/code] is not supported in Web exports due to browsers' restrictions. </member> - <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled"> - If [code]true[/code], SSL certificate verification is enabled. + <member name="verify_tls" type="bool" setter="set_verify_tls_enabled" getter="is_verify_tls_enabled"> + If [code]true[/code], TLS certificate verification is enabled. [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported. </member> </members> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 19c36700e6..07a55b73f1 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -79,16 +79,16 @@ When not set to [code]*[/code] will restrict incoming connections to the specified IP address. Setting [code]bind_ip[/code] to [code]127.0.0.1[/code] will cause the server to listen only to the local host. </member> <member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain"> - When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake. + When using TLS (see [member private_key] and [member tls_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the TLS handshake. </member> <member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0"> The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected. </member> <member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key"> - When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol). + When set to a valid [CryptoKey] (along with [member tls_certificate]) will cause the server to require TLS instead of regular TCP (i.e. the [code]wss://[/code] protocol). </member> - <member name="ssl_certificate" type="X509Certificate" setter="set_ssl_certificate" getter="get_ssl_certificate"> - When set to a valid [X509Certificate] (along with [member private_key]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol). + <member name="tls_certificate" type="X509Certificate" setter="set_tls_certificate" getter="get_tls_certificate"> + When set to a valid [X509Certificate] (along with [member private_key]) will cause the server to require TLS instead of regular TCP (i.e. the [code]wss://[/code] protocol). </member> </members> <signals> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 65e0703c00..933a1f43e9 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -65,7 +65,7 @@ void EMWSClient::_esws_on_close(void *obj, int code, const char *reason, int was client->_on_disconnect(was_clean != 0); } -Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_tls, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { if (_js_id) { godot_js_websocket_destroy(_js_id); _js_id = 0; @@ -84,9 +84,9 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, if (p_custom_headers.size()) { WARN_PRINT_ONCE("Custom headers are not supported in Web platform."); } - if (p_ssl) { + if (p_tls) { str = "wss://"; - if (ssl_cert.is_valid()) { + if (tls_cert.is_valid()) { WARN_PRINT_ONCE("Custom SSL certificate is not supported in Web platform."); } } diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index ff63a76753..cdcec31e19 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -54,7 +54,7 @@ private: public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_tls, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; Ref<WebSocketPeer> get_peer(int p_peer_id) const override; void disconnect_from_host(int p_code = 1000, String p_reason = "") override; IPAddress get_connected_host() const override; diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 2734b4b88f..0b2d5d1918 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -48,34 +48,34 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto Error err = p_url.parse_url(scheme, host, port, path); ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url); - bool ssl = false; + bool tls = false; if (scheme == "wss://") { - ssl = true; + tls = true; } if (port == 0) { - port = ssl ? 443 : 80; + port = tls ? 443 : 80; } if (path.is_empty()) { path = "/"; } - return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers); + return connect_to_host(host, path, port, tls, p_protocols, p_custom_headers); } -void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { - verify_ssl = p_verify_ssl; +void WebSocketClient::set_verify_tls_enabled(bool p_verify_tls) { + verify_tls = p_verify_tls; } -bool WebSocketClient::is_verify_ssl_enabled() const { - return verify_ssl; +bool WebSocketClient::is_verify_tls_enabled() const { + return verify_tls; } -Ref<X509Certificate> WebSocketClient::get_trusted_ssl_certificate() const { - return ssl_cert; +Ref<X509Certificate> WebSocketClient::get_trusted_tls_certificate() const { + return tls_cert; } -void WebSocketClient::set_trusted_ssl_certificate(Ref<X509Certificate> p_cert) { +void WebSocketClient::set_trusted_tls_certificate(Ref<X509Certificate> p_cert) { ERR_FAIL_COND(get_connection_status() != CONNECTION_DISCONNECTED); - ssl_cert = p_cert; + tls_cert = p_cert; } bool WebSocketClient::is_server() const { @@ -123,15 +123,15 @@ void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketClient::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketClient::get_connected_port); - ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); - ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); + ClassDB::bind_method(D_METHOD("set_verify_tls_enabled", "enabled"), &WebSocketClient::set_verify_tls_enabled); + ClassDB::bind_method(D_METHOD("is_verify_tls_enabled"), &WebSocketClient::is_verify_tls_enabled); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_tls", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_tls_enabled", "is_verify_tls_enabled"); - ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate", "cert"), &WebSocketClient::set_trusted_ssl_certificate); + ClassDB::bind_method(D_METHOD("get_trusted_tls_certificate"), &WebSocketClient::get_trusted_tls_certificate); + ClassDB::bind_method(D_METHOD("set_trusted_tls_certificate", "cert"), &WebSocketClient::set_trusted_tls_certificate); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_tls_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_tls_certificate", "get_trusted_tls_certificate"); ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index d6c072ae16..e747aee4e4 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -42,20 +42,20 @@ class WebSocketClient : public WebSocketMultiplayerPeer { protected: Ref<WebSocketPeer> _peer; - bool verify_ssl = true; - Ref<X509Certificate> ssl_cert; + bool verify_tls = true; + Ref<X509Certificate> tls_cert; static void _bind_methods(); public: Error connect_to_url(String p_url, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false, const Vector<String> p_custom_headers = Vector<String>()); - void set_verify_ssl_enabled(bool p_verify_ssl); - bool is_verify_ssl_enabled() const; - Ref<X509Certificate> get_trusted_ssl_certificate() const; - void set_trusted_ssl_certificate(Ref<X509Certificate> p_cert); + void set_verify_tls_enabled(bool p_verify_tls); + bool is_verify_tls_enabled() const; + Ref<X509Certificate> get_trusted_tls_certificate() const; + void set_trusted_tls_certificate(Ref<X509Certificate> p_cert); - virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0; + virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_tls, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0; virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual IPAddress get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index b7851b02c4..25a6e420fc 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -58,9 +58,9 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_private_key", "key"), &WebSocketServer::set_private_key); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", PROPERTY_USAGE_NONE), "set_private_key", "get_private_key"); - ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_ssl_certificate", "cert"), &WebSocketServer::set_ssl_certificate); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ssl_certificate", "get_ssl_certificate"); + ClassDB::bind_method(D_METHOD("get_tls_certificate"), &WebSocketServer::get_tls_certificate); + ClassDB::bind_method(D_METHOD("set_tls_certificate", "cert"), &WebSocketServer::set_tls_certificate); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tls_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_tls_certificate", "get_tls_certificate"); ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain); ClassDB::bind_method(D_METHOD("set_ca_chain", "ca_chain"), &WebSocketServer::set_ca_chain); @@ -95,13 +95,13 @@ void WebSocketServer::set_private_key(Ref<CryptoKey> p_key) { private_key = p_key; } -Ref<X509Certificate> WebSocketServer::get_ssl_certificate() const { - return ssl_cert; +Ref<X509Certificate> WebSocketServer::get_tls_certificate() const { + return tls_cert; } -void WebSocketServer::set_ssl_certificate(Ref<X509Certificate> p_cert) { +void WebSocketServer::set_tls_certificate(Ref<X509Certificate> p_cert) { ERR_FAIL_COND(is_listening()); - ssl_cert = p_cert; + tls_cert = p_cert; } Ref<X509Certificate> WebSocketServer::get_ca_chain() const { diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index ac04c4e57e..de23ee884d 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -46,7 +46,7 @@ protected: static void _bind_methods(); Ref<CryptoKey> private_key; - Ref<X509Certificate> ssl_cert; + Ref<X509Certificate> tls_cert; Ref<X509Certificate> ca_chain; uint32_t handshake_timeout = 3000; @@ -74,8 +74,8 @@ public: Ref<CryptoKey> get_private_key() const; void set_private_key(Ref<CryptoKey> p_key); - Ref<X509Certificate> get_ssl_certificate() const; - void set_ssl_certificate(Ref<X509Certificate> p_cert); + Ref<X509Certificate> get_tls_certificate() const; + void set_tls_certificate(Ref<X509Certificate> p_cert); Ref<X509Certificate> get_ca_chain() const; void set_ca_chain(Ref<X509Certificate> p_ca_chain); diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 2bb57226ea..50ef53e267 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -161,7 +161,7 @@ bool WSLClient::_verify_headers(String &r_protocol) { return true; } -Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { +Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_tls, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); @@ -196,7 +196,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, return err; } _connection = _tcp; - _use_ssl = p_ssl; + _use_tls = p_tls; _host = p_host; _port = p_port; // Strip edges from protocols. @@ -209,7 +209,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _key = WSLPeer::generate_key(); String request = "GET " + p_path + " HTTP/1.1\r\n"; String port = ""; - if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { + if ((p_port != 80 && !p_tls) || (p_port != 443 && p_tls)) { port = ":" + itos(p_port); } request += "Host: " + p_host + port + "\r\n"; @@ -288,27 +288,27 @@ void WSLClient::poll() { break; case StreamPeerTCP::STATUS_CONNECTED: { _ip_candidates.clear(); - Ref<StreamPeerSSL> ssl; - if (_use_ssl) { + Ref<StreamPeerTLS> tls; + if (_use_tls) { if (_connection == _tcp) { // Start SSL handshake - ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); - ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build."); - ssl->set_blocking_handshake_enabled(false); - if (ssl->connect_to_stream(_tcp, verify_ssl, _host, ssl_cert) != OK) { + tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); + ERR_FAIL_COND_MSG(tls.is_null(), "SSL is not available in this build."); + tls->set_blocking_handshake_enabled(false); + if (tls->connect_to_stream(_tcp, verify_tls, _host, tls_cert) != OK) { disconnect_from_host(); _on_error(); return; } - _connection = ssl; + _connection = tls; } else { - ssl = static_cast<Ref<StreamPeerSSL>>(_connection); - ERR_FAIL_COND(ssl.is_null()); // Bug? - ssl->poll(); + tls = static_cast<Ref<StreamPeerTLS>>(_connection); + ERR_FAIL_COND(tls.is_null()); // Bug? + tls->poll(); } - if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { + if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) { return; // Need more polling. - } else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) { disconnect_from_host(); _on_error(); return; // Error. @@ -356,7 +356,7 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { _key = ""; _host = ""; _protocols.clear(); - _use_ssl = false; + _use_tls = false; _request = ""; _requested = 0; diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 5d90bc4034..dfb989fdd3 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -34,8 +34,8 @@ #ifndef WEB_ENABLED #include "core/error/error_list.h" -#include "core/io/stream_peer_ssl.h" #include "core/io/stream_peer_tcp.h" +#include "core/io/stream_peer_tls.h" #include "websocket_client.h" #include "wsl_peer.h" #include "wslay/wslay.h" @@ -65,7 +65,7 @@ private: uint16_t _port = 0; Array _ip_candidates; Vector<String> _protocols; - bool _use_ssl = false; + bool _use_tls = false; IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID; void _do_handshake(); @@ -73,7 +73,7 @@ private: public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_tls, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; int get_max_packet_size() const override; Ref<WebSocketPeer> get_peer(int p_peer_id) const override; void disconnect_from_host(int p_code = 1000, String p_reason = "") override; diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 7457ac7087..01dcd53839 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -102,16 +102,16 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uin return ERR_TIMEOUT; } - if (use_ssl) { - Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL>>(connection); - if (ssl.is_null()) { - ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerSSL for WebSocket handshake."); + if (use_tls) { + Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(connection); + if (tls.is_null()) { + ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerTLS for WebSocket handshake."); } - ssl->poll(); - if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { + tls->poll(); + if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) { return ERR_BUSY; - } else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { - print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerSSL status code %d).", ssl->get_status())); + } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) { + print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerTLS status code %d).", tls->get_status())); return FAILED; } } @@ -247,12 +247,12 @@ void WSLServer::poll() { } Ref<PendingPeer> peer = memnew(PendingPeer); - if (private_key.is_valid() && ssl_cert.is_valid()) { - Ref<StreamPeerSSL> ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); - ssl->set_blocking_handshake_enabled(false); - ssl->accept_stream(conn, private_key, ssl_cert, ca_chain); - peer->connection = ssl; - peer->use_ssl = true; + if (private_key.is_valid() && tls_cert.is_valid()) { + Ref<StreamPeerTLS> tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); + tls->set_blocking_handshake_enabled(false); + tls->accept_stream(conn, private_key, tls_cert, ca_chain); + peer->connection = tls; + peer->use_tls = true; } else { peer->connection = conn; } diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index b0b7a6a5c9..df0c1dc68a 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -36,8 +36,8 @@ #include "websocket_server.h" #include "wsl_peer.h" -#include "core/io/stream_peer_ssl.h" #include "core/io/stream_peer_tcp.h" +#include "core/io/stream_peer_tls.h" #include "core/io/tcp_server.h" class WSLServer : public WebSocketServer { @@ -51,7 +51,7 @@ private: public: Ref<StreamPeerTCP> tcp; Ref<StreamPeer> connection; - bool use_ssl = false; + bool use_tls = false; uint64_t time = 0; uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {}; |