2
0
Fork 0

Merge pull request #79708 from YuriSizov/4.0-cherrypicks

Cherry-picks for the 4.0 branch (future 4.0.4) - 3rd batch
4.0
Yuri Sizov 2023-07-20 17:46:13 +07:00 committed by GitHub
commit cfedb0a7a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 779 additions and 325 deletions

@ -4,6 +4,207 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [4.0.4] - TBD
See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-0-4) for details.
### Added
#### C#/.NET
- Add version defines to help users deal with breaking changes ([GH-78270](https://github.com/godotengine/godot/pull/78270)).
#### Documentation
- Document GI techniques ignoring VisualInstance3D and Camera3D layers ([GH-74688](https://github.com/godotengine/godot/pull/74688)).
- Document how to use the global animation library in GDScript ([GH-74894](https://github.com/godotengine/godot/pull/74894)).
- Add more info on the nature of NAN ([GH-75614](https://github.com/godotengine/godot/pull/75614)).
- Add Stretch Mode description to ProjectSettings.xml ([GH-76272](https://github.com/godotengine/godot/pull/76272)).
- Document seamless caveats on small textures in NoiseTexture2D and NoiseTexture3D ([GH-77017](https://github.com/godotengine/godot/pull/77017)).
- Document the database for `Input.get_joy_name()` and `Input.get_joy_guid()` ([GH-77768](https://github.com/godotengine/godot/pull/77768)).
- Document the InitialAction enum in RenderingDevice ([GH-77882](https://github.com/godotengine/godot/pull/77882)).
- Document ShaderInclude ([GH-78562](https://github.com/godotengine/godot/pull/78562)).
#### Editor
- Add an editor option to copy system info to clipboard ([GH-65902](https://github.com/godotengine/godot/pull/65902)).
#### Input
- Add support for DPAD Center key of Android TV remote controller ([GH-77115](https://github.com/godotengine/godot/pull/77115)).
### Changed
#### 2D
- Make tile atlas merge dialog use filter nearest on both sides ([GH-77385](https://github.com/godotengine/godot/pull/77385)).
- Don't create bones from empty scene ([GH-77473](https://github.com/godotengine/godot/pull/77473)).
- Don't disable `Material` and `PlaceholderMaterial` when `disable_3d=yes` ([GH-77654](https://github.com/godotengine/godot/pull/77654)).
- Draw materials in tile atlas view ([GH-77909](https://github.com/godotengine/godot/pull/77909)).
#### Animation
- Hide Animation Frames section when there are no animations ([GH-77221](https://github.com/godotengine/godot/pull/77221)).
- Improve `Skeleton3D::find_bone()` performance ([GH-77307](https://github.com/godotengine/godot/pull/77307)).
- Avoid mutating the same Skin multiple times ([GH-77505](https://github.com/godotengine/godot/pull/77505)).
#### C#/.NET
- Always decode `dotnet` output as UTF-8 ([GH-74065](https://github.com/godotengine/godot/pull/74065)).
- Link the right build property to REAL_T_IS_DOUBLE ([GH-77198](https://github.com/godotengine/godot/pull/77198)).
#### Editor
- Use nearest with mipmaps texture filter in SpriteFrames editor plugin ([GH-74341](https://github.com/godotengine/godot/pull/74341)).
- Make sure script cache is created after reimport ([GH-75798](https://github.com/godotengine/godot/pull/75798)).
- Make SpriteFrames editor toolbar a `FlowContainer` ([GH-77034](https://github.com/godotengine/godot/pull/77034)).
- Prevent selecting unselectable `EditorProperty` with RMB ([GH-77148](https://github.com/godotengine/godot/pull/77148)).
- Do not translate node name when assigned to an exported field ([GH-77217](https://github.com/godotengine/godot/pull/77217)).
- Allow up to INT32_MAX max size in Array/Dictionary editor ([GH-77225](https://github.com/godotengine/godot/pull/77225)).
- Avoid error spam on first opening of a not yet imported project ([GH-77276](https://github.com/godotengine/godot/pull/77276)).
- Ensure quotes are escaped when converting built-in scripts ([GH-77399](https://github.com/godotengine/godot/pull/77399)).
- Ignore the `project_settings_override` file when in editor ([GH-77459](https://github.com/godotengine/godot/pull/77459)).
#### GDScript
- Treat `BitField<Enum>` as `int` (not `Enum`) ([GH-77579](https://github.com/godotengine/godot/pull/77579)).
#### GUI
- Stop dragging when `Slider` changes editability ([GH-77242](https://github.com/godotengine/godot/pull/77242)).
- Use defined key mapping for closing popups and dialogs ([GH-77297](https://github.com/godotengine/godot/pull/77297)).
- TextServer: Prevent duplicate line breaks on virtual spaces when line width is significantly smaller than character width ([GH-77514](https://github.com/godotengine/godot/pull/77514)).
- Cancel tooltip when mouse leaves viewport ([GH-77933](https://github.com/godotengine/godot/pull/77933)).
- Preserve selection when focusing SpinBox ([GH-78092](https://github.com/godotengine/godot/pull/78092)).
#### Input
- Improve touchpad and mouse support for the Android editor ([GH-77498](https://github.com/godotengine/godot/pull/77498)).
- Skip error messages for buttons that don't exist ([GH-77748](https://github.com/godotengine/godot/pull/77748)).
#### Rendering
- Disable AMD switchable graphics on Windows with Vulkan to fix driver issue ([GH-73450](https://github.com/godotengine/godot/pull/73450)).
- Take 3D resolution scaling into account for mesh LOD ([GH-77294](https://github.com/godotengine/godot/pull/77294)).
#### Thirdparty
- brotli updated to version ed1995b6b.
- msdfgen updated to version 1.10.
- recast updated to version 1.6.0.
- tinyexr updated to version 1.0.5.
- wslay updated to version 0e7d106ff.
- zstd updated to version 1.5.5.
- CA root certificates updated to 2023-06-02 bundle from Mozilla.
### Fixed
#### 2D
- Fix crash when opening a TileSet with invalid tiles ([GH-78165](https://github.com/godotengine/godot/pull/78165)).
- Fix crash with failed compatibility tiles ([GH-78796](https://github.com/godotengine/godot/pull/78796)).
#### 3D
- Fix CSGPolygon3D in path mode disappearing at runtime ([GH-77118](https://github.com/godotengine/godot/pull/77118)).
#### Animation
- Fix type check in AnimationTrackKeyEdit for methods ([GH-74948](https://github.com/godotengine/godot/pull/74948)).
- Fix `AnimatedSprite3D` autoplay warning ([GH-77028](https://github.com/godotengine/godot/pull/77028)).
- Adjust BoneAttachment3D children/meshes during rest fixer ([GH-77123](https://github.com/godotengine/godot/pull/77123)).
- Fix `get_bone_pose_global_no_override()` returning incorrect values ([GH-77194](https://github.com/godotengine/godot/pull/77194)).
- Fix for SkeletonIK3D interpolation and bone roll ([GH-77469](https://github.com/godotengine/godot/pull/77469)).
- Fix AnimationPlayer cumulative `speed_scale` ([GH-77500](https://github.com/godotengine/godot/pull/77500)).
- Fix adding bones with the same name after calling `Skeleton3D.clear_bones()` ([GH-77874](https://github.com/godotengine/godot/pull/77874)).
#### Audio
- Fix trim when importing WAV ([GH-75261](https://github.com/godotengine/godot/pull/75261)).
- Fix 2D audio in multiple viewports ([GH-76713](https://github.com/godotengine/godot/pull/76713)).
- Fix crash in AudioStream preview ([GH-77664](https://github.com/godotengine/godot/pull/77664)).
- Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone ([GH-77686](https://github.com/godotengine/godot/pull/77686)).
#### Buildsystem
- CI: Fix running the unit tests on windows ([GH-76887](https://github.com/godotengine/godot/pull/76887)).
- Linux: Fix udev fallback logic with `use_sowrap=no` ([GH-79111](https://github.com/godotengine/godot/pull/79111)).
#### C#/.NET
- Fix C# glue generation for enums with negative values ([GH-77018](https://github.com/godotengine/godot/pull/77018)).
- Fix `SendToScriptDebugger` crash ([GH-77377](https://github.com/godotengine/godot/pull/77377)).
#### Core
- Fix `StringName` comparison ([GH-77197](https://github.com/godotengine/godot/pull/77197)).
- Fix calling `TextureStorage::texture_3d_update()` could cause a crash ([GH-77266](https://github.com/godotengine/godot/pull/77266)).
#### Editor
- Fix theme of editor VCS dialogs ([GH-75983](https://github.com/godotengine/godot/pull/75983)).
- Fix calculation bug with `TextEdit::get_line_height()` ([GH-76605](https://github.com/godotengine/godot/pull/76605)).
- Fix Input Map key assignments missing after project conversion ([GH-77134](https://github.com/godotengine/godot/pull/77134)).
- Fix `Window` derived nodes being unselectable for `ViewportTexture` `NodePath` ([GH-77312](https://github.com/godotengine/godot/pull/77312)).
- Fix crash when using tile atlas merge with recreated alt tile ([GH-77382](https://github.com/godotengine/godot/pull/77382)).
- Fix filesystem cache split error ([GH-78324](https://github.com/godotengine/godot/pull/78324)).
- Fix saving size in `CreateDialog` ([GH-78403](https://github.com/godotengine/godot/pull/78403)).
- Shaders: Exclude incorrect completion options for `render_mode` in shaders ([GH-77086](https://github.com/godotengine/godot/pull/77086)).
#### GDScript
- Add missing `script_type` `nullptr` check ([GH-75943](https://github.com/godotengine/godot/pull/75943)).
- Fix warning ignoring for member variables ([GH-76203](https://github.com/godotengine/godot/pull/76203)).
- Fix `validate_call_arg()` for unresolved datatype ([GH-77091](https://github.com/godotengine/godot/pull/77091)).
- Fix extraction of chained `tr()` calls ([GH-77538](https://github.com/godotengine/godot/pull/77538)).
#### GUI
- Fix `Range`-derived nodes not redrawing after `set_value_no_signal` ([GH-70834](https://github.com/godotengine/godot/pull/70834)).
- Fix adding colors to swatches not updating in previous ColorPickers ([GH-76751](https://github.com/godotengine/godot/pull/76751)).
- Fix crash when changing node type from PopupMenu to ItemList ([GH-76854](https://github.com/godotengine/godot/pull/76854)).
- Fix `ItemList` item text positions in RTL mode ([GH-77166](https://github.com/godotengine/godot/pull/77166)).
- Fix crash when selecting lines in text edit ([GH-77667](https://github.com/godotengine/godot/pull/77667)).
- Fix SVG font rendering after ThorVG update ([GH-77942](https://github.com/godotengine/godot/pull/77942)).
- Fix disabled slider highlighting ([GH-78776](https://github.com/godotengine/godot/pull/78776)).
#### Input
- Fix errors that appear while reordering input map entries ([GH-77009](https://github.com/godotengine/godot/pull/77009)).
- Fix spatial viewport multitouch detection support ([GH-78083](https://github.com/godotengine/godot/pull/78083)).
#### Navigation
- Fix agent avoidance position not updated when entering SceneTree ([GH-77110](https://github.com/godotengine/godot/pull/77110)).
#### Networking
- Fix HTTPClient `_request` using wrong size ([GH-75867](https://github.com/godotengine/godot/pull/75867)).
- ENet: Better handle truncated socket messages ([GH-79699](https://github.com/godotengine/godot/pull/79699)).
#### Particles
- Correctly reset particle size and rotation in ParticlesProcessMaterial ([GH-78021](https://github.com/godotengine/godot/pull/78021)).
- Avoid error spam when (un)pausing GPUParticles out of tree ([GH-78143](https://github.com/godotengine/godot/pull/78143)).
#### Physics
- Fix width and center position of `CapsuleShape2D::get_rect` ([GH-77065](https://github.com/godotengine/godot/pull/77065)).
#### Porting
- Android: Set pending intent flag to stop insta-crash ([GH-78175](https://github.com/godotengine/godot/pull/78175)).
- Windows: Fix for Win+M crashing the editor ([GH-78235](https://github.com/godotengine/godot/pull/78235)).
#### Rendering
- Fix typo in FinalAction `switch` statement in RenderingDevice ([GH-75945](https://github.com/godotengine/godot/pull/75945)).
- Fix modulation propagation for Y-sorted CanvasItems ([GH-77079](https://github.com/godotengine/godot/pull/77079)).
- Fix LightmapGI dynamic object lighting ([GH-77089](https://github.com/godotengine/godot/pull/77089)).
- Fix calculation of skinned AABB for unused bones ([GH-77265](https://github.com/godotengine/godot/pull/77265)).
- Fix uninitialized Y-sort modulate for CanvasItems ([GH-78134](https://github.com/godotengine/godot/pull/78134)).
## [4.0.3] - 2023-05-19 ## [4.0.3] - 2023-05-19
See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-0-3) for details. See the [release announcement](https://godotengine.org/article/maintenance-release-godot-4-0-3) for details.
@ -3106,6 +3307,7 @@ See the [release announcement](https://godotengine.org/article/godot-3-3-has-arr
- Only WebAssembly is supported now, since all browsers supporting WebGL 2.0 also support WebAssembly. - Only WebAssembly is supported now, since all browsers supporting WebGL 2.0 also support WebAssembly.
[4.0.4]: https://github.com/godotengine/godot/compare/4.0.3-stable...4.0.4-stable
[4.0.3]: https://github.com/godotengine/godot/compare/4.0.2-stable...4.0.3-stable [4.0.3]: https://github.com/godotengine/godot/compare/4.0.2-stable...4.0.3-stable
[4.0.2]: https://github.com/godotengine/godot/compare/4.0.1-stable...4.0.2-stable [4.0.2]: https://github.com/godotengine/godot/compare/4.0.1-stable...4.0.2-stable
[4.0.1]: https://github.com/godotengine/godot/compare/4.0-stable...4.0.1-stable [4.0.1]: https://github.com/godotengine/godot/compare/4.0-stable...4.0.1-stable

@ -1354,8 +1354,9 @@ void Input::parse_mapping(String p_mapping) {
String output = entry[idx].get_slice(":", 0).replace(" ", ""); String output = entry[idx].get_slice(":", 0).replace(" ", "");
String input = entry[idx].get_slice(":", 1).replace(" ", ""); String input = entry[idx].get_slice(":", 1).replace(" ", "");
ERR_CONTINUE_MSG(output.length() < 1 || input.length() < 2, if (output.length() < 1 || input.length() < 2) {
vformat("Invalid device mapping entry \"%s\" in mapping:\n%s", entry[idx], p_mapping)); continue;
}
if (output == "platform" || output == "hint") { if (output == "platform" || output == "hint") {
continue; continue;

@ -397,7 +397,7 @@ void Basis::rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction)
real_t dot = p_start_direction.dot(p_end_direction); real_t dot = p_start_direction.dot(p_end_direction);
dot = CLAMP(dot, -1.0f, 1.0f); dot = CLAMP(dot, -1.0f, 1.0f);
const real_t angle_rads = Math::acos(dot); const real_t angle_rads = Math::acos(dot);
set_axis_angle(axis, angle_rads); *this = Basis(axis, angle_rads) * (*this);
} }
} }

@ -30,10 +30,10 @@
func _get_preset_count(): func _get_preset_count():
return 1 return 1
func _get_preset_name(i): func _get_preset_name(preset_index):
return "Default" return "Default"
func _get_import_options(i): func _get_import_options(path, preset_index):
return [{"name": "my_option", "default_value": false}] return [{"name": "my_option", "default_value": false}]
func _import(source_file, save_path, options, platform_variants, gen_files): func _import(source_file, save_path, options, platform_variants, gen_files):

@ -22,7 +22,7 @@
<return type="bool" /> <return type="bool" />
<description> <description>
Tries locking this [Mutex], but does not block. Returns [code]true[/code] on success, [code]false[/code] otherwise. Tries locking this [Mutex], but does not block. Returns [code]true[/code] on success, [code]false[/code] otherwise.
[b]Note:[/b] This function returns [constant OK] if the thread already has ownership of the mutex. [b]Note:[/b] This function returns [code]true[/code] if the thread already has ownership of the mutex.
</description> </description>
</method> </method>
<method name="unlock"> <method name="unlock">

@ -1,13 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<class name="ShaderInclude" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <class name="ShaderInclude" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description> <brief_description>
A snippet of shader code to be included in a [Shader] with [code]#include[/code].
</brief_description> </brief_description>
<description> <description>
A shader include file, saved with the [code].gdshaderinc[/code] extension. This class allows you to define a custom shader snippet that can be included in a [Shader] by using the preprocessor directive [code]#include[/code], followed by the file path (e.g. [code]#include "res://shader_lib.gdshaderinc"[/code]). The snippet doesn't have to be a valid shader on its own.
</description> </description>
<tutorials> <tutorials>
<link title="Shader preprocessor">$DOCS_URL/tutorials/shaders/shader_reference/shader_preprocessor.html</link>
</tutorials> </tutorials>
<members> <members>
<member name="code" type="String" setter="set_code" getter="get_code" default="&quot;&quot;"> <member name="code" type="String" setter="set_code" getter="get_code" default="&quot;&quot;">
Returns the code of the shader include file. The returned text is what the user has written, not the full generated code used internally.
</member> </member>
</members> </members>
</class> </class>

@ -884,11 +884,11 @@
<description> <description>
Converts the string representing a decimal number into a [float]. This method stops on the first non-number character, except the first decimal point ([code].[/code]) and the exponent letter ([code]e[/code]). See also [method is_valid_float]. Converts the string representing a decimal number into a [float]. This method stops on the first non-number character, except the first decimal point ([code].[/code]) and the exponent letter ([code]e[/code]). See also [method is_valid_float].
[codeblock] [codeblock]
var a = "12.35".to_float() # a is 12.35 var a = "12.35".to_float() # a is 12.35
var b = "1.2.3".to_float() # b is 1.2 var b = "1.2.3".to_float() # b is 1.2
var c = "12xy3".to_float() # c is 12.0 var c = "12xy3".to_float() # c is 12.0
var d = "1e3".to_float() # d is 1000.0 var d = "1e3".to_float() # d is 1000.0
var e = "Hello!".to_int() # e is 0.0 var e = "Hello!".to_float() # e is 0.0
[/codeblock] [/codeblock]
</description> </description>
</method> </method>

@ -204,6 +204,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (err == WSAEACCES) { if (err == WSAEACCES) {
return ERR_NET_UNAUTHORIZED; return ERR_NET_UNAUTHORIZED;
} }
if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
return ERR_NET_BUFFER_TOO_SMALL;
}
print_verbose("Socket error: " + itos(err)); print_verbose("Socket error: " + itos(err));
return ERR_NET_OTHER; return ERR_NET_OTHER;
#else #else
@ -222,6 +225,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == EACCES) { if (errno == EACCES) {
return ERR_NET_UNAUTHORIZED; return ERR_NET_UNAUTHORIZED;
} }
if (errno == ENOBUFS) {
return ERR_NET_BUFFER_TOO_SMALL;
}
print_verbose("Socket error: " + itos(errno)); print_verbose("Socket error: " + itos(errno));
return ERR_NET_OTHER; return ERR_NET_OTHER;
#endif #endif
@ -550,6 +556,10 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
return ERR_BUSY; return ERR_BUSY;
} }
if (err == ERR_NET_BUFFER_TOO_SMALL) {
return ERR_OUT_OF_MEMORY;
}
return FAILED; return FAILED;
} }
@ -571,6 +581,10 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr
return ERR_BUSY; return ERR_BUSY;
} }
if (err == ERR_NET_BUFFER_TOO_SMALL) {
return ERR_OUT_OF_MEMORY;
}
return FAILED; return FAILED;
} }
@ -606,6 +620,9 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
if (err == ERR_NET_WOULD_BLOCK) { if (err == ERR_NET_WOULD_BLOCK) {
return ERR_BUSY; return ERR_BUSY;
} }
if (err == ERR_NET_BUFFER_TOO_SMALL) {
return ERR_OUT_OF_MEMORY;
}
return FAILED; return FAILED;
} }
@ -625,6 +642,9 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP
if (err == ERR_NET_WOULD_BLOCK) { if (err == ERR_NET_WOULD_BLOCK) {
return ERR_BUSY; return ERR_BUSY;
} }
if (err == ERR_NET_BUFFER_TOO_SMALL) {
return ERR_OUT_OF_MEMORY;
}
return FAILED; return FAILED;
} }

@ -56,6 +56,7 @@ private:
ERR_NET_IN_PROGRESS, ERR_NET_IN_PROGRESS,
ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
ERR_NET_UNAUTHORIZED, ERR_NET_UNAUTHORIZED,
ERR_NET_BUFFER_TOO_SMALL,
ERR_NET_OTHER, ERR_NET_OTHER,
}; };

@ -2276,6 +2276,7 @@ Error VulkanContext::prepare_buffers() {
// presentation engine will still present the image correctly. // presentation engine will still present the image correctly.
print_verbose("Vulkan: Early suboptimal swapchain, recreating."); print_verbose("Vulkan: Early suboptimal swapchain, recreating.");
_update_swap_chain(w); _update_swap_chain(w);
break;
} else if (err != VK_SUCCESS) { } else if (err != VK_SUCCESS) {
ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err))); ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err)));
} else { } else {

@ -201,7 +201,7 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
if (t != args[idx].get_type()) { if (t != args[idx].get_type()) {
Callable::CallError err; Callable::CallError err;
if (Variant::can_convert(args[idx].get_type(), t)) { if (Variant::can_convert_strict(args[idx].get_type(), t)) {
Variant old = args[idx]; Variant old = args[idx];
Variant *ptrs[1] = { &old }; Variant *ptrs[1] = { &old };
Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err);
@ -786,7 +786,7 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p
if (t != args[idx].get_type()) { if (t != args[idx].get_type()) {
Callable::CallError err; Callable::CallError err;
if (Variant::can_convert(args[idx].get_type(), t)) { if (Variant::can_convert_strict(args[idx].get_type(), t)) {
Variant old = args[idx]; Variant old = args[idx];
Variant *ptrs[1] = { &old }; Variant *ptrs[1] = { &old };
Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err); Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err);

@ -408,7 +408,7 @@ void FindReplaceBar::_update_matches_label() {
matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor"))); matches_label->add_theme_color_override("font_color", results_count > 0 ? get_theme_color(SNAME("font_color"), SNAME("Label")) : get_theme_color(SNAME("error_color"), SNAME("Editor")));
if (results_count == 0) { if (results_count == 0) {
matches_label->set_text("No match"); matches_label->set_text(TTR("No match"));
} else if (results_count_to_current == -1) { } else if (results_count_to_current == -1) {
matches_label->set_text(vformat(TTRN("%d match", "%d matches", results_count), results_count)); matches_label->set_text(vformat(TTRN("%d match", "%d matches", results_count), results_count));
} else { } else {

@ -262,7 +262,7 @@ void EditorFileSystem::_scan_filesystem() {
} else { } else {
Vector<String> split = l.split("::"); Vector<String> split = l.split("::");
ERR_CONTINUE(split.size() != 9); ERR_CONTINUE(split.size() < 9);
String name = split[0]; String name = split[0];
String file; String file;
@ -2328,6 +2328,7 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
ResourceUID::get_singleton()->update_cache(); // After reimporting, update the cache. ResourceUID::get_singleton()->update_cache(); // After reimporting, update the cache.
_save_filesystem_cache(); _save_filesystem_cache();
_update_pending_script_classes();
importing = false; importing = false;
if (!is_scanning()) { if (!is_scanning()) {
emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("filesystem_changed"));

@ -31,6 +31,7 @@
#include "post_import_plugin_skeleton_rest_fixer.h" #include "post_import_plugin_skeleton_rest_fixer.h"
#include "editor/import/scene_import_settings.h" #include "editor/import/scene_import_settings.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h" #include "scene/animation/animation_player.h"
@ -105,42 +106,6 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin. global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin.
} }
// Calc IBM difference.
LocalVector<Vector<Transform3D>> ibm_diffs;
{
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
while (nodes.size()) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
ERR_CONTINUE(!mi);
Ref<Skin> skin = mi->get_skin();
ERR_CONTINUE(!skin.is_valid());
Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node);
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
if (!mesh_skeleton || mesh_skeleton != src_skeleton) {
continue;
}
Vector<Transform3D> ibm_diff;
ibm_diff.resize(src_skeleton->get_bone_count());
Transform3D *ibm_diff_w = ibm_diff.ptrw();
int skin_len = skin->get_bind_count();
for (int i = 0; i < skin_len; i++) {
StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) {
ibm_diff_w[bone_idx] = global_transform * src_skeleton->get_bone_global_rest(bone_idx) * skin->get_bind_pose(i);
}
}
ibm_diffs.push_back(ibm_diff);
}
}
// Apply node transforms. // Apply node transforms.
if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) {
Vector3 scl = global_transform.basis.get_scale_local(); Vector3 scl = global_transform.basis.get_scale_local();
@ -288,12 +253,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis. Vector<Transform3D> silhouette_diff; // Transform values to be ignored when overwrite axis.
silhouette_diff.resize(src_skeleton->get_bone_count()); silhouette_diff.resize(src_skeleton->get_bone_count());
Transform3D *silhouette_diff_w = silhouette_diff.ptrw(); Transform3D *silhouette_diff_w = silhouette_diff.ptrw();
LocalVector<Transform3D> pre_silhouette_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
pre_silhouette_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) { if (bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
LocalVector<Transform3D> old_skeleton_global_rest;
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
}
Vector<int> bones_to_process = prof_skeleton->get_parentless_bones(); Vector<int> bones_to_process = prof_skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) { while (bones_to_process.size() > 0) {
int prof_idx = bones_to_process[0]; int prof_idx = bones_to_process[0];
@ -450,7 +414,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
// For skin modification in overwrite rest. // For skin modification in overwrite rest.
for (int i = 0; i < src_skeleton->get_bone_count(); i++) { for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
silhouette_diff_w[i] = old_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).inverse(); silhouette_diff_w[i] = pre_silhouette_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).affine_inverse();
} }
is_rest_changed = true; is_rest_changed = true;
@ -645,14 +609,20 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
if (is_rest_changed) { if (is_rest_changed) {
// Fix skin. // Fix skin.
{ {
HashSet<Ref<Skin>> mutated_skins;
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
int skin_idx = 0;
while (nodes.size()) { while (nodes.size()) {
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back());
ERR_CONTINUE(!mi); ERR_CONTINUE(!mi);
Ref<Skin> skin = mi->get_skin(); Ref<Skin> skin = mi->get_skin();
ERR_CONTINUE(!skin.is_valid()); if (skin.is_null()) {
continue;
}
if (mutated_skins.has(skin)) {
continue;
}
mutated_skins.insert(skin);
Node *node = mi->get_node(mi->get_skeleton_path()); Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node); ERR_CONTINUE(!node);
@ -662,19 +632,39 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
continue; continue;
} }
Vector<Transform3D> ibm_diff = ibm_diffs[skin_idx];
int skin_len = skin->get_bind_count(); int skin_len = skin->get_bind_count();
for (int i = 0; i < skin_len; i++) { for (int i = 0; i < skin_len; i++) {
StringName bn = skin->get_bind_name(i); StringName bn = skin->get_bind_name(i);
int bone_idx = src_skeleton->find_bone(bn); int bone_idx = src_skeleton->find_bone(bn);
if (bone_idx >= 0) { if (bone_idx >= 0) {
Transform3D new_rest = silhouette_diff[bone_idx] * src_skeleton->get_bone_global_rest(bone_idx); Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[bone_idx]); adjust_transform.scale(global_transform.basis.get_scale_local());
skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i));
} }
} }
}
skin_idx++; nodes = src_skeleton->get_children();
while (nodes.size()) {
BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(nodes.pop_back());
if (attachment == nullptr) {
continue;
}
int bone_idx = attachment->get_bone_idx();
if (bone_idx == -1) {
bone_idx = src_skeleton->find_bone(attachment->get_bone_name());
}
ERR_CONTINUE(bone_idx < 0 || bone_idx >= src_skeleton->get_bone_count());
Transform3D adjust_transform = src_skeleton->get_bone_global_rest(bone_idx).affine_inverse() * silhouette_diff[bone_idx].affine_inverse() * pre_silhouette_skeleton_global_rest[bone_idx];
adjust_transform.scale(global_transform.basis.get_scale_local());
TypedArray<Node> child_nodes = attachment->get_children();
while (child_nodes.size()) {
Node3D *child = Object::cast_to<Node3D>(child_nodes.pop_back());
if (child == nullptr) {
continue;
}
child->set_transform(adjust_transform * child->get_transform());
}
} }
} }

@ -386,7 +386,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
bool trim = p_options["edit/trim"]; bool trim = p_options["edit/trim"];
if (trim && (loop_mode != AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) { if (trim && (loop_mode == AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) {
int first = 0; int first = 0;
int last = (frames / format_channels) - 1; int last = (frames / format_channels) - 1;
bool found = false; bool found = false;

@ -244,13 +244,16 @@ void TileAtlasView::_draw_base_tiles() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i); Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_set_atlas_source->get_tile_data(atlas_coords, 0), base_tiles_draw, material_tiles_draw);
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) { for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
// Update the y to max value. // Update the y to max value.
Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame); Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin()); Vector2 offset_pos = Rect2(base_frame_rect).get_center() + Vector2(tile_set_atlas_source->get_tile_data(atlas_coords, 0)->get_texture_origin());
// Draw the tile. // Draw the tile.
TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame); TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, 0, frame);
} }
} }
@ -286,6 +289,33 @@ void TileAtlasView::_draw_base_tiles() {
} }
} }
RID TileAtlasView::_get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map) {
Ref<Material> mat = p_for_data->get_material();
if (mat.is_null()) {
return p_base_item->get_canvas_item();
} else if (p_material_map.has(mat)) {
return p_material_map[mat];
} else {
RID ci_rid = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->canvas_item_set_parent(ci_rid, p_base_item->get_canvas_item());
RS::get_singleton()->canvas_item_set_material(ci_rid, mat->get_rid());
p_material_map[mat] = ci_rid;
return ci_rid;
}
}
void TileAtlasView::_clear_material_canvas_items() {
for (KeyValue<Ref<Material>, RID> kv : material_tiles_draw) {
RS::get_singleton()->free(kv.value);
}
material_tiles_draw.clear();
for (KeyValue<Ref<Material>, RID> kv : material_alternatives_draw) {
RS::get_singleton()->free(kv.value);
}
material_alternatives_draw.clear();
}
void TileAtlasView::_draw_base_tiles_texture_grid() { void TileAtlasView::_draw_base_tiles_texture_grid() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) { if (texture.is_valid()) {
@ -370,6 +400,9 @@ void TileAtlasView::_draw_alternatives() {
TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id); TileData *tile_data = tile_set_atlas_source->get_tile_data(atlas_coords, alternative_id);
bool transposed = tile_data->get_transpose(); bool transposed = tile_data->get_transpose();
// Different materials need to be drawn with different CanvasItems.
RID ci_rid = _get_canvas_item_to_draw(tile_data, alternatives_draw, material_alternatives_draw);
// Update the y to max value. // Update the y to max value.
Vector2i offset_pos; Vector2i offset_pos;
if (transposed) { if (transposed) {
@ -381,7 +414,7 @@ void TileAtlasView::_draw_alternatives() {
} }
// Draw the tile. // Draw the tile.
TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); TileMap::draw_tile(ci_rid, offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position. // Increment the x position.
current_pos.x += transposed ? texture_region_size.y : texture_region_size.x; current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
@ -407,6 +440,8 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
tile_set = p_tile_set; tile_set = p_tile_set;
tile_set_atlas_source = p_tile_set_atlas_source; tile_set_atlas_source = p_tile_set_atlas_source;
_clear_material_canvas_items();
if (!tile_set) { if (!tile_set) {
return; return;
} }
@ -690,3 +725,7 @@ TileAtlasView::TileAtlasView() {
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
alternative_tiles_drawing_root->add_child(alternatives_draw); alternative_tiles_drawing_root->add_child(alternatives_draw);
} }
TileAtlasView::~TileAtlasView() {
_clear_material_canvas_items();
}

@ -89,7 +89,11 @@ private:
Control *base_tiles_drawing_root = nullptr; Control *base_tiles_drawing_root = nullptr;
Control *base_tiles_draw = nullptr; Control *base_tiles_draw = nullptr;
HashMap<Ref<Material>, RID> material_tiles_draw;
HashMap<Ref<Material>, RID> material_alternatives_draw;
void _draw_base_tiles(); void _draw_base_tiles();
RID _get_canvas_item_to_draw(const TileData *p_for_data, const CanvasItem *p_base_item, HashMap<Ref<Material>, RID> &p_material_map);
void _clear_material_canvas_items();
Control *base_tiles_texture_grid = nullptr; Control *base_tiles_texture_grid = nullptr;
void _draw_base_tiles_texture_grid(); void _draw_base_tiles_texture_grid();
@ -157,6 +161,7 @@ public:
void queue_redraw(); void queue_redraw();
TileAtlasView(); TileAtlasView();
~TileAtlasView();
}; };
#endif // TILE_ATLAS_VIEW_H #endif // TILE_ATLAS_VIEW_H

@ -106,9 +106,11 @@ void TilesEditorPlugin::_thread() {
Vector2i coords = tile_map->get_cell_atlas_coords(0, cell); Vector2i coords = tile_map->get_cell_atlas_coords(0, cell);
int alternative = tile_map->get_cell_alternative_tile(0, cell); int alternative = tile_map->get_cell_alternative_tile(0, cell);
Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin(); if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) {
encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2); Vector2 center = world_pos - atlas_source->get_tile_data(coords, alternative)->get_texture_origin();
encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2); encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
}
} }
} }

@ -126,7 +126,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
xml_body += vformat("</%s>", parser->get_node_name()); xml_body += vformat("</%s>", parser->get_node_name());
} }
} }
String temp_xml_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body; String temp_xml_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1 1\">" + xml_body;
CharString temp_xml = temp_xml_str.utf8(); CharString temp_xml = temp_xml_str.utf8();
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

@ -126,7 +126,7 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin
xml_body += vformat("</%s>", parser->get_node_name()); xml_body += vformat("</%s>", parser->get_node_name());
} }
} }
String temp_xml_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body; String temp_xml_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1 1\">" + xml_body;
CharString temp_xml = temp_xml_str.utf8(); CharString temp_xml = temp_xml_str.utf8();
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

@ -272,7 +272,8 @@ Error AudioDriverOpenSL::input_start() {
return init_input_device(); return init_input_device();
} }
return OK; WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
return ERR_UNAUTHORIZED;
} }
Error AudioDriverOpenSL::input_stop() { Error AudioDriverOpenSL::input_stop() {

@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/> android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<application <application
android:allowBackup="false" android:allowBackup="false"

@ -81,7 +81,9 @@ open class GodotEditor : FullScreenGodotApp() {
private val commandLineParams = ArrayList<String>() private val commandLineParams = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
PermissionsUtil.requestManifestPermissions(this) // We exclude certain permissions from the set we request at startup, as they'll be
// requested on demand based on use-cases.
PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS) val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS)
updateCommandLineParams(params) updateCommandLineParams(params)
@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() {
val longPressEnabled = enableLongPressGestures() val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures() val panScaleEnabled = enablePanAndScaleGestures()
checkForProjectPermissionsToEnable()
runOnUiThread { runOnUiThread {
// Enable long press, panning and scaling gestures // Enable long press, panning and scaling gestures
godotFragment?.renderView?.inputHandler?.apply { godotFragment?.renderView?.inputHandler?.apply {
@ -107,6 +111,17 @@ open class GodotEditor : FullScreenGodotApp() {
} }
} }
/**
* Check for project permissions to enable
*/
protected open fun checkForProjectPermissionsToEnable() {
// Check for RECORD_AUDIO permission
val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"));
if (audioInputEnabled) {
PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
}
}
private fun updateCommandLineParams(args: Array<String>?) { private fun updateCommandLineParams(args: Array<String>?) {
// Update the list of command line params with the new args // Update the list of command line params with the new args
commandLineParams.clear() commandLineParams.clear()

@ -39,4 +39,9 @@ class GodotGame : GodotEditor() {
override fun enableLongPressGestures() = false override fun enableLongPressGestures() = false
override fun enablePanAndScaleGestures() = false override fun enablePanAndScaleGestures() = false
override fun checkForProjectPermissionsToEnable() {
// Nothing to do.. by the time we get here, the project permissions will have already
// been requested by the Editor window.
}
} }

@ -37,4 +37,9 @@ package org.godotengine.editor
* Upon selection of a project, this activity (via its parent logic) starts the * Upon selection of a project, this activity (via its parent logic) starts the
* [GodotEditor] activity. * [GodotEditor] activity.
*/ */
class GodotProjectManager : GodotEditor() class GodotProjectManager : GodotEditor() {
override fun checkForProjectPermissionsToEnable() {
// Nothing to do here.. we have yet to select a project to load.
}
}

@ -685,8 +685,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
Intent notifierIntent = new Intent(activity, activity.getClass()); Intent notifierIntent = new Intent(activity, activity.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0, PendingIntent pendingIntent;
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
pendingIntent = PendingIntent.getActivity(activity, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(activity, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
int startResult; int startResult;
try { try {

@ -42,10 +42,12 @@ import android.os.Environment;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* This class includes utility functions for Android permissions related operations. * This class includes utility functions for Android permissions related operations.
@ -58,6 +60,7 @@ public final class PermissionsUtil {
static final int REQUEST_CAMERA_PERMISSION = 2; static final int REQUEST_CAMERA_PERMISSION = 2;
static final int REQUEST_VIBRATE_PERMISSION = 3; static final int REQUEST_VIBRATE_PERMISSION = 3;
public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001; public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002; public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
private PermissionsUtil() { private PermissionsUtil() {
@ -65,31 +68,57 @@ public final class PermissionsUtil {
/** /**
* Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a> * Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a>
* @param name the name of the requested permission. * @param permissionName the name of the requested permission.
* @param activity the caller activity for this method. * @param activity the caller activity for this method.
* @return true/false. "true" if permission was granted otherwise returns "false". * @return true/false. "true" if permission was granted otherwise returns "false".
*/ */
public static boolean requestPermission(String name, Activity activity) { public static boolean requestPermission(String permissionName, Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Not necessary, asked on install already // Not necessary, asked on install already
return true; return true;
} }
if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { switch (permissionName) {
activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION); case "RECORD_AUDIO":
return false; case Manifest.permission.RECORD_AUDIO:
} if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
return false;
}
return true;
if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { case "CAMERA":
activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION); case Manifest.permission.CAMERA:
return false; if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
} activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
return false;
}
return true;
if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { case "VIBRATE":
activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION); case Manifest.permission.VIBRATE:
return false; if (ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
return false;
}
return true;
default:
// Check if the given permission is a dangerous permission
try {
PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName);
int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE);
return false;
}
} catch (PackageManager.NameNotFoundException e) {
// Unknown permission - return false as it can't be granted.
Log.w(TAG, "Unable to identify permission " + permissionName, e);
return false;
}
return true;
} }
return true;
} }
/** /**
@ -98,6 +127,16 @@ public final class PermissionsUtil {
* @return true/false. "true" if all permissions were granted otherwise returns "false". * @return true/false. "true" if all permissions were granted otherwise returns "false".
*/ */
public static boolean requestManifestPermissions(Activity activity) { public static boolean requestManifestPermissions(Activity activity) {
return requestManifestPermissions(activity, null);
}
/**
* Request dangerous permissions which are defined in the Android manifest file from the user.
* @param activity the caller activity for this method.
* @param excludes Set of permissions to exclude from the request
* @return true/false. "true" if all permissions were granted otherwise returns "false".
*/
public static boolean requestManifestPermissions(Activity activity, @Nullable Set<String> excludes) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true; return true;
} }
@ -115,6 +154,9 @@ public final class PermissionsUtil {
List<String> requestedPermissions = new ArrayList<>(); List<String> requestedPermissions = new ArrayList<>();
for (String manifestPermission : manifestPermissions) { for (String manifestPermission : manifestPermissions) {
if (excludes != null && excludes.contains(manifestPermission)) {
continue;
}
try { try {
if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) { if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {

@ -551,10 +551,12 @@ void GPUParticles2D::_notification(int p_what) {
case NOTIFICATION_PAUSED: case NOTIFICATION_PAUSED:
case NOTIFICATION_UNPAUSED: { case NOTIFICATION_UNPAUSED: {
if (can_process()) { if (is_inside_tree()) {
RS::get_singleton()->particles_set_speed_scale(particles, speed_scale); if (can_process()) {
} else { RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
RS::get_singleton()->particles_set_speed_scale(particles, 0); } else {
RS::get_singleton()->particles_set_speed_scale(particles, 0);
}
} }
} break; } break;

@ -417,15 +417,6 @@ NodePath GPUParticles3D::get_sub_emitter() const {
void GPUParticles3D::_notification(int p_what) { void GPUParticles3D::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_PAUSED:
case NOTIFICATION_UNPAUSED: {
if (can_process()) {
RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
} else {
RS::get_singleton()->particles_set_speed_scale(particles, 0);
}
} break;
// Use internal process when emitting and one_shot is on so that when // Use internal process when emitting and one_shot is on so that when
// the shot ends the editor can properly update. // the shot ends the editor can properly update.
case NOTIFICATION_INTERNAL_PROCESS: { case NOTIFICATION_INTERNAL_PROCESS: {
@ -450,6 +441,17 @@ void GPUParticles3D::_notification(int p_what) {
RS::get_singleton()->particles_set_subemitter(particles, RID()); RS::get_singleton()->particles_set_subemitter(particles, RID());
} break; } break;
case NOTIFICATION_PAUSED:
case NOTIFICATION_UNPAUSED: {
if (is_inside_tree()) {
if (can_process()) {
RS::get_singleton()->particles_set_speed_scale(particles, speed_scale);
} else {
RS::get_singleton()->particles_set_speed_scale(particles, 0);
}
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: { case NOTIFICATION_VISIBILITY_CHANGED: {
// Make sure particles are updated before rendering occurs if they were active before. // Make sure particles are updated before rendering occurs if they were active before.
if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) { if (is_visible_in_tree() && !RS::get_singleton()->particles_is_inactive(particles)) {

@ -249,26 +249,6 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_invers
} }
} }
static Vector3 get_bone_axis_forward_vector(Skeleton3D *skeleton, int p_bone) {
// If it is a child/leaf bone...
if (skeleton->get_bone_parent(p_bone) > 0) {
return skeleton->get_bone_rest(p_bone).origin.normalized();
}
// If it has children...
Vector<int> child_bones = skeleton->get_bone_children(p_bone);
if (child_bones.size() == 0) {
WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone));
WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone");
return Vector3(0, 1, 0);
}
Vector3 combined_child_dir = Vector3(0, 0, 0);
for (int i = 0; i < child_bones.size(); i++) {
combined_child_dir += skeleton->get_bone_rest(child_bones[i]).origin.normalized();
}
combined_child_dir = combined_child_dir / child_bones.size();
return combined_child_dir.normalized();
}
void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) {
if (blending_delta <= 0.01f) { if (blending_delta <= 0.01f) {
// Before skipping, make sure we undo the global pose overrides // Before skipping, make sure we undo the global pose overrides
@ -307,7 +287,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
new_bone_pose.origin = ci->current_pos; new_bone_pose.origin = ci->current_pos;
if (!ci->children.is_empty()) { if (!ci->children.is_empty()) {
Vector3 forward_vector = get_bone_axis_forward_vector(p_task->skeleton, ci->bone); Vector3 forward_vector = (ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized();
// Rotate the bone towards the next bone in the chain: // Rotate the bone towards the next bone in the chain:
new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos)); new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos));

@ -1527,6 +1527,22 @@ void LineEdit::set_text(String p_text) {
scroll_offset = 0.0; scroll_offset = 0.0;
} }
void LineEdit::set_text_with_selection(const String &p_text) {
Selection selection_copy = selection;
clear_internal();
insert_text_at_caret(p_text);
_create_undo_state();
int tlen = text.length();
selection = selection_copy;
selection.begin = MIN(selection.begin, tlen);
selection.end = MIN(selection.end, tlen);
selection.start_column = MIN(selection.start_column, tlen);
queue_redraw();
}
void LineEdit::set_text_direction(Control::TextDirection p_text_direction) { void LineEdit::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) { if (text_direction != p_text_direction) {

@ -282,6 +282,7 @@ public:
void set_text(String p_text); void set_text(String p_text);
String get_text() const; String get_text() const;
void set_text_with_selection(const String &p_text); // Set text, while preserving selection.
void set_text_direction(TextDirection p_text_direction); void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const; TextDirection get_text_direction() const;

@ -1952,8 +1952,13 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_icon(item_index); r_ret = get_item_icon(item_index);
return true; return true;
} else if (property == "checkable") { } else if (property == "checkable") {
r_ret = this->items[item_index].checkable_type; if (item_index >= 0 && item_index < items.size()) {
return true; r_ret = items[item_index].checkable_type;
return true;
} else {
r_ret = Item::CHECKABLE_TYPE_NONE;
ERR_FAIL_V(true);
}
} else if (property == "checked") { } else if (property == "checked") {
r_ret = is_item_checked(item_index); r_ret = is_item_checked(item_index);
return true; return true;

@ -74,16 +74,26 @@ void Range::Shared::emit_changed(const char *p_what) {
} }
} }
void Range::Shared::redraw_owners() {
for (Range *E : owners) {
Range *r = E;
if (!r->is_inside_tree()) {
continue;
}
r->queue_redraw();
}
}
void Range::set_value(double p_val) { void Range::set_value(double p_val) {
double prev_val = shared->val; double prev_val = shared->val;
set_value_no_signal(p_val); _set_value_no_signal(p_val);
if (shared->val != prev_val) { if (shared->val != prev_val) {
shared->emit_value_changed(); shared->emit_value_changed();
} }
} }
void Range::set_value_no_signal(double p_val) { void Range::_set_value_no_signal(double p_val) {
if (shared->step > 0) { if (shared->step > 0) {
p_val = Math::round((p_val - shared->min) / shared->step) * shared->step + shared->min; p_val = Math::round((p_val - shared->min) / shared->step) * shared->step + shared->min;
} }
@ -107,6 +117,15 @@ void Range::set_value_no_signal(double p_val) {
shared->val = p_val; shared->val = p_val;
} }
void Range::set_value_no_signal(double p_val) {
double prev_val = shared->val;
_set_value_no_signal(p_val);
if (shared->val != prev_val) {
shared->redraw_owners();
}
}
void Range::set_min(double p_min) { void Range::set_min(double p_min) {
if (shared->min == p_min) { if (shared->min == p_min) {
return; return;

@ -48,6 +48,7 @@ class Range : public Control {
HashSet<Range *> owners; HashSet<Range *> owners;
void emit_value_changed(); void emit_value_changed();
void emit_changed(const char *p_what = ""); void emit_changed(const char *p_what = "");
void redraw_owners();
}; };
Shared *shared = nullptr; Shared *shared = nullptr;
@ -59,6 +60,7 @@ class Range : public Control {
void _value_changed_notify(); void _value_changed_notify();
void _changed_notify(const char *p_what = ""); void _changed_notify(const char *p_what = "");
void _set_value_no_signal(double p_val);
protected: protected:
virtual void _value_changed(double p_value); virtual void _value_changed(double p_value);

@ -192,7 +192,7 @@ void Slider::_notification(int p_what) {
Ref<StyleBox> style = theme_cache.slider_style; Ref<StyleBox> style = theme_cache.slider_style;
Ref<Texture2D> tick = theme_cache.tick_icon; Ref<Texture2D> tick = theme_cache.tick_icon;
bool highlighted = mouse_inside || has_focus(); bool highlighted = editable && (mouse_inside || has_focus());
Ref<Texture2D> grabber; Ref<Texture2D> grabber;
if (editable) { if (editable) {
if (highlighted) { if (highlighted) {

@ -39,7 +39,7 @@ Size2 SpinBox::get_minimum_size() const {
return ms; return ms;
} }
void SpinBox::_value_changed(double p_value) { void SpinBox::_update_text() {
String value = String::num(get_value(), Math::range_step_decimals(get_step())); String value = String::num(get_value(), Math::range_step_decimals(get_step()));
if (is_localizing_numeral_system()) { if (is_localizing_numeral_system()) {
value = TS->format_number(value); value = TS->format_number(value);
@ -54,8 +54,7 @@ void SpinBox::_value_changed(double p_value) {
} }
} }
line_edit->set_text(value); line_edit->set_text_with_selection(value);
Range::_value_changed(p_value);
} }
void SpinBox::_text_submitted(const String &p_string) { void SpinBox::_text_submitted(const String &p_string) {
@ -73,7 +72,7 @@ void SpinBox::_text_submitted(const String &p_string) {
if (value.get_type() != Variant::NIL) { if (value.get_type() != Variant::NIL) {
set_value(value); set_value(value);
} }
_value_changed(0); _update_text();
} }
void SpinBox::_text_changed(const String &p_string) { void SpinBox::_text_changed(const String &p_string) {
@ -192,7 +191,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
void SpinBox::_line_edit_focus_enter() { void SpinBox::_line_edit_focus_enter() {
int col = line_edit->get_caret_column(); int col = line_edit->get_caret_column();
_value_changed(0); // Update the LineEdit's text. _update_text();
line_edit->set_caret_column(col); line_edit->set_caret_column(col);
// LineEdit text might change and it clears any selection. Have to re-select here. // LineEdit text might change and it clears any selection. Have to re-select here.
@ -202,6 +201,10 @@ void SpinBox::_line_edit_focus_enter() {
} }
void SpinBox::_line_edit_focus_exit() { void SpinBox::_line_edit_focus_exit() {
// Discontinue because the focus_exit was caused by left-clicking the arrows.
if (get_viewport()->gui_get_focus_owner() == get_line_edit()) {
return;
}
// Discontinue because the focus_exit was caused by right-click context menu. // Discontinue because the focus_exit was caused by right-click context menu.
if (line_edit->is_menu_visible()) { if (line_edit->is_menu_visible()) {
return; return;
@ -228,6 +231,7 @@ void SpinBox::_update_theme_item_cache() {
void SpinBox::_notification(int p_what) { void SpinBox::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_DRAW: { case NOTIFICATION_DRAW: {
_update_text();
_adjust_width_for_icon(theme_cache.updown_icon); _adjust_width_for_icon(theme_cache.updown_icon);
RID ci = get_canvas_item(); RID ci = get_canvas_item();
@ -242,7 +246,7 @@ void SpinBox::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: { case NOTIFICATION_ENTER_TREE: {
_adjust_width_for_icon(theme_cache.updown_icon); _adjust_width_for_icon(theme_cache.updown_icon);
_value_changed(0); _update_text();
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
@ -250,7 +254,6 @@ void SpinBox::_notification(int p_what) {
} break; } break;
case NOTIFICATION_TRANSLATION_CHANGED: { case NOTIFICATION_TRANSLATION_CHANGED: {
_value_changed(0);
queue_redraw(); queue_redraw();
} break; } break;
@ -279,7 +282,7 @@ void SpinBox::set_suffix(const String &p_suffix) {
} }
suffix = p_suffix; suffix = p_suffix;
_value_changed(0); _update_text();
} }
String SpinBox::get_suffix() const { String SpinBox::get_suffix() const {
@ -292,7 +295,7 @@ void SpinBox::set_prefix(const String &p_prefix) {
} }
prefix = p_prefix; prefix = p_prefix;
_value_changed(0); _update_text();
} }
String SpinBox::get_prefix() const { String SpinBox::get_prefix() const {

@ -46,8 +46,8 @@ class SpinBox : public Range {
void _range_click_timeout(); void _range_click_timeout();
void _release_mouse(); void _release_mouse();
void _update_text();
void _text_submitted(const String &p_string); void _text_submitted(const String &p_string);
virtual void _value_changed(double p_value) override;
void _text_changed(const String &p_string); void _text_changed(const String &p_string);
String prefix; String prefix;

@ -7153,7 +7153,9 @@ void TextEdit::_update_selection_mode_line() {
if (line < carets[caret_idx].selection.selecting_line) { if (line < carets[caret_idx].selection.selecting_line) {
/* Caret is above us. */ /* Caret is above us. */
set_caret_line(line - 1, false, true, 0, caret_idx); set_caret_line(line - 1, false, true, 0, caret_idx);
carets.write[caret_idx].selection.selecting_column = text[get_selection_line(caret_idx)].length(); carets.write[caret_idx].selection.selecting_column = has_selection(caret_idx)
? text[get_selection_line(caret_idx)].length()
: 0;
} else { } else {
/* Caret is below us. */ /* Caret is below us. */
set_caret_line(line + 1, false, true, 0, caret_idx); set_caret_line(line + 1, false, true, 0, caret_idx);

@ -1251,6 +1251,17 @@ Node *Node::get_child(int p_index, bool p_include_internal) const {
} }
} }
TypedArray<Node> Node::get_children(bool p_include_internal) const {
TypedArray<Node> arr;
int cc = get_child_count(p_include_internal);
arr.resize(cc);
for (int i = 0; i < cc; i++) {
arr[i] = get_child(i, p_include_internal);
}
return arr;
}
Node *Node::_get_child_by_name(const StringName &p_name) const { Node *Node::_get_child_by_name(const StringName &p_name) const {
int cc = data.children.size(); int cc = data.children.size();
Node *const *cd = data.children.ptr(); Node *const *cd = data.children.ptr();
@ -2660,17 +2671,6 @@ void Node::queue_free() {
} }
} }
TypedArray<Node> Node::_get_children(bool p_include_internal) const {
TypedArray<Node> arr;
int cc = get_child_count(p_include_internal);
arr.resize(cc);
for (int i = 0; i < cc; i++) {
arr[i] = get_child(i, p_include_internal);
}
return arr;
}
void Node::set_import_path(const NodePath &p_import_path) { void Node::set_import_path(const NodePath &p_import_path) {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
data.import_path = p_import_path; data.import_path = p_import_path;
@ -2824,7 +2824,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child);
ClassDB::bind_method(D_METHOD("reparent", "new_parent", "keep_global_transform"), &Node::reparent, DEFVAL(true)); ClassDB::bind_method(D_METHOD("reparent", "new_parent", "keep_global_transform"), &Node::reparent, DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript.
ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::get_children, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node); ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node);
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);

@ -181,7 +181,6 @@ private:
void _duplicate_signals(const Node *p_original, Node *p_copy) const; void _duplicate_signals(const Node *p_original, Node *p_copy) const;
Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const; Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const;
TypedArray<Node> _get_children(bool p_include_internal = true) const;
TypedArray<StringName> _get_groups() const; TypedArray<StringName> _get_groups() const;
Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@ -311,6 +310,7 @@ public:
int get_child_count(bool p_include_internal = true) const; int get_child_count(bool p_include_internal = true) const;
Node *get_child(int p_index, bool p_include_internal = true) const; Node *get_child(int p_index, bool p_include_internal = true) const;
TypedArray<Node> get_children(bool p_include_internal = true) const;
bool has_node(const NodePath &p_path) const; bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const; Node *get_node_or_null(const NodePath &p_path) const;

@ -474,14 +474,20 @@ void ParticleProcessMaterial::_update_shader() {
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
code += " CUSTOM.y = 0.0;\n"; // phase code += " CUSTOM.y = 0.0;\n"; // phase
code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; code += " CUSTOM.w = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n";
code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n"; // animation offset (0-1) code += " CUSTOM.z = (tex_anim_offset) * mix(anim_offset_min, anim_offset_max, anim_offset_rand);\n\n"; // animation offset (0-1)
code += " if (RESTART_ROT_SCALE) {\n";
code += " TRANSFORM[0].xyz = vec3(1.0, 0.0, 0.0);\n";
code += " TRANSFORM[1].xyz = vec3(0.0, 1.0, 0.0);\n";
code += " TRANSFORM[2].xyz = vec3(0.0, 0.0, 1.0);\n";
code += " }\n\n";
code += " if (RESTART_POSITION) {\n"; code += " if (RESTART_POSITION) {\n";
switch (emission_shape) { switch (emission_shape) {
case EMISSION_SHAPE_POINT: { case EMISSION_SHAPE_POINT: {
//do none, identity (will later be multiplied by emission transform) //do none, identity (will later be multiplied by emission transform)
code += " TRANSFORM = mat4(vec4(1,0,0,0),vec4(0,1,0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; code += " TRANSFORM[3].xyz = vec3(0.0, 0.0, 0.0);\n";
} break; } break;
case EMISSION_SHAPE_SPHERE: { case EMISSION_SHAPE_SPHERE: {
code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n"; code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";

@ -2590,6 +2590,7 @@ void TileSet::_compatibility_conversion() {
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile); TileData *tile_data = atlas_source->get_tile_data(coords, alternative_tile);
ERR_CONTINUE(!tile_data);
tile_data->set_flip_h(flip_h); tile_data->set_flip_h(flip_h);
tile_data->set_flip_v(flip_v); tile_data->set_flip_v(flip_v);

@ -115,7 +115,7 @@ commits.
## enet ## enet
- Upstream: http://enet.bespin.org - Upstream: http://enet.bespin.org
- Version: 1.3.17 (e0e7045b7e056b454b5093cb34df49dc4cee0bee, 2020) - Version: git (ea4607a90dbfbcf4da2669ea998585253d8e70b1, 2023)
- License: MIT - License: MIT
Files extracted from upstream source: Files extracted from upstream source:

@ -68,7 +68,8 @@ typedef enum _ENetSocketOption
ENET_SOCKOPT_RCVTIMEO = 6, ENET_SOCKOPT_RCVTIMEO = 6,
ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_SNDTIMEO = 7,
ENET_SOCKOPT_ERROR = 8, ENET_SOCKOPT_ERROR = 8,
ENET_SOCKOPT_NODELAY = 9 ENET_SOCKOPT_NODELAY = 9,
ENET_SOCKOPT_TTL = 10
} ENetSocketOption; } ENetSocketOption;
typedef enum _ENetSocketShutdown typedef enum _ENetSocketShutdown
@ -179,7 +180,7 @@ typedef struct _ENetOutgoingCommand
enet_uint16 unreliableSequenceNumber; enet_uint16 unreliableSequenceNumber;
enet_uint32 sentTime; enet_uint32 sentTime;
enet_uint32 roundTripTimeout; enet_uint32 roundTripTimeout;
enet_uint32 roundTripTimeoutLimit; enet_uint32 queueTime;
enet_uint32 fragmentOffset; enet_uint32 fragmentOffset;
enet_uint16 fragmentLength; enet_uint16 fragmentLength;
enet_uint16 sendAttempts; enet_uint16 sendAttempts;
@ -222,7 +223,7 @@ enum
ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
ENET_HOST_DEFAULT_MTU = 1400, ENET_HOST_DEFAULT_MTU = 1392,
ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
@ -262,7 +263,8 @@ typedef struct _ENetChannel
typedef enum _ENetPeerFlag typedef enum _ENetPeerFlag
{ {
ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0) ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0),
ENET_PEER_FLAG_CONTINUE_SENDING = (1 << 1)
} ENetPeerFlag; } ENetPeerFlag;
/** /**
@ -322,7 +324,7 @@ typedef struct _ENetPeer
enet_uint16 outgoingReliableSequenceNumber; enet_uint16 outgoingReliableSequenceNumber;
ENetList acknowledgements; ENetList acknowledgements;
ENetList sentReliableCommands; ENetList sentReliableCommands;
ENetList sentUnreliableCommands; ENetList outgoingSendReliableCommands;
ENetList outgoingCommands; ENetList outgoingCommands;
ENetList dispatchedCommands; ENetList dispatchedCommands;
enet_uint16 flags; enet_uint16 flags;
@ -385,7 +387,7 @@ typedef struct _ENetHost
size_t channelLimit; /**< maximum number of channels allowed for connected peers */ size_t channelLimit; /**< maximum number of channels allowed for connected peers */
enet_uint32 serviceTime; enet_uint32 serviceTime;
ENetList dispatchQueue; ENetList dispatchQueue;
int continueSending; enet_uint32 totalQueued;
size_t packetSize; size_t packetSize;
enet_uint16 headerFlags; enet_uint16 headerFlags;
ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
@ -585,6 +587,7 @@ ENET_API void enet_host_channel_limit (ENetHost *, size_t);
ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
extern void enet_host_bandwidth_throttle (ENetHost *); extern void enet_host_bandwidth_throttle (ENetHost *);
extern enet_uint32 enet_host_random_seed (void); extern enet_uint32 enet_host_random_seed (void);
extern enet_uint32 enet_host_random (ENetHost *);
ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
@ -598,6 +601,7 @@ ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32
ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
extern int enet_peer_throttle (ENetPeer *, enet_uint32); extern int enet_peer_throttle (ENetPeer *, enet_uint32);
extern void enet_peer_reset_queues (ENetPeer *); extern void enet_peer_reset_queues (ENetPeer *);
extern int enet_peer_has_outgoing_commands (ENetPeer *);
extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);

@ -535,6 +535,10 @@ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buf
if (err == ERR_BUSY) { if (err == ERR_BUSY) {
return 0; return 0;
} }
if (err == ERR_OUT_OF_MEMORY) {
// A packet above the ENET_PROTOCOL_MAXIMUM_MTU was received.
return -2;
}
if (err != OK) { if (err != OK) {
return -1; return -1;

@ -96,6 +96,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL
host -> totalSentPackets = 0; host -> totalSentPackets = 0;
host -> totalReceivedData = 0; host -> totalReceivedData = 0;
host -> totalReceivedPackets = 0; host -> totalReceivedPackets = 0;
host -> totalQueued = 0;
host -> connectedPeers = 0; host -> connectedPeers = 0;
host -> bandwidthLimitedPeers = 0; host -> bandwidthLimitedPeers = 0;
@ -123,8 +124,8 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL
enet_list_clear (& currentPeer -> acknowledgements); enet_list_clear (& currentPeer -> acknowledgements);
enet_list_clear (& currentPeer -> sentReliableCommands); enet_list_clear (& currentPeer -> sentReliableCommands);
enet_list_clear (& currentPeer -> sentUnreliableCommands);
enet_list_clear (& currentPeer -> outgoingCommands); enet_list_clear (& currentPeer -> outgoingCommands);
enet_list_clear (& currentPeer -> outgoingSendReliableCommands);
enet_list_clear (& currentPeer -> dispatchedCommands); enet_list_clear (& currentPeer -> dispatchedCommands);
enet_peer_reset (currentPeer); enet_peer_reset (currentPeer);
@ -160,6 +161,16 @@ enet_host_destroy (ENetHost * host)
enet_free (host); enet_free (host);
} }
enet_uint32
enet_host_random (ENetHost * host)
{
/* Mulberry32 by Tommy Ettinger */
enet_uint32 n = (host -> randomSeed += 0x6D2B79F5U);
n = (n ^ (n >> 15)) * (n | 1U);
n ^= n + (n ^ (n >> 7)) * (n | 61U);
return n ^ (n >> 14);
}
/** Initiates a connection to a foreign host. /** Initiates a connection to a foreign host.
@param host host seeking the connection @param host host seeking the connection
@param address destination for the connection @param address destination for the connection
@ -199,7 +210,8 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC
currentPeer -> channelCount = channelCount; currentPeer -> channelCount = channelCount;
currentPeer -> state = ENET_PEER_STATE_CONNECTING; currentPeer -> state = ENET_PEER_STATE_CONNECTING;
currentPeer -> address = * address; currentPeer -> address = * address;
currentPeer -> connectID = ++ host -> randomSeed; currentPeer -> connectID = enet_host_random (host);
currentPeer -> mtu = host -> mtu;
if (host -> outgoingBandwidth == 0) if (host -> outgoingBandwidth == 0)
currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;

@ -98,53 +98,46 @@ enet_packet_resize (ENetPacket * packet, size_t dataLength)
return 0; return 0;
} }
static int initializedCRC32 = 0; static const enet_uint32 crcTable [256] =
static enet_uint32 crcTable [256];
static enet_uint32
reflect_crc (int val, int bits)
{ {
int result = 0, bit; 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
for (bit = 0; bit < bits; bit ++) 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
{ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
if(val & 1) result |= 1 << (bits - 1 - bit); 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
val >>= 1; 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
} 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
return result; 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
} 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
static void 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
initialize_crc32 (void) 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
{ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
int byte; 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
for (byte = 0; byte < 256; ++ byte) 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
{ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
enet_uint32 crc = reflect_crc (byte, 8) << 24; 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
int offset; 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x5005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
for(offset = 0; offset < 8; ++ offset)
{
if (crc & 0x80000000)
crc = (crc << 1) ^ 0x04c11db7;
else
crc <<= 1;
}
crcTable [byte] = reflect_crc (crc, 32);
}
initializedCRC32 = 1;
}
enet_uint32 enet_uint32
enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
{ {
enet_uint32 crc = 0xFFFFFFFF; enet_uint32 crc = 0xFFFFFFFF;
if (! initializedCRC32) initialize_crc32 ();
while (bufferCount -- > 0) while (bufferCount -- > 0)
{ {
@ -153,7 +146,7 @@ enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
while (data < dataEnd) while (data < dataEnd)
{ {
crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
} }
++ buffers; ++ buffers;

@ -90,6 +90,13 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
} }
/** Queues a packet to be sent. /** Queues a packet to be sent.
On success, ENet will assume ownership of the packet, and so enet_packet_destroy
should not be called on it thereafter. On failure, the caller still must destroy
the packet on its own as ENet has not queued the packet. The caller can also
check the packet's referenceCount field after sending to check if ENet queued
the packet and thus incremented the referenceCount.
@param peer destination for the packet @param peer destination for the packet
@param channelID channel on which to send @param channelID channel on which to send
@param packet packet to send @param packet packet to send
@ -99,7 +106,7 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
int int
enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
{ {
ENetChannel * channel = & peer -> channels [channelID]; ENetChannel * channel;
ENetProtocol command; ENetProtocol command;
size_t fragmentLength; size_t fragmentLength;
@ -108,6 +115,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
packet -> dataLength > peer -> host -> maximumPacketSize) packet -> dataLength > peer -> host -> maximumPacketSize)
return -1; return -1;
channel = & peer -> channels [channelID];
fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
if (peer -> host -> checksum != NULL) if (peer -> host -> checksum != NULL)
fragmentLength -= sizeof(enet_uint32); fragmentLength -= sizeof(enet_uint32);
@ -320,8 +328,8 @@ enet_peer_reset_queues (ENetPeer * peer)
enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
enet_peer_reset_outgoing_commands (& peer -> outgoingCommands); enet_peer_reset_outgoing_commands (& peer -> outgoingCommands);
enet_peer_reset_outgoing_commands (& peer -> outgoingSendReliableCommands);
enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
if (peer -> channels != NULL && peer -> channelCount > 0) if (peer -> channels != NULL && peer -> channelCount > 0)
@ -563,6 +571,17 @@ enet_peer_disconnect (ENetPeer * peer, enet_uint32 data)
} }
} }
int
enet_peer_has_outgoing_commands (ENetPeer * peer)
{
if (enet_list_empty (& peer -> outgoingCommands) &&
enet_list_empty (& peer -> outgoingSendReliableCommands) &&
enet_list_empty (& peer -> sentReliableCommands))
return 0;
return 1;
}
/** Request a disconnection from a peer, but only after all queued outgoing packets are sent. /** Request a disconnection from a peer, but only after all queued outgoing packets are sent.
@param peer peer to request a disconnection @param peer peer to request a disconnection
@param data data describing the disconnection @param data data describing the disconnection
@ -573,8 +592,7 @@ void
enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
{ {
if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) &&
! (enet_list_empty (& peer -> outgoingCommands) && enet_peer_has_outgoing_commands (peer))
enet_list_empty (& peer -> sentReliableCommands)))
{ {
peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
peer -> eventData = data; peer -> eventData = data;
@ -618,8 +636,6 @@ enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command,
void void
enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand)
{ {
ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength;
if (outgoingCommand -> command.header.channelID == 0xFF) if (outgoingCommand -> command.header.channelID == 0xFF)
@ -630,36 +646,40 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
outgoingCommand -> unreliableSequenceNumber = 0; outgoingCommand -> unreliableSequenceNumber = 0;
} }
else else
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{ {
++ channel -> outgoingReliableSequenceNumber; ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
channel -> outgoingUnreliableSequenceNumber = 0;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
outgoingCommand -> unreliableSequenceNumber = 0; {
} ++ channel -> outgoingReliableSequenceNumber;
else channel -> outgoingUnreliableSequenceNumber = 0;
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
{
++ peer -> outgoingUnsequencedGroup;
outgoingCommand -> reliableSequenceNumber = 0; outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = 0; outgoingCommand -> unreliableSequenceNumber = 0;
} }
else else
{ if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
if (outgoingCommand -> fragmentOffset == 0) {
++ channel -> outgoingUnreliableSequenceNumber; ++ peer -> outgoingUnsequencedGroup;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; outgoingCommand -> reliableSequenceNumber = 0;
outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; outgoingCommand -> unreliableSequenceNumber = 0;
}
else
{
if (outgoingCommand -> fragmentOffset == 0)
++ channel -> outgoingUnreliableSequenceNumber;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
}
} }
outgoingCommand -> sendAttempts = 0; outgoingCommand -> sendAttempts = 0;
outgoingCommand -> sentTime = 0; outgoingCommand -> sentTime = 0;
outgoingCommand -> roundTripTimeout = 0; outgoingCommand -> roundTripTimeout = 0;
outgoingCommand -> roundTripTimeoutLimit = 0;
outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber);
outgoingCommand -> queueTime = ++ peer -> host -> totalQueued;
switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK)
{ {
@ -670,12 +690,16 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
break; break;
default: default:
break; break;
} }
enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); if ((outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0 &&
outgoingCommand -> packet != NULL)
enet_list_insert (enet_list_end (& peer -> outgoingSendReliableCommands), outgoingCommand);
else
enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand);
} }
ENetOutgoingCommand * ENetOutgoingCommand *

@ -9,7 +9,7 @@
#include "enet/time.h" #include "enet/time.h"
#include "enet/enet.h" #include "enet/enet.h"
static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = static const size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
{ {
0, 0,
sizeof (ENetProtocolAcknowledge), sizeof (ENetProtocolAcknowledge),
@ -159,16 +159,16 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e
} }
static void static void
enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer, ENetList * sentUnreliableCommands)
{ {
ENetOutgoingCommand * outgoingCommand; ENetOutgoingCommand * outgoingCommand;
if (enet_list_empty (& peer -> sentUnreliableCommands)) if (enet_list_empty (sentUnreliableCommands))
return; return;
do do
{ {
outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); outgoingCommand = (ENetOutgoingCommand *) enet_list_front (sentUnreliableCommands);
enet_list_remove (& outgoingCommand -> outgoingCommandList); enet_list_remove (& outgoingCommand -> outgoingCommandList);
@ -185,14 +185,38 @@ enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
} }
enet_free (outgoingCommand); enet_free (outgoingCommand);
} while (! enet_list_empty (& peer -> sentUnreliableCommands)); } while (! enet_list_empty (sentUnreliableCommands));
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
enet_list_empty (& peer -> outgoingCommands) && ! enet_peer_has_outgoing_commands (peer))
enet_list_empty (& peer -> sentReliableCommands))
enet_peer_disconnect (peer, peer -> eventData); enet_peer_disconnect (peer, peer -> eventData);
} }
static ENetOutgoingCommand *
enet_protocol_find_sent_reliable_command (ENetList * list, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
{
ENetListIterator currentCommand;
for (currentCommand = enet_list_begin (list);
currentCommand != enet_list_end (list);
currentCommand = enet_list_next (currentCommand))
{
ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) currentCommand;
if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
continue;
if (outgoingCommand -> sendAttempts < 1)
break;
if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
outgoingCommand -> command.header.channelID == channelID)
return outgoingCommand;
}
return NULL;
}
static ENetProtocolCommand static ENetProtocolCommand
enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
{ {
@ -214,24 +238,9 @@ enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliabl
if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
{ {
for (currentCommand = enet_list_begin (& peer -> outgoingCommands); outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingCommands, reliableSequenceNumber, channelID);
currentCommand != enet_list_end (& peer -> outgoingCommands); if (outgoingCommand == NULL)
currentCommand = enet_list_next (currentCommand)) outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingSendReliableCommands, reliableSequenceNumber, channelID);
{
outgoingCommand = (ENetOutgoingCommand *) currentCommand;
if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
continue;
if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE;
if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
outgoingCommand -> command.header.channelID == channelID)
break;
}
if (currentCommand == enet_list_end (& peer -> outgoingCommands))
return ENET_PROTOCOL_COMMAND_NONE;
wasSent = 0; wasSent = 0;
} }
@ -331,6 +340,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
peer -> connectID = command -> connect.connectID; peer -> connectID = command -> connect.connectID;
peer -> address = host -> receivedAddress; peer -> address = host -> receivedAddress;
peer -> mtu = host -> mtu;
peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
@ -375,7 +385,8 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
mtu = ENET_PROTOCOL_MAXIMUM_MTU; mtu = ENET_PROTOCOL_MAXIMUM_MTU;
peer -> mtu = mtu; if (mtu < peer -> mtu)
peer -> mtu = mtu;
if (host -> outgoingBandwidth == 0 && if (host -> outgoingBandwidth == 0 &&
peer -> incomingBandwidth == 0) peer -> incomingBandwidth == 0)
@ -542,7 +553,8 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
* currentData += fragmentLength; * currentData += fragmentLength;
if (fragmentLength > host -> maximumPacketSize || if (fragmentLength <= 0 ||
fragmentLength > host -> maximumPacketSize ||
* currentData < host -> receivedData || * currentData < host -> receivedData ||
* currentData > & host -> receivedData [host -> receivedDataLength]) * currentData > & host -> receivedData [host -> receivedDataLength])
return -1; return -1;
@ -566,6 +578,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
fragmentNumber >= fragmentCount || fragmentNumber >= fragmentCount ||
totalLength > host -> maximumPacketSize || totalLength > host -> maximumPacketSize ||
totalLength < fragmentCount ||
fragmentOffset >= totalLength || fragmentOffset >= totalLength ||
fragmentLength > totalLength - fragmentOffset) fragmentLength > totalLength - fragmentOffset)
return -1; return -1;
@ -921,8 +934,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer *
break; break;
case ENET_PEER_STATE_DISCONNECT_LATER: case ENET_PEER_STATE_DISCONNECT_LATER:
if (enet_list_empty (& peer -> outgoingCommands) && if (! enet_peer_has_outgoing_commands (peer))
enet_list_empty (& peer -> sentReliableCommands))
enet_peer_disconnect (peer, peer -> eventData); enet_peer_disconnect (peer, peer -> eventData);
break; break;
@ -1230,6 +1242,9 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
& buffer, & buffer,
1); 1);
if (receivedLength == -2)
continue;
if (receivedLength < 0) if (receivedLength < 0)
return -1; return -1;
@ -1293,7 +1308,7 @@ enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
{ {
host -> continueSending = 1; peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
break; break;
} }
@ -1333,10 +1348,11 @@ static int
enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
{ {
ENetOutgoingCommand * outgoingCommand; ENetOutgoingCommand * outgoingCommand;
ENetListIterator currentCommand, insertPosition; ENetListIterator currentCommand, insertPosition, insertSendReliablePosition;
currentCommand = enet_list_begin (& peer -> sentReliableCommands); currentCommand = enet_list_begin (& peer -> sentReliableCommands);
insertPosition = enet_list_begin (& peer -> outgoingCommands); insertPosition = enet_list_begin (& peer -> outgoingCommands);
insertSendReliablePosition = enet_list_begin (& peer -> outgoingSendReliableCommands);
while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
{ {
@ -1353,7 +1369,7 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
if (peer -> earliestTimeout != 0 && if (peer -> earliestTimeout != 0 &&
(ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum ||
(outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && ((1 << (outgoingCommand -> sendAttempts - 1)) >= peer -> timeoutLimit &&
ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum)))
{ {
enet_protocol_notify_disconnect (host, peer, event); enet_protocol_notify_disconnect (host, peer, event);
@ -1361,14 +1377,18 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
return 1; return 1;
} }
if (outgoingCommand -> packet != NULL)
peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
++ peer -> packetsLost; ++ peer -> packetsLost;
outgoingCommand -> roundTripTimeout *= 2; outgoingCommand -> roundTripTimeout *= 2;
enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); if (outgoingCommand -> packet != NULL)
{
peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
enet_list_insert (insertSendReliablePosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
}
else
enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
! enet_list_empty (& peer -> sentReliableCommands)) ! enet_list_empty (& peer -> sentReliableCommands))
@ -1383,22 +1403,41 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
} }
static int static int
enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer, ENetList * sentUnreliableCommands)
{ {
ENetProtocol * command = & host -> commands [host -> commandCount]; ENetProtocol * command = & host -> commands [host -> commandCount];
ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
ENetOutgoingCommand * outgoingCommand; ENetOutgoingCommand * outgoingCommand;
ENetListIterator currentCommand; ENetListIterator currentCommand, currentSendReliableCommand;
ENetChannel *channel; ENetChannel *channel = NULL;
enet_uint16 reliableWindow; enet_uint16 reliableWindow = 0;
size_t commandSize; size_t commandSize;
int windowExceeded = 0, windowWrap = 0, canPing = 1; int windowWrap = 0, canPing = 1;
currentCommand = enet_list_begin (& peer -> outgoingCommands); currentCommand = enet_list_begin (& peer -> outgoingCommands);
currentSendReliableCommand = enet_list_begin (& peer -> outgoingSendReliableCommands);
while (currentCommand != enet_list_end (& peer -> outgoingCommands))
for (;;)
{ {
outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (currentCommand != enet_list_end (& peer -> outgoingCommands))
{
outgoingCommand = (ENetOutgoingCommand *) currentCommand;
if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands) &&
ENET_TIME_LESS (((ENetOutgoingCommand *) currentSendReliableCommand) -> queueTime, outgoingCommand -> queueTime))
goto useSendReliableCommand;
currentCommand = enet_list_next (currentCommand);
}
else
if (currentSendReliableCommand != enet_list_end (& peer -> outgoingSendReliableCommands))
{
useSendReliableCommand:
outgoingCommand = (ENetOutgoingCommand *) currentSendReliableCommand;
currentSendReliableCommand = enet_list_next (currentSendReliableCommand);
}
else
break;
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{ {
@ -1406,33 +1445,29 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
if (channel != NULL) if (channel != NULL)
{ {
if (! windowWrap && if (windowWrap)
outgoingCommand -> sendAttempts < 1 && continue;
else
if (outgoingCommand -> sendAttempts < 1 &&
! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) &&
(channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE ||
channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) | channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) |
(((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) (((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))))
windowWrap = 1;
if (windowWrap)
{ {
currentCommand = enet_list_next (currentCommand); windowWrap = 1;
currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands);
continue; continue;
} }
} }
if (outgoingCommand -> packet != NULL) if (outgoingCommand -> packet != NULL)
{ {
if (! windowExceeded) enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
{
enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
windowExceeded = 1;
}
if (windowExceeded)
{ {
currentCommand = enet_list_next (currentCommand); currentSendReliableCommand = enet_list_end (& peer -> outgoingSendReliableCommands);
continue; continue;
} }
@ -1448,13 +1483,11 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
(outgoingCommand -> packet != NULL && (outgoingCommand -> packet != NULL &&
(enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
{ {
host -> continueSending = 1; peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
break; break;
} }
currentCommand = enet_list_next (currentCommand);
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{ {
if (channel != NULL && outgoingCommand -> sendAttempts < 1) if (channel != NULL && outgoingCommand -> sendAttempts < 1)
@ -1466,10 +1499,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
++ outgoingCommand -> sendAttempts; ++ outgoingCommand -> sendAttempts;
if (outgoingCommand -> roundTripTimeout == 0) if (outgoingCommand -> roundTripTimeout == 0)
{ outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
}
if (enet_list_empty (& peer -> sentReliableCommands)) if (enet_list_empty (& peer -> sentReliableCommands))
peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
@ -1522,7 +1552,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
enet_list_remove (& outgoingCommand -> outgoingCommandList); enet_list_remove (& outgoingCommand -> outgoingCommandList);
if (outgoingCommand -> packet != NULL) if (outgoingCommand -> packet != NULL)
enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); enet_list_insert (enet_list_end (sentUnreliableCommands), outgoingCommand);
} }
buffer -> data = command; buffer -> data = command;
@ -1555,9 +1585,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
host -> bufferCount = buffer - host -> buffers; host -> bufferCount = buffer - host -> buffers;
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
enet_list_empty (& peer -> outgoingCommands) && ! enet_peer_has_outgoing_commands (peer) &&
enet_list_empty (& peer -> sentReliableCommands) && enet_list_empty (sentUnreliableCommands))
enet_list_empty (& peer -> sentUnreliableCommands))
enet_peer_disconnect (peer, peer -> eventData); enet_peer_disconnect (peer, peer -> eventData);
return canPing; return canPing;
@ -1568,22 +1597,24 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
{ {
enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
ENetPeer * currentPeer; int sentLength = 0;
int sentLength;
size_t shouldCompress = 0; size_t shouldCompress = 0;
ENetList sentUnreliableCommands;
host -> continueSending = 1;
while (host -> continueSending) enet_list_clear (& sentUnreliableCommands);
for (host -> continueSending = 0,
currentPeer = host -> peers; for (int sendPass = 0, continueSending = 0; sendPass <= continueSending; ++ sendPass)
for (ENetPeer * currentPeer = host -> peers;
currentPeer < & host -> peers [host -> peerCount]; currentPeer < & host -> peers [host -> peerCount];
++ currentPeer) ++ currentPeer)
{ {
if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
currentPeer -> state == ENET_PEER_STATE_ZOMBIE) currentPeer -> state == ENET_PEER_STATE_ZOMBIE ||
(sendPass > 0 && ! (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING)))
continue; continue;
currentPeer -> flags &= ~ ENET_PEER_FLAG_CONTINUE_SENDING;
host -> headerFlags = 0; host -> headerFlags = 0;
host -> commandCount = 0; host -> commandCount = 0;
host -> bufferCount = 1; host -> bufferCount = 1;
@ -1600,21 +1631,22 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
return 1; return 1;
else else
continue; goto nextPeer;
} }
if ((enet_list_empty (& currentPeer -> outgoingCommands) || if (((enet_list_empty (& currentPeer -> outgoingCommands) &&
enet_protocol_check_outgoing_commands (host, currentPeer)) && enet_list_empty (& currentPeer -> outgoingSendReliableCommands)) ||
enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands)) &&
enet_list_empty (& currentPeer -> sentReliableCommands) && enet_list_empty (& currentPeer -> sentReliableCommands) &&
ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
{ {
enet_peer_ping (currentPeer); enet_peer_ping (currentPeer);
enet_protocol_check_outgoing_commands (host, currentPeer); enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands);
} }
if (host -> commandCount == 0) if (host -> commandCount == 0)
continue; goto nextPeer;
if (currentPeer -> packetLossEpoch == 0) if (currentPeer -> packetLossEpoch == 0)
currentPeer -> packetLossEpoch = host -> serviceTime; currentPeer -> packetLossEpoch = host -> serviceTime;
@ -1625,7 +1657,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
#ifdef ENET_DEBUG #ifdef ENET_DEBUG
printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands) + enet_list_size (& currentPeer -> outgoingSendReliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
#endif #endif
currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4; currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4;
@ -1687,13 +1719,17 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
enet_protocol_remove_sent_unreliable_commands (currentPeer); enet_protocol_remove_sent_unreliable_commands (currentPeer, & sentUnreliableCommands);
if (sentLength < 0) if (sentLength < 0)
return -1; return -1;
host -> totalSentData += sentLength; host -> totalSentData += sentLength;
host -> totalSentPackets ++; host -> totalSentPackets ++;
nextPeer:
if (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING)
continueSending = sendPass + 1;
} }
return 0; return 0;