Merge pull request #80773 from akien-mga/3.5-cherrypicks
Cherry-picks for the 3.5 branch (future 3.5.3) - 1st batch
This commit is contained in:
commit
ffd1181cef
2
.github/workflows/javascript_builds.yml
vendored
2
.github/workflows/javascript_builds.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Emscripten latest
|
||||
uses: mymindstorm/setup-emsdk@v11
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
version: ${{env.EM_VERSION}}
|
||||
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
|
||||
|
@ -865,29 +865,28 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
|
||||
#ifdef MATH_CHECKS
|
||||
ERR_FAIL_COND(!is_rotation());
|
||||
#endif
|
||||
*/
|
||||
real_t angle, x, y, z; // variables for result
|
||||
real_t angle_epsilon = 0.1; // margin to distinguish between 0 and 180 degrees
|
||||
*/
|
||||
|
||||
if ((Math::abs(elements[1][0] - elements[0][1]) < CMP_EPSILON) && (Math::abs(elements[2][0] - elements[0][2]) < CMP_EPSILON) && (Math::abs(elements[2][1] - elements[1][2]) < CMP_EPSILON)) {
|
||||
// singularity found
|
||||
// first check for identity matrix which must have +1 for all terms
|
||||
// in leading diagonaland zero in other terms
|
||||
if ((Math::abs(elements[1][0] + elements[0][1]) < angle_epsilon) && (Math::abs(elements[2][0] + elements[0][2]) < angle_epsilon) && (Math::abs(elements[2][1] + elements[1][2]) < angle_epsilon) && (Math::abs(elements[0][0] + elements[1][1] + elements[2][2] - 3) < angle_epsilon)) {
|
||||
// this singularity is identity matrix so angle = 0
|
||||
// https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
|
||||
real_t x, y, z; // Variables for result.
|
||||
if (Math::is_zero_approx(elements[0][1] - elements[1][0]) && Math::is_zero_approx(elements[0][2] - elements[2][0]) && Math::is_zero_approx(elements[1][2] - elements[2][1])) {
|
||||
// Singularity found.
|
||||
// First check for identity matrix which must have +1 for all terms in leading diagonal and zero in other terms.
|
||||
if (is_diagonal() && (Math::abs(elements[0][0] + elements[1][1] + elements[2][2] - 3) < 3 * CMP_EPSILON)) {
|
||||
// This singularity is identity matrix so angle = 0.
|
||||
r_axis = Vector3(0, 1, 0);
|
||||
r_angle = 0;
|
||||
return;
|
||||
}
|
||||
// otherwise this singularity is angle = 180
|
||||
angle = Math_PI;
|
||||
// Otherwise this singularity is angle = 180.
|
||||
real_t xx = (elements[0][0] + 1) / 2;
|
||||
real_t yy = (elements[1][1] + 1) / 2;
|
||||
real_t zz = (elements[2][2] + 1) / 2;
|
||||
real_t xy = (elements[1][0] + elements[0][1]) / 4;
|
||||
real_t xz = (elements[2][0] + elements[0][2]) / 4;
|
||||
real_t yz = (elements[2][1] + elements[1][2]) / 4;
|
||||
if ((xx > yy) && (xx > zz)) { // elements[0][0] is the largest diagonal term
|
||||
real_t xy = (elements[0][1] + elements[1][0]) / 4;
|
||||
real_t xz = (elements[0][2] + elements[2][0]) / 4;
|
||||
real_t yz = (elements[1][2] + elements[2][1]) / 4;
|
||||
|
||||
if ((xx > yy) && (xx > zz)) { // elements[0][0] is the largest diagonal term.
|
||||
if (xx < CMP_EPSILON) {
|
||||
x = 0;
|
||||
y = Math_SQRT12;
|
||||
@ -897,7 +896,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
|
||||
y = xy / x;
|
||||
z = xz / x;
|
||||
}
|
||||
} else if (yy > zz) { // elements[1][1] is the largest diagonal term
|
||||
} else if (yy > zz) { // elements[1][1] is the largest diagonal term.
|
||||
if (yy < CMP_EPSILON) {
|
||||
x = Math_SQRT12;
|
||||
y = 0;
|
||||
@ -907,7 +906,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
|
||||
x = xy / y;
|
||||
z = yz / y;
|
||||
}
|
||||
} else { // elements[2][2] is the largest diagonal term so base result on this
|
||||
} else { // elements[2][2] is the largest diagonal term so base result on this.
|
||||
if (zz < CMP_EPSILON) {
|
||||
x = Math_SQRT12;
|
||||
y = Math_SQRT12;
|
||||
@ -919,22 +918,24 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
|
||||
}
|
||||
}
|
||||
r_axis = Vector3(x, y, z);
|
||||
r_angle = angle;
|
||||
r_angle = Math_PI;
|
||||
return;
|
||||
}
|
||||
// as we have reached here there are no singularities so we can handle normally
|
||||
real_t s = Math::sqrt((elements[1][2] - elements[2][1]) * (elements[1][2] - elements[2][1]) + (elements[2][0] - elements[0][2]) * (elements[2][0] - elements[0][2]) + (elements[0][1] - elements[1][0]) * (elements[0][1] - elements[1][0])); // s=|axis||sin(angle)|, used to normalise
|
||||
// As we have reached here there are no singularities so we can handle normally.
|
||||
double s = Math::sqrt((elements[2][1] - elements[1][2]) * (elements[2][1] - elements[1][2]) + (elements[0][2] - elements[2][0]) * (elements[0][2] - elements[2][0]) + (elements[1][0] - elements[0][1]) * (elements[1][0] - elements[0][1])); // Used to normalise.
|
||||
|
||||
angle = Math::acos((elements[0][0] + elements[1][1] + elements[2][2] - 1) / 2);
|
||||
if (angle < 0) {
|
||||
s = -s;
|
||||
if (Math::abs(s) < CMP_EPSILON) {
|
||||
// Prevent divide by zero, should not happen if matrix is orthogonal and should be caught by singularity test above.
|
||||
s = 1;
|
||||
}
|
||||
|
||||
x = (elements[2][1] - elements[1][2]) / s;
|
||||
y = (elements[0][2] - elements[2][0]) / s;
|
||||
z = (elements[1][0] - elements[0][1]) / s;
|
||||
|
||||
r_axis = Vector3(x, y, z);
|
||||
r_angle = angle;
|
||||
// acos does clamping.
|
||||
r_angle = Math::acos((elements[0][0] + elements[1][1] + elements[2][2] - 1) / 2);
|
||||
}
|
||||
|
||||
void Basis::set_quat(const Quat &p_quat) {
|
||||
|
@ -56,7 +56,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
|
||||
// Cache options
|
||||
String variable_prefix = options["variable_prefix"];
|
||||
|
||||
if (line == "") {
|
||||
if (line.empty() && !feof(stdin)) {
|
||||
print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'");
|
||||
print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'");
|
||||
print_line("Enter \"help\" for assistance.");
|
||||
@ -185,7 +185,7 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue, b
|
||||
print_line("Added breakpoint at " + source + ":" + itos(linenr));
|
||||
}
|
||||
|
||||
} else if (line == "q" || line == "quit") {
|
||||
} else if (line == "q" || line == "quit" || (line.empty() && feof(stdin))) {
|
||||
// Do not stop again on quit
|
||||
clear_breakpoints();
|
||||
ScriptDebugger::get_singleton()->set_depth(-1);
|
||||
|
@ -7,6 +7,7 @@
|
||||
3D area that detects [CollisionObject] nodes overlapping, entering, or exiting. Can also alter or override local physics parameters (gravity, damping) and route audio to a custom audio bus.
|
||||
To give the area its shape, add a [CollisionShape] or a [CollisionPolygon] node as a [i]direct[/i] child (or add multiple such nodes as direct children) of the area.
|
||||
[b]Warning:[/b] See [ConcavePolygonShape] (also called "trimesh") for a warning about possibly unexpected behavior when using that shape for an area.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
|
||||
|
@ -127,13 +127,16 @@
|
||||
Animation speed randomness ratio.
|
||||
</member>
|
||||
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color( 1, 1, 1, 1 )">
|
||||
Each particle's initial color. To have particle display color in a [SpatialMaterial] make sure to set [member SpatialMaterial.vertex_color_use_as_albedo] to [code]true[/code].
|
||||
Each particle's initial color.
|
||||
[b]Note:[/b] [member color] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color] will have no visible effect.
|
||||
</member>
|
||||
<member name="color_initial_ramp" type="Gradient" setter="set_color_initial_ramp" getter="get_color_initial_ramp">
|
||||
Each particle's initial color will vary along this [GradientTexture] (multiplied with [member color]).
|
||||
[b]Note:[/b] [member color_initial_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_initial_ramp] will have no visible effect.
|
||||
</member>
|
||||
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
|
||||
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
|
||||
[b]Note:[/b] [member color_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_ramp] will have no visible effect.
|
||||
</member>
|
||||
<member name="damping" type="float" setter="set_param" getter="get_param" default="0.0">
|
||||
The rate at which particles lose velocity.
|
||||
@ -155,6 +158,7 @@
|
||||
</member>
|
||||
<member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors">
|
||||
Sets the [Color]s to modulate particles by when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS].
|
||||
[b]Note:[/b] [member emission_colors] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member emission_colors] will have no visible effect.
|
||||
</member>
|
||||
<member name="emission_normals" type="PoolVector3Array" setter="set_emission_normals" getter="get_emission_normals">
|
||||
Sets the direction the particles will be emitted in when using [constant EMISSION_SHAPE_DIRECTED_POINTS].
|
||||
|
@ -289,7 +289,7 @@
|
||||
<method name="get_canvas_transform" qualifiers="const">
|
||||
<return type="Transform2D" />
|
||||
<description>
|
||||
Returns the transform matrix of this item's canvas.
|
||||
Returns the transform from the coordinate system of the canvas, this item is in, to the [Viewport]s coordinate system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_mouse_position" qualifiers="const">
|
||||
@ -307,7 +307,7 @@
|
||||
<method name="get_global_transform_with_canvas" qualifiers="const">
|
||||
<return type="Transform2D" />
|
||||
<description>
|
||||
Returns the global transform matrix of this item in relation to the canvas.
|
||||
Returns the transform from the local coordinate system of this [CanvasItem] to the [Viewport]s coordinate system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_local_mouse_position" qualifiers="const">
|
||||
@ -331,7 +331,7 @@
|
||||
<method name="get_viewport_transform" qualifiers="const">
|
||||
<return type="Transform2D" />
|
||||
<description>
|
||||
Returns this item's transform in relation to the viewport.
|
||||
Returns the transform from the coordinate system of the canvas, this item is in, to the [Viewport]s embedders coordinate system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_world_2d" qualifiers="const">
|
||||
@ -367,7 +367,7 @@
|
||||
<method name="is_visible_in_tree" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its antecedents are also visible. If any antecedent is hidden, this node will not be visible in the scene tree, and is consequently not drawn (see [method _draw]).
|
||||
Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree, and is consequently not drawn (see [method _draw]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="make_canvas_position_local" qualifiers="const">
|
||||
@ -441,7 +441,7 @@
|
||||
If [code]true[/code], the parent [CanvasItem]'s [member material] property is used as this one's material.
|
||||
</member>
|
||||
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
|
||||
If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
|
||||
If [code]true[/code], this [CanvasItem] is drawn. The node is only visible if all of its ancestors are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
|
||||
[b]Note:[/b] For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead.
|
||||
</member>
|
||||
</members>
|
||||
|
@ -18,6 +18,12 @@
|
||||
Returns the RID of the canvas used by this layer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_final_transform" qualifiers="const">
|
||||
<return type="Transform2D" />
|
||||
<description>
|
||||
Returns the transform from the [CanvasLayer]s coordinate system to the [Viewport]s coordinate system.
|
||||
</description>
|
||||
</method>
|
||||
<method name="hide">
|
||||
<return type="void" />
|
||||
<description>
|
||||
|
@ -5,6 +5,7 @@
|
||||
</brief_description>
|
||||
<description>
|
||||
CollisionObject is the base class for physics objects. It can hold any number of collision [Shape]s. Each shape must be assigned to a [i]shape owner[/i]. The CollisionObject can have any number of shape owners. Shape owners are not nodes and do not appear in the editor, but are accessible through code using the [code]shape_owner_*[/code] methods.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -5,6 +5,7 @@
|
||||
</brief_description>
|
||||
<description>
|
||||
Allows editing a collision polygon's vertices on a selected plane. Can also set a depth perpendicular to that plane. This class is only available in the editor. It will not appear in the scene tree at run-time. Creates a [Shape] for gameplay. Properties modified during gameplay will have no effect.
|
||||
[b]Warning:[/b] A non-uniformly scaled CollisionPolygon3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change its [member polygon]'s vertices instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -6,6 +6,7 @@
|
||||
<description>
|
||||
Editor facility for creating and editing collision shapes in 3D space. Set the [member shape] property to configure the shape. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject.shape_owner_get_shape] to get the actual shape.
|
||||
You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object.
|
||||
[b]Warning:[/b] A non-uniformly scaled CollisionShape3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size of its [member shape] resource instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link>
|
||||
|
@ -7,6 +7,7 @@
|
||||
Kinematic bodies are special types of bodies that are meant to be user-controlled. They are not affected by physics at all; to other types of bodies, such as a character or a rigid body, these are the same as a static body. However, they have two main uses:
|
||||
[b]Simulated motion:[/b] When these bodies are moved manually, either from code or from an [AnimationPlayer] (with [member AnimationPlayer.playback_process_mode] set to "physics"), the physics will automatically compute an estimate of their linear and angular velocity. This makes them very useful for moving platforms or other AnimationPlayer-controlled objects (like a door, a bridge that opens, etc).
|
||||
[b]Kinematic characters:[/b] KinematicBody also has an API for moving objects (the [method move_and_collide] and [method move_and_slide] methods) while performing collision tests. This makes them really useful to implement characters that collide against a world, but don't require advanced physics.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Kinematic character (2D)">$DOCS_URL/tutorials/physics/kinematic_character_2d.html</link>
|
||||
|
@ -15,6 +15,7 @@
|
||||
@"." # The current node.
|
||||
@".." # The parent node.
|
||||
@"../C" # A sibling node C.
|
||||
@"../.." # The grandparent node.
|
||||
# A leading slash means it is absolute from the SceneTree.
|
||||
@"/root" # Equivalent to get_tree().get_root().
|
||||
@"/root/Main" # If your main scene's root node were named "Main".
|
||||
@ -96,7 +97,7 @@
|
||||
<return type="String" />
|
||||
<argument index="0" name="idx" type="int" />
|
||||
<description>
|
||||
Gets the resource or property name indicated by [code]idx[/code] (0 to [method get_subname_count]).
|
||||
Gets the resource or property name indicated by [code]idx[/code] (0 to [method get_subname_count] - 1).
|
||||
[codeblock]
|
||||
var node_path = NodePath("Path2D/PathFollow2D/Sprite:texture:load_path")
|
||||
print(node_path.get_subname(0)) # texture
|
||||
|
@ -6,6 +6,7 @@
|
||||
<description>
|
||||
OptionButton is a type button that provides a selectable list of items when pressed. The item selected becomes the "current" item and is displayed as the button text.
|
||||
See also [BaseButton] which contains common properties and methods associated with this node.
|
||||
[b]Note:[/b] The ID values used for items are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -112,13 +112,16 @@
|
||||
Animation speed randomness ratio.
|
||||
</member>
|
||||
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color( 1, 1, 1, 1 )">
|
||||
Each particle's initial color. If the [Particles2D]'s [code]texture[/code] is defined, it will be multiplied by this color. To have particle display color in a [SpatialMaterial] make sure to set [member SpatialMaterial.vertex_color_use_as_albedo] to [code]true[/code].
|
||||
Each particle's initial color. If the [Particles2D]'s or [Particles]'s [code]texture[/code] is defined, it will be multiplied by this color.
|
||||
[b]Note:[/b] [member color] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color] will have no visible effect.
|
||||
</member>
|
||||
<member name="color_initial_ramp" type="Texture" setter="set_color_initial_ramp" getter="get_color_initial_ramp">
|
||||
Each particle's initial color will vary along this [GradientTexture] (multiplied with [member color]).
|
||||
[b]Note:[/b] [member color_initial_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_initial_ramp] will have no visible effect.
|
||||
</member>
|
||||
<member name="color_ramp" type="Texture" setter="set_color_ramp" getter="get_color_ramp">
|
||||
Each particle's color will vary along this [GradientTexture] over its lifetime (multiplied with [member color]).
|
||||
[b]Note:[/b] [member color_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_ramp] will have no visible effect.
|
||||
</member>
|
||||
<member name="damping" type="float" setter="set_param" getter="get_param" default="0.0">
|
||||
The rate at which particles lose velocity.
|
||||
@ -137,6 +140,7 @@
|
||||
</member>
|
||||
<member name="emission_color_texture" type="Texture" setter="set_emission_color_texture" getter="get_emission_color_texture">
|
||||
Particle color will be modulated by color determined by sampling this texture at the same point as the [member emission_point_texture].
|
||||
[b]Note:[/b] [member emission_color_texture] multiplies the particle mesh's vertex colors. To have a visible effect on a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member emission_color_texture] will have no visible effect.
|
||||
</member>
|
||||
<member name="emission_normal_texture" type="Texture" setter="set_emission_normal_texture" getter="get_emission_normal_texture">
|
||||
Particle velocity and rotation will be set by sampling this texture at the same point as the [member emission_point_texture]. Used only in [constant EMISSION_SHAPE_DIRECTED_POINTS]. Can be created automatically from mesh or node by selecting "Create Emission Points from Mesh/Node" under the "Particles" tool in the toolbar.
|
||||
|
@ -3,6 +3,7 @@
|
||||
<brief_description>
|
||||
</brief_description>
|
||||
<description>
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -5,6 +5,7 @@
|
||||
</brief_description>
|
||||
<description>
|
||||
PhysicsBody is an abstract base class for implementing a physics body. All *Body types inherit from it.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link>$DOCS_URL/tutorials/physics/physics_introduction.html</link>
|
||||
|
@ -6,6 +6,7 @@
|
||||
<description>
|
||||
[PopupMenu] is a [Control] that displays a list of options. They are popular in toolbars or context menus.
|
||||
[b]Incremental search:[/b] Like [ItemList] and [Tree], [PopupMenu] supports searching within the list while the control is focused. Press a key that matches the first letter of an item's name to select the first item starting with the given letter. After that point, there are two ways to perform incremental search: 1) Press the same key again before the timeout duration to select the next item starting with the same letter. 2) Press letter keys that match the rest of the word before the timeout duration to match to select the item in question directly. Both of these actions will be reset to the beginning of the list if the timeout duration has passed since the last keystroke was registered. You can adjust the timeout duration by changing [member ProjectSettings.gui/timers/incremental_search_max_interval_msec].
|
||||
[b]Note:[/b] The ID values used for items are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -1545,9 +1545,10 @@
|
||||
<member name="rendering/gles3/shaders/shader_compilation_mode" type="int" setter="" getter="" default="0">
|
||||
If set to [code]Asynchronous[/code] and available on the target device, asynchronous compilation of shaders is enabled (in contrast to [code]Asynchronous[/code]).
|
||||
That means that when a shader is first used under some new rendering situation, the game won't stall while such shader is being compiled. Instead, a fallback will be used and the real shader will be compiled in the background. Once the actual shader is compiled, it will be used the next times it's used to draw a frame.
|
||||
Depending on the async mode configured for a given material/shader, the fallback will be an "ubershader" (the default) or just skip rendering any item it is applied to.
|
||||
Depending on the [member SpatialMaterial.async_mode] mode configured for a given material, the fallback will be an "ubershader" (the default) or just skip rendering any item it is applied to. In custom [ShaderMaterial]s, the async mode is set using [code]render_mode async_visible;[/code] (default) or [code]render_mode async_hidden;[/code] at the top of the shader.
|
||||
An ubershader is a very complex shader, slow but suited to any rendering situation, that the engine generates internally so it can be used from the beginning while the traditional conditioned, optimized version of it is being compiled.
|
||||
To reduce loading times after the project has been launched at least once, you can use [code]Asynchronous + Cache[/code]. This also causes the ubershaders to be cached into storage so they can be ready faster next time they are used (provided the platform provides support for it).
|
||||
[b]Note:[/b] Asynchronous compilation requires driver support for the [code]GL_ARB_get_program_binary[/code] OpenGL extension. This extension is supported by all hardware that supports OpenGL 4.1 or higher as well as most hardware that supports OpenGL 3.3 or higher.
|
||||
[b]Note:[/b] Asynchronous compilation is currently only supported for spatial (3D) and particle materials/shaders. CanvasItem (2D) shaders will not use asynchronous compilation even if this setting is set to [code]Asynchronous[/code] or [code]Asynchronous + Cache[/code].
|
||||
</member>
|
||||
<member name="rendering/gles3/shaders/shader_compilation_mode.mobile" type="int" setter="" getter="" default="0">
|
||||
|
@ -9,6 +9,7 @@
|
||||
[b]Note:[/b] Don't change a RigidBody's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed Hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop may result in strange behavior. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state.
|
||||
If you need to override the default physics behavior, you can write a custom force integration function. See [member custom_integrator].
|
||||
With Bullet physics (the default), the center of mass is the RigidBody3D center. With GodotPhysics, the center of mass is the average of the [CollisionShape] centers.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Physics introduction">$DOCS_URL/tutorials/physics/physics_introduction.html</link>
|
||||
|
@ -93,7 +93,7 @@
|
||||
<method name="is_visible_in_tree" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its antecedents are also visible. If any antecedent is hidden, this node will not be visible in the scene tree.
|
||||
Returns [code]true[/code] if the node is present in the [SceneTree], its [member visible] property is [code]true[/code] and all its ancestors are also visible. If any ancestor is hidden, this node will not be visible in the scene tree.
|
||||
</description>
|
||||
</method>
|
||||
<method name="look_at">
|
||||
@ -282,7 +282,7 @@
|
||||
Local translation of this node.
|
||||
</member>
|
||||
<member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
|
||||
If [code]true[/code], this node is drawn. The node is only visible if all of its antecedents are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
|
||||
If [code]true[/code], this node is drawn. The node is only visible if all of its ancestors are visible as well (in other words, [method is_visible_in_tree] must return [code]true[/code]).
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
|
@ -4,8 +4,12 @@
|
||||
Static body for 3D physics.
|
||||
</brief_description>
|
||||
<description>
|
||||
Static body for 3D physics. A static body is a simple body that is not intended to move. In contrast to [RigidBody], they don't consume any CPU resources as long as they don't move.
|
||||
Additionally, a constant linear or angular velocity can be set for the static body, so even if it doesn't move, it affects other bodies as if it was moving (this is useful for simulating conveyor belts or conveyor wheels).
|
||||
Static body for 3D physics.
|
||||
A static body is a simple body that doesn't move under physics simulation, i.e. it can't be moved by external forces or contacts but its transformation can still be updated manually by the user. It is ideal for implementing objects in the environment, such as walls or platforms. In contrast to [RigidBody], it doesn't consume any CPU resources as long as they don't move.
|
||||
They have extra functionalities to move and affect other bodies:
|
||||
[i]Static transform change:[/i] Static bodies can be moved by animation or script. In this case, they are just teleported and don't affect other bodies on their path.
|
||||
[i]Constant velocity:[/i] When [member constant_linear_velocity] or [member constant_angular_velocity] is set, static bodies don't move themselves but affect touching bodies as if they were moving. This is useful for simulating conveyor belts or conveyor wheels.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link>
|
||||
|
@ -358,7 +358,7 @@
|
||||
<argument index="0" name="delimiter" type="String" />
|
||||
<argument index="1" name="slice" type="int" />
|
||||
<description>
|
||||
Splits a string using a [code]delimiter[/code] and returns a substring at index [code]slice[/code]. Returns an empty string if the index doesn't exist.
|
||||
Splits a string using a [code]delimiter[/code] and returns a substring at index [code]slice[/code]. Returns the original string if [code]delimiter[/code] does not occur in the string. Returns an empty string if the index doesn't exist.
|
||||
This is a more performant alternative to [method split] for cases when you need only one element from the array at a fixed index.
|
||||
Example:
|
||||
[codeblock]
|
||||
|
@ -6,6 +6,7 @@
|
||||
<description>
|
||||
Control for a single item inside a [Tree]. May have child [TreeItem]s and be styled as well as contain buttons.
|
||||
You can remove a [TreeItem] by using [method Object.free].
|
||||
[b]Note:[/b] The ID values used for buttons are limited to 32 bits, not full 64 bits of [int]. This has a range of [code]-2^32[/code] to [code]2^32 - 1[/code], i.e. [code]-2147483648[/code] to [code]2147483647[/code].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -7,6 +7,7 @@
|
||||
This node implements all the physics logic needed to simulate a car. It is based on the raycast vehicle system commonly found in physics engines. You will need to add a [CollisionShape] for the main body of your vehicle and add [VehicleWheel] nodes for the wheels. You should also add a [MeshInstance] to this node for the 3D model of your car but this model should not include meshes for the wheels. You should control the vehicle by using the [member brake], [member engine_force], and [member steering] properties and not change the position or orientation of this node directly.
|
||||
[b]Note:[/b] The origin point of your VehicleBody will determine the center of gravity of your vehicle so it is better to keep this low and move the [CollisionShape] and [MeshInstance] upwards.
|
||||
[b]Note:[/b] This class has known issues and isn't designed to provide realistic 3D vehicle physics. If you want advanced vehicle physics, you will probably have to write your own physics integration using another [PhysicsBody] class.
|
||||
[b]Warning:[/b] With a non-uniform scale this node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size(s) of its collision shape(s) instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Truck Town Demo">https://godotengine.org/asset-library/asset/524</link>
|
||||
|
@ -3694,7 +3694,7 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_
|
||||
} else if (directional_shadow.light_count == 2) {
|
||||
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2);
|
||||
if (light_instance->light_directional_index == 1) {
|
||||
light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
|
||||
light_instance->directional_rect.position.y += light_instance->directional_rect.size.y;
|
||||
}
|
||||
} else { //3 and 4
|
||||
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2);
|
||||
|
@ -4718,7 +4718,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_
|
||||
} else if (directional_shadow.light_count == 2) {
|
||||
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2);
|
||||
if (light_instance->light_directional_index == 1) {
|
||||
light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
|
||||
light_instance->directional_rect.position.y += light_instance->directional_rect.size.y;
|
||||
}
|
||||
} else { //3 and 4
|
||||
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2);
|
||||
|
@ -195,6 +195,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
|
||||
return ERR_NET_IN_PROGRESS;
|
||||
if (err == WSAEWOULDBLOCK)
|
||||
return ERR_NET_WOULD_BLOCK;
|
||||
if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
|
||||
return ERR_NET_BUFFER_TOO_SMALL;
|
||||
}
|
||||
print_verbose("Socket error: " + itos(err));
|
||||
return ERR_NET_OTHER;
|
||||
#else
|
||||
@ -207,6 +210,9 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return ERR_NET_WOULD_BLOCK;
|
||||
}
|
||||
if (errno == ENOBUFS) {
|
||||
return ERR_NET_BUFFER_TOO_SMALL;
|
||||
}
|
||||
print_verbose("Socket error: " + itos(errno));
|
||||
return ERR_NET_OTHER;
|
||||
#endif
|
||||
@ -535,6 +541,9 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
@ -556,6 +565,9 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Add
|
||||
if (err == ERR_NET_WOULD_BLOCK) {
|
||||
return ERR_BUSY;
|
||||
}
|
||||
if (err == ERR_NET_BUFFER_TOO_SMALL) {
|
||||
return ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return FAILED;
|
||||
}
|
||||
@ -592,6 +604,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;
|
||||
}
|
||||
@ -611,6 +626,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;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ private:
|
||||
ERR_NET_WOULD_BLOCK,
|
||||
ERR_NET_IS_CONNECTED,
|
||||
ERR_NET_IN_PROGRESS,
|
||||
ERR_NET_BUFFER_TOO_SMALL,
|
||||
ERR_NET_OTHER
|
||||
};
|
||||
|
||||
|
@ -105,7 +105,7 @@ void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_h
|
||||
|
||||
Ref<BitMap> src_bitmap;
|
||||
src_bitmap.instance();
|
||||
src_bitmap->create(aabb.size / divide_by);
|
||||
src_bitmap->create(Size2(Math::ceil(aabb.size.x / (real_t)divide_by), Math::ceil(aabb.size.y / (real_t)divide_by)));
|
||||
|
||||
int w = src_bitmap->get_size().width;
|
||||
int h = src_bitmap->get_size().height;
|
||||
|
@ -556,6 +556,10 @@ void EditorNode::_notification(int p_what) {
|
||||
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
|
||||
FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
|
||||
EditorFileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
|
||||
EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EditorSettings::get_singleton()->get("filesystem/file_dialog/display_mode").operator int());
|
||||
|
||||
theme = create_custom_theme(theme_base->get_theme());
|
||||
|
||||
theme_base->set_theme(theme);
|
||||
|
@ -96,7 +96,8 @@ Error ResourceImporterTextureAtlas::import(const String &p_source_file, const St
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) {
|
||||
// FIXME: Rasterization has issues, see https://github.com/godotengine/godot/issues/68350#issuecomment-1305610290
|
||||
static void _plot_triangle(Vector2 *p_vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) {
|
||||
int width = p_image->get_width();
|
||||
int height = p_image->get_height();
|
||||
int src_width = p_src_image->get_width();
|
||||
@ -106,8 +107,8 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr
|
||||
int y[3];
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
x[j] = vertices[j].x;
|
||||
y[j] = vertices[j].y;
|
||||
x[j] = p_vertices[j].x;
|
||||
y[j] = p_vertices[j].y;
|
||||
}
|
||||
|
||||
// sort the points vertically
|
||||
@ -129,7 +130,7 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr
|
||||
double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
|
||||
double xf = x[0];
|
||||
double xt = x[0] + dx_upper; // if y[0] == y[1], special case
|
||||
int max_y = MIN(y[2], height - p_offset.y - 1);
|
||||
int max_y = MIN(y[2], p_transposed ? (width - p_offset.x - 1) : (height - p_offset.y - 1));
|
||||
for (int yi = y[0]; yi < max_y; yi++) {
|
||||
if (yi >= 0) {
|
||||
for (int xi = (xf > 0 ? int(xf) : 0); xi < (xt <= src_width ? xt : src_width); xi++) {
|
||||
@ -246,7 +247,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
|
||||
Ref<BitMap> bit_map;
|
||||
bit_map.instance();
|
||||
bit_map->create_from_image_alpha(image);
|
||||
Vector<Vector<Vector2>> polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height()));
|
||||
Vector<Vector<Vector2>> polygons = bit_map->clip_opaque_to_polygons(Rect2(Vector2(), image->get_size()));
|
||||
|
||||
for (int j = 0; j < polygons.size(); j++) {
|
||||
EditorAtlasPacker::Chart chart;
|
||||
|
@ -404,7 +404,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
|
||||
|
||||
bool trim = p_options["edit/trim"];
|
||||
|
||||
if (trim && (loop_mode != AudioStreamSample::LOOP_DISABLED) && format_channels > 0) {
|
||||
if (trim && (loop_mode == AudioStreamSample::LOOP_DISABLED) && format_channels > 0) {
|
||||
int first = 0;
|
||||
int last = (frames / format_channels) - 1;
|
||||
bool found = false;
|
||||
|
@ -7013,11 +7013,12 @@ void SpatialEditorPlugin::edit(Object *p_object) {
|
||||
bool SpatialEditorPlugin::handles(Object *p_object) const {
|
||||
if (p_object->is_class("Spatial")) {
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
if (!p_object->is_class("Resource")) {
|
||||
// This ensures that gizmos are cleared when selecting a non-Spatial node.
|
||||
const_cast<SpatialEditorPlugin *>(this)->edit((Object *)nullptr);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Dictionary SpatialEditorPlugin::get_state() const {
|
||||
|
@ -1588,6 +1588,13 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
|
||||
const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
|
||||
shape_anchor += current_tile_region.position;
|
||||
if (tools[TOOL_SELECT]->is_pressed()) {
|
||||
if (current_shape.size() > 0) {
|
||||
for (int i = 0; i < current_shape.size(); i++) {
|
||||
current_shape.set(i, snap_point(current_shape[i]));
|
||||
}
|
||||
workspace->update();
|
||||
}
|
||||
|
||||
if (mb.is_valid()) {
|
||||
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
|
||||
if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) {
|
||||
|
@ -315,9 +315,73 @@ void test_euler_conversion() {
|
||||
}
|
||||
}
|
||||
|
||||
void check_test(const char *test_case_name, bool condition) {
|
||||
if (!condition) {
|
||||
OS::get_singleton()->print("FAILED - %s\n", test_case_name);
|
||||
} else {
|
||||
OS::get_singleton()->print("PASSED - %s\n", test_case_name);
|
||||
}
|
||||
}
|
||||
|
||||
void test_set_axis_angle() {
|
||||
Vector3 axis;
|
||||
real_t angle;
|
||||
real_t pi = (real_t)Math_PI;
|
||||
|
||||
// Testing the singularity when the angle is 0°.
|
||||
Basis identity(1, 0, 0, 0, 1, 0, 0, 0, 1);
|
||||
identity.get_axis_angle(axis, angle);
|
||||
check_test("Testing the singularity when the angle is 0.", angle == 0);
|
||||
|
||||
// Testing the singularity when the angle is 180°.
|
||||
Basis singularityPi(-1, 0, 0, 0, 1, 0, 0, 0, -1);
|
||||
singularityPi.get_axis_angle(axis, angle);
|
||||
check_test("Testing the singularity when the angle is 180.", Math::is_equal_approx(angle, pi));
|
||||
|
||||
// Testing reversing the an axis (of an 30° angle).
|
||||
float cos30deg = Math::cos(Math::deg2rad((real_t)30.0));
|
||||
Basis z_positive(cos30deg, -0.5, 0, 0.5, cos30deg, 0, 0, 0, 1);
|
||||
Basis z_negative(cos30deg, 0.5, 0, -0.5, cos30deg, 0, 0, 0, 1);
|
||||
|
||||
z_positive.get_axis_angle(axis, angle);
|
||||
check_test("Testing reversing the an axis (of an 30 angle).", Math::is_equal_approx(angle, Math::deg2rad((real_t)30.0)));
|
||||
check_test("Testing reversing the an axis (of an 30 angle).", axis == Vector3(0, 0, 1));
|
||||
|
||||
z_negative.get_axis_angle(axis, angle);
|
||||
check_test("Testing reversing the an axis (of an 30 angle).", Math::is_equal_approx(angle, Math::deg2rad((real_t)30.0)));
|
||||
check_test("Testing reversing the an axis (of an 30 angle).", axis == Vector3(0, 0, -1));
|
||||
|
||||
// Testing a rotation of 90° on x-y-z.
|
||||
Basis x90deg(1, 0, 0, 0, 0, -1, 0, 1, 0);
|
||||
x90deg.get_axis_angle(axis, angle);
|
||||
check_test("Testing a rotation of 90 on x-y-z.", Math::is_equal_approx(angle, pi / (real_t)2));
|
||||
check_test("Testing a rotation of 90 on x-y-z.", axis == Vector3(1, 0, 0));
|
||||
|
||||
Basis y90deg(0, 0, 1, 0, 1, 0, -1, 0, 0);
|
||||
y90deg.get_axis_angle(axis, angle);
|
||||
check_test("Testing a rotation of 90 on x-y-z.", axis == Vector3(0, 1, 0));
|
||||
|
||||
Basis z90deg(0, -1, 0, 1, 0, 0, 0, 0, 1);
|
||||
z90deg.get_axis_angle(axis, angle);
|
||||
check_test("Testing a rotation of 90 on x-y-z.", axis == Vector3(0, 0, 1));
|
||||
|
||||
// Regression test: checks that the method returns a small angle (not 0).
|
||||
Basis tiny(1, 0, 0, 0, 0.9999995, -0.001, 0, 001, 0.9999995); // The min angle possible with float is 0.001rad.
|
||||
tiny.get_axis_angle(axis, angle);
|
||||
check_test("Regression test: checks that the method returns a small angle (not 0).", Math::is_equal_approx(angle, (real_t)0.001, (real_t)0.0001));
|
||||
|
||||
// Regression test: checks that the method returns an angle which is a number (not NaN)
|
||||
Basis bugNan(1.00000024, 0, 0.000100001693, 0, 1, 0, -0.000100009143, 0, 1.00000024);
|
||||
bugNan.get_axis_angle(axis, angle);
|
||||
check_test("Regression test: checks that the method returns an angle which is a number (not NaN)", !Math::is_nan(angle));
|
||||
}
|
||||
|
||||
MainLoop *test() {
|
||||
OS::get_singleton()->print("Start euler conversion checks.\n");
|
||||
test_euler_conversion();
|
||||
OS::get_singleton()->print("\n---------------\n");
|
||||
OS::get_singleton()->print("Start set axis angle checks.\n");
|
||||
test_set_axis_angle();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2194,7 +2194,7 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
|
||||
"breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
|
||||
"breakpoint", "class", "class_name", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
|
||||
"const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif",
|
||||
"else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "puppet", "slave",
|
||||
"remotesync", "mastersync", "puppetsync",
|
||||
|
@ -8592,8 +8592,8 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
|
||||
}
|
||||
|
||||
// Parse sub blocks
|
||||
for (int i = 0; i < p_block->sub_blocks.size(); i++) {
|
||||
current_block = p_block->sub_blocks[i];
|
||||
for (const List<BlockNode *>::Element *E = p_block->sub_blocks.front(); E; E = E->next()) {
|
||||
current_block = E->get();
|
||||
_check_block_types(current_block);
|
||||
current_block = p_block;
|
||||
if (error_set) {
|
||||
|
@ -473,7 +473,10 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
|
||||
|
||||
//erase navigation
|
||||
for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
|
||||
NavigationServer::get_singleton()->free(E->get().region);
|
||||
if (E->get().region.is_valid()) {
|
||||
NavigationServer::get_singleton()->free(E->get().region);
|
||||
E->get().region = RID();
|
||||
}
|
||||
if (E->get().navmesh_debug_instance.is_valid()) {
|
||||
VS::get_singleton()->free(E->get().navmesh_debug_instance);
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ static void *_regex_malloc(PCRE2_SIZE size, void *user) {
|
||||
}
|
||||
|
||||
static void _regex_free(void *ptr, void *user) {
|
||||
memfree(ptr);
|
||||
if (ptr) {
|
||||
memfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
int RegExMatch::_find(const Variant &p_name) const {
|
||||
|
@ -6,7 +6,7 @@
|
||||
<description>
|
||||
[VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU.
|
||||
[b]Note:[/b] Alpha channel (also known as transparency) is not supported. The video will always appear to have a black background, even if it originally contains an alpha channel.
|
||||
[b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora]
|
||||
[b]Note:[/b] Not supported on iOS, or when compiled for RISC-V, and there are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora]
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -226,7 +226,7 @@ static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
|
||||
static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
|
||||
|
||||
static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
|
||||
static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
|
||||
static const int DEFAULT_TARGET_SDK_VERSION = 33; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||
@ -968,6 +968,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
|
||||
encode_uint32(is_resizeable, &p_manifest.write[iofs + 16]);
|
||||
}
|
||||
|
||||
if (tname == "provider" && attrname == "authorities") {
|
||||
string_table.write[attr_value] = get_package_name(package_name) + String(".fileprovider");
|
||||
}
|
||||
|
||||
if (tname == "supports-screens") {
|
||||
if (attrname == "smallScreens") {
|
||||
encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
|
||||
|
@ -1,9 +1,9 @@
|
||||
ext.versions = [
|
||||
androidGradlePlugin: '7.2.1',
|
||||
compileSdk : 32,
|
||||
compileSdk : 33,
|
||||
minSdk : 19, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
|
||||
targetSdk : 32, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
|
||||
buildTools : '32.0.0',
|
||||
targetSdk : 33, // Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
|
||||
buildTools : '33.0.2',
|
||||
kotlinVersion : '1.7.0',
|
||||
fragmentVersion : '1.3.6',
|
||||
nexusPublishVersion: '1.1.0',
|
||||
|
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -20,6 +20,16 @@
|
||||
android:exported="false"
|
||||
/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/godot_provider_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
11
platform/android/java/lib/res/xml/godot_provider_paths.xml
Normal file
11
platform/android/java/lib/res/xml/godot_provider_paths.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<external-path
|
||||
name="public"
|
||||
path="." />
|
||||
|
||||
<external-files-path
|
||||
name="app"
|
||||
path="." />
|
||||
</paths>
|
@ -49,6 +49,9 @@ import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -84,29 +87,42 @@ public class GodotIO {
|
||||
// MISCELLANEOUS OS IO
|
||||
/////////////////////////
|
||||
|
||||
public int openURI(String p_uri) {
|
||||
public int openURI(String uriString) {
|
||||
try {
|
||||
String path = p_uri;
|
||||
String type = "";
|
||||
if (path.startsWith("/")) {
|
||||
//absolute path to filesystem, prepend file://
|
||||
path = "file://" + path;
|
||||
if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) {
|
||||
type = "image/*";
|
||||
Uri dataUri;
|
||||
String dataType = "";
|
||||
boolean grantReadUriPermission = false;
|
||||
|
||||
if (uriString.startsWith("/") || uriString.startsWith("file://")) {
|
||||
String filePath = uriString;
|
||||
// File uris needs to be provided via the FileProvider
|
||||
grantReadUriPermission = true;
|
||||
if (filePath.startsWith("file://")) {
|
||||
filePath = filePath.replace("file://", "");
|
||||
}
|
||||
|
||||
File targetFile = new File(filePath);
|
||||
dataUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", targetFile);
|
||||
dataType = activity.getContentResolver().getType(dataUri);
|
||||
} else {
|
||||
dataUri = Uri.parse(uriString);
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
if (!type.equals("")) {
|
||||
intent.setDataAndType(Uri.parse(path), type);
|
||||
if (TextUtils.isEmpty(dataType)) {
|
||||
intent.setData(dataUri);
|
||||
} else {
|
||||
intent.setData(Uri.parse(path));
|
||||
intent.setDataAndType(dataUri, dataType);
|
||||
}
|
||||
if (grantReadUriPermission) {
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
activity.startActivity(intent);
|
||||
return 0;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "Unable to open uri " + uriString, e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +128,12 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
|
||||
public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) {
|
||||
if (this.mEdit == pTextView && this.isFullScreenEdit() && pKeyEvent != null) {
|
||||
final String characters = pKeyEvent.getCharacters();
|
||||
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
final int ch = characters.codePointAt(i);
|
||||
GodotLib.key(0, 0, ch, true);
|
||||
GodotLib.key(0, 0, ch, false);
|
||||
if (characters != null) {
|
||||
for (int i = 0; i < characters.length(); i++) {
|
||||
final int ch = characters.codePointAt(i);
|
||||
GodotLib.key(0, 0, ch, true);
|
||||
GodotLib.key(0, 0, ch, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,13 @@ internal enum class StorageScope {
|
||||
return UNKNOWN
|
||||
}
|
||||
|
||||
// If we have 'All Files Access' permission, we can access all directories without
|
||||
// restriction.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& Environment.isExternalStorageManager()) {
|
||||
return APP
|
||||
}
|
||||
|
||||
val canonicalPathFile = pathFile.canonicalPath
|
||||
|
||||
if (internalAppDir != null && canonicalPathFile.startsWith(internalAppDir)) {
|
||||
@ -90,6 +97,11 @@ internal enum class StorageScope {
|
||||
return APP
|
||||
}
|
||||
|
||||
val rootDir: String? = System.getenv("ANDROID_ROOT")
|
||||
if (rootDir != null && canonicalPathFile.startsWith(rootDir)) {
|
||||
return APP
|
||||
}
|
||||
|
||||
if (sharedDir != null && canonicalPathFile.startsWith(sharedDir)) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
// Before R, apps had access to shared storage so long as they have the right
|
||||
|
5117
platform/javascript/package-lock.json
generated
5117
platform/javascript/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -20,10 +20,10 @@
|
||||
"author": "Godot Engine contributors",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"jsdoc": "^3.6.7",
|
||||
"serve": "^13.0.2"
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"jsdoc": "^4.0.2",
|
||||
"serve": "^14.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
#include "joypad_linux.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -77,13 +79,41 @@ void JoypadLinux::Joypad::reset() {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
// This function is derived from SDL:
|
||||
// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
|
||||
static bool detect_sandbox() {
|
||||
if (access("/.flatpak-info", F_OK) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For Snap, we check multiple variables because they might be set for
|
||||
// unrelated reasons. This is the same thing WebKitGTK does.
|
||||
if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (access("/run/host/container-manager", F_OK) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JoypadLinux::JoypadLinux(InputDefault *in) {
|
||||
#ifdef UDEV_ENABLED
|
||||
use_udev = initialize_libudev() == 0;
|
||||
if (use_udev) {
|
||||
print_verbose("JoypadLinux: udev enabled and loaded successfully.");
|
||||
if (detect_sandbox()) {
|
||||
// Linux binaries in sandboxes / containers need special handling because
|
||||
// libudev doesn't work there. So we need to fallback to manual parsing
|
||||
// of /dev/input in such case.
|
||||
use_udev = false;
|
||||
print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
|
||||
} else {
|
||||
print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
|
||||
use_udev = initialize_libudev() == 0;
|
||||
if (use_udev) {
|
||||
print_verbose("JoypadLinux: udev enabled and loaded successfully.");
|
||||
} else {
|
||||
print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
|
||||
}
|
||||
}
|
||||
#else
|
||||
print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
|
||||
|
@ -4381,7 +4381,7 @@ uint32_t OS_X11::keyboard_get_scancode_from_physical(uint32_t p_scancode) const
|
||||
unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK;
|
||||
unsigned int scancode_no_mod = p_scancode & KEY_CODE_MASK;
|
||||
unsigned int xkeycode = KeyMappingX11::get_xlibcode((uint32_t)scancode_no_mod);
|
||||
KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, 0, 0);
|
||||
KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, keyboard_get_current_layout(), 0);
|
||||
if (xkeysym >= 'a' && xkeysym <= 'z') {
|
||||
xkeysym -= ('a' - 'A');
|
||||
}
|
||||
|
@ -274,6 +274,7 @@ void SpriteFrames::_set_animations(const Array &p_animations) {
|
||||
}
|
||||
|
||||
animations[d["name"]] = anim;
|
||||
animations[d["name"]].normal_name = String(d["name"]) + NORMAL_SUFFIX;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,7 +466,7 @@ void CanvasItem::_update_callback() {
|
||||
|
||||
Transform2D CanvasItem::get_global_transform_with_canvas() const {
|
||||
if (canvas_layer) {
|
||||
return canvas_layer->get_transform() * get_global_transform();
|
||||
return canvas_layer->get_final_transform() * get_global_transform();
|
||||
} else if (is_inside_tree()) {
|
||||
return get_viewport()->get_canvas_transform() * get_global_transform();
|
||||
} else {
|
||||
@ -1213,7 +1213,7 @@ Transform2D CanvasItem::get_canvas_transform() const {
|
||||
ERR_FAIL_COND_V(!is_inside_tree(), Transform2D());
|
||||
|
||||
if (canvas_layer) {
|
||||
return canvas_layer->get_transform();
|
||||
return canvas_layer->get_final_transform();
|
||||
} else if (Object::cast_to<CanvasItem>(get_parent())) {
|
||||
return Object::cast_to<CanvasItem>(get_parent())->get_canvas_transform();
|
||||
} else {
|
||||
@ -1226,9 +1226,9 @@ Transform2D CanvasItem::get_viewport_transform() const {
|
||||
|
||||
if (canvas_layer) {
|
||||
if (get_viewport()) {
|
||||
return get_viewport()->get_final_transform() * canvas_layer->get_transform();
|
||||
return get_viewport()->get_final_transform() * canvas_layer->get_final_transform();
|
||||
} else {
|
||||
return canvas_layer->get_transform();
|
||||
return canvas_layer->get_final_transform();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -40,8 +40,6 @@ void GridContainer::_notification(int p_what) {
|
||||
|
||||
int hsep = get_constant("hseparation");
|
||||
int vsep = get_constant("vseparation");
|
||||
int max_col = MIN(get_child_count(), columns);
|
||||
int max_row = ceil((float)get_child_count() / (float)columns);
|
||||
|
||||
// Compute the per-column/per-row data.
|
||||
int valid_controls_index = 0;
|
||||
@ -78,6 +76,9 @@ void GridContainer::_notification(int p_what) {
|
||||
}
|
||||
}
|
||||
|
||||
int max_col = MIN(valid_controls_index, columns);
|
||||
int max_row = ceil((float)valid_controls_index / (float)columns);
|
||||
|
||||
// Consider all empty columns expanded.
|
||||
for (int i = valid_controls_index; i < columns; i++) {
|
||||
col_expanded.insert(i);
|
||||
|
@ -5475,7 +5475,7 @@ int TextEdit::_is_line_in_region(int p_line) {
|
||||
// If not find the closest line we have.
|
||||
int previous_line = p_line - 1;
|
||||
for (; previous_line > -1; previous_line--) {
|
||||
if (color_region_cache.has(p_line)) {
|
||||
if (color_region_cache.has(previous_line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,18 @@ Transform2D CanvasLayer::get_transform() const {
|
||||
return transform;
|
||||
}
|
||||
|
||||
Transform2D CanvasLayer::get_final_transform() const {
|
||||
if (is_following_viewport()) {
|
||||
Transform2D follow;
|
||||
follow.scale(Vector2(get_follow_viewport_scale(), get_follow_viewport_scale()));
|
||||
if (vp) {
|
||||
follow = vp->get_canvas_transform() * follow;
|
||||
}
|
||||
return follow * transform;
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
void CanvasLayer::_update_xform() {
|
||||
transform.set_rotation_and_scale(rot, scale);
|
||||
transform.set_origin(ofs);
|
||||
@ -300,6 +312,7 @@ void CanvasLayer::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_final_transform"), &CanvasLayer::get_final_transform);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CanvasLayer::set_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_offset"), &CanvasLayer::get_offset);
|
||||
|
@ -77,6 +77,7 @@ public:
|
||||
|
||||
void set_transform(const Transform2D &p_xform);
|
||||
Transform2D get_transform() const;
|
||||
Transform2D get_final_transform() const;
|
||||
|
||||
void set_offset(const Vector2 &p_offset);
|
||||
Vector2 get_offset() const;
|
||||
|
@ -547,7 +547,7 @@ void Viewport::_process_picking(bool p_ignore_paused) {
|
||||
ObjectID canvas_layer_id;
|
||||
if (E->get()) {
|
||||
// A descendant CanvasLayer
|
||||
canvas_transform = E->get()->get_transform();
|
||||
canvas_transform = E->get()->get_final_transform();
|
||||
canvas_layer_id = E->get()->get_instance_id();
|
||||
} else {
|
||||
// This Viewport's builtin canvas
|
||||
|
@ -318,7 +318,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)
|
||||
prevx = stepx;
|
||||
prevy = stepy;
|
||||
|
||||
ERR_FAIL_COND_V((int)count > width * height, _points);
|
||||
ERR_FAIL_COND_V((int)count > 2 * (width * height + 1), _points);
|
||||
} while (curx != startx || cury != starty);
|
||||
return _points;
|
||||
}
|
||||
|
@ -890,8 +890,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
||||
theme->set_constant("vseparation", "GridContainer", 4 * scale);
|
||||
theme->set_constant("separation", "HSplitContainer", 12 * scale);
|
||||
theme->set_constant("separation", "VSplitContainer", 12 * scale);
|
||||
theme->set_constant("autohide", "HSplitContainer", 1 * scale);
|
||||
theme->set_constant("autohide", "VSplitContainer", 1 * scale);
|
||||
theme->set_constant("autohide", "HSplitContainer", 1);
|
||||
theme->set_constant("autohide", "VSplitContainer", 1);
|
||||
theme->set_constant("hseparation", "HFlowContainer", 4 * scale);
|
||||
theme->set_constant("vseparation", "HFlowContainer", 4 * scale);
|
||||
theme->set_constant("hseparation", "VFlowContainer", 4 * scale);
|
||||
|
2
thirdparty/README.md
vendored
2
thirdparty/README.md
vendored
@ -73,7 +73,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:
|
||||
|
16
thirdparty/enet/enet/enet.h
vendored
16
thirdparty/enet/enet/enet.h
vendored
@ -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);
|
||||
|
16
thirdparty/enet/host.c
vendored
16
thirdparty/enet/host.c
vendored
@ -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;
|
||||
|
77
thirdparty/enet/packet.c
vendored
77
thirdparty/enet/packet.c
vendored
@ -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;
|
||||
|
84
thirdparty/enet/peer.c
vendored
84
thirdparty/enet/peer.c
vendored
@ -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 *
|
||||
|
210
thirdparty/enet/protocol.c
vendored
210
thirdparty/enet/protocol.c
vendored
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user