summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/input/input_map.cpp7
-rw-r--r--core/io/resource_saver.cpp33
-rw-r--r--core/io/resource_saver.h2
-rw-r--r--core/variant/variant_call.cpp4
-rw-r--r--core/variant/variant_internal.h2
-rw-r--r--doc/classes/Basis.xml2
-rw-r--r--doc/classes/CanvasItem.xml10
-rw-r--r--doc/classes/MultiplayerPeer.xml20
-rw-r--r--doc/classes/NavigationAgent2D.xml2
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--doc/classes/PortableCompressedTexture2D.xml2
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/RenderingDevice.xml4
-rw-r--r--doc/classes/RenderingServer.xml6
-rw-r--r--doc/classes/ResourceFormatSaver.xml9
-rw-r--r--doc/classes/TextEdit.xml6
-rw-r--r--doc/classes/TextLine.xml1
-rw-r--r--doc/classes/Vector3.xml8
-rw-r--r--doc/classes/Vector4i.xml8
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp45
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp4
-rw-r--r--drivers/gles3/shaders/scene.glsl10
-rw-r--r--drivers/gles3/storage/config.cpp1
-rw-r--r--drivers/gles3/storage/config.h1
-rw-r--r--drivers/gles3/storage/material_storage.cpp33
-rw-r--r--drivers/gles3/storage/material_storage.h2
-rw-r--r--drivers/gles3/storage/mesh_storage.h11
-rw-r--r--drivers/gles3/storage/utilities.cpp9
-rw-r--r--drivers/gles3/storage/utilities.h2
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp4
-rw-r--r--editor/editor_properties_array_dict.cpp6
-rw-r--r--editor/import/collada.cpp2
-rw-r--r--editor/import/editor_import_collada.cpp6
-rw-r--r--editor/import/resource_importer_layered_texture.cpp2
-rw-r--r--editor/import/resource_importer_scene.h2
-rw-r--r--editor/plugins/font_config_plugin.cpp4
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp8
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp130
-rw-r--r--main/main.cpp10
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml3
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp225
-rw-r--r--modules/enet/enet_multiplayer_peer.h20
-rw-r--r--modules/gdscript/gdscript.cpp5
-rw-r--r--modules/gdscript/gdscript.h1
-rw-r--r--modules/multiplayer/doc_classes/SceneMultiplayer.xml4
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp6
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp163
-rw-r--r--modules/multiplayer/scene_multiplayer.h18
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp3
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp9
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp20
-rw-r--r--modules/webp/resource_saver_webp.cpp6
-rw-r--r--modules/webp/webp_common.cpp2
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml39
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp85
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h20
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp254
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h18
-rw-r--r--platform/android/display_server_android.cpp6
-rw-r--r--platform/android/display_server_android.h4
-rw-r--r--platform/ios/display_server_ios.h4
-rw-r--r--platform/ios/display_server_ios.mm6
-rw-r--r--platform/linuxbsd/display_server_x11.cpp13
-rw-r--r--platform/linuxbsd/display_server_x11.h4
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp1
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp4
-rw-r--r--platform/macos/display_server_macos.h4
-rw-r--r--platform/macos/display_server_macos.mm11
-rw-r--r--platform/web/display_server_web.cpp6
-rw-r--r--platform/web/display_server_web.h4
-rw-r--r--platform/windows/display_server_windows.cpp55
-rw-r--r--platform/windows/display_server_windows.h7
-rw-r--r--platform/windows/os_windows.cpp4
-rw-r--r--scene/2d/navigation_agent_2d.cpp4
-rw-r--r--scene/3d/navigation_agent_3d.cpp2
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp2
-rw-r--r--scene/gui/text_edit.cpp47
-rw-r--r--scene/gui/text_edit.h1
-rw-r--r--scene/main/canvas_item.cpp26
-rw-r--r--scene/main/canvas_item.h18
-rw-r--r--scene/main/multiplayer_peer.cpp16
-rw-r--r--scene/main/multiplayer_peer.h8
-rw-r--r--scene/main/scene_tree.cpp11
-rw-r--r--scene/main/viewport.cpp5
-rw-r--r--scene/main/window.cpp17
-rw-r--r--scene/resources/environment.cpp2
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--servers/display_server.cpp4
-rw-r--r--servers/display_server.h4
-rw-r--r--servers/display_server_headless.h2
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp113
-rw-r--r--servers/physics_3d/godot_shape_3d.h2
-rw-r--r--servers/rendering/dummy/storage/material_storage.h2
-rw-r--r--servers/rendering/dummy/storage/utilities.h2
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp49
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl22
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl10
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp34
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.h2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h10
-rw-r--r--servers/rendering/renderer_rd/storage_rd/utilities.cpp8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/utilities.h2
-rw-r--r--servers/rendering/renderer_scene_cull.cpp34
-rw-r--r--servers/rendering/rendering_device.cpp2
-rw-r--r--servers/rendering/rendering_device.h2
-rw-r--r--servers/rendering/rendering_server_default.cpp8
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering/shader_compiler.cpp8
-rw-r--r--servers/rendering/shader_language.cpp3
-rw-r--r--servers/rendering/storage/material_storage.h2
-rw-r--r--servers/rendering/storage/utilities.h2
-rw-r--r--servers/rendering_server.cpp3
-rw-r--r--servers/rendering_server.h5
-rw-r--r--tests/core/io/test_image.h4
-rw-r--r--tests/core/math/test_basis.h3
-rw-r--r--tests/core/math/test_quaternion.h8
-rw-r--r--tests/scene/test_text_edit.h40
-rw-r--r--tests/test_main.cpp2
124 files changed, 1288 insertions, 750 deletions
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index ce76d11b6e..0c765fe23c 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -337,6 +337,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_text_scroll_down.macos", TTRC("Scroll Down") },
{ "ui_text_select_all", TTRC("Select All") },
{ "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") },
+ { "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") },
{ "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") },
{ "ui_text_submit", TTRC("Text Submitted") },
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
@@ -641,10 +642,14 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
default_builtin_cache.insert("ui_text_select_all", inputs);
inputs = List<Ref<InputEvent>>();
- inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
+ inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT));
default_builtin_cache.insert("ui_text_select_word_under_caret", inputs);
inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs);
+
+ inputs = List<Ref<InputEvent>>();
inputs.push_back(InputEventKey::create_reference(Key::INSERT));
default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs);
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 2f863baaac..6cda840604 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -69,10 +69,31 @@ void ResourceFormatSaver::get_recognized_extensions(const Ref<Resource> &p_resou
}
}
+bool ResourceFormatSaver::recognize_path(const Ref<Resource> &p_resource, const String &p_path) const {
+ bool ret = false;
+ if (GDVIRTUAL_CALL(_recognize_path, p_resource, p_path, ret)) {
+ return ret;
+ }
+
+ String extension = p_path.get_extension();
+
+ List<String> extensions;
+ get_recognized_extensions(p_resource, &extensions);
+
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void ResourceFormatSaver::_bind_methods() {
GDVIRTUAL_BIND(_save, "resource", "path", "flags");
GDVIRTUAL_BIND(_recognize, "resource");
GDVIRTUAL_BIND(_get_recognized_extensions, "resource");
+ GDVIRTUAL_BIND(_recognize_path, "resource", "path");
}
Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
@@ -90,17 +111,7 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path,
continue;
}
- List<String> extensions;
- bool recognized = false;
- saver[i]->get_recognized_extensions(p_resource, &extensions);
-
- for (const String &E : extensions) {
- if (E.nocasecmp_to(extension) == 0) {
- recognized = true;
- }
- }
-
- if (!recognized) {
+ if (!saver[i]->recognize_path(p_resource, path)) {
continue;
}
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 4fee2bcfd1..5e48ce88c3 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -44,11 +44,13 @@ protected:
GDVIRTUAL3R(int64_t, _save, Ref<Resource>, String, uint32_t)
GDVIRTUAL1RC(bool, _recognize, Ref<Resource>)
GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, Ref<Resource>)
+ GDVIRTUAL2RC(bool, _recognize_path, Ref<Resource>, String)
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
virtual bool recognize(const Ref<Resource> &p_resource) const;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize_path(const Ref<Resource> &p_resource, const String &p_path) const;
virtual ~ResourceFormatSaver() {}
};
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ef4807ba71..688650f532 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1930,7 +1930,7 @@ static void _register_variant_builtin_methods() {
bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, real_t) const>(&Basis::rotated), sarray("axis", "angle"), varray());
bind_method(Basis, scaled, sarray("scale"), varray());
bind_method(Basis, get_scale, sarray(), varray());
- bind_method(Basis, get_euler, sarray("order"), varray(Basis::EULER_ORDER_YXZ));
+ bind_method(Basis, get_euler, sarray("order"), varray((int64_t)Basis::EULER_ORDER_YXZ));
bind_method(Basis, tdotx, sarray("with"), varray());
bind_method(Basis, tdoty, sarray("with"), varray());
bind_method(Basis, tdotz, sarray("with"), varray());
@@ -1940,7 +1940,7 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
bind_static_method(Basis, from_scale, sarray("scale"), varray());
- bind_static_method(Basis, from_euler, sarray("euler", "order"), varray(Basis::EULER_ORDER_YXZ));
+ bind_static_method(Basis, from_euler, sarray("euler", "order"), varray((int64_t)Basis::EULER_ORDER_YXZ));
/* AABB */
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 7ae2368fe4..636710c934 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -826,7 +826,7 @@ VARIANT_ACCESSOR_NUMBER(Projection::Planes)
template <>
struct VariantInternalAccessor<Basis::EulerOrder> {
static _FORCE_INLINE_ Basis::EulerOrder get(const Variant *v) { return Basis::EulerOrder(*VariantInternal::get_int(v)); }
- static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = p_value; }
+ static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = (int64_t)p_value; }
};
template <>
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 0f1f4b57a9..652d6d2407 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -70,7 +70,7 @@
<param index="0" name="euler" type="Vector3" />
<param index="1" name="order" type="int" default="2" />
<description>
- Creates a [Basis] from the given [Vector3] representing Euler angles. [param order] determines in what order rotation components are applied. Defaults to [constant EULER_ORDER_YXZ].
+ Constructs a pure rotation Basis matrix from Euler angles in the specified Euler rotation order. By default, use YXZ order (most common).
</description>
</method>
<method name="from_scale" qualifiers="static">
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 947e6a3d4c..b2d3e5cfdb 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -535,7 +535,7 @@
</method>
</methods>
<members>
- <member name="clip_children" type="bool" setter="set_clip_children" getter="is_clipping_children" default="false">
+ <member name="clip_children" type="int" setter="set_clip_children_mode" getter="get_clip_children_mode" enum="CanvasItem.ClipChildrenMode" default="0">
Allows the current node to clip children nodes, essentially acting as a mask.
</member>
<member name="light_mask" type="int" setter="set_light_mask" getter="get_light_mask" default="1">
@@ -653,5 +653,13 @@
<constant name="TEXTURE_REPEAT_MAX" value="4" enum="TextureRepeat">
Represents the size of the [enum TextureRepeat] enum.
</constant>
+ <constant name="CLIP_CHILDREN_DISABLED" value="0" enum="ClipChildrenMode">
+ </constant>
+ <constant name="CLIP_CHILDREN_ONLY" value="1" enum="ClipChildrenMode">
+ </constant>
+ <constant name="CLIP_CHILDREN_AND_DRAW" value="2" enum="ClipChildrenMode">
+ </constant>
+ <constant name="CLIP_CHILDREN_MAX" value="3" enum="ClipChildrenMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml
index 0f57ff9e55..ba6c60a088 100644
--- a/doc/classes/MultiplayerPeer.xml
+++ b/doc/classes/MultiplayerPeer.xml
@@ -25,10 +25,22 @@
Returns the current state of the connection. See [enum ConnectionStatus].
</description>
</method>
+ <method name="get_packet_channel" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the channel over which the next available packet was received. See [method PacketPeer.get_available_packet_count].
+ </description>
+ </method>
+ <method name="get_packet_mode" qualifiers="const">
+ <return type="int" enum="MultiplayerPeer.TransferMode" />
+ <description>
+ Returns the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count].
+ </description>
+ </method>
<method name="get_packet_peer" qualifiers="const">
<return type="int" />
<description>
- Returns the ID of the [MultiplayerPeer] who sent the most recent packet.
+ Returns the ID of the [MultiplayerPeer] who sent the next available packet. See [method PacketPeer.get_available_packet_count].
</description>
</method>
<method name="get_unique_id" qualifiers="const">
@@ -37,6 +49,12 @@
Returns the ID of this [MultiplayerPeer].
</description>
</method>
+ <method name="is_server_relay_supported" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns true if the server can act as a relay in the current configuration (i.e. if the higher level [MultiplayerAPI] should notify connected clients of other peers, and implement a relay protocol to allow communication between them).
+ </description>
+ </method>
<method name="poll">
<return type="void" />
<description>
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 3437e0eca4..a69491ec66 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -163,7 +163,7 @@
</description>
</signal>
<signal name="velocity_computed">
- <param index="0" name="safe_velocity" type="Vector3" />
+ <param index="0" name="safe_velocity" type="Vector2" />
<description>
Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
</description>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index e305491f16..0c74690924 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -452,7 +452,7 @@
Returns the video adapter driver name and version for the user's currently active graphics card.
The first element holds the driver name, such as [code]nvidia[/code], [code]amdgpu[/code], etc.
The second element holds the driver version. For e.g. the [code]nvidia[/code] driver on a Linux/BSD platform, the version is in the format [code]510.85.02[/code]. For Windows, the driver's format is [code]31.0.15.1659[/code].
- [b]Note:[/b] This method is only supported on the platforms Linux/BSD and Windows. It returns an empty array on other platforms.
+ [b]Note:[/b] This method is only supported on the platforms Linux/BSD and Windows when not running in headless mode. It returns an empty array on other platforms.
</description>
</method>
<method name="has_environment" qualifiers="const">
diff --git a/doc/classes/PortableCompressedTexture2D.xml b/doc/classes/PortableCompressedTexture2D.xml
index c7e2f2fbdc..a0492f2c07 100644
--- a/doc/classes/PortableCompressedTexture2D.xml
+++ b/doc/classes/PortableCompressedTexture2D.xml
@@ -22,7 +22,7 @@
<description>
Initializes the compressed texture from a base image. The compression mode must be provided.
If this image will be used as a normal map, the "normal map" flag is recommended, to ensure optimum quality.
- If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WEBP compression quality.
+ If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WebP compression quality.
</description>
</method>
<method name="get_compression_mode" qualifiers="const">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index c575fedebb..8da73f1b7d 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -831,6 +831,13 @@
</member>
<member name="input/ui_swap_input_direction" type="Dictionary" setter="" getter="">
</member>
+ <member name="input/ui_text_add_selection_for_next_occurrence" type="Dictionary" setter="" getter="">
+ If a selection is currently active with the last caret in text fields, searches for the next occurrence of the selection, adds a caret and selects the next occurrence.
+ If no selection is currently active with the last caret in text fields, selects the word currently under the caret.
+ The action can be performed sequentially for all occurrences of the selection of the last caret and for all existing carets.
+ The viewport is adjusted to the latest newly added caret.
+ [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
+ </member>
<member name="input/ui_text_backspace" type="Dictionary" setter="" getter="">
Default [InputEventAction] to delete the character before the text cursor.
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
@@ -985,7 +992,7 @@
</member>
<member name="input/ui_text_select_word_under_caret" type="Dictionary" setter="" getter="">
If no selection is currently active, selects the word currently under the caret in text fields. If a selection is currently active, deselects the current selection.
- [b]Note:[/b] Currently, this is only implemented in [TextEdit], not [LineEdit].
+ [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
</member>
<member name="input/ui_text_submit" type="Dictionary" setter="" getter="">
Default [InputEventAction] to submit a text field.
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index eb85a4adb4..51c2498ea7 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -1581,6 +1581,10 @@
</constant>
<constant name="LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z" value="34" enum="Limit">
</constant>
+ <constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_X" value="35" enum="Limit">
+ </constant>
+ <constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_Y" value="36" enum="Limit">
+ </constant>
<constant name="MEMORY_TEXTURES" value="0" enum="MemoryType">
</constant>
<constant name="MEMORY_BUFFERS" value="1" enum="MemoryType">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 9c6f7ba6b0..35fe7bdbd2 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -4577,9 +4577,11 @@
</constant>
<constant name="CANVAS_GROUP_MODE_DISABLED" value="0" enum="CanvasGroupMode">
</constant>
- <constant name="CANVAS_GROUP_MODE_OPAQUE" value="1" enum="CanvasGroupMode">
+ <constant name="CANVAS_GROUP_MODE_CLIP_ONLY" value="1" enum="CanvasGroupMode">
</constant>
- <constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="2" enum="CanvasGroupMode">
+ <constant name="CANVAS_GROUP_MODE_CLIP_AND_DRAW" value="2" enum="CanvasGroupMode">
+ </constant>
+ <constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="3" enum="CanvasGroupMode">
</constant>
<constant name="CANVAS_LIGHT_MODE_POINT" value="0" enum="CanvasLightMode">
</constant>
diff --git a/doc/classes/ResourceFormatSaver.xml b/doc/classes/ResourceFormatSaver.xml
index 05bfcf3446..1f2af6d157 100644
--- a/doc/classes/ResourceFormatSaver.xml
+++ b/doc/classes/ResourceFormatSaver.xml
@@ -24,6 +24,15 @@
Returns whether the given resource object can be saved by this saver.
</description>
</method>
+ <method name="_recognize_path" qualifiers="virtual const">
+ <return type="bool" />
+ <param index="0" name="resource" type="Resource" />
+ <param index="1" name="path" type="String" />
+ <description>
+ Returns [code]true[/code] if this saver handles a given save path and [code]false[/code] otherwise.
+ If this method is not implemented, the default behavior returns whether the path's extension is within the ones provided by [method _get_recognized_extensions].
+ </description>
+ </method>
<method name="_save" qualifiers="virtual">
<return type="int" />
<param index="0" name="resource" type="Resource" />
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index b1fda25ecd..879a355f25 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -70,6 +70,12 @@
Register a new gutter to this [TextEdit]. Use [param at] to have a specific gutter order. A value of [code]-1[/code] appends the gutter to the right.
</description>
</method>
+ <method name="add_selection_for_next_occurrence">
+ <return type="void" />
+ <description>
+ Adds a selection and a caret for the next occurrence of the current selection. If there is no active selection, selects word under caret.
+ </description>
+ </method>
<method name="adjust_carets_after_edit">
<return type="void" />
<param index="0" name="caret" type="int" />
diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml
index 471c1a9040..d1dfdecbd2 100644
--- a/doc/classes/TextLine.xml
+++ b/doc/classes/TextLine.xml
@@ -144,6 +144,7 @@
</methods>
<members>
<member name="alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="0">
+ Sets text alignment within the line as if the line was horizontal.
</member>
<member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0">
Text writing direction.
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 81e8dd2260..1a2cdfe10e 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -183,7 +183,7 @@
<method name="is_normalized" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise.
+ Returns [code]true[/code] if the vector is [method normalized], [code]false[/code] otherwise.
</description>
</method>
<method name="is_zero_approx" qualifiers="const">
@@ -244,18 +244,22 @@
<method name="normalized" qualifiers="const">
<return type="Vector3" />
<description>
- Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
+ Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code]. See also [method is_normalized].
</description>
</method>
<method name="octahedron_decode" qualifiers="static">
<return type="Vector3" />
<param index="0" name="uv" type="Vector2" />
<description>
+ Returns the [Vector3] from an octahedral-compressed form created using [method octahedron_encode] (stored as a [Vector2]).
</description>
</method>
<method name="octahedron_encode" qualifiers="const">
<return type="Vector2" />
<description>
+ Returns the octahedral-encoded (oct32) form of this [Vector3] as a [Vector2]. Since a [Vector2] occupies 1/3 less memory compared to [Vector3], this form of compression can be used to pass greater amounts of [method normalized] [Vector3]s without increasing storage or memory requirements. See also [method octahedron_decode].
+ [b]Note:[/b] [method octahedron_encode] can only be used for [method normalized] vectors. [method octahedron_encode] does [i]not[/i] check whether this [Vector3] is normalized, and will return a value that does not decompress to the original value if the [Vector3] is not normalized.
+ [b]Note:[/b] Octahedral compression is [i]lossy[/i], although visual differences are rarely perceptible in real world scenarios.
</description>
</method>
<method name="outer" qualifiers="const">
diff --git a/doc/classes/Vector4i.xml b/doc/classes/Vector4i.xml
index 3eea93ce1f..e9ac5b9475 100644
--- a/doc/classes/Vector4i.xml
+++ b/doc/classes/Vector4i.xml
@@ -133,12 +133,20 @@
<return type="Vector4i" />
<param index="0" name="right" type="Vector4i" />
<description>
+ Gets the remainder of each component of the [Vector4i] with the components of the given [Vector4i]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers.
+ [codeblock]
+ print(Vector4i(10, -20, 30, -40) % Vector4i(7, 8, 9, 10)) # Prints "(3, -4, 3, 0)"
+ [/codeblock]
</description>
</operator>
<operator name="operator %">
<return type="Vector4i" />
<param index="0" name="right" type="int" />
<description>
+ Gets the remainder of each component of the [Vector4i] with the the given [int]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers.
+ [codeblock]
+ print(Vector4i(10, -20, 30, -40) % 7) # Prints "(3, -6, 2, -5)"
+ [/codeblock]
</description>
</operator>
<operator name="operator *">
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index b407670098..5d0edda6c5 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -436,10 +436,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
item_count = 0;
- Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
-
- if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) {
+ if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
+ Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
+ if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ items[item_count++] = ci->canvas_group_owner;
+ }
} else if (!backbuffer_cleared) {
texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
backbuffer_cleared = true;
@@ -547,9 +549,18 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
-
- if (material.is_null() && ci->canvas_group != nullptr) {
- material = default_canvas_group_material;
+ if (ci->canvas_group != nullptr) {
+ if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ if (!p_to_backbuffer) {
+ material = default_clip_children_material;
+ }
+ } else {
+ if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
+ material = default_clip_children_material;
+ } else {
+ material = default_canvas_group_material;
+ }
+ }
}
GLES3::CanvasShaderData *shader_data_cache = nullptr;
@@ -2078,6 +2089,26 @@ void fragment() {
material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
}
+ {
+ default_clip_children_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(default_clip_children_shader);
+
+ material_storage->shader_set_code(default_clip_children_shader, R"(
+// Default clip children shader.
+
+shader_type canvas_item;
+
+void fragment() {
+ vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
+ COLOR.rgb = c.rgb;
+}
+)");
+ default_clip_children_material = material_storage->material_allocate();
+ material_storage->material_initialize(default_clip_children_material);
+
+ material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader);
+ }
+
default_canvas_texture = texture_storage->canvas_texture_allocate();
texture_storage->canvas_texture_initialize(default_canvas_texture);
@@ -2090,6 +2121,8 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version);
material_storage->material_free(default_canvas_group_material);
material_storage->shader_free(default_canvas_group_shader);
+ material_storage->material_free(default_clip_children_material);
+ material_storage->shader_free(default_clip_children_shader);
singleton = nullptr;
glDeleteBuffers(1, &data.canvas_quad_vertices);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 65e5f14838..aee2782b62 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -284,6 +284,8 @@ public:
RID default_canvas_texture;
RID default_canvas_group_material;
RID default_canvas_group_shader;
+ RID default_clip_children_material;
+ RID default_clip_children_shader;
typedef void Texture;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index da713a5259..6e9d4e5963 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1189,8 +1189,8 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
distance = 1.0;
}
- uint32_t indices;
- surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices);
+ uint32_t indices = 0;
+ surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices);
/*
if (p_render_data->render_info) {
indices = _indices_to_primitives(surf->primitive, indices);
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 2bd93796bd..ed176c7829 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1129,9 +1129,15 @@ void main() {
#if defined(CUSTOM_IRRADIANCE_USED)
ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a);
#endif // CUSTOM_IRRADIANCE_USED
- ambient_light *= albedo.rgb;
- ambient_light *= ao;
+ {
+#if defined(AMBIENT_LIGHT_DISABLED)
+ ambient_light = vec3(0.0, 0.0, 0.0);
+#else
+ ambient_light *= albedo.rgb;
+ ambient_light *= ao;
+#endif // AMBIENT_LIGHT_DISABLED
+ }
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 609ecacded..943df3b156 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -94,6 +94,7 @@ Config::Config() {
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &max_viewport_size);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment);
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
index de08385e04..d4b38acd18 100644
--- a/drivers/gles3/storage/config.h
+++ b/drivers/gles3/storage/config.h
@@ -64,6 +64,7 @@ public:
int max_texture_image_units = 0;
int max_texture_size = 0;
int max_uniform_buffer_size = 0;
+ int max_viewport_size = 0;
int max_renderable_elements = 0;
int max_renderable_lights = 0;
int max_lights_per_object = 0;
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index f5241e33ab..724ad57d64 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -89,7 +89,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
gui[j + 3] = 0; // ignored
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = v & 1 ? 1 : 0;
gui[1] = v & 2 ? 1 : 0;
}
@@ -116,7 +116,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
gui[j + 3] = 0; // ignored
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
@@ -145,7 +145,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
}
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
@@ -728,7 +728,7 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type,
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
- *gui = value[0].boolean ? 1 : 0;
+ gui[0] = value[0].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_BVEC2: {
uint32_t *gui = (uint32_t *)data;
@@ -2314,7 +2314,7 @@ void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) {
global_shader_uniforms.instance_buffer_pos.erase(p_instance);
}
-void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) {
+void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) {
if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) {
return; //just not allocated, ignore
}
@@ -2324,7 +2324,9 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i
return; //again, not allocated, ignore
}
ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES);
- ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported
+
+ Variant::Type value_type = p_value.get_type();
+ ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported
ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = {
ShaderLanguage::TYPE_MAX, //nil
@@ -2350,9 +2352,24 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i
ShaderLanguage::TYPE_VEC4 //color
};
- ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()];
+ ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX;
+ if (value_type == Variant::INT && p_flags_count > 0) {
+ switch (p_flags_count) {
+ case 1:
+ datatype = ShaderLanguage::TYPE_BVEC2;
+ break;
+ case 2:
+ datatype = ShaderLanguage::TYPE_BVEC3;
+ break;
+ case 3:
+ datatype = ShaderLanguage::TYPE_BVEC4;
+ break;
+ }
+ } else {
+ datatype = datatype_from_value[value_type];
+ }
- ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported
+ ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported
pos += p_index;
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index 6504c7748c..24d9a0fee1 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -530,7 +530,7 @@ public:
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override;
virtual void global_shader_parameters_instance_free(RID p_instance) override;
- virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override;
+ virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override;
GLuint global_shader_parameters_get_uniform_buffer() const;
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index 74f5800795..a31db24f2d 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -325,13 +325,12 @@ public:
return s->index_count ? s->index_count : s->vertex_count;
}
- _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const {
+ _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
int32_t current_lod = -1;
- if (r_index_count) {
- *r_index_count = s->index_count;
- }
+ r_index_count = s->index_count;
+
for (uint32_t i = 0; i < s->lod_count; i++) {
float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold;
if (screen_size > p_mesh_lod_threshold) {
@@ -342,9 +341,7 @@ public:
if (current_lod == -1) {
return 0;
} else {
- if (r_index_count) {
- *r_index_count = s->lods[current_lod].index_count;
- }
+ r_index_count = s->lods[current_lod].index_count;
return current_lod + 1;
}
}
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 6e91f38050..0a04f0d4d0 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -356,4 +356,13 @@ String Utilities::get_video_adapter_api_version() const {
return (const char *)glGetString(GL_VERSION);
}
+Size2i Utilities::get_maximum_viewport_size() const {
+ Config *config = Config::get_singleton();
+ if (!config) {
+ return Size2i();
+ }
+
+ return Size2i(config->max_viewport_size, config->max_viewport_size);
+}
+
#endif // GLES3_ENABLED
diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h
index e054f2f816..936ac35cc9 100644
--- a/drivers/gles3/storage/utilities.h
+++ b/drivers/gles3/storage/utilities.h
@@ -150,6 +150,8 @@ public:
virtual String get_video_adapter_vendor() const override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
virtual String get_video_adapter_api_version() const override;
+
+ virtual Size2i get_maximum_viewport_size() const override;
};
} // namespace GLES3
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 72c9a80a94..de29363556 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -9686,6 +9686,10 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const {
return limits.maxComputeWorkGroupSize[1];
case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z:
return limits.maxComputeWorkGroupSize[2];
+ case LIMIT_MAX_VIEWPORT_DIMENSIONS_X:
+ return limits.maxViewportDimensions[0];
+ case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y:
+ return limits.maxViewportDimensions[1];
case LIMIT_SUBGROUP_SIZE: {
VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities();
return subgroup_capabilities.size;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index d98ad117d7..7cc42f4d24 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -510,7 +510,7 @@ void EditorPropertyArray::_notification(int p_what) {
change_type->add_separator();
change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX);
- if (Object::cast_to<Button>(button_add_item)) {
+ if (button_add_item) {
button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
} break;
@@ -1205,7 +1205,7 @@ void EditorPropertyDictionary::_notification(int p_what) {
change_type->add_separator();
change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX);
- if (Object::cast_to<Button>(button_add_item)) {
+ if (button_add_item) {
button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
} break;
@@ -1431,7 +1431,7 @@ void EditorPropertyLocalizableString::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
- if (Object::cast_to<Button>(button_add_item)) {
+ if (button_add_item) {
button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
} break;
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index 7cf35c519b..c54b10842f 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -848,6 +848,8 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
CurveData &curvedata = state.curve_data_map[p_id];
curvedata.name = p_name;
+ String closed = parser.get_attribute_value_safe("closed").to_lower();
+ curvedata.closed = closed == "true" || closed == "1";
COLLADA_PRINT("curve name: " + p_name);
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 8d44329022..6890a46193 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -1090,6 +1090,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
c->set_point_tilt(i, tilts->array[i]);
}
}
+ if (cd.closed && pc > 1) {
+ Vector3 pos = c->get_point_position(0);
+ Vector3 in = c->get_point_in(0);
+ Vector3 out = c->get_point_out(0);
+ c->add_point(pos, in, out, -1);
+ }
curve_cache[ng->source] = c;
path->set_curve(c);
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index e1df78e741..2acb3a6b4e 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -135,7 +135,7 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const {
}
void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless (PNG),Lossy (WebP),Video RAM (S3TC/ETC/BPTC),Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 498e9f2964..5f64330453 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -474,7 +474,7 @@ Transform3D ResourceImporterScene::get_collision_shapes_transform(const M &p_opt
}
if (p_options.has(SNAME("primitive/rotation"))) {
- transform.basis.set_euler((p_options[SNAME("primitive/rotation")].operator Vector3() / 180.0) * Math_PI);
+ transform.basis = Basis::from_euler(p_options[SNAME("primitive/rotation")].operator Vector3() * (Math_PI / 180.0));
}
}
return transform;
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index ba11479714..58256ae549 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -154,7 +154,7 @@ void EditorPropertyFontMetaOverride::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- if (Object::cast_to<Button>(button_add)) {
+ if (button_add) {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
} break;
@@ -549,7 +549,7 @@ void EditorPropertyOTFeatures::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- if (Object::cast_to<Button>(button_add)) {
+ if (button_add) {
button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
} break;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index e5d4b262aa..3c9d93f13a 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2986,7 +2986,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Transform3D xform;
if (orthogonal) {
xform = sp->get_global_transform();
- xform.basis.set_euler(camera_transform.basis.get_euler());
+ xform.basis = Basis::from_euler(camera_transform.basis.get_euler());
} else {
xform = camera_transform;
xform.scale_basis(sp->get_scale());
@@ -3781,7 +3781,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo
if (child) {
AABB child_bounds = _calculate_spatial_bounds(child, false);
- if (bounds.size == Vector3() && Object::cast_to<Node3D>(p_parent)) {
+ if (bounds.size == Vector3() && p_parent) {
bounds = child_bounds;
} else {
bounds.merge_with(child_bounds);
@@ -3789,7 +3789,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo
}
}
- if (bounds.size == Vector3() && !Object::cast_to<Node3D>(p_parent)) {
+ if (bounds.size == Vector3() && !p_parent) {
bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
@@ -7615,7 +7615,7 @@ void Node3DEditor::_preview_settings_changed() {
{ // preview sun
Transform3D t;
- t.basis = Basis(Vector3(sun_rotation.x, sun_rotation.y, 0));
+ t.basis = Basis::from_euler(Vector3(sun_rotation.x, sun_rotation.y, 0));
preview_sun->set_transform(t);
sun_direction->queue_redraw();
preview_sun->set_param(Light3D::PARAM_ENERGY, sun_energy->get_value());
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 8355f64fe5..d9f8d28067 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -5087,23 +5087,23 @@ VisualShaderEditor::VisualShaderEditor() {
const String &compare_func_desc = TTR("Returns the boolean result of the %s comparison between two parameters.");
- add_options.push_back(AddOption("Equal", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("GreaterThan", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("GreaterThanEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("Equal (==)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("GreaterThan (>)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("GreaterThanEqual (>=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("If", "Conditional/Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("IsInf", "Conditional/Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_INF }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("IsNaN", "Conditional/Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between NaN and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_NAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("LessThan", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("LessThanEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("NotEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("Switch", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Switch2D", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("SwitchBool", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("SwitchFloat", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("SwitchInt", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("SwitchTransform", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_TRANSFORM }, VisualShaderNode::PORT_TYPE_TRANSFORM));
-
- add_options.push_back(AddOption("Compare", "Conditional/Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("LessThan (<)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("LessThanEqual (<=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("NotEqual (!=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("Switch (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Switch2D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("SwitchBool (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
+ add_options.push_back(AddOption("SwitchFloat (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("SwitchInt (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("SwitchTransform (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_TRANSFORM }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+
+ add_options.push_back(AddOption("Compare (==)", "Conditional/Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("Is", "Conditional/Common", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("BooleanConstant", "Conditional/Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN));
@@ -5308,7 +5308,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("EmitParticle", "Particles", "VisualShaderNodeParticleEmit", "", {}, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleAccelerator", "Particles", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleRandomness", "Particles", "VisualShaderNodeParticleRandomness", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles/Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MultiplyByAxisAngle (*)", "Particles/Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("BoxEmitter", "Particles/Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("MeshEmitter", "Particles/Emitters", "VisualShaderNodeParticleMeshEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
@@ -5359,10 +5359,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Max", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the greater of two values."), { VisualShaderNodeFloatOp::OP_MAX }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Min", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the lesser of two values."), { VisualShaderNodeFloatOp::OP_MIN }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Mix", "Scalar/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two scalars."), { VisualShaderNodeMix::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("MultiplyAdd", "Scalar/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), { VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Negate", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Negate", "Scalar/Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeIntFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("OneMinus", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), { VisualShaderNodeFloatFunc::FUNC_ONEMINUS }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Scalar/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), { VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Negate (*-1)", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Negate (*-1)", "Scalar/Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeIntFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("OneMinus (1-)", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), { VisualShaderNodeFloatFunc::FUNC_ONEMINUS }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Pow", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeFloatOp::OP_POW }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Radians", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeFloatFunc::FUNC_RADIANS }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Reciprocal", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 / scalar"), { VisualShaderNodeFloatFunc::FUNC_RECIPROCAL }, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -5381,21 +5381,21 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("TanH", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TANH }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Trunc", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TRUNC }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Add", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), { VisualShaderNodeFloatOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Add", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), { VisualShaderNodeIntOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseAND", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_AND }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseLeftShift", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseOR", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_OR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseRightShift", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("BitwiseXOR", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_XOR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Divide", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), { VisualShaderNodeFloatOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Divide", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), { VisualShaderNodeIntOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Multiply", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Multiply", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), { VisualShaderNodeIntOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Add (+)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), { VisualShaderNodeFloatOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Add (+)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), { VisualShaderNodeIntOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseAND (&)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_AND }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseLeftShift (<<)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseOR (|)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_OR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseRightShift (>>)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseXOR (^)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_XOR }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Divide (/)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), { VisualShaderNodeFloatOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Divide (/)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), { VisualShaderNodeIntOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Multiply (*)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Multiply (*)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), { VisualShaderNodeIntOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("Remainder", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Returns the remainder of the two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Remainder", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the remainder of the two integer scalars."), { VisualShaderNodeIntOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
- add_options.push_back(AddOption("Subtract", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), { VisualShaderNodeFloatOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Subtract", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), { VisualShaderNodeIntOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("Subtract (-)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), { VisualShaderNodeFloatOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Subtract (-)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), { VisualShaderNodeIntOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("FloatConstant", "Scalar/Variables", "VisualShaderNodeFloatConstant", TTR("Scalar floating-point constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("IntConstant", "Scalar/Variables", "VisualShaderNodeIntConstant", TTR("Scalar integer constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT));
@@ -5452,12 +5452,12 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Inverse", "Transform/Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), { VisualShaderNodeTransformFunc::FUNC_INVERSE }, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("Transpose", "Transform/Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), { VisualShaderNodeTransformFunc::FUNC_TRANSPOSE }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Add", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), { VisualShaderNodeTransformOp::OP_ADD }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Divide", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), { VisualShaderNodeTransformOp::OP_A_DIV_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Multiply", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("MultiplyComp", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("Subtract", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformVectorMult", "Transform/Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Add (+)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), { VisualShaderNodeTransformOp::OP_ADD }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Divide (/)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), { VisualShaderNodeTransformOp::OP_A_DIV_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Multiply (*)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("MultiplyComp (*)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("Subtract (-)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
+ add_options.push_back(AddOption("TransformVectorMult (*)", "Transform/Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("TransformConstant", "Transform/Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("TransformParameter", "Transform/Variables", "VisualShaderNodeTransformParameter", TTR("Transform parameter."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
@@ -5573,21 +5573,21 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
@@ -5632,9 +5632,9 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
@@ -5645,21 +5645,21 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 2D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 4D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 4D vector from 4D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 4D vector from 4D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Vector2Constant", "Vector/Variables", "VisualShaderNodeVec2Constant", TTR("2D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Vector2Parameter", "Vector/Variables", "VisualShaderNodeVec2Parameter", TTR("2D vector parameter."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
diff --git a/main/main.cpp b/main/main.cpp
index bb6ab11362..dbaf42b3e4 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1956,9 +1956,15 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
{
String display_driver = DisplayServer::get_create_function_name(display_driver_idx);
+ Vector2i *window_position = nullptr;
+ Vector2i position = init_custom_pos;
+ if (init_use_custom_pos) {
+ window_position = &position;
+ }
+
// rendering_driver now held in static global String in main and initialized in setup()
Error err;
- display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err);
+ display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, err);
if (err != OK || display_server == nullptr) {
// We can't use this display server, try other ones as fallback.
// Skip headless (always last registered) because that's not what users
@@ -1967,7 +1973,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
if (i == display_driver_idx) {
continue; // Don't try the same twice.
}
- display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err);
+ display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, err);
if (err == OK && display_server != nullptr) {
break;
}
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
index 5181ae76ce..f02fc893fd 100644
--- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -77,8 +77,5 @@
<member name="host" type="ENetConnection" setter="" getter="get_host">
The underlying [ENetConnection] created after [method create_client] and [method create_server].
</member>
- <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
- Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
- </member>
</members>
</class>
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index e7728f4aec..c93db3b972 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -44,6 +44,22 @@ int ENetMultiplayerPeer::get_packet_peer() const {
return incoming_packets.front()->get().from;
}
+MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, TRANSFER_MODE_RELIABLE);
+ return incoming_packets.front()->get().transfer_mode;
+}
+
+int ENetMultiplayerPeer::get_packet_channel() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
+ int ch = incoming_packets.front()->get().channel;
+ if (ch >= SYSCH_MAX) { // First 2 channels are reserved.
+ return ch - SYSCH_MAX + 1;
+ }
+ return 0;
+}
+
Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
set_refuse_new_connections(false);
@@ -114,6 +130,22 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
return OK;
}
+void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) {
+ Packet packet;
+ packet.packet = p_event.packet;
+ packet.channel = p_event.channel_id;
+ packet.from = p_source;
+ if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) {
+ packet.transfer_mode = TRANSFER_MODE_RELIABLE;
+ } else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) {
+ packet.transfer_mode = TRANSFER_MODE_UNRELIABLE;
+ } else {
+ packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED;
+ }
+ packet.packet->referenceCount++;
+ incoming_packets.push_back(packet);
+}
+
bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
switch (p_type) {
case ENetConnection::EVENT_CONNECT: {
@@ -131,9 +163,6 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type,
peers[id] = p_event.peer;
emit_signal(SNAME("peer_connected"), id);
- if (server_relay) {
- _notify_peers(id, true);
- }
return false;
}
case ENetConnection::EVENT_DISCONNECT: {
@@ -145,50 +174,11 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type,
emit_signal(SNAME("peer_disconnected"), id);
peers.erase(id);
- if (server_relay) {
- _notify_peers(id, false);
- }
return false;
}
case ENetConnection::EVENT_RECEIVE: {
- if (p_event.channel_id == SYSCH_CONFIG) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Only server can send config messages");
- } else {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- uint32_t source = decode_uint32(&p_event.packet->data[0]);
- int target = decode_uint32(&p_event.packet->data[4]);
-
- uint32_t id = p_event.peer->get_meta(SNAME("_net_id"));
- // Someone is cheating and trying to fake the source!
- if (source != id) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!");
- }
-
- Packet packet;
- packet.packet = p_event.packet;
- packet.channel = p_event.channel_id;
- packet.from = id;
-
- // Even if relaying is disabled, these targets are valid as incoming packets.
- if (target == 1 || target == 0 || target < -1) {
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- }
-
- if (server_relay && target != 1) {
- packet.packet->referenceCount++;
- _relay(source, target, p_event.channel_id, p_event.packet);
- packet.packet->referenceCount--;
- _destroy_unused(p_event.packet);
- }
- // Destroy packet later
- }
+ int32_t source = p_event.peer->get_meta(SNAME("_net_id"));
+ _store_packet(source, p_event);
return false;
}
default:
@@ -215,44 +205,7 @@ bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type,
return true;
}
case ENetConnection::EVENT_RECEIVE: {
- if (p_event.channel_id == SYSCH_CONFIG) {
- // Config message
- if (p_event.packet->dataLength != 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V(false);
- }
-
- int msg = decode_uint32(&p_event.packet->data[0]);
- int id = decode_uint32(&p_event.packet->data[4]);
-
- switch (msg) {
- case SYSMSG_ADD_PEER: {
- peers[id] = Ref<ENetPacketPeer>();
- emit_signal(SNAME("peer_connected"), id);
-
- } break;
- case SYSMSG_REMOVE_PEER: {
- peers.erase(id);
- emit_signal(SNAME("peer_disconnected"), id);
- } break;
- }
- _destroy_unused(p_event.packet);
- } else {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- uint32_t source = decode_uint32(&p_event.packet->data[0]);
- Packet packet;
- packet.packet = p_event.packet;
- packet.from = source;
- packet.channel = p_event.channel_id;
-
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- // Destroy packet later
- }
+ _store_packet(1, p_event);
return false;
}
default:
@@ -273,18 +226,7 @@ bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, EN
hosts.erase(p_peer_id);
return true;
case ENetConnection::EVENT_RECEIVE: {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- Packet packet;
- packet.packet = p_event.packet;
- packet.from = p_peer_id;
- packet.channel = p_event.channel_id;
-
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
+ _store_packet(p_peer_id, p_event);
return false;
} break;
default:
@@ -376,6 +318,10 @@ bool ENetMultiplayerPeer::is_server() const {
return active_mode == MODE_SERVER;
}
+bool ENetMultiplayerPeer::is_server_relay_supported() const {
+ return active_mode == MODE_SERVER || active_mode == MODE_CLIENT;
+}
+
void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
if (!_is_active()) {
return;
@@ -422,8 +368,8 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si
current_packet = incoming_packets.front()->get();
incoming_packets.pop_front();
- *r_buffer = (const uint8_t *)(&current_packet.packet->data[8]);
- r_buffer_size = current_packet.packet->dataLength - 8;
+ *r_buffer = (const uint8_t *)(current_packet.packet->data);
+ r_buffer_size = current_packet.packet->dataLength;
return OK;
}
@@ -457,15 +403,13 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
}
#ifdef DEBUG_ENABLED
- if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) {
- WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU));
+ if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) {
+ WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU));
}
#endif
- ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); // Source ID
- encode_uint32(target_peer, &packet->data[4]); // Dest ID
- memcpy(&packet->data[8], p_buffer, p_buffer_size);
+ ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags);
+ memcpy(&packet->data[0], p_buffer, p_buffer_size);
if (is_server()) {
if (target_peer == 0) {
@@ -548,16 +492,6 @@ void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) {
MultiplayerPeer::set_refuse_new_connections(p_enabled);
}
-void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) {
- ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active.");
-
- server_relay = p_enabled;
-}
-
-bool ENetMultiplayerPeer::is_server_relay_enabled() const {
- return server_relay;
-}
-
Ref<ENetConnection> ENetMultiplayerPeer::get_host() const {
ERR_FAIL_COND_V(!_is_active(), nullptr);
ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr);
@@ -577,70 +511,6 @@ void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) {
}
}
-void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) {
- if (p_to == 0) {
- // Re-send to everyone but sender :|
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_from) {
- continue;
- }
-
- E.value->send(p_channel, p_packet);
- }
- } else if (p_to < 0) {
- // Re-send to everyone but excluded and sender.
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded
- continue;
- }
-
- E.value->send(p_channel, p_packet);
- }
- } else {
- // To someone else, specifically
- ERR_FAIL_COND(!peers.has(p_to));
- ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags);
- peers[p_to]->send(p_channel, packet);
- }
-}
-
-void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) {
- if (p_connected) {
- ERR_FAIL_COND(!peers.has(p_id));
- // Someone connected, notify all the peers available.
- Ref<ENetPacketPeer> peer = peers[p_id];
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
- encode_uint32(p_id, &packet->data[4]);
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_id) {
- continue;
- }
- // Send new peer to existing peer.
- E.value->send(SYSCH_CONFIG, packet);
- // Send existing peer to new peer.
- // This packet will be automatically destroyed by ENet after send.
- ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]);
- encode_uint32(E.key, &packet2->data[4]);
- peer->send(SYSCH_CONFIG, packet2);
- }
- _destroy_unused(packet);
- } else {
- // Server just received a client disconnect and is in relay mode, notify everyone else.
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
- encode_uint32(p_id, &packet->data[4]);
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_id) {
- continue;
- }
- E.value->send(SYSCH_CONFIG, packet);
- }
- _destroy_unused(packet);
- }
-}
-
void ENetMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
@@ -649,12 +519,9 @@ void ENetMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100));
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip);
- ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled);
- ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled);
ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host);
ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
}
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index 3152068d46..85692e8eee 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -47,10 +47,9 @@ private:
};
enum {
- SYSCH_CONFIG = 0,
- SYSCH_RELIABLE = 1,
- SYSCH_UNRELIABLE = 2,
- SYSCH_MAX = 3
+ SYSCH_RELIABLE = 0,
+ SYSCH_UNRELIABLE = 1,
+ SYSCH_MAX = 2
};
enum Mode {
@@ -66,8 +65,6 @@ private:
int target_peer = 0;
- bool server_relay = true;
-
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
HashMap<int, Ref<ENetConnection>> hosts;
@@ -77,18 +74,18 @@ private:
ENetPacket *packet = nullptr;
int from = 0;
int channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
};
List<Packet> incoming_packets;
Packet current_packet;
+ void _store_packet(int32_t p_source, ENetConnection::Event &p_event);
void _pop_current_packet();
bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
bool _parse_mesh_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event, int p_peer_id);
- void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet);
- void _notify_peers(int p_id, bool p_connected);
void _destroy_unused(ENetPacket *p_packet);
_FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
@@ -99,10 +96,15 @@ protected:
public:
virtual void set_target_peer(int p_peer) override;
+
virtual int get_packet_peer() const override;
+ virtual TransferMode get_packet_mode() const override;
+ virtual int get_packet_channel() const override;
virtual void poll() override;
virtual bool is_server() const override;
+ virtual bool is_server_relay_supported() const override;
+
// Overridden so we can instrument the DTLSServer when needed.
virtual void set_refuse_new_connections(bool p_enabled) override;
@@ -125,8 +127,6 @@ public:
void disconnect_peer(int p_peer, bool now = false);
void set_bind_ip(const IPAddress &p_ip);
- void set_server_relay_enabled(bool p_enabled);
- bool is_server_relay_enabled() const;
Ref<ENetConnection> get_host() const;
Ref<ENetPacketPeer> get_peer(int p_id) const;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 0a9dad04c7..b4da9c1224 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1050,6 +1050,11 @@ Error GDScript::load_byte_code(const String &p_path) {
return ERR_COMPILATION_FAILED;
}
+void GDScript::set_path(const String &p_path, bool p_take_over) {
+ Script::set_path(p_path, p_take_over);
+ this->path = p_path;
+}
+
Error GDScript::load_source_code(const String &p_path) {
Vector<uint8_t> sourcef;
Error err;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 0a010e5ad0..72ad890fbc 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -222,6 +222,7 @@ public:
virtual Error reload(bool p_keep_state = false) override;
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
Error load_source_code(const String &p_path);
Error load_byte_code(const String &p_path);
diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
index 62bb396d15..a7b89f58ac 100644
--- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml
+++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
@@ -42,6 +42,10 @@
The root path to use for RPCs and replication. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene.
</member>
+ <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
+ Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
+ [b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported].
+ </member>
</members>
<signals>
<signal name="peer_packet">
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
index b3b642f815..7df9b95b30 100644
--- a/modules/multiplayer/scene_cache_interface.cpp
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -105,8 +105,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
multiplayer_peer->set_transfer_channel(0);
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
- multiplayer_peer->set_target_peer(p_from);
- multiplayer_peer->put_packet(packet.ptr(), packet.size());
+ multiplayer->send_command(p_from, packet.ptr(), packet.size());
}
void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
@@ -162,10 +161,9 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat
Error err = OK;
for (int peer_id : p_peers) {
- multiplayer_peer->set_target_peer(peer_id);
multiplayer_peer->set_transfer_channel(0);
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
- err = multiplayer_peer->put_packet(packet.ptr(), packet.size());
+ err = multiplayer->send_command(peer_id, packet.ptr(), packet.size());
ERR_FAIL_COND_V(err != OK, err);
// Insert into confirmed, but as false since it was not confirmed.
psc->confirmed_peers.insert(peer_id, false);
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
index 3fc1eef366..f94f8ef658 100644
--- a/modules/multiplayer/scene_multiplayer.cpp
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -67,12 +67,20 @@ Error SceneMultiplayer::poll() {
const uint8_t *packet;
int len;
+ int channel = multiplayer_peer->get_packet_channel();
+ MultiplayerPeer::TransferMode mode = multiplayer_peer->get_packet_mode();
+
Error err = multiplayer_peer->get_packet(&packet, len);
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err));
- remote_sender_id = sender;
- _process_packet(sender, packet, len);
- remote_sender_id = 0;
+ if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) {
+ // Sys messages are processed separately since they might call _process_packet themselves.
+ _process_sys(sender, packet, len, mode, channel);
+ } else {
+ remote_sender_id = sender;
+ _process_packet(sender, packet, len);
+ remote_sender_id = 0;
+ }
if (!multiplayer_peer.is_valid()) {
return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here.
@@ -86,6 +94,7 @@ void SceneMultiplayer::clear() {
connected_peers.clear();
packet_cache.clear();
cache->clear();
+ relay_buffer->clear();
}
void SceneMultiplayer::set_root_path(const NodePath &p_path) {
@@ -166,10 +175,123 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
case NETWORK_COMMAND_SYNC: {
replicator->on_sync_receive(p_from, p_packet, p_packet_len);
} break;
+ default: {
+ ERR_FAIL_MSG("Invalid network command from " + itos(p_from));
+ } break;
+ }
+}
+
+Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) {
+ if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Send relay packet.
+ relay_buffer->seek(0);
+ relay_buffer->put_u8(NETWORK_COMMAND_SYS);
+ relay_buffer->put_u8(SYS_COMMAND_RELAY);
+ relay_buffer->put_32(p_to); // Set the destination.
+ relay_buffer->put_data(p_packet, p_packet_len);
+ multiplayer_peer->set_target_peer(1);
+ const Vector<uint8_t> data = relay_buffer->get_data_array();
+ return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ }
+ if (p_to < 0) {
+ for (const int &pid : connected_peers) {
+ if (pid == -p_to) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(pid);
+ multiplayer_peer->put_packet(p_packet, p_packet_len);
+ }
+ return OK;
+ } else {
+ multiplayer_peer->set_target_peer(p_to);
+ return multiplayer_peer->put_packet(p_packet, p_packet_len);
+ }
+}
+
+void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel) {
+ ERR_FAIL_COND_MSG(p_packet_len < SYS_CMD_SIZE, "Invalid packet received. Size too small.");
+ uint8_t sys_cmd_type = p_packet[1];
+ int32_t peer = int32_t(decode_uint32(&p_packet[2]));
+ switch (sys_cmd_type) {
+ case SYS_COMMAND_ADD_PEER: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
+ _add_peer(peer);
+ } break;
+ case SYS_COMMAND_DEL_PEER: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
+ _del_peer(peer);
+ } break;
+ case SYS_COMMAND_RELAY: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported());
+ ERR_FAIL_COND(p_packet_len < SYS_CMD_SIZE + 1);
+ const uint8_t *packet = p_packet + SYS_CMD_SIZE;
+ int len = p_packet_len - SYS_CMD_SIZE;
+ bool should_process = false;
+ if (get_unique_id() == 1) { // I am the server.
+ // Direct messages to server should not go through relay.
+ ERR_FAIL_COND(peer > 0 && !connected_peers.has(peer));
+ // Send relay packet.
+ relay_buffer->seek(0);
+ relay_buffer->put_u8(NETWORK_COMMAND_SYS);
+ relay_buffer->put_u8(SYS_COMMAND_RELAY);
+ relay_buffer->put_32(p_from); // Set the source.
+ relay_buffer->put_data(packet, len);
+ const Vector<uint8_t> data = relay_buffer->get_data_array();
+ multiplayer_peer->set_transfer_mode(p_mode);
+ multiplayer_peer->set_transfer_channel(p_channel);
+ if (peer > 0) {
+ multiplayer_peer->set_target_peer(peer);
+ multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ } else {
+ for (const int &P : connected_peers) {
+ // Not to sender, nor excluded.
+ if (P == p_from || (peer < 0 && P != -peer)) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(P);
+ multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ }
+ }
+ if (peer == 0 || peer == -1) {
+ should_process = true;
+ peer = p_from; // Process as the source.
+ }
+ } else {
+ ERR_FAIL_COND(p_from != 1); // Bug.
+ should_process = true;
+ }
+ if (should_process) {
+ remote_sender_id = peer;
+ _process_packet(peer, packet, len);
+ remote_sender_id = 0;
+ }
+ } break;
+ default: {
+ ERR_FAIL();
+ }
}
}
void SceneMultiplayer::_add_peer(int p_id) {
+ if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Notify others of connection, and send connected peers to newly connected one.
+ uint8_t buf[SYS_CMD_SIZE];
+ buf[0] = NETWORK_COMMAND_SYS;
+ buf[1] = SYS_COMMAND_ADD_PEER;
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ for (const int &P : connected_peers) {
+ // Send new peer to already connected.
+ encode_uint32(p_id, &buf[2]);
+ multiplayer_peer->set_target_peer(P);
+ multiplayer_peer->put_packet(buf, sizeof(buf));
+ // Send already connected to new peer.
+ encode_uint32(P, &buf[2]);
+ multiplayer_peer->set_target_peer(p_id);
+ multiplayer_peer->put_packet(buf, sizeof(buf));
+ }
+ }
+
connected_peers.insert(p_id);
cache->on_peer_change(p_id, true);
replicator->on_peer_change(p_id, true);
@@ -177,6 +299,23 @@ void SceneMultiplayer::_add_peer(int p_id) {
}
void SceneMultiplayer::_del_peer(int p_id) {
+ if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Notify others of disconnection.
+ uint8_t buf[SYS_CMD_SIZE];
+ buf[0] = NETWORK_COMMAND_SYS;
+ buf[1] = SYS_COMMAND_DEL_PEER;
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ encode_uint32(p_id, &buf[2]);
+ for (const int &P : connected_peers) {
+ if (P == p_id) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(P);
+ multiplayer_peer->put_packet(buf, sizeof(buf));
+ }
+ }
+
replicator->on_peer_change(p_id, false);
cache->on_peer_change(p_id, false);
connected_peers.erase(p_id);
@@ -209,11 +348,9 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer
packet_cache.write[0] = NETWORK_COMMAND_RAW;
memcpy(&packet_cache.write[1], &r[0], p_data.size());
- multiplayer_peer->set_target_peer(p_to);
multiplayer_peer->set_transfer_channel(p_channel);
multiplayer_peer->set_transfer_mode(p_mode);
-
- return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
+ return send_command(p_to, packet_cache.ptr(), p_data.size() + 1);
}
void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
@@ -303,6 +440,15 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con
return ERR_INVALID_PARAMETER;
}
+void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) {
+ ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active.");
+ server_relay = p_enabled;
+}
+
+bool SceneMultiplayer::is_server_relay_enabled() const {
+ return server_relay;
+}
+
void SceneMultiplayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path);
ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path);
@@ -311,17 +457,22 @@ void SceneMultiplayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections);
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding);
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &SceneMultiplayer::is_object_decoding_allowed);
+ ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &SceneMultiplayer::set_server_relay_enabled);
+ ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled);
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
+
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
}
SceneMultiplayer::SceneMultiplayer() {
+ relay_buffer.instantiate();
replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this)));
rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this)));
cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));
diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h
index a99cca7b21..06d7b2c7a7 100644
--- a/modules/multiplayer/scene_multiplayer.h
+++ b/modules/multiplayer/scene_multiplayer.h
@@ -49,6 +49,17 @@ public:
NETWORK_COMMAND_SPAWN,
NETWORK_COMMAND_DESPAWN,
NETWORK_COMMAND_SYNC,
+ NETWORK_COMMAND_SYS,
+ };
+
+ enum SysCommands {
+ SYS_COMMAND_ADD_PEER,
+ SYS_COMMAND_DEL_PEER,
+ SYS_COMMAND_RELAY,
+ };
+
+ enum {
+ SYS_CMD_SIZE = 6, // Command + sys command + peer_id (+ optional payload).
};
// For each command, the 4 MSB can contain custom flags, as defined by subsystems.
@@ -74,6 +85,8 @@ private:
NodePath root_path;
bool allow_object_decoding = false;
+ bool server_relay = true;
+ Ref<StreamPeerBuffer> relay_buffer;
Ref<SceneCacheInterface> cache;
Ref<SceneReplicationInterface> replicator;
@@ -84,6 +97,7 @@ protected:
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel);
void _add_peer(int p_id);
void _del_peer(int p_id);
@@ -111,6 +125,7 @@ public:
void set_root_path(const NodePath &p_path);
NodePath get_root_path() const;
+ Error send_command(int p_to, const uint8_t *p_packet, int p_packet_len); // Used internally to relay packets when needed.
Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
String get_rpc_md5(const Object *p_obj);
@@ -123,6 +138,9 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
+ void set_server_relay_enabled(bool p_enabled);
+ bool is_server_relay_enabled() const;
+
Ref<SceneCacheInterface> get_path_cache() { return cache; }
#ifdef DEBUG_ENABLED
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index 8359580805..da425076a4 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -370,10 +370,9 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size,
#endif
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer);
peer->set_transfer_channel(0);
peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE);
- return peer->put_packet(p_buffer, p_size);
+ return multiplayer->send_command(p_peer, p_buffer, p_size);
}
Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) {
diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp
index 65090b9316..acc113c901 100644
--- a/modules/multiplayer/scene_rpc_interface.cpp
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -412,8 +412,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
if (has_all_peers) {
// They all have verified paths, so send fast.
- peer->set_target_peer(p_to); // To all of you.
- peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
+ multiplayer->send_command(p_to, packet_cache.ptr(), ofs);
} else {
// Unreachable because the node ID is never compressed if the peers doesn't know it.
CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
@@ -438,16 +437,14 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P);
- peer->set_target_peer(P); // To this one specifically.
-
if (confirmed) {
// This one confirmed path, so use id.
encode_uint32(psc_id, &(packet_cache.write[1]));
- peer->put_packet(packet_cache.ptr(), ofs);
+ multiplayer->send_command(P, packet_cache.ptr(), ofs);
} else {
// This one did not confirm path yet, so use entire path (sorry!).
encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
- peer->put_packet(packet_cache.ptr(), ofs + path_len);
+ multiplayer->send_command(P, packet_cache.ptr(), ofs + path_len);
}
}
}
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index 185e44c29d..123f860ce9 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -224,7 +224,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our interaction profiles
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -236,7 +236,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Vive controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -257,7 +257,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our WMR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -280,7 +280,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Meta touch controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -305,7 +305,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Valve index controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -333,7 +333,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our HP MR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -356,7 +356,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Samsung Odyssey controller profile,
// Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -379,7 +379,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Vive Cosmos controller
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -403,7 +403,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Note, Vive Focus 3 currently is not yet supported as a stand alone device
// however HTC currently has a beta OpenXR runtime in testing we may support in the near future
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
@@ -427,7 +427,7 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Huawei controller
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
index bd71c2869a..56ff8097b4 100644
--- a/modules/webp/resource_saver_webp.cpp
+++ b/modules/webp/resource_saver_webp.cpp
@@ -38,8 +38,8 @@
Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<ImageTexture> texture = p_resource;
- ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP.");
- ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP.");
+ ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WebP.");
+ ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WebP.");
Ref<Image> img = texture->get_image();
@@ -52,7 +52,7 @@ Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_im
Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality);
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path));
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WebP at path: '%s'.", p_path));
const uint8_t *reader = buffer.ptr();
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
index 049c1c3a32..c6440f1796 100644
--- a/modules/webp/webp_common.cpp
+++ b/modules/webp/webp_common.cpp
@@ -139,7 +139,7 @@ Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>());
WebPBitstreamFeatures features;
if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WebP image.");
}
Vector<uint8_t> dst_image;
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index 927888fe21..0b42c6ed35 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -6,7 +6,7 @@
<description>
This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer].
You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections.
- [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer].
+ [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless the peer is created using [method create_client]. Beside that data transfer works like in a [MultiplayerPeer].
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
@@ -28,6 +28,31 @@
Close all the add peer connections and channels, freeing all resources.
</description>
</method>
+ <method name="create_client">
+ <return type="int" enum="Error" />
+ <param index="0" name="peer_id" type="int" />
+ <param index="1" name="channels_config" type="Array" default="[]" />
+ <description>
+ Initialize the multiplayer peer as a client with the given [code]peer_id[/code] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [code]peer_id[/code] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
+ You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ </description>
+ </method>
+ <method name="create_mesh">
+ <return type="int" enum="Error" />
+ <param index="0" name="peer_id" type="int" />
+ <param index="1" name="channels_config" type="Array" default="[]" />
+ <description>
+ Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [code]peer_id[/code] (must be between 1 and 2147483647).
+ </description>
+ </method>
+ <method name="create_server">
+ <return type="int" enum="Error" />
+ <param index="0" name="channels_config" type="Array" default="[]" />
+ <description>
+ Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
+ You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ </description>
+ </method>
<method name="get_peer">
<return type="Dictionary" />
<param index="0" name="peer_id" type="int" />
@@ -48,18 +73,6 @@
Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
</description>
</method>
- <method name="initialize">
- <return type="int" enum="Error" />
- <param index="0" name="peer_id" type="int" />
- <param index="1" name="server_compatibility" type="bool" default="false" />
- <param index="2" name="channels_config" type="Array" default="[]" />
- <description>
- Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647).
- If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted.
- If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED].
- You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
- </description>
- </method>
<method name="remove_peer">
<return type="void" />
<param index="0" name="peer_id" type="int" />
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index e03b6b2473..5ea81d5a1b 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -34,7 +34,9 @@
#include "core/os/os.h"
void WebRTCMultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_server", "channels_config"), &WebRTCMultiplayerPeer::create_server, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_client", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_client, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_mesh", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_mesh, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1));
ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
@@ -52,6 +54,15 @@ int WebRTCMultiplayerPeer::get_packet_peer() const {
return next_packet_peer;
}
+int WebRTCMultiplayerPeer::get_packet_channel() const {
+ return next_packet_channel < CH_RESERVED_MAX ? 0 : next_packet_channel - CH_RESERVED_MAX + 1;
+}
+
+MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_packet_mode() const {
+ ERR_FAIL_INDEX_V(next_packet_channel, channels_modes.size(), TRANSFER_MODE_RELIABLE);
+ return channels_modes[next_packet_channel];
+}
+
bool WebRTCMultiplayerPeer::is_server() const {
return unique_id == TARGET_PEER_SERVER;
}
@@ -113,24 +124,14 @@ void WebRTCMultiplayerPeer::poll() {
// Signal newly connected peers
for (int &E : add) {
// Already connected to server: simply notify new peer.
- // NOTE: Mesh is always connected.
- if (connection_status == CONNECTION_CONNECTED) {
- emit_signal(SNAME("peer_connected"), E);
- }
-
- // Server emulation mode suppresses peer_conencted until server connects.
- if (server_compat && E == TARGET_PEER_SERVER) {
+ if (network_mode == MODE_CLIENT) {
+ ERR_CONTINUE(E != TARGET_PEER_SERVER); // Bug.
// Server connected.
connection_status = CONNECTION_CONNECTED;
emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER);
emit_signal(SNAME("connection_succeeded"));
- // Notify of all previously connected peers
- for (const KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
- if (F.key != 1 && F.value->connected) {
- emit_signal(SNAME("peer_connected"), F.key);
- }
- }
- break; // Because we already notified of all newly added peers.
+ } else {
+ emit_signal(SNAME("peer_connected"), E);
}
}
// Fetch next packet
@@ -150,11 +151,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
continue;
}
+ int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
+ next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
+ idx++;
}
++E;
}
@@ -165,11 +169,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
continue;
}
+ int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
+ next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
+ idx++;
}
if (E->key == (int)next_packet_peer) {
break;
@@ -177,6 +184,7 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
}
// No packet found
+ next_packet_channel = 0;
next_packet_peer = 0;
}
@@ -184,11 +192,28 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status()
return connection_status;
}
-Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
+Error WebRTCMultiplayerPeer::create_server(Array p_channels_config) {
+ return _initialize(1, MODE_SERVER, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::create_client(int p_self_id, Array p_channels_config) {
+ ERR_FAIL_COND_V_MSG(p_self_id == 1, ERR_INVALID_PARAMETER, "Clients cannot have ID 1.");
+ return _initialize(p_self_id, MODE_CLIENT, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::create_mesh(int p_self_id, Array p_channels_config) {
+ return _initialize(p_self_id, MODE_MESH, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::_initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config) {
ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
channels_config.clear();
+ channels_modes.clear();
+ channels_modes.push_back(TRANSFER_MODE_RELIABLE);
+ channels_modes.push_back(TRANSFER_MODE_UNRELIABLE_ORDERED);
+ channels_modes.push_back(TRANSFER_MODE_UNRELIABLE);
for (int i = 0; i < p_channels_config.size(); i++) {
- ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'");
+ ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
@@ -207,16 +232,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
case TRANSFER_MODE_RELIABLE:
break;
default:
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode));
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
+ channels_modes.push_back((TransferMode)mode);
}
unique_id = p_self_id;
- server_compat = p_server_compat;
+ network_mode = p_mode;
// Mesh and server are always connected
- if (!server_compat || p_self_id == 1) {
+ if (p_mode != MODE_CLIENT) {
connection_status = CONNECTION_CONNECTED;
} else {
connection_status = CONNECTION_CONNECTING;
@@ -224,6 +250,10 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
return OK;
}
+bool WebRTCMultiplayerPeer::is_server_relay_supported() const {
+ return network_mode == MODE_SERVER || network_mode == MODE_CLIENT;
+}
+
int WebRTCMultiplayerPeer::get_unique_id() const {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
return unique_id;
@@ -261,7 +291,10 @@ Dictionary WebRTCMultiplayerPeer::get_peers() {
}
Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
- ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(network_mode == MODE_NONE, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(network_mode == MODE_CLIENT && p_peer_id != 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(network_mode == MODE_SERVER && p_peer_id == 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_peer_id < 1 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED);
// Peer must be valid, and in new state (to create data channels)
@@ -308,8 +341,12 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
if (peer->connected) {
peer->connected = false;
emit_signal(SNAME("peer_disconnected"), p_peer_id);
- if (server_compat && p_peer_id == TARGET_PEER_SERVER) {
- emit_signal(SNAME("server_disconnected"));
+ if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
+ if (connection_status == CONNECTION_CONNECTING) {
+ emit_signal(SNAME("connection_failed"));
+ } else {
+ emit_signal(SNAME("server_disconnected"));
+ }
connection_status = CONNECTION_DISCONNECTED;
}
}
@@ -403,7 +440,9 @@ void WebRTCMultiplayerPeer::close() {
channels_config.clear();
unique_id = 0;
next_packet_peer = 0;
+ next_packet_channel = 0;
target_peer = 0;
+ network_mode = MODE_NONE;
connection_status = CONNECTION_DISCONNECTED;
}
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index ea7c60036b..3f608200fd 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -48,6 +48,13 @@ private:
CH_RESERVED_MAX = 3
};
+ enum NetworkMode {
+ MODE_NONE,
+ MODE_SERVER,
+ MODE_CLIENT,
+ MODE_MESH,
+ };
+
class ConnectedPeer : public RefCounted {
public:
Ref<WebRTCPeerConnection> connection;
@@ -67,19 +74,25 @@ private:
int client_count = 0;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
int next_packet_peer = 0;
- bool server_compat = false;
+ int next_packet_channel = 0;
+ NetworkMode network_mode = MODE_NONE;
HashMap<int, Ref<ConnectedPeer>> peer_map;
+ List<TransferMode> channels_modes;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer();
+ Ref<ConnectedPeer> _get_next_peer();
+ Error _initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config = Array());
public:
WebRTCMultiplayerPeer() {}
~WebRTCMultiplayerPeer();
- Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
+ Error create_server(Array p_channels_config = Array());
+ Error create_client(int p_self_id, Array p_channels_config = Array());
+ Error create_mesh(int p_self_id, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id);
@@ -98,8 +111,11 @@ public:
int get_unique_id() const override;
int get_packet_peer() const override;
+ int get_packet_channel() const override;
+ TransferMode get_packet_mode() const override;
bool is_server() const override;
+ bool is_server_relay_supported() const override;
void poll() override;
diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index c314ebd049..0202c5bac7 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -142,12 +142,21 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff
Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(get_connection_status() != CONNECTION_CONNECTED, ERR_UNCONFIGURED);
- Vector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), target_peer, p_buffer, p_buffer_size);
-
if (is_server()) {
- return _server_relay(1, target_peer, &(buffer.ptr()[0]), buffer.size());
+ if (target_peer > 0) {
+ ERR_FAIL_COND_V_MSG(!peers_map.has(target_peer), ERR_INVALID_PARAMETER, "Peer not found: " + itos(target_peer));
+ get_peer(target_peer)->put_packet(p_buffer, p_buffer_size);
+ } else {
+ for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
+ if (target_peer && -target_peer == E.key) {
+ continue; // Excluded.
+ }
+ E.value->put_packet(p_buffer, p_buffer_size);
+ }
+ }
+ return OK;
} else {
- return get_peer(1)->put_packet(&(buffer.ptr()[0]), buffer.size());
+ return get_peer(1)->put_packet(p_buffer, p_buffer_size);
}
}
@@ -219,8 +228,41 @@ void WebSocketMultiplayerPeer::_poll_client() {
peer->poll(); // Update state and fetch packets.
WebSocketPeer::State ready_state = peer->get_ready_state();
if (ready_state == WebSocketPeer::STATE_OPEN) {
- while (peer->get_available_packet_count()) {
- _process_multiplayer(peer, 1);
+ if (connection_status == CONNECTION_CONNECTING) {
+ if (peer->get_available_packet_count() > 0) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = peer->get_packet(&in_buffer, size);
+ if (err != OK || size != 4) {
+ peer->close(); // Will cause connection error on next poll.
+ ERR_FAIL_MSG("Invalid ID received from server");
+ }
+ unique_id = *((int32_t *)in_buffer);
+ if (unique_id < 2) {
+ peer->close(); // Will cause connection error on next poll.
+ ERR_FAIL_MSG("Invalid ID received from server");
+ }
+ connection_status = CONNECTION_CONNECTED;
+ emit_signal("peer_connected", 1);
+ emit_signal("connection_succeeded");
+ } else {
+ return; // Still waiting for an ID.
+ }
+ }
+ int pkts = peer->get_available_packet_count();
+ while (pkts > 0 && peer->get_ready_state() == WebSocketPeer::STATE_OPEN) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = peer->get_packet(&in_buffer, size);
+ ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(size <= 0);
+ Packet packet;
+ packet.data = (uint8_t *)memalloc(size);
+ memcpy(packet.data, in_buffer, size);
+ packet.size = size;
+ packet.source = 1;
+ incoming_packets.push_back(packet);
+ pkts--;
}
} else if (peer->get_ready_state() == WebSocketPeer::STATE_CLOSED) {
if (connection_status == CONNECTION_CONNECTED) {
@@ -278,9 +320,14 @@ void WebSocketMultiplayerPeer::_poll_server() {
// The user does not want new connections, dropping it.
continue;
}
- peers_map[id] = peer.ws;
- _send_ack(peer.ws, id);
- emit_signal("peer_connected", id);
+ int32_t peer_id = id;
+ Error err = peer.ws->put_packet((const uint8_t *)&peer_id, sizeof(peer_id));
+ if (err == OK) {
+ peers_map[id] = peer.ws;
+ emit_signal("peer_connected", id);
+ } else {
+ ERR_PRINT("Failed to send ID to newly connected peer.");
+ }
continue;
} else if (state == WebSocketPeer::STATE_CONNECTING) {
continue; // Still connecting.
@@ -338,8 +385,19 @@ void WebSocketMultiplayerPeer::_poll_server() {
}
// Fetch packets
int pkts = ws->get_available_packet_count();
- while (pkts && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) {
- _process_multiplayer(ws, id);
+ while (pkts > 0 && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = ws->get_packet(&in_buffer, size);
+ if (err != OK || size <= 0) {
+ break;
+ }
+ Packet packet;
+ packet.data = (uint8_t *)memalloc(size);
+ memcpy(packet.data, in_buffer, size);
+ packet.size = size;
+ packet.source = E.key;
+ incoming_packets.push_back(packet);
pkts--;
}
}
@@ -371,180 +429,6 @@ Ref<WebSocketPeer> WebSocketMultiplayerPeer::get_peer(int p_id) const {
return peers_map[p_id];
}
-void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) {
- ERR_FAIL_COND(!p_peer.is_valid());
- ERR_FAIL_COND(p_peer->get_ready_state() != WebSocketPeer::STATE_OPEN);
-
- Vector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4);
- p_peer->put_packet(&(message.ptr()[0]), message.size());
-}
-
-Vector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
- Vector<uint8_t> out;
- out.resize(PROTO_SIZE + p_data_size);
-
- uint8_t *w = out.ptrw();
- memcpy(&w[0], &p_type, 1);
- memcpy(&w[1], &p_from, 4);
- memcpy(&w[5], &p_to, 4);
- memcpy(&w[PROTO_SIZE], p_data, p_data_size);
-
- return out;
-}
-
-void WebSocketMultiplayerPeer::_send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id) {
- ERR_FAIL_COND(p_peer.is_null());
- // First of all, confirm the ID!
- _send_sys(p_peer, SYS_ID, p_peer_id);
-
- // Then send the server peer (which will trigger connection_succeded in client)
- _send_sys(p_peer, SYS_ADD, 1);
-
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
- ERR_CONTINUE(E.value.is_null());
-
- int32_t id = E.key;
- if (p_peer_id == id) {
- continue; // Skip the newly added peer (already confirmed)
- }
-
- // Send new peer to others
- _send_sys(E.value, SYS_ADD, p_peer_id);
- // Send others to new peer
- _send_sys(E.value, SYS_ADD, id);
- }
-}
-
-void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
- int32_t id = E.key;
- if (p_peer_id != id) {
- _send_sys(E.value, SYS_DEL, p_peer_id);
- }
- }
-}
-
-void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) {
- Packet packet;
- packet.data = (uint8_t *)memalloc(p_data_size);
- packet.size = p_data_size;
- packet.source = p_source;
- packet.destination = p_dest;
- memcpy(packet.data, &p_data[PROTO_SIZE], p_data_size);
- incoming_packets.push_back(packet);
-}
-
-Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) {
- if (p_to == 1) {
- return OK; // Will not send to self
-
- } else if (p_to == 0) {
- for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
- if (E.key != p_from) {
- E.value->put_packet(p_buffer, p_buffer_size);
- }
- }
- return OK; // Sent to all but sender
-
- } else if (p_to < 0) {
- for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
- if (E.key != p_from && E.key != -p_to) {
- E.value->put_packet(p_buffer, p_buffer_size);
- }
- }
- return OK; // Sent to all but sender and excluded
-
- } else {
- ERR_FAIL_COND_V(p_to == p_from, FAILED);
-
- Ref<WebSocketPeer> peer_to = get_peer(p_to);
- ERR_FAIL_COND_V(peer_to.is_null(), FAILED);
-
- return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
- }
-}
-
-void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) {
- ERR_FAIL_COND(!p_peer.is_valid());
-
- const uint8_t *in_buffer;
- int size = 0;
- int data_size = 0;
-
- Error err = p_peer->get_packet(&in_buffer, size);
-
- ERR_FAIL_COND(err != OK);
- ERR_FAIL_COND(size < PROTO_SIZE);
-
- data_size = size - PROTO_SIZE;
-
- uint8_t type = 0;
- uint32_t from = 0;
- int32_t to = 0;
- memcpy(&type, in_buffer, 1);
- memcpy(&from, &in_buffer[1], 4);
- memcpy(&to, &in_buffer[5], 4);
-
- if (is_server()) { // Server can resend
-
- ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
- ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
-
- if (to == 1) {
- // This is for the server
- _store_pkt(from, to, in_buffer, data_size);
-
- } else if (to == 0) {
- // Broadcast, for us too
- _store_pkt(from, to, in_buffer, data_size);
-
- } else if (to < -1) {
- // All but one, for us if not excluded
- _store_pkt(from, to, in_buffer, data_size);
- }
- // Relay if needed (i.e. "to" includes a peer that is not the server)
- _server_relay(from, to, in_buffer, size);
-
- } else {
- if (type == SYS_NONE) {
- // Payload message
- _store_pkt(from, to, in_buffer, data_size);
- return;
- }
-
- // System message
- ERR_FAIL_COND(data_size < 4);
- int id = 0;
- memcpy(&id, &in_buffer[PROTO_SIZE], 4);
-
- switch (type) {
- case SYS_ADD: // Add peer
- if (id != 1) {
- peers_map[id] = Ref<WebSocketPeer>();
- } else {
- pending_peers.clear();
- connection_status = CONNECTION_CONNECTED;
- }
- emit_signal(SNAME("peer_connected"), id);
- if (id == 1) { // We just connected to the server
- emit_signal(SNAME("connection_succeeded"));
- }
- break;
-
- case SYS_DEL: // Remove peer
- emit_signal(SNAME("peer_disconnected"), id);
- peers_map.erase(id);
- break;
- case SYS_ID: // Hello, server assigned ID
- unique_id = id;
- break;
- default:
- ERR_FAIL_MSG("Invalid multiplayer message.");
- break;
- }
- }
-}
-
void WebSocketMultiplayerPeer::set_supported_protocols(const Vector<String> &p_protocols) {
peer_config->set_supported_protocols(p_protocols);
}
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 8e7b118faa..ebe013a7bf 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -42,9 +42,6 @@ class WebSocketMultiplayerPeer : public MultiplayerPeer {
GDCLASS(WebSocketMultiplayerPeer, MultiplayerPeer);
private:
- Vector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
- void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size);
- Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size);
Ref<WebSocketPeer> _create_peer();
protected:
@@ -59,7 +56,6 @@ protected:
struct Packet {
int source = 0;
- int destination = 0;
uint8_t *data = nullptr;
uint32_t size = 0;
};
@@ -90,20 +86,18 @@ protected:
static void _bind_methods();
- void _send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id);
- void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id);
- void _send_del(int32_t p_peer_id);
- void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id);
-
void _poll_client();
void _poll_server();
void _clear();
public:
/* MultiplayerPeer */
- void set_target_peer(int p_target_peer) override;
- int get_packet_peer() const override;
- int get_unique_id() const override;
+ virtual void set_target_peer(int p_target_peer) override;
+ virtual int get_packet_peer() const override;
+ virtual int get_packet_channel() const override { return 0; }
+ virtual TransferMode get_packet_mode() const override { return TRANSFER_MODE_RELIABLE; }
+ virtual int get_unique_id() const override;
+ virtual bool is_server_relay_supported() const override { return true; }
virtual int get_max_packet_size() const override;
virtual bool is_server() const override;
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 6f8efbf069..08369e735d 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -440,8 +440,8 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
}
@@ -485,7 +485,7 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
rect_changed_callback.callp(reinterpret_cast<const Variant **>(&sizep), 1, ret, ce);
}
-DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
// TODO: rendering_driver is broken, change when different drivers are supported again
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 6e14ba3e23..a6bc88e048 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -194,7 +194,7 @@ public:
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_android_driver();
@@ -204,7 +204,7 @@ public:
virtual Point2i mouse_get_position() const override;
virtual MouseButton mouse_get_button_state() const override;
- DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
~DisplayServerAndroid();
};
diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h
index f3624f24ab..da06ff7431 100644
--- a/platform/ios/display_server_ios.h
+++ b/platform/ios/display_server_ios.h
@@ -73,7 +73,7 @@ class DisplayServerIOS : public DisplayServer {
void perform_event(const Ref<InputEvent> &p_event);
- DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
~DisplayServerIOS();
public:
@@ -82,7 +82,7 @@ public:
static DisplayServerIOS *get_singleton();
static void register_ios_driver();
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
// MARK: - Events
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index cf0fae4997..b13561c511 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -50,7 +50,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() {
return (DisplayServerIOS *)DisplayServer::get_singleton();
}
-DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
// Init TTS
@@ -152,8 +152,8 @@ DisplayServerIOS::~DisplayServerIOS() {
#endif
}
-DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
}
Vector<String> DisplayServerIOS::get_rendering_drivers_func() {
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 162db675b0..88c6500e10 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -126,7 +126,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
case FEATURE_WINDOW_TRANSPARENCY:
//case FEATURE_HIDPI:
case FEATURE_ICON:
- case FEATURE_NATIVE_ICON:
+ //case FEATURE_NATIVE_ICON:
case FEATURE_SWAP_BUFFERS:
#ifdef DBUS_ENABLED
case FEATURE_KEEP_SCREEN_ON:
@@ -4432,8 +4432,8 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
@@ -4693,7 +4693,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V
return id;
}
-DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -4920,6 +4920,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
+
+ if (p_position != nullptr) {
+ window_position = *p_position;
+ }
+
WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution));
if (main_window == INVALID_WINDOW_ID) {
r_error = ERR_CANT_CREATE;
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 5723abc751..9ef8f71c05 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -444,12 +444,12 @@ public:
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref<Image> &p_icon) override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_x11_driver();
- DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
~DisplayServerX11();
};
diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 91a260182e..fa5e20891c 100644
--- a/platform/linuxbsd/godot_linuxbsd.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -69,6 +69,7 @@ int main(int argc, char *argv[]) {
}
if (Main::start()) {
+ os.set_exit_code(EXIT_SUCCESS);
os.run(); // it is actually the OS that decides how to run
}
Main::cleanup();
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 995a904398..11b667fcef 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -246,6 +246,10 @@ String OS_LinuxBSD::get_version() const {
}
Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
+ if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) {
+ return Vector<String>();
+ }
+
const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970`
const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA`
const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970`
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 484b8ffebc..618da6b388 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -430,12 +430,12 @@ public:
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref<Image> &p_icon) override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_macos_driver();
- DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
~DisplayServerMacOS();
};
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index c7f49870f2..49633e6efa 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3405,8 +3405,8 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
[NSApp setApplicationIconImage:nsimg];
}
-DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_command;
@@ -3575,7 +3575,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) {
return closed;
}
-DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
@@ -3684,6 +3684,11 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
Point2i window_position(
screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2,
screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2);
+
+ if (p_position != nullptr) {
+ window_position = *p_position;
+ }
+
WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution));
ERR_FAIL_COND(main_window == INVALID_WINDOW_ID);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 2ee1edd2b4..10ed8b8343 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -738,11 +738,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &p_event) {
}
}
-DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
}
-DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) {
+DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) {
r_error = OK; // Always succeeds for now.
// Ensure the canvas ID.
diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h
index 85076b906f..3222e2483e 100644
--- a/platform/web/display_server_web.h
+++ b/platform/web/display_server_web.h
@@ -96,7 +96,7 @@ private:
static void _js_utterance_callback(int p_event, int p_id, int p_pos);
static Vector<String> get_rendering_drivers_func();
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
@@ -221,7 +221,7 @@ public:
virtual void swap_buffers() override;
static void register_web_driver();
- DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error);
+ DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error);
~DisplayServerWeb();
};
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 43d7208501..d99670243e 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2444,10 +2444,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return 0; // Jump back.
}
case WM_MOUSELEAVE: {
- old_invalid = true;
- windows[window_id].mouse_outside = true;
+ if (window_mouseover_id == window_id) {
+ old_invalid = true;
+ window_mouseover_id = INVALID_WINDOW_ID;
- _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
} break;
case WM_INPUT: {
@@ -2678,17 +2680,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- if (windows[window_id].mouse_outside) {
+ if (window_mouseover_id != window_id) {
// Mouse enter.
if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ // Leave previous window.
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
cursor_set_shape(c);
- windows[window_id].mouse_outside = false;
+ window_mouseover_id = window_id;
// Once-off notification, must call again.
track_mouse_leave_event(hWnd);
@@ -2779,17 +2785,29 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
- if (windows[window_id].mouse_outside) {
+ DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position());
+ if (!Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) {
+ // Don't consider the windowborder as part of the window.
+ over_id = INVALID_WINDOW_ID;
+ }
+ if (window_mouseover_id != over_id) {
// Mouse enter.
if (mouse_mode != MOUSE_MODE_CAPTURED) {
- _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ if (window_mouseover_id != INVALID_WINDOW_ID) {
+ // Leave previous window.
+ _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
+
+ if (over_id != INVALID_WINDOW_ID) {
+ _send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
}
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
cursor_set_shape(c);
- windows[window_id].mouse_outside = false;
+ window_mouseover_id = over_id;
// Once-off notification, must call again.
track_mouse_leave_event(hWnd);
@@ -2800,9 +2818,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
break;
}
+ DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup();
+ if (receiving_window_id == INVALID_WINDOW_ID) {
+ receiving_window_id = window_id;
+ }
Ref<InputEventMouseMotion> mm;
mm.instantiate();
- mm->set_window_id(window_id);
+ mm->set_window_id(receiving_window_id);
mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0);
mm->set_shift_pressed((wParam & MK_SHIFT) != 0);
mm->set_alt_pressed(alt_mem);
@@ -2859,9 +2881,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
old_x = mm->get_position().x;
old_y = mm->get_position().y;
- if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) {
- Input::get_singleton()->parse_input_event(mm);
- }
+
+ Input::get_singleton()->parse_input_event(mm);
} break;
case WM_LBUTTONDOWN:
@@ -3704,7 +3725,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
}
}
-DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
drop_events = false;
key_event_pos = 0;
@@ -3856,6 +3877,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
+ if (p_position != nullptr) {
+ window_position = *p_position;
+ }
+
WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution));
ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window.");
@@ -3919,8 +3944,8 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
return drivers;
}
-DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
+DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
+ DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error));
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 53cde001ae..8ac0086d69 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -323,6 +323,8 @@ class DisplayServerWindows : public DisplayServer {
LPARAM lParam;
};
+ WindowID window_mouseover_id = INVALID_WINDOW_ID;
+
KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
int key_event_pos;
@@ -398,7 +400,6 @@ class DisplayServerWindows : public DisplayServer {
Size2 window_rect;
Point2 last_pos;
- bool mouse_outside = true;
ObjectID instance_id;
@@ -623,11 +624,11 @@ public:
virtual void set_context(Context p_context) override;
- static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_windows_driver();
- DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
~DisplayServerWindows();
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 5ca064e523..a8911788fe 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -311,6 +311,10 @@ String OS_Windows::get_version() const {
}
Vector<String> OS_Windows::get_video_adapter_driver_info() const {
+ if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) {
+ return Vector<String>();
+ }
+
REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator; // Interface UUID
IWbemLocator *wbemLocator = NULL; // to get the services
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index f077f7f5e6..748fbbe2d1 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -104,7 +104,7 @@ void NavigationAgent2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
ADD_SIGNAL(MethodInfo("navigation_finished"));
- ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity")));
+ ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR2, "safe_velocity")));
}
void NavigationAgent2D::_notification(int p_what) {
@@ -478,8 +478,8 @@ void NavigationAgent2D::_request_repath() {
void NavigationAgent2D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
- emit_signal(SNAME("target_reached"));
target_reached = true;
+ emit_signal(SNAME("target_reached"));
}
}
}
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 39068fe83c..f64cabb75c 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -495,8 +495,8 @@ void NavigationAgent3D::_request_repath() {
void NavigationAgent3D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
- emit_signal(SNAME("target_reached"));
target_reached = true;
+ emit_signal(SNAME("target_reached"));
}
}
}
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 5f515acead..1743fa1838 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -253,9 +253,7 @@ Vector3 Node3D::get_global_rotation() const {
void Node3D::set_global_rotation(const Vector3 &p_euler_rad) {
Transform3D transform = get_global_transform();
- Basis new_basis = transform.get_basis();
- new_basis.set_euler(p_euler_rad);
- transform.set_basis(new_basis);
+ transform.basis = Basis::from_euler(p_euler_rad);
set_global_transform(transform);
}
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index 2013a93e26..6d4c18a69e 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -189,7 +189,7 @@ void VisibleOnScreenEnabler3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler3D::get_enable_node_path);
ADD_GROUP("Enabling", "enable_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,When Paused"), "set_enable_mode", "get_enable_mode");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path");
BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index b1c2676bc1..48853cde9c 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2051,7 +2051,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (is_shortcut_keys_enabled()) {
- // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE.
+ // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, CUT, COPY, PASTE.
if (k->is_action("ui_text_select_all", true)) {
select_all();
accept_event();
@@ -2062,6 +2062,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
accept_event();
return;
}
+ if (k->is_action("ui_text_add_selection_for_next_occurrence", true)) {
+ add_selection_for_next_occurrence();
+ accept_event();
+ return;
+ }
if (k->is_action("ui_cut", true)) {
cut();
accept_event();
@@ -4832,6 +4837,45 @@ void TextEdit::select_word_under_caret(int p_caret) {
merge_overlapping_carets();
}
+void TextEdit::add_selection_for_next_occurrence() {
+ if (!selecting_enabled || !is_multiple_carets_enabled()) {
+ return;
+ }
+
+ if (text.size() == 1 && text[0].length() == 0) {
+ return;
+ }
+
+ // Always use the last caret, to correctly search for
+ // the next occurrence that comes after this caret.
+ int caret = get_caret_count() - 1;
+
+ if (!has_selection(caret)) {
+ select_word_under_caret(caret);
+ return;
+ }
+
+ const String &highlighted_text = get_selected_text(caret);
+ int column = get_selection_from_column(caret) + 1;
+ int line = get_caret_line(caret);
+
+ const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column);
+
+ if (next_occurrence.x == -1 || next_occurrence.y == -1) {
+ return;
+ }
+
+ int to_column = get_selection_to_column(caret) + 1;
+ int end = next_occurrence.x + (to_column - column);
+ int new_caret = add_caret(next_occurrence.y, end);
+
+ if (new_caret != -1) {
+ select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret);
+ adjust_viewport_to_caret(new_caret);
+ merge_overlapping_carets();
+ }
+}
+
void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret) {
ERR_FAIL_INDEX(p_caret, carets.size());
if (!selecting_enabled) {
@@ -6000,6 +6044,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all);
ClassDB::bind_method(D_METHOD("select_word_under_caret", "caret_index"), &TextEdit::select_word_under_caret, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("add_selection_for_next_occurrence"), &TextEdit::add_selection_for_next_occurrence);
ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column", "caret_index"), &TextEdit::select, DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_selection", "caret_index"), &TextEdit::has_selection, DEFVAL(-1));
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index e4af621b73..86b838e387 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -851,6 +851,7 @@ public:
void select_all();
void select_word_under_caret(int p_caret = -1);
+ void add_selection_for_next_occurrence();
void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret = 0);
bool has_selection(int p_caret = -1) const;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 1dc28d91c5..840913d466 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -986,8 +986,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat);
ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat);
- ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children);
- ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children);
+ ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode);
+ ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode);
GDVIRTUAL_BIND(_draw);
@@ -997,7 +997,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only,Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Texture", "texture_");
@@ -1035,6 +1035,11 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED);
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR);
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX);
+
+ BIND_ENUM_CONSTANT(CLIP_CHILDREN_DISABLED);
+ BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY);
+ BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW);
+ BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX);
}
Transform2D CanvasItem::get_canvas_transform() const {
@@ -1185,20 +1190,23 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) {
notify_property_list_changed();
}
-void CanvasItem::set_clip_children(bool p_enabled) {
- if (clip_children == p_enabled) {
+void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) {
+ ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX);
+
+ if (clip_children_mode == p_clip_mode) {
return;
}
- clip_children = p_enabled;
+ clip_children_mode = p_clip_mode;
if (Object::cast_to<CanvasGroup>(this) != nullptr) {
//avoid accidental bugs, make this not work on CanvasGroup
return;
}
- RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED);
+
+ RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CanvasGroupMode(clip_children_mode));
}
-bool CanvasItem::is_clipping_children() const {
- return clip_children;
+CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const {
+ return clip_children_mode;
}
CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index ca91fff9ea..565ea930ce 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -66,8 +66,16 @@ public:
TEXTURE_REPEAT_MAX,
};
+ enum ClipChildrenMode {
+ CLIP_CHILDREN_DISABLED,
+ CLIP_CHILDREN_ONLY,
+ CLIP_CHILDREN_AND_DRAW,
+ CLIP_CHILDREN_MAX,
+ };
+
private:
- mutable SelfList<Node> xform_change;
+ mutable SelfList<Node>
+ xform_change;
RID canvas_item;
StringName canvas_group;
@@ -85,7 +93,6 @@ private:
Window *window = nullptr;
bool visible = true;
bool parent_visible_in_tree = false;
- bool clip_children = false;
bool pending_update = false;
bool top_level = false;
bool drawing = false;
@@ -95,6 +102,8 @@ private:
bool notify_local_transform = false;
bool notify_transform = false;
+ ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED;
+
RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
@@ -202,8 +211,8 @@ public:
void queue_redraw();
void move_to_front();
- void set_clip_children(bool p_enabled);
- bool is_clipping_children() const;
+ void set_clip_children_mode(ClipChildrenMode p_clip_mode);
+ ClipChildrenMode get_clip_children_mode() const;
virtual void set_light_mask(int p_light_mask);
int get_light_mask() const;
@@ -326,6 +335,7 @@ public:
VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
+VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode)
class CanvasTexture : public Texture2D {
GDCLASS(CanvasTexture, Texture2D);
diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp
index 9b63118f7c..462dc1babb 100644
--- a/scene/main/multiplayer_peer.cpp
+++ b/scene/main/multiplayer_peer.cpp
@@ -78,6 +78,10 @@ bool MultiplayerPeer::is_refusing_new_connections() const {
return refuse_connections;
}
+bool MultiplayerPeer::is_server_relay_supported() const {
+ return false;
+}
+
void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
@@ -86,6 +90,8 @@ void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
+ ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel);
+ ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
@@ -96,6 +102,8 @@ void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
+ ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
@@ -177,6 +185,14 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const {
return MultiplayerPeer::is_refusing_new_connections();
}
+bool MultiplayerPeerExtension::is_server_relay_supported() const {
+ bool can_relay;
+ if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) {
+ return can_relay;
+ }
+ return MultiplayerPeer::is_server_relay_supported();
+}
+
void MultiplayerPeerExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h
index ab7483ece5..63ce66871e 100644
--- a/scene/main/multiplayer_peer.h
+++ b/scene/main/multiplayer_peer.h
@@ -74,10 +74,13 @@ public:
virtual TransferMode get_transfer_mode() const;
virtual void set_refuse_new_connections(bool p_enable);
virtual bool is_refusing_new_connections() const;
+ virtual bool is_server_relay_supported() const;
virtual void set_target_peer(int p_peer_id) = 0;
virtual int get_packet_peer() const = 0;
+ virtual TransferMode get_packet_mode() const = 0;
+ virtual int get_packet_channel() const = 0;
virtual bool is_server() const = 0;
@@ -123,12 +126,17 @@ public:
virtual bool is_refusing_new_connections() const override;
GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional.
+ virtual bool is_server_relay_supported() const override;
+ GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional.
+
EXBIND1(set_transfer_channel, int);
EXBIND0RC(int, get_transfer_channel);
EXBIND1(set_transfer_mode, TransferMode);
EXBIND0RC(TransferMode, get_transfer_mode);
EXBIND1(set_target_peer, int);
EXBIND0RC(int, get_packet_peer);
+ EXBIND0RC(TransferMode, get_packet_mode);
+ EXBIND0RC(int, get_packet_channel);
EXBIND0RC(bool, is_server);
EXBIND0(poll);
EXBIND0RC(int, get_unique_id);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 270e5b7025..5ff9a29792 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -896,7 +896,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
call_lock++;
- Vector<Node *> no_context_nodes;
+ Vector<ObjectID> no_context_node_ids; // Nodes may be deleted due to this shortcut input.
for (int i = gr_node_count - 1; i >= 0; i--) {
if (p_viewport->is_input_handled()) {
@@ -922,7 +922,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
// If calling shortcut input on a control, ensure it respects the shortcut context.
// Shortcut context (based on focus) only makes sense for controls (UI), so don't need to worry about it for nodes
if (c->get_shortcut_context() == nullptr) {
- no_context_nodes.append(n);
+ no_context_node_ids.append(n->get_instance_id());
continue;
}
if (!c->is_focus_owner_in_shortcut_context()) {
@@ -941,8 +941,11 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
}
- for (Node *n : no_context_nodes) {
- n->_call_shortcut_input(p_input);
+ for (const ObjectID &id : no_context_node_ids) {
+ Node *n = Object::cast_to<Node>(ObjectDB::get_instance(id));
+ if (n) {
+ n->_call_shortcut_input(p_input);
+ }
}
call_lock--;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index a2963eadd5..ad40346c36 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1263,6 +1263,7 @@ void Viewport::_gui_show_tooltip() {
Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset");
Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size());
+ r.size = r.size.min(panel->get_max_size());
Window *window = gui.tooltip_popup->get_parent_visible_window();
Rect2i vr = window->get_usable_parent_rect();
@@ -1403,10 +1404,6 @@ Control *Viewport::gui_find_control(const Point2 &p_global) {
}
Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform) {
- if (Object::cast_to<Viewport>(p_node)) {
- return nullptr;
- }
-
if (!p_node->is_visible()) {
return nullptr; // Canvas item hidden, discard.
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 8fb497113d..ebb11ecee8 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -587,6 +587,18 @@ bool Window::is_visible() const {
}
void Window::_update_window_size() {
+ // Force window to respect size limitations of rendering server
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ if (rendering_server) {
+ Size2i max_window_size = rendering_server->get_maximum_viewport_size();
+
+ if (max_window_size != Size2i()) {
+ size = size.min(max_window_size);
+ min_size = min_size.min(max_window_size);
+ max_size = max_size.min(max_window_size);
+ }
+ }
+
Size2i size_limit;
if (wrap_controls) {
size_limit = get_contents_minimum_size();
@@ -1828,6 +1840,11 @@ void Window::_bind_methods() {
}
Window::Window() {
+ RenderingServer *rendering_server = RenderingServer::get_singleton();
+ if (rendering_server) {
+ max_size = rendering_server->get_maximum_viewport_size();
+ }
+
theme_owner = memnew(ThemeOwner);
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
}
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index ebdaaaa95f..23bd8a4be4 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -78,7 +78,7 @@ float Environment::get_sky_custom_fov() const {
void Environment::set_sky_rotation(const Vector3 &p_rotation) {
bg_sky_rotation = p_rotation;
- RS::get_singleton()->environment_set_sky_orientation(environment, Basis(p_rotation));
+ RS::get_singleton()->environment_set_sky_orientation(environment, Basis::from_euler(p_rotation));
}
Vector3 Environment::get_sky_rotation() const {
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index fbcf140925..b994498dbf 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -653,7 +653,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si
Image::Format format = Image::Format(f->get_32());
if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
- //look for a PNG or WEBP file inside
+ //look for a PNG or WebP file inside
int sw = w;
int sh = h;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 4b97bede56..f079085543 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -865,9 +865,9 @@ Vector<String> DisplayServer::get_create_function_rendering_drivers(int p_index)
return server_create_functions[p_index].get_rendering_drivers_function();
}
-DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr);
- return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error);
+ return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error);
}
void DisplayServer::_input_set_mouse_mode(Input::MouseMode p_mode) {
diff --git a/servers/display_server.h b/servers/display_server.h
index 31ebf12531..55e9fb55dc 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -72,7 +72,7 @@ public:
WINDOW_VIEW,
};
- typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Size2i &, Error &r_error);
+ typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, Error &r_error);
typedef Vector<String> (*GetRenderingDriversFunction)();
private:
@@ -483,7 +483,7 @@ public:
static int get_create_function_count();
static const char *get_create_function_name(int p_index);
static Vector<String> get_create_function_rendering_drivers(int p_index);
- static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error);
DisplayServer();
~DisplayServer();
diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h
index 3853ff4fdc..e1a27b6137 100644
--- a/servers/display_server_headless.h
+++ b/servers/display_server_headless.h
@@ -45,7 +45,7 @@ private:
return drivers;
}
- static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerHeadless());
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index cd61ceab62..1443cd166b 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -817,48 +817,78 @@ GodotCylinderShape3D::GodotCylinderShape3D() {}
/********** CONVEX POLYGON *************/
void GodotConvexPolygonShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {
- int vertex_count = mesh.vertices.size();
+ uint32_t vertex_count = mesh.vertices.size();
if (vertex_count == 0) {
return;
}
const Vector3 *vrts = &mesh.vertices[0];
- for (int i = 0; i < vertex_count; i++) {
- real_t d = p_normal.dot(p_transform.xform(vrts[i]));
+ if (vertex_count > 3 * extreme_vertices.size()) {
+ // For a large mesh, two calls to get_support() is faster than a full
+ // scan over all vertices.
- if (i == 0 || d > r_max) {
- r_max = d;
- }
- if (i == 0 || d < r_min) {
- r_min = d;
+ Vector3 n = p_transform.basis.xform_inv(p_normal).normalized();
+ r_min = p_normal.dot(p_transform.xform(get_support(-n)));
+ r_max = p_normal.dot(p_transform.xform(get_support(n)));
+ } else {
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ real_t d = p_normal.dot(p_transform.xform(vrts[i]));
+
+ if (i == 0 || d > r_max) {
+ r_max = d;
+ }
+ if (i == 0 || d < r_min) {
+ r_min = d;
+ }
}
}
}
Vector3 GodotConvexPolygonShape3D::get_support(const Vector3 &p_normal) const {
- Vector3 n = p_normal;
-
- int vert_support_idx = -1;
- real_t support_max = 0;
-
- int vertex_count = mesh.vertices.size();
- if (vertex_count == 0) {
+ if (mesh.vertices.size() == 0) {
return Vector3();
}
- const Vector3 *vrts = &mesh.vertices[0];
-
- for (int i = 0; i < vertex_count; i++) {
- real_t d = n.dot(vrts[i]);
+ // Find an initial guess for the support vertex by checking the ones we
+ // found in _setup().
- if (i == 0 || d > support_max) {
- support_max = d;
- vert_support_idx = i;
+ int best_vertex = -1;
+ real_t max_support = 0.0;
+ for (uint32_t i = 0; i < extreme_vertices.size(); i++) {
+ real_t s = p_normal.dot(mesh.vertices[extreme_vertices[i]]);
+ if (best_vertex == -1 || s > max_support) {
+ best_vertex = extreme_vertices[i];
+ max_support = s;
}
}
+ if (extreme_vertices.size() == mesh.vertices.size()) {
+ // We've already checked every vertex, so we can return now.
+ return mesh.vertices[best_vertex];
+ }
+
+ // Move along the surface until we reach the true support vertex.
- return vrts[vert_support_idx];
+ int last_vertex = -1;
+ while (true) {
+ int next_vertex = -1;
+ for (uint32_t i = 0; i < vertex_neighbors[best_vertex].size(); i++) {
+ int vert = vertex_neighbors[best_vertex][i];
+ if (vert != last_vertex) {
+ real_t s = p_normal.dot(mesh.vertices[vert]);
+ if (s > max_support) {
+ next_vertex = vert;
+ max_support = s;
+ break;
+ }
+ }
+ }
+ if (next_vertex == -1) {
+ return mesh.vertices[best_vertex];
+ }
+ last_vertex = best_vertex;
+ best_vertex = next_vertex;
+ }
}
void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {
@@ -1067,6 +1097,43 @@ void GodotConvexPolygonShape3D::_setup(const Vector<Vector3> &p_vertices) {
}
configure(_aabb);
+
+ // Pre-compute the extreme vertices in 26 directions. This will be used
+ // to speed up get_support() by letting us quickly get a good guess for
+ // the support vertex.
+
+ for (int x = -1; x < 2; x++) {
+ for (int y = -1; y < 2; y++) {
+ for (int z = -1; z < 2; z++) {
+ if (x != 0 || y != 0 || z != 0) {
+ Vector3 dir(x, y, z);
+ dir.normalize();
+ real_t max_support = 0.0;
+ int best_vertex = -1;
+ for (uint32_t i = 0; i < mesh.vertices.size(); i++) {
+ real_t s = dir.dot(mesh.vertices[i]);
+ if (best_vertex == -1 || s > max_support) {
+ best_vertex = i;
+ max_support = s;
+ }
+ }
+ if (extreme_vertices.find(best_vertex) == -1)
+ extreme_vertices.push_back(best_vertex);
+ }
+ }
+ }
+ }
+
+ // Record all the neighbors of each vertex. This is used in get_support().
+
+ if (extreme_vertices.size() < mesh.vertices.size()) {
+ vertex_neighbors.resize(mesh.vertices.size());
+ for (uint32_t i = 0; i < mesh.edges.size(); i++) {
+ Geometry3D::MeshData::Edge &edge = mesh.edges[i];
+ vertex_neighbors[edge.vertex_a].push_back(edge.vertex_b);
+ vertex_neighbors[edge.vertex_b].push_back(edge.vertex_a);
+ }
+ }
}
void GodotConvexPolygonShape3D::set_data(const Variant &p_data) {
diff --git a/servers/physics_3d/godot_shape_3d.h b/servers/physics_3d/godot_shape_3d.h
index 1fc8f7c711..dc8e34e2bc 100644
--- a/servers/physics_3d/godot_shape_3d.h
+++ b/servers/physics_3d/godot_shape_3d.h
@@ -277,6 +277,8 @@ public:
struct GodotConvexPolygonShape3D : public GodotShape3D {
Geometry3D::MeshData mesh;
+ LocalVector<int> extreme_vertices;
+ LocalVector<LocalVector<int>> vertex_neighbors;
void _setup(const Vector<Vector3> &p_vertices);
diff --git a/servers/rendering/dummy/storage/material_storage.h b/servers/rendering/dummy/storage/material_storage.h
index ed8fefc558..b4e0a21ee1 100644
--- a/servers/rendering/dummy/storage/material_storage.h
+++ b/servers/rendering/dummy/storage/material_storage.h
@@ -54,7 +54,7 @@ public:
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override { return 0; }
virtual void global_shader_parameters_instance_free(RID p_instance) override {}
- virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override {}
+ virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override {}
/* SHADER API */
diff --git a/servers/rendering/dummy/storage/utilities.h b/servers/rendering/dummy/storage/utilities.h
index cb7b2a2b63..a42ab0d814 100644
--- a/servers/rendering/dummy/storage/utilities.h
+++ b/servers/rendering/dummy/storage/utilities.h
@@ -109,6 +109,8 @@ public:
virtual String get_video_adapter_vendor() const override { return String(); }
virtual RenderingDevice::DeviceType get_video_adapter_type() const override { return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER; }
virtual String get_video_adapter_api_version() const override { return String(); }
+
+ virtual Size2i get_maximum_viewport_size() const override { return Size2i(); };
};
} // namespace RendererDummy
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index 53237c1dfb..a14228eb3d 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -357,8 +357,8 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons
copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE;
}
- copy.push_constant.section[0] = 0;
- copy.push_constant.section[1] = 0;
+ copy.push_constant.section[0] = p_rect.position.x;
+ copy.push_constant.section[1] = p_rect.position.y;
copy.push_constant.section[2] = p_rect.size.width;
copy.push_constant.section[3] = p_rect.size.height;
copy.push_constant.target[0] = p_rect.position.x;
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index 59a8cdf30a..ceed287184 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -963,8 +963,8 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con
distance = 1.0;
}
- uint32_t indices;
- surf->sort.lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, &indices);
+ uint32_t indices = 0;
+ surf->sort.lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, indices);
if (p_render_data->render_info) {
indices = _indices_to_primitives(surf->primitive, indices);
if (p_render_list == RENDER_LIST_OPAQUE) { //opaque
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 2dfdb302dc..f76d016ae7 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -1830,8 +1830,8 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const
distance = 1.0;
}
- uint32_t indices;
- surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, &indices);
+ uint32_t indices = 0;
+ surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, indices);
if (p_render_data->render_info) {
indices = _indices_to_primitives(surf->primitive, indices);
if (p_render_list == RENDER_LIST_OPAQUE) { //opaque
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 5728444312..dc0214e307 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1111,8 +1111,20 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
- if (material.is_null() && ci->canvas_group != nullptr) {
- material = default_canvas_group_material;
+ if (ci->canvas_group != nullptr) {
+ if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ if (!p_to_backbuffer) {
+ material = default_clip_children_material;
+ }
+ } else {
+ if (material.is_null()) {
+ if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) {
+ material = default_clip_children_material;
+ } else {
+ material = default_canvas_group_material;
+ }
+ }
+ }
}
if (material != prev_material) {
@@ -1437,10 +1449,12 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
_render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
item_count = 0;
- Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
-
- if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) {
+ if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) {
+ Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
+ if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
+ items[item_count++] = ci->canvas_group_owner;
+ }
} else if (!backbuffer_cleared) {
texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
backbuffer_cleared = true;
@@ -1620,7 +1634,7 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index,
projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
}
- Vector3 cam_target = Basis(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
ShadowRenderPushConstant push_constant;
@@ -2738,6 +2752,26 @@ void fragment() {
material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
}
+ {
+ default_clip_children_shader = material_storage->shader_allocate();
+ material_storage->shader_initialize(default_clip_children_shader);
+
+ material_storage->shader_set_code(default_clip_children_shader, R"(
+// Default clip children shader.
+
+shader_type canvas_item;
+
+void fragment() {
+ vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
+ COLOR.rgb = c.rgb;
+}
+)");
+ default_clip_children_material = material_storage->material_allocate();
+ material_storage->material_initialize(default_clip_children_material);
+
+ material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader);
+ }
+
static_assert(sizeof(PushConstant) == 128);
}
@@ -2789,6 +2823,9 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() {
material_storage->material_free(default_canvas_group_material);
material_storage->shader_free(default_canvas_group_shader);
+ material_storage->material_free(default_clip_children_material);
+ material_storage->shader_free(default_clip_children_shader);
+
{
if (state.canvas_state_buffer.is_valid()) {
RD::get_singleton()->free(state.canvas_state_buffer);
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 67db56d913..4f1f77af5e 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -421,6 +421,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
RID default_canvas_group_shader;
RID default_canvas_group_material;
+ RID default_clip_children_material;
+ RID default_clip_children_shader;
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 1a8a1f3aa3..293532bf2d 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -1447,18 +1447,24 @@ void fragment_shader(in SceneData scene_data) {
}
//finalize ambient light here
- ambient_light *= albedo.rgb;
- ambient_light *= ao;
+ {
+#if defined(AMBIENT_LIGHT_DISABLED)
+ ambient_light = vec3(0.0, 0.0, 0.0);
+#else
+ ambient_light *= albedo.rgb;
+ ambient_light *= ao;
+
+ if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) {
+ vec4 ssil = textureLod(sampler2D(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv, 0.0);
+ ambient_light *= 1.0 - ssil.a;
+ ambient_light += ssil.rgb * albedo.rgb;
+ }
+#endif // AMBIENT_LIGHT_DISABLED
+ }
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
- if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) {
- vec4 ssil = textureLod(sampler2D(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv, 0.0);
- ambient_light *= 1.0 - ssil.a;
- ambient_light += ssil.rgb * albedo.rgb;
- }
-
//this saves some VGPRs
vec3 f0 = F0(metallic, specular, albedo);
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 33fd4c35b1..9aeaa6d978 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -1172,8 +1172,14 @@ void main() {
} //Reflection probes
// finalize ambient light here
- ambient_light *= albedo.rgb;
- ambient_light *= ao;
+ {
+#if defined(AMBIENT_LIGHT_DISABLED)
+ ambient_light = vec3(0.0, 0.0, 0.0);
+#else
+ ambient_light *= albedo.rgb;
+ ambient_light *= ao;
+#endif // AMBIENT_LIGHT_DISABLED
+ }
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index f0b8d006cb..d8ef4baa41 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -85,7 +85,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
gui[j + 3] = 0; // ignored
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = v & 1 ? 1 : 0;
gui[1] = v & 2 ? 1 : 0;
}
@@ -112,7 +112,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
gui[j + 3] = 0; // ignored
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
@@ -141,7 +141,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy
}
}
} else {
- int v = value;
+ uint32_t v = value;
gui[0] = (v & 1) ? 1 : 0;
gui[1] = (v & 2) ? 1 : 0;
gui[2] = (v & 4) ? 1 : 0;
@@ -734,7 +734,7 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type,
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
- *gui = value[0].boolean ? 1 : 0;
+ gui[0] = value[0].boolean ? 1 : 0;
} break;
case ShaderLanguage::TYPE_BVEC2: {
uint32_t *gui = (uint32_t *)data;
@@ -2213,7 +2213,7 @@ void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) {
global_shader_uniforms.instance_buffer_pos.erase(p_instance);
}
-void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) {
+void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) {
if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) {
return; //just not allocated, ignore
}
@@ -2223,7 +2223,9 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i
return; //again, not allocated, ignore
}
ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES);
- ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported
+
+ Variant::Type value_type = p_value.get_type();
+ ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported
const ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = {
ShaderLanguage::TYPE_MAX, //nil
@@ -2249,9 +2251,23 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i
ShaderLanguage::TYPE_VEC4 //color
};
- ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()];
-
- ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported
+ ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX;
+ if (value_type == Variant::INT && p_flags_count > 0) {
+ switch (p_flags_count) {
+ case 1:
+ datatype = ShaderLanguage::TYPE_BVEC2;
+ break;
+ case 2:
+ datatype = ShaderLanguage::TYPE_BVEC3;
+ break;
+ case 3:
+ datatype = ShaderLanguage::TYPE_BVEC4;
+ break;
+ }
+ } else {
+ datatype = datatype_from_value[value_type];
+ }
+ ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported
pos += p_index;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h
index 19c2f39553..fb25e56ed3 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h
@@ -361,7 +361,7 @@ public:
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override;
virtual void global_shader_parameters_instance_free(RID p_instance) override;
- virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override;
+ virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override;
RID global_shader_uniforms_get_storage_buffer() const;
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index c8a33bd4d7..6a7400631d 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -398,13 +398,11 @@ public:
return s->index_count ? s->index_count : s->vertex_count;
}
- _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const {
+ _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
int32_t current_lod = -1;
- if (r_index_count) {
- *r_index_count = s->index_count;
- }
+ r_index_count = s->index_count;
for (uint32_t i = 0; i < s->lod_count; i++) {
float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold;
if (screen_size > p_mesh_lod_threshold) {
@@ -415,9 +413,7 @@ public:
if (current_lod == -1) {
return 0;
} else {
- if (r_index_count) {
- *r_index_count = s->lods[current_lod].index_count;
- }
+ r_index_count = s->lods[current_lod].index_count;
return current_lod + 1;
}
}
diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
index e517186955..4048c46d28 100644
--- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
@@ -321,3 +321,11 @@ RenderingDevice::DeviceType Utilities::get_video_adapter_type() const {
String Utilities::get_video_adapter_api_version() const {
return RenderingDevice::get_singleton()->get_device_api_version();
}
+
+Size2i Utilities::get_maximum_viewport_size() const {
+ RenderingDevice *device = RenderingDevice::get_singleton();
+
+ int max_x = device->limit_get(RenderingDevice::LIMIT_MAX_VIEWPORT_DIMENSIONS_X);
+ int max_y = device->limit_get(RenderingDevice::LIMIT_MAX_VIEWPORT_DIMENSIONS_Y);
+ return Size2i(max_x, max_y);
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.h b/servers/rendering/renderer_rd/storage_rd/utilities.h
index a80eb8510e..dda656c380 100644
--- a/servers/rendering/renderer_rd/storage_rd/utilities.h
+++ b/servers/rendering/renderer_rd/storage_rd/utilities.h
@@ -115,6 +115,8 @@ public:
virtual String get_video_adapter_vendor() const override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
virtual String get_video_adapter_api_version() const override;
+
+ virtual Size2i get_maximum_viewport_size() const override;
};
} // namespace RendererRD
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index d0cb46dee9..348f6e4695 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1449,8 +1449,23 @@ void RendererSceneCull::instance_geometry_set_shader_parameter(RID p_instance, c
} else {
E->value.value = p_value;
if (E->value.index >= 0 && instance->instance_allocated_shader_uniforms) {
+ int flags_count = 0;
+ if (E->value.info.hint == PROPERTY_HINT_FLAGS) {
+ // A small hack to detect boolean flags count and prevent overhead.
+ switch (E->value.info.hint_string.length()) {
+ case 3: // "x,y"
+ flags_count = 1;
+ break;
+ case 5: // "x,y,z"
+ flags_count = 2;
+ break;
+ case 7: // "x,y,z,w"
+ flags_count = 3;
+ break;
+ }
+ }
//update directly
- RSG::material_storage->global_shader_parameters_instance_update(p_instance, E->value.index, p_value);
+ RSG::material_storage->global_shader_parameters_instance_update(p_instance, E->value.index, p_value, flags_count);
}
}
}
@@ -3884,7 +3899,22 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) {
for (const KeyValue<StringName, Instance::InstanceShaderParameter> &E : p_instance->instance_shader_uniforms) {
if (E.value.value.get_type() != Variant::NIL) {
- RSG::material_storage->global_shader_parameters_instance_update(p_instance->self, E.value.index, E.value.value);
+ int flags_count = 0;
+ if (E.value.info.hint == PROPERTY_HINT_FLAGS) {
+ // A small hack to detect boolean flags count and prevent overhead.
+ switch (E.value.info.hint_string.length()) {
+ case 3: // "x,y"
+ flags_count = 1;
+ break;
+ case 5: // "x,y,z"
+ flags_count = 2;
+ break;
+ case 7: // "x,y,z,w"
+ flags_count = 3;
+ break;
+ }
+ }
+ RSG::material_storage->global_shader_parameters_instance_update(p_instance->self, E.value.index, E.value.value, flags_count);
}
}
} else {
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index dd190437a3..93118f39e7 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -974,6 +974,8 @@ void RenderingDevice::_bind_methods() {
BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X);
BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y);
BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z);
+ BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_X);
+ BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_Y);
BIND_ENUM_CONSTANT(MEMORY_TEXTURES);
BIND_ENUM_CONSTANT(MEMORY_BUFFERS);
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index ca30a30786..d0b85d8220 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1252,6 +1252,8 @@ public:
LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X,
LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y,
LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z,
+ LIMIT_MAX_VIEWPORT_DIMENSIONS_X,
+ LIMIT_MAX_VIEWPORT_DIMENSIONS_Y,
LIMIT_SUBGROUP_SIZE,
LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc.
LIMIT_SUBGROUP_OPERATIONS,
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 9103b0cf56..c0cd564308 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -331,6 +331,14 @@ bool RenderingServerDefault::is_low_end() const {
return RendererCompositor::is_low_end();
}
+Size2i RenderingServerDefault::get_maximum_viewport_size() const {
+ if (RSG::utilities) {
+ return RSG::utilities->get_maximum_viewport_size();
+ } else {
+ return Size2i();
+ }
+}
+
void RenderingServerDefault::_thread_exit() {
exit.set();
}
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index fe212151e3..a68c7ddf2c 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -994,6 +994,8 @@ public:
virtual void set_print_gpu_profile(bool p_enable) override;
+ virtual Size2i get_maximum_viewport_size() const override;
+
RenderingServerDefault(bool p_create_thread = false);
~RenderingServerDefault();
};
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 54cc7a011a..1606f537e8 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -373,16 +373,16 @@ void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const Str
static String _get_global_shader_uniform_from_type_and_index(const String &p_buffer, const String &p_index, ShaderLanguage::DataType p_type) {
switch (p_type) {
case ShaderLanguage::TYPE_BOOL: {
- return "(" + p_buffer + "[" + p_index + "].x != 0.0)";
+ return "(floatBitsToUint(" + p_buffer + "[" + p_index + "].x) != 0)";
}
case ShaderLanguage::TYPE_BVEC2: {
- return "(notEqual(" + p_buffer + "[" + p_index + "].xy, vec2(0.0)))";
+ return "bvec2(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y))";
}
case ShaderLanguage::TYPE_BVEC3: {
- return "(notEqual(" + p_buffer + "[" + p_index + "].xyz, vec3(0.0)))";
+ return "bvec3(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y), floatBitsToUint(" + p_buffer + "[" + p_index + "].z))";
}
case ShaderLanguage::TYPE_BVEC4: {
- return "(notEqual(" + p_buffer + "[" + p_index + "].xyzw, vec4(0.0)))";
+ return "bvec4(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y), floatBitsToUint(" + p_buffer + "[" + p_index + "].z), floatBitsToUint(" + p_buffer + "[" + p_index + "].w))";
}
case ShaderLanguage::TYPE_INT: {
return "floatBitsToInt(" + p_buffer + "[" + p_index + "].x)";
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index a92292209f..4d27c400f0 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -8635,12 +8635,15 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
} break;
case TK_HINT_SCREEN_TEXTURE: {
new_hint = ShaderNode::Uniform::HINT_SCREEN_TEXTURE;
+ --texture_uniforms;
} break;
case TK_HINT_NORMAL_ROUGHNESS_TEXTURE: {
new_hint = ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE;
+ --texture_uniforms;
} break;
case TK_HINT_DEPTH_TEXTURE: {
new_hint = ShaderNode::Uniform::HINT_DEPTH_TEXTURE;
+ --texture_uniforms;
} break;
case TK_FILTER_NEAREST: {
new_filter = FILTER_NEAREST;
diff --git a/servers/rendering/storage/material_storage.h b/servers/rendering/storage/material_storage.h
index 396668c9ed..a3de6343e5 100644
--- a/servers/rendering/storage/material_storage.h
+++ b/servers/rendering/storage/material_storage.h
@@ -53,7 +53,7 @@ public:
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) = 0;
virtual void global_shader_parameters_instance_free(RID p_instance) = 0;
- virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) = 0;
+ virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) = 0;
/* SHADER API */
virtual RID shader_allocate() = 0;
diff --git a/servers/rendering/storage/utilities.h b/servers/rendering/storage/utilities.h
index d240d58917..23a782c14a 100644
--- a/servers/rendering/storage/utilities.h
+++ b/servers/rendering/storage/utilities.h
@@ -181,6 +181,8 @@ public:
virtual String get_video_adapter_vendor() const = 0;
virtual RenderingDevice::DeviceType get_video_adapter_type() const = 0;
virtual String get_video_adapter_api_version() const = 0;
+
+ virtual Size2i get_maximum_viewport_size() const = 0;
};
#endif // RENDERER_UTILITIES_H
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index cf13118451..e1a3fe46be 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2636,7 +2636,8 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX);
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_DISABLED);
- BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_OPAQUE);
+ BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_ONLY);
+ BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_AND_DRAW);
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_TRANSPARENT);
/* CANVAS LIGHT */
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 6885d80301..36779690de 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1367,7 +1367,8 @@ public:
enum CanvasGroupMode {
CANVAS_GROUP_MODE_DISABLED,
- CANVAS_GROUP_MODE_OPAQUE,
+ CANVAS_GROUP_MODE_CLIP_ONLY,
+ CANVAS_GROUP_MODE_CLIP_AND_DRAW,
CANVAS_GROUP_MODE_TRANSPARENT,
};
@@ -1574,6 +1575,8 @@ public:
virtual void set_print_gpu_profile(bool p_enable) = 0;
+ virtual Size2i get_maximum_viewport_size() const = 0;
+
RenderingDevice *get_rendering_device() const;
RenderingDevice *create_local_rendering_device() const;
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 38b616cda0..181d9a8a54 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -124,7 +124,7 @@ TEST_CASE("[Image] Saving and loading") {
image_jpg->load_jpg_from_buffer(data_jpg) == OK,
"The JPG image should load successfully.");
- // Load WEBP
+ // Load WebP
Ref<Image> image_webp = memnew(Image());
Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err);
PackedByteArray data_webp;
@@ -132,7 +132,7 @@ TEST_CASE("[Image] Saving and loading") {
f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length());
CHECK_MESSAGE(
image_webp->load_webp_from_buffer(data_webp) == OK,
- "The WEBP image should load successfully.");
+ "The WebP image should load successfully.");
// Load PNG
Ref<Image> image_png = memnew(Image());
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index a65020597a..90ca9e439f 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -170,8 +170,7 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
// Double check `to_rotation` decomposing with XYZ rotation order.
const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ);
- Basis rotation_from_xyz_computed_euler;
- rotation_from_xyz_computed_euler.set_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ);
+ Basis rotation_from_xyz_computed_euler = Basis::from_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ);
res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h
index d1912cbf42..eb5fea1d2d 100644
--- a/tests/core/math/test_quaternion.h
+++ b/tests/core/math/test_quaternion.h
@@ -192,7 +192,7 @@ TEST_CASE("[Quaternion] Construct Basis Euler") {
double roll = Math::deg_to_rad(10.0);
Vector3 euler_yxz(pitch, yaw, roll);
Quaternion q_yxz(euler_yxz);
- Basis basis_axes(euler_yxz);
+ Basis basis_axes = Basis::from_euler(euler_yxz);
Quaternion q(basis_axes);
CHECK(q.is_equal_approx(q_yxz));
}
@@ -218,7 +218,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
// This is by design, but may be subject to change.
// Workaround by constructing Basis from Euler angles.
// basis_axes = Basis(i_unit, j_unit, k_unit);
- Basis basis_axes(euler_yxz);
+ Basis basis_axes = Basis::from_euler(euler_yxz);
Quaternion q(basis_axes);
CHECK(basis_axes.get_column(0).is_equal_approx(i_unit));
@@ -334,7 +334,7 @@ TEST_CASE("[Quaternion] xform unit vectors") {
TEST_CASE("[Quaternion] xform vector") {
// Arbitrary quaternion rotates an arbitrary vector.
Vector3 euler_yzx(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
- Basis basis_axes(euler_yzx);
+ Basis basis_axes = Basis::from_euler(euler_yzx);
Quaternion q(basis_axes);
Vector3 v_arb(3.0, 4.0, 5.0);
@@ -347,7 +347,7 @@ TEST_CASE("[Quaternion] xform vector") {
// Test vector xform for a single combination of Quaternion and Vector.
void test_quat_vec_rotate(Vector3 euler_yzx, Vector3 v_in) {
- Basis basis_axes(euler_yzx);
+ Basis basis_axes = Basis::from_euler(euler_yzx);
Quaternion q(basis_axes);
Vector3 v_rot = q.xform(v_in);
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 2ef0a3345f..4b92b9daff 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -733,6 +733,46 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("caret_changed");
}
+ SUBCASE("[TextEdit] add selection for next occurrence") {
+ text_edit->set_text("\ntest other_test\nrandom test\nword test word");
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_line(1);
+
+ text_edit->select_word_under_caret();
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "test");
+
+ text_edit->add_selection_for_next_occurrence();
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_selected_text(1) == "test");
+ CHECK(text_edit->get_selection_from_line(1) == 1);
+ CHECK(text_edit->get_selection_from_column(1) == 13);
+ CHECK(text_edit->get_selection_to_line(1) == 1);
+ CHECK(text_edit->get_selection_to_column(1) == 17);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 17);
+
+ text_edit->add_selection_for_next_occurrence();
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_selected_text(2) == "test");
+ CHECK(text_edit->get_selection_from_line(2) == 2);
+ CHECK(text_edit->get_selection_from_column(2) == 9);
+ CHECK(text_edit->get_selection_to_line(2) == 2);
+ CHECK(text_edit->get_selection_to_column(2) == 13);
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 13);
+
+ text_edit->add_selection_for_next_occurrence();
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK(text_edit->get_selected_text(3) == "test");
+ CHECK(text_edit->get_selection_from_line(3) == 3);
+ CHECK(text_edit->get_selection_from_column(3) == 5);
+ CHECK(text_edit->get_selection_to_line(3) == 3);
+ CHECK(text_edit->get_selection_to_column(3) == 9);
+ CHECK(text_edit->get_caret_line(3) == 3);
+ CHECK(text_edit->get_caret_column(3) == 9);
+ }
+
SUBCASE("[TextEdit] deselect on focus loss") {
text_edit->set_text("test");
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 4c861eacba..d30d4a9e70 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -199,7 +199,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
OS::get_singleton()->set_has_server_feature_callback(nullptr);
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (String("headless") == DisplayServer::get_create_function_name(i)) {
- DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, Vector2i(0, 0), err);
+ DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), err);
break;
}
}