Merge pull request #65045 from akien-mga/3.5-cherrypicks
This commit is contained in:
commit
a264b68f50
92 changed files with 683 additions and 415 deletions
|
@ -518,8 +518,6 @@ void _err_flush_stdout();
|
||||||
} else \
|
} else \
|
||||||
((void)0)
|
((void)0)
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should be a 'free' assert for program flow and should not be needed in any releases,
|
* This should be a 'free' assert for program flow and should not be needed in any releases,
|
||||||
* only used in dev builds.
|
* only used in dev builds.
|
||||||
|
@ -558,5 +556,6 @@ void _err_flush_stdout();
|
||||||
((void)0)
|
((void)0)
|
||||||
#else
|
#else
|
||||||
#define DEV_CHECK_ONCE(m_cond)
|
#define DEV_CHECK_ONCE(m_cond)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // ERROR_MACROS_H
|
#endif // ERROR_MACROS_H
|
||||||
|
|
|
@ -724,6 +724,8 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
|
||||||
// We also fall back in case of regional locales as done in TranslationServer::translate
|
// We also fall back in case of regional locales as done in TranslationServer::translate
|
||||||
// (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping).
|
// (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping).
|
||||||
|
|
||||||
|
// An extra remap may still be necessary afterwards due to the text -> binary converter on export.
|
||||||
|
|
||||||
String locale = TranslationServer::get_singleton()->get_locale();
|
String locale = TranslationServer::get_singleton()->get_locale();
|
||||||
ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid.");
|
ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid.");
|
||||||
String lang = TranslationServer::get_language_code(locale);
|
String lang = TranslationServer::get_language_code(locale);
|
||||||
|
@ -763,12 +765,10 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
|
||||||
|
|
||||||
if (path_remaps.has(new_path)) {
|
if (path_remaps.has(new_path)) {
|
||||||
new_path = path_remaps[new_path];
|
new_path = path_remaps[new_path];
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (new_path == p_path) { // Did not remap.
|
|
||||||
// Try file remap.
|
// Try file remap.
|
||||||
Error err;
|
Error err;
|
||||||
FileAccess *f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err);
|
FileAccess *f = FileAccess::open(new_path + ".remap", FileAccess::READ, &err);
|
||||||
|
|
||||||
if (f) {
|
if (f) {
|
||||||
VariantParser::StreamFile stream;
|
VariantParser::StreamFile stream;
|
||||||
|
|
|
@ -358,6 +358,15 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
|
||||||
* If nothing was found, error out.
|
* If nothing was found, error out.
|
||||||
*/
|
*/
|
||||||
Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) {
|
Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) {
|
||||||
|
if (OS::get_singleton()->get_resource_dir() != "") {
|
||||||
|
// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
|
||||||
|
// If the OS would rather use a specific location, then it will not be empty.
|
||||||
|
resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
|
||||||
|
if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
|
||||||
|
resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If looking for files in a network client, use it directly
|
// If looking for files in a network client, use it directly
|
||||||
|
|
||||||
if (FileAccessNetworkClient::get_singleton()) {
|
if (FileAccessNetworkClient::get_singleton()) {
|
||||||
|
@ -439,13 +448,6 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
|
||||||
// (Only Android -when reading from pck- and iOS use this.)
|
// (Only Android -when reading from pck- and iOS use this.)
|
||||||
|
|
||||||
if (OS::get_singleton()->get_resource_dir() != "") {
|
if (OS::get_singleton()->get_resource_dir() != "") {
|
||||||
// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
|
|
||||||
// If the OS would rather use a specific location, then it will not be empty.
|
|
||||||
resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
|
|
||||||
if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
|
|
||||||
resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.
|
|
||||||
}
|
|
||||||
|
|
||||||
Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
|
Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
|
||||||
if (err == OK && !p_ignore_override) {
|
if (err == OK && !p_ignore_override) {
|
||||||
// Optional, we don't mind if it fails.
|
// Optional, we don't mind if it fails.
|
||||||
|
|
|
@ -57,20 +57,20 @@
|
||||||
<method name="get_caption" qualifiers="virtual">
|
<method name="get_caption" qualifiers="virtual">
|
||||||
<return type="String" />
|
<return type="String" />
|
||||||
<description>
|
<description>
|
||||||
Gets the text caption for this node (used by some editors).
|
When inheriting from [AnimationRootNode], implement this virtual method to override the text caption for this node.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_child_by_name" qualifiers="virtual">
|
<method name="get_child_by_name" qualifiers="virtual">
|
||||||
<return type="Object" />
|
<return type="Object" />
|
||||||
<argument index="0" name="name" type="String" />
|
<argument index="0" name="name" type="String" />
|
||||||
<description>
|
<description>
|
||||||
Gets a child node by index (used by editors inheriting from [AnimationRootNode]).
|
When inheriting from [AnimationRootNode], implement this virtual method to return a child node by its [code]name[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_child_nodes" qualifiers="virtual">
|
<method name="get_child_nodes" qualifiers="virtual">
|
||||||
<return type="Dictionary" />
|
<return type="Dictionary" />
|
||||||
<description>
|
<description>
|
||||||
Gets all children nodes in order as a [code]name: node[/code] dictionary. Only useful when inheriting [AnimationRootNode].
|
When inheriting from [AnimationRootNode], implement this virtual method to return all children nodes in order as a [code]name: node[/code] dictionary.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_input_count" qualifiers="const">
|
<method name="get_input_count" qualifiers="const">
|
||||||
|
@ -97,19 +97,19 @@
|
||||||
<return type="Variant" />
|
<return type="Variant" />
|
||||||
<argument index="0" name="name" type="String" />
|
<argument index="0" name="name" type="String" />
|
||||||
<description>
|
<description>
|
||||||
Gets the default value of a parameter. Parameters are custom local memory used for your nodes, given a resource can be reused in multiple trees.
|
When inheriting from [AnimationRootNode], implement this virtual method to return the default value of parameter "[code]name[/code]". Parameters are custom local memory used for your nodes, given a resource can be reused in multiple trees.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_parameter_list" qualifiers="virtual">
|
<method name="get_parameter_list" qualifiers="virtual">
|
||||||
<return type="Array" />
|
<return type="Array" />
|
||||||
<description>
|
<description>
|
||||||
Gets the property information for parameter. Parameters are custom local memory used for your nodes, given a resource can be reused in multiple trees. Format is similar to [method Object.get_property_list].
|
When inheriting from [AnimationRootNode], implement this virtual method to return a list of the properties on this node. Parameters are custom local memory used for your nodes, given a resource can be reused in multiple trees. Format is similar to [method Object.get_property_list].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="has_filter" qualifiers="virtual">
|
<method name="has_filter" qualifiers="virtual">
|
||||||
<return type="String" />
|
<return type="bool" />
|
||||||
<description>
|
<description>
|
||||||
Returns [code]true[/code] whether you want the blend tree editor to display filter editing on this node.
|
When inheriting from [AnimationRootNode], implement this virtual method to return whether the blend tree editor should display filter editing on this node.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="is_path_filtered" qualifiers="const">
|
<method name="is_path_filtered" qualifiers="const">
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
<argument index="0" name="time" type="float" />
|
<argument index="0" name="time" type="float" />
|
||||||
<argument index="1" name="seek" type="bool" />
|
<argument index="1" name="seek" type="bool" />
|
||||||
<description>
|
<description>
|
||||||
User-defined callback called when a custom node is processed. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] is [code]true[/code], in which case it is absolute.
|
When inheriting from [AnimationRootNode], implement this virtual method to run some code when this node is processed. The [code]time[/code] parameter is a relative delta, unless [code]seek[/code] is [code]true[/code], in which case it is absolute.
|
||||||
Here, call the [method blend_input], [method blend_node] or [method blend_animation] functions. You can also use [method get_parameter] and [method set_parameter] to modify local memory.
|
Here, call the [method blend_input], [method blend_node] or [method blend_animation] functions. You can also use [method get_parameter] and [method set_parameter] to modify local memory.
|
||||||
This function should return the time left for the current animation to finish (if unsure, pass the value from the main blend being called).
|
This function should return the time left for the current animation to finish (if unsure, pass the value from the main blend being called).
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</methods>
|
</methods>
|
||||||
<members>
|
<members>
|
||||||
<member name="advance_condition" type="String" setter="set_advance_condition" getter="get_advance_condition" default="""">
|
<member name="advance_condition" type="String" setter="set_advance_condition" getter="get_advance_condition" default="""">
|
||||||
Turn on auto advance when this condition is set. The provided name will become a boolean parameter on the [AnimationTree] that can be controlled from code (see [url=$DOCS_URL/tutorials/animation/animation_tree.html#controlling-from-code][/url]). For example, if [member AnimationTree.tree_root] is an [AnimationNodeStateMachine] and [member advance_condition] is set to [code]"idle"[/code]:
|
Turn on auto advance when this condition is set. The provided name will become a boolean parameter on the [AnimationTree] that can be controlled from code (see [url=$DOCS_URL/tutorials/animation/animation_tree.html#controlling-from-code]Using AnimationTree[/url]). For example, if [member AnimationTree.tree_root] is an [AnimationNodeStateMachine] and [member advance_condition] is set to [code]"idle"[/code]:
|
||||||
[codeblock]
|
[codeblock]
|
||||||
$animation_tree["parameters/conditions/idle"] = is_on_floor and (linear_velocity.x == 0)
|
$animation_tree["parameters/conditions/idle"] = is_on_floor and (linear_velocity.x == 0)
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
<return type="Animation" />
|
<return type="Animation" />
|
||||||
<argument index="0" name="name" type="String" />
|
<argument index="0" name="name" type="String" />
|
||||||
<description>
|
<description>
|
||||||
Returns the [Animation] with key [code]name[/code] or [code]null[/code] if not found.
|
Returns the [Animation] with the key [code]name[/code]. If the animation does not exist, [code]null[/code] is returned and an error is logged.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_animation_list" qualifiers="const">
|
<method name="get_animation_list" qualifiers="const">
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
Plays positional sound in 2D space.
|
Plays positional sound in 2D space.
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
Plays audio that dampens with distance from screen center.
|
Plays audio that dampens with distance from a given position.
|
||||||
|
By default, audio is heard from the screen center. This can be changed by adding a [Listener2D] node to the scene and enabling it by calling [method Listener2D.make_current] on it.
|
||||||
See also [AudioStreamPlayer] to play a sound non-positionally.
|
See also [AudioStreamPlayer] to play a sound non-positionally.
|
||||||
[b]Note:[/b] Hiding an [AudioStreamPlayer2D] node does not disable its audio output. To temporarily disable an [AudioStreamPlayer2D]'s audio output, set [member volume_db] to a very low value like [code]-100[/code] (which isn't audible to human hearing).
|
[b]Note:[/b] Hiding an [AudioStreamPlayer2D] node does not disable its audio output. To temporarily disable an [AudioStreamPlayer2D]'s audio output, set [member volume_db] to a very low value like [code]-100[/code] (which isn't audible to human hearing).
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -179,7 +179,7 @@
|
||||||
The camera's projection mode. In [constant PROJECTION_PERSPECTIVE] mode, objects' Z distance from the camera's local space scales their perceived size.
|
The camera's projection mode. In [constant PROJECTION_PERSPECTIVE] mode, objects' Z distance from the camera's local space scales their perceived size.
|
||||||
</member>
|
</member>
|
||||||
<member name="size" type="float" setter="set_size" getter="get_size" default="1.0">
|
<member name="size" type="float" setter="set_size" getter="get_size" default="1.0">
|
||||||
The camera's size measured as 1/2 the width or height. Only applicable in orthogonal and frustum modes. Since [member keep_aspect] locks on axis, [code]size[/code] sets the other axis' size length.
|
The camera's size in meters measured as the diameter of the width or height, depending on [member keep_aspect]. Only applicable in orthogonal and frustum modes.
|
||||||
</member>
|
</member>
|
||||||
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset" default="0.0">
|
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset" default="0.0">
|
||||||
The vertical (Y) offset of the camera viewport.
|
The vertical (Y) offset of the camera viewport.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
Base class of anything 2D. Canvas items are laid out in a tree; children inherit and extend their parent's transform. [CanvasItem] is extended by [Control] for anything GUI-related, and by [Node2D] for anything related to the 2D engine.
|
Base class of anything 2D. Canvas items are laid out in a tree; children inherit and extend their parent's transform. [CanvasItem] is extended by [Control] for anything GUI-related, and by [Node2D] for anything related to the 2D engine.
|
||||||
Any [CanvasItem] can draw. For this, [method update] must be called, then [constant NOTIFICATION_DRAW] will be received on idle time to request redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the [CanvasItem] are provided (see [code]draw_*[/code] functions). However, they can only be used inside the [method Object._notification], signal or [method _draw] virtual functions.
|
Any [CanvasItem] can draw. For this, [method update] is called by the engine, then [constant NOTIFICATION_DRAW] will be received on idle time to request redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the [CanvasItem] are provided (see [code]draw_*[/code] functions). However, they can only be used inside [method _draw], its corresponding [method Object._notification] or methods connected to the [signal draw] signal.
|
||||||
Canvas items are drawn in tree order. By default, children are on top of their parents so a root [CanvasItem] will be drawn behind everything. This behavior can be changed on a per-item basis.
|
Canvas items are drawn in tree order. By default, children are on top of their parents so a root [CanvasItem] will be drawn behind everything. This behavior can be changed on a per-item basis.
|
||||||
A [CanvasItem] can also be hidden, which will also hide its children. It provides many ways to change parameters such as modulation (for itself and its children) and self modulation (only for itself), as well as its blend mode.
|
A [CanvasItem] can also be hidden, which will also hide its children. It provides many ways to change parameters such as modulation (for itself and its children) and self modulation (only for itself), as well as its blend mode.
|
||||||
Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed.
|
Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed.
|
||||||
|
@ -20,7 +20,8 @@
|
||||||
<method name="_draw" qualifiers="virtual">
|
<method name="_draw" qualifiers="virtual">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<description>
|
<description>
|
||||||
Overridable function called by the engine (if defined) to draw the canvas item.
|
Called when [CanvasItem] has been requested to redraw (when [method update] is called, either manually or by the engine).
|
||||||
|
Corresponds to the [constant NOTIFICATION_DRAW] notification in [method Object._notification].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="draw_arc">
|
<method name="draw_arc">
|
||||||
|
@ -366,7 +367,7 @@
|
||||||
<method name="is_visible_in_tree" qualifiers="const">
|
<method name="is_visible_in_tree" qualifiers="const">
|
||||||
<return type="bool" />
|
<return type="bool" />
|
||||||
<description>
|
<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 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]).
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="make_canvas_position_local" qualifiers="const">
|
<method name="make_canvas_position_local" qualifiers="const">
|
||||||
|
@ -413,7 +414,7 @@
|
||||||
<method name="update">
|
<method name="update">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<description>
|
<description>
|
||||||
Queue the [CanvasItem] for update. [constant NOTIFICATION_DRAW] will be called on idle time to request redraw.
|
Queues the [CanvasItem] to redraw. During idle time, if [CanvasItem] is visible, [constant NOTIFICATION_DRAW] is sent and [method _draw] is called. This only occurs [b]once[/b] per frame, even if this method has been called multiple times.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
|
@ -447,7 +448,8 @@
|
||||||
<signals>
|
<signals>
|
||||||
<signal name="draw">
|
<signal name="draw">
|
||||||
<description>
|
<description>
|
||||||
Emitted when the [CanvasItem] must redraw. This can only be connected realtime, as deferred will not allow drawing.
|
Emitted when the [CanvasItem] must redraw, [i]after[/i] the related [constant NOTIFICATION_DRAW] notification, and [i]before[/i] [method _draw] is called.
|
||||||
|
[b]Note:[/b] Deferred connections do not allow drawing through the [code]draw_*[/code] methods.
|
||||||
</description>
|
</description>
|
||||||
</signal>
|
</signal>
|
||||||
<signal name="hide">
|
<signal name="hide">
|
||||||
|
@ -492,7 +494,7 @@
|
||||||
The [CanvasItem]'s local transform has changed. This notification is only received if enabled by [method set_notify_local_transform].
|
The [CanvasItem]'s local transform has changed. This notification is only received if enabled by [method set_notify_local_transform].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="NOTIFICATION_DRAW" value="30">
|
<constant name="NOTIFICATION_DRAW" value="30">
|
||||||
The [CanvasItem] is requested to draw.
|
The [CanvasItem] is requested to draw (see [method _draw]).
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="NOTIFICATION_VISIBILITY_CHANGED" value="31">
|
<constant name="NOTIFICATION_VISIBILITY_CHANGED" value="31">
|
||||||
The [CanvasItem]'s visibility has changed.
|
The [CanvasItem]'s visibility has changed.
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
<argument index="0" name="position" type="Vector2" />
|
<argument index="0" name="position" type="Vector2" />
|
||||||
<argument index="1" name="in" type="Vector2" default="Vector2( 0, 0 )" />
|
<argument index="1" name="in" type="Vector2" default="Vector2( 0, 0 )" />
|
||||||
<argument index="2" name="out" type="Vector2" default="Vector2( 0, 0 )" />
|
<argument index="2" name="out" type="Vector2" default="Vector2( 0, 0 )" />
|
||||||
<argument index="3" name="at_position" type="int" default="-1" />
|
<argument index="3" name="index" type="int" default="-1" />
|
||||||
<description>
|
<description>
|
||||||
Adds a point to a curve at [code]position[/code] relative to the [Curve2D]'s position, with control points [code]in[/code] and [code]out[/code].
|
Adds a point with the specified [code]position[/code] relative to the curve's own position, with control points [code]in[/code] and [code]out[/code]. Appends the new point at the end of the point list.
|
||||||
If [code]at_position[/code] is given, the point is inserted before the point number [code]at_position[/code], moving that point (and every point after) after the inserted point. If [code]at_position[/code] is not given, or is an illegal value ([code]at_position <0[/code] or [code]at_position >= [method get_point_count][/code]), the point will be appended at the end of the point list.
|
If [code]index[/code] is given, the new point is inserted before the existing point identified by index [code]index[/code]. Every existing point starting from [code]index[/code] is shifted further down the list of points. The index must be greater than or equal to [code]0[/code] and must not exceed the number of existing points in the line. See [method get_point_count].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="clear_points">
|
<method name="clear_points">
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
<argument index="0" name="position" type="Vector3" />
|
<argument index="0" name="position" type="Vector3" />
|
||||||
<argument index="1" name="in" type="Vector3" default="Vector3( 0, 0, 0 )" />
|
<argument index="1" name="in" type="Vector3" default="Vector3( 0, 0, 0 )" />
|
||||||
<argument index="2" name="out" type="Vector3" default="Vector3( 0, 0, 0 )" />
|
<argument index="2" name="out" type="Vector3" default="Vector3( 0, 0, 0 )" />
|
||||||
<argument index="3" name="at_position" type="int" default="-1" />
|
<argument index="3" name="index" type="int" default="-1" />
|
||||||
<description>
|
<description>
|
||||||
Adds a point to a curve at [code]position[/code] relative to the [Curve3D]'s position, with control points [code]in[/code] and [code]out[/code].
|
Adds a point with the specified [code]position[/code] relative to the curve's own position, with control points [code]in[/code] and [code]out[/code]. Appends the new point at the end of the point list.
|
||||||
If [code]at_position[/code] is given, the point is inserted before the point number [code]at_position[/code], moving that point (and every point after) after the inserted point. If [code]at_position[/code] is not given, or is an illegal value ([code]at_position <0[/code] or [code]at_position >= [method get_point_count][/code]), the point will be appended at the end of the point list.
|
If [code]index[/code] is given, the new point is inserted before the existing point identified by index [code]index[/code]. Every existing point starting from [code]index[/code] is shifted further down the list of points. The index must be greater than or equal to [code]0[/code] and must not exceed the number of existing points in the line. See [method get_point_count].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="clear_points">
|
<method name="clear_points">
|
||||||
|
|
|
@ -96,6 +96,8 @@
|
||||||
<signal name="property_changed">
|
<signal name="property_changed">
|
||||||
<argument index="0" name="property" type="String" />
|
<argument index="0" name="property" type="String" />
|
||||||
<argument index="1" name="value" type="Variant" />
|
<argument index="1" name="value" type="Variant" />
|
||||||
|
<argument index="2" name="field" type="String" />
|
||||||
|
<argument index="3" name="changing" type="bool" />
|
||||||
<description>
|
<description>
|
||||||
Do not emit this manually, use the [method emit_changed] method instead.
|
Do not emit this manually, use the [method emit_changed] method instead.
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<argument index="5" name="outline" type="bool" default="false" />
|
<argument index="5" name="outline" type="bool" default="false" />
|
||||||
<description>
|
<description>
|
||||||
Draw character [code]char[/code] into a canvas item using the font at a given position, with [code]modulate[/code] color, and optionally kerning if [code]next[/code] is passed. clipping the width. [code]position[/code] specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis. The width used by the character is returned, making this function useful for drawing strings character by character.
|
Draw character [code]char[/code] into a canvas item using the font at a given position, with [code]modulate[/code] color, and optionally kerning if [code]next[/code] is passed. clipping the width. [code]position[/code] specifies the baseline, not the top. To draw from the top, [i]ascent[/i] must be added to the Y axis. The width used by the character is returned, making this function useful for drawing strings character by character.
|
||||||
|
If [code]outline[/code] is [code]true[/code], the outline of the character is drawn instead of the character itself.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_ascent" qualifiers="const">
|
<method name="get_ascent" qualifiers="const">
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
<argument index="0" name="replace" type="bool" default="false" />
|
<argument index="0" name="replace" type="bool" default="false" />
|
||||||
<argument index="1" name="custom_scene" type="PackedScene" default="null" />
|
<argument index="1" name="custom_scene" type="PackedScene" default="null" />
|
||||||
<description>
|
<description>
|
||||||
Not thread-safe. Use [method Object.call_deferred] if calling from a thread.
|
Call this method to actually load in the node. The created node will be placed as a sibling [i]above[/i] the [InstancePlaceholder] in the scene tree. The [Node]'s reference is also returned for convenience.
|
||||||
|
[b]Note:[/b] [method create_instance] is not thread-safe. Use [method Object.call_deferred] if calling from a thread.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_instance_path" qualifiers="const">
|
<method name="get_instance_path" qualifiers="const">
|
||||||
|
@ -28,6 +29,8 @@
|
||||||
<return type="Dictionary" />
|
<return type="Dictionary" />
|
||||||
<argument index="0" name="with_order" type="bool" default="false" />
|
<argument index="0" name="with_order" type="bool" default="false" />
|
||||||
<description>
|
<description>
|
||||||
|
Returns the list of properties that will be applied to the node when [method create_instance] is called.
|
||||||
|
If [code]with_order[/code] is [code]true[/code], a key named [code].order[/code] (note the leading period) is added to the dictionary. This [code].order[/code] key is an [Array] of [String] property names specifying the order in which properties will be applied (with index 0 being the first).
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="replace_by_instance">
|
<method name="replace_by_instance">
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
<method name="add_point">
|
<method name="add_point">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="position" type="Vector2" />
|
<argument index="0" name="position" type="Vector2" />
|
||||||
<argument index="1" name="at_position" type="int" default="-1" />
|
<argument index="1" name="index" type="int" default="-1" />
|
||||||
<description>
|
<description>
|
||||||
Adds a point at the [code]position[/code]. Appends the point at the end of the line.
|
Adds a point with the specified [code]position[/code] relative to the line's own position. Appends the new point at the end of the point list.
|
||||||
If [code]at_position[/code] is given, the point is inserted before the point number [code]at_position[/code], moving that point (and every point after) after the inserted point. If [code]at_position[/code] is not given, or is an illegal value ([code]at_position < 0[/code] or [code]at_position >= [method get_point_count][/code]), the point will be appended at the end of the point list.
|
If [code]index[/code] is given, the new point is inserted before the existing point identified by index [code]index[/code]. Every existing point starting from [code]index[/code] is shifted further down the list of points. The index must be greater than or equal to [code]0[/code] and must not exceed the number of existing points in the line. See [method get_point_count].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="clear_points">
|
<method name="clear_points">
|
||||||
|
@ -30,29 +30,29 @@
|
||||||
<method name="get_point_count" qualifiers="const">
|
<method name="get_point_count" qualifiers="const">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<description>
|
<description>
|
||||||
Returns the Line2D's amount of points.
|
Returns the amount of points in the line.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="get_point_position" qualifiers="const">
|
<method name="get_point_position" qualifiers="const">
|
||||||
<return type="Vector2" />
|
<return type="Vector2" />
|
||||||
<argument index="0" name="i" type="int" />
|
<argument index="0" name="index" type="int" />
|
||||||
<description>
|
<description>
|
||||||
Returns point [code]i[/code]'s position.
|
Returns the position of the point at index [code]index[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="remove_point">
|
<method name="remove_point">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="i" type="int" />
|
<argument index="0" name="index" type="int" />
|
||||||
<description>
|
<description>
|
||||||
Removes the point at index [code]i[/code] from the line.
|
Removes the point at index [code]index[/code] from the line.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="set_point_position">
|
<method name="set_point_position">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="i" type="int" />
|
<argument index="0" name="index" type="int" />
|
||||||
<argument index="1" name="position" type="Vector2" />
|
<argument index="1" name="position" type="Vector2" />
|
||||||
<description>
|
<description>
|
||||||
Overwrites the position in point [code]i[/code] with the supplied [code]position[/code].
|
Overwrites the position of the point at index [code]index[/code] with the supplied [code]position[/code].
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
|
|
|
@ -213,7 +213,7 @@
|
||||||
</method>
|
</method>
|
||||||
<method name="map_is_active" qualifiers="const">
|
<method name="map_is_active" qualifiers="const">
|
||||||
<return type="bool" />
|
<return type="bool" />
|
||||||
<argument index="0" name="nap" type="RID" />
|
<argument index="0" name="map" type="RID" />
|
||||||
<description>
|
<description>
|
||||||
Returns [code]true[/code] if the map is active.
|
Returns [code]true[/code] if the map is active.
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -237,7 +237,7 @@
|
||||||
</method>
|
</method>
|
||||||
<method name="map_is_active" qualifiers="const">
|
<method name="map_is_active" qualifiers="const">
|
||||||
<return type="bool" />
|
<return type="bool" />
|
||||||
<argument index="0" name="nap" type="RID" />
|
<argument index="0" name="map" type="RID" />
|
||||||
<description>
|
<description>
|
||||||
Returns [code]true[/code] if the map is active.
|
Returns [code]true[/code] if the map is active.
|
||||||
</description>
|
</description>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
Class for displaying popups with a panel background. In some cases it might be simpler to use than [Popup], since it provides a configurable background. If you are making windows, better check [WindowDialog].
|
Class for displaying popups with a panel background. In some cases it might be simpler to use than [Popup], since it provides a configurable background. If you are making windows, better check [WindowDialog].
|
||||||
|
If any [Control] node is added as a child of this [PopupPanel], it will be stretched to fit the panel's size (similar to how [PanelContainer] works).
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
|
|
|
@ -1660,7 +1660,9 @@
|
||||||
[b]Note:[/b] The two video drivers are not drop-in replacements for each other, so a game designed for GLES3 might not work properly when falling back to GLES2. In particular, some features of the GLES3 backend are not available in GLES2. Enabling this setting also means that both ETC and ETC2 VRAM-compressed textures will be exported on Android and iOS, increasing the data pack's size.
|
[b]Note:[/b] The two video drivers are not drop-in replacements for each other, so a game designed for GLES3 might not work properly when falling back to GLES2. In particular, some features of the GLES3 backend are not available in GLES2. Enabling this setting also means that both ETC and ETC2 VRAM-compressed textures will be exported on Android and iOS, increasing the data pack's size.
|
||||||
</member>
|
</member>
|
||||||
<member name="rendering/quality/filters/anisotropic_filter_level" type="int" setter="" getter="" default="4">
|
<member name="rendering/quality/filters/anisotropic_filter_level" type="int" setter="" getter="" default="4">
|
||||||
Maximum anisotropic filter level used for textures with anisotropy enabled. Higher values will result in sharper textures when viewed from oblique angles, at the cost of performance. Only power-of-two values are valid (2, 4, 8, 16).
|
Maximum anisotropic filter level used for textures with anisotropy enabled. Higher values will result in sharper textures when viewed from oblique angles, at the cost of performance. With the exception of [code]1[/code], only power-of-two values are valid ([code]2[/code], [code]4[/code], [code]8[/code], [code]16[/code]). A value of [code]1[/code] forcibly disables anisotropic filtering, even on textures where it is enabled.
|
||||||
|
[b]Note:[/b] For performance reasons, anisotropic filtering [i]is not enabled by default[/i] on textures. For this setting to have an effect, anisotropic texture filtering can be enabled by selecting a texture in the FileSystem dock, going to the Import dock, checking the [b]Anisotropic[/b] checkbox then clicking [b]Reimport[/b]. However, anisotropic filtering is rarely useful in 2D, so only enable it for textures in 2D if it makes a meaningful visual difference.
|
||||||
|
[b]Note:[/b] This property is only read when the project starts. There is currently no way to change this setting at run-time.
|
||||||
</member>
|
</member>
|
||||||
<member name="rendering/quality/filters/msaa" type="int" setter="" getter="" default="0">
|
<member name="rendering/quality/filters/msaa" type="int" setter="" getter="" default="0">
|
||||||
Sets the number of MSAA samples to use. MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware.
|
Sets the number of MSAA samples to use. MSAA is used to reduce aliasing around the edges of polygons. A higher MSAA value results in smoother edges but can be significantly slower on some hardware.
|
||||||
|
|
|
@ -233,9 +233,11 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint" default="false">
|
<member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint" default="false">
|
||||||
If [code]true[/code], collision shapes will be visible when running the game from the editor for debugging purposes.
|
If [code]true[/code], collision shapes will be visible when running the game from the editor for debugging purposes.
|
||||||
|
[b]Note:[/b] This property is not designed to be changed at run-time. Changing the value of [member debug_collisions_hint] while the project is running will not have the desired effect.
|
||||||
</member>
|
</member>
|
||||||
<member name="debug_navigation_hint" type="bool" setter="set_debug_navigation_hint" getter="is_debugging_navigation_hint" default="false">
|
<member name="debug_navigation_hint" type="bool" setter="set_debug_navigation_hint" getter="is_debugging_navigation_hint" default="false">
|
||||||
If [code]true[/code], navigation polygons will be visible when running the game from the editor for debugging purposes.
|
If [code]true[/code], navigation polygons will be visible when running the game from the editor for debugging purposes.
|
||||||
|
[b]Note:[/b] This property is not designed to be changed at run-time. Changing the value of [member debug_navigation_hint] while the project is running will not have the desired effect.
|
||||||
</member>
|
</member>
|
||||||
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
|
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
|
||||||
The root of the edited scene.
|
The root of the edited scene.
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<member name="anisotropy_enabled" type="bool" setter="set_feature" getter="get_feature" default="false">
|
<member name="anisotropy_enabled" type="bool" setter="set_feature" getter="get_feature" default="false">
|
||||||
If [code]true[/code], anisotropy is enabled. Anisotropy changes the shape of the specular blob and aligns it to tangent space. This is useful for brushed aluminium and hair reflections.
|
If [code]true[/code], anisotropy is enabled. Anisotropy changes the shape of the specular blob and aligns it to tangent space. This is useful for brushed aluminium and hair reflections.
|
||||||
[b]Note:[/b] Mesh tangents are needed for anisotropy to work. If the mesh does not contain tangents, the anisotropy effect will appear broken.
|
[b]Note:[/b] Mesh tangents are needed for anisotropy to work. If the mesh does not contain tangents, the anisotropy effect will appear broken.
|
||||||
[b]Note:[/b] Material anisotropy should not to be confused with anisotropic texture filtering. Anisotropic texture filtering can be enabled by selecting a texture in the FileSystem dock, going to the Import dock, checking the [b]Anisotropic[/b] checkbox then clicking [b]Reimport[/b].
|
[b]Note:[/b] Material anisotropy should not to be confused with anisotropic texture filtering. Anisotropic texture filtering can be enabled by selecting a texture in the FileSystem dock, going to the Import dock, checking the [b]Anisotropic[/b] checkbox then clicking [b]Reimport[/b]. The anisotropic filtering level can be changed by adjusting [member ProjectSettings.rendering/quality/filters/anisotropic_filter_level].
|
||||||
</member>
|
</member>
|
||||||
<member name="anisotropy_flowmap" type="Texture" setter="set_texture" getter="get_texture">
|
<member name="anisotropy_flowmap" type="Texture" setter="set_texture" getter="get_texture">
|
||||||
Texture that offsets the tangent map for anisotropy calculations and optionally controls the anisotropy effect (if an alpha channel is present). The flowmap texture is expected to be a derivative map, with the red channel representing distortion on the X axis and green channel representing distortion on the Y axis. Values below 0.5 will result in negative distortion, whereas values above 0.5 will result in positive distortion.
|
Texture that offsets the tangent map for anisotropy calculations and optionally controls the anisotropy effect (if an alpha channel is present). The flowmap texture is expected to be a derivative map, with the red channel representing distortion on the X axis and green channel representing distortion on the Y axis. Values below 0.5 will result in negative distortion, whereas values above 0.5 will result in positive distortion.
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
The region of the atlas texture to display. [member region_enabled] must be [code]true[/code].
|
The region of the atlas texture to display. [member region_enabled] must be [code]true[/code].
|
||||||
</member>
|
</member>
|
||||||
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
|
<member name="texture" type="Texture" setter="set_texture" getter="get_texture">
|
||||||
[Texture] object to draw. If [member GeometryInstance.material_override] is used, this will be overridden.
|
[Texture] object to draw. If [member GeometryInstance.material_override] is used, this will be overridden. The size information is still used.
|
||||||
</member>
|
</member>
|
||||||
<member name="vframes" type="int" setter="set_vframes" getter="get_vframes" default="1">
|
<member name="vframes" type="int" setter="set_vframes" getter="get_vframes" default="1">
|
||||||
The number of rows in the sprite sheet.
|
The number of rows in the sprite sheet.
|
||||||
|
|
|
@ -42,6 +42,13 @@
|
||||||
Returns [code]true[/code] if select with right mouse button is enabled.
|
Returns [code]true[/code] if select with right mouse button is enabled.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_tab_button_icon" qualifiers="const">
|
||||||
|
<return type="Texture" />
|
||||||
|
<argument index="0" name="tab_idx" type="int" />
|
||||||
|
<description>
|
||||||
|
Returns the button icon from the tab at index [code]tab_idx[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_tab_count" qualifiers="const">
|
<method name="get_tab_count" qualifiers="const">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<description>
|
<description>
|
||||||
|
@ -110,6 +117,14 @@
|
||||||
If [code]true[/code], enables selecting a tab with the right mouse button.
|
If [code]true[/code], enables selecting a tab with the right mouse button.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="set_tab_button_icon">
|
||||||
|
<return type="void" />
|
||||||
|
<argument index="0" name="tab_idx" type="int" />
|
||||||
|
<argument index="1" name="icon" type="Texture" />
|
||||||
|
<description>
|
||||||
|
Sets the button icon from the tab at index [code]tab_idx[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="set_tab_disabled">
|
<method name="set_tab_disabled">
|
||||||
<return type="void" />
|
<return type="void" />
|
||||||
<argument index="0" name="tab_idx" type="int" />
|
<argument index="0" name="tab_idx" type="int" />
|
||||||
|
@ -169,7 +184,7 @@
|
||||||
<signal name="right_button_pressed">
|
<signal name="right_button_pressed">
|
||||||
<argument index="0" name="tab" type="int" />
|
<argument index="0" name="tab" type="int" />
|
||||||
<description>
|
<description>
|
||||||
Emitted when a tab is right-clicked.
|
Emitted when a tab's right button is pressed. See [method set_tab_button_icon].
|
||||||
</description>
|
</description>
|
||||||
</signal>
|
</signal>
|
||||||
<signal name="tab_changed">
|
<signal name="tab_changed">
|
||||||
|
|
|
@ -168,9 +168,8 @@ Error AudioDriverALSA::init() {
|
||||||
return ERR_CANT_OPEN;
|
return ERR_CANT_OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
active = false;
|
active.clear();
|
||||||
thread_exited = false;
|
exit_thread.clear();
|
||||||
exit_thread = false;
|
|
||||||
|
|
||||||
Error err = init_device();
|
Error err = init_device();
|
||||||
if (err == OK) {
|
if (err == OK) {
|
||||||
|
@ -183,11 +182,11 @@ Error AudioDriverALSA::init() {
|
||||||
void AudioDriverALSA::thread_func(void *p_udata) {
|
void AudioDriverALSA::thread_func(void *p_udata) {
|
||||||
AudioDriverALSA *ad = (AudioDriverALSA *)p_udata;
|
AudioDriverALSA *ad = (AudioDriverALSA *)p_udata;
|
||||||
|
|
||||||
while (!ad->exit_thread) {
|
while (!ad->exit_thread.is_set()) {
|
||||||
ad->lock();
|
ad->lock();
|
||||||
ad->start_counting_ticks();
|
ad->start_counting_ticks();
|
||||||
|
|
||||||
if (!ad->active) {
|
if (!ad->active.is_set()) {
|
||||||
for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
|
for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
|
||||||
ad->samples_out.write[i] = 0;
|
ad->samples_out.write[i] = 0;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +202,7 @@ void AudioDriverALSA::thread_func(void *p_udata) {
|
||||||
int todo = ad->period_size;
|
int todo = ad->period_size;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
while (todo && !ad->exit_thread) {
|
while (todo && !ad->exit_thread.is_set()) {
|
||||||
int16_t *src = (int16_t *)ad->samples_out.ptr();
|
int16_t *src = (int16_t *)ad->samples_out.ptr();
|
||||||
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
|
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
|
||||||
|
|
||||||
|
@ -222,8 +221,8 @@ void AudioDriverALSA::thread_func(void *p_udata) {
|
||||||
wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0);
|
wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0);
|
||||||
if (wrote < 0) {
|
if (wrote < 0) {
|
||||||
ERR_PRINT("ALSA: Failed and can't recover: " + String(snd_strerror(wrote)));
|
ERR_PRINT("ALSA: Failed and can't recover: " + String(snd_strerror(wrote)));
|
||||||
ad->active = false;
|
ad->active.clear();
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,8 +240,8 @@ void AudioDriverALSA::thread_func(void *p_udata) {
|
||||||
|
|
||||||
err = ad->init_device();
|
err = ad->init_device();
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ad->active = false;
|
ad->active.clear();
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,12 +249,10 @@ void AudioDriverALSA::thread_func(void *p_udata) {
|
||||||
ad->stop_counting_ticks();
|
ad->stop_counting_ticks();
|
||||||
ad->unlock();
|
ad->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
ad->thread_exited = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverALSA::start() {
|
void AudioDriverALSA::start() {
|
||||||
active = true;
|
active.set();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioDriverALSA::get_mix_rate() const {
|
int AudioDriverALSA::get_mix_rate() const {
|
||||||
|
@ -327,7 +324,7 @@ void AudioDriverALSA::finish_device() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverALSA::finish() {
|
void AudioDriverALSA::finish() {
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
finish_device();
|
finish_device();
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
|
|
||||||
#include "asound-so_wrap.h"
|
#include "asound-so_wrap.h"
|
||||||
|
@ -64,9 +65,8 @@ class AudioDriverALSA : public AudioDriver {
|
||||||
snd_pcm_uframes_t period_size;
|
snd_pcm_uframes_t period_size;
|
||||||
int channels;
|
int channels;
|
||||||
|
|
||||||
bool active;
|
SafeFlag active;
|
||||||
bool thread_exited;
|
SafeFlag exit_thread;
|
||||||
mutable bool exit_thread;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char *get_name() const {
|
const char *get_name() const {
|
||||||
|
|
|
@ -79,7 +79,7 @@ void MIDIDriverALSAMidi::thread_func(void *p_udata) {
|
||||||
int expected_size = 255;
|
int expected_size = 255;
|
||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
|
|
||||||
while (!md->exit_thread) {
|
while (!md->exit_thread.is_set()) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
md->lock();
|
md->lock();
|
||||||
|
@ -149,14 +149,14 @@ Error MIDIDriverALSAMidi::open() {
|
||||||
}
|
}
|
||||||
snd_device_name_free_hint(hints);
|
snd_device_name_free_hint(hints);
|
||||||
|
|
||||||
exit_thread = false;
|
exit_thread.clear();
|
||||||
thread.start(MIDIDriverALSAMidi::thread_func, this);
|
thread.start(MIDIDriverALSAMidi::thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIDriverALSAMidi::close() {
|
void MIDIDriverALSAMidi::close() {
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
for (int i = 0; i < connected_inputs.size(); i++) {
|
for (int i = 0; i < connected_inputs.size(); i++) {
|
||||||
|
@ -193,7 +193,7 @@ PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
|
MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
|
||||||
exit_thread = false;
|
exit_thread.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
MIDIDriverALSAMidi::~MIDIDriverALSAMidi() {
|
MIDIDriverALSAMidi::~MIDIDriverALSAMidi() {
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "core/os/midi_driver.h"
|
#include "core/os/midi_driver.h"
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
#include "core/vector.h"
|
#include "core/vector.h"
|
||||||
|
|
||||||
#include "../alsa/asound-so_wrap.h"
|
#include "../alsa/asound-so_wrap.h"
|
||||||
|
@ -47,7 +48,7 @@ class MIDIDriverALSAMidi : public MIDIDriver {
|
||||||
|
|
||||||
Vector<snd_rawmidi_t *> connected_inputs;
|
Vector<snd_rawmidi_t *> connected_inputs;
|
||||||
|
|
||||||
bool exit_thread;
|
SafeFlag exit_thread;
|
||||||
|
|
||||||
static void thread_func(void *p_udata);
|
static void thread_func(void *p_udata);
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ VERTEX_SHADER_CODE
|
||||||
|
|
||||||
xform = transpose(xform);
|
xform = transpose(xform);
|
||||||
|
|
||||||
out_velocity_active.a = mix(0.0, 1.0, shader_active);
|
out_velocity_active.a = float(shader_active);
|
||||||
|
|
||||||
out_xform_1 = xform[0];
|
out_xform_1 = xform[0];
|
||||||
out_xform_2 = xform[1];
|
out_xform_2 = xform[1];
|
||||||
|
|
|
@ -285,9 +285,8 @@ Error AudioDriverPulseAudio::init() {
|
||||||
return ERR_CANT_OPEN;
|
return ERR_CANT_OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
active = false;
|
active.clear();
|
||||||
thread_exited = false;
|
exit_thread.clear();
|
||||||
exit_thread = false;
|
|
||||||
|
|
||||||
mix_rate = GLOBAL_GET("audio/mix_rate");
|
mix_rate = GLOBAL_GET("audio/mix_rate");
|
||||||
|
|
||||||
|
@ -384,7 +383,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
size_t avail_bytes = 0;
|
size_t avail_bytes = 0;
|
||||||
uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();
|
uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();
|
||||||
|
|
||||||
while (!ad->exit_thread) {
|
while (!ad->exit_thread.is_set()) {
|
||||||
size_t read_bytes = 0;
|
size_t read_bytes = 0;
|
||||||
size_t written_bytes = 0;
|
size_t written_bytes = 0;
|
||||||
|
|
||||||
|
@ -394,7 +393,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
|
|
||||||
int16_t *out_ptr = ad->samples_out.ptrw();
|
int16_t *out_ptr = ad->samples_out.ptrw();
|
||||||
|
|
||||||
if (!ad->active) {
|
if (!ad->active.is_set()) {
|
||||||
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
|
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
|
||||||
out_ptr[i] = 0;
|
out_ptr[i] = 0;
|
||||||
}
|
}
|
||||||
|
@ -464,8 +463,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
|
|
||||||
err = ad->init_device();
|
err = ad->init_device();
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ad->active = false;
|
ad->active.clear();
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,8 +502,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
Error err = ad->init_device();
|
Error err = ad->init_device();
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT("PulseAudio: init_device error");
|
ERR_PRINT("PulseAudio: init_device error");
|
||||||
ad->active = false;
|
ad->active.clear();
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,8 +556,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
|
|
||||||
err = ad->capture_init_device();
|
err = ad->capture_init_device();
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ad->active = false;
|
ad->active.clear();
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,12 +572,10 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||||
OS::get_singleton()->delay_usec(1000);
|
OS::get_singleton()->delay_usec(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ad->thread_exited = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverPulseAudio::start() {
|
void AudioDriverPulseAudio::start() {
|
||||||
active = true;
|
active.set();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioDriverPulseAudio::get_mix_rate() const {
|
int AudioDriverPulseAudio::get_mix_rate() const {
|
||||||
|
@ -663,7 +660,7 @@ void AudioDriverPulseAudio::finish() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
finish_device();
|
finish_device();
|
||||||
|
@ -840,9 +837,6 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() :
|
||||||
channels(0),
|
channels(0),
|
||||||
pa_ready(0),
|
pa_ready(0),
|
||||||
pa_status(0),
|
pa_status(0),
|
||||||
active(false),
|
|
||||||
thread_exited(false),
|
|
||||||
exit_thread(false),
|
|
||||||
latency(0) {
|
latency(0) {
|
||||||
samples_in.clear();
|
samples_in.clear();
|
||||||
samples_out.clear();
|
samples_out.clear();
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
|
|
||||||
#include "pulse-so_wrap.h"
|
#include "pulse-so_wrap.h"
|
||||||
|
@ -70,9 +71,8 @@ class AudioDriverPulseAudio : public AudioDriver {
|
||||||
Array pa_devices;
|
Array pa_devices;
|
||||||
Array pa_rec_devices;
|
Array pa_rec_devices;
|
||||||
|
|
||||||
bool active;
|
SafeFlag active;
|
||||||
bool thread_exited;
|
SafeFlag exit_thread;
|
||||||
mutable bool exit_thread;
|
|
||||||
|
|
||||||
float latency;
|
float latency;
|
||||||
|
|
||||||
|
|
|
@ -365,12 +365,12 @@ Error AudioDriverWASAPI::init_capture_device(bool reinit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
|
Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
|
||||||
if (p_device->active) {
|
if (p_device->active.is_set()) {
|
||||||
if (p_device->audio_client) {
|
if (p_device->audio_client) {
|
||||||
p_device->audio_client->Stop();
|
p_device->audio_client->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
p_device->active = false;
|
p_device->active.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
SAFE_RELEASE(p_device->audio_client)
|
SAFE_RELEASE(p_device->audio_client)
|
||||||
|
@ -396,8 +396,7 @@ Error AudioDriverWASAPI::init() {
|
||||||
ERR_PRINT("WASAPI: init_render_device error");
|
ERR_PRINT("WASAPI: init_render_device error");
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_thread = false;
|
exit_thread.clear();
|
||||||
thread_exited = false;
|
|
||||||
|
|
||||||
thread.start(thread_func, this);
|
thread.start(thread_func, this);
|
||||||
|
|
||||||
|
@ -543,7 +542,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
||||||
uint32_t avail_frames = 0;
|
uint32_t avail_frames = 0;
|
||||||
uint32_t write_ofs = 0;
|
uint32_t write_ofs = 0;
|
||||||
|
|
||||||
while (!ad->exit_thread) {
|
while (!ad->exit_thread.is_set()) {
|
||||||
uint32_t read_frames = 0;
|
uint32_t read_frames = 0;
|
||||||
uint32_t written_frames = 0;
|
uint32_t written_frames = 0;
|
||||||
|
|
||||||
|
@ -551,7 +550,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
||||||
ad->lock();
|
ad->lock();
|
||||||
ad->start_counting_ticks();
|
ad->start_counting_ticks();
|
||||||
|
|
||||||
if (ad->audio_output.active) {
|
if (ad->audio_output.active.is_set()) {
|
||||||
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
|
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < ad->samples_in.size(); i++) {
|
for (int i = 0; i < ad->samples_in.size(); i++) {
|
||||||
|
@ -617,7 +616,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ERR_PRINT("WASAPI: Get buffer error");
|
ERR_PRINT("WASAPI: Get buffer error");
|
||||||
ad->exit_thread = true;
|
ad->exit_thread.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
|
} else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||||
|
@ -666,7 +665,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
||||||
write_ofs = 0;
|
write_ofs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ad->audio_input.active) {
|
if (ad->audio_input.active.is_set()) {
|
||||||
UINT32 packet_length = 0;
|
UINT32 packet_length = 0;
|
||||||
BYTE *data;
|
BYTE *data;
|
||||||
UINT32 num_frames_available;
|
UINT32 num_frames_available;
|
||||||
|
@ -745,8 +744,6 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
|
||||||
OS::get_singleton()->delay_usec(1000);
|
OS::get_singleton()->delay_usec(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ad->thread_exited = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverWASAPI::start() {
|
void AudioDriverWASAPI::start() {
|
||||||
|
@ -755,7 +752,7 @@ void AudioDriverWASAPI::start() {
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
ERR_PRINT("WASAPI: Start failed");
|
ERR_PRINT("WASAPI: Start failed");
|
||||||
} else {
|
} else {
|
||||||
audio_output.active = true;
|
audio_output.active.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -769,7 +766,7 @@ void AudioDriverWASAPI::unlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverWASAPI::finish() {
|
void AudioDriverWASAPI::finish() {
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
finish_capture_device();
|
finish_capture_device();
|
||||||
|
@ -783,19 +780,19 @@ Error AudioDriverWASAPI::capture_start() {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_input.active) {
|
if (audio_input.active.is_set()) {
|
||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_input.audio_client->Start();
|
audio_input.audio_client->Start();
|
||||||
audio_input.active = true;
|
audio_input.active.set();
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error AudioDriverWASAPI::capture_stop() {
|
Error AudioDriverWASAPI::capture_stop() {
|
||||||
if (audio_input.active) {
|
if (audio_input.active.is_set()) {
|
||||||
audio_input.audio_client->Stop();
|
audio_input.audio_client->Stop();
|
||||||
audio_input.active = false;
|
audio_input.active.clear();
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -827,9 +824,6 @@ AudioDriverWASAPI::AudioDriverWASAPI() {
|
||||||
channels = 0;
|
channels = 0;
|
||||||
mix_rate = 0;
|
mix_rate = 0;
|
||||||
buffer_frames = 0;
|
buffer_frames = 0;
|
||||||
|
|
||||||
thread_exited = false;
|
|
||||||
exit_thread = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
|
|
||||||
#include <audioclient.h>
|
#include <audioclient.h>
|
||||||
|
@ -48,7 +49,7 @@ class AudioDriverWASAPI : public AudioDriver {
|
||||||
IAudioClient *audio_client;
|
IAudioClient *audio_client;
|
||||||
IAudioRenderClient *render_client;
|
IAudioRenderClient *render_client;
|
||||||
IAudioCaptureClient *capture_client;
|
IAudioCaptureClient *capture_client;
|
||||||
bool active;
|
SafeFlag active;
|
||||||
|
|
||||||
WORD format_tag;
|
WORD format_tag;
|
||||||
WORD bits_per_sample;
|
WORD bits_per_sample;
|
||||||
|
@ -62,7 +63,6 @@ class AudioDriverWASAPI : public AudioDriver {
|
||||||
audio_client(NULL),
|
audio_client(NULL),
|
||||||
render_client(NULL),
|
render_client(NULL),
|
||||||
capture_client(NULL),
|
capture_client(NULL),
|
||||||
active(false),
|
|
||||||
format_tag(0),
|
format_tag(0),
|
||||||
bits_per_sample(0),
|
bits_per_sample(0),
|
||||||
channels(0),
|
channels(0),
|
||||||
|
@ -84,8 +84,7 @@ class AudioDriverWASAPI : public AudioDriver {
|
||||||
int mix_rate;
|
int mix_rate;
|
||||||
int buffer_frames;
|
int buffer_frames;
|
||||||
|
|
||||||
bool thread_exited;
|
SafeFlag exit_thread;
|
||||||
mutable bool exit_thread;
|
|
||||||
|
|
||||||
static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample);
|
static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample);
|
||||||
static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i);
|
static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i);
|
||||||
|
|
|
@ -403,6 +403,8 @@ DirAccessWindows::DirAccessWindows() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DirAccessWindows::~DirAccessWindows() {
|
DirAccessWindows::~DirAccessWindows() {
|
||||||
|
list_dir_end();
|
||||||
|
|
||||||
memdelete(p);
|
memdelete(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,8 @@ const char *AudioDriverXAudio2::get_name() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Error AudioDriverXAudio2::init() {
|
Error AudioDriverXAudio2::init() {
|
||||||
active = false;
|
active.clear();
|
||||||
thread_exited = false;
|
exit_thread.clear();
|
||||||
exit_thread = false;
|
|
||||||
pcm_open = false;
|
pcm_open = false;
|
||||||
samples_in = NULL;
|
samples_in = NULL;
|
||||||
|
|
||||||
|
@ -86,17 +85,19 @@ Error AudioDriverXAudio2::init() {
|
||||||
void AudioDriverXAudio2::thread_func(void *p_udata) {
|
void AudioDriverXAudio2::thread_func(void *p_udata) {
|
||||||
AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata;
|
AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata;
|
||||||
|
|
||||||
while (!ad->exit_thread) {
|
while (!ad->exit_thread.is_set()) {
|
||||||
if (!ad->active) {
|
if (!ad->active.is_set()) {
|
||||||
for (int i = 0; i < AUDIO_BUFFERS; i++) {
|
for (int i = 0; i < AUDIO_BUFFERS; i++) {
|
||||||
ad->xaudio_buffer[i].Flags = XAUDIO2_END_OF_STREAM;
|
ad->xaudio_buffer[i].Flags = XAUDIO2_END_OF_STREAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ad->lock();
|
ad->lock();
|
||||||
|
ad->start_counting_ticks();
|
||||||
|
|
||||||
ad->audio_server_process(ad->buffer_size, ad->samples_in);
|
ad->audio_server_process(ad->buffer_size, ad->samples_in);
|
||||||
|
|
||||||
|
ad->stop_counting_ticks();
|
||||||
ad->unlock();
|
ad->unlock();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
|
for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
|
||||||
|
@ -117,12 +118,10 @@ void AudioDriverXAudio2::thread_func(void *p_udata) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ad->thread_exited = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverXAudio2::start() {
|
void AudioDriverXAudio2::start() {
|
||||||
active = true;
|
active.set();
|
||||||
HRESULT hr = source_voice->Start(0);
|
HRESULT hr = source_voice->Start(0);
|
||||||
ERR_FAIL_COND_MSG(hr != S_OK, "Error starting XAudio2 driver. Error code: " + itos(hr) + ".");
|
ERR_FAIL_COND_MSG(hr != S_OK, "Error starting XAudio2 driver. Error code: " + itos(hr) + ".");
|
||||||
}
|
}
|
||||||
|
@ -156,7 +155,7 @@ void AudioDriverXAudio2::finish() {
|
||||||
if (!thread.is_started())
|
if (!thread.is_started())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
if (source_voice) {
|
if (source_voice) {
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
|
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
@ -77,9 +78,8 @@ class AudioDriverXAudio2 : public AudioDriver {
|
||||||
|
|
||||||
int channels;
|
int channels;
|
||||||
|
|
||||||
bool active;
|
SafeFlag active;
|
||||||
bool thread_exited;
|
SafeFlag exit_thread;
|
||||||
mutable bool exit_thread;
|
|
||||||
bool pcm_open;
|
bool pcm_open;
|
||||||
|
|
||||||
WAVEFORMATEX wave_format;
|
WAVEFORMATEX wave_format;
|
||||||
|
|
|
@ -569,7 +569,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorProperty::_unhandled_key_input(const Ref<InputEvent> &p_event) {
|
void EditorProperty::_unhandled_key_input(const Ref<InputEvent> &p_event) {
|
||||||
if (!selected) {
|
if (!selected || !is_visible_in_tree()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +765,7 @@ void EditorProperty::_bind_methods() {
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
|
||||||
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::STRING, "field"), PropertyInfo(Variant::BOOL, "changing")));
|
||||||
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
|
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
|
||||||
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
|
ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
|
||||||
ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
||||||
|
|
|
@ -58,18 +58,23 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorLog::_notification(int p_what) {
|
void EditorLog::_notification(int p_what) {
|
||||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
switch (p_what) {
|
||||||
//button->set_icon(get_icon("Console","EditorIcons"));
|
case NOTIFICATION_ENTER_TREE:
|
||||||
log->add_font_override("normal_font", get_font("output_source", "EditorFonts"));
|
case NOTIFICATION_THEME_CHANGED: {
|
||||||
log->add_color_override("selection_color", get_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
if (log != nullptr) {
|
||||||
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
|
|
||||||
Ref<DynamicFont> df_output_code = get_font("output_source", "EditorFonts");
|
Ref<DynamicFont> df_output_code = get_font("output_source", "EditorFonts");
|
||||||
if (df_output_code.is_valid()) {
|
if (df_output_code.is_valid()) {
|
||||||
if (log != nullptr) {
|
|
||||||
log->add_font_override("normal_font", get_font("output_source", "EditorFonts"));
|
log->add_font_override("normal_font", get_font("output_source", "EditorFonts"));
|
||||||
log->add_color_override("selection_color", get_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
log->add_color_override("selection_color", get_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
theme_cache.error_color = get_color("error_color", "Editor");
|
||||||
|
theme_cache.error_icon = get_icon("Error", "EditorIcons");
|
||||||
|
theme_cache.warning_color = get_color("warning_color", "Editor");
|
||||||
|
theme_cache.warning_icon = get_icon("Warning", "EditorIcons");
|
||||||
|
theme_cache.message_color = get_color("font_color", "Editor") * Color(1, 1, 1, 0.6);
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,22 +109,22 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
|
||||||
case MSG_TYPE_STD: {
|
case MSG_TYPE_STD: {
|
||||||
} break;
|
} break;
|
||||||
case MSG_TYPE_ERROR: {
|
case MSG_TYPE_ERROR: {
|
||||||
log->push_color(get_color("error_color", "Editor"));
|
log->push_color(theme_cache.error_color);
|
||||||
Ref<Texture> icon = get_icon("Error", "EditorIcons");
|
Ref<Texture> icon = theme_cache.error_icon;
|
||||||
log->add_image(icon);
|
log->add_image(icon);
|
||||||
log->add_text(" ");
|
log->add_text(" ");
|
||||||
tool_button->set_icon(icon);
|
tool_button->set_icon(icon);
|
||||||
} break;
|
} break;
|
||||||
case MSG_TYPE_WARNING: {
|
case MSG_TYPE_WARNING: {
|
||||||
log->push_color(get_color("warning_color", "Editor"));
|
log->push_color(theme_cache.warning_color);
|
||||||
Ref<Texture> icon = get_icon("Warning", "EditorIcons");
|
Ref<Texture> icon = theme_cache.warning_icon;
|
||||||
log->add_image(icon);
|
log->add_image(icon);
|
||||||
log->add_text(" ");
|
log->add_text(" ");
|
||||||
tool_button->set_icon(icon);
|
tool_button->set_icon(icon);
|
||||||
} break;
|
} break;
|
||||||
case MSG_TYPE_EDITOR: {
|
case MSG_TYPE_EDITOR: {
|
||||||
// Distinguish editor messages from messages printed by the project
|
// Distinguish editor messages from messages printed by the project
|
||||||
log->push_color(get_color("font_color", "Editor") * Color(1, 1, 1, 0.6));
|
log->push_color(theme_cache.message_color);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,16 @@
|
||||||
class EditorLog : public VBoxContainer {
|
class EditorLog : public VBoxContainer {
|
||||||
GDCLASS(EditorLog, VBoxContainer);
|
GDCLASS(EditorLog, VBoxContainer);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
Color error_color;
|
||||||
|
Ref<Texture> error_icon;
|
||||||
|
|
||||||
|
Color warning_color;
|
||||||
|
Ref<Texture> warning_icon;
|
||||||
|
|
||||||
|
Color message_color;
|
||||||
|
} theme_cache;
|
||||||
|
|
||||||
Button *clearbutton;
|
Button *clearbutton;
|
||||||
Button *copybutton;
|
Button *copybutton;
|
||||||
Label *title;
|
Label *title;
|
||||||
|
|
|
@ -1129,24 +1129,63 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case TOOL_TOGGLE_SCENE_UNIQUE_NAME: {
|
case TOOL_TOGGLE_SCENE_UNIQUE_NAME: {
|
||||||
List<Node *> selection = editor_selection->get_selected_node_list();
|
// Enabling/disabling based on the same node based on which the checkbox in the menu is checked/unchecked.
|
||||||
List<Node *>::Element *e = selection.front();
|
List<Node *>::Element *first_selected = editor_selection->get_selected_node_list().front();
|
||||||
if (e) {
|
if (first_selected == nullptr) {
|
||||||
UndoRedo *undo_redo = &editor_data->get_undo_redo();
|
|
||||||
Node *node = e->get();
|
|
||||||
bool enabled = node->is_unique_name_in_owner();
|
|
||||||
if (!enabled && get_tree()->get_edited_scene_root()->get_node_or_null(UNIQUE_NODE_PREFIX + String(node->get_name())) != nullptr) {
|
|
||||||
accept->set_text(TTR("Another node already uses this unique name in the scene."));
|
|
||||||
accept->popup_centered();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!enabled) {
|
bool enabling = !first_selected->get()->is_unique_name_in_owner();
|
||||||
undo_redo->create_action(TTR("Enable Scene Unique Name"));
|
|
||||||
} else {
|
List<Node *> full_selection = editor_selection->get_full_selected_node_list();
|
||||||
undo_redo->create_action(TTR("Disable Scene Unique Name"));
|
UndoRedo *undo_redo = &editor_data->get_undo_redo();
|
||||||
|
|
||||||
|
if (enabling) {
|
||||||
|
Vector<Node *> new_unique_nodes;
|
||||||
|
Vector<StringName> new_unique_names;
|
||||||
|
Vector<StringName> cant_be_set_unique_names;
|
||||||
|
|
||||||
|
for (List<Node *>::Element *e = full_selection.front(); e; e = e->next()) {
|
||||||
|
Node *node = e->get();
|
||||||
|
if (node->is_unique_name_in_owner()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StringName name = node->get_name();
|
||||||
|
if (new_unique_names.find(name) != -1 || get_tree()->get_edited_scene_root()->get_node_or_null(UNIQUE_NODE_PREFIX + String(name)) != nullptr) {
|
||||||
|
cant_be_set_unique_names.push_back(name);
|
||||||
|
} else {
|
||||||
|
new_unique_nodes.push_back(node);
|
||||||
|
new_unique_names.push_back(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_unique_nodes.size()) {
|
||||||
|
undo_redo->create_action(TTR("Enable Scene Unique Name(s)"));
|
||||||
|
for (int i = 0; i < new_unique_nodes.size(); i++) {
|
||||||
|
undo_redo->add_do_method(new_unique_nodes[i], "set_unique_name_in_owner", true);
|
||||||
|
undo_redo->add_undo_method(new_unique_nodes[i], "set_unique_name_in_owner", false);
|
||||||
|
}
|
||||||
|
undo_redo->commit_action();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cant_be_set_unique_names.size()) {
|
||||||
|
String popup_text = TTR("Unique names already used by another node in the scene:");
|
||||||
|
popup_text += "\n";
|
||||||
|
for (int i = 0; i < cant_be_set_unique_names.size(); i++) {
|
||||||
|
popup_text += "\n" + String(cant_be_set_unique_names[i]);
|
||||||
|
}
|
||||||
|
accept->set_text(popup_text);
|
||||||
|
accept->popup_centered();
|
||||||
|
}
|
||||||
|
} else { // Disabling.
|
||||||
|
undo_redo->create_action(TTR("Disable Scene Unique Name(s)"));
|
||||||
|
for (List<Node *>::Element *e = full_selection.front(); e; e = e->next()) {
|
||||||
|
Node *node = e->get();
|
||||||
|
if (!node->is_unique_name_in_owner()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
undo_redo->add_do_method(node, "set_unique_name_in_owner", false);
|
||||||
|
undo_redo->add_undo_method(node, "set_unique_name_in_owner", true);
|
||||||
}
|
}
|
||||||
undo_redo->add_do_method(node, "set_unique_name_in_owner", !enabled);
|
|
||||||
undo_redo->add_undo_method(node, "set_unique_name_in_owner", enabled);
|
|
||||||
undo_redo->commit_action();
|
undo_redo->commit_action();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
@ -2815,9 +2854,18 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile_allow_editing) {
|
if (profile_allow_editing) {
|
||||||
if (selection[0]->get_owner() == EditorNode::get_singleton()->get_edited_scene()) {
|
// Allow multi-toggling scene unique names but only if all selected nodes are owned by the edited scene root.
|
||||||
// Only for nodes owned by the edited scene root.
|
bool all_owned = true;
|
||||||
|
for (List<Node *>::Element *e = full_selection.front(); e; e = e->next()) {
|
||||||
|
Node *node = e->get();
|
||||||
|
if (node->get_owner() != EditorNode::get_singleton()->get_edited_scene()) {
|
||||||
|
all_owned = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_owned) {
|
||||||
menu->add_icon_check_item(get_icon("SceneUniqueName", "EditorIcons"), TTR("Access as Scene Unique Name"), TOOL_TOGGLE_SCENE_UNIQUE_NAME);
|
menu->add_icon_check_item(get_icon("SceneUniqueName", "EditorIcons"), TTR("Access as Scene Unique Name"), TOOL_TOGGLE_SCENE_UNIQUE_NAME);
|
||||||
|
// Checked based on `selection[0]` because `full_selection` has undesired ordering.
|
||||||
menu->set_item_checked(menu->get_item_index(TOOL_TOGGLE_SCENE_UNIQUE_NAME), selection[0]->is_unique_name_in_owner());
|
menu->set_item_checked(menu->get_item_index(TOOL_TOGGLE_SCENE_UNIQUE_NAME), selection[0]->is_unique_name_in_owner());
|
||||||
menu->add_separator();
|
menu->add_separator();
|
||||||
}
|
}
|
||||||
|
|
|
@ -841,11 +841,9 @@ void InputDefault::joy_axis(int p_device, int p_axis, float p_value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pressed = map.value > 0.5;
|
bool pressed = map.value > 0.5;
|
||||||
if (pressed == joy_buttons_pressed.has(_combine_device(map.index, p_device))) {
|
if (pressed != joy_buttons_pressed.has(_combine_device(map.index, p_device))) {
|
||||||
// Button already pressed or released; so ignore.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_button_event(p_device, map.index, pressed);
|
_button_event(p_device, map.index, pressed);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure opposite D-Pad button is also released.
|
// Ensure opposite D-Pad button is also released.
|
||||||
switch (map.index) {
|
switch (map.index) {
|
||||||
|
@ -992,7 +990,7 @@ InputDefault::JoyEvent InputDefault::_get_mapped_axis_event(const JoyDeviceMappi
|
||||||
value = -value;
|
value = -value;
|
||||||
}
|
}
|
||||||
if (binding.input.axis.range == FULL_AXIS ||
|
if (binding.input.axis.range == FULL_AXIS ||
|
||||||
(binding.input.axis.range == POSITIVE_HALF_AXIS && value > 0) ||
|
(binding.input.axis.range == POSITIVE_HALF_AXIS && value >= 0) ||
|
||||||
(binding.input.axis.range == NEGATIVE_HALF_AXIS && value < 0)) {
|
(binding.input.axis.range == NEGATIVE_HALF_AXIS && value < 0)) {
|
||||||
event.type = binding.outputType;
|
event.type = binding.outputType;
|
||||||
float shifted_positive_value = 0;
|
float shifted_positive_value = 0;
|
||||||
|
|
|
@ -333,6 +333,7 @@ def use_windows_spawn_fix(self, platform=None):
|
||||||
startupinfo=startupinfo,
|
startupinfo=startupinfo,
|
||||||
shell=False,
|
shell=False,
|
||||||
env=env,
|
env=env,
|
||||||
|
text=True,
|
||||||
)
|
)
|
||||||
_, err = proc.communicate()
|
_, err = proc.communicate()
|
||||||
rv = proc.wait()
|
rv = proc.wait()
|
||||||
|
|
|
@ -904,10 +904,12 @@ void GridMapEditor::update_palette() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected != -1 && mesh_library_palette->get_item_count() > 0) {
|
if (selected != -1 && mesh_library_palette->get_item_count() > 0) {
|
||||||
mesh_library_palette->select(selected);
|
// Make sure that this variable is set correctly.
|
||||||
|
selected_palette = MIN(selected, mesh_library_palette->get_item_count() - 1);
|
||||||
|
mesh_library_palette->select(selected_palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_mesh_library = mesh_library.operator->();
|
last_mesh_library = *mesh_library;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridMapEditor::edit(GridMap *p_gridmap) {
|
void GridMapEditor::edit(GridMap *p_gridmap) {
|
||||||
|
|
|
@ -722,19 +722,24 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||||
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
|
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
|
||||||
|
|
||||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||||
|
String assembly_name = ProjectSettings::get_singleton()->get_setting("mono/project/assembly_name");
|
||||||
|
|
||||||
|
if (assembly_name.empty()) {
|
||||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||||
if (appname_safe.empty()) {
|
if (appname_safe.empty()) {
|
||||||
appname_safe = "UnnamedProject";
|
appname_safe = "UnnamedProject";
|
||||||
}
|
}
|
||||||
|
assembly_name = appname_safe;
|
||||||
|
}
|
||||||
|
|
||||||
appname_safe += ".dll";
|
assembly_name += ".dll";
|
||||||
|
|
||||||
if (proj_assembly) {
|
if (proj_assembly) {
|
||||||
String proj_asm_path = proj_assembly->get_path();
|
String proj_asm_path = proj_assembly->get_path();
|
||||||
|
|
||||||
if (!FileAccess::exists(proj_asm_path)) {
|
if (!FileAccess::exists(proj_asm_path)) {
|
||||||
// Maybe it wasn't loaded from the default path, so check this as well
|
// Maybe it wasn't loaded from the default path, so check this as well
|
||||||
proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
|
proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(assembly_name);
|
||||||
if (!FileAccess::exists(proj_asm_path))
|
if (!FileAccess::exists(proj_asm_path))
|
||||||
return false; // No assembly to load
|
return false; // No assembly to load
|
||||||
}
|
}
|
||||||
|
@ -742,7 +747,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||||
if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
|
if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
|
||||||
return false; // Already up to date
|
return false; // Already up to date
|
||||||
} else {
|
} else {
|
||||||
if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe)))
|
if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(assembly_name)))
|
||||||
return false; // No assembly to load
|
return false; // No assembly to load
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ namespace GodotTools.Export
|
||||||
RunLipo(new[] {CompileForArch("arm64"), CompileForArch("x86_64")}, libFilePath);
|
RunLipo(new[] {CompileForArch("arm64"), CompileForArch("x86_64")}, libFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName;
|
string projectAssemblyName = GodotSharpDirs.ProjectAssemblyName;
|
||||||
string libAotName = $"lib-aot-{projectAssemblyName}";
|
string libAotName = $"lib-aot-{projectAssemblyName}";
|
||||||
|
|
||||||
string libAotXcFrameworkPath = Path.Combine(aotTempDir, $"{libAotName}.xcframework");
|
string libAotXcFrameworkPath = Path.Combine(aotTempDir, $"{libAotName}.xcframework");
|
||||||
|
|
|
@ -167,7 +167,7 @@ namespace GodotTools.Export
|
||||||
|
|
||||||
var assemblies = new Godot.Collections.Dictionary<string, string>();
|
var assemblies = new Godot.Collections.Dictionary<string, string>();
|
||||||
|
|
||||||
string projectDllName = GodotSharpEditor.ProjectAssemblyName;
|
string projectDllName = GodotSharpDirs.ProjectAssemblyName;
|
||||||
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
||||||
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
||||||
|
|
||||||
|
|
|
@ -39,18 +39,6 @@ namespace GodotTools
|
||||||
|
|
||||||
public bool SkipBuildBeforePlaying { get; set; } = false;
|
public bool SkipBuildBeforePlaying { get; set; } = false;
|
||||||
|
|
||||||
public static string ProjectAssemblyName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
|
|
||||||
projectAssemblyName = projectAssemblyName.ToSafeDirName();
|
|
||||||
if (string.IsNullOrEmpty(projectAssemblyName))
|
|
||||||
projectAssemblyName = "UnnamedProject";
|
|
||||||
return projectAssemblyName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CreateProjectSolution()
|
private bool CreateProjectSolution()
|
||||||
{
|
{
|
||||||
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
|
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
|
||||||
|
@ -60,7 +48,7 @@ namespace GodotTools
|
||||||
string resourceDir = ProjectSettings.GlobalizePath("res://");
|
string resourceDir = ProjectSettings.GlobalizePath("res://");
|
||||||
|
|
||||||
string path = resourceDir;
|
string path = resourceDir;
|
||||||
string name = ProjectAssemblyName;
|
string name = GodotSharpDirs.ProjectAssemblyName;
|
||||||
|
|
||||||
string guid = CsProjOperations.GenerateGameProject(path, name);
|
string guid = CsProjOperations.GenerateGameProject(path, name);
|
||||||
|
|
||||||
|
@ -375,7 +363,8 @@ namespace GodotTools
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public bool OverridesExternalEditor()
|
public bool OverridesExternalEditor()
|
||||||
{
|
{
|
||||||
return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
|
return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") !=
|
||||||
|
ExternalEditorId.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Build()
|
public override bool Build()
|
||||||
|
@ -396,7 +385,7 @@ namespace GodotTools
|
||||||
// NOTE: The order in which changes are made to the project is important
|
// NOTE: The order in which changes are made to the project is important
|
||||||
|
|
||||||
// Migrate to MSBuild project Sdks style if using the old style
|
// Migrate to MSBuild project Sdks style if using the old style
|
||||||
ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, ProjectAssemblyName);
|
ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
|
||||||
|
|
||||||
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
|
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace GodotTools.Internals
|
||||||
public static string MonoSolutionsDir => internal_MonoSolutionsDir();
|
public static string MonoSolutionsDir => internal_MonoSolutionsDir();
|
||||||
public static string BuildLogsDirs => internal_BuildLogsDirs();
|
public static string BuildLogsDirs => internal_BuildLogsDirs();
|
||||||
|
|
||||||
|
public static string ProjectAssemblyName => internal_ProjectAssemblyName();
|
||||||
public static string ProjectSlnPath => internal_ProjectSlnPath();
|
public static string ProjectSlnPath => internal_ProjectSlnPath();
|
||||||
public static string ProjectCsProjPath => internal_ProjectCsProjPath();
|
public static string ProjectCsProjPath => internal_ProjectCsProjPath();
|
||||||
|
|
||||||
|
@ -74,6 +75,9 @@ namespace GodotTools.Internals
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
private static extern string internal_BuildLogsDirs();
|
private static extern string internal_BuildLogsDirs();
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
|
private static extern string internal_ProjectAssemblyName();
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
private static extern string internal_ProjectSlnPath();
|
private static extern string internal_ProjectSlnPath();
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,14 @@ MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MonoString *godot_icall_GodotSharpDirs_ProjectAssemblyName() {
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_assembly_name());
|
||||||
|
#else
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
|
MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
|
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
|
||||||
|
@ -388,6 +396,7 @@ void register_editor_internal_calls() {
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
|
||||||
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectAssemblyName", godot_icall_GodotSharpDirs_ProjectAssemblyName);
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
|
||||||
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
|
GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
|
||||||
|
|
|
@ -102,6 +102,7 @@ public:
|
||||||
String mono_solutions_dir;
|
String mono_solutions_dir;
|
||||||
String build_logs_dir;
|
String build_logs_dir;
|
||||||
|
|
||||||
|
String project_assembly_name;
|
||||||
String sln_filepath;
|
String sln_filepath;
|
||||||
String csproj_filepath;
|
String csproj_filepath;
|
||||||
|
|
||||||
|
@ -144,16 +145,35 @@ private:
|
||||||
mono_solutions_dir = mono_user_dir.plus_file("solutions");
|
mono_solutions_dir = mono_user_dir.plus_file("solutions");
|
||||||
build_logs_dir = mono_user_dir.plus_file("build_logs");
|
build_logs_dir = mono_user_dir.plus_file("build_logs");
|
||||||
|
|
||||||
|
GLOBAL_DEF("mono/project/assembly_name", "");
|
||||||
|
GLOBAL_DEF("mono/project/solution_directory", "");
|
||||||
|
GLOBAL_DEF("mono/project/c#_project_directory", "");
|
||||||
|
|
||||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||||
if (appname_safe.empty()) {
|
if (appname_safe.empty()) {
|
||||||
appname_safe = "UnnamedProject";
|
appname_safe = "UnnamedProject";
|
||||||
}
|
}
|
||||||
|
|
||||||
String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
|
project_assembly_name = ProjectSettings::get_singleton()->get("mono/project/assembly_name");
|
||||||
|
if (project_assembly_name.empty()) {
|
||||||
|
project_assembly_name = appname_safe;
|
||||||
|
ProjectSettings::get_singleton()->set("mono/project/assembly_name", project_assembly_name);
|
||||||
|
}
|
||||||
|
|
||||||
sln_filepath = base_path.plus_file(appname_safe + ".sln");
|
String sln_parent_dir = ProjectSettings::get_singleton()->get("mono/project/solution_directory");
|
||||||
csproj_filepath = base_path.plus_file(appname_safe + ".csproj");
|
if (sln_parent_dir.empty()) {
|
||||||
|
sln_parent_dir = "res://";
|
||||||
|
}
|
||||||
|
|
||||||
|
String csproj_parent_dir = ProjectSettings::get_singleton()->get("mono/project/c#_project_directory");
|
||||||
|
if (csproj_parent_dir.empty()) {
|
||||||
|
csproj_parent_dir = "res://";
|
||||||
|
}
|
||||||
|
|
||||||
|
sln_filepath = ProjectSettings::get_singleton()->globalize_path(sln_parent_dir).plus_file(project_assembly_name + ".sln");
|
||||||
|
|
||||||
|
csproj_filepath = ProjectSettings::get_singleton()->globalize_path(csproj_parent_dir).plus_file(project_assembly_name + ".csproj");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||||
|
@ -288,6 +308,10 @@ String get_build_logs_dir() {
|
||||||
return _GodotSharpDirs::get_singleton().build_logs_dir;
|
return _GodotSharpDirs::get_singleton().build_logs_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get_project_assembly_name() {
|
||||||
|
return _GodotSharpDirs::get_singleton().project_assembly_name;
|
||||||
|
}
|
||||||
|
|
||||||
String get_project_sln_path() {
|
String get_project_sln_path() {
|
||||||
return _GodotSharpDirs::get_singleton().sln_filepath;
|
return _GodotSharpDirs::get_singleton().sln_filepath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ String get_mono_logs_dir();
|
||||||
String get_mono_solutions_dir();
|
String get_mono_solutions_dir();
|
||||||
String get_build_logs_dir();
|
String get_build_logs_dir();
|
||||||
|
|
||||||
|
String get_project_assembly_name();
|
||||||
String get_project_sln_path();
|
String get_project_sln_path();
|
||||||
String get_project_csproj_path();
|
String get_project_csproj_path();
|
||||||
|
|
||||||
|
|
|
@ -987,13 +987,15 @@ bool GDMono::_load_project_assembly() {
|
||||||
if (project_assembly)
|
if (project_assembly)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
String assembly_name = ProjectSettings::get_singleton()->get("mono/project/assembly_name");
|
||||||
|
|
||||||
|
if (assembly_name.empty()) {
|
||||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||||
if (appname_safe.empty()) {
|
assembly_name = appname_safe;
|
||||||
appname_safe = "UnnamedProject";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = load_assembly(appname_safe, &project_assembly);
|
bool success = load_assembly(assembly_name, &project_assembly);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
mono_assembly_set_main(project_assembly->get_assembly());
|
mono_assembly_set_main(project_assembly->get_assembly());
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<class name="UPNP" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
<class name="UPNP" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||||
<brief_description>
|
<brief_description>
|
||||||
UPNP network functions.
|
Universal Plug and Play (UPnP) functions for network device discovery, querying and port forwarding.
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
Provides UPNP functionality to discover [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (port forwarding) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
|
This class can be used to discover compatible [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (for port forwarding/NAT traversal) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
|
||||||
To forward a specific port:
|
To forward a specific port (here [code]7777[/code], note both [method discover] and [method add_port_mapping] can return errors that should be checked):
|
||||||
[codeblock]
|
[codeblock]
|
||||||
const PORT = 7777
|
|
||||||
var upnp = UPNP.new()
|
var upnp = UPNP.new()
|
||||||
upnp.discover(2000, 2, "InternetGatewayDevice")
|
upnp.discover()
|
||||||
upnp.add_port_mapping(port)
|
upnp.add_port_mapping(7777)
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
To close a specific port (e.g. after you have finished using it):
|
To close a specific port (e.g. after you have finished using it):
|
||||||
[codeblock]
|
[codeblock]
|
||||||
|
@ -21,7 +20,7 @@
|
||||||
# Emitted when UPnP port mapping setup is completed (regardless of success or failure).
|
# Emitted when UPnP port mapping setup is completed (regardless of success or failure).
|
||||||
signal upnp_completed(error)
|
signal upnp_completed(error)
|
||||||
|
|
||||||
# Replace this with your own server port number between 1025 and 65535.
|
# Replace this with your own server port number between 1024 and 65535.
|
||||||
const SERVER_PORT = 3928
|
const SERVER_PORT = 3928
|
||||||
var thread = null
|
var thread = null
|
||||||
|
|
||||||
|
@ -48,6 +47,14 @@
|
||||||
# Wait for thread finish here to handle game exit while the thread is running.
|
# Wait for thread finish here to handle game exit while the thread is running.
|
||||||
thread.wait_to_finish()
|
thread.wait_to_finish()
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
|
[b]Terminology:[/b] In the context of UPnP networking, "gateway" (or "internet gateway device", short IGD) refers to network devices that allow computers in the local network to access the internet ("wide area network", WAN). These gateways are often also called "routers".
|
||||||
|
[b]Pitfalls:[/b]
|
||||||
|
- As explained above, these calls are blocking and shouldn't be run on the main thread, especially as they can block for multiple seconds at a time. Use threading!
|
||||||
|
- Networking is physical and messy. Packets get lost in transit or get filtered, addresses, free ports and assigned mappings change, and devices may leave or join the network at any time. Be mindful of this, be diligent when checking and handling errors, and handle these gracefully if you can: add clear error UI, timeouts and re-try handling.
|
||||||
|
- Port mappings may change (and be removed) at any time, and the remote/external IP address of the gateway can change likewise. You should consider re-querying the external IP and try to update/refresh the port mapping periodically (for example, every 5 minutes and on networking failures).
|
||||||
|
- Not all devices support UPnP, and some users disable UPnP support. You need to handle this (e.g. documenting and requiring the user to manually forward ports, or adding alternative methods of NAT traversal, like a relay/mirror server, or NAT hole punching, STUN/TURN, etc.).
|
||||||
|
- Consider what happens on mapping conflicts. Maybe multiple users on the same network would like to play your game at the same time, or maybe another application uses the same port. Make the port configurable, and optimally choose a port automatically (re-trying with a different port on failure).
|
||||||
|
[b]Further reading:[/b] If you want to know more about UPnP (and the Internet Gateway Device (IGD) and Port Control Protocol (PCP) specifically), [url=https://en.wikipedia.org/wiki/Universal_Plug_and_Play]Wikipedia[/url] is a good first stop, the specification can be found at the [url=https://openconnectivity.org/developer/specifications/upnp-resources/upnp/]Open Connectivity Foundation[/url] and Godot's implementation is based on the [url=https://github.com/miniupnp/miniupnp]MiniUPnP client[/url].
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
|
@ -67,9 +74,11 @@
|
||||||
<argument index="3" name="proto" type="String" default=""UDP"" />
|
<argument index="3" name="proto" type="String" default=""UDP"" />
|
||||||
<argument index="4" name="duration" type="int" default="0" />
|
<argument index="4" name="duration" type="int" default="0" />
|
||||||
<description>
|
<description>
|
||||||
Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any.
|
Adds a mapping to forward the external [code]port[/code] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device.
|
||||||
|
Depending on the gateway device, if a mapping for that port already exists, it will either be updated or it will refuse this command due to that conflict, especially if the existing mapping for that port wasn't created via UPnP or points to a different network address (or device) than this one.
|
||||||
If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
|
If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
|
||||||
The description ([code]desc[/code]) is shown in some router UIs and can be used to point out which application added the mapping. The mapping's lease duration can be limited by specifying a [code]duration[/code] (in seconds). However, some routers are incompatible with one or both of these, so use with caution and add fallback logic in case of errors to retry without them if in doubt.
|
The description ([code]desc[/code]) is shown in some routers management UIs and can be used to point out which application added the mapping.
|
||||||
|
The mapping's lease [code]duration[/code] can be limited by specifying a duration in seconds. The default of [code]0[/code] means no duration, i.e. a permanent lease and notably some devices only support these permanent leases. Note that whether permanent or not, this is only a request and the gateway may still decide at any point to remove the mapping (which usually happens on a reboot of the gateway, when its external IP address changes, or on some models when it detects a port mapping has become inactive, i.e. had no traffic for multiple minutes). If not [code]0[/code] (permanent), the allowed range according to spec is between [code]120[/code] (2 minutes) and [code]86400[/code] seconds (24 hours).
|
||||||
See [enum UPNPResult] for possible return values.
|
See [enum UPNPResult] for possible return values.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
@ -84,7 +93,7 @@
|
||||||
<argument index="0" name="port" type="int" />
|
<argument index="0" name="port" type="int" />
|
||||||
<argument index="1" name="proto" type="String" default=""UDP"" />
|
<argument index="1" name="proto" type="String" default=""UDP"" />
|
||||||
<description>
|
<description>
|
||||||
Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values.
|
Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="discover">
|
<method name="discover">
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<class name="UPNPDevice" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
<class name="UPNPDevice" inherits="Reference" version="3.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||||
<brief_description>
|
<brief_description>
|
||||||
UPNP device.
|
Universal Plug and Play (UPnP) device.
|
||||||
</brief_description>
|
</brief_description>
|
||||||
<description>
|
<description>
|
||||||
UPNP device. See [UPNP] for UPNP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
|
Universal Plug and Play (UPnP) device. See [UPNP] for UPnP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
|
|
|
@ -518,10 +518,10 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
|
||||||
t = Variant::REAL;
|
t = Variant::REAL;
|
||||||
} break;
|
} break;
|
||||||
case MATH_ABS: {
|
case MATH_ABS: {
|
||||||
t = Variant::NIL;
|
t = Variant::REAL;
|
||||||
} break;
|
} break;
|
||||||
case MATH_SIGN: {
|
case MATH_SIGN: {
|
||||||
t = Variant::NIL;
|
t = Variant::REAL;
|
||||||
} break;
|
} break;
|
||||||
case MATH_POW:
|
case MATH_POW:
|
||||||
case MATH_LOG:
|
case MATH_LOG:
|
||||||
|
@ -547,7 +547,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
|
||||||
case MATH_MOVE_TOWARD:
|
case MATH_MOVE_TOWARD:
|
||||||
case MATH_DECTIME: {
|
case MATH_DECTIME: {
|
||||||
t = Variant::REAL;
|
t = Variant::REAL;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case MATH_RANDOMIZE: {
|
case MATH_RANDOMIZE: {
|
||||||
} break;
|
} break;
|
||||||
|
@ -584,34 +583,29 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
|
||||||
case LOGIC_MAX:
|
case LOGIC_MAX:
|
||||||
case LOGIC_MIN:
|
case LOGIC_MIN:
|
||||||
case LOGIC_CLAMP: {
|
case LOGIC_CLAMP: {
|
||||||
|
t = Variant::REAL;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case LOGIC_NEAREST_PO2: {
|
case LOGIC_NEAREST_PO2: {
|
||||||
t = Variant::NIL;
|
t = Variant::INT;
|
||||||
} break;
|
} break;
|
||||||
case OBJ_WEAKREF: {
|
case OBJ_WEAKREF: {
|
||||||
t = Variant::OBJECT;
|
t = Variant::OBJECT;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case FUNC_FUNCREF: {
|
case FUNC_FUNCREF: {
|
||||||
t = Variant::OBJECT;
|
t = Variant::OBJECT;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TYPE_CONVERT: {
|
case TYPE_CONVERT: {
|
||||||
} break;
|
} break;
|
||||||
case TEXT_ORD:
|
case TEXT_ORD:
|
||||||
case TYPE_OF: {
|
case TYPE_OF: {
|
||||||
t = Variant::INT;
|
t = Variant::INT;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TYPE_EXISTS: {
|
case TYPE_EXISTS: {
|
||||||
t = Variant::BOOL;
|
t = Variant::BOOL;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TEXT_CHAR:
|
case TEXT_CHAR:
|
||||||
case TEXT_STR: {
|
case TEXT_STR: {
|
||||||
t = Variant::STRING;
|
t = Variant::STRING;
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case TEXT_PRINT: {
|
case TEXT_PRINT: {
|
||||||
} break;
|
} break;
|
||||||
|
@ -630,7 +624,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
|
||||||
} else {
|
} else {
|
||||||
t = Variant::BOOL;
|
t = Variant::BOOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case BYTES_TO_VAR: {
|
case BYTES_TO_VAR: {
|
||||||
if (p_idx == 1) {
|
if (p_idx == 1) {
|
||||||
|
@ -1218,10 +1211,6 @@ public:
|
||||||
|
|
||||||
VisualScriptBuiltinFunc::BuiltinFunc func;
|
VisualScriptBuiltinFunc::BuiltinFunc func;
|
||||||
|
|
||||||
//virtual int get_working_memory_size() const { return 0; }
|
|
||||||
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
|
|
||||||
//virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
|
|
||||||
|
|
||||||
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
|
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
|
||||||
VisualScriptBuiltinFunc::exec_func(func, p_inputs, p_outputs[0], r_error, r_error_str);
|
VisualScriptBuiltinFunc::exec_func(func, p_inputs, p_outputs[0], r_error, r_error_str);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -134,6 +134,20 @@ String DirAccessJAndroid::get_drive(int p_drive) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String DirAccessJAndroid::get_current_dir() {
|
||||||
|
String base = _get_root_path();
|
||||||
|
String bd = current_dir;
|
||||||
|
if (base != "") {
|
||||||
|
bd = current_dir.replace_first(base, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bd.begins_with("/")) {
|
||||||
|
return _get_root_string() + bd.substr(1, bd.length());
|
||||||
|
} else {
|
||||||
|
return _get_root_string() + bd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error DirAccessJAndroid::change_dir(String p_dir) {
|
Error DirAccessJAndroid::change_dir(String p_dir) {
|
||||||
String new_dir = get_absolute_path(p_dir);
|
String new_dir = get_absolute_path(p_dir);
|
||||||
if (new_dir == current_dir) {
|
if (new_dir == current_dir) {
|
||||||
|
|
|
@ -69,6 +69,7 @@ public:
|
||||||
virtual String get_drive(int p_drive) override;
|
virtual String get_drive(int p_drive) override;
|
||||||
|
|
||||||
virtual Error change_dir(String p_dir) override; ///< can be relative or absolute, return false on success
|
virtual Error change_dir(String p_dir) override; ///< can be relative or absolute, return false on success
|
||||||
|
virtual String get_current_dir() override; ///< return current dir location
|
||||||
|
|
||||||
virtual bool file_exists(String p_file) override;
|
virtual bool file_exists(String p_file) override;
|
||||||
virtual bool dir_exists(String p_dir) override;
|
virtual bool dir_exists(String p_dir) override;
|
||||||
|
|
|
@ -228,6 +228,7 @@ static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/instal
|
||||||
static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
|
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 = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk'
|
||||||
|
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||||
EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
|
EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
|
||||||
|
|
||||||
|
@ -259,7 +260,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ANDROID_ENABLED
|
|
||||||
// Check for devices updates
|
// Check for devices updates
|
||||||
String adb = get_adb_path();
|
String adb = get_adb_path();
|
||||||
if (FileAccess::exists(adb)) {
|
if (FileAccess::exists(adb)) {
|
||||||
|
@ -373,7 +373,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||||
|
|
||||||
ea->device_lock.unlock();
|
ea->device_lock.unlock();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
uint64_t sleep = 300'000;
|
uint64_t sleep = 300'000;
|
||||||
uint64_t wait = 3'000'000;
|
uint64_t wait = 3'000'000;
|
||||||
|
@ -386,7 +385,6 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ANDROID_ENABLED
|
|
||||||
if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
|
if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
|
||||||
String adb = get_adb_path();
|
String adb = get_adb_path();
|
||||||
if (!FileAccess::exists(adb)) {
|
if (!FileAccess::exists(adb)) {
|
||||||
|
@ -397,8 +395,8 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
|
||||||
args.push_back("kill-server");
|
args.push_back("kill-server");
|
||||||
OS::get_singleton()->execute(adb, args, true);
|
OS::get_singleton()->execute(adb, args, true);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
|
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
|
||||||
String aname;
|
String aname;
|
||||||
|
@ -3438,10 +3436,14 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
|
||||||
|
|
||||||
devices_changed.set();
|
devices_changed.set();
|
||||||
plugins_changed.set();
|
plugins_changed.set();
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorExportPlatformAndroid::~EditorExportPlatformAndroid() {
|
EditorExportPlatformAndroid::~EditorExportPlatformAndroid() {
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
quit_request.set();
|
quit_request.set();
|
||||||
check_for_changes_thread.wait_to_finish();
|
check_for_changes_thread.wait_to_finish();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,10 +98,12 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||||
Vector<Device> devices;
|
Vector<Device> devices;
|
||||||
SafeFlag devices_changed;
|
SafeFlag devices_changed;
|
||||||
Mutex device_lock;
|
Mutex device_lock;
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
Thread check_for_changes_thread;
|
Thread check_for_changes_thread;
|
||||||
SafeFlag quit_request;
|
SafeFlag quit_request;
|
||||||
|
|
||||||
static void _check_for_changes_poll_thread(void *ud);
|
static void _check_for_changes_poll_thread(void *ud);
|
||||||
|
#endif
|
||||||
|
|
||||||
String get_project_name(const String &p_name) const;
|
String get_project_name(const String &p_name) const;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ jmethodID FileAccessFilesystemJAndroid::_file_seek_end = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_read = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_tell = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_eof = nullptr;
|
||||||
|
jmethodID FileAccessFilesystemJAndroid::_file_set_eof = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_close = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_write = nullptr;
|
||||||
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
|
jmethodID FileAccessFilesystemJAndroid::_file_flush = nullptr;
|
||||||
|
@ -161,6 +162,16 @@ bool FileAccessFilesystemJAndroid::eof_reached() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileAccessFilesystemJAndroid::_set_eof(bool eof) {
|
||||||
|
if (_file_set_eof) {
|
||||||
|
ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
|
||||||
|
|
||||||
|
JNIEnv *env = get_jni_env();
|
||||||
|
ERR_FAIL_COND(env == nullptr);
|
||||||
|
env->CallVoidMethod(file_access_handler, _file_set_eof, id, eof);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t FileAccessFilesystemJAndroid::get_8() const {
|
uint8_t FileAccessFilesystemJAndroid::get_8() const {
|
||||||
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
@ -183,6 +194,7 @@ String FileAccessFilesystemJAndroid::get_line() const {
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
|
size_t line_buffer_size = MIN(buffer_size_limit, file_size - get_position());
|
||||||
if (line_buffer_size <= 0) {
|
if (line_buffer_size <= 0) {
|
||||||
|
const_cast<FileAccessFilesystemJAndroid *>(this)->_set_eof(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +321,7 @@ void FileAccessFilesystemJAndroid::setup(jobject p_file_access_handler) {
|
||||||
_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
|
_file_get_size = env->GetMethodID(cls, "fileGetSize", "(I)J");
|
||||||
_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
|
_file_tell = env->GetMethodID(cls, "fileGetPosition", "(I)J");
|
||||||
_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
|
_file_eof = env->GetMethodID(cls, "isFileEof", "(I)Z");
|
||||||
|
_file_set_eof = env->GetMethodID(cls, "setFileEof", "(IZ)V");
|
||||||
_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
|
_file_seek = env->GetMethodID(cls, "fileSeek", "(IJ)V");
|
||||||
_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
|
_file_seek_end = env->GetMethodID(cls, "fileSeekFromEnd", "(IJ)V");
|
||||||
_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
|
_file_read = env->GetMethodID(cls, "fileRead", "(ILjava/nio/ByteBuffer;)I");
|
||||||
|
|
|
@ -44,6 +44,7 @@ class FileAccessFilesystemJAndroid : public FileAccess {
|
||||||
static jmethodID _file_seek_end;
|
static jmethodID _file_seek_end;
|
||||||
static jmethodID _file_tell;
|
static jmethodID _file_tell;
|
||||||
static jmethodID _file_eof;
|
static jmethodID _file_eof;
|
||||||
|
static jmethodID _file_set_eof;
|
||||||
static jmethodID _file_read;
|
static jmethodID _file_read;
|
||||||
static jmethodID _file_write;
|
static jmethodID _file_write;
|
||||||
static jmethodID _file_flush;
|
static jmethodID _file_flush;
|
||||||
|
@ -55,6 +56,8 @@ class FileAccessFilesystemJAndroid : public FileAccess {
|
||||||
String absolute_path;
|
String absolute_path;
|
||||||
String path_src;
|
String path_src;
|
||||||
|
|
||||||
|
void _set_eof(bool eof);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
|
virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||||
virtual void close() override; ///< close a file
|
virtual void close() override; ///< close a file
|
||||||
|
|
|
@ -104,7 +104,6 @@ internal abstract class DataAccess(private val filePath: String) {
|
||||||
|
|
||||||
protected abstract val fileChannel: FileChannel
|
protected abstract val fileChannel: FileChannel
|
||||||
internal var endOfFile = false
|
internal var endOfFile = false
|
||||||
private set
|
|
||||||
|
|
||||||
fun close() {
|
fun close() {
|
||||||
try {
|
try {
|
||||||
|
@ -125,9 +124,7 @@ internal abstract class DataAccess(private val filePath: String) {
|
||||||
fun seek(position: Long) {
|
fun seek(position: Long) {
|
||||||
try {
|
try {
|
||||||
fileChannel.position(position)
|
fileChannel.position(position)
|
||||||
if (position <= size()) {
|
endOfFile = position >= fileChannel.size()
|
||||||
endOfFile = false
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "Exception when seeking file $filePath.", e)
|
Log.w(TAG, "Exception when seeking file $filePath.", e)
|
||||||
}
|
}
|
||||||
|
@ -161,8 +158,7 @@ internal abstract class DataAccess(private val filePath: String) {
|
||||||
fun read(buffer: ByteBuffer): Int {
|
fun read(buffer: ByteBuffer): Int {
|
||||||
return try {
|
return try {
|
||||||
val readBytes = fileChannel.read(buffer)
|
val readBytes = fileChannel.read(buffer)
|
||||||
endOfFile = readBytes == -1
|
endOfFile = readBytes == -1 || (fileChannel.position() >= fileChannel.size())
|
||||||
|| (fileChannel.position() >= fileChannel.size() && fileChannel.size() > 0)
|
|
||||||
if (readBytes == -1) {
|
if (readBytes == -1) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -194,6 +194,11 @@ class FileAccessHandler(val context: Context) {
|
||||||
return files[fileId].endOfFile
|
return files[fileId].endOfFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setFileEof(fileId: Int, eof: Boolean) {
|
||||||
|
val file = files[fileId] ?: return
|
||||||
|
file.endOfFile = eof
|
||||||
|
}
|
||||||
|
|
||||||
fun fileClose(fileId: Int) {
|
fun fileClose(fileId: Int) {
|
||||||
if (hasFileId(fileId)) {
|
if (hasFileId(fileId)) {
|
||||||
files[fileId].close()
|
files[fileId].close()
|
||||||
|
|
|
@ -57,8 +57,10 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
SafeFlag plugins_changed;
|
SafeFlag plugins_changed;
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
Thread check_for_changes_thread;
|
Thread check_for_changes_thread;
|
||||||
SafeFlag quit_request;
|
SafeFlag quit_request;
|
||||||
|
#endif
|
||||||
Mutex plugins_lock;
|
Mutex plugins_lock;
|
||||||
Vector<PluginConfigIOS> plugins;
|
Vector<PluginConfigIOS> plugins;
|
||||||
|
|
||||||
|
@ -141,6 +143,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
static void _check_for_changes_poll_thread(void *ud) {
|
static void _check_for_changes_poll_thread(void *ud) {
|
||||||
EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
|
EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
|
||||||
|
|
||||||
|
@ -176,6 +179,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
|
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
|
||||||
|
@ -409,27 +413,27 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with Retina display
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone/iPod Touch with Retina display
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with Retina HD display
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPhone with Retina HD display
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with Retina display
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad with Retina display
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Home screen on iPad Pro
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // App Store
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with Retina display
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), "")); // Spotlight on devices with Retina display
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
|
||||||
|
|
||||||
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
|
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png,*.jpg,*.jpeg"), ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,26 +664,37 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
|
||||||
String locale_files;
|
String locale_files;
|
||||||
Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
|
Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
|
||||||
if (translations.size() > 0) {
|
if (translations.size() > 0) {
|
||||||
|
Set<String> languages;
|
||||||
for (int j = 0; j < translations.size(); j++) {
|
for (int j = 0; j < translations.size(); j++) {
|
||||||
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
||||||
if (tr.is_valid()) {
|
if (tr.is_valid() && tr->get_locale() != "en") {
|
||||||
String lang = tr->get_locale();
|
languages.insert(tr->get_locale());
|
||||||
locale_files += "D0BCFE4518AEBDA2004A" + itos(j).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"<group>\"; };";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int index = 0;
|
||||||
|
for (const Set<String>::Element *E = languages.front(); E; E = E->next()) {
|
||||||
|
const String &lang = E->get();
|
||||||
|
locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + lang + " */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = " + lang + "; path = " + lang + ".lproj/InfoPlist.strings; sourceTree = \"<group>\"; };\n";
|
||||||
|
index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
strnew += lines[i].replace("$pbx_locale_file_reference", locale_files);
|
strnew += lines[i].replace("$pbx_locale_file_reference", locale_files);
|
||||||
} else if (lines[i].find("$pbx_locale_build_reference") != -1) {
|
} else if (lines[i].find("$pbx_locale_build_reference") != -1) {
|
||||||
String locale_files;
|
String locale_files;
|
||||||
Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
|
Vector<String> translations = ProjectSettings::get_singleton()->get("locale/translations");
|
||||||
if (translations.size() > 0) {
|
if (translations.size() > 0) {
|
||||||
|
Set<String> languages;
|
||||||
for (int j = 0; j < translations.size(); j++) {
|
for (int j = 0; j < translations.size(); j++) {
|
||||||
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
||||||
if (tr.is_valid()) {
|
if (tr.is_valid() && tr->get_locale() != "en") {
|
||||||
String lang = tr->get_locale();
|
languages.insert(tr->get_locale());
|
||||||
locale_files += "D0BCFE4518AEBDA2004A" + itos(j).pad_zeros(4) + " /* " + lang + " */,";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int index = 0;
|
||||||
|
for (const Set<String>::Element *E = languages.front(); E; E = E->next()) {
|
||||||
|
locale_files += "D0BCFE4518AEBDA2004A" + itos(index).pad_zeros(4) + " /* " + E->get() + " */,\n";
|
||||||
|
index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
strnew += lines[i].replace("$pbx_locale_build_reference", locale_files);
|
strnew += lines[i].replace("$pbx_locale_build_reference", locale_files);
|
||||||
} else {
|
} else {
|
||||||
|
@ -777,26 +792,27 @@ struct IconInfo {
|
||||||
const char *actual_size_side;
|
const char *actual_size_side;
|
||||||
const char *scale;
|
const char *scale;
|
||||||
const char *unscaled_size;
|
const char *unscaled_size;
|
||||||
|
const bool force_opaque;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const IconInfo icon_infos[] = {
|
static const IconInfo icon_infos[] = {
|
||||||
// Home screen on iPhone
|
// Home screen on iPhone
|
||||||
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60" },
|
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", false },
|
||||||
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40" },
|
{ "icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", false },
|
||||||
{ "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60" },
|
{ "icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
|
||||||
|
|
||||||
// Home screen on iPad
|
// Home screen on iPad
|
||||||
{ "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76" },
|
{ "icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
|
||||||
{ "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76" },
|
{ "icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false },
|
||||||
{ "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5" },
|
{ "icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false },
|
||||||
|
|
||||||
// App Store
|
// App Store
|
||||||
{ "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024" },
|
{ "icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true },
|
||||||
|
|
||||||
// Spotlight
|
// Spotlight
|
||||||
{ "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40" },
|
{ "icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false },
|
||||||
{ "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40" },
|
{ "icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
|
||||||
{ "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40" }
|
{ "icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
|
||||||
};
|
};
|
||||||
|
|
||||||
Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
|
Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
|
||||||
|
@ -816,14 +832,20 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
|
||||||
Ref<Image> img = memnew(Image);
|
Ref<Image> img = memnew(Image);
|
||||||
Error err = ImageLoader::load_image(icon_path, img);
|
Error err = ImageLoader::load_image(icon_path, img);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
|
memdelete(da);
|
||||||
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path));
|
||||||
|
return ERR_UNCONFIGURED;
|
||||||
|
}
|
||||||
|
if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) {
|
||||||
|
memdelete(da);
|
||||||
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key));
|
||||||
return ERR_UNCONFIGURED;
|
return ERR_UNCONFIGURED;
|
||||||
}
|
}
|
||||||
img->resize(side_size, side_size);
|
img->resize(side_size, side_size);
|
||||||
err = img->save_png(p_iconset_dir + info.export_name);
|
err = img->save_png(p_iconset_dir + info.export_name);
|
||||||
if (err) {
|
if (err) {
|
||||||
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
|
memdelete(da);
|
||||||
ERR_PRINT(err_str.utf8().get_data());
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -831,11 +853,17 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
|
||||||
Ref<Image> img = memnew(Image);
|
Ref<Image> img = memnew(Image);
|
||||||
Error err = ImageLoader::load_image(icon_path, img);
|
Error err = ImageLoader::load_image(icon_path, img);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'.");
|
memdelete(da);
|
||||||
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Invalid icon (%s): '%s'.", info.preset_key, icon_path));
|
||||||
|
return ERR_UNCONFIGURED;
|
||||||
|
}
|
||||||
|
if (info.force_opaque && img->detect_alpha() != Image::ALPHA_NONE) {
|
||||||
|
memdelete(da);
|
||||||
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Icon (%s) must be opaque.", info.preset_key));
|
||||||
return ERR_UNCONFIGURED;
|
return ERR_UNCONFIGURED;
|
||||||
}
|
}
|
||||||
if (img->get_width() != side_size || img->get_height() != side_size) {
|
if (img->get_width() != side_size || img->get_height() != side_size) {
|
||||||
WARN_PRINT("Icon (" + String(info.preset_key) + "): '" + icon_path + "' has incorrect size (" + String::num_int64(img->get_width()) + "x" + String::num_int64(img->get_height()) + ") and was automatically resized to " + String::num_int64(side_size) + "x" + String::num_int64(side_size) + ".");
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Export Icons"), vformat("Icon (%s): '%s' has incorrect size %s and was automatically resized to %s.", info.preset_key, icon_path, img->get_size(), Vector2(side_size, side_size)));
|
||||||
img->resize(side_size, side_size);
|
img->resize(side_size, side_size);
|
||||||
err = img->save_png(p_iconset_dir + info.export_name);
|
err = img->save_png(p_iconset_dir + info.export_name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -844,8 +872,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
memdelete(da);
|
memdelete(da);
|
||||||
String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'.");
|
add_message(EXPORT_MESSAGE_ERROR, TTR("Export Icons"), vformat("Failed to export icon (%s): '%s'.", info.preset_key, icon_path));
|
||||||
ERR_PRINT(err_str.utf8().get_data());
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1905,19 +1932,23 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
|
||||||
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
|
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < translations.size(); i++) {
|
Set<String> languages;
|
||||||
Ref<Translation> tr = ResourceLoader::load(translations[i]);
|
for (int j = 0; j < translations.size(); j++) {
|
||||||
if (tr.is_valid()) {
|
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
||||||
String fname = dest_dir + binary_name + "/" + tr->get_locale() + ".lproj";
|
if (tr.is_valid() && tr->get_locale() != "en") {
|
||||||
|
languages.insert(tr->get_locale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Set<String>::Element *E = languages.front(); E; E = E->next()) {
|
||||||
|
String fname = dest_dir + binary_name + "/" + E->get() + ".lproj";
|
||||||
tmp_app_path->make_dir_recursive(fname);
|
tmp_app_path->make_dir_recursive(fname);
|
||||||
FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
|
FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
|
||||||
String prop = "application/config/name_" + tr->get_locale();
|
String prop = "application/config/name_" + E->get();
|
||||||
if (ProjectSettings::get_singleton()->has_setting(prop)) {
|
if (ProjectSettings::get_singleton()->has_setting(prop)) {
|
||||||
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get(prop).operator String() + "\";");
|
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get(prop).operator String() + "\";");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Copy project static libs to the project
|
// Copy project static libs to the project
|
||||||
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
|
||||||
|
@ -2124,12 +2155,16 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
|
||||||
|
|
||||||
plugins_changed.set();
|
plugins_changed.set();
|
||||||
|
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
|
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
|
||||||
|
#ifndef ANDROID_ENABLED
|
||||||
quit_request.set();
|
quit_request.set();
|
||||||
check_for_changes_thread.wait_to_finish();
|
check_for_changes_thread.wait_to_finish();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_iphone_exporter() {
|
void register_iphone_exporter() {
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
#define GLES2_INCLUDE_H <ES2/gl.h>
|
#define GLES2_INCLUDE_H <ES2/gl.h>
|
||||||
#define GLES3_INCLUDE_H <ES3/gl.h>
|
#define GLES3_INCLUDE_H <ES3/gl.h>
|
||||||
|
|
||||||
#define PLATFORM_REFCOUNT
|
|
||||||
|
|
||||||
#define PTHREAD_RENAME_SELF
|
#define PTHREAD_RENAME_SELF
|
||||||
|
|
||||||
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
|
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
|
||||||
|
|
|
@ -896,19 +896,23 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
|
||||||
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
|
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < translations.size(); i++) {
|
Set<String> languages;
|
||||||
Ref<Translation> tr = ResourceLoader::load(translations[i]);
|
for (int j = 0; j < translations.size(); j++) {
|
||||||
if (tr.is_valid()) {
|
Ref<Translation> tr = ResourceLoader::load(translations[j]);
|
||||||
String fname = tmp_app_path_name + "/Contents/Resources/" + tr->get_locale() + ".lproj";
|
if (tr.is_valid() && tr->get_locale() != "en") {
|
||||||
|
languages.insert(tr->get_locale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Set<String>::Element *E = languages.front(); E; E = E->next()) {
|
||||||
|
String fname = tmp_app_path_name + "/Contents/Resources/" + E->get() + ".lproj";
|
||||||
tmp_app_dir->make_dir_recursive(fname);
|
tmp_app_dir->make_dir_recursive(fname);
|
||||||
FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
|
FileAccessRef f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE);
|
||||||
String prop = "application/config/name_" + tr->get_locale();
|
String prop = "application/config/name_" + E->get();
|
||||||
if (ProjectSettings::get_singleton()->has_setting(prop)) {
|
if (ProjectSettings::get_singleton()->has_setting(prop)) {
|
||||||
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get(prop).operator String() + "\";");
|
f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get(prop).operator String() + "\";");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Now process our template.
|
// Now process our template.
|
||||||
bool found_binary = false;
|
bool found_binary = false;
|
||||||
|
|
|
@ -405,7 +405,9 @@ def configure(env):
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
|
|
||||||
linker_version_str = subprocess.check_output([env.subst(env["LINK"]), "-Wl,--version"]).decode("utf-8")
|
linker_version_str = subprocess.check_output(
|
||||||
|
[env.subst(env["LINK"]), "-Wl,--version"] + env.subst(env["LINKFLAGS"])
|
||||||
|
).decode("utf-8")
|
||||||
gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE)
|
gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE)
|
||||||
if not gnu_ld_version:
|
if not gnu_ld_version:
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -355,13 +355,13 @@ void Line2D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_points", "points"), &Line2D::set_points);
|
ClassDB::bind_method(D_METHOD("set_points", "points"), &Line2D::set_points);
|
||||||
ClassDB::bind_method(D_METHOD("get_points"), &Line2D::get_points);
|
ClassDB::bind_method(D_METHOD("get_points"), &Line2D::get_points);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_point_position", "i", "position"), &Line2D::set_point_position);
|
ClassDB::bind_method(D_METHOD("set_point_position", "index", "position"), &Line2D::set_point_position);
|
||||||
ClassDB::bind_method(D_METHOD("get_point_position", "i"), &Line2D::get_point_position);
|
ClassDB::bind_method(D_METHOD("get_point_position", "index"), &Line2D::get_point_position);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count);
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("add_point", "position", "at_position"), &Line2D::add_point, DEFVAL(-1));
|
ClassDB::bind_method(D_METHOD("add_point", "position", "index"), &Line2D::add_point, DEFVAL(-1));
|
||||||
ClassDB::bind_method(D_METHOD("remove_point", "i"), &Line2D::remove_point);
|
ClassDB::bind_method(D_METHOD("remove_point", "index"), &Line2D::remove_point);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points);
|
ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points);
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,12 @@ void NavigationObstacle2D::set_navigation(Navigation2D *p_nav) {
|
||||||
}
|
}
|
||||||
|
|
||||||
navigation = p_nav;
|
navigation = p_nav;
|
||||||
Navigation2DServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
|
||||||
|
if (navigation != nullptr) {
|
||||||
|
Navigation2DServer::get_singleton()->agent_set_map(agent, navigation->get_rid());
|
||||||
|
} else if (parent_node2d && parent_node2d->is_inside_tree()) {
|
||||||
|
Navigation2DServer::get_singleton()->agent_set_map(agent, parent_node2d->get_world_2d()->get_navigation_map());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
|
void NavigationObstacle2D::set_navigation_node(Node *p_nav) {
|
||||||
|
|
|
@ -419,5 +419,7 @@ Particles2D::Particles2D() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Particles2D::~Particles2D() {
|
Particles2D::~Particles2D() {
|
||||||
|
if (particles.is_valid()) {
|
||||||
VS::get_singleton()->free(particles);
|
VS::get_singleton()->free(particles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,9 @@ GIProbeData::GIProbeData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
GIProbeData::~GIProbeData() {
|
GIProbeData::~GIProbeData() {
|
||||||
|
if (probe.is_valid()) {
|
||||||
VS::get_singleton()->free(probe);
|
VS::get_singleton()->free(probe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
@ -527,5 +529,7 @@ GIProbe::GIProbe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
GIProbe::~GIProbe() {
|
GIProbe::~GIProbe() {
|
||||||
|
if (gi_probe.is_valid()) {
|
||||||
VS::get_singleton()->free(gi_probe);
|
VS::get_singleton()->free(gi_probe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,12 @@ void NavigationObstacle::set_navigation(Navigation *p_nav) {
|
||||||
}
|
}
|
||||||
|
|
||||||
navigation = p_nav;
|
navigation = p_nav;
|
||||||
NavigationServer::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid());
|
|
||||||
|
if (navigation != nullptr) {
|
||||||
|
NavigationServer::get_singleton()->agent_set_map(agent, navigation->get_rid());
|
||||||
|
} else if (parent_spatial && parent_spatial->is_inside_tree()) {
|
||||||
|
NavigationServer::get_singleton()->agent_set_map(agent, parent_spatial->get_world()->get_navigation_map());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationObstacle::set_navigation_node(Node *p_nav) {
|
void NavigationObstacle::set_navigation_node(Node *p_nav) {
|
||||||
|
|
|
@ -416,5 +416,7 @@ Particles::Particles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Particles::~Particles() {
|
Particles::~Particles() {
|
||||||
|
if (particles.is_valid()) {
|
||||||
VS::get_singleton()->free(particles);
|
VS::get_singleton()->free(particles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -962,8 +962,13 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_inf
|
||||||
|
|
||||||
bool collided = move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only);
|
bool collided = move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only);
|
||||||
|
|
||||||
|
// Ugly hack as a hot fix, 65b3200 fix an issue but cause a problem with Bullet that broke games using Bullet.
|
||||||
|
// The bug is something internal to Bullet, seems to be related to the Bullet’s margin. As not proper fix was found yet,
|
||||||
|
// this temporary solution solves the issue for games using Bullet.
|
||||||
|
bool is_bullet = PhysicsServerManager::current_server_id != 0;
|
||||||
|
|
||||||
// Don't report collision when the whole motion is done.
|
// Don't report collision when the whole motion is done.
|
||||||
if (collided && col.collision_safe_fraction < 1) {
|
if (collided && (col.collision_safe_fraction < 1 || is_bullet)) {
|
||||||
// Create a new instance when the cached reference is invalid or still in use in script.
|
// Create a new instance when the cached reference is invalid or still in use in script.
|
||||||
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
|
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
|
||||||
motion_cache.instance();
|
motion_cache.instance();
|
||||||
|
|
|
@ -1052,13 +1052,15 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
|
||||||
bool AnimationPlayer::has_animation(const StringName &p_name) const {
|
bool AnimationPlayer::has_animation(const StringName &p_name) const {
|
||||||
return animation_set.has(p_name);
|
return animation_set.has(p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const {
|
Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const {
|
||||||
ERR_FAIL_COND_V(!animation_set.has(p_name), Ref<Animation>());
|
ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name));
|
||||||
|
|
||||||
const AnimationData &data = animation_set[p_name];
|
const AnimationData &data = animation_set[p_name];
|
||||||
|
|
||||||
return data.animation;
|
return data.animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
|
void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
|
||||||
List<String> anims;
|
List<String> anims;
|
||||||
|
|
||||||
|
|
|
@ -357,6 +357,9 @@ bool AnimationNode::is_path_filtered(const NodePath &p_path) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationNode::has_filter() const {
|
bool AnimationNode::has_filter() const {
|
||||||
|
if (get_script_instance()) {
|
||||||
|
return get_script_instance()->call("has_filter");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,7 +430,7 @@ void AnimationNode::_bind_methods() {
|
||||||
}
|
}
|
||||||
BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek")));
|
BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek")));
|
||||||
BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption"));
|
BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption"));
|
||||||
BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter"));
|
BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_filter"));
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("removed_from_graph"));
|
ADD_SIGNAL(MethodInfo("removed_from_graph"));
|
||||||
|
|
||||||
|
|
|
@ -754,11 +754,7 @@ void ItemList::_notification(int p_what) {
|
||||||
scroll_bar->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -bg->get_margin(MARGIN_BOTTOM));
|
scroll_bar->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -bg->get_margin(MARGIN_BOTTOM));
|
||||||
|
|
||||||
Size2 size = get_size();
|
Size2 size = get_size();
|
||||||
|
|
||||||
int width = size.width - bg->get_minimum_size().width;
|
int width = size.width - bg->get_minimum_size().width;
|
||||||
if (scroll_bar->is_visible()) {
|
|
||||||
width -= mw;
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_style_box(bg, Rect2(Point2(), size));
|
draw_style_box(bg, Rect2(Point2(), size));
|
||||||
|
|
||||||
|
@ -917,6 +913,10 @@ void ItemList::_notification(int p_what) {
|
||||||
shape_changed = false;
|
shape_changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scroll_bar->is_visible()) {
|
||||||
|
width -= mw;
|
||||||
|
}
|
||||||
|
|
||||||
//ensure_selected_visible needs to be checked before we draw the list.
|
//ensure_selected_visible needs to be checked before we draw the list.
|
||||||
if (ensure_selected_visible && current >= 0 && current < items.size()) {
|
if (ensure_selected_visible && current >= 0 && current < items.size()) {
|
||||||
Rect2 r = items[current].rect_cache;
|
Rect2 r = items[current].rect_cache;
|
||||||
|
|
|
@ -223,6 +223,7 @@ void PopupMenu::_scroll(float p_factor, const Point2 &p_over) {
|
||||||
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
|
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
|
||||||
ERR_FAIL_COND(p_event.is_null());
|
ERR_FAIL_COND(p_event.is_null());
|
||||||
|
|
||||||
|
if (!items.empty()) {
|
||||||
if (p_event->is_action("ui_down") && p_event->is_pressed()) {
|
if (p_event->is_action("ui_down") && p_event->is_pressed()) {
|
||||||
int search_from = mouse_over + 1;
|
int search_from = mouse_over + 1;
|
||||||
if (search_from >= items.size()) {
|
if (search_from >= items.size()) {
|
||||||
|
@ -312,6 +313,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
|
||||||
accept_event();
|
accept_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ref<InputEventMouseButton> b = p_event;
|
Ref<InputEventMouseButton> b = p_event;
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
|
||||||
|
|
||||||
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
|
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
|
||||||
if (rb_hover != -1) {
|
if (rb_hover != -1) {
|
||||||
// Right mouse button pressed.
|
// Right tab button pressed.
|
||||||
emit_signal("right_button_pressed", rb_hover);
|
emit_signal("right_button_pressed", rb_hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -955,6 +955,8 @@ void Tabs::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
|
ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
|
||||||
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
|
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
|
||||||
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon);
|
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &Tabs::set_tab_right_button);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &Tabs::get_tab_right_button);
|
||||||
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled);
|
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled);
|
||||||
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled);
|
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled);
|
||||||
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab);
|
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab);
|
||||||
|
|
|
@ -6341,7 +6341,8 @@ void TextEdit::fold_line(int p_line) {
|
||||||
int last_line = start_indent;
|
int last_line = start_indent;
|
||||||
for (int i = p_line + 1; i < text.size(); i++) {
|
for (int i = p_line + 1; i < text.size(); i++) {
|
||||||
if (text[i].strip_edges().size() != 0) {
|
if (text[i].strip_edges().size() != 0) {
|
||||||
if (is_line_comment(i)) {
|
if (is_line_comment(i) && get_indent_level(i) <= start_indent) {
|
||||||
|
// Checked indent to make sure indented comments that finish a code block are folded.
|
||||||
continue;
|
continue;
|
||||||
} else if (get_indent_level(i) > start_indent) {
|
} else if (get_indent_level(i) > start_indent) {
|
||||||
last_line = i;
|
last_line = i;
|
||||||
|
|
|
@ -1177,7 +1177,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
|
||||||
}
|
}
|
||||||
p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
|
p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
|
||||||
|
|
||||||
if (p_item->cells[i].selected) {
|
if (select_mode != SELECT_ROW && p_item->cells[i].selected) {
|
||||||
if (has_focus()) {
|
if (has_focus()) {
|
||||||
cache.selected_focus->draw(ci, r);
|
cache.selected_focus->draw(ci, r);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3031,7 +3031,7 @@ String Viewport::get_configuration_warning() const {
|
||||||
warning += TTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything.");
|
warning += TTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdr && (usage == USAGE_2D || usage == USAGE_2D_NO_SAMPLING)) {
|
if (!VisualServer::get_singleton()->is_low_end() && hdr && (usage == USAGE_2D || usage == USAGE_2D_NO_SAMPLING)) {
|
||||||
if (warning != String()) {
|
if (warning != String()) {
|
||||||
warning += "\n\n";
|
warning += "\n\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -962,7 +962,7 @@ PoolVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) const
|
||||||
|
|
||||||
void Curve2D::_bind_methods() {
|
void Curve2D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count);
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count);
|
||||||
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1));
|
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "index"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1));
|
||||||
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position);
|
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position);
|
||||||
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position);
|
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position);
|
||||||
ClassDB::bind_method(D_METHOD("set_point_in", "idx", "position"), &Curve2D::set_point_in);
|
ClassDB::bind_method(D_METHOD("set_point_in", "idx", "position"), &Curve2D::set_point_in);
|
||||||
|
@ -1626,7 +1626,7 @@ PoolVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) const
|
||||||
|
|
||||||
void Curve3D::_bind_methods() {
|
void Curve3D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count);
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count);
|
||||||
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1));
|
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "index"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1));
|
||||||
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position);
|
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position);
|
||||||
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position);
|
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position);
|
||||||
ClassDB::bind_method(D_METHOD("set_point_tilt", "idx", "tilt"), &Curve3D::set_point_tilt);
|
ClassDB::bind_method(D_METHOD("set_point_tilt", "idx", "tilt"), &Curve3D::set_point_tilt);
|
||||||
|
|
|
@ -523,7 +523,7 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT
|
||||||
cpos.y -= font->get_ascent();
|
cpos.y -= font->get_ascent();
|
||||||
cpos.y += ch->v_align;
|
cpos.y += ch->v_align;
|
||||||
Color modulate = p_modulate;
|
Color modulate = p_modulate;
|
||||||
if (FT_HAS_COLOR(font->face)) {
|
if (font->textures[ch->texture_idx].texture->get_format() == Image::FORMAT_RGBA8) {
|
||||||
modulate.r = modulate.g = modulate.b = 1.0;
|
modulate.r = modulate.g = modulate.b = 1.0;
|
||||||
}
|
}
|
||||||
RID texture = font->textures[ch->texture_idx].texture->get_rid();
|
RID texture = font->textures[ch->texture_idx].texture->get_rid();
|
||||||
|
|
|
@ -34,9 +34,8 @@
|
||||||
#include "core/project_settings.h"
|
#include "core/project_settings.h"
|
||||||
|
|
||||||
Error AudioDriverDummy::init() {
|
Error AudioDriverDummy::init() {
|
||||||
active = false;
|
active.clear();
|
||||||
thread_exited = false;
|
exit_thread.clear();
|
||||||
exit_thread = false;
|
|
||||||
samples_in = nullptr;
|
samples_in = nullptr;
|
||||||
|
|
||||||
mix_rate = GLOBAL_GET("audio/mix_rate");
|
mix_rate = GLOBAL_GET("audio/mix_rate");
|
||||||
|
@ -58,23 +57,23 @@ void AudioDriverDummy::thread_func(void *p_udata) {
|
||||||
|
|
||||||
uint64_t usdelay = (ad->buffer_frames / float(ad->mix_rate)) * 1000000;
|
uint64_t usdelay = (ad->buffer_frames / float(ad->mix_rate)) * 1000000;
|
||||||
|
|
||||||
while (!ad->exit_thread) {
|
while (!ad->exit_thread.is_set()) {
|
||||||
if (ad->active) {
|
if (ad->active.is_set()) {
|
||||||
ad->lock();
|
ad->lock();
|
||||||
|
ad->start_counting_ticks();
|
||||||
|
|
||||||
ad->audio_server_process(ad->buffer_frames, ad->samples_in);
|
ad->audio_server_process(ad->buffer_frames, ad->samples_in);
|
||||||
|
|
||||||
|
ad->stop_counting_ticks();
|
||||||
ad->unlock();
|
ad->unlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
OS::get_singleton()->delay_usec(usdelay);
|
OS::get_singleton()->delay_usec(usdelay);
|
||||||
};
|
};
|
||||||
|
|
||||||
ad->thread_exited = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudioDriverDummy::start() {
|
void AudioDriverDummy::start() {
|
||||||
active = true;
|
active.set();
|
||||||
};
|
};
|
||||||
|
|
||||||
int AudioDriverDummy::get_mix_rate() const {
|
int AudioDriverDummy::get_mix_rate() const {
|
||||||
|
@ -94,7 +93,7 @@ void AudioDriverDummy::unlock() {
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudioDriverDummy::finish() {
|
void AudioDriverDummy::finish() {
|
||||||
exit_thread = true;
|
exit_thread.set();
|
||||||
thread.wait_to_finish();
|
thread.wait_to_finish();
|
||||||
|
|
||||||
if (samples_in) {
|
if (samples_in) {
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
#include "core/safe_refcount.h"
|
||||||
|
|
||||||
class AudioDriverDummy : public AudioDriver {
|
class AudioDriverDummy : public AudioDriver {
|
||||||
Thread thread;
|
Thread thread;
|
||||||
|
@ -50,9 +51,8 @@ class AudioDriverDummy : public AudioDriver {
|
||||||
|
|
||||||
int channels;
|
int channels;
|
||||||
|
|
||||||
bool active;
|
SafeFlag active;
|
||||||
bool thread_exited;
|
SafeFlag exit_thread;
|
||||||
mutable bool exit_thread;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char *get_name() const {
|
const char *get_name() const {
|
||||||
|
|
|
@ -175,7 +175,7 @@ void Navigation2DServer::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create);
|
ClassDB::bind_method(D_METHOD("map_create"), &Navigation2DServer::map_create);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active);
|
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &Navigation2DServer::map_set_active);
|
||||||
ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &Navigation2DServer::map_is_active);
|
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &Navigation2DServer::map_is_active);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size);
|
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &Navigation2DServer::map_set_cell_size);
|
||||||
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size);
|
ClassDB::bind_method(D_METHOD("map_get_cell_size", "map"), &Navigation2DServer::map_get_cell_size);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_cell_height", "map", "cell_height"), &Navigation2DServer::map_set_cell_size);
|
ClassDB::bind_method(D_METHOD("map_set_cell_height", "map", "cell_height"), &Navigation2DServer::map_set_cell_size);
|
||||||
|
|
|
@ -37,7 +37,7 @@ void NavigationServer::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer::map_create);
|
ClassDB::bind_method(D_METHOD("map_create"), &NavigationServer::map_create);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer::map_set_active);
|
ClassDB::bind_method(D_METHOD("map_set_active", "map", "active"), &NavigationServer::map_set_active);
|
||||||
ClassDB::bind_method(D_METHOD("map_is_active", "nap"), &NavigationServer::map_is_active);
|
ClassDB::bind_method(D_METHOD("map_is_active", "map"), &NavigationServer::map_is_active);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_up", "map", "up"), &NavigationServer::map_set_up);
|
ClassDB::bind_method(D_METHOD("map_set_up", "map", "up"), &NavigationServer::map_set_up);
|
||||||
ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer::map_get_up);
|
ClassDB::bind_method(D_METHOD("map_get_up", "map"), &NavigationServer::map_get_up);
|
||||||
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer::map_set_cell_size);
|
ClassDB::bind_method(D_METHOD("map_set_cell_size", "map", "cell_size"), &NavigationServer::map_set_cell_size);
|
||||||
|
|
|
@ -810,6 +810,7 @@ PhysicsServer::~PhysicsServer() {
|
||||||
|
|
||||||
Vector<PhysicsServerManager::ClassInfo> PhysicsServerManager::physics_servers;
|
Vector<PhysicsServerManager::ClassInfo> PhysicsServerManager::physics_servers;
|
||||||
int PhysicsServerManager::default_server_id = -1;
|
int PhysicsServerManager::default_server_id = -1;
|
||||||
|
int PhysicsServerManager::current_server_id = -1;
|
||||||
int PhysicsServerManager::default_server_priority = -1;
|
int PhysicsServerManager::default_server_priority = -1;
|
||||||
const String PhysicsServerManager::setting_property_name(PNAME("physics/3d/physics_engine"));
|
const String PhysicsServerManager::setting_property_name(PNAME("physics/3d/physics_engine"));
|
||||||
|
|
||||||
|
@ -857,6 +858,7 @@ String PhysicsServerManager::get_server_name(int p_id) {
|
||||||
|
|
||||||
PhysicsServer *PhysicsServerManager::new_default_server() {
|
PhysicsServer *PhysicsServerManager::new_default_server() {
|
||||||
ERR_FAIL_COND_V(default_server_id == -1, nullptr);
|
ERR_FAIL_COND_V(default_server_id == -1, nullptr);
|
||||||
|
current_server_id = default_server_id;
|
||||||
return physics_servers[default_server_id].create_callback();
|
return physics_servers[default_server_id].create_callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,6 +867,7 @@ PhysicsServer *PhysicsServerManager::new_server(const String &p_name) {
|
||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
|
current_server_id = id;
|
||||||
return physics_servers[id].create_callback();
|
return physics_servers[id].create_callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -807,6 +807,7 @@ class PhysicsServerManager {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const String setting_property_name;
|
static const String setting_property_name;
|
||||||
|
static int current_server_id;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void on_servers_changed();
|
static void on_servers_changed();
|
||||||
|
|
2
thirdparty/README.md
vendored
2
thirdparty/README.md
vendored
|
@ -399,7 +399,7 @@ Collection of single-file libraries used in Godot components.
|
||||||
## nanosvg
|
## nanosvg
|
||||||
|
|
||||||
- Upstream: https://github.com/memononen/nanosvg
|
- Upstream: https://github.com/memononen/nanosvg
|
||||||
- Version: git (4c8f0139b62c6e7faa3b67ce1fbe6e63590ed148, 2022)
|
- Version: git (bd16c4e6b2842e1f0286dc374d21f85c659862e5, 2022)
|
||||||
- License: zlib
|
- License: zlib
|
||||||
|
|
||||||
Files extracted from the upstream source:
|
Files extracted from the upstream source:
|
||||||
|
|
67
thirdparty/nanosvg/nanosvg.h
vendored
67
thirdparty/nanosvg/nanosvg.h
vendored
|
@ -181,8 +181,6 @@ void nsvgDelete(NSVGimage* image);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NANOSVG_H
|
|
||||||
|
|
||||||
#ifdef NANOSVG_IMPLEMENTATION
|
#ifdef NANOSVG_IMPLEMENTATION
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -1224,15 +1222,58 @@ static unsigned int nsvg__parseColorHex(const char* str)
|
||||||
return NSVG_RGB(128, 128, 128);
|
return NSVG_RGB(128, 128, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters).
|
||||||
|
// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors
|
||||||
|
// for backwards compatibility. Note: other image viewers return black instead.
|
||||||
|
|
||||||
static unsigned int nsvg__parseColorRGB(const char* str)
|
static unsigned int nsvg__parseColorRGB(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int r=0, g=0, b=0;
|
int i;
|
||||||
float rf=0, gf=0, bf=0;
|
unsigned int rgbi[3];
|
||||||
if (sscanf(str, "rgb(%u, %u, %u)", &r, &g, &b) == 3) // decimal integers
|
float rgbf[3];
|
||||||
return NSVG_RGB(r, g, b);
|
// try decimal integers first
|
||||||
if (sscanf(str, "rgb(%f%%, %f%%, %f%%)", &rf, &gf, &bf) == 3) // decimal integer percentage
|
if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) {
|
||||||
return NSVG_RGB(roundf(rf*2.55f), roundf(gf*2.55f), roundf(bf*2.55f)); // (255 / 100.0f)
|
// integers failed, try percent values (float, locale independent)
|
||||||
return NSVG_RGB(128, 128, 128);
|
const char delimiter[3] = {',', ',', ')'};
|
||||||
|
str += 4; // skip "rgb("
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces
|
||||||
|
if (*str == '+') str++; // skip '+' (don't allow '-')
|
||||||
|
if (!*str) break;
|
||||||
|
rgbf[i] = nsvg__atof(str);
|
||||||
|
|
||||||
|
// Note 1: it would be great if nsvg__atof() returned how many
|
||||||
|
// bytes it consumed but it doesn't. We need to skip the number,
|
||||||
|
// the '%' character, spaces, and the delimiter ',' or ')'.
|
||||||
|
|
||||||
|
// Note 2: The following code does not allow values like "33.%",
|
||||||
|
// i.e. a decimal point w/o fractional part, but this is consistent
|
||||||
|
// with other image viewers, e.g. firefox, chrome, eog, gimp.
|
||||||
|
|
||||||
|
while (*str && nsvg__isdigit(*str)) str++; // skip integer part
|
||||||
|
if (*str == '.') {
|
||||||
|
str++;
|
||||||
|
if (!nsvg__isdigit(*str)) break; // error: no digit after '.'
|
||||||
|
while (*str && nsvg__isdigit(*str)) str++; // skip fractional part
|
||||||
|
}
|
||||||
|
if (*str == '%') str++; else break;
|
||||||
|
while (nsvg__isspace(*str)) str++;
|
||||||
|
if (*str == delimiter[i]) str++;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
if (i == 3) {
|
||||||
|
rgbi[0] = roundf(rgbf[0] * 2.55f);
|
||||||
|
rgbi[1] = roundf(rgbf[1] * 2.55f);
|
||||||
|
rgbi[2] = roundf(rgbf[2] * 2.55f);
|
||||||
|
} else {
|
||||||
|
rgbi[0] = rgbi[1] = rgbi[2] = 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clip values as the CSS spec requires
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (rgbi[i] > 255) rgbi[i] = 255;
|
||||||
|
}
|
||||||
|
return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct NSVGNamedColor {
|
typedef struct NSVGNamedColor {
|
||||||
|
@ -1640,9 +1681,9 @@ static void nsvg__parseUrl(char* id, const char* str)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
str += 4; // "url(";
|
str += 4; // "url(";
|
||||||
if (*str == '#')
|
if (*str && *str == '#')
|
||||||
str++;
|
str++;
|
||||||
while (i < 63 && *str != ')') {
|
while (i < 63 && *str && *str != ')') {
|
||||||
id[i] = *str++;
|
id[i] = *str++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -3007,4 +3048,6 @@ void nsvgDelete(NSVGimage* image)
|
||||||
free(image);
|
free(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // NANOSVG_IMPLEMENTATION
|
||||||
|
|
||||||
|
#endif // NANOSVG_H
|
||||||
|
|
8
thirdparty/nanosvg/nanosvgrast.h
vendored
8
thirdparty/nanosvg/nanosvgrast.h
vendored
|
@ -25,6 +25,8 @@
|
||||||
#ifndef NANOSVGRAST_H
|
#ifndef NANOSVGRAST_H
|
||||||
#define NANOSVGRAST_H
|
#define NANOSVGRAST_H
|
||||||
|
|
||||||
|
#include "nanosvg.h"
|
||||||
|
|
||||||
#ifndef NANOSVGRAST_CPLUSPLUS
|
#ifndef NANOSVGRAST_CPLUSPLUS
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -72,8 +74,6 @@ void nsvgDeleteRasterizer(NSVGrasterizer*);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // NANOSVGRAST_H
|
|
||||||
|
|
||||||
#ifdef NANOSVGRAST_IMPLEMENTATION
|
#ifdef NANOSVGRAST_IMPLEMENTATION
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -1453,4 +1453,6 @@ void nsvgRasterize(NSVGrasterizer* r,
|
||||||
r->stride = 0;
|
r->stride = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // NANOSVGRAST_IMPLEMENTATION
|
||||||
|
|
||||||
|
#endif // NANOSVGRAST_H
|
||||||
|
|
Loading…
Reference in a new issue