Merge pull request #52638 from akien-mga/3.x-cherrypicks

This commit is contained in:
Rémi Verschelde 2021-09-14 15:56:16 +02:00 committed by GitHub
commit 47d56ace79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 296 additions and 70 deletions

View file

@ -184,7 +184,7 @@ Error ConfigFile::_internal_save(FileAccess *file) {
for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) { for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) {
String vstr; String vstr;
VariantWriter::write_to_string(F.get(), vstr); VariantWriter::write_to_string(F.get(), vstr);
file->store_string(F.key() + "=" + vstr + "\n"); file->store_string(F.key().property_name_encode() + "=" + vstr + "\n");
} }
} }

View file

@ -52,6 +52,8 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
conn_port = p_port; conn_port = p_port;
conn_host = p_host; conn_host = p_host;
ip_candidates.clear();
ssl = p_ssl; ssl = p_ssl;
ssl_verify_host = p_verify_host; ssl_verify_host = p_verify_host;
@ -306,6 +308,7 @@ void HTTPClient::close() {
resolving = IP::RESOLVER_INVALID_ID; resolving = IP::RESOLVER_INVALID_ID;
} }
ip_candidates.clear();
response_headers.clear(); response_headers.clear();
response_str.clear(); response_str.clear();
body_size = -1; body_size = -1;
@ -328,10 +331,17 @@ Error HTTPClient::poll() {
return OK; // Still resolving return OK; // Still resolving
case IP::RESOLVER_STATUS_DONE: { case IP::RESOLVER_STATUS_DONE: {
IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving); ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
Error err = tcp_connection->connect_to_host(host, conn_port);
IP::get_singleton()->erase_resolve_item(resolving); IP::get_singleton()->erase_resolve_item(resolving);
resolving = IP::RESOLVER_INVALID_ID; resolving = IP::RESOLVER_INVALID_ID;
Error err = ERR_BUG; // Should be at least one entry.
while (ip_candidates.size() > 0) {
err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
if (err == OK) {
break;
}
}
if (err) { if (err) {
status = STATUS_CANT_CONNECT; status = STATUS_CANT_CONNECT;
return err; return err;
@ -385,6 +395,7 @@ Error HTTPClient::poll() {
if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
// Handshake has been successful // Handshake has been successful
handshaking = false; handshaking = false;
ip_candidates.clear();
status = STATUS_CONNECTED; status = STATUS_CONNECTED;
return OK; return OK;
} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
@ -395,15 +406,24 @@ Error HTTPClient::poll() {
} }
// ... we will need to poll more for handshake to finish // ... we will need to poll more for handshake to finish
} else { } else {
ip_candidates.clear();
status = STATUS_CONNECTED; status = STATUS_CONNECTED;
} }
return OK; return OK;
} break; } break;
case StreamPeerTCP::STATUS_ERROR: case StreamPeerTCP::STATUS_ERROR:
case StreamPeerTCP::STATUS_NONE: { case StreamPeerTCP::STATUS_NONE: {
Error err = ERR_CANT_CONNECT;
while (ip_candidates.size() > 0) {
tcp_connection->disconnect_from_host();
err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port);
if (err == OK) {
return OK;
}
}
close(); close();
status = STATUS_CANT_CONNECT; status = STATUS_CANT_CONNECT;
return ERR_CANT_CONNECT; return err;
} break; } break;
} }
} break; } break;

View file

@ -159,6 +159,7 @@ private:
#ifndef JAVASCRIPT_ENABLED #ifndef JAVASCRIPT_ENABLED
Status status; Status status;
IP::ResolverID resolving; IP::ResolverID resolving;
Array ip_candidates;
int conn_port; int conn_port;
String conn_host; String conn_host;
bool ssl; bool ssl;

View file

