diff options
148 files changed, 1619 insertions, 955 deletions
diff --git a/SConstruct b/SConstruct index 128c5b0e92..cdbb47faea 100644 --- a/SConstruct +++ b/SConstruct @@ -189,7 +189,7 @@ Help(opts.GenerateHelpText(env_base)) # generate help # add default include paths -env_base.Prepend(CPPPATH=['#', '#editor']) +env_base.Prepend(CPPPATH=['#']) # configure ENV for platform env_base.platform_exporters = platform_exporters diff --git a/core/cowdata.h b/core/cowdata.h index 3e40ad0f4b..c92e20920c 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -33,6 +33,7 @@ #include <string.h> +#include "core/error_macros.h" #include "core/os/memory.h" #include "core/safe_refcount.h" diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 271e5c5a0b..5684c82d1c 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -123,6 +123,13 @@ void ConfigFile::erase_section(const String &p_section) { values.erase(p_section); } +void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { + + ERR_FAIL_COND_MSG(!values.has(p_section), "Cannot erase key from nonexistent section '" + p_section + "'."); + + values[p_section].erase(p_key); +} + Error ConfigFile::save(const String &p_path) { Error err; @@ -293,6 +300,7 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("get_section_keys", "section"), &ConfigFile::_get_section_keys); ClassDB::bind_method(D_METHOD("erase_section", "section"), &ConfigFile::erase_section); + ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key); ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); diff --git a/core/io/config_file.h b/core/io/config_file.h index 3ab6fef868..d927779f9c 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -60,6 +60,7 @@ public: void get_section_keys(const String &p_section, List<String> *r_keys) const; void erase_section(const String &p_section); + void erase_section_key(const String &p_section, const String &p_key); Error save(const String &p_path); Error load(const String &p_path); diff --git a/core/list.h b/core/list.h index d1b528562d..c46888e01c 100644 --- a/core/list.h +++ b/core/list.h @@ -31,6 +31,7 @@ #ifndef GLOBALS_LIST_H #define GLOBALS_LIST_H +#include "core/error_macros.h" #include "core/os/memory.h" #include "core/sort_array.h" diff --git a/core/map.h b/core/map.h index c87ee42e1b..77e73d70cb 100644 --- a/core/map.h +++ b/core/map.h @@ -31,6 +31,7 @@ #ifndef MAP_H #define MAP_H +#include "core/error_macros.h" #include "core/set.h" // based on the very nice implementation of rb-trees by: diff --git a/core/math/basis.h b/core/math/basis.h index 053effda69..4be4ea4cd3 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -28,13 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -// Circular dependency between Vector3 and Basis :/ -#include "core/math/vector3.h" - #ifndef BASIS_H #define BASIS_H #include "core/math/quat.h" +#include "core/math/vector3.h" class Basis { public: diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index f04e40cb6c..50fcdb2c13 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -30,6 +30,8 @@ #include "math_funcs.h" +#include "core/error_macros.h" + RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); #define PHI 0x9e3779b9 diff --git a/core/math/rect2.h b/core/math/rect2.h index d636aa223f..f58756ee40 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -99,8 +99,8 @@ struct Rect2 { inline bool encloses(const Rect2 &p_rect) const { return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y)); } _FORCE_INLINE_ bool has_no_area() const { diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 73927821cf..ebc1599820 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -134,6 +134,21 @@ Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { return len <= p_delta || len < CMP_EPSILON ? p_to : v + vd / len * p_delta; } +Basis Vector3::outer(const Vector3 &p_b) const { + + Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); + Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); + Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); + + return Basis(row0, row1, row2); +} + +Basis Vector3::to_diagonal_matrix() const { + return Basis(x, 0, 0, + 0, y, 0, + 0, 0, z); +} + Vector3::operator String() const { return (rtos(x) + ", " + rtos(y) + ", " + rtos(z)); diff --git a/core/math/vector3.h b/core/math/vector3.h index c68b075613..de1743d88f 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -96,8 +96,8 @@ struct Vector3 { _FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const; _FORCE_INLINE_ real_t dot(const Vector3 &p_b) const; - _FORCE_INLINE_ Basis outer(const Vector3 &p_b) const; - _FORCE_INLINE_ Basis to_diagonal_matrix() const; + Basis outer(const Vector3 &p_b) const; + Basis to_diagonal_matrix() const; _FORCE_INLINE_ Vector3 abs() const; _FORCE_INLINE_ Vector3 floor() const; @@ -154,9 +154,6 @@ struct Vector3 { _FORCE_INLINE_ Vector3() { x = y = z = 0; } }; -// Should be included after class definition, otherwise we get circular refs -#include "core/math/basis.h" - Vector3 Vector3::cross(const Vector3 &p_b) const { Vector3 ret( @@ -172,21 +169,6 @@ real_t Vector3::dot(const Vector3 &p_b) const { return x * p_b.x + y * p_b.y + z * p_b.z; } -Basis Vector3::outer(const Vector3 &p_b) const { - - Vector3 row0(x * p_b.x, x * p_b.y, x * p_b.z); - Vector3 row1(y * p_b.x, y * p_b.y, y * p_b.z); - Vector3 row2(z * p_b.x, z * p_b.y, z * p_b.z); - - return Basis(row0, row1, row2); -} - -Basis Vector3::to_diagonal_matrix() const { - return Basis(x, 0, 0, - 0, y, 0, - 0, 0, z); -} - Vector3 Vector3::abs() const { return Vector3(Math::abs(x), Math::abs(y), Math::abs(z)); diff --git a/core/node_path.cpp b/core/node_path.cpp index b43f76f680..970ed100fe 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -269,7 +269,7 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { NodePath NodePath::get_as_property_path() const { - if (!data->path.size()) { + if (!data || !data->path.size()) { return *this; } else { Vector<StringName> new_path = data->subpath; diff --git a/core/os/memory.h b/core/os/memory.h index 8778cb63ad..a68a359546 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -31,6 +31,7 @@ #ifndef MEMORY_H #define MEMORY_H +#include "core/error_macros.h" #include "core/safe_refcount.h" #include <stddef.h> diff --git a/core/self_list.h b/core/self_list.h index 314d440977..07abcd1d53 100644 --- a/core/self_list.h +++ b/core/self_list.h @@ -31,6 +31,7 @@ #ifndef SELF_LIST_H #define SELF_LIST_H +#include "core/error_macros.h" #include "core/typedefs.h" template <class T> diff --git a/core/sort_array.h b/core/sort_array.h index 8660ee3333..d330e0c647 100644 --- a/core/sort_array.h +++ b/core/sort_array.h @@ -31,6 +31,7 @@ #ifndef SORT_ARRAY_H #define SORT_ARRAY_H +#include "core/error_macros.h" #include "core/typedefs.h" #define ERR_BAD_COMPARE(cond) \ diff --git a/core/typedefs.h b/core/typedefs.h index 660139b90a..767a97ac38 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -108,7 +108,6 @@ T *_nullptr() { #include "core/int_types.h" #include "core/error_list.h" -#include "core/error_macros.h" /** Generic ABS function, for math uses please use Math::abs */ diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 1b5ca9d3e5..40b944744b 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -316,6 +316,10 @@ struct _VariantCall { static void _call_String_to_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) { String *s = reinterpret_cast<String *>(p_self._data._mem); + if (s->empty()) { + r_ret = PoolByteArray(); + return; + } CharString charstr = s->ascii(); PoolByteArray retval; @@ -331,6 +335,10 @@ struct _VariantCall { static void _call_String_to_utf8(Variant &r_ret, Variant &p_self, const Variant **p_args) { String *s = reinterpret_cast<String *>(p_self._data._mem); + if (s->empty()) { + r_ret = PoolByteArray(); + return; + } CharString charstr = s->utf8(); PoolByteArray retval; @@ -545,7 +553,7 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); String s; - if (ba->size() >= 0) { + if (ba->size() > 0) { PoolByteArray::Read r = ba->read(); CharString cs; cs.resize(ba->size() + 1); @@ -561,7 +569,7 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); String s; - if (ba->size() >= 0) { + if (ba->size() > 0) { PoolByteArray::Read r = ba->read(); s.parse_utf8((const char *)r.ptr(), ba->size()); } @@ -572,14 +580,15 @@ struct _VariantCall { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); PoolByteArray compressed; - Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]); + if (ba->size() > 0) { + Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]); - compressed.resize(Compression::get_max_compressed_buffer_size(ba->size(), mode)); - int result = Compression::compress(compressed.write().ptr(), ba->read().ptr(), ba->size(), mode); - - result = result >= 0 ? result : 0; - compressed.resize(result); + compressed.resize(Compression::get_max_compressed_buffer_size(ba->size(), mode)); + int result = Compression::compress(compressed.write().ptr(), ba->read().ptr(), ba->size(), mode); + result = result >= 0 ? result : 0; + compressed.resize(result); + } r_ret = compressed; } @@ -591,9 +600,9 @@ struct _VariantCall { int buffer_size = (int)(*p_args[0]); - if (buffer_size < 0) { + if (buffer_size <= 0) { r_ret = decompressed; - ERR_FAIL_MSG("Decompression buffer size is less than zero."); + ERR_FAIL_MSG("Decompression buffer size must be greater than zero."); } decompressed.resize(buffer_size); @@ -607,6 +616,10 @@ struct _VariantCall { static void _call_PoolByteArray_hex_encode(Variant &r_ret, Variant &p_self, const Variant **p_args) { PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); + if (ba->size() == 0) { + r_ret = String(); + return; + } PoolByteArray::Read r = ba->read(); String s = String::hex_encode_buffer(&r[0], ba->size()); r_ret = s; diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index 775ad4c922..56f54e4434 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -39,6 +39,16 @@ Deletes the specified section along with all the key-value pairs inside. </description> </method> + <method name="erase_section_key"> + <return type="void"> + </return> + <argument index="0" name="section" type="String"> + </argument> + <argument index="1" name="key" type="String"> + </argument> + <description> + </description> + </method> <method name="get_section_keys" qualifiers="const"> <return type="PoolStringArray"> </return> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 9acddb3115..9e1b25abe1 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -326,7 +326,7 @@ <argument index="0" name="screen" type="int" default="-1"> </argument> <description> - Returns the dots per inch density of the specified screen. + Returns the dots per inch density of the specified screen. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used. On Android devices, the actual screen densities are grouped into six generalized densities: [codeblock] ldpi - 120 dpi @@ -344,7 +344,7 @@ <argument index="0" name="screen" type="int" default="-1"> </argument> <description> - Returns the position of the specified screen by index. If no screen index is provided, the current screen will be used. + Returns the position of the specified screen by index. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used. </description> </method> <method name="get_screen_size" qualifiers="const"> @@ -353,7 +353,7 @@ <argument index="0" name="screen" type="int" default="-1"> </argument> <description> - Returns the dimensions in pixels of the specified screen. + Returns the dimensions in pixels of the specified screen. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used. </description> </method> <method name="get_splash_tick_msec" qualifiers="const"> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index d9400088dd..691aec2eb1 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -89,6 +89,36 @@ An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators. </description> </method> + <method name="add_icon_radio_check_item"> + <return type="void"> + </return> + <argument index="0" name="texture" type="Texture"> + </argument> + <argument index="1" name="label" type="String"> + </argument> + <argument index="2" name="id" type="int" default="-1"> + </argument> + <argument index="3" name="accel" type="int" default="0"> + </argument> + <description> + Same as [method add_icon_check_item], but uses a radio check button. + </description> + </method> + <method name="add_icon_radio_check_shortcut"> + <return type="void"> + </return> + <argument index="0" name="texture" type="Texture"> + </argument> + <argument index="1" name="shortcut" type="ShortCut"> + </argument> + <argument index="2" name="id" type="int" default="-1"> + </argument> + <argument index="3" name="global" type="bool" default="false"> + </argument> + <description> + Same as [method add_icon_check_shortcut], but uses a radio check button. + </description> + </method> <method name="add_icon_shortcut"> <return type="void"> </return> @@ -119,6 +149,25 @@ An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators. </description> </method> + <method name="add_multistate_item"> + <return type="void"> + </return> + <argument index="0" name="label" type="String"> + </argument> + <argument index="1" name="max_states" type="int"> + </argument> + <argument index="2" name="default_state" type="int" default="0"> + </argument> + <argument index="3" name="id" type="int" default="-1"> + </argument> + <argument index="4" name="accel" type="int" default="0"> + </argument> + <description> + Adds a new multistate item with text [code]label[/code]. + Contrarily to normal binary items, multistate items can have more than two states, as defined by [code]max_states[/code]. Each press or activate of the item will increase the state by one. The default value is defined by [code]default_state[/code]. + An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators. + </description> + </method> <method name="add_radio_check_item"> <return type="void"> </return> @@ -129,7 +178,7 @@ <argument index="2" name="accel" type="int" default="0"> </argument> <description> - Adds a new radio button with text [code]label[/code]. + Adds a new radio check button with text [code]label[/code]. An [code]id[/code] can optionally be provided, as well as an accelerator ([code]accel[/code]). If no [code]id[/code] is provided, one will be created from the index. If no [code]accel[/code] is provided then the default [code]0[/code] will be assigned to it. See [method get_item_accelerator] for more info on accelerators. [b]Note:[/b] Checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method set_item_checked] for more info on how to control it. </description> diff --git a/doc/classes/ReflectionProbe.xml b/doc/classes/ReflectionProbe.xml index 68b9110b50..0a1cf962c8 100644 --- a/doc/classes/ReflectionProbe.xml +++ b/doc/classes/ReflectionProbe.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ReflectionProbe" inherits="VisualInstance" category="Core" version="3.2"> <brief_description> + Captures its surroundings to create reflections. </brief_description> <description> + Capture its surroundings as a dual parabolid image, and stores versions of it with increasing levels of blur to simulate different material roughnesses. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/reflection_probes.html</link> @@ -11,14 +13,17 @@ </methods> <members> <member name="box_projection" type="bool" setter="set_enable_box_projection" getter="is_box_projection_enabled" default="false"> + If [code]true[/code], enables box projection. This makes reflections look more correct in rectangle-shaped rooms by offsetting the reflection center depending on the camera's location. </member> <member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="1048575"> </member> <member name="enable_shadows" type="bool" setter="set_enable_shadows" getter="are_shadows_enabled" default="false"> + If [code]true[/code], computes shadows in the reflection probe. This makes the reflection probe slower to render; you may want to disable this if using the [constant UPDATE_ALWAYS] [member update_mode]. </member> <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )"> </member> <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="1.0"> + Defines the reflection intensity. </member> <member name="interior_ambient_color" type="Color" setter="set_interior_ambient" getter="get_interior_ambient" default="Color( 0, 0, 0, 1 )"> </member> diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml index 30d67d47f3..3d0fa9a0d5 100644 --- a/doc/classes/ScriptCreateDialog.xml +++ b/doc/classes/ScriptCreateDialog.xml @@ -31,9 +31,9 @@ </methods> <members> <member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" override="true" default="false" /> - <member name="margin_bottom" type="float" setter="set_margin" getter="get_margin" override="true" default="76.0" /> - <member name="margin_right" type="float" setter="set_margin" getter="get_margin" override="true" default="200.0" /> - <member name="rect_size" type="Vector2" setter="_set_size" getter="get_size" override="true" default="Vector2( 200, 76 )" /> + <member name="margin_bottom" type="float" setter="set_margin" getter="get_margin" override="true" default="232.0" /> + <member name="margin_right" type="float" setter="set_margin" getter="get_margin" override="true" default="361.0" /> + <member name="rect_size" type="Vector2" setter="_set_size" getter="get_size" override="true" default="Vector2( 361, 232 )" /> <member name="window_title" type="String" setter="set_title" getter="get_title" override="true" default=""Attach Node Script"" /> </members> <signals> diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml index 064a7266bd..886e8244a2 100644 --- a/doc/classes/Skeleton2D.xml +++ b/doc/classes/Skeleton2D.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Skeleton2D" inherits="Node2D" category="Core" version="3.2"> <brief_description> + Skeleton for 2D characters and animated objects. </brief_description> <description> </description> @@ -20,6 +21,7 @@ <return type="int"> </return> <description> + Returns the amount of bones in the skeleton. </description> </method> <method name="get_skeleton" qualifiers="const"> diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index a4c976591f..c77388e5f4 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -26,6 +26,15 @@ Adds a button with [Texture] [code]button[/code] at column [code]column[/code]. The [code]button_idx[/code] index is used to identify the button when calling other methods. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately after this method. Optionally, the button can be [code]disabled[/code] and have a [code]tooltip[/code]. </description> </method> + <method name="call_recursive" qualifiers="vararg"> + <return type="Variant"> + </return> + <argument index="0" name="method" type="String"> + </argument> + <description> + Calls the [code]method[/code] on the actual TreeItem and its children recursively. Pass parameters as a comma separated list. + </description> + </method> <method name="clear_custom_bg_color"> <return type="void"> </return> @@ -594,15 +603,6 @@ Sets the given column's tooltip text. </description> </method> - <method name="call_recursive" qualifiers="vararg"> - <return type="Variant"> - </return> - <argument index="0" name="method" type="String"> - </argument> - <description> - Calls the [code]method[/code] on the actual TreeItem and its children recursively. Pass parameters as a comma separated list. - </description> - </method> </methods> <members> <member name="collapsed" type="bool" setter="set_collapsed" getter="is_collapsed"> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml index 7d7002e752..f0f03b6c21 100644 --- a/doc/classes/VisualShader.xml +++ b/doc/classes/VisualShader.xml @@ -145,12 +145,6 @@ <description> </description> </method> - <method name="rebuild"> - <return type="void"> - </return> - <description> - </description> - </method> <method name="remove_node"> <return type="void"> </return> diff --git a/doc/classes/VisualShaderNodeCubeMapUniform.xml b/doc/classes/VisualShaderNodeCubeMapUniform.xml index ad34e7d30c..c6b3db5a9d 100644 --- a/doc/classes/VisualShaderNodeCubeMapUniform.xml +++ b/doc/classes/VisualShaderNodeCubeMapUniform.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualShaderNodeCubeMapUniform" inherits="VisualShaderNode" category="Core" version="3.2"> +<class name="VisualShaderNodeCubeMapUniform" inherits="VisualShaderNodeTextureUniform" category="Core" version="3.2"> <brief_description> </brief_description> <description> @@ -8,9 +8,6 @@ </tutorials> <methods> </methods> - <members> - <member name="default_input_values" type="Array" setter="_set_default_input_values" getter="_get_default_input_values" override="true" default="[ ]" /> - </members> <constants> </constants> </class> diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index 974ac2d05c..6e34cffc05 100644 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -259,7 +259,8 @@ class ClassStatus: status.progresses[tag.tag].increment(len(descr.text.strip()) > 0) elif tag.tag in ['constants', 'members']: for sub_tag in list(tag): - status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0) + if not sub_tag.text is None: + status.progresses[tag.tag].increment(len(sub_tag.text.strip()) > 0) elif tag.tag in ['tutorials']: pass # Ignore those tags for now diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 6be48a4c58..e34705f7b7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -761,6 +761,14 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur source.size.y = tex->height; } + float screen_scale = 1.0; + + if (source.size.x != 0 && source.size.y != 0) { + + screen_scale = MIN(np->rect.size.x / source.size.x, np->rect.size.y / source.size.y); + screen_scale = MIN(1.0, screen_scale); + } + // prepare vertex buffer // this buffer contains [ POS POS UV UV ] * @@ -777,13 +785,13 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x; buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y; - buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; + buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; buffer[(0 * 4 * 4) + 5] = np->rect.position.y; buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y; - buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; + buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; buffer[(0 * 4 * 4) + 9] = np->rect.position.y; buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; @@ -798,25 +806,25 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur // second row buffer[(1 * 4 * 4) + 0] = np->rect.position.x; - buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; + buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale; buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x; buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; - buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP]; + buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; + buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale; buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; - buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP]; + buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; + buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale; buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP]; + buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP] * screen_scale; buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; @@ -824,25 +832,25 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur // third row buffer[(2 * 4 * 4) + 0] = np->rect.position.x; - buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; + buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale; buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x; buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; - buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; + buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; + buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale; buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; - buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; + buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; + buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale; buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; + buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM] * screen_scale; buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; @@ -855,13 +863,13 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x; buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y; - buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; + buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT] * screen_scale; buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y; buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y; - buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; + buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT] * screen_scale; buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y; buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 35f414cf09..519fdf2b3b 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1093,7 +1093,7 @@ void RasterizerSceneGLES3::gi_probe_instance_set_bounds(RID p_probe, const Vecto //////////////////////////// //////////////////////////// -bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_material, bool p_alpha_pass) { +bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass) { /* this is handled outside if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES3::Shader::Spatial::CULL_MODE_DISABLED) { @@ -1121,7 +1121,7 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m if (state.current_depth_draw != p_material->shader->spatial.depth_draw_mode) { switch (p_material->shader->spatial.depth_draw_mode) { case RasterizerStorageGLES3::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS: { - glDepthMask(GL_TRUE); + glDepthMask(p_depth_pass); // If some transparent objects write to depth, we need to re-copy depth texture when we need it if (p_alpha_pass && !state.used_depth_prepass) { state.prepared_depth_texture = false; @@ -2241,7 +2241,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_ storage->info.render.material_switch_count++; - rebind = _setup_material(material, p_alpha_pass); + rebind = _setup_material(material, use_opaque_prepass, p_alpha_pass); if (rebind) { storage->info.render.shader_rebind_count++; diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 3d09adcfeb..e6d2449653 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -832,7 +832,7 @@ public: _FORCE_INLINE_ void _set_cull(bool p_front, bool p_disabled, bool p_reverse_cull); - _FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES3::Material *p_material, bool p_alpha_pass); + _FORCE_INLINE_ bool _setup_material(RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_alpha_pass); _FORCE_INLINE_ void _setup_geometry(RenderList::Element *e, const Transform &p_view_transform); _FORCE_INLINE_ void _render_geometry(RenderList::Element *e); _FORCE_INLINE_ void _setup_light(RenderList::Element *e, const Transform &p_view_transform); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index e83f53d648..7255b0425c 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -380,14 +380,16 @@ uniform bool np_draw_center; // left top right bottom in pixel coordinates uniform vec4 np_margins; -float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { +float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, float s_ratio, int np_repeat, inout int draw_center) { float tex_size = 1.0 / tex_pixel_size; - if (pixel < margin_begin) { - return pixel * tex_pixel_size; - } else if (pixel >= draw_size - margin_end) { - return (tex_size - (draw_size - pixel)) * tex_pixel_size; + float screen_margin_begin = margin_begin / s_ratio; + float screen_margin_end = margin_end / s_ratio; + if (pixel < screen_margin_begin) { + return pixel * s_ratio * tex_pixel_size; + } else if (pixel >= draw_size - screen_margin_end) { + return (tex_size - (draw_size - pixel) * s_ratio) * tex_pixel_size; } else { if (!np_draw_center) { draw_center--; @@ -395,22 +397,22 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo if (np_repeat == 0) { //stretch //convert to ratio - float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); + float ratio = (pixel - screen_margin_begin) / (draw_size - screen_margin_begin - screen_margin_end); //scale to source texture return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; } else if (np_repeat == 1) { //tile //convert to ratio - float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); + float ofs = mod((pixel - screen_margin_begin), tex_size - margin_begin - margin_end); //scale to source texture return (margin_begin + ofs) * tex_pixel_size; } else if (np_repeat == 2) { //tile fit //convert to ratio - float src_area = draw_size - margin_begin - margin_end; + float src_area = draw_size - screen_margin_begin - screen_margin_end; float dst_area = tex_size - margin_begin - margin_end; float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); //convert to ratio - float ratio = (pixel - margin_begin) / src_area; + float ratio = (pixel - screen_margin_begin) / src_area; ratio = mod(ratio * scale, 1.0); return (margin_begin + ratio * dst_area) * tex_pixel_size; } @@ -432,9 +434,11 @@ void main() { #ifdef USE_NINEPATCH int draw_center = 2; + float s_ratio = max((1.0 / color_texpixel_size.x) / abs(dst_rect.z), (1.0 / color_texpixel_size.y) / abs(dst_rect.w)); + s_ratio = max(1.0, s_ratio); uv = vec2( - map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, np_repeat_h, draw_center), - map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, np_repeat_v, draw_center)); + map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, s_ratio, np_repeat_h, draw_center), + map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, s_ratio, np_repeat_v, draw_center)); if (draw_center == 0) { color.a = 0.0; diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 6a57a2e562..da46b393c6 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -30,6 +30,7 @@ #include "net_socket_posix.h" +#ifndef UNIX_SOCKET_UNAVAILABLE #if defined(UNIX_ENABLED) #include <errno.h> @@ -280,6 +281,21 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_ _sock = p_sock; _ip_type = p_ip_type; _is_stream = p_is_stream; + // Disable descriptor sharing with subprocesses. + _set_close_exec_enabled(true); +} + +void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { +#ifndef WINDOWS_ENABLED + // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. +#if defined(NO_FCNTL) + unsigned long par = p_enabled ? 1 : 0; + SOCK_IOCTL(_sock, FIOCLEX, &par); +#else + int opts = fcntl(_sock, F_GETFD); + fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); +#endif +#endif } Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -320,6 +336,9 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { _is_stream = p_sock_type == TYPE_TCP; + // Disable descriptor sharing with subprocesses. + _set_close_exec_enabled(true); + #if defined(WINDOWS_ENABLED) if (!_is_stream) { // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when @@ -691,3 +710,4 @@ Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, St Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, false); } +#endif diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index 40406b241a..e549ea1d6a 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -61,6 +61,7 @@ private: NetError _get_socket_error(); void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream); _FORCE_INLINE_ Error _change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add); + _FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled); protected: static NetSocket *_create_func(); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 90f54df485..9510092a86 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1606,6 +1606,9 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < so_files.size() && err == OK; i++) { err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); + if (err == OK) { + err = sign_shared_object(p_preset, p_debug, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); + } } memdelete(da); } @@ -1614,6 +1617,10 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr return err; } +Error EditorExportPlatformPC::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { + return OK; +} + void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) { extensions[p_feature_key] = p_extension; } diff --git a/editor/editor_export.h b/editor/editor_export.h index 3152e249bd..11dc464b5a 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -423,6 +423,7 @@ public: virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); void set_extension(const String &p_extension, const String &p_feature_key = "default"); void set_name(const String &p_name); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 83434a6d9f..4a1e93eaad 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -72,9 +72,12 @@ void EditorHelp::_unhandled_key_input(const Ref<InputEvent> &p_ev) { } } -void EditorHelp::_search(const String &) { +void EditorHelp::_search(bool p_search_previous) { - find_bar->search_next(); + if (p_search_previous) + find_bar->search_prev(); + else + find_bar->search_next(); } void EditorHelp::_class_list_select(const String &p_select) { @@ -1502,8 +1505,8 @@ String EditorHelp::get_class() { return edited_class; } -void EditorHelp::search_again() { - _search(prev_search); +void EditorHelp::search_again(bool p_search_previous) { + _search(p_search_previous); } int EditorHelp::get_scroll() const { diff --git a/editor/editor_help.h b/editor/editor_help.h index 1019cafffc..23a6e005a0 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -158,7 +158,7 @@ class EditorHelp : public VBoxContainer { void _update_doc(); void _request_help(const String &p_string); - void _search(const String &p_str); + void _search(bool p_search_previous = false); void _unhandled_key_input(const Ref<InputEvent> &p_ev); @@ -179,7 +179,7 @@ public: void scroll_to_section(int p_section_index); void popup_search(); - void search_again(); + void search_again(bool p_search_previous = false); String get_class(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index b29417b4c3..78e058eeaa 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1566,11 +1566,11 @@ void EditorInspector::update_tree() { if (dot != -1) { String ov = name.right(dot); name = name.substr(0, dot); - name = name.camelcase_to_underscore().capitalize(); + name = name.capitalize(); name += ov; } else { - name = name.camelcase_to_underscore().capitalize(); + name = name.capitalize(); } } diff --git a/editor/editor_sub_scene.cpp b/editor/editor_sub_scene.cpp index 987033b123..a77051c80b 100644 --- a/editor/editor_sub_scene.cpp +++ b/editor/editor_sub_scene.cpp @@ -97,8 +97,14 @@ void EditorSubScene::_fill_tree(Node *p_node, TreeItem *p_parent) { } void EditorSubScene::_selected_changed() { - selection.clear(); - is_root = false; + TreeItem *item = tree->get_selected(); + ERR_FAIL_COND(!item); + Node *n = item->get_metadata(0); + + if (!n || !selection.find(n)) { + selection.clear(); + is_root = false; + } } void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_selected) { @@ -116,6 +122,11 @@ void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_s selection.clear(); } selection.push_back(n); + } else { + List<Node *>::Element *E = selection.find(n); + + if (E) + selection.erase(E); } } } diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h index b96bc1b656..adad21da61 100644 --- a/editor/import/resource_importer_obj.h +++ b/editor/import/resource_importer_obj.h @@ -31,7 +31,7 @@ #ifndef RESOURCEIMPORTEROBJ_H #define RESOURCEIMPORTEROBJ_H -#include "import/resource_importer_scene.h" +#include "resource_importer_scene.h" class EditorOBJImporter : public EditorSceneImporter { diff --git a/editor/output_strings.cpp b/editor/output_strings.cpp deleted file mode 100644 index baabaff9a8..0000000000 --- a/editor/output_strings.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/*************************************************************************/ -/* output_strings.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 "output_strings.h" - -void OutputStrings::update_scrollbars() { - - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_anchor(MARGIN_LEFT, ANCHOR_END); - v_scroll->set_anchor(MARGIN_RIGHT, ANCHOR_END); - v_scroll->set_anchor(MARGIN_BOTTOM, ANCHOR_END); - - v_scroll->set_begin(Point2(-vmin.width, 0)); - v_scroll->set_end(Point2(0, 0)); - - h_scroll->set_anchor(MARGIN_RIGHT, ANCHOR_END); - h_scroll->set_anchor(MARGIN_TOP, ANCHOR_END); - h_scroll->set_anchor(MARGIN_BOTTOM, ANCHOR_END); - - h_scroll->set_begin(Point2(0, -hmin.y)); - h_scroll->set_end(Point2(-vmin.x, 0)); - - margin.y = hmin.y; - margin.x = vmin.x; - - Ref<StyleBox> tree_st = get_stylebox("bg", "Tree"); - int page = ((size_height - (int)margin.y - tree_st->get_margin(MARGIN_TOP)) / font_height); - v_scroll->set_page(page); -} - -void OutputStrings::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_DRAW: { - - if (following) { - - updating = true; - v_scroll->set_value(v_scroll->get_max() - v_scroll->get_page()); - updating = false; - } - - RID ci = get_canvas_item(); - Size2 size = get_size(); - - Ref<Font> font = get_font("font", "Tree"); - Ref<StyleBox> tree_st = get_stylebox("bg", "Tree"); - tree_st->draw(ci, Rect2(Point2(), size)); - Color color = get_color("font_color", "Tree"); - Ref<Texture> icon_error = get_icon("Error", "EditorIcons"); - Ref<Texture> icon_warning = get_icon("Warning", "EditorIcons"); - - //int lines = (size_height-(int)margin.y) / font_height; - Point2 ofs = tree_st->get_offset(); - - LineMap::Element *E = line_map.find(v_scroll->get_value()); - float h_ofs = (int)h_scroll->get_value(); - Point2 icon_ofs = Point2(0, (font_height - (int)icon_error->get_height()) / 2); - - FontDrawer drawer(font, Color(1, 1, 1)); - while (E && ofs.y < (size_height - (int)margin.y)) { - - String str = E->get().text; - Point2 line_ofs = ofs; - - switch (E->get().type) { - - case LINE_WARNING: { - icon_warning->draw(ci, line_ofs + icon_ofs); - - } break; - case LINE_ERROR: { - icon_error->draw(ci, line_ofs + icon_ofs); - } break; - case LINE_LINK: { - - } break; - default: { - } - } - - line_ofs.y += font->get_ascent(); - line_ofs.x += icon_error->get_width() + 4; - - for (int i = 0; i < str.length(); i++) { - if (line_ofs.x - h_ofs < 0) { - line_ofs.x += font->get_char_size(str[i], str[i + 1]).width; - } else if (line_ofs.x - h_ofs > size.width - margin.width) { - break; - } else { - line_ofs.x += font->draw_char(ci, Point2(line_ofs.x - h_ofs, line_ofs.y), str[i], str[i + 1], color); - } - } - - ofs.y += font_height; - E = E->next(); - } - - } break; - - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_RESIZED: { - - font_height = get_font("font", "Tree")->get_height(); - size_height = get_size().height; - update_scrollbars(); - } break; - } -} - -void OutputStrings::_hscroll_changed(float p_value) { - - if (updating) - return; - - update(); -} -void OutputStrings::_vscroll_changed(float p_value) { - - if (updating) - return; - //user changed scroll - following = (p_value + v_scroll->get_page()) >= v_scroll->get_max(); - update(); -} - -void OutputStrings::add_line(const String &p_text, const Variant &p_meta, const LineType p_type) { - - Vector<String> strings = p_text.split("\n"); - - for (int i = 0; i < strings.size(); i++) { - - if (strings[i].length() == 0) - continue; - - int last = line_map.empty() ? 0 : (line_map.back()->key() + 1); - - Line l; - l.text = strings[i]; - l.meta = p_meta; - l.type = p_type; - line_map.insert(last, l); - - updating = true; - v_scroll->set_max(last + 1); - v_scroll->set_min(line_map.front()->key()); - updating = false; - } - - while (line_map.size() > line_max_count) { - - line_map.erase(line_map.front()); - } - - update(); -} - -void OutputStrings::_bind_methods() { - - ClassDB::bind_method("_vscroll_changed", &OutputStrings::_vscroll_changed); - ClassDB::bind_method("_hscroll_changed", &OutputStrings::_hscroll_changed); -} - -OutputStrings::OutputStrings() { - - following = true; - updating = false; - line_max_count = 4096; - h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - add_child(h_scroll); - add_child(v_scroll); - size_height = 1; - font_height = 1; - update_scrollbars(); - h_scroll->connect("value_changed", this, "_hscroll_changed"); - v_scroll->connect("value_changed", this, "_vscroll_changed"); -} diff --git a/editor/output_strings.h b/editor/output_strings.h deleted file mode 100644 index 4fd3f7d836..0000000000 --- a/editor/output_strings.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************/ -/* output_strings.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 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 OUTPUT_STRINGS_H -#define OUTPUT_STRINGS_H - -#include "core/map.h" -#include "scene/gui/control.h" -#include "scene/gui/scroll_bar.h" - -class OutputStrings : public Control { - - GDCLASS(OutputStrings, Control); - -public: - enum LineType { - - LINE_NORMAL, - LINE_WARNING, - LINE_ERROR, - LINE_LINK - }; - -private: - struct Line { - - LineType type; - Variant meta; - String text; - }; - - int font_height; - int size_height; - - Size2 margin; - typedef Map<int, Line> LineMap; - Map<int, Line> line_map; - - VScrollBar *v_scroll; - HScrollBar *h_scroll; - - bool following; - int line_max_count; - bool updating; - - void _vscroll_changed(float p_value); - void _hscroll_changed(float p_value); - void update_scrollbars(); - -protected: - static void _bind_methods(); - void _notification(int p_what); - -public: - void add_line(const String &p_text, const Variant &p_meta = Variant(), const LineType p_type = LINE_NORMAL); - - OutputStrings(); -}; - -#endif // OUTPUT_STRINGS_H diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 3d161dc5b9..d5b1f46333 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -32,8 +32,8 @@ #include "core/io/json.h" #include "core/version.h" -#include "editor_node.h" -#include "editor_settings.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) { diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 7e934ac6cb..94289f3b49 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -31,24 +31,22 @@ #ifndef ASSET_LIBRARY_EDITOR_PLUGIN_H #define ASSET_LIBRARY_EDITOR_PLUGIN_H -#include "editor_plugin.h" +#include "editor/editor_asset_installer.h" +#include "editor/editor_plugin.h" +#include "editor/editor_plugin_settings.h" #include "scene/gui/box_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/grid_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/link_button.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/progress_bar.h" -#include "scene/gui/separator.h" -#include "scene/gui/tab_container.h" - -#include "editor_plugin_settings.h" -#include "scene/gui/grid_container.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/tab_container.h" #include "scene/gui/texture_button.h" - -#include "editor_asset_installer.h" #include "scene/main/http_request.h" class EditorAssetLibraryItem : public PanelContainer { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 7055abd643..117b88bc7e 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -72,7 +72,7 @@ class SnapDialog : public ConfirmationDialog { public: SnapDialog() { - const int SPIN_BOX_GRID_RANGE = 256; + const int SPIN_BOX_GRID_RANGE = 16384; const int SPIN_BOX_ROTATION_RANGE = 360; Label *label; VBoxContainer *container; @@ -96,6 +96,8 @@ public: grid_offset_x = memnew(SpinBox); grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); + grid_offset_x->set_allow_lesser(true); + grid_offset_x->set_allow_greater(true); grid_offset_x->set_suffix("px"); grid_offset_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_x); @@ -103,6 +105,8 @@ public: grid_offset_y = memnew(SpinBox); grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); + grid_offset_y->set_allow_lesser(true); + grid_offset_y->set_allow_greater(true); grid_offset_y->set_suffix("px"); grid_offset_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_y); @@ -115,6 +119,7 @@ public: grid_step_x = memnew(SpinBox); grid_step_x->set_min(0.01); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); + grid_step_x->set_allow_greater(true); grid_step_x->set_suffix("px"); grid_step_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_x); @@ -122,6 +127,7 @@ public: grid_step_y = memnew(SpinBox); grid_step_y->set_min(0.01); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); + grid_step_y->set_allow_greater(true); grid_step_y->set_suffix("px"); grid_step_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_y); @@ -278,7 +284,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig snap_target[0] = SNAP_TARGET_NONE; snap_target[1] = SNAP_TARGET_NONE; - bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // Smart snap using the canvas position Vector2 output = p_target; @@ -378,7 +384,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig } } - if (((is_snap_active && snap_grid && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { + if (((grid_snap_active && (p_modes & SNAP_GRID)) || (p_forced_modes & SNAP_GRID)) && fmod(rotation, (real_t)360.0) == 0.0) { // Grid Point2 offset = grid_offset; if (snap_relative) { @@ -406,7 +412,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig } float CanvasItemEditor::snap_angle(float p_target, float p_start) const { - return (((snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; + return (((smart_snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL)) && snap_rotation_step != 0) ? Math::stepify(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset : p_target; } void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { @@ -421,11 +427,11 @@ void CanvasItemEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) { } if (k->is_pressed() && !k->get_control() && !k->is_echo()) { - if ((snap_grid || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { + if ((grid_snap_active || show_grid) && multiply_grid_step_shortcut.is_valid() && multiply_grid_step_shortcut->is_shortcut(p_ev)) { // Multiply the grid size grid_step_multiplier = MIN(grid_step_multiplier + 1, 12); viewport->update(); - } else if ((snap_grid || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) { + } else if ((grid_snap_active || show_grid) && divide_grid_step_shortcut.is_valid() && divide_grid_step_shortcut->is_shortcut(p_ev)) { // Divide the grid size Point2 new_grid_step = grid_step * Math::pow(2.0, grid_step_multiplier - 1); if (new_grid_step.x >= 1.0 && new_grid_step.y >= 1.0) @@ -2281,7 +2287,7 @@ bool CanvasItemEditor::_gui_input_ruler_tool(const Ref<InputEvent> &p_event) { return true; } - bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); if (m.is_valid() && (ruler_tool_active || (is_snap_active && previous_origin != ruler_tool_origin))) { @@ -2552,11 +2558,10 @@ void CanvasItemEditor::_draw_rulers() { Color font_color = get_color("font_color", "Editor"); font_color.a = 0.8; Ref<Font> font = get_font("rulers", "EditorFonts"); - bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); // The rule transform Transform2D ruler_transform = Transform2D(); - if (show_grid || (is_snap_active && snap_grid)) { + if (show_grid || grid_snap_active) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (snap_relative && selection.size() > 0) { ruler_transform.translate(_get_encompassing_rect_from_list(selection).position); @@ -2636,7 +2641,7 @@ void CanvasItemEditor::_draw_rulers() { } void CanvasItemEditor::_draw_grid() { - if (show_grid) { + if (show_grid || grid_snap_active) { //Draw the grid Size2 s = viewport->get_size(); int last_cell = 0; @@ -2682,7 +2687,7 @@ void CanvasItemEditor::_draw_ruler_tool() { if (tool != TOOL_RULER) return; - bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); + bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); if (ruler_tool_active) { Color ruler_primary_color = get_color("accent_color", "Editor"); @@ -3691,7 +3696,8 @@ void CanvasItemEditor::_notification(int p_what) { move_button->set_icon(get_icon("ToolMove", "EditorIcons")); scale_button->set_icon(get_icon("ToolScale", "EditorIcons")); rotate_button->set_icon(get_icon("ToolRotate", "EditorIcons")); - snap_button->set_icon(get_icon("Snap", "EditorIcons")); + smart_snap_button->set_icon(get_icon("Snap", "EditorIcons")); + grid_snap_button->set_icon(get_icon("SnapGrid", "EditorIcons")); snap_config_menu->set_icon(get_icon("GuiTabMenu", "EditorIcons")); skeleton_menu->set_icon(get_icon("Bone", "EditorIcons")); pan_button->set_icon(get_icon("ToolPan", "EditorIcons")); @@ -4095,8 +4101,13 @@ void CanvasItemEditor::_button_zoom_plus() { _zoom_on_position(zoom * Math_SQRT2, viewport_scrollable->get_size() / 2.0); } -void CanvasItemEditor::_button_toggle_snap(bool p_status) { - snap_active = p_status; +void CanvasItemEditor::_button_toggle_smart_snap(bool p_status) { + smart_snap_active = p_status; + viewport->update(); +} + +void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) { + grid_snap_active = p_status; viewport->update(); } @@ -4255,11 +4266,6 @@ void CanvasItemEditor::_popup_callback(int p_op) { int idx = smartsnap_config_popup->get_item_index(SNAP_USE_GUIDES); smartsnap_config_popup->set_item_checked(idx, snap_guides); } break; - case SNAP_USE_GRID: { - snap_grid = !snap_grid; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); - snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); - } break; case SNAP_USE_ROTATION: { snap_rotation = !snap_rotation; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4791,7 +4797,8 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_button_zoom_minus", &CanvasItemEditor::_button_zoom_minus); ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset); ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus); - ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap); + ClassDB::bind_method("_button_toggle_smart_snap", &CanvasItemEditor::_button_toggle_smart_snap); + ClassDB::bind_method("_button_toggle_grid_snap", &CanvasItemEditor::_button_toggle_grid_snap); ClassDB::bind_method("_button_toggle_anchor_mode", &CanvasItemEditor::_button_toggle_anchor_mode); ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll); ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars); @@ -4826,13 +4833,13 @@ Dictionary CanvasItemEditor::get_state() const { state["grid_step"] = grid_step; state["snap_rotation_offset"] = snap_rotation_offset; state["snap_rotation_step"] = snap_rotation_step; - state["snap_active"] = snap_active; + state["smart_snap_active"] = smart_snap_active; + state["grid_snap_active"] = grid_snap_active; state["snap_node_parent"] = snap_node_parent; state["snap_node_anchors"] = snap_node_anchors; state["snap_node_sides"] = snap_node_sides; state["snap_node_center"] = snap_node_center; state["snap_other_nodes"] = snap_other_nodes; - state["snap_grid"] = snap_grid; state["snap_guides"] = snap_guides; state["show_grid"] = show_grid; state["show_origin"] = show_origin; @@ -4880,9 +4887,14 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { snap_rotation_offset = state["snap_rotation_offset"]; } - if (state.has("snap_active")) { - snap_active = state["snap_active"]; - snap_button->set_pressed(snap_active); + if (state.has("smart_snap_active")) { + smart_snap_active = state["smart_snap_active"]; + smart_snap_button->set_pressed(smart_snap_active); + } + + if (state.has("grid_snap_active")) { + grid_snap_active = state["grid_snap_active"]; + grid_snap_button->set_pressed(smart_snap_active); } if (state.has("snap_node_parent")) { @@ -4921,12 +4933,6 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { smartsnap_config_popup->set_item_checked(idx, snap_guides); } - if (state.has("snap_grid")) { - snap_grid = state["snap_grid"]; - int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_GRID); - snap_config_menu->get_popup()->set_item_checked(idx, snap_grid); - } - if (state.has("show_grid")) { show_grid = state["show_grid"]; int idx = view_menu->get_popup()->get_item_index(SHOW_GRID); @@ -5065,13 +5071,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { grid_step_multiplier = 0; snap_rotation_offset = 0; snap_rotation_step = 15 / (180 / Math_PI); - snap_active = false; + smart_snap_active = false; + grid_snap_active = false; snap_node_parent = true; snap_node_anchors = true; snap_node_sides = true; snap_node_center = true; snap_other_nodes = true; - snap_grid = true; snap_guides = true; snap_rotation = false; snap_relative = false; @@ -5263,12 +5269,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(memnew(VSeparator)); - snap_button = memnew(ToolButton); - hb->add_child(snap_button); - snap_button->set_toggle_mode(true); - snap_button->connect("toggled", this, "_button_toggle_snap"); - snap_button->set_tooltip(TTR("Toggle snapping.")); - snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap"), KEY_MASK_SHIFT | KEY_S)); + smart_snap_button = memnew(ToolButton); + hb->add_child(smart_snap_button); + smart_snap_button->set_toggle_mode(true); + smart_snap_button->connect("toggled", this, "_button_toggle_smart_snap"); + smart_snap_button->set_tooltip(TTR("Toggle smart snapping.")); + smart_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_smart_snap", TTR("Use Smart Snap"), KEY_MASK_SHIFT | KEY_S)); + + grid_snap_button = memnew(ToolButton); + hb->add_child(grid_snap_button); + grid_snap_button->set_toggle_mode(true); + grid_snap_button->connect("toggled", this, "_button_toggle_grid_snap"); + grid_snap_button->set_tooltip(TTR("Toggle grid snapping.")); + grid_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_grid_snap", TTR("Use Grid Snap"), KEY_MASK_SHIFT | KEY_G)); snap_config_menu = memnew(MenuButton); hb->add_child(snap_config_menu); @@ -5279,7 +5292,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { PopupMenu *p = snap_config_menu->get_popup(); p->connect("id_pressed", this, "_popup_callback"); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to Grid")), SNAP_USE_GRID); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL); @@ -5351,9 +5363,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p = view_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid"), KEY_G), SHOW_GRID); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_G), SHOW_GRID); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers"), KEY_MASK_CMD | KEY_R), SHOW_RULERS); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 6cce1ca10e..480fb89621 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -259,13 +259,14 @@ private: float snap_rotation_step; float snap_rotation_offset; - bool snap_active; + bool smart_snap_active; + bool grid_snap_active; + bool snap_node_parent; bool snap_node_anchors; bool snap_node_sides; bool snap_node_center; bool snap_other_nodes; - bool snap_grid; bool snap_guides; bool snap_rotation; bool snap_relative; @@ -347,7 +348,8 @@ private: ToolButton *ruler_button; - ToolButton *snap_button; + ToolButton *smart_snap_button; + ToolButton *grid_snap_button; MenuButton *snap_config_menu; PopupMenu *smartsnap_config_popup; @@ -529,7 +531,8 @@ private: void _button_zoom_minus(); void _button_zoom_reset(); void _button_zoom_plus(); - void _button_toggle_snap(bool p_status); + void _button_toggle_smart_snap(bool p_status); + void _button_toggle_grid_snap(bool p_status); void _button_tool_select(int p_index); HSplitContainer *palette_split; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 80b0f0738a..f7e997a269 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -43,6 +43,7 @@ #include "editor/plugins/shader_editor_plugin.h" #include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" +#include "scene/scene_string_names.h" #include "script_text_editor.h" #include "text_editor.h" @@ -1325,6 +1326,9 @@ void ScriptEditor::_menu_option(int p_option) { case HELP_SEARCH_FIND_NEXT: { help->search_again(); } break; + case HELP_SEARCH_FIND_PREVIOUS: { + help->search_again(true); + } break; case FILE_CLOSE: { _close_current_tab(); } break; @@ -1417,16 +1421,6 @@ void ScriptEditor::_notification(int p_what) { members_overview->connect("item_selected", this, "_members_overview_selected"); help_overview->connect("item_selected", this, "_help_overview_selected"); script_split->connect("dragged", this, "_script_split_dragged"); - autosave_timer->connect("timeout", this, "_autosave_scripts"); - { - float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); - if (autosave_time > 0) { - autosave_timer->set_wait_time(autosave_time); - autosave_timer->start(); - } else { - autosave_timer->stop(); - } - } EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); FALLTHROUGH; @@ -2332,13 +2326,7 @@ void ScriptEditor::_editor_settings_changed() { _update_members_overview_visibility(); _update_help_overview_visibility(); - float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); - if (autosave_time > 0) { - autosave_timer->set_wait_time(autosave_time); - autosave_timer->start(); - } else { - autosave_timer->stop(); - } + _update_autosave_timer(); if (current_theme == "") { current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme"); @@ -2366,6 +2354,21 @@ void ScriptEditor::_autosave_scripts() { save_all_scripts(); } +void ScriptEditor::_update_autosave_timer() { + + if (!autosave_timer->is_inside_tree()) { + return; + } + + float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); + if (autosave_time > 0) { + autosave_timer->set_wait_time(autosave_time); + autosave_timer->start(); + } else { + autosave_timer->stop(); + } +} + void ScriptEditor::_tree_changed() { if (waiting_update_names) @@ -2827,6 +2830,7 @@ void ScriptEditor::_update_selected_editor_menu() { script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS); script_search_menu->get_popup()->add_separator(); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); script_search_menu->show(); @@ -3088,6 +3092,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger); ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip); ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts); + ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer); ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed); ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names); ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections); @@ -3424,6 +3429,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { autosave_timer = memnew(Timer); autosave_timer->set_one_shot(false); + autosave_timer->connect(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer"); + autosave_timer->connect("timeout", this, "_autosave_scripts"); add_child(autosave_timer); grab_focus_block = false; diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 0c876108a5..294294fc56 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -168,6 +168,7 @@ class ScriptEditor : public PanelContainer { REQUEST_DOCS, HELP_SEARCH_FIND, HELP_SEARCH_FIND_NEXT, + HELP_SEARCH_FIND_PREVIOUS, WINDOW_MOVE_UP, WINDOW_MOVE_DOWN, WINDOW_NEXT, @@ -342,6 +343,7 @@ class ScriptEditor : public PanelContainer { void _save_layout(); void _editor_settings_changed(); void _autosave_scripts(); + void _update_autosave_timer(); void _update_members_overview_visibility(); void _update_members_overview(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 69fd592652..40734cffc4 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -178,6 +178,7 @@ void SpriteEditor::_update_mesh_data() { err_dialog->popup_centered_minsize(); return; } + Ref<Image> image = texture->get_data(); ERR_FAIL_COND(image.is_null()); Rect2 rect; @@ -190,7 +191,12 @@ void SpriteEditor::_update_mesh_data() { bm.instance(); bm->create_from_image_alpha(image); - int grow = island_merging->get_value(); + int shrink = shrink_pixels->get_value(); + if (shrink > 0) { + bm->shrink_mask(shrink, rect); + } + + int grow = grow_pixels->get_value(); if (grow > 0) { bm->grow_mask(grow, rect); } @@ -338,6 +344,13 @@ void SpriteEditor::_convert_to_mesh_2d_node() { } void SpriteEditor::_convert_to_polygon_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); + err_dialog->popup_centered_minsize(); + return; + } + Polygon2D *polygon_2d_instance = memnew(Polygon2D); int total_point_count = 0; @@ -362,12 +375,6 @@ void SpriteEditor::_convert_to_polygon_2d_node() { Vector<Vector2> outline = computed_outline_lines[i]; Vector<Vector2> uv_outline = outline_lines[i]; - if (outline.size() < 3) { - err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); - err_dialog->popup_centered_minsize(); - return; - } - PoolIntArray pia; pia.resize(outline.size()); PoolIntArray::Write pia_write = pia.write(); @@ -396,16 +403,17 @@ void SpriteEditor::_convert_to_polygon_2d_node() { } void SpriteEditor::_create_collision_polygon_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); + err_dialog->popup_centered_minsize(); + return; + } + for (int i = 0; i < computed_outline_lines.size(); i++) { Vector<Vector2> outline = computed_outline_lines[i]; - if (outline.size() < 3) { - err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); - err_dialog->popup_centered_minsize(); - continue; - } - CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); collision_polygon_2d_instance->set_polygon(outline); @@ -419,16 +427,17 @@ void SpriteEditor::_create_collision_polygon_2d_node() { } void SpriteEditor::_create_light_occluder_2d_node() { + + if (computed_outline_lines.empty()) { + err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); + err_dialog->popup_centered_minsize(); + return; + } + for (int i = 0; i < computed_outline_lines.size(); i++) { Vector<Vector2> outline = computed_outline_lines[i]; - if (outline.size() < 3) { - err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); - err_dialog->popup_centered_minsize(); - continue; - } - Ref<OccluderPolygon2D> polygon; polygon.instance(); @@ -531,10 +540,14 @@ void SpriteEditor::_debug_uv_draw() { Ref<Texture> tex = node->get_texture(); ERR_FAIL_COND(!tex.is_valid()); + + Point2 draw_pos_offset = Point2(1.0, 1.0); + Size2 draw_size_offset = Size2(2.0, 2.0); + debug_uv->set_clip_contents(true); - debug_uv->draw_texture(tex, Point2()); - debug_uv->set_custom_minimum_size(tex->get_size()); - //debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); + debug_uv->draw_texture(tex, draw_pos_offset); + debug_uv->set_custom_minimum_size(tex->get_size() + draw_size_offset); + debug_uv->draw_set_transform(draw_pos_offset, 0, Size2(1.0, 1.0)); Color color = Color(1.0, 0.8, 0.7); @@ -604,13 +617,21 @@ SpriteEditor::SpriteEditor() { simplification->set_value(2); hb->add_child(simplification); hb->add_spacer(); + hb->add_child(memnew(Label(TTR("Shrink (Pixels): ")))); + shrink_pixels = memnew(SpinBox); + shrink_pixels->set_min(0); + shrink_pixels->set_max(10); + shrink_pixels->set_step(1); + shrink_pixels->set_value(0); + hb->add_child(shrink_pixels); + hb->add_spacer(); hb->add_child(memnew(Label(TTR("Grow (Pixels): ")))); - island_merging = memnew(SpinBox); - island_merging->set_min(0); - island_merging->set_max(10); - island_merging->set_step(1); - island_merging->set_value(2); - hb->add_child(island_merging); + grow_pixels = memnew(SpinBox); + grow_pixels->set_min(0); + grow_pixels->set_max(10); + grow_pixels->set_step(1); + grow_pixels->set_value(2); + hb->add_child(grow_pixels); hb->add_spacer(); update_preview = memnew(Button); update_preview->set_text(TTR("Update Preview")); diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 81be4a19e9..4ca7bca1a8 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -67,7 +67,8 @@ class SpriteEditor : public Control { Vector<int> computed_indices; SpinBox *simplification; - SpinBox *island_merging; + SpinBox *grow_pixels; + SpinBox *shrink_pixels; Button *update_preview; void _menu_option(int p_option); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index d0320bcd8b..0cef5a8b6f 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -31,7 +31,7 @@ #include "text_editor.h" #include "core/os/keyboard.h" -#include "editor_node.h" +#include "editor/editor_node.h" void TextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { highlighters[p_highlighter->get_name()] = p_highlighter; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c962751c7a..3a9e48cfdb 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -178,6 +178,9 @@ bool VisualShaderEditor::_is_available(int p_mode) { } void VisualShaderEditor::update_custom_nodes() { + if (members_dialog->is_visible()) { + return; + } clear_custom_types(); List<StringName> class_list; ScriptServer::get_global_class_list(&class_list); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index f70dcab931..d903e153a7 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1753,6 +1753,12 @@ void ProjectManager::_notification(int p_what) { Engine::get_singleton()->set_editor_hint(false); } break; + case NOTIFICATION_RESIZED: { + + if (open_templates->is_visible()) { + open_templates->popup_centered_minsize(); + } + } break; case NOTIFICATION_READY: { if (_project_list->get_project_count() == 0 && StreamPeerSSL::is_available()) diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index f177e634a6..35187214a6 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -845,13 +845,9 @@ void ProjectSettingsEditor::_item_adds(String) { void ProjectSettingsEditor::_item_add() { - Variant value; - switch (type->get_selected()) { - case 0: value = false; break; - case 1: value = 0; break; - case 2: value = 0.0; break; - case 3: value = ""; break; - } + // Initialize the property with the default value for the given type + Variant::CallError ce; + const Variant value = Variant::construct(Variant::Type(type->get_selected()), NULL, 0, ce); String catname = category->get_text().strip_edges(); String propname = property->get_text().strip_edges(); @@ -1834,10 +1830,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { type = memnew(OptionButton); type->set_h_size_flags(Control::SIZE_EXPAND_FILL); add_prop_bar->add_child(type); - type->add_item("bool"); - type->add_item("int"); - type->add_item("float"); - type->add_item("string"); + + // Start at 1 to avoid adding "Nil" as an option + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + type->add_item(Variant::get_type_name(Variant::Type(i)), i); + } Button *add = memnew(Button); add_prop_bar->add_child(add); diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 489049c543..16da2771b9 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -3730,6 +3730,7 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { Ref<ConcavePolygonShape> cs2 = s; Ref<ArrayMesh> mesh = cs2->get_debug_mesh(); p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), material); + p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); } if (Object::cast_to<RayShape>(*s)) { diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index f2f51d9dd3..1ea9399c02 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -29,11 +29,12 @@ /*************************************************************************/ #include "editor_scene_importer_assimp.h" + #include "core/bind/core_bind.h" #include "core/io/image_loader.h" #include "editor/editor_file_system.h" +#include "editor/editor_settings.h" #include "editor/import/resource_importer_scene.h" -#include "editor_settings.h" #include "import_utils.h" #include "scene/3d/camera.h" #include "scene/3d/light.h" @@ -1350,4 +1351,4 @@ void EditorSceneImporterAssimp::_generate_node( _generate_node(state, recursive_state.skeleton, recursive_state.assimp_node->mChildren[i], recursive_state.new_node != NULL ? recursive_state.new_node : recursive_state.parent_node); } -}
\ No newline at end of file +} diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index ae9a00543c..aa48ab44f2 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -39,7 +39,7 @@ [b]Note:[/b] If the library defines tool scripts that run inside the editor, [code]reloadable[/code] must be [code]false[/code]. Otherwise, the editor will attempt to unload the tool scripts while they're in use and crash. </member> <member name="singleton" type="bool" setter="set_singleton" getter="is_singleton" default="false"> - If [code]true[/code], Godot loads the library at startup rather than the first time a script uses the library, calling [code]gdnative_singleton[/code] after initializing the library. The library remains loaded as long as Godot is running. + If [code]true[/code], Godot loads the library at startup rather than the first time a script uses the library, calling [code]{prefix}gdnative_singleton[/code] after initializing the library (where [code]{prefix}[/code] is the value of [member symbol_prefix]). The library remains loaded as long as Godot is running. [b]Note:[/b] A singleton library cannot be [member reloadable]. </member> <member name="symbol_prefix" type="String" setter="set_symbol_prefix" getter="get_symbol_prefix" default=""godot_""> diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 783ad4e147..ee9e71d4a0 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -339,7 +339,7 @@ bool GDNative::initialize() { if (err || !library_init) { OS::get_singleton()->close_dynamic_library(native_handle); native_handle = NULL; - ERR_PRINT("Failed to obtain godot_gdnative_init symbol"); + ERR_PRINTS("Failed to obtain " + library->get_symbol_prefix() + "gdnative_init symbol"); return false; } diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 6ff6262b56..0194199133 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -277,7 +277,7 @@ void register_gdnative_types() { proc_ptr); if (err != OK) { - ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); + ERR_PRINTS("No " + lib->get_symbol_prefix() + "gdnative_singleton in \"" + singleton->get_library()->get_current_library_path() + "\" found"); } else { singleton_gdnatives.push_back(singleton); ((void (*)())proc_ptr)(); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 03d731a5f0..6b5c26ec81 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -298,7 +298,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN const int line = LINE_NUMBER_TO_INDEX(p_func->line); r_symbol.range.start.line = line; r_symbol.range.start.character = p_func->column; - r_symbol.range.end.line = MAX(p_func->body->end_line - 2, p_func->body->line); + r_symbol.range.end.line = MAX(p_func->body->end_line - 2, r_symbol.range.start.line); r_symbol.range.end.character = lines[r_symbol.range.end.line].length(); r_symbol.selectionRange.start.line = r_symbol.range.start.line; r_symbol.documentation = parse_documentation(line); diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index ae2aaf6aee..90646f73ba 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -134,6 +134,22 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { } void GDScriptLanguageProtocol::initialized(const Variant &p_params) { + + lsp::GodotCapabilities capabilities; + + DocData *doc = EditorHelp::get_doc_data(); + for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { + + lsp::GodotNativeClassInfo gdclass; + gdclass.name = E->get().name; + gdclass.class_doc = &(E->get()); + if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E->get().name))) { + gdclass.class_info = ptr; + } + capabilities.native_classes.push_back(gdclass); + } + + notify_client("gdscript/capabilities", capabilities.to_json()); } void GDScriptLanguageProtocol::poll() { diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 62c212a2bd..8d58b99e02 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -56,8 +56,9 @@ void GDScriptLanguageServer::_notification(int p_what) { void GDScriptLanguageServer::thread_main(void *p_userdata) { GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata); while (!self->thread_exit) { + // Poll 20 times per second self->protocol.poll(); - OS::get_singleton()->delay_usec(10); + OS::get_singleton()->delay_usec(50000); } } diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 6baa7e4219..c289ff6c07 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -269,7 +269,11 @@ Error GDScriptWorkspace::initialize() { params += params.empty() ? "..." : ", ..."; } - symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type; + String return_type = data.return_type; + if (return_type.empty()) { + return_type = "void"; + } + symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type; symbol.documentation = data.description; class_symbol.children.push_back(symbol); } diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 61a0980c41..cf360b5291 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -31,7 +31,9 @@ #ifndef GODOT_LSP_H #define GODOT_LSP_H -#include "core/variant.h" +#include "core/class_db.h" +#include "core/list.h" +#include "editor/doc/doc_data.h" namespace lsp { @@ -1567,6 +1569,39 @@ struct InitializeResult { } }; +struct GodotNativeClassInfo { + + String name; + const DocData::ClassDoc *class_doc = NULL; + const ClassDB::ClassInfo *class_info = NULL; + + Dictionary to_json() { + Dictionary dict; + dict["name"] = name; + dict["inherits"] = class_doc->inherits; + return dict; + } +}; + +/** Features not included in the standart lsp specifications */ +struct GodotCapabilities { + + /** + * Native class list + */ + List<GodotNativeClassInfo> native_classes; + + Dictionary to_json() { + Dictionary dict; + Array classes; + for (List<GodotNativeClassInfo>::Element *E = native_classes.front(); E; E = E->next()) { + classes.push_back(E->get().to_json()); + } + dict["native_classes"] = classes; + return dict; + } +}; + /** Format BBCode documentation from DocData to markdown */ static String marked_documentation(const String &p_bbcode) { diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index ca656b4b9b..204f4e8905 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -237,6 +237,7 @@ Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) { Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS> >(p_key); + ERR_FAIL_COND_V_MSG(key.is_null(), NULL, "Invalid private key argument."); mbedtls_x509write_cert crt; mbedtls_x509write_crt_init(&crt); diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs index 417032da54..ab37d89955 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs @@ -160,9 +160,16 @@ namespace GodotTools if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) return true; // No solution to build - // Make sure to update the API assemblies if they happen to be missing. Just in - // case the user decided to delete them at some point after they were loaded. - Internal.UpdateApiAssembliesFromPrebuilt(); + // Make sure the API assemblies are up to date before building the project. + // We may not have had the chance to update the release API assemblies, and the debug ones + // may have been deleted by the user at some point after they were loaded by the Godot editor. + string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "Release" ? "Release" : "Debug"); + + if (!string.IsNullOrEmpty(apiAssembliesUpdateError)) + { + ShowBuildErrorDialog("Failed to update the Godot API assemblies"); + return false; + } var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings(); var buildTool = (BuildTool) editorSettings.GetSetting("mono/builds/build_tool"); diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 7da7cff933..12edd651df 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -34,7 +34,7 @@ namespace GodotTools private bool CreateProjectSolution() { - using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2)) + using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3)) { pr.Step("Generating C# project...".TTR()); @@ -73,9 +73,23 @@ namespace GodotTools return false; } - // Make sure to update the API assemblies if they happen to be missing. Just in - // case the user decided to delete them at some point after they were loaded. - Internal.UpdateApiAssembliesFromPrebuilt(); + pr.Step("Updating Godot API assemblies...".TTR()); + + string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug"); + + if (!string.IsNullOrEmpty(debugApiAssembliesError)) + { + ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError); + return false; + } + + string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release"); + + if (!string.IsNullOrEmpty(releaseApiAssembliesError)) + { + ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError); + return false; + } pr.Step("Done".TTR()); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index 9e24138143..01aa0d0ab1 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -40,8 +40,7 @@ namespace GodotTools.Ides protected ILogger Logger { - get => logger ?? (logger = new ConsoleLogger()); - set => logger = value; + get => logger ?? (logger = new GodotLogger()); } private void StartServer() diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 7783576910..836c9c11e4 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -10,8 +10,8 @@ namespace GodotTools.Internals public const string CSharpLanguageType = "CSharpScript"; public const string CSharpLanguageExtension = "cs"; - public static string UpdateApiAssembliesFromPrebuilt() => - internal_UpdateApiAssembliesFromPrebuilt(); + public static string UpdateApiAssembliesFromPrebuilt(string config) => + internal_UpdateApiAssembliesFromPrebuilt(config); public static string FullTemplatesDir => internal_FullTemplatesDir(); @@ -55,7 +55,7 @@ namespace GodotTools.Internals // Internal Calls [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_UpdateApiAssembliesFromPrebuilt(); + private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_FullTemplatesDir(); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 0e6c58c9d7..748447005f 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -75,7 +75,7 @@ bool generate_api_solution(const String &p_solution_dir, const String &p_core_pr p_editor_proj_dir, p_editor_compile_items, GDMono::get_singleton()->get_tools_project_editor_assembly()); } else { - MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.ApiSolutionGenerationDomain"); + MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ApiSolutionGeneration"); CRASH_COND(temp_domain == NULL); _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 5a84d9e3b8..1564d73c2a 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -230,31 +230,9 @@ uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies); } -float godot_icall_Globals_EditorScale() { - return EDSCALE; -} - -MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { - String setting = GDMonoMarshal::mono_string_to_godot(p_setting); - Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); - Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); - return GDMonoMarshal::variant_to_mono_object(result); -} - -MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { - String setting = GDMonoMarshal::mono_string_to_godot(p_setting); - Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); - Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); - return GDMonoMarshal::variant_to_mono_object(result); -} - -MonoString *godot_icall_Globals_TTR(MonoString *p_text) { - String text = GDMonoMarshal::mono_string_to_godot(p_text); - return GDMonoMarshal::mono_string_from_godot(TTR(text)); -} - -MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() { - String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(); +MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { + String config = GDMonoMarshal::mono_string_to_godot(p_config); + String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config); return GDMonoMarshal::mono_string_from_godot(error_str); } @@ -365,6 +343,29 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { } } +float godot_icall_Globals_EditorScale() { + return EDSCALE; +} + +MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { + String setting = GDMonoMarshal::mono_string_to_godot(p_setting); + Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); + Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed); + return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) { + String setting = GDMonoMarshal::mono_string_to_godot(p_setting); + Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value); + Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed); + return GDMonoMarshal::variant_to_mono_object(result); +} + +MonoString *godot_icall_Globals_TTR(MonoString *p_text) { + String text = GDMonoMarshal::mono_string_to_godot(p_text); + return GDMonoMarshal::mono_string_from_godot(TTR(text)); +} + MonoString *godot_icall_Utils_OS_GetPlatformName() { String os_name = OS::get_singleton()->get_name(); return GDMonoMarshal::mono_string_from_godot(os_name); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 80a7335b1d..e83152d668 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -32,9 +32,13 @@ #include <mono/metadata/image.h> +#include "core/os/os.h" + #include "../mono_gd/gd_mono.h" #include "../mono_gd/gd_mono_assembly.h" +namespace GodotSharpExport { + String get_assemblyref_name(MonoImage *p_image, int index) { const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF); @@ -45,7 +49,7 @@ String get_assemblyref_name(MonoImage *p_image, int index) { return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])); } -Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { MonoImage *image = p_assembly->get_image(); for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) { @@ -96,8 +100,8 @@ Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, co return OK; } -Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies) { - MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); +Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) { + MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport"); ERR_FAIL_NULL_V(export_domain, FAILED); _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain); @@ -110,7 +114,9 @@ Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_proje ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + p_project_dll_name + "'."); Vector<String> search_dirs; - GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir); + GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir); return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies); } + +} // namespace GodotSharpExport diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 8d121a6bc3..58e46e2f2d 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -39,10 +39,11 @@ namespace GodotSharpExport { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); + Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies); -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies); } // namespace GodotSharpExport diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 4b2525c692..5fa8aed5a9 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -43,6 +43,8 @@ #include "utils/android_utils.h" #endif +#include "mono_gd/gd_mono.h" + namespace GodotSharpDirs { String _get_expected_build_config() { @@ -59,20 +61,6 @@ String _get_expected_build_config() { #endif } -String _get_expected_api_build_config() { -#ifdef TOOLS_ENABLED - return "Debug"; -#else - -#ifdef DEBUG_ENABLED - return "Debug"; -#else - return "Release"; -#endif - -#endif -} - String _get_mono_user_dir() { #ifdef TOOLS_ENABLED if (EditorSettings::get_singleton()) { @@ -134,7 +122,7 @@ private: res_data_dir = "res://.mono"; res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); - res_assemblies_dir = res_assemblies_base_dir.plus_file(_get_expected_api_build_config()); + res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); res_config_dir = res_data_dir.plus_file("etc").plus_file("mono"); // TODO use paths from csproj diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 544bfc4615..25f8141dc9 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -381,10 +381,10 @@ void GDMono::initialize_load_assemblies() { } bool GDMono::_are_api_assemblies_out_of_sync() { - bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated); + bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated); #ifdef TOOLS_ENABLED if (!out_of_sync) - out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync; + out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync; #endif return out_of_sync; } @@ -523,10 +523,10 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo return true; } -APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { - APIAssembly::Version api_assembly_version; +ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) { + ApiAssemblyInfo::Version api_assembly_version; - const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ? + const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE ? BINDINGS_CLASS_NATIVECALLS : BINDINGS_CLASS_NATIVECALLS_EDITOR; @@ -549,8 +549,8 @@ APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssemb return api_assembly_version; } -String APIAssembly::to_string(APIAssembly::Type p_type) { - return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR"; +String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) { + return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR"; } bool GDMono::_load_corlib_assembly() { @@ -567,16 +567,12 @@ bool GDMono::_load_corlib_assembly() { } #ifdef TOOLS_ENABLED -bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config) { - - bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ? - GDMono::get_singleton()->core_api_assembly_out_of_sync : - GDMono::get_singleton()->editor_api_assembly_out_of_sync; +bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) { String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); - String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; + String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME; // Create destination directory if needed if (!DirAccess::exists(dst_dir)) { @@ -590,35 +586,102 @@ bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const Stri } } + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + String xml_file = assembly_name + ".xml"; + if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) + WARN_PRINTS("Failed to copy '" + xml_file + "'."); + + String pdb_file = assembly_name + ".pdb"; + if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) + WARN_PRINTS("Failed to copy '" + pdb_file + "'."); + String assembly_file = assembly_name + ".dll"; - String assembly_src = src_dir.plus_file(assembly_file); - String assembly_dst = dst_dir.plus_file(assembly_file); + if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) { + ERR_PRINTS("Failed to copy '" + assembly_file + "'."); + return false; + } - if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) { - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + return true; +} - String xml_file = assembly_name + ".xml"; - if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) - WARN_PRINTS("Failed to copy '" + xml_file + "'."); +static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) { + String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - String pdb_file = assembly_name + ".pdb"; - if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) - WARN_PRINTS("Failed to copy '" + pdb_file + "'."); + if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) + return false; - Error err = da->copy(assembly_src, assembly_dst); + String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); - if (err != OK) { - ERR_PRINTS("Failed to copy '" + assembly_file + "'."); - return false; - } + if (!FileAccess::exists(cached_api_hash_path)) + return false; + + Ref<ConfigFile> cfg; + cfg.instance(); + Error cfg_err = cfg->load(cached_api_hash_path); + ERR_FAIL_COND_V(cfg_err != OK, false); - api_assembly_out_of_sync = false; + // Checking the modified time is good enough + if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") || + FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) { + return false; } + r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") || + GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") || + GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") || + GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") || + GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") || + GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash"); + return true; } -String GDMono::update_api_assemblies_from_prebuilt() { +static void create_cached_api_hash_for(const String &p_api_assemblies_dir) { + + String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg"); + + Ref<ConfigFile> cfg; + cfg.instance(); + + cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path)); + cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path)); + + cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version()); + cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); + cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version()); + cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version()); + + // This assumes the prebuilt api assemblies we copied to the project are not out of sync + cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash()); + cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash()); + + Error err = cfg->save(cached_api_hash_path); + ERR_FAIL_COND(err != OK); +} + +bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) { + MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies"); + ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies"); + _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain); + + _GDMONO_SCOPE_DOMAIN_(temp_domain); + + GDMono::LoadedApiAssembly temp_core_api_assembly; + GDMono::LoadedApiAssembly temp_editor_api_assembly; + + if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly, + p_config, /* refonly: */ true, /* loaded_callback: */ NULL)) { + return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync; + } + + return true; // Failed to load, assume they're outdated assemblies +} + +String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) { #define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \ ( \ @@ -629,46 +692,55 @@ String GDMono::update_api_assemblies_from_prebuilt() { String("and the prebuilt assemblies are missing.") : \ String("and we failed to copy the prebuilt assemblies."))) - bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync; + String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config); - String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) - return String(); // No update needed + bool api_assemblies_out_of_sync = false; - const int CONFIGS_LEN = 2; - String configs[CONFIGS_LEN] = { String("Debug"), String("Release") }; + if (p_core_api_out_of_sync && p_editor_api_out_of_sync) { + api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync; + } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) { + // Determine if they're out of sync + if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) { + api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config); + } + } - for (int i = 0; i < CONFIGS_LEN; i++) { - String config = configs[i]; + // Note: Even if only one of the assemblies if missing or out of sync, we update both - print_verbose("Updating '" + config + "' API assemblies"); + if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) + return String(); // No update needed - String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(config); - String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + print_verbose("Updating '" + p_config + "' API assemblies"); - if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) { - return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ false); - } + String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); + String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - // Copy the prebuilt Api - if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE, config) || - !copy_prebuilt_api_assembly(APIAssembly::API_EDITOR, config)) { - return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ true); - } + if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) { + return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false); } + // Copy the prebuilt Api + if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) || + !copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) { + return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true); + } + + // Cache the api hash of the assemblies we just copied + create_cached_api_hash_for(dst_assemblies_dir); + return String(); // Updated successfully #undef FAIL_REASON } #endif -bool GDMono::_load_core_api_assembly() { +bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { - if (core_api_assembly) + if (r_loaded_api_assembly.assembly) return true; #ifdef TOOLS_ENABLED @@ -676,101 +748,115 @@ bool GDMono::_load_core_api_assembly() { // If running the project manager, load it from the prebuilt API directory String assembly_dir = !Main::is_project_manager() ? - GodotSharpDirs::get_res_assemblies_dir() : - GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : + GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); bool success = FileAccess::exists(assembly_path) && - load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly); + load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); #else - bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly); + bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly); #endif if (success) { - APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE); - core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; - if (!core_api_assembly_out_of_sync) { - GDMonoUtils::update_godot_api_cache(); - - _install_trace_listener(); - } + ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE); + r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; } else { - core_api_assembly_out_of_sync = false; + r_loaded_api_assembly.out_of_sync = false; } return success; } #ifdef TOOLS_ENABLED -bool GDMono::_load_editor_api_assembly() { +bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) { - if (editor_api_assembly) + if (r_loaded_api_assembly.assembly) return true; // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date // If running the project manager, load it from the prebuilt API directory String assembly_dir = !Main::is_project_manager() ? - GodotSharpDirs::get_res_assemblies_dir() : - GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) : + GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config); String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); bool success = FileAccess::exists(assembly_path) && - load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly); + load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly); if (success) { - APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR); - editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || - GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || - GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; + ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR); + r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash || + GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || + GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version; } else { - editor_api_assembly_out_of_sync = false; + r_loaded_api_assembly.out_of_sync = false; } return success; } #endif -bool GDMono::_try_load_api_assemblies() { - - if (!_load_core_api_assembly()) { +bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, + const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) { + if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load Core API assembly"); return false; } #ifdef TOOLS_ENABLED - if (!_load_editor_api_assembly()) { + if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load Editor API assembly"); return false; } - if (editor_api_assembly_out_of_sync) + if (r_editor_api_assembly.out_of_sync) return false; #endif // Check if the core API assembly is out of sync only after trying to load the // editor API assembly. Otherwise, if both assemblies are out of sync, we would // only update the former as we won't know the latter also needs to be updated. - if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated) + if (r_core_api_assembly.out_of_sync) return false; + if (p_callback) + return p_callback(); + return true; } +bool GDMono::_on_core_api_assembly_loaded() { + GDMonoUtils::update_godot_api_cache(); + + if (!GDMonoUtils::mono_cache.godot_api_cache_updated) + return false; + + get_singleton()->_install_trace_listener(); + + return true; +} + +bool GDMono::_try_load_api_assemblies_preset() { + return _try_load_api_assemblies(core_api_assembly, editor_api_assembly, + get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded); +} + void GDMono::_load_api_assemblies() { - bool api_assemblies_loaded = _try_load_api_assemblies(); + bool api_assemblies_loaded = _try_load_api_assemblies_preset(); if (!api_assemblies_loaded) { #ifdef TOOLS_ENABLED - // The API assemblies are out of sync. Fine, try one more time, but this time - // update them from the prebuilt assemblies directory before trying to load them. + // The API assemblies are out of sync or some other error happened. Fine, try one more time, but + // this time update them from the prebuilt assemblies directory before trying to load them again. // Shouldn't happen. The project manager loads the prebuilt API assemblies CRASH_COND_MSG(Main::is_project_manager(), "Failed to load one of the prebuilt API assemblies."); @@ -780,7 +866,7 @@ void GDMono::_load_api_assemblies() { CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain."); // 2. Update the API assemblies - String update_error = update_api_assemblies_from_prebuilt(); + String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync); CRASH_COND_MSG(!update_error.empty(), update_error); // 3. Load the scripts domain again @@ -788,7 +874,7 @@ void GDMono::_load_api_assemblies() { CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); // 4. Try loading the updated assemblies - api_assemblies_loaded = _try_load_api_assemblies(); + api_assemblies_loaded = _try_load_api_assemblies_preset(); #endif } @@ -796,14 +882,14 @@ void GDMono::_load_api_assemblies() { // welp... too bad if (_are_api_assemblies_out_of_sync()) { - if (core_api_assembly_out_of_sync) { + if (core_api_assembly.out_of_sync) { ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync."); } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed."); } #ifdef TOOLS_ENABLED - if (editor_api_assembly_out_of_sync) { + if (editor_api_assembly.out_of_sync) { ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync."); } #endif @@ -852,15 +938,14 @@ void GDMono::_install_trace_listener() { #ifdef DEBUG_ENABLED // Install the trace listener now before the project assembly is loaded - typedef void (*DebuggingUtils_InstallTraceListener)(MonoObject **); + GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); + GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener"); + MonoException *exc = NULL; - GDMonoClass *debug_utils = core_api_assembly->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); - DebuggingUtils_InstallTraceListener install_func = - (DebuggingUtils_InstallTraceListener)debug_utils->get_method_thunk("InstallTraceListener"); - install_func((MonoObject **)&exc); + install_func->invoke_raw(NULL, NULL, &exc); if (exc) { - ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener."); GDMonoUtils::debug_print_unhandled_exception(exc); + ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener."); } #endif } @@ -871,7 +956,7 @@ Error GDMono::_load_scripts_domain() { print_verbose("Mono: Loading scripts domain..."); - scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain"); + scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts"); ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain."); @@ -903,10 +988,8 @@ Error GDMono::_unload_scripts_domain() { _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED - editor_api_assembly = NULL; tools_assembly = NULL; tools_project_editor_assembly = NULL; #endif @@ -1076,16 +1159,9 @@ GDMono::GDMono() { root_domain = NULL; scripts_domain = NULL; - core_api_assembly_out_of_sync = false; -#ifdef TOOLS_ENABLED - editor_api_assembly_out_of_sync = false; -#endif - corlib_assembly = NULL; - core_api_assembly = NULL; project_assembly = NULL; #ifdef TOOLS_ENABLED - editor_api_assembly = NULL; tools_assembly = NULL; tools_project_editor_assembly = NULL; #endif diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 343d68bc2d..e14a0d8409 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -41,7 +41,7 @@ #include "../utils/mono_reg_utils.h" #endif -namespace APIAssembly { +namespace ApiAssemblyInfo { enum Type { API_CORE, API_EDITOR @@ -76,7 +76,7 @@ struct Version { }; String to_string(Type p_type); -} // namespace APIAssembly +} // namespace ApiAssemblyInfo class GDMono { @@ -86,44 +86,58 @@ public: POLICY_LOG_ERROR }; + struct LoadedApiAssembly { + GDMonoAssembly *assembly; + bool out_of_sync; + + LoadedApiAssembly() : + assembly(NULL), + out_of_sync(false) { + } + }; + private: bool runtime_initialized; bool finalizing_scripts_domain; + UnhandledExceptionPolicy unhandled_exception_policy; + MonoDomain *root_domain; MonoDomain *scripts_domain; - bool core_api_assembly_out_of_sync; -#ifdef TOOLS_ENABLED - bool editor_api_assembly_out_of_sync; -#endif + HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; GDMonoAssembly *corlib_assembly; - GDMonoAssembly *core_api_assembly; GDMonoAssembly *project_assembly; #ifdef TOOLS_ENABLED - GDMonoAssembly *editor_api_assembly; GDMonoAssembly *tools_assembly; GDMonoAssembly *tools_project_editor_assembly; #endif - HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; - - UnhandledExceptionPolicy unhandled_exception_policy; + LoadedApiAssembly core_api_assembly; + LoadedApiAssembly editor_api_assembly; - void _domain_assemblies_cleanup(uint32_t p_domain_id); + typedef bool (*CoreApiAssemblyLoadedCallback)(); bool _are_api_assemblies_out_of_sync(); + bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config); + + bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); +#ifdef TOOLS_ENABLED + bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly); +#endif + + static bool _on_core_api_assembly_loaded(); bool _load_corlib_assembly(); - bool _load_core_api_assembly(); #ifdef TOOLS_ENABLED - bool _load_editor_api_assembly(); bool _load_tools_assemblies(); #endif bool _load_project_assembly(); - bool _try_load_api_assemblies(); + bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly, + const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback); + bool _try_load_api_assemblies_preset(); void _load_api_assemblies(); void _install_trace_listener(); @@ -133,6 +147,8 @@ private: Error _load_scripts_domain(); Error _unload_scripts_domain(); + void _domain_assemblies_cleanup(uint32_t p_domain_id); + uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; @@ -166,9 +182,21 @@ public: #endif // TOOLS_ENABLED #endif // DEBUG_METHODS_ENABLED + _FORCE_INLINE_ static String get_expected_api_build_config() { +#ifdef TOOLS_ENABLED + return "Debug"; +#else +#ifdef DEBUG_ENABLED + return "Debug"; +#else + return "Release"; +#endif +#endif + } + #ifdef TOOLS_ENABLED - bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config); - String update_api_assemblies_from_prebuilt(); + bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config); + String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = NULL, const bool *p_editor_api_out_of_sync = NULL); #endif static GDMono *get_singleton() { return singleton; } @@ -188,10 +216,10 @@ public: _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; } _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } #ifdef TOOLS_ENABLED - _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; } _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; } #endif diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index e385f4c601..6504fbe423 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -550,6 +550,8 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) } MonoDomain *create_domain(const String &p_friendly_name) { + print_verbose("Mono: Creating domain '" + p_friendly_name + "'..."); + MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL); if (domain) { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 7f36549ae4..4e90a08009 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1039,7 +1039,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const } List<PropertyInfo> props; - ClassDB::get_property_list(_get_base_type(), &props, true); + ClassDB::get_property_list(_get_base_type(), &props, false); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { if (E->get().name == property) { PropertyInfo pinfo = PropertyInfo(E->get().type, "value", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); @@ -1808,7 +1808,7 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const { List<PropertyInfo> props; - ClassDB::get_property_list(_get_base_type(), &props, true); + ClassDB::get_property_list(_get_base_type(), &props, false); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { if (E->get().name == property) { return PropertyInfo(E->get().type, "value." + String(index)); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 42d4c5e209..62b818150c 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -32,7 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" -#include "editor_scale.h" +#include "editor/editor_scale.h" #include "modules/visual_script/visual_script.h" #include "modules/visual_script/visual_script_builtin_funcs.h" #include "modules/visual_script/visual_script_flow_control.h" diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 13ce9bdca2..405949e8c1 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -31,8 +31,8 @@ #ifndef VISUALSCRIPT_PROPERTYSELECTOR_H #define VISUALSCRIPT_PROPERTYSELECTOR_H +#include "editor/editor_help.h" #include "editor/property_editor.h" -#include "editor_help.h" #include "scene/gui/rich_text_label.h" class VisualScriptPropertySelector : public ConfirmationDialog { diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index c3baf9de83..705e3485f5 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -21,10 +21,13 @@ </argument> <argument index="2" name="gd_mp_api" type="bool" default="false"> </argument> + <argument index="3" name="custom_headers" type="PoolStringArray" default="PoolStringArray( )"> + </argument> <description> Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network 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 (not supported in HTML5 platform). </description> </method> <method name="disconnect_from_host"> @@ -38,8 +41,25 @@ Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information. </description> </method> + <method name="get_connected_host" qualifiers="const"> + <return type="String"> + </return> + <description> + Return the IP address of the currently connected host. + </description> + </method> + <method name="get_connected_port" qualifiers="const"> + <return type="int"> + </return> + <description> + Return the IP port of the currently connected host. + </description> + </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. + </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. [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported. diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 63318e5874..86f2dae64f 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -82,6 +82,17 @@ </description> </method> </methods> + <members> + <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. + </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). + </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> + </members> <signals> <signal name="client_close_request"> <argument index="0" name="id" type="int"> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 409cc9f699..fad766ea5d 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,13 +64,20 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int } } -Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocols, const Vector<String> p_custom_headers) { String proto_string = p_protocols.join(","); String str = "ws://"; - if (p_ssl) + if (p_custom_headers.size()) { + WARN_PRINT_ONCE("Custom headers are not supported in in HTML5 platform."); + } + if (p_ssl) { str = "wss://"; + if (ssl_cert.is_valid()) { + WARN_PRINT_ONCE("Custom SSL certificate is not supported in HTML5 platform."); + } + } str += p_host + ":" + itos(p_port) + p_path; _is_connecting = true; @@ -193,12 +200,12 @@ void EMWSClient::disconnect_from_host(int p_code, String p_reason) { IP_Address EMWSClient::get_connected_host() const { - return IP_Address(); + ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export."); }; uint16_t EMWSClient::get_connected_port() const { - return 1025; + ERR_FAIL_V_MSG(0, "Not supported in HTML5 export."); }; int EMWSClient::get_max_packet_size() const { diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 1811d05eea..2d35f7f0f6 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -50,7 +50,7 @@ public: bool _is_connecting; Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocol = PoolVector<String>(), const Dictionary p_custom_headers = Dictionary()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 4ff5404c61..8bbd5aa37f 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -40,7 +40,7 @@ WebSocketClient::WebSocketClient() { WebSocketClient::~WebSocketClient() { } -Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) { +Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_protocols, bool gd_mp_api, const Vector<String> p_custom_headers) { _is_multiplayer = gd_mp_api; String host = p_url; @@ -72,7 +72,7 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco host = host.substr(0, p_len); } - return connect_to_host(host, path, port, ssl, p_protocols); + return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers); } void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { @@ -85,6 +85,17 @@ bool WebSocketClient::is_verify_ssl_enabled() const { return verify_ssl; } +Ref<X509Certificate> WebSocketClient::get_trusted_ssl_certificate() const { + + return ssl_cert; +} + +void WebSocketClient::set_trusted_ssl_certificate(Ref<X509Certificate> p_cert) { + + ERR_FAIL_COND(get_connection_status() != CONNECTION_DISCONNECTED); + ssl_cert = p_cert; +} + bool WebSocketClient::is_server() const { return false; @@ -132,13 +143,20 @@ void WebSocketClient::_on_error() { } void WebSocketClient::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api", "custom_headers"), &WebSocketClient::connect_to_url, DEFVAL(Vector<String>()), DEFVAL(false), DEFVAL(Vector<String>())); 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); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); + ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate"); + ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 7ddb9468a5..08fd6798fe 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -31,6 +31,7 @@ #ifndef WEBSOCKET_CLIENT_H #define WEBSOCKET_CLIENT_H +#include "core/crypto/crypto.h" #include "core/error_list.h" #include "websocket_multiplayer_peer.h" #include "websocket_peer.h" @@ -43,17 +44,20 @@ class WebSocketClient : public WebSocketMultiplayerPeer { protected: Ref<WebSocketPeer> _peer; bool verify_ssl; + Ref<X509Certificate> ssl_cert; static void _bind_methods(); public: - Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + 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); virtual void poll() = 0; - virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; + 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 void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual IP_Address 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 ef5f6f5c20..c7414075ed 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -42,19 +42,58 @@ WebSocketServer::~WebSocketServer() { void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); - ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(Vector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port); ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key); + ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", 0), "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"), &WebSocketServer::set_ssl_certificate); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ssl_certificate", "get_ssl_certificate"); + + ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain); + ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_ca_chain"); + ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); } +Ref<CryptoKey> WebSocketServer::get_private_key() const { + return private_key; +} + +void WebSocketServer::set_private_key(Ref<CryptoKey> p_key) { + ERR_FAIL_COND(is_listening()); + private_key = p_key; +} + +Ref<X509Certificate> WebSocketServer::get_ssl_certificate() const { + return ssl_cert; +} + +void WebSocketServer::set_ssl_certificate(Ref<X509Certificate> p_cert) { + ERR_FAIL_COND(is_listening()); + ssl_cert = p_cert; +} + +Ref<X509Certificate> WebSocketServer::get_ca_chain() const { + return ca_chain; +} + +void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) { + ERR_FAIL_COND(is_listening()); + ca_chain = p_ca_chain; +} + NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { if (is_listening()) return CONNECTION_CONNECTED; diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 83c0c10419..0b39f94473 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -31,6 +31,7 @@ #ifndef WEBSOCKET_H #define WEBSOCKET_H +#include "core/crypto/crypto.h" #include "core/reference.h" #include "websocket_multiplayer_peer.h" #include "websocket_peer.h" @@ -43,9 +44,13 @@ class WebSocketServer : public WebSocketMultiplayerPeer { protected: static void _bind_methods(); + Ref<CryptoKey> private_key; + Ref<X509Certificate> ssl_cert; + Ref<X509Certificate> ca_chain; + public: virtual void poll() = 0; - virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0; + virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0; virtual void stop() = 0; virtual bool is_listening() const = 0; virtual bool has_peer(int p_id) const = 0; @@ -62,6 +67,15 @@ public: void _on_disconnect(int32_t p_peer_id, bool p_was_clean); void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); + 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_ca_chain() const; + void set_ca_chain(Ref<X509Certificate> p_ca_chain); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; WebSocketServer(); diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 0006a057e0..a422f65cfc 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -86,6 +86,7 @@ void WSLClient::_do_handshake() { WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); data->obj = this; data->conn = _connection; + data->tcp = _tcp; data->is_server = false; data->id = 1; _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); @@ -151,7 +152,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, PoolVector<String> p_protocols) { +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) { ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); @@ -180,7 +181,8 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _connection = _tcp; _use_ssl = p_ssl; _host = p_host; - _protocols = p_protocols; + _protocols.clear(); + _protocols.append_array(p_protocols); _key = WSLPeer::generate_key(); // TODO custom extra headers (allow overriding this too?) @@ -199,6 +201,9 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, } request += "\r\n"; } + for (int i = 0; i < p_custom_headers.size(); i++) { + request += p_custom_headers[i] + "\r\n"; + } request += "\r\n"; _request = request.utf8(); @@ -236,7 +241,7 @@ void WSLClient::poll() { 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) != OK) { + if (ssl->connect_to_stream(_tcp, verify_ssl, _host, ssl_cert) != OK) { disconnect_from_host(); _on_error(); return; @@ -293,7 +298,7 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { _key = ""; _host = ""; - _protocols.resize(0); + _protocols.clear(); _use_ssl = false; _request = ""; @@ -305,12 +310,14 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { IP_Address WSLClient::get_connected_host() const { - return IP_Address(); + ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IP_Address()); + return _peer->get_connected_host(); } uint16_t WSLClient::get_connected_port() const { - return 1025; + ERR_FAIL_COND_V(!_peer->is_connected_to_host(), 0); + return _peer->get_connected_port(); } Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 57dfd635b7..870be94a87 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -64,7 +64,7 @@ private: String _key; String _host; - PoolVector<String> _protocols; + Vector<String> _protocols; bool _use_ssl; void _do_handshake(); @@ -72,7 +72,7 @@ private: public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + 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>()); int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 74fb901232..32beccde5d 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -208,7 +208,6 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne _data = p_data; _data->peer = this; _data->valid = true; - _connection = Ref<StreamPeer>(_data->conn); if (_data->is_server) wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data); @@ -302,18 +301,16 @@ void WSLPeer::close(int p_code, String p_reason) { IP_Address WSLPeer::get_connected_host() const { - ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); + ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address()); - IP_Address ip; - return ip; + return _data->tcp->get_connected_host(); } uint16_t WSLPeer::get_connected_port() const { - ERR_FAIL_COND_V(!is_connected_to_host(), 0); + ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), 0); - uint16_t port = 0; - return port; + return _data->tcp->get_connected_port(); } void WSLPeer::invalidate() { diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index d51b304fe1..01ad250468 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -35,6 +35,7 @@ #include "core/error_list.h" #include "core/io/packet_peer.h" +#include "core/io/stream_peer_tcp.h" #include "core/ring_buffer.h" #include "packet_buffer.h" #include "websocket_peer.h" @@ -55,6 +56,7 @@ public: void *obj; void *peer; Ref<StreamPeer> conn; + Ref<StreamPeerTCP> tcp; int id; wslay_event_context_ptr ctx; @@ -77,7 +79,6 @@ private: static bool _wsl_poll(struct PeerData *p_data); static void _wsl_destroy(struct PeerData **p_data); - Ref<StreamPeer> _connection; struct PeerData *_data; uint8_t _is_string; // Our packet info is just a boolean (is_string), using uint8_t for it. diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index efb526eed1..993dceafb9 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -35,6 +35,7 @@ #include "core/project_settings.h" WSLServer::PendingPeer::PendingPeer() { + use_ssl = false; time = 0; has_request = false; response_sent = 0; @@ -42,7 +43,7 @@ WSLServer::PendingPeer::PendingPeer() { memset(req_buf, 0, sizeof(req_buf)); } -bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { +bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) { Vector<String> psa = String((char *)req_buf).split("\r\n"); int len = psa.size(); ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); @@ -97,9 +98,19 @@ bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { return true; } -Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { +Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) { if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) return ERR_TIMEOUT; + if (use_ssl) { + Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL> >(connection); + if (ssl.is_null()) + return FAILED; + ssl->poll(); + if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) + return ERR_BUSY; + else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) + return FAILED; + } if (!has_request) { int read = 0; while (true) { @@ -143,11 +154,11 @@ Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { return OK; } -Error WSLServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { +Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp_api) { ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); _is_multiplayer = gd_mp_api; - _protocols = p_protocols; + _protocols.append_array(p_protocols); _server->listen(p_port); return OK; @@ -185,6 +196,7 @@ void WSLServer::poll() { WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); data->obj = this; data->conn = ppeer->connection; + data->tcp = ppeer->tcp; data->is_server = true; data->id = id; @@ -204,12 +216,21 @@ void WSLServer::poll() { return; while (_server->is_connection_available()) { - Ref<StreamPeer> conn = _server->take_connection(); + Ref<StreamPeerTCP> conn = _server->take_connection(); if (is_refusing_new_connections()) continue; // Conn will go out-of-scope and be closed. Ref<PendingPeer> peer = memnew(PendingPeer); - peer->connection = conn; + 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; + } else { + peer->connection = conn; + } + peer->tcp = conn; peer->time = OS::get_singleton()->get_ticks_msec(); _pending.push_back(peer); } @@ -231,6 +252,7 @@ void WSLServer::stop() { } _pending.clear(); _peer_map.clear(); + _protocols.clear(); } bool WSLServer::has_peer(int p_id) const { diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index 2ceb941073..aae563355e 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -36,6 +36,7 @@ #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/tcp_server.h" @@ -49,10 +50,12 @@ private: class PendingPeer : public Reference { private: - bool _parse_request(const PoolStringArray p_protocols); + bool _parse_request(const Vector<String> p_protocols); public: + Ref<StreamPeerTCP> tcp; Ref<StreamPeer> connection; + bool use_ssl; int time; uint8_t req_buf[WSL_MAX_HEADER_SIZE]; @@ -65,7 +68,7 @@ private: PendingPeer(); - Error do_handshake(const PoolStringArray p_protocols); + Error do_handshake(const Vector<String> p_protocols); }; int _in_buf_size; @@ -75,11 +78,11 @@ private: List<Ref<PendingPeer> > _pending; Ref<TCP_Server> _server; - PoolStringArray _protocols; + Vector<String> _protocols; public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; int get_max_packet_size() const; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b61575e2aa..a43f195b84 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -766,17 +766,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); - String value; - if (attr_value != 0xFFFFFFFF) - value = string_table[attr_value]; - else - value = "Res #" + itos(attr_resid); + const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid); String attrname = string_table[attr_name]; - String nspace; - if (attr_nspace != 0xFFFFFFFF) - nspace = string_table[attr_nspace]; - else - nspace = ""; + const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : ""; //replace project information if (tname == "manifest" && attrname == "package") { @@ -828,14 +820,16 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { encode_uint32(min_gles3 ? 0x00030000 : 0x00020000, &p_manifest.write[iofs + 16]); } - if (tname == "meta-data" && attrname == "name" && string_table[attr_value] == "xr_mode_metadata_name") { + // FIXME: `attr_value != 0xFFFFFFFF` below added as a stopgap measure for GH-32553, + // but the issue should be debugged further and properly addressed. + if (tname == "meta-data" && attrname == "name" && value == "xr_mode_metadata_name") { // Update the meta-data 'android:name' attribute based on the selected XR mode. if (xr_mode_index == 1 /* XRMode.OVR */) { string_table.write[attr_value] = "com.samsung.android.vr.application.mode"; } } - if (tname == "meta-data" && attrname == "value" && string_table[attr_value] == "xr_mode_metadata_value") { + if (tname == "meta-data" && attrname == "value" && value == "xr_mode_metadata_value") { // Update the meta-data 'android:value' attribute based on the selected XR mode. if (xr_mode_index == 1 /* XRMode.OVR */) { string_table.write[attr_value] = "vr_only"; diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index 067fa6f4b9..67dce172dc 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -95,6 +95,11 @@ public class GodotLib { public static native void touch(int what, int pointer, int howmany, int[] arr); /** + * Forward hover events from the main thread to the GL thread. + */ + public static native void hover(int type, int x, int y); + + /** * Forward accelerometer sensor events from the main thread to the GL thread. * @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent) */ diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 2beca67922..2756ca6c83 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -188,7 +188,18 @@ public class GodotInputHandler implements InputDeviceListener { } return true; } - }; + } else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) { + final int x = Math.round(event.getX()); + final int y = Math.round(event.getY()); + final int type = event.getAction(); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.hover(type, x, y); + } + }); + return true; + } return false; } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 30676783db..7daea19961 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -826,6 +826,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo */ } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y) { + if (step == 0) + return; + + os_android->process_hover(p_type, Point2(p_x, p_y)); +} + /* * Android Key codes. */ diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 11bda94f8d..a564bbd4a1 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -45,6 +45,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jobject obj, jint p_type, jint p_x, jint p_y); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 49ab0ea84a..91bd6cbdd2 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -477,6 +477,23 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> } } +void OS_Android::process_hover(int p_type, Point2 p_pos) { + // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER + switch (p_type) { + case 7: // hover move + case 9: // hover enter + case 10: { // hover exit + Ref<InputEventMouseMotion> ev; + ev.instance(); + ev->set_position(p_pos); + ev->set_global_position(p_pos); + ev->set_relative(p_pos - hover_prev_pos); + input->parse_input_event(ev); + hover_prev_pos = p_pos; + } break; + } +} + void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) { input->set_accelerometer(p_accelerometer); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index a17941f7c0..9bad9b2e01 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -70,6 +70,7 @@ public: private: Vector<TouchPos> touch; + Point2 hover_prev_pos; // needed to calculate the relative position on hover events bool use_gl2; bool use_apk_expansion; @@ -186,6 +187,7 @@ public: void process_magnetometer(const Vector3 &p_magnetometer); void process_gyroscope(const Vector3 &p_gyroscope); void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); + void process_hover(int p_type, Point2 p_pos); void process_joy_event(JoypadEvent p_event); void process_event(Ref<InputEvent> p_event); void init_video_mode(int p_video_width, int p_video_height); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 69dd038709..4de98f7039 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "core/io/zip_io.h" +#include "editor/editor_export.h" #include "editor/editor_node.h" -#include "editor_export.h" #include "main/splash.gen.h" #include "platform/javascript/logo.gen.h" #include "platform/javascript/run_icon.gen.h" diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index c8ecbd5a2d..9226aea369 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -132,10 +132,12 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); #ifdef OSX_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray())); #endif r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); @@ -375,16 +377,27 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese args.push_back("--entitlements"); args.push_back(p_preset->get("codesign/entitlements")); } + + PoolStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.empty()) { + args.push_back(user_arg); + } + } + args.push_back("-s"); args.push_back(p_preset->get("codesign/identity")); + args.push_back("-v"); /* provide some more feedback */ + args.push_back(p_path); String str; Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true); ERR_FAIL_COND_V(err != OK, err); - print_line("codesign: " + str); + print_line("codesign (" + p_path + "): " + str); if (str.find("no identity found") != -1) { EditorNode::add_io_error("codesign: no identity found"); return FAILED; @@ -663,20 +676,20 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p err = save_pack(p_preset, pack_path, &shared_objects); // see if we can code sign our new package - String identity = p_preset->get("codesign/identity"); + bool sign_enabled = p_preset->get("codesign/enable"); if (err == OK) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); - if (err == OK && identity != "") { + if (err == OK && sign_enabled) { err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); } } memdelete(da); } - if (err == OK && identity != "") { + if (err == OK && sign_enabled) { if (ep.step("Code signing bundle", 2)) { return ERR_SKIP; } diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 827daa2d58..3abb05b494 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -31,6 +31,7 @@ #include "core/os/file_access.h" #include "core/os/os.h" #include "editor/editor_export.h" +#include "editor/editor_node.h" #include "editor/editor_settings.h" #include "platform/windows/logo.gen.h" @@ -38,11 +39,22 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, class EditorExportPlatformWindows : public EditorExportPlatformPC { + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); virtual void get_export_options(List<ExportOption> *r_options); }; +Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { + if (p_preset->get("codesign/enable")) { + return _code_sign(p_preset, p_path); + } else { + return OK; + } +} + Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); @@ -133,12 +145,28 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> OS::get_singleton()->execute(wine_path, args, true); #endif - return OK; + if (p_preset->get("codesign/enable") && err == OK) { + err = _code_sign(p_preset, p_path); + } + + return err; } void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { EditorExportPlatformPC::get_export_options(r_options); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); +#ifdef WINDOWS_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); +#endif + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); @@ -149,11 +177,164 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); } +Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + List<String> args; + +#ifdef WINDOWS_ENABLED + String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINTS("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "signtool"; // try to run signtool from PATH + } +#else + String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINTS("Could not find osslsigncode executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "osslsigncode"; // try to run signtool from PATH + } +#endif + + args.push_back("sign"); + + //identity +#ifdef WINDOWS_ENABLED + int id_type = p_preset->get("codesign/identity_type"); + if (id_type == 0) { //auto select + args.push_back("/a"); + } else if (id_type == 1) { //pkcs12 + if (p_preset->get("codesign/identity") != "") { + args.push_back("/f"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else if (id_type == 2) { //Windows certificate store + if (p_preset->get("codesign/identity") != "") { + args.push_back("/sha1"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else { + EditorNode::add_io_error("codesign: invalid identity type"); + return FAILED; + } +#else + if (p_preset->get("codesign/identity") != "") { + args.push_back("-pkcs12"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } +#endif + + //password + if (p_preset->get("codesign/password") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/p"); +#else + args.push_back("-pass"); +#endif + args.push_back(p_preset->get("codesign/password")); + } + + //timestamp + if (p_preset->get("codesign/timestamp")) { + if (p_preset->get("codesign/timestamp_server") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/tr"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); + args.push_back("/td"); + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } +#else + args.push_back("-ts"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); +#endif + } else { + EditorNode::add_io_error("codesign: invalid timestamp server"); + return FAILED; + } + } + + //digest +#ifdef WINDOWS_ENABLED + args.push_back("/fd"); +#else + args.push_back("-h"); +#endif + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } + + //description + if (p_preset->get("codesign/description") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/d"); +#else + args.push_back("-n"); +#endif + args.push_back(p_preset->get("codesign/description")); + } + + //user options + PoolStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.empty()) { + args.push_back(user_arg); + } + } + +#ifndef WINDOWS_ENABLED + args.push_back("-in"); +#endif + args.push_back(p_path); +#ifndef WINDOWS_ENABLED + args.push_back("-out"); + args.push_back(p_path); +#endif + + String str; + Error err = OS::get_singleton()->execute(signtool_path, args, true, NULL, &str, NULL, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign (" + p_path + "): " + str); +#ifndef WINDOWS_ENABLED + if (str.find("SignTool Error") != -1) { +#else + if (str.find("Failed") != -1) { +#endif + return FAILED; + } + + return OK; +} + void register_windows_exporter() { EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); -#ifndef WINDOWS_ENABLED +#ifdef WINDOWS_ENABLED + EDITOR_DEF("export/windows/signtool", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); +#else + EDITOR_DEF("export/windows/osslsigncode", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE)); // On non-Windows we need WINE to run rcedit EDITOR_DEF("export/windows/wine", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE)); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 39160ee720..694aea7462 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -1767,7 +1767,8 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { XKeyEvent xkeyevent_no_mod = *xkeyevent; xkeyevent_no_mod.state &= ~ShiftMask; xkeyevent_no_mod.state &= ~ControlMask; - XLookupString(&xkeyevent_no_mod, str, 256, &keysym_keycode, NULL); + XLookupString(xkeyevent, str, 256, &keysym_unicode, NULL); + XLookupString(&xkeyevent_no_mod, NULL, 0, &keysym_keycode, NULL); // Meanwhile, XLookupString returns keysyms useful for unicode. @@ -1777,8 +1778,6 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { xmblen = 8; } - keysym_unicode = keysym_keycode; - if (xkeyevent->type == KeyPress && xic) { Status status; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c9ba51bafb..173214dfe4 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -955,6 +955,7 @@ void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end void TileMap::update_cell_bitmask(int p_x, int p_y) { + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot update cell bitmask if Tileset is not open."); PosKey p(p_x, p_y); Map<PosKey, Cell>::Element *E = tile_map.find(p); if (E != NULL) { diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index ffe011e5f7..96b62b97f9 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -35,8 +35,8 @@ #include "core/os/os.h" #ifdef TOOLS_ENABLED -#include "editor_scale.h" -#include "editor_settings.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" #endif #include "scene/main/viewport.h" diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index d304a37f82..154e67b6f3 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -44,7 +44,7 @@ void GridContainer::_notification(int p_what) { int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); int max_col = MIN(get_child_count(), columns); - int max_row = get_child_count() / columns; + int max_row = ceil((float)get_child_count() / (float)columns); // Compute the per-column/per-row data. int valid_controls_index = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 4d06bee0d4..1a0539effa 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -926,7 +926,7 @@ void ItemList::_notification(int p_what) { current_columns = max_columns; while (true) { - //repeat util all fits + //repeat until all fits bool all_fit = true; Vector2 ofs; int col = 0; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index a7c6c5ccab..08faaf7d45 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -79,7 +79,7 @@ Size2 PopupMenu::get_minimum_size() const { if (items[i].checkable_type) has_check = true; - String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; + String text = items[i].xl_text; size.width += font->get_string_size(text).width; if (i > 0) size.height += vseparation; @@ -519,7 +519,7 @@ void PopupMenu::_notification(int p_what) { hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation))); } - String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; + String text = items[i].xl_text; item_ofs.x += items[i].h_ofs; if (items[i].separator) { @@ -627,63 +627,50 @@ void PopupMenu::_notification(int p_what) { } } -void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { +/* Methods to add items with or without icon, checkbox, shortcut. + * Be sure to keep them in sync when adding new properties in the Item struct. + */ - Item item; - item.icon = p_icon; - item.text = p_label; - item.xl_text = tr(p_label); +#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \ + item.text = p_label; \ + item.xl_text = tr(p_label); \ + item.id = p_id == -1 ? items.size() : p_id; \ item.accel = p_accel; - item.id = p_id; - items.push_back(item); - update(); - minimum_size_changed(); -} + void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) { Item item; - item.text = p_label; - item.xl_text = tr(p_label); - item.accel = p_accel; - item.id = p_id == -1 ? items.size() : p_id; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { +void PopupMenu::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { Item item; - item.text = p_label; - item.xl_text = tr(p_label); - item.id = p_id; - item.submenu = p_submenu; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); + item.icon = p_icon; items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { +void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) { Item item; - item.icon = p_icon; - item.text = p_label; - item.xl_text = tr(p_label); - item.accel = p_accel; - item.id = p_id; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) { +void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { Item item; - item.text = p_label; - item.xl_text = tr(p_label); - item.accel = p_accel; - item.id = p_id == -1 ? items.size() : p_id; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); + item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); @@ -692,63 +679,59 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel void PopupMenu::add_radio_check_item(const String &p_label, int p_id, uint32_t p_accel) { - add_check_item(p_label, p_id, p_accel); - items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + Item item; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); + item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.push_back(item); update(); minimum_size_changed(); } void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id, uint32_t p_accel) { - add_icon_check_item(p_icon, p_label, p_id, p_accel); - items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + Item item; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); + item.icon = p_icon; + item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - - ERR_FAIL_COND(p_shortcut.is_null()); - - _ref_shortcut(p_shortcut); +void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) { Item item; - item.id = p_id; - item.icon = p_icon; - item.shortcut = p_shortcut; - item.shortcut_is_global = p_global; + ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); + item.max_states = p_max_states; + item.state = p_default_state; items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - - ERR_FAIL_COND(p_shortcut.is_null()); +#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \ + ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid ShortCut."); \ + _ref_shortcut(p_shortcut); \ + item.text = p_shortcut->get_name(); \ + item.xl_text = tr(item.text); \ + item.id = p_id == -1 ? items.size() : p_id; \ + item.shortcut = p_shortcut; \ + item.shortcut_is_global = p_global; - _ref_shortcut(p_shortcut); +void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { Item item; - item.id = p_id; - item.shortcut = p_shortcut; - item.shortcut_is_global = p_global; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - - ERR_FAIL_COND(p_shortcut.is_null()); - - _ref_shortcut(p_shortcut); +void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { Item item; - item.id = p_id; - item.shortcut = p_shortcut; - item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); item.icon = p_icon; - item.shortcut_is_global = p_global; items.push_back(item); update(); minimum_size_changed(); @@ -756,14 +739,19 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - ERR_FAIL_COND(p_shortcut.is_null()); + Item item; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; + items.push_back(item); + update(); + minimum_size_changed(); +} - _ref_shortcut(p_shortcut); +void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { Item item; - item.id = p_id; - item.shortcut = p_shortcut; - item.shortcut_is_global = p_global; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + item.icon = p_icon; item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); update(); @@ -772,26 +760,42 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bo void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { - add_check_shortcut(p_shortcut, p_id, p_global); - items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + Item item; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.push_back(item); update(); minimum_size_changed(); } -void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id, uint32_t p_accel) { +void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) { + + Item item; + ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global); + item.icon = p_icon; + item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.push_back(item); + update(); + minimum_size_changed(); +} + +void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { Item item; item.text = p_label; item.xl_text = tr(p_label); - item.accel = p_accel; - item.id = p_id; - item.max_states = p_max_states; - item.state = p_default_state; + item.id = p_id == -1 ? items.size() : p_id; + item.submenu = p_submenu; items.push_back(item); update(); minimum_size_changed(); } +#undef ITEM_SETUP_WITH_ACCEL +#undef ITEM_SETUP_WITH_SHORTCUT + +/* Methods to modify existing items. */ + void PopupMenu::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); @@ -1380,18 +1384,24 @@ void PopupMenu::clear_autohide_areas() { void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &PopupMenu::_gui_input); - ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_radio_check_item, DEFVAL(-1), DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("add_multistate_item", "label", "max_states", "default_state", "id", "accel"), &PopupMenu::add_multistate_item, DEFVAL(0), DEFVAL(-1), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_icon_radio_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_radio_check_shortcut, DEFVAL(-1), DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 8bfe8fc607..8c33178b09 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -120,21 +120,23 @@ protected: static void _bind_methods(); public: - void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); - void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); + void add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); + void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_radio_check_item(const String &p_label, int p_id = -1, uint32_t p_accel = 0); void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_id = -1, uint32_t p_accel = 0); - void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1); - void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); + void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, uint32_t p_accel = 0); + void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); - void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); + void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); + void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); + void add_icon_radio_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false); - void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_id = -1, uint32_t p_accel = 0); + void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1); void set_item_text(int p_idx, const String &p_text); void set_item_icon(int p_idx, const Ref<Texture> &p_icon); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e2bb4e3e91..a22ddb265b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1271,7 +1271,8 @@ void TextEdit::_notification(int p_what) { } // Loop through characters in one line. - for (int j = 0; j < str.length(); j++) { + int j = 0; + for (; j < str.length(); j++) { if (syntax_coloring) { if (color_map.has(last_wrap_column + j)) { @@ -1501,7 +1502,7 @@ void TextEdit::_notification(int p_what) { } } - if (cursor.column == last_wrap_column + str.length() && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) { + if (cursor.column == (last_wrap_column + j) && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) { cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y); cursor_pos.y += (get_row_height() - cache.font->get_height()) / 2; @@ -4070,6 +4071,7 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) { int new_column, new_line; _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column); + _update_scrollbars(); cursor_set_line(new_line); cursor_set_column(new_column); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 39c5759871..52ef225364 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1465,18 +1465,19 @@ void Viewport::_gui_show_tooltip() { rp->add_child(gui.tooltip_popup); gui.tooltip_popup->force_parent_owned(); gui.tooltip_popup->set_as_toplevel(true); - //gui.tooltip_popup->hide(); + if (gui.tooltip) // Avoids crash when rapidly switching controls. + gui.tooltip_popup->set_scale(gui.tooltip->get_global_transform().get_scale()); Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset"); Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_minimum_size()); Rect2 vr = gui.tooltip_popup->get_viewport_rect(); - if (r.size.x + r.position.x > vr.size.x) - r.position.x = vr.size.x - r.size.x; + if (r.size.x * gui.tooltip_popup->get_scale().x + r.position.x > vr.size.x) + r.position.x = vr.size.x - r.size.x * gui.tooltip_popup->get_scale().x; else if (r.position.x < 0) r.position.x = 0; - if (r.size.y + r.position.y > vr.size.y) - r.position.y = vr.size.y - r.size.y; + if (r.size.y * gui.tooltip_popup->get_scale().y + r.position.y > vr.size.y) + r.position.y = vr.size.y - r.size.y * gui.tooltip_popup->get_scale().y; else if (r.position.y < 0) r.position.y = 0; diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index e4a64a1de1..b5354bc3e2 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -197,16 +197,14 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) | 4 | 8 | <- current pixel (curx,cury) +---+---+ */ - //NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel - Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2)); Point2i tl = Point2i(curx - 1, cury - 1); - sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0; + sv += (rect.has_point(tl) && get_bit(tl)) ? 1 : 0; Point2i tr = Point2i(curx, cury - 1); - sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0; + sv += (rect.has_point(tr) && get_bit(tr)) ? 2 : 0; Point2i bl = Point2i(curx - 1, cury); - sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0; + sv += (rect.has_point(bl) && get_bit(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); - sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0; + sv += (rect.has_point(br) && get_bit(br)) ? 8 : 0; ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); } @@ -300,15 +298,15 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) +---+---+ | 4 | | +---+---+ - this normally go RIGHT, but if its coming from UP, it should go LEFT + this normally go RIGHT, but if its coming from RIGHT, it should go LEFT */ if (case6s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case6s; + //found, so we go left, and delete from case6s; stepx = -1; stepy = 0; case6s.erase(Point2i(curx, cury)); } else { - //not found, we go up, and add to case6s; + //not found, we go right, and add to case6s; stepx = 1; stepy = 0; case6s.insert(Point2i(curx, cury)); @@ -510,12 +508,19 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl for (int j = r.position.x; j < r.position.x + r.size.width; j++) { if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { + fill_bits(this, fill, Point2i(j, i), r); + Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); polygon = reduce(polygon, r, p_epsilon); print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + + if (polygon.size() < 3) { + print_verbose("Invalid polygon, skipped"); + continue; + } + polygons.push_back(polygon); - fill_bits(this, fill, Point2i(j, i), r); } } } @@ -525,6 +530,13 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { + if (p_pixels == 0) { + return; + } + + bool bit_value = (p_pixels > 0) ? true : false; + p_pixels = Math::abs(p_pixels); + Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); Ref<BitMap> copy; @@ -534,7 +546,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { for (int i = r.position.y; i < r.position.y + r.size.height; i++) { for (int j = r.position.x; j < r.position.x + r.size.width; j++) { - if (copy->get_bit(Point2(j, i))) + if (bit_value == get_bit(Point2(j, i))) continue; bool found = false; @@ -542,16 +554,21 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { for (int y = i - p_pixels; y <= i + p_pixels; y++) { for (int x = j - p_pixels; x <= j + p_pixels; x++) { - if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x) - continue; - if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y) - continue; + bool outside = false; + + if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) { + // outside of rectangle counts as bit not set + if (!bit_value) + outside = true; + else + continue; + } float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON; if (d > p_pixels) continue; - if (copy->get_bit(Point2(x, y))) { + if (outside || (bit_value == copy->get_bit(Point2(x, y)))) { found = true; break; } @@ -561,12 +578,17 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { } if (found) { - set_bit(Point2(j, i), true); + set_bit(Point2(j, i), bit_value); } } } } +void BitMap::shrink_mask(int p_pixels, const Rect2 &p_rect) { + + grow_mask(-p_pixels, p_rect); +} + Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index daf24affb1..b062dd7376 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -67,6 +67,7 @@ public: void resize(const Size2 &p_new_size); void grow_mask(int p_pixels, const Rect2 &p_rect); + void shrink_mask(int p_pixels, const Rect2 &p_rect); void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap); Ref<Image> convert_to_image() const; diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp index d819e9f776..c6fe14e55e 100644 --- a/scene/resources/box_shape.cpp +++ b/scene/resources/box_shape.cpp @@ -31,7 +31,7 @@ #include "box_shape.h" #include "servers/physics_server.h" -Vector<Vector3> BoxShape::_gen_debug_mesh_lines() { +Vector<Vector3> BoxShape::get_debug_mesh_lines() { Vector<Vector3> lines; AABB aabb; diff --git a/scene/resources/box_shape.h b/scene/resources/box_shape.h index 42d54310a8..0bce929aee 100644 --- a/scene/resources/box_shape.h +++ b/scene/resources/box_shape.h @@ -42,12 +42,13 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); public: void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + BoxShape(); }; diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp index 669b261bfe..1ec3bd7158 100644 --- a/scene/resources/capsule_shape.cpp +++ b/scene/resources/capsule_shape.cpp @@ -31,7 +31,7 @@ #include "capsule_shape.h" #include "servers/physics_server.h" -Vector<Vector3> CapsuleShape::_gen_debug_mesh_lines() { +Vector<Vector3> CapsuleShape::get_debug_mesh_lines() { float radius = get_radius(); float height = get_height(); diff --git a/scene/resources/capsule_shape.h b/scene/resources/capsule_shape.h index 6bca53f783..befbc1dcd5 100644 --- a/scene/resources/capsule_shape.h +++ b/scene/resources/capsule_shape.h @@ -44,14 +44,14 @@ protected: virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); - public: void set_radius(float p_radius); float get_radius() const; void set_height(float p_height); float get_height() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + CapsuleShape(); }; diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp index b4cc38c8c0..dbc07ef591 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -32,7 +32,7 @@ #include "servers/physics_server.h" -Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() { +Vector<Vector3> ConcavePolygonShape::get_debug_mesh_lines() { Set<DrawEdge> edges; diff --git a/scene/resources/concave_polygon_shape.h b/scene/resources/concave_polygon_shape.h index 1b8ddfc308..57362a29be 100644 --- a/scene/resources/concave_polygon_shape.h +++ b/scene/resources/concave_polygon_shape.h @@ -61,12 +61,13 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); public: void set_faces(const PoolVector<Vector3> &p_faces); PoolVector<Vector3> get_faces() const; + Vector<Vector3> get_debug_mesh_lines(); + ConcavePolygonShape(); }; diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index 499688a185..af459ec311 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -32,7 +32,7 @@ #include "core/math/quick_hull.h" #include "servers/physics_server.h" -Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { +Vector<Vector3> ConvexPolygonShape::get_debug_mesh_lines() { PoolVector<Vector3> points = get_points(); diff --git a/scene/resources/convex_polygon_shape.h b/scene/resources/convex_polygon_shape.h index 5c192476d3..e6daf1bef4 100644 --- a/scene/resources/convex_polygon_shape.h +++ b/scene/resources/convex_polygon_shape.h @@ -43,12 +43,12 @@ protected: virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); - public: void set_points(const PoolVector<Vector3> &p_points); PoolVector<Vector3> get_points() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + ConvexPolygonShape(); }; diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp index f60f7ab376..c1a0a0ac5d 100644 --- a/scene/resources/cylinder_shape.cpp +++ b/scene/resources/cylinder_shape.cpp @@ -31,7 +31,7 @@ #include "cylinder_shape.h" #include "servers/physics_server.h" -Vector<Vector3> CylinderShape::_gen_debug_mesh_lines() { +Vector<Vector3> CylinderShape::get_debug_mesh_lines() { float radius = get_radius(); float height = get_height(); diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h index 58a4f5a817..411c1515ed 100644 --- a/scene/resources/cylinder_shape.h +++ b/scene/resources/cylinder_shape.h @@ -43,14 +43,14 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); - public: void set_radius(float p_radius); float get_radius() const; void set_height(float p_height); float get_height() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + CylinderShape(); }; diff --git a/scene/resources/height_map_shape.cpp b/scene/resources/height_map_shape.cpp index f763700d52..214706626d 100644 --- a/scene/resources/height_map_shape.cpp +++ b/scene/resources/height_map_shape.cpp @@ -31,7 +31,7 @@ #include "height_map_shape.h" #include "servers/physics_server.h" -Vector<Vector3> HeightMapShape::_gen_debug_mesh_lines() { +Vector<Vector3> HeightMapShape::get_debug_mesh_lines() { Vector<Vector3> points; if ((map_width != 0) && (map_depth != 0)) { diff --git a/scene/resources/height_map_shape.h b/scene/resources/height_map_shape.h index b062f4e893..4cf2a76213 100644 --- a/scene/resources/height_map_shape.h +++ b/scene/resources/height_map_shape.h @@ -46,8 +46,6 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); - public: void set_map_width(int p_new); int get_map_width() const; @@ -56,6 +54,8 @@ public: void set_map_data(PoolRealArray p_new); PoolRealArray get_map_data() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + HeightMapShape(); }; diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp index 08f6ccd764..d3274ec5f8 100644 --- a/scene/resources/plane_shape.cpp +++ b/scene/resources/plane_shape.cpp @@ -32,7 +32,7 @@ #include "servers/physics_server.h" -Vector<Vector3> PlaneShape::_gen_debug_mesh_lines() { +Vector<Vector3> PlaneShape::get_debug_mesh_lines() { Plane p = get_plane(); Vector<Vector3> points; diff --git a/scene/resources/plane_shape.h b/scene/resources/plane_shape.h index 87c367cfde..f853d1966b 100644 --- a/scene/resources/plane_shape.h +++ b/scene/resources/plane_shape.h @@ -42,12 +42,12 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); - public: void set_plane(Plane p_plane); Plane get_plane() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + PlaneShape(); }; #endif // PLANE_SHAPE_H diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp index 0acfffdc06..5a696aee23 100644 --- a/scene/resources/ray_shape.cpp +++ b/scene/resources/ray_shape.cpp @@ -32,7 +32,7 @@ #include "servers/physics_server.h" -Vector<Vector3> RayShape::_gen_debug_mesh_lines() { +Vector<Vector3> RayShape::get_debug_mesh_lines() { Vector<Vector3> points; points.push_back(Vector3()); diff --git a/scene/resources/ray_shape.h b/scene/resources/ray_shape.h index 89fc34051c..fee7475c69 100644 --- a/scene/resources/ray_shape.h +++ b/scene/resources/ray_shape.h @@ -41,7 +41,6 @@ class RayShape : public Shape { protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); public: void set_length(float p_length); @@ -50,6 +49,8 @@ public: void set_slips_on_slope(bool p_active); bool get_slips_on_slope() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + RayShape(); }; #endif // RAY_SHAPE_H diff --git a/scene/resources/shape.cpp b/scene/resources/shape.cpp index 6ba46f066c..3500fdb4bc 100644 --- a/scene/resources/shape.cpp +++ b/scene/resources/shape.cpp @@ -37,7 +37,7 @@ void Shape::add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p_xform) { - Vector<Vector3> toadd = _gen_debug_mesh_lines(); + Vector<Vector3> toadd = get_debug_mesh_lines(); if (toadd.size()) { @@ -64,7 +64,7 @@ Ref<ArrayMesh> Shape::get_debug_mesh() { if (debug_mesh_cache.is_valid()) return debug_mesh_cache; - Vector<Vector3> lines = _gen_debug_mesh_lines(); + Vector<Vector3> lines = get_debug_mesh_lines(); debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh)); diff --git a/scene/resources/shape.h b/scene/resources/shape.h index ba763eaab1..2743fd0c65 100644 --- a/scene/resources/shape.h +++ b/scene/resources/shape.h @@ -50,13 +50,13 @@ protected: _FORCE_INLINE_ RID get_shape() const { return shape; } Shape(RID p_shape); - virtual Vector<Vector3> _gen_debug_mesh_lines() = 0; // { return Vector<Vector3>(); } virtual void _update_shape(); public: virtual RID get_rid() const { return shape; } Ref<ArrayMesh> get_debug_mesh(); + virtual Vector<Vector3> get_debug_mesh_lines() = 0; // { return Vector<Vector3>(); } void add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p_xform); diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp index af89413ced..019bc9189a 100644 --- a/scene/resources/sphere_shape.cpp +++ b/scene/resources/sphere_shape.cpp @@ -31,7 +31,7 @@ #include "sphere_shape.h" #include "servers/physics_server.h" -Vector<Vector3> SphereShape::_gen_debug_mesh_lines() { +Vector<Vector3> SphereShape::get_debug_mesh_lines() { float r = get_radius(); diff --git a/scene/resources/sphere_shape.h b/scene/resources/sphere_shape.h index 682928e885..679882fe23 100644 --- a/scene/resources/sphere_shape.h +++ b/scene/resources/sphere_shape.h @@ -42,12 +42,13 @@ protected: static void _bind_methods(); virtual void _update_shape(); - virtual Vector<Vector3> _gen_debug_mesh_lines(); public: void set_radius(float p_radius); float get_radius() const; + virtual Vector<Vector3> get_debug_mesh_lines(); + SphereShape(); }; diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 4453032f67..9ee7c2936e 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -40,16 +40,22 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { void StyleBox::set_default_margin(Margin p_margin, float p_value) { + ERR_FAIL_INDEX((int)p_margin, 4); + margin[p_margin] = p_value; emit_changed(); } float StyleBox::get_default_margin(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); + return margin[p_margin]; } float StyleBox::get_margin(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); + if (margin[p_margin] < 0) return get_style_margin(p_margin); else @@ -157,11 +163,15 @@ void StyleBoxTexture::set_margin_size(Margin p_margin, float p_size) { } float StyleBoxTexture::get_margin_size(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); + return margin[p_margin]; } float StyleBoxTexture::get_style_margin(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); + return margin[p_margin]; } @@ -250,6 +260,7 @@ Rect2 StyleBoxTexture::get_region_rect() const { void StyleBoxTexture::set_h_axis_stretch_mode(AxisStretchMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 3); axis_h = p_mode; emit_changed(); } @@ -261,6 +272,7 @@ StyleBoxTexture::AxisStretchMode StyleBoxTexture::get_h_axis_stretch_mode() cons void StyleBoxTexture::set_v_axis_stretch_mode(AxisStretchMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 3); axis_v = p_mode; emit_changed(); } @@ -391,11 +403,13 @@ int StyleBoxFlat::get_border_width_min() const { } void StyleBoxFlat::set_border_width(Margin p_margin, int p_width) { + ERR_FAIL_INDEX((int)p_margin, 4); border_width[p_margin] = p_width; emit_changed(); } int StyleBoxFlat::get_border_width(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0); return border_width[p_margin]; } @@ -437,15 +451,19 @@ int StyleBoxFlat::get_corner_radius_min() const { void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { + ERR_FAIL_INDEX((int)p_corner, 4); corner_radius[p_corner] = radius; emit_changed(); } int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { + + ERR_FAIL_INDEX_V((int)p_corner, 4, 0); return corner_radius[p_corner]; } void StyleBoxFlat::set_expand_margin_size(Margin p_expand_margin, float p_size) { + ERR_FAIL_INDEX((int)p_expand_margin, 4); expand_margin[p_expand_margin] = p_size; emit_changed(); } @@ -468,6 +486,7 @@ void StyleBoxFlat::set_expand_margin_size_all(float p_expand_margin_size) { float StyleBoxFlat::get_expand_margin_size(Margin p_expand_margin) const { + ERR_FAIL_INDEX_V((int)p_expand_margin, 4, 0.0); return expand_margin[p_expand_margin]; } void StyleBoxFlat::set_draw_center(bool p_enabled) { @@ -814,6 +833,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } float StyleBoxFlat::get_style_margin(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, 0.0); return border_width[p_margin]; } void StyleBoxFlat::_bind_methods() { @@ -997,6 +1017,7 @@ void StyleBoxLine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } float StyleBoxLine::get_style_margin(Margin p_margin) const { + ERR_FAIL_INDEX_V((int)p_margin, 4, thickness); return thickness; } Size2 StyleBoxLine::get_center_size() const { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 58bbf86241..bd6835f816 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1349,7 +1349,6 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id); ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node); - ClassDB::bind_method(D_METHOD("rebuild"), &VisualShader::rebuild); ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 8b6b659836..4f73316404 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -353,6 +353,7 @@ public: class VisualShaderNodeUniform : public VisualShaderNode { GDCLASS(VisualShaderNodeUniform, VisualShaderNode); +private: String uniform_name; protected: diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 2e58c512b8..a7df736c78 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -386,7 +386,7 @@ String VisualShaderNodeTexture::get_input_port_name(int p_port) const { case 1: return "lod"; case 2: - return "sampler"; + return "sampler2D"; default: return ""; } @@ -453,7 +453,12 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String code; if (p_input_vars[0] == String()) { // Use UV by default. - code += "\tvec4 " + id + "_read = texture( " + id + " , UV.xy );\n"; + if (p_input_vars[1] == String()) { + code += "\tvec4 " + id + "_read = texture( " + id + " , UV.xy );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; + } + } else if (p_input_vars[1] == String()) { //no lod code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; @@ -468,13 +473,18 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: if (source == SOURCE_PORT) { String id = p_input_vars[2]; + String code; if (id == String()) { code += "\tvec4 " + id + "_tex_read = vec4(0.0);\n"; } else { if (p_input_vars[0] == String()) { // Use UV by default. - code += "\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n"; + if (p_input_vars[1] == String()) { + code += "\tvec4 " + id + "_tex_read = texture( " + id + " , UV.xy );\n"; + } else { + code += "\tvec4 " + id + "_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; + } } else if (p_input_vars[1] == String()) { //no lod @@ -494,11 +504,15 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String code = "\t{\n"; if (p_input_vars[0] == String() || p_for_preview) { // Use UV by default. - code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy, 0.0 );\n"; + if (p_input_vars[1] == String()) { + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy , 0.0 );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , UV.xy , " + p_input_vars[1] + ");\n"; + } } else if (p_input_vars[1] == String()) { //no lod - code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n"; + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , 0.0 );\n"; } else { code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; } @@ -514,7 +528,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String code = "\t{\n"; if (p_input_vars[0] == String()) { // Use UV by default. - code += "\t\tvec4 _tex_read = texture( TEXTURE , UV.xy );\n"; + if (p_input_vars[1] == String()) { + code += "\t\tvec4 _tex_read = texture( TEXTURE , UV.xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( TEXTURE , UV.xy , " + p_input_vars[1] + " );\n"; + } } else if (p_input_vars[1] == String()) { //no lod @@ -534,7 +552,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String code = "\t{\n"; if (p_input_vars[0] == String()) { // Use UV by default. - code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , UV.xy );\n"; + if (p_input_vars[1] == String()) { + code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , UV.xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , UV.xy , " + p_input_vars[1] + " );\n"; + } } else if (p_input_vars[1] == String()) { //no lod @@ -564,7 +586,11 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: String code = "\t{\n"; if (p_input_vars[0] == String()) { // Use UV by default. - code += "\t\tfloat _depth = texture( DEPTH_TEXTURE , UV.xy ).r;\n"; + if (p_input_vars[1] == String()) { + code += "\t\tfloat _depth = texture( DEPTH_TEXTURE , UV.xy ).r;\n"; + } else { + code += "\t\tfloat _depth = textureLod( DEPTH_TEXTURE , UV.xy , " + p_input_vars[1] + " ).r;\n"; + } } else if (p_input_vars[1] == String()) { //no lod @@ -704,15 +730,33 @@ String VisualShaderNodeCubeMap::get_caption() const { } int VisualShaderNodeCubeMap::get_input_port_count() const { - return 2; + return 3; } VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR; + case 1: + return PORT_TYPE_SCALAR; + case 2: + return PORT_TYPE_SAMPLER; + default: + return PORT_TYPE_SCALAR; + } } String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const { - return p_port == 0 ? "uv" : "lod"; + switch (p_port) { + case 0: + return "uv"; + case 1: + return "lod"; + case 2: + return "samplerCube"; + default: + return ""; + } } int VisualShaderNodeCubeMap::get_output_port_count() const { @@ -738,22 +782,44 @@ Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_t String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { - String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube"); - switch (texture_type) { - case TYPE_DATA: break; - case TYPE_COLOR: u += " : hint_albedo"; break; - case TYPE_NORMALMAP: u += " : hint_normal"; break; + if (source == SOURCE_TEXTURE) { + String u = "uniform samplerCube " + make_unique_id(p_type, p_id, "cube"); + switch (texture_type) { + case TYPE_DATA: break; + case TYPE_COLOR: u += " : hint_albedo"; break; + case TYPE_NORMALMAP: u += " : hint_normal"; break; + } + return u + ";"; } - return u + ";"; + return String(); } String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - String id = make_unique_id(p_type, p_id, "cube"); String code; - if (p_input_vars[0] == String()) { //none bound, do nothing + String id; + if (source == SOURCE_TEXTURE) { + id = make_unique_id(p_type, p_id, "cube"); + } else if (source == SOURCE_PORT) { + id = p_input_vars[2]; + } else { + return String(); + } + if (id == String()) { code += "\tvec4 " + id + "_read = vec4(0.0);\n"; + code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; + } + + if (p_input_vars[0] == String()) { // Use UV by default. + + if (p_input_vars[1] == String()) { + code += "\tvec4 " + id + "_read = texture( " + id + " , vec3( UV, 0.0 ) );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , vec3( UV, 0.0 )" + " , " + p_input_vars[1] + " );\n"; + } } else if (p_input_vars[1] == String()) { //no lod @@ -761,12 +827,29 @@ String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader: } else { code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; } - code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; } +String VisualShaderNodeCubeMap::get_input_port_default_hint(int p_port) const { + if (p_port == 0) { + return "vec3(UV, 0.0)"; + } + return ""; +} + +void VisualShaderNodeCubeMap::set_source(Source p_source) { + source = p_source; + emit_changed(); + emit_signal("editor_refresh_request"); +} + +VisualShaderNodeCubeMap::Source VisualShaderNodeCubeMap::get_source() const { + return source; +} + void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) { cube_map = p_value; @@ -789,22 +872,32 @@ VisualShaderNodeCubeMap::TextureType VisualShaderNodeCubeMap::get_texture_type() Vector<StringName> VisualShaderNodeCubeMap::get_editable_properties() const { Vector<StringName> props; - props.push_back("cube_map"); - props.push_back("texture_type"); + props.push_back("source"); + if (source == SOURCE_TEXTURE) { + props.push_back("cube_map"); + props.push_back("texture_type"); + } return props; } void VisualShaderNodeCubeMap::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeCubeMap::set_source); + ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeCubeMap::get_source); + ClassDB::bind_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubeMap::set_cube_map); ClassDB::bind_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubeMap::get_cube_map); ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeCubeMap::set_texture_type); ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubeMap::get_texture_type); + ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,SamplerPort"), "set_source", "get_source"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "CubeMap"), "set_cube_map", "get_cube_map"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + BIND_ENUM_CONSTANT(SOURCE_TEXTURE); + BIND_ENUM_CONSTANT(SOURCE_PORT); + BIND_ENUM_CONSTANT(TYPE_DATA); BIND_ENUM_CONSTANT(TYPE_COLOR); BIND_ENUM_CONSTANT(TYPE_NORMALMAP); @@ -3101,7 +3194,7 @@ String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { case 1: return "alpha"; case 2: - return "sampler"; + return "sampler2D"; default: return ""; } @@ -3135,8 +3228,11 @@ String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, Visual String id = get_uniform_name(); String code = "\t{\n"; if (p_input_vars[0] == String()) { // Use UV by default. - - code += "\t\tvec4 n_tex_read = texture( " + id + " , UV.xy );\n"; + if (p_input_vars[1] == String()) { + code += "\t\tvec4 n_tex_read = texture( " + id + " , UV.xy );\n"; + } else { + code += "\t\tvec4 n_tex_read = textureLod( " + id + " , UV.xy , " + p_input_vars[1] + " );\n"; + } } else if (p_input_vars[1] == String()) { //no lod code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; @@ -3314,28 +3410,55 @@ String VisualShaderNodeCubeMapUniform::get_caption() const { return "CubeMapUniform"; } +int VisualShaderNodeCubeMapUniform::get_output_port_count() const { + return 1; +} + +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_SAMPLER; +} + +String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { + return "samplerCube"; +} + int VisualShaderNodeCubeMapUniform::get_input_port_count() const { - return 2; + return 0; } VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; + return PORT_TYPE_SCALAR; } String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { - return p_port == 0 ? "normal" : "lod"; + return ""; } -int VisualShaderNodeCubeMapUniform::get_output_port_count() const { - return 2; +String VisualShaderNodeCubeMapUniform::get_input_port_default_hint(int p_port) const { + return ""; } -VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; -} +String VisualShaderNodeCubeMapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = "uniform samplerCube " + get_uniform_name(); -String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { - return p_port == 0 ? "rgb" : "alpha"; + switch (texture_type) { + case TYPE_DATA: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black;\n"; + else + code += ";\n"; + break; + case TYPE_COLOR: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black_albedo;\n"; + else + code += " : hint_albedo;\n"; + break; + case TYPE_NORMALMAP: code += " : hint_normal;\n"; break; + case TYPE_ANISO: code += " : hint_aniso;\n"; break; + } + + return code; } String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index d5ee990191..339e59bda9 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -259,6 +259,11 @@ class VisualShaderNodeCubeMap : public VisualShaderNode { Ref<CubeMap> cube_map; public: + enum Source { + SOURCE_TEXTURE, + SOURCE_PORT + }; + enum TextureType { TYPE_DATA, TYPE_COLOR, @@ -266,6 +271,7 @@ public: }; private: + Source source; TextureType texture_type; protected: @@ -277,6 +283,7 @@ public: virtual int get_input_port_count() const; virtual PortType get_input_port_type(int p_port) const; virtual String get_input_port_name(int p_port) const; + virtual String get_input_port_default_hint(int p_port) const; virtual int get_output_port_count() const; virtual PortType get_output_port_type(int p_port) const; @@ -286,6 +293,9 @@ public: virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + void set_source(Source p_source); + Source get_source() const; + void set_cube_map(Ref<CubeMap> p_value); Ref<CubeMap> get_cube_map() const; @@ -298,6 +308,7 @@ public: }; VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::Source) /////////////////////////////////////// /// OPS @@ -1412,7 +1423,7 @@ public: COLOR_DEFAULT_BLACK }; -private: +protected: TextureType texture_type; ColorDefault color_default; @@ -1471,8 +1482,8 @@ public: /////////////////////////////////////// -class VisualShaderNodeCubeMapUniform : public VisualShaderNode { - GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode); +class VisualShaderNodeCubeMapUniform : public VisualShaderNodeTextureUniform { + GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNodeTextureUniform); public: virtual String get_caption() const; @@ -1485,6 +1496,8 @@ public: virtual PortType get_output_port_type(int p_port) const; virtual String get_output_port_name(int p_port) const; + virtual String get_input_port_default_hint(int p_port) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty VisualShaderNodeCubeMapUniform(); diff --git a/scene/resources/world.cpp b/scene/resources/world.cpp index 0ca5d7eb36..2c22f45f9d 100644 --- a/scene/resources/world.cpp +++ b/scene/resources/world.cpp @@ -332,7 +332,9 @@ World::World() { PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/3d/default_gravity", 9.8)); PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/3d/default_linear_damp", 0.1)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_linear_damp", PropertyInfo(Variant::REAL, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); PhysicsServer::get_singleton()->area_set_param(space, PhysicsServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/3d/default_angular_damp", 0.1)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); #ifdef _3D_DISABLED indexer = NULL; diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 13b45f58dc..b5743ad416 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -394,7 +394,9 @@ World2D::World2D() { Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_GRAVITY, GLOBAL_DEF("physics/2d/default_gravity", 98)); Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF("physics/2d/default_gravity_vector", Vector2(0, 1))); Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF("physics/2d/default_linear_damp", 0.1)); - Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_linear_damp", PropertyInfo(Variant::REAL, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); + Physics2DServer::get_singleton()->area_set_param(space, Physics2DServer::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF("physics/2d/default_angular_damp", 1.0)); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/default_angular_damp", PropertyInfo(Variant::REAL, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater")); indexer = memnew(SpatialIndexer2D); } diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 2cf6a67bef..1da0146084 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -1079,6 +1079,7 @@ void AudioServer::finish() { for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) { AudioDriverManager::get_driver(i)->finish(); + AudioDriverManager::get_driver(i)->clear_capture_buffer(); } for (int i = 0; i < buses.size(); i++) { diff --git a/servers/audio_server.h b/servers/audio_server.h index 72bb6faf42..da4b9daf5b 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -115,6 +115,8 @@ public: unsigned int get_capture_position() { return capture_position; } unsigned int get_capture_size() { return capture_size; } + void clear_capture_buffer() { capture_buffer.resize(0); } + #ifdef DEBUG_ENABLED uint64_t get_profiling_time() const { return prof_time; } void reset_profiling_time() { prof_time = 0; } diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index a3bb581cb5..7d575c57ae 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -794,7 +794,7 @@ BodySW::BodySW() : still_time = 0; continuous_cd = false; - can_sleep = false; + can_sleep = true; fi_callback = NULL; } diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index 6ba159ca0a..f9939e3843 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -694,7 +694,7 @@ Body2DSW::Body2DSW() : still_time = 0; continuous_cd_mode = Physics2DServer::CCD_MODE_DISABLED; - can_sleep = false; + can_sleep = true; fi_callback = NULL; } diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ae99d64eee..5b5ba56ebe 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -5419,7 +5419,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct if (block->parent_function) { if (comp_ident) { for (int i = 0; i < block->parent_function->arguments.size(); i++) { - matches.insert(block->parent_function->arguments[i].name, ScriptCodeCompletionOption::KIND_FUNCTION); + matches.insert(block->parent_function->arguments[i].name, ScriptCodeCompletionOption::KIND_VARIABLE); } } skip_function = block->parent_function->name; diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index f5a1276c27..ed06a67e4c 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -680,11 +680,22 @@ void VisualServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const R rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_H; rect->rect.size.x = -rect->rect.size.x; } + if (p_src_rect.size.x < 0) { + + rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_H; + rect->source.size.x = -rect->source.size.x; + } if (p_rect.size.y < 0) { rect->flags |= RasterizerCanvas::CANVAS_RECT_FLIP_V; rect->rect.size.y = -rect->rect.size.y; } + if (p_src_rect.size.y < 0) { + + rect->flags ^= RasterizerCanvas::CANVAS_RECT_FLIP_V; + rect->source.size.y = -rect->source.size.y; + } + if (p_transpose) { rect->flags |= RasterizerCanvas::CANVAS_RECT_TRANSPOSE; SWAP(rect->rect.size.x, rect->rect.size.y); |