Merge pull request #79708 from YuriSizov/4.0-cherrypicks

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

View File

@ -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/).
## [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
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.
[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.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

View File

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

View File

@ -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);
dot = CLAMP(dot, -1.0f, 1.0f);
const real_t angle_rads = Math::acos(dot);
set_axis_angle(axis, angle_rads);
*this = Basis(axis, angle_rads) * (*this);
}
}

View File

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

View File

@ -22,7 +22,7 @@
<return type="bool" />
<description>
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>
</method>
<method name="unlock">

View File

@ -1,13 +1,17 @@
<?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">
<brief_description>
A snippet of shader code to be included in a [Shader] with [code]#include[/code].
</brief_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>
<tutorials>
<link title="Shader preprocessor">$DOCS_URL/tutorials/shaders/shader_reference/shader_preprocessor.html</link>
</tutorials>
<members>
<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>
</members>
</class>

View File

@ -884,11 +884,11 @@
<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].
[codeblock]
var a = "12.35".to_float() # a is 12.35
var b = "1.2.3".to_float() # b is 1.2
var c = "12xy3".to_float() # c is 12.0
var d = "1e3".to_float() # d is 1000.0
var e = "Hello!".to_int() # e is 0.0
var a = "12.35".to_float() # a is 12.35
var b = "1.2.3".to_float() # b is 1.2
var c = "12xy3".to_float() # c is 12.0
var d = "1e3".to_float() # d is 1000.0
var e = "Hello!".to_float() # e is 0.0
[/codeblock]
</description>
</method>

View File

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

View File

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

View File

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

View File

@ -201,7 +201,7 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
if (t != args[idx].get_type()) {
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 *ptrs[1] = { &old };
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()) {
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 *ptrs[1] = { &old };
Variant::construct(t, args.write[idx], (const Variant **)ptrs, 1, err);

View File

@ -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")));
if (results_count == 0) {
matches_label->set_text("No match");
matches_label->set_text(TTR("No match"));
} else if (results_count_to_current == -1) {
matches_label->set_text(vformat(TTRN("%d match", "%d matches", results_count), results_count));
} else {

View File

@ -262,7 +262,7 @@ void EditorFileSystem::_scan_filesystem() {
} else {
Vector<String> split = l.split("::");
ERR_CONTINUE(split.size() != 9);
ERR_CONTINUE(split.size() < 9);
String name = split[0];
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.
_save_filesystem_cache();
_update_pending_script_classes();
importing = false;
if (!is_scanning()) {
emit_signal(SNAME("filesystem_changed"));

View File

@ -31,6 +31,7 @@
#include "post_import_plugin_skeleton_rest_fixer.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/skeleton_3d.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.
}
// 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.
if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) {
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.
silhouette_diff.resize(src_skeleton->get_bone_count());
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"])) {
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();
while (bones_to_process.size() > 0) {
int prof_idx = bones_to_process[0];
@ -450,7 +414,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
// For skin modification in overwrite rest.
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;
@ -645,14 +609,20 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
if (is_rest_changed) {
// Fix skin.
{
HashSet<Ref<Skin>> mutated_skins;
TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D");
int skin_idx = 0;
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());
if (skin.is_null()) {
continue;
}
if (mutated_skins.has(skin)) {
continue;
}
mutated_skins.insert(skin);
Node *node = mi->get_node(mi->get_skeleton_path());
ERR_CONTINUE(!node);
@ -662,19 +632,39 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
continue;
}
Vector<Transform3D> ibm_diff = ibm_diffs[skin_idx];
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) {
Transform3D new_rest = silhouette_diff[bone_idx] * src_skeleton->get_bone_global_rest(bone_idx);
skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[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];
adjust_transform.scale(global_transform.basis.get_scale_local());
skin->set_bind_pose(i, adjust_transform * skin->get_bind_pose(i));
}
}
}
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());
skin_idx++;
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());
}
}
}

View File

@ -386,7 +386,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
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 last = (frames / format_channels) - 1;
bool found = false;

View File