@ -169,7 +169,7 @@ public:
push_back(p_val); push_back(p_val);
} else { } else {
resize(count + 1); resize(count + 1);
for (U i = count; i > p_pos; i--) { for (U i = count - 1; i > p_pos; i--) {
data[i] = data[i - 1]; data[i] = data[i - 1];
} }
data[p_pos] = p_val; data[p_pos] = p_val;

View file

@ -285,7 +285,7 @@
<theme_item name="more" data_type="icon" type="Texture"> <theme_item name="more" data_type="icon" type="Texture">
The icon for the zoom in button. The icon for the zoom in button.
</theme_item> </theme_item>
<theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="48"> <theme_item name="port_grab_distance_horizontal" data_type="constant" type="int" default="24">
The horizontal range within which a port can be grabbed (on both sides). The horizontal range within which a port can be grabbed (on both sides).
</theme_item> </theme_item>
<theme_item name="port_grab_distance_vertical" data_type="constant" type="int" default="6"> <theme_item name="port_grab_distance_vertical" data_type="constant" type="int" default="6">

View file

@ -163,6 +163,7 @@
<argument index="0" name="renormalize" type="bool" default="false" /> <argument index="0" name="renormalize" type="bool" default="false" />
<description> <description>
Generates mipmaps for the image. Mipmaps are precalculated lower-resolution copies of the image that are automatically used if the image needs to be scaled down when rendered. They help improve image quality and performance when rendering. This method returns an error if the image is compressed, in a custom format, or if the image's width/height is [code]0[/code]. Generates mipmaps for the image. Mipmaps are precalculated lower-resolution copies of the image that are automatically used if the image needs to be scaled down when rendered. They help improve image quality and performance when rendering. This method returns an error if the image is compressed, in a custom format, or if the image's width/height is [code]0[/code].
[b]Note:[/b] Mipmap generation is done on the CPU, is single-threaded and is [i]always[/i] done on the main thread. This means generating mipmaps will result in noticeable stuttering during gameplay, even if [method generate_mipmaps] is called from a [Thread].
</description> </description>
</method> </method>
<method name="get_data" qualifiers="const"> <method name="get_data" qualifiers="const">

View file

@ -59,6 +59,7 @@
<members> <members>
<member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false"> <member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false">
If [code]true[/code], the line's border will be anti-aliased. If [code]true[/code], the line's border will be anti-aliased.
[b]Note:[/b] Line2D is not accelerated by batching when being anti-aliased.
</member> </member>
<member name="begin_cap_mode" type="int" setter="set_begin_cap_mode" getter="get_begin_cap_mode" enum="Line2D.LineCapMode" default="0"> <member name="begin_cap_mode" type="int" setter="set_begin_cap_mode" getter="get_begin_cap_mode" enum="Line2D.LineCapMode" default="0">
Controls the style of the line's first point. Use [enum LineCapMode] constants. Controls the style of the line's first point. Use [enum LineCapMode] constants.

View file

@ -45,6 +45,13 @@
Returns the item's mesh. Returns the item's mesh.
</description> </description>
</method> </method>
<method name="get_item_mesh_transform" qualifiers="const">
<return type="Transform" />
<argument index="0" name="id" type="int" />
<description>
Returns the transform applied to the item's mesh.
</description>
</method>
<method name="get_item_name" qualifiers="const"> <method name="get_item_name" qualifiers="const">
<return type="String" /> <return type="String" />
<argument index="0" name="id" type="int" /> <argument index="0" name="id" type="int" />
@ -102,6 +109,14 @@
Sets the item's mesh. Sets the item's mesh.
</description> </description>
</method> </method>
<method name="set_item_mesh_transform">
<return type="void" />
<argument index="0" name="id" type="int" />
<argument index="1" name="mesh_transform" type="Transform" />
<description>
Sets the transform to apply to the item's mesh.
</description>
</method>
<method name="set_item_name"> <method name="set_item_name">
<return type="void" /> <return type="void" />
<argument index="0" name="id" type="int" /> <argument index="0" name="id" type="int" />

View file

@ -1727,7 +1727,7 @@ void EditorNode::_dialog_action(String p_file) {
ml = Ref<MeshLibrary>(memnew(MeshLibrary)); ml = Ref<MeshLibrary>(memnew(MeshLibrary));
} }
MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true); MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true, file_export_lib_apply_xforms->is_pressed());
Error err = ResourceSaver::save(p_file, ml); Error err = ResourceSaver::save(p_file, ml);
if (err) { if (err) {
@ -6751,6 +6751,10 @@ EditorNode::EditorNode() {
file_export_lib_merge->set_text(TTR("Merge With Existing")); file_export_lib_merge->set_text(TTR("Merge With Existing"));
file_export_lib_merge->set_pressed(true); file_export_lib_merge->set_pressed(true);
file_export_lib->get_vbox()->add_child(file_export_lib_merge); file_export_lib->get_vbox()->add_child(file_export_lib_merge);
file_export_lib_apply_xforms = memnew(CheckBox);
file_export_lib_apply_xforms->set_text(TTR("Apply MeshInstance Transforms"));
file_export_lib_apply_xforms->set_pressed(false);
file_export_lib->get_vbox()->add_child(file_export_lib_apply_xforms);
gui_base->add_child(file_export_lib); gui_base->add_child(file_export_lib);
file_script = memnew(EditorFileDialog); file_script = memnew(EditorFileDialog);

View file

@ -335,6 +335,7 @@ private:
EditorFileDialog *file_script; EditorFileDialog *file_script;
EditorFileDialog *file_android_build_source; EditorFileDialog *file_android_build_source;
CheckBox *file_export_lib_merge; CheckBox *file_export_lib_merge;
CheckBox *file_export_lib_apply_xforms;
String current_path; String current_path;
MenuButton *update_spinner; MenuButton *update_spinner;

View file

@ -47,23 +47,25 @@ void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {
} }
} }
void MeshLibraryEditor::_menu_confirm() { void MeshLibraryEditor::_menu_remove_confirm() {
switch (option) { switch (option) {
case MENU_OPTION_REMOVE_ITEM: { case MENU_OPTION_REMOVE_ITEM: {
mesh_library->remove_item(to_erase); mesh_library->remove_item(to_erase);
} break; } break;
case MENU_OPTION_UPDATE_FROM_SCENE: {
String existing = mesh_library->get_meta("_editor_source_scene");
ERR_FAIL_COND(existing == "");
_import_scene_cbk(existing);
} break;
default: { default: {
}; };
} }
} }
void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge) { void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {
cd_update->hide();
apply_xforms = p_apply_xforms;
String existing = mesh_library->get_meta("_editor_source_scene");
ERR_FAIL_COND(existing == "");
_import_scene_cbk(existing);
}
void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {
if (!p_merge) { if (!p_merge) {
p_library->clear(); p_library->clear();
} }
@ -108,6 +110,13 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
} }
p_library->set_item_mesh(id, mesh); p_library->set_item_mesh(id, mesh);
if (p_apply_xforms) {
p_library->set_item_mesh_transform(id, mi->get_transform());
} else {
p_library->set_item_mesh_transform(id, Transform());
}
mesh_instances[id] = mi; mesh_instances[id] = mi;
Vector<MeshLibrary::ShapeData> collisions; Vector<MeshLibrary::ShapeData> collisions;
@ -197,15 +206,16 @@ void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'."); ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE); _import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
memdelete(scene); memdelete(scene);
mesh_library->set_meta("_editor_source_scene", p_str); mesh_library->set_meta("_editor_source_scene", p_str);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false); menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);
} }
Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge) { Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {
_import_scene(p_base_scene, ml, p_merge); _import_scene(p_base_scene, ml, p_merge, p_apply_xforms);
return OK; return OK;
} }
@ -219,23 +229,29 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
String p = editor->get_inspector()->get_selected_path(); String p = editor->get_inspector()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) { if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int(); to_erase = p.get_slice("/", 3).to_int();
cd->set_text(vformat(TTR("Remove item %d?"), to_erase)); cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
cd->popup_centered(Size2(300, 60)); cd_remove->popup_centered(Size2(300, 60));
} }
} break; } break;
case MENU_OPTION_IMPORT_FROM_SCENE: { case MENU_OPTION_IMPORT_FROM_SCENE: {
apply_xforms = false;
file->popup_centered_ratio();
} break;
case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {
apply_xforms = true;
file->popup_centered_ratio(); file->popup_centered_ratio();
} break; } break;
case MENU_OPTION_UPDATE_FROM_SCENE: { case MENU_OPTION_UPDATE_FROM_SCENE: {
cd->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene")))); cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
cd->popup_centered(Size2(500, 60)); cd_update->popup_centered(Size2(500, 60));
} break; } break;
} }
} }
void MeshLibraryEditor::_bind_methods() { void MeshLibraryEditor::_bind_methods() {
ClassDB::bind_method("_menu_cbk", &MeshLibraryEditor::_menu_cbk); ClassDB::bind_method("_menu_cbk", &MeshLibraryEditor::_menu_cbk);
ClassDB::bind_method("_menu_confirm", &MeshLibraryEditor::_menu_confirm); ClassDB::bind_method("_menu_remove_confirm", &MeshLibraryEditor::_menu_remove_confirm);
ClassDB::bind_method("_menu_update_confirm", &MeshLibraryEditor::_menu_update_confirm);
ClassDB::bind_method("_import_scene_cbk", &MeshLibraryEditor::_import_scene_cbk); ClassDB::bind_method("_import_scene_cbk", &MeshLibraryEditor::_import_scene_cbk);
} }
@ -261,16 +277,22 @@ MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) {
menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM); menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);
menu->get_popup()->add_separator(); menu->get_popup()->add_separator();
menu->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE); menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);
menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);
menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE); menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true); menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);
menu->get_popup()->connect("id_pressed", this, "_menu_cbk"); menu->get_popup()->connect("id_pressed", this, "_menu_cbk");
menu->hide(); menu->hide();
editor = p_editor; editor = p_editor;
cd = memnew(ConfirmationDialog); cd_remove = memnew(ConfirmationDialog);
add_child(cd); add_child(cd_remove);
cd->get_ok()->connect("pressed", this, "_menu_confirm"); cd_remove->get_ok()->connect("pressed", this, "_menu_remove_confirm");
cd_update = memnew(ConfirmationDialog);
add_child(cd_update);
cd_update->get_ok()->set_text("Apply without Transforms");
cd_update->get_ok()->connect("pressed", this, "_menu_update_confirm", varray(false));
cd_update->add_button("Apply with Transforms")->connect("pressed", this, "_menu_update_confirm", varray(true));
} }
void MeshLibraryEditorPlugin::edit(Object *p_node) { void MeshLibraryEditorPlugin::edit(Object *p_node) {

View file

@ -41,8 +41,10 @@ class MeshLibraryEditor : public Control {
EditorNode *editor; EditorNode *editor;
MenuButton *menu; MenuButton *menu;
ConfirmationDialog *cd; ConfirmationDialog *cd_remove;
ConfirmationDialog *cd_update;
EditorFileDialog *file; EditorFileDialog *file;
bool apply_xforms;
int to_erase; int to_erase;
enum { enum {
@ -50,15 +52,17 @@ class MeshLibraryEditor : public Control {
MENU_OPTION_ADD_ITEM, MENU_OPTION_ADD_ITEM,
MENU_OPTION_REMOVE_ITEM, MENU_OPTION_REMOVE_ITEM,
MENU_OPTION_UPDATE_FROM_SCENE, MENU_OPTION_UPDATE_FROM_SCENE,
MENU_OPTION_IMPORT_FROM_SCENE MENU_OPTION_IMPORT_FROM_SCENE,
MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS
}; };
int option; int option;
void _import_scene_cbk(const String &p_str); void _import_scene_cbk(const String &p_str);
void _menu_cbk(int p_option); void _menu_cbk(int p_option);
void _menu_confirm(); void _menu_remove_confirm();
void _menu_update_confirm(bool p_apply_xforms);
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge); static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -67,7 +71,7 @@ public:
MenuButton *get_menu_button() const { return menu; } MenuButton *get_menu_button() const { return menu; }
void edit(const Ref<MeshLibrary> &p_mesh_library); void edit(const Ref<MeshLibrary> &p_mesh_library);
static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true); static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true, bool p_apply_xforms = false);
MeshLibraryEditor(EditorNode *p_editor); MeshLibraryEditor(EditorNode *p_editor);
}; };

View file

@ -15,7 +15,7 @@ PY_FILES=$(find \( -path "./.git" \
\) -print) \) -print)
black -l 120 $PY_FILES black -l 120 $PY_FILES
git diff > patch.patch git diff --color > patch.patch
# If no patch has been generated all is OK, clean up, and exit. # If no patch has been generated all is OK, clean up, and exit.
if [ ! -s patch.patch ] ; then if [ ! -s patch.patch ] ; then

View file

@ -40,7 +40,7 @@ while IFS= read -rd '' f; do
done done
done done
git diff > patch.patch git diff --color > patch.patch
# If no patch has been generated all is OK, clean up, and exit. # If no patch has been generated all is OK, clean up, and exit.
if [ ! -s patch.patch ] ; then if [ ! -s patch.patch ] ; then

View file

@ -44,7 +44,7 @@ while IFS= read -rd '' f; do
perl -i -pe 's/\x20== true//g' "$f" perl -i -pe 's/\x20== true//g' "$f"
done done
git diff > patch.patch git diff --color > patch.patch
# If no patch has been generated all is OK, clean up, and exit. # If no patch has been generated all is OK, clean up, and exit.
if [ ! -s patch.patch ] ; then if [ ! -s patch.patch ] ; then

View file

@ -460,7 +460,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
} }
Pair<Transform, IndexKey> p; Pair<Transform, IndexKey> p;
p.first = xform; p.first = xform * mesh_library->get_item_mesh_transform(c.item);
p.second = E->get(); p.second = E->get();
multimesh_items[c.item].push_back(p); multimesh_items[c.item].push_back(p);
} }