@ -244,13 +244,16 @@ void TileAtlasView::_draw_base_tiles() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); 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++) {
// Update the y to max value.
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());
// 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() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
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);
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.
Vector2i offset_pos;
if (transposed) {
@ -381,7 +414,7 @@ void TileAtlasView::_draw_alternatives() {
}
// 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.
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_atlas_source = p_tile_set_atlas_source;
_clear_material_canvas_items();
if (!tile_set) {
return;
}
@ -690,3 +725,7 @@ TileAtlasView::TileAtlasView() {
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
alternative_tiles_drawing_root->add_child(alternatives_draw);
}
TileAtlasView::~TileAtlasView() {
_clear_material_canvas_items();
}

View File

@ -89,7 +89,11 @@ private:
Control *base_tiles_drawing_root = nullptr;
Control *base_tiles_draw = nullptr;
HashMap<Ref<Material>, RID> material_tiles_draw;
HashMap<Ref<Material>, RID> material_alternatives_draw;
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;
void _draw_base_tiles_texture_grid();
@ -157,6 +161,7 @@ public:
void queue_redraw();
TileAtlasView();
~TileAtlasView();
};
#endif // TILE_ATLAS_VIEW_H

View File

@ -106,9 +106,11 @@ void TilesEditorPlugin::_thread() {
Vector2i coords = tile_map->get_cell_atlas_coords(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();
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);
if (atlas_source->has_tile(coords) && atlas_source->has_alternative_tile(coords, alternative)) {
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);
}
}
}

View File

@ -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());
}
}
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();
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

View File

@ -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());
}
}
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();
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

View File

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

View File

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

View File

@ -81,7 +81,9 @@ open class GodotEditor : FullScreenGodotApp() {
private val commandLineParams = ArrayList<String>()
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)
updateCommandLineParams(params)
@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
checkForProjectPermissionsToEnable()
runOnUiThread {
// Enable long press, panning and scaling gestures
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>?) {
// Update the list of command line params with the new args
commandLineParams.clear()

View File

@ -39,4 +39,9 @@ class GodotGame : GodotEditor() {
override fun enableLongPressGestures() = 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.
}
}

View File

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

View File