View file

@ -261,6 +261,12 @@ void GridMapEditor::_update_cursor_transform() {
cursor_transform.basis *= node->get_cell_scale(); cursor_transform.basis *= node->get_cell_scale();
cursor_transform = node->get_global_transform() * cursor_transform; cursor_transform = node->get_global_transform() * cursor_transform;
if (selected_palette >= 0) {
if (node && !node->get_mesh_library().is_null()) {
cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette);
}
}
if (cursor_instance.is_valid()) { if (cursor_instance.is_valid()) {
VisualServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform); VisualServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform);
VisualServer::get_singleton()->instance_set_visible(cursor_instance, cursor_visible); VisualServer::get_singleton()->instance_set_visible(cursor_instance, cursor_visible);

View file

@ -1630,6 +1630,28 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
return Variant::NIL; return Variant::NIL;
} }
void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
if (!script->is_valid() || !script->script_class)
return;
GD_MONO_SCOPE_THREAD_ATTACH;
// TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
const Vector<GDMonoMethod *> &methods = top->get_all_methods();
for (int i = 0; i < methods.size(); ++i) {
MethodInfo minfo = methods[i]->get_method_info();
if (minfo.name != CACHED_STRING_NAME(dotctor)) {
p_list->push_back(minfo);
}
}
top = top->get_parent_class();
}
}
bool CSharpInstance::has_method(const StringName &p_method) const { bool CSharpInstance::has_method(const StringName &p_method) const {
if (!script.is_valid()) if (!script.is_valid())
return false; return false;
@ -3034,10 +3056,19 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
GD_MONO_SCOPE_THREAD_ATTACH; GD_MONO_SCOPE_THREAD_ATTACH;
// TODO: Filter out things unsuitable for explicit calls, like constructors. // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
const Vector<GDMonoMethod *> &methods = script_class->get_all_methods(); GDMonoClass *top = script_class;
for (int i = 0; i < methods.size(); ++i) {
p_list->push_back(methods[i]->get_method_info()); while (top && top != native) {
const Vector<GDMonoMethod *> &methods = top->get_all_methods();
for (int i = 0; i < methods.size(); ++i) {
MethodInfo minfo = methods[i]->get_method_info();
if (minfo.name != CACHED_STRING_NAME(dotctor)) {
p_list->push_back(methods[i]->get_method_info());
}
}
top = top->get_parent_class();
} }
} }

View file

@ -255,7 +255,7 @@ public:
virtual void get_property_list(List<PropertyInfo> *p_properties) const; virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const; virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const;
/* TODO */ virtual void get_method_list(List<MethodInfo> *p_list) const {} virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const; virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount); virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);

View file

@ -1126,6 +1126,17 @@ namespace Godot
return 2.0f * inter / sum; return 2.0f * inter / sum;
} }
/// <summary>
/// Returns a simplified canonical path.
/// </summary>
public static string SimplifyPath(this string instance)
{
return godot_icall_String_simplify_path(instance);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_String_simplify_path(string str);
/// <summary> /// <summary>
/// Split the string by a divisor string, return an array of the substrings. /// Split the string by a divisor string, return an array of the substrings.
/// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". /// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".

View file

@ -67,6 +67,11 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
return GDMonoMarshal::mono_string_from_godot(ret); return GDMonoMarshal::mono_string_from_godot(ret);
} }
MonoString *godot_icall_String_simplify_path(MonoString *p_str) {
String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path();
return GDMonoMarshal::mono_string_from_godot(ret);
}
void godot_register_string_icalls() { void godot_register_string_icalls() {
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
@ -74,6 +79,7 @@ void godot_register_string_icalls() {
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text); GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path);
} }
#endif // MONO_GLUE_ENABLED #endif // MONO_GLUE_ENABLED

View file