@ -685,8 +685,14 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
Intent notifierIntent = new Intent(activity, activity.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0,
notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent;
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;
try {

View File

@ -42,10 +42,12 @@ import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 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_VIBRATE_PERMISSION = 3;
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;
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>
* @param name the name of the requested permission.
* @param permissionName the name of the requested permission.
* @param activity the caller activity for this method.
* @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) {
// Not necessary, asked on install already
return true;
}
if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
return false;
}
switch (permissionName) {
case "RECORD_AUDIO":
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) {
activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
return false;
}
case "CAMERA":
case Manifest.permission.CAMERA:
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) {
activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
return false;
case "VIBRATE":
case Manifest.permission.VIBRATE:
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".
*/
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) {
return true;
}
@ -115,6 +154,9 @@ public final class PermissionsUtil {
List<String> requestedPermissions = new ArrayList<>();
for (String manifestPermission : manifestPermissions) {
if (excludes != null && excludes.contains(manifestPermission)) {
continue;
}
try {
if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {

View File

@ -551,10 +551,12 @@ void GPUParticles2D::_notification(int 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);
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;

View File

@ -417,15 +417,6 @@ NodePath GPUParticles3D::get_sub_emitter() const {
void GPUParticles3D::_notification(int 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
// the shot ends the editor can properly update.
case NOTIFICATION_INTERNAL_PROCESS: {
@ -450,6 +441,17 @@ void GPUParticles3D::_notification(int p_what) {
RS::get_singleton()->particles_set_subemitter(particles, RID());
} 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: {
// 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)) {

View File

@ -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) {
if (blending_delta <= 0.01f) {
// 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;
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:
new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos));

View File

@ -1527,6 +1527,22 @@ void LineEdit::set_text(String p_text) {
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) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {

View File

@ -282,6 +282,7 @@ public:
void set_text(String p_text);
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);
TextDirection get_text_direction() const;

View File

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

View File

@ -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) {
double prev_val = shared->val;
set_value_no_signal(p_val);
_set_value_no_signal(p_val);
if (shared->val != prev_val) {
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) {
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;
}
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) {
if (shared->min == p_min) {
return;

View File

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

View File

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

View File

@ -39,7 +39,7 @@ Size2 SpinBox::get_minimum_size() const {
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()));
if (is_localizing_numeral_system()) {
value = TS->format_number(value);
@ -54,8 +54,7 @@ void SpinBox::_value_changed(double p_value) {
}
}
line_edit->set_text(value);
Range::_value_changed(p_value);
line_edit->set_text_with_selection(value);
}
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) {
set_value(value);
}
_value_changed(0);
_update_text();
}
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() {
int col = line_edit->get_caret_column();
_value_changed(0); // Update the LineEdit's text.
_update_text();
line_edit->set_caret_column(col);
// 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() {
// 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.
if (line_edit->is_menu_visible()) {
return;
@ -228,6 +231,7 @@ void SpinBox::_update_theme_item_cache() {
void SpinBox::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
_update_text();
_adjust_width_for_icon(theme_cache.updown_icon);
RID ci = get_canvas_item();
@ -242,7 +246,7 @@ void SpinBox::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
_adjust_width_for_icon(theme_cache.updown_icon);
_value_changed(0);
_update_text();
} break;
case NOTIFICATION_EXIT_TREE: {
@ -250,7 +254,6 @@ void SpinBox::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
_value_changed(0);
queue_redraw();
} break;
@ -279,7 +282,7 @@ void SpinBox::set_suffix(const String &p_suffix) {
}
suffix = p_suffix;
_value_changed(0);
_update_text();
}
String SpinBox::get_suffix() const {
@ -292,7 +295,7 @@ void SpinBox::set_prefix(const String &p_prefix) {
}
prefix = p_prefix;
_value_changed(0);
_update_text();
}
String SpinBox::get_prefix() const {

View File

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

View File

@ -7153,7 +7153,9 @@ void TextEdit::_update_selection_mode_line() {
if (line < carets[caret_idx].selection.selecting_line) {
/* Caret is above us. */
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 {
/* Caret is below us. */
set_caret_line(line + 1, false, true, 0, caret_idx);

View File

@ -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 {
int cc = data.children.size();
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) {
#ifdef TOOLS_ENABLED
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("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_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("has_node", "path"), &Node::has_node);
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);

View File

@ -181,7 +181,6 @@ private:
void _duplicate_signals(const Node *p_original, Node *p_copy) 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;
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;
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;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;

View File

@ -474,14 +474,20 @@ void ParticleProcessMaterial::_update_shader() {
code += " CUSTOM.x = base_angle * degree_to_rad;\n"; // angle
code += " CUSTOM.y = 0.0;\n"; // phase
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";
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//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;
case EMISSION_SHAPE_SPHERE: {
code += " float s = rand_from_seed(alt_seed) * 2.0 - 1.0;\n";

View File

@ -2590,6 +2590,7 @@ void TileSet::_compatibility_conversion() {
compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_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_v(flip_v);

View File

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

View File

@ -68,7 +68,8 @@ typedef enum _ENetSocketOption
ENET_SOCKOPT_RCVTIMEO = 6,
ENET_SOCKOPT_SNDTIMEO = 7,
ENET_SOCKOPT_ERROR = 8,
ENET_SOCKOPT_NODELAY = 9
ENET_SOCKOPT_NODELAY = 9,
ENET_SOCKOPT_TTL = 10
} ENetSocketOption;
typedef enum _ENetSocketShutdown
@ -179,7 +180,7 @@ typedef struct _ENetOutgoingCommand
enet_uint16 unreliableSequenceNumber;
enet_uint32 sentTime;
enet_uint32 roundTripTimeout;
enet_uint32 roundTripTimeoutLimit;
enet_uint32 queueTime;
enet_uint32 fragmentOffset;
enet_uint16 fragmentLength;
enet_uint16 sendAttempts;
@ -222,7 +223,7 @@ enum
ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
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_WAITING_DATA = 32 * 1024 * 1024,
@ -262,7 +263,8 @@ typedef struct _ENetChannel
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;
/**
@ -322,7 +324,7 @@ typedef struct _ENetPeer
enet_uint16 outgoingReliableSequenceNumber;
ENetList acknowledgements;
ENetList sentReliableCommands;
ENetList sentUnreliableCommands;
ENetList outgoingSendReliableCommands;
ENetList outgoingCommands;
ENetList dispatchedCommands;
enet_uint16 flags;
@ -385,7 +387,7 @@ typedef struct _ENetHost
size_t channelLimit; /**< maximum number of channels allowed for connected peers */
enet_uint32 serviceTime;
ENetList dispatchQueue;
int continueSending;
enet_uint32 totalQueued;
size_t packetSize;
enet_uint16 headerFlags;
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);
extern void enet_host_bandwidth_throttle (ENetHost *);
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 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);
extern int enet_peer_throttle (ENetPeer *, enet_uint32);
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 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);

View File

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

View File

@ -96,6 +96,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL
host -> totalSentPackets = 0;
host -> totalReceivedData = 0;
host -> totalReceivedPackets = 0;
host -> totalQueued = 0;
host -> connectedPeers = 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 -> sentReliableCommands);
enet_list_clear (& currentPeer -> sentUnreliableCommands);
enet_list_clear (& currentPeer -> outgoingCommands);
enet_list_clear (& currentPeer -> outgoingSendReliableCommands);
enet_list_clear (& currentPeer -> dispatchedCommands);
enet_peer_reset (currentPeer);
@ -160,6 +161,16 @@ enet_host_destroy (ENetHost * 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.
@param host host seeking 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 -> state = ENET_PEER_STATE_CONNECTING;
currentPeer -> address = * address;
currentPeer -> connectID = ++ host -> randomSeed;
currentPeer -> connectID = enet_host_random (host);
currentPeer -> mtu = host -> mtu;
if (host -> outgoingBandwidth == 0)
currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;

View File

@ -98,53 +98,46 @@ enet_packet_resize (ENetPacket * packet, size_t dataLength)
return 0;
}
static int initializedCRC32 = 0;
static enet_uint32 crcTable [256];
static enet_uint32
reflect_crc (int val, int bits)
static const enet_uint32 crcTable [256] =
{
int result = 0, bit;
0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
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 (bit = 0; bit < bits; bit ++)
{
if(val & 1) result |= 1 << (bits - 1 - bit);
val >>= 1;
}
return result;
}
static void
initialize_crc32 (void)
{
int byte;
for (byte = 0; byte < 256; ++ byte)
{
enet_uint32 crc = reflect_crc (byte, 8) << 24;
int offset;
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_crc32 (const ENetBuffer * buffers, size_t bufferCount)
{
enet_uint32 crc = 0xFFFFFFFF;
if (! initializedCRC32) initialize_crc32 ();
while (bufferCount -- > 0)
{
@ -153,7 +146,7 @@ enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
while (data < dataEnd)
{
crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
}
++ buffers;

View File

@ -90,6 +90,13 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
}
/** 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 channelID channel on which to send
@param packet packet to send
@ -99,7 +106,7 @@ enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
int
enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
{
ENetChannel * channel = & peer -> channels [channelID];
ENetChannel * channel;
ENetProtocol command;
size_t fragmentLength;
@ -108,6 +115,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
packet -> dataLength > peer -> host -> maximumPacketSize)
return -1;
channel = & peer -> channels [channelID];
fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
if (peer -> host -> checksum != NULL)
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_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 -> outgoingSendReliableCommands);
enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
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.
@param peer peer to request a disconnection
@param data data describing the disconnection
@ -573,8 +592,7 @@ void
enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
{
if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) &&
! (enet_list_empty (& peer -> outgoingCommands) &&
enet_list_empty (& peer -> sentReliableCommands)))
enet_peer_has_outgoing_commands (peer))
{
peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
peer -> eventData = data;
@ -618,8 +636,6 @@ enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command,
void
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;
if (outgoingCommand -> command.header.channelID == 0xFF)
@ -630,36 +646,40 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
outgoingCommand -> unreliableSequenceNumber = 0;
}
else
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
++ channel -> outgoingReliableSequenceNumber;
channel -> outgoingUnreliableSequenceNumber = 0;
ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = 0;
}
else
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
{
++ peer -> outgoingUnsequencedGroup;
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
++ channel -> outgoingReliableSequenceNumber;
channel -> outgoingUnreliableSequenceNumber = 0;
outgoingCommand -> reliableSequenceNumber = 0;
outgoingCommand -> unreliableSequenceNumber = 0;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = 0;
}
else
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
{
++ peer -> outgoingUnsequencedGroup;
outgoingCommand -> reliableSequenceNumber = 0;
outgoingCommand -> unreliableSequenceNumber = 0;
}
else
{
if (outgoingCommand -> fragmentOffset == 0)
++ channel -> outgoingUnreliableSequenceNumber;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
}
}
else
{
if (outgoingCommand -> fragmentOffset == 0)
++ channel -> outgoingUnreliableSequenceNumber;
outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
}
outgoingCommand -> sendAttempts = 0;
outgoingCommand -> sentTime = 0;
outgoingCommand -> roundTripTimeout = 0;
outgoingCommand -> roundTripTimeoutLimit = 0;
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)
{
@ -670,12 +690,16 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin
case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
break;
default:
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 *

View File

@ -9,7 +9,7 @@
#include "enet/time.h"
#include "enet/enet.h"
static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
static const size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
{
0,
sizeof (ENetProtocolAcknowledge),
@ -159,16 +159,16 @@ enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * e
}
static void
enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer, ENetList * sentUnreliableCommands)
{
ENetOutgoingCommand * outgoingCommand;
if (enet_list_empty (& peer -> sentUnreliableCommands))
if (enet_list_empty (sentUnreliableCommands))
return;
do
{
outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
outgoingCommand = (ENetOutgoingCommand *) enet_list_front (sentUnreliableCommands);
enet_list_remove (& outgoingCommand -> outgoingCommandList);
@ -185,14 +185,38 @@ enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
}
enet_free (outgoingCommand);
} while (! enet_list_empty (& peer -> sentUnreliableCommands));
} while (! enet_list_empty (sentUnreliableCommands));
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
enet_list_empty (& peer -> outgoingCommands) &&
enet_list_empty (& peer -> sentReliableCommands))
! enet_peer_has_outgoing_commands (peer))
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
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))
{
for (currentCommand = enet_list_begin (& peer -> outgoingCommands);
currentCommand != enet_list_end (& peer -> outgoingCommands);
currentCommand = enet_list_next (currentCommand))
{
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;
outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingCommands, reliableSequenceNumber, channelID);
if (outgoingCommand == NULL)
outgoingCommand = enet_protocol_find_sent_reliable_command (& peer -> outgoingSendReliableCommands, reliableSequenceNumber, channelID);
wasSent = 0;
}
@ -331,6 +340,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
peer -> connectID = command -> connect.connectID;
peer -> address = host -> receivedAddress;
peer -> mtu = host -> mtu;
peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
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)
mtu = ENET_PROTOCOL_MAXIMUM_MTU;
peer -> mtu = mtu;
if (mtu < peer -> mtu)
peer -> mtu = mtu;
if (host -> outgoingBandwidth == 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);
* currentData += fragmentLength;
if (fragmentLength > host -> maximumPacketSize ||
if (fragmentLength <= 0 ||
fragmentLength > host -> maximumPacketSize ||
* currentData < host -> receivedData ||
* currentData > & host -> receivedData [host -> receivedDataLength])
return -1;
@ -566,6 +578,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
fragmentNumber >= fragmentCount ||
totalLength > host -> maximumPacketSize ||
totalLength < fragmentCount ||
fragmentOffset >= totalLength ||
fragmentLength > totalLength - fragmentOffset)
return -1;
@ -921,8 +934,7 @@ enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer *
break;
case ENET_PEER_STATE_DISCONNECT_LATER:
if (enet_list_empty (& peer -> outgoingCommands) &&
enet_list_empty (& peer -> sentReliableCommands))
if (! enet_peer_has_outgoing_commands (peer))
enet_peer_disconnect (peer, peer -> eventData);
break;
@ -1230,6 +1242,9 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
& buffer,
1);
if (receivedLength == -2)
continue;
if (receivedLength < 0)
return -1;
@ -1293,7 +1308,7 @@ enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
{
host -> continueSending = 1;
peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
break;
}
@ -1333,10 +1348,11 @@ static int
enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
{
ENetOutgoingCommand * outgoingCommand;
ENetListIterator currentCommand, insertPosition;
ENetListIterator currentCommand, insertPosition, insertSendReliablePosition;
currentCommand = enet_list_begin (& peer -> sentReliableCommands);
insertPosition = enet_list_begin (& peer -> outgoingCommands);
insertSendReliablePosition = enet_list_begin (& peer -> outgoingSendReliableCommands);
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 &&
(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_protocol_notify_disconnect (host, peer, event);
@ -1361,14 +1377,18 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
return 1;
}
if (outgoingCommand -> packet != NULL)
peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
++ peer -> packetsLost;
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) &&
! enet_list_empty (& peer -> sentReliableCommands))
@ -1383,22 +1403,41 @@ enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * even
}
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];
ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
ENetOutgoingCommand * outgoingCommand;
ENetListIterator currentCommand;
ENetChannel *channel;
enet_uint16 reliableWindow;
ENetListIterator currentCommand, currentSendReliableCommand;
ENetChannel *channel = NULL;
enet_uint16 reliableWindow = 0;
size_t commandSize;
int windowExceeded = 0, windowWrap = 0, canPing = 1;
int windowWrap = 0, canPing = 1;
currentCommand = enet_list_begin (& peer -> outgoingCommands);
while (currentCommand != enet_list_end (& peer -> outgoingCommands))
currentSendReliableCommand = enet_list_begin (& peer -> outgoingSendReliableCommands);
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)
{
@ -1406,33 +1445,29 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
if (channel != NULL)
{
if (! windowWrap &&
outgoingCommand -> sendAttempts < 1 &&
if (windowWrap)
continue;
else
if (outgoingCommand -> sendAttempts < 1 &&
! (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 -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << 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;
}
}
if (outgoingCommand -> packet != NULL)
{
if (! windowExceeded)
enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
{
enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
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;
}
@ -1448,13 +1483,11 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
(outgoingCommand -> packet != NULL &&
(enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
{
host -> continueSending = 1;
peer -> flags |= ENET_PEER_FLAG_CONTINUE_SENDING;
break;
}
currentCommand = enet_list_next (currentCommand);
if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
{
if (channel != NULL && outgoingCommand -> sendAttempts < 1)
@ -1466,10 +1499,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
++ outgoingCommand -> sendAttempts;
if (outgoingCommand -> roundTripTimeout == 0)
{
outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
}
outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
if (enet_list_empty (& peer -> sentReliableCommands))
peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
@ -1522,7 +1552,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
enet_list_remove (& outgoingCommand -> outgoingCommandList);
if (outgoingCommand -> packet != NULL)
enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
enet_list_insert (enet_list_end (sentUnreliableCommands), outgoingCommand);
}
buffer -> data = command;
@ -1555,9 +1585,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
host -> bufferCount = buffer - host -> buffers;
if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
enet_list_empty (& peer -> outgoingCommands) &&
enet_list_empty (& peer -> sentReliableCommands) &&
enet_list_empty (& peer -> sentUnreliableCommands))
! enet_peer_has_outgoing_commands (peer) &&
enet_list_empty (sentUnreliableCommands))
enet_peer_disconnect (peer, peer -> eventData);
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)];
ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
ENetPeer * currentPeer;
int sentLength;
int sentLength = 0;
size_t shouldCompress = 0;
host -> continueSending = 1;
ENetList sentUnreliableCommands;
while (host -> continueSending)
for (host -> continueSending = 0,
currentPeer = host -> peers;
enet_list_clear (& sentUnreliableCommands);
for (int sendPass = 0, continueSending = 0; sendPass <= continueSending; ++ sendPass)
for (ENetPeer * currentPeer = host -> peers;
currentPeer < & host -> peers [host -> peerCount];
++ currentPeer)
{
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;
currentPeer -> flags &= ~ ENET_PEER_FLAG_CONTINUE_SENDING;
host -> headerFlags = 0;
host -> commandCount = 0;
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)
return 1;
else
continue;
goto nextPeer;
}
if ((enet_list_empty (& currentPeer -> outgoingCommands) ||
enet_protocol_check_outgoing_commands (host, currentPeer)) &&
if (((enet_list_empty (& currentPeer -> outgoingCommands) &&
enet_list_empty (& currentPeer -> outgoingSendReliableCommands)) ||
enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands)) &&
enet_list_empty (& currentPeer -> sentReliableCommands) &&
ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
{
enet_peer_ping (currentPeer);
enet_protocol_check_outgoing_commands (host, currentPeer);
enet_protocol_check_outgoing_commands (host, currentPeer, & sentUnreliableCommands);
}
if (host -> commandCount == 0)
continue;
goto nextPeer;
if (currentPeer -> packetLossEpoch == 0)
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;
#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
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);
enet_protocol_remove_sent_unreliable_commands (currentPeer);
enet_protocol_remove_sent_unreliable_commands (currentPeer, & sentUnreliableCommands);
if (sentLength < 0)
return -1;
host -> totalSentData += sentLength;
host -> totalSentPackets ++;
nextPeer:
if (currentPeer -> flags & ENET_PEER_FLAG_CONTINUE_SENDING)
continueSending = sendPass + 1;
}
return 0;