@ -161,22 +161,28 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
ERR_FAIL_COND_V(p_path.empty(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_path.empty(), ERR_INVALID_PARAMETER);
_peer = Ref<WSLPeer>(memnew(WSLPeer)); _peer = Ref<WSLPeer>(memnew(WSLPeer));
IP_Address addr;
if (!p_host.is_valid_ip_address()) { if (p_host.is_valid_ip_address()) {
addr = IP::get_singleton()->resolve_hostname(p_host); ip_candidates.clear();
ip_candidates.push_back(IP_Address(p_host));
} else { } else {
addr = p_host; ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
} }
ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(ip_candidates.empty(), ERR_INVALID_PARAMETER);
String port = ""; String port = "";
if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
port = ":" + itos(p_port); port = ":" + itos(p_port);
} }
Error err = _tcp->connect_to_host(addr, p_port); Error err = ERR_BUG; // Should be at least one entry.
while (ip_candidates.size() > 0) {
err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
if (err == OK) {
break;
}
}
if (err != OK) { if (err != OK) {
_tcp->disconnect_from_host(); _tcp->disconnect_from_host();
_on_error(); _on_error();
@ -185,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
_connection = _tcp; _connection = _tcp;
_use_ssl = p_ssl; _use_ssl = p_ssl;
_host = p_host; _host = p_host;
_port = p_port;
// Strip edges from protocols. // Strip edges from protocols.
_protocols.resize(p_protocols.size()); _protocols.resize(p_protocols.size());
String *pw = _protocols.ptrw(); String *pw = _protocols.ptrw();
@ -244,6 +251,7 @@ void WSLClient::poll() {
_on_error(); _on_error();
break; break;
case StreamPeerTCP::STATUS_CONNECTED: { case StreamPeerTCP::STATUS_CONNECTED: {
ip_candidates.clear();
Ref<StreamPeerSSL> ssl; Ref<StreamPeerSSL> ssl;
if (_use_ssl) { if (_use_ssl) {
if (_connection == _tcp) { if (_connection == _tcp) {
@ -274,6 +282,12 @@ void WSLClient::poll() {
_do_handshake(); _do_handshake();
} break; } break;
case StreamPeerTCP::STATUS_ERROR: case StreamPeerTCP::STATUS_ERROR:
while (ip_candidates.size() > 0) {
_tcp->disconnect_from_host();
if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
return;
}
}
disconnect_from_host(); disconnect_from_host();
_on_error(); _on_error();
break; break;
@ -315,6 +329,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
memset(_resp_buf, 0, sizeof(_resp_buf)); memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0; _resp_pos = 0;
ip_candidates.clear();
} }
IP_Address WSLClient::get_connected_host() const { IP_Address WSLClient::get_connected_host() const {

View file

@ -63,6 +63,8 @@ private:
String _key; String _key;
String _host; String _host;
int _port;
Array ip_candidates;
Vector<String> _protocols; Vector<String> _protocols;
bool _use_ssl; bool _use_ssl;

View file

@ -419,7 +419,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
@Keep @Keep
private void vibrate(int durationMs) { private void vibrate(int durationMs) {
if (requestPermission("VIBRATE")) { if (durationMs > 0 && requestPermission("VIBRATE")) {
Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE); Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) { if (v != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

View file

@ -32,6 +32,8 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/project_settings.h" #include "core/project_settings.h"
#include "core/version.h"
#include "core/version_hash.gen.h"
#include "main/main.h" #include "main/main.h"
#include <string.h> #include <string.h>
@ -85,11 +87,18 @@ static void handle_crash(int sig) {
} }
// Dump the backtrace to stderr with a message to the user // Dump the backtrace to stderr with a message to the user
fprintf(stderr, "\n================================================================\n");
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
if (OS::get_singleton()->get_main_loop()) if (OS::get_singleton()->get_main_loop())
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
if (String(VERSION_HASH).length() != 0) {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
} else {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
}
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size); char **strings = backtrace_symbols(bt_buffer, size);
if (strings) { if (strings) {
@ -148,6 +157,7 @@ static void handle_crash(int sig) {
free(strings); free(strings);
} }
fprintf(stderr, "-- END OF BACKTRACE --\n"); fprintf(stderr, "-- END OF BACKTRACE --\n");
fprintf(stderr, "================================================================\n");
// Abort to pass the error to the OS // Abort to pass the error to the OS
abort(); abort();

View file

@ -32,6 +32,8 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/project_settings.h" #include "core/project_settings.h"
#include "core/version.h"
#include "core/version_hash.gen.h"
#include "main/main.h" #include "main/main.h"
#ifdef CRASH_HANDLER_EXCEPTION #ifdef CRASH_HANDLER_EXCEPTION
@ -127,6 +129,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
fprintf(stderr, "\n================================================================\n");
fprintf(stderr, "%s: Program crashed\n", __FUNCTION__); fprintf(stderr, "%s: Program crashed\n", __FUNCTION__);
if (OS::get_singleton()->get_main_loop()) if (OS::get_singleton()->get_main_loop())
@ -175,6 +178,12 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
msg = proj_settings->get("debug/settings/crash_handler/message"); msg = proj_settings->get("debug/settings/crash_handler/message");
} }
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
if (String(VERSION_HASH).length() != 0) {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
} else {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
}
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
int n = 0; int n = 0;
@ -200,6 +209,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
} while (frame.AddrReturn.Offset != 0 && n < 256); } while (frame.AddrReturn.Offset != 0 && n < 256);
fprintf(stderr, "-- END OF BACKTRACE --\n"); fprintf(stderr, "-- END OF BACKTRACE --\n");
fprintf(stderr, "================================================================\n");
SymCleanup(process); SymCleanup(process);

View file

@ -32,6 +32,8 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/project_settings.h" #include "core/project_settings.h"
#include "core/version.h"
#include "core/version_hash.gen.h"
#include "main/main.h" #include "main/main.h"
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -61,12 +63,19 @@ static void handle_crash(int sig) {
} }
// Dump the backtrace to stderr with a message to the user // Dump the backtrace to stderr with a message to the user
fprintf(stderr, "\n================================================================\n");
fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig);
if (OS::get_singleton()->get_main_loop()) { if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
} }
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
if (String(VERSION_HASH).length() != 0) {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
} else {
fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
}
fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
char **strings = backtrace_symbols(bt_buffer, size); char **strings = backtrace_symbols(bt_buffer, size);
if (strings) { if (strings) {
@ -115,6 +124,7 @@ static void handle_crash(int sig) {
free(strings); free(strings);
} }
fprintf(stderr, "-- END OF BACKTRACE --\n"); fprintf(stderr, "-- END OF BACKTRACE --\n");
fprintf(stderr, "================================================================\n");
// Abort to pass the error to the OS // Abort to pass the error to the OS
abort(); abort();

View file

@ -1131,7 +1131,7 @@ void AnimatedSprite3D::stop() {
} }
bool AnimatedSprite3D::is_playing() const { bool AnimatedSprite3D::is_playing() const {
return is_processing(); return playing;
} }
void AnimatedSprite3D::_reset_timeout() { void AnimatedSprite3D::_reset_timeout() {

View file

@ -549,7 +549,7 @@ Transform SkeletonIK::_get_target_transform() {
target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override));
} }
if (target_node_override) { if (target_node_override && target_node_override->is_inside_tree()) {
return target_node_override->get_global_transform(); return target_node_override->get_global_transform();
} else { } else {
return target; return target;

View file

@ -520,6 +520,7 @@ void GraphEdit::_notification(int p_what) {
bool GraphEdit::_filter_input(const Point2 &p_point) { bool GraphEdit::_filter_input(const Point2 &p_point) {
Ref<Texture> port = get_icon("port", "GraphNode"); Ref<Texture> port = get_icon("port", "GraphNode");
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
for (int i = get_child_count() - 1; i >= 0; i--) { for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@ -529,14 +530,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
for (int j = 0; j < gn->get_connection_output_count(); j++) { for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
if (is_in_hot_zone(pos / zoom, p_point / zoom)) { if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, false)) {
return true; return true;
} }
} }
for (int j = 0; j < gn->get_connection_input_count(); j++) { for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
if (is_in_hot_zone(pos / zoom, p_point / zoom)) { if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, true)) {
return true; return true;
} }
} }
@ -548,8 +549,10 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev; Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
connecting_valid = false;
Ref<Texture> port = get_icon("port", "GraphNode"); Ref<Texture> port = get_icon("port", "GraphNode");
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
connecting_valid = false;
click_pos = mb->get_position() / zoom; click_pos = mb->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) { for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@ -559,7 +562,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) { for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
if (is_in_hot_zone(pos / zoom, click_pos)) { if (is_in_hot_zone(pos / zoom, click_pos, port_size, false)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect //check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@ -601,7 +604,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) { for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
if (is_in_hot_zone(pos / zoom, click_pos)) { if (is_in_hot_zone(pos / zoom, click_pos, port_size, true)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect //check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@ -650,10 +653,12 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false; connecting_target = false;
top_layer->update(); top_layer->update();
minimap->update(); minimap->update();
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0 * zoom; connecting_valid = just_disconnected || click_pos.distance_to(connecting_to / zoom) > 20.0;
if (connecting_valid) { if (connecting_valid) {
Ref<Texture> port = get_icon("port", "GraphNode"); Ref<Texture> port = get_icon("port", "GraphNode");
Vector2i port_size = Vector2i(port->get_width(), port->get_height());
Vector2 mpos = mm->get_position() / zoom; Vector2 mpos = mm->get_position() / zoom;
for (int i = get_child_count() - 1; i >= 0; i--) { for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@ -665,7 +670,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) { for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
int type = gn->get_connection_output_type(j); int type = gn->get_connection_output_type(j);
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) { if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, false)) {
connecting_target = true; connecting_target = true;
connecting_to = pos; connecting_to = pos;
connecting_target_to = gn->get_name(); connecting_target_to = gn->get_name();
@ -677,7 +682,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) { for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
int type = gn->get_connection_input_type(j); int type = gn->get_connection_input_type(j);
if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos)) { if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, true)) {
connecting_target = true; connecting_target = true;
connecting_to = pos; connecting_to = pos;
connecting_target_to = gn->get_name(); connecting_target_to = gn->get_name();
@ -748,9 +753,25 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos)
} }
} }
bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) { bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) { if (p_left) {
return false; if (!Rect2(
pos.x - p_port_size.x / 2 - port_grab_distance_horizontal,
pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
p_port_size.x + port_grab_distance_horizontal,
p_port_size.y + port_grab_distance_vertical)
.has_point(p_mouse_pos)) {
return false;
}
} else {
if (!Rect2(
pos.x - p_port_size.x / 2,
pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
p_port_size.x + port_grab_distance_horizontal,
p_port_size.y + port_grab_distance_vertical)
.has_point(p_mouse_pos)) {
return false;
}
} }
for (int i = 0; i < get_child_count(); i++) { for (int i = 0; i < get_child_count(); i++) {

View file

@ -181,7 +181,7 @@ private:
GraphEditMinimap *minimap; GraphEditMinimap *minimap;
void _top_layer_input(const Ref<InputEvent> &p_ev); void _top_layer_input(const Ref<InputEvent> &p_ev);
bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos); bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw(); void _top_layer_draw();
void _connections_layer_draw(); void _connections_layer_draw();

View file

@ -1090,7 +1090,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const { Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const {
if (!underline_meta) { if (!underline_meta) {
return CURSOR_ARROW; return get_default_cursor_shape();
} }
if (selection.click) { if (selection.click) {
@ -1098,10 +1098,11 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
} }
if (main->first_invalid_line < main->lines.size()) { if (main->first_invalid_line < main->lines.size()) {
return CURSOR_ARROW; //invalid return get_default_cursor_shape(); //invalid
} }
int line = 0; int line = 0;
Item *item = nullptr; Item *item = nullptr;
bool outside; bool outside;
((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line, &outside); ((RichTextLabel *)(this))->_find_click(main, p_pos, &item, &line, &outside);
@ -1110,7 +1111,7 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return CURSOR_POINTING_HAND; return CURSOR_POINTING_HAND;
} }
return CURSOR_ARROW; return get_default_cursor_shape();
} }
void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {

View file

@ -888,7 +888,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale); theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale);
// Visual Node Ports // Visual Node Ports
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale);
theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 24 * scale);
theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale);
theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0)); theme->set_stylebox("bg", "GraphEditMinimap", make_flat_stylebox(Color(0.24, 0.24, 0.24), 0, 0, 0, 0));

View file

@ -43,6 +43,8 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
set_item_name(idx, p_value); set_item_name(idx, p_value);
} else if (what == "mesh") { } else if (what == "mesh") {
set_item_mesh(idx, p_value); set_item_mesh(idx, p_value);
} else if (what == "mesh_transform") {
set_item_mesh_transform(idx, p_value);
} else if (what == "shape") { } else if (what == "shape") {
Vector<ShapeData> shapes; Vector<ShapeData> shapes;
ShapeData sd; ShapeData sd;
@ -77,6 +79,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_name(idx); r_ret = get_item_name(idx);
} else if (what == "mesh") { } else if (what == "mesh") {
r_ret = get_item_mesh(idx); r_ret = get_item_mesh(idx);
} else if (what == "mesh_transform") {
r_ret = get_item_mesh_transform(idx);
} else if (what == "shapes") { } else if (what == "shapes") {
r_ret = _get_item_shapes(idx); r_ret = _get_item_shapes(idx);
} else if (what == "navmesh") { } else if (what == "navmesh") {
@ -127,6 +131,14 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
_change_notify(); _change_notify();
} }
void MeshLibrary::set_item_mesh_transform(int p_item, const Transform &p_transform) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh_transform = p_transform;
notify_change_to_owners();
emit_changed();
_change_notify();
}
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) { void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes; item_map[p_item].shapes = p_shapes;
@ -170,6 +182,11 @@ Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
return item_map[p_item].mesh; return item_map[p_item].mesh;
} }
Transform MeshLibrary::get_item_mesh_transform(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].mesh_transform;
}
Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const { Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const {
ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].shapes; return item_map[p_item].shapes;
@ -267,12 +284,14 @@ void MeshLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item); ClassDB::bind_method(D_METHOD("create_item", "id"), &MeshLibrary::create_item);
ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name);
ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh);
ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform);
ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh); ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh);
ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform); ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes); ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes);
ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview); ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview);
ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name);
ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh);
ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform);
ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh); ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh);
ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform); ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform);
ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes); ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes);

View file

@ -52,6 +52,7 @@ public:
Vector<ShapeData> shapes; Vector<ShapeData> shapes;
Ref<Texture> preview; Ref<Texture> preview;
Transform navmesh_transform; Transform navmesh_transform;
Transform mesh_transform;
Ref<NavigationMesh> navmesh; Ref<NavigationMesh> navmesh;
}; };
@ -71,12 +72,14 @@ public:
void create_item(int p_item); void create_item(int p_item);
void set_item_name(int p_item, const String &p_name); void set_item_name(int p_item, const String &p_name);
void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh);
void set_item_mesh_transform(int p_item, const Transform &p_transform);
void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh);
void set_item_navmesh_transform(int p_item, const Transform &p_transform); void set_item_navmesh_transform(int p_item, const Transform &p_transform);
void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes);
void set_item_preview(int p_item, const Ref<Texture> &p_preview); void set_item_preview(int p_item, const Ref<Texture> &p_preview);
String get_item_name(int p_item) const; String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const;
Transform get_item_mesh_transform(int p_item) const;
Ref<NavigationMesh> get_item_navmesh(int p_item) const; Ref<NavigationMesh> get_item_navmesh(int p_item) const;
Transform get_item_navmesh_transform(int p_item) const; Transform get_item_navmesh_transform(int p_item) const;
Vector<ShapeData> get_item_shapes(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const;

View file

@ -377,10 +377,17 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
return OK; return OK;
} }
// save the child instanced scenes that are chosen as editable, so they can be restored bool is_editable_instance = false;
// save the child instantiated scenes that are chosen as editable, so they can be restored
// upon load back // upon load back
if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) { if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) {
editable_instances.push_back(p_owner->get_path_to(p_node)); editable_instances.push_back(p_owner->get_path_to(p_node));
// Node is the root of an editable instance.
is_editable_instance = true;
} else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
// Node is part of an editable instance.
is_editable_instance = true;
} }
NodeData nd; NodeData nd;
@ -610,7 +617,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// Save the right type. If this node was created by an instance // Save the right type. If this node was created by an instance
// then flag that the node should not be created but reused // then flag that the node should not be created but reused
if (pack_state_stack.empty()) { if (pack_state_stack.empty() && !is_editable_instance) {
//this node is not part of an instancing process, so save the type //this node is not part of an instancing process, so save the type
nd.type = _nm_get_string(p_node->get_class(), name_map); nd.type = _nm_get_string(p_node->get_class(), name_map);
} else { } else {

View file

@ -1456,6 +1456,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
int i, j, prevrow, thisrow, point; int i, j, prevrow, thisrow, point;
float x, y, z; float x, y, z;
float scale = height * (is_hemisphere ? 1.0 : 0.5);
// set our bounding box // set our bounding box
PoolVector<Vector3> points; PoolVector<Vector3> points;
@ -1479,7 +1481,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
v /= (rings + 1); v /= (rings + 1);
w = sin(Math_PI * v); w = sin(Math_PI * v);
y = height * (is_hemisphere ? 1.0 : 0.5) * cos(Math_PI * v); y = scale * cos(Math_PI * v);
for (i = 0; i <= radial_segments; i++) { for (i = 0; i <= radial_segments; i++) {
float u = i; float u = i;
@ -1494,7 +1496,8 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
} else { } else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w); Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p); points.push_back(p);
normals.push_back(p.normalized()); Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
normals.push_back(normal.normalized());
}; };
ADD_TANGENT(z, 0.0, -x, 1.0) ADD_TANGENT(z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v)); uvs.push_back(Vector2(u, v));