summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/arkit/SCsub3
-rw-r--r--modules/bullet/SCsub4
-rw-r--r--modules/bullet/space_bullet.cpp22
-rw-r--r--modules/camera/SCsub3
-rw-r--r--modules/csg/csg.cpp8
-rw-r--r--modules/dds/texture_loader_dds.cpp10
-rw-r--r--modules/gdnative/config.py1
-rw-r--r--modules/gdnative/doc_classes/@NativeScript.xml13
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp2
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.cpp6
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.h4
-rw-r--r--modules/gdscript/gdscript_compiler.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp9
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp6
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h4
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp148
-rw-r--r--modules/mbedtls/crypto_mbedtls.h15
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py4
-rw-r--r--modules/mono/build_scripts/mono_configure.py11
-rw-r--r--modules/mono/config.py1
-rw-r--r--modules/mono/doc_classes/@C#.xml13
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml5
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs48
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs270
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs15
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs37
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs19
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs48
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj1
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp82
-rw-r--r--modules/mono/glue/scene_tree_glue.h50
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp4
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp1
-rw-r--r--modules/visual_script/config.py1
-rw-r--r--modules/visual_script/doc_classes/@VisualScript.xml15
51 files changed, 868 insertions, 148 deletions
diff --git a/modules/arkit/SCsub b/modules/arkit/SCsub
index 61c0a8248c..7e103d6565 100644
--- a/modules/arkit/SCsub
+++ b/modules/arkit/SCsub
@@ -5,6 +5,9 @@ Import("env_modules")
env_arkit = env_modules.Clone()
+# (iOS) Enable module support
+env_arkit.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
# (iOS) Build as separate static library
modules_sources = []
env_arkit.add_source_files(modules_sources, "*.cpp")
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index b853ebfc63..21bdcca18e 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -201,8 +201,8 @@ if env["builtin_bullet"]:
env_bullet.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
env_bullet.Prepend(CPPPATH=[thirdparty_dir])
- # if env['target'] == "debug" or env['target'] == "release_debug":
- # env_bullet.Append(CPPDEFINES=['BT_DEBUG'])
+ if env["target"] == "debug" or env["target"] == "release_debug":
+ env_bullet.Append(CPPDEFINES=["DEBUG"])
env_bullet.Append(CPPDEFINES=["BT_USE_OLD_DAMPING_METHOD"])
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 99f58e7059..cfe8cd5322 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -123,6 +123,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
}
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, 0);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
@@ -152,7 +153,17 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
}
bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
+ r_closest_safe = 0.0f;
+ r_closest_unsafe = 0.0f;
+ btVector3 bt_motion;
+ G_TO_B(p_motion, bt_motion);
+
+ if (bt_motion.fuzzyZero()) {
+ return false;
+ }
+
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
if (!btShape->isConvex()) {
@@ -162,9 +173,6 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
}
btConvexShape *bt_convex_shape = static_cast<btConvexShape *>(btShape);
- btVector3 bt_motion;
- G_TO_B(p_motion, bt_motion);
-
btTransform bt_xform_from;
G_TO_B(p_xform, bt_xform_from);
UNSCALE_BT_BASIS(bt_xform_from);
@@ -178,9 +186,6 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, space->dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration);
- r_closest_unsafe = 1.0;
- r_closest_safe = 1.0;
-
if (btResult.hasHit()) {
const btScalar l = bt_motion.length();
r_closest_unsafe = btResult.m_closestHitFraction;
@@ -196,6 +201,9 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
r_info->collider_id = collision_object->get_instance_id();
r_info->shape = btResult.m_shapeId;
}
+ } else {
+ r_closest_safe = 1.0f;
+ r_closest_unsafe = 1.0f;
}
bulletdelete(bt_convex_shape);
@@ -209,6 +217,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
}
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
@@ -240,6 +249,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index 63c4e9fbab..631a65bde2 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_camera = env_modules.Clone()
if env["platform"] == "iphone":
+ # (iOS) Enable module support
+ env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
# (iOS) Build as separate static library
modules_sources = []
env_camera.add_source_files(modules_sources, "register_types.cpp")
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index ded0b970dc..d0c9bf5d38 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -904,8 +904,12 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
vertices[p_segment_indices[closest_idx]].point
};
if (are_segements_parallel(edge1, edge2, vertex_snap2)) {
- degenerate_points.push_back(outer_edge_idx[0]);
- degenerate_points.push_back(outer_edge_idx[1]);
+ if (!degenerate_points.find(outer_edge_idx[0])) {
+ degenerate_points.push_back(outer_edge_idx[0]);
+ }
+ if (!degenerate_points.find(outer_edge_idx[1])) {
+ degenerate_points.push_back(outer_edge_idx[1]);
+ }
continue;
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index ba425371a8..2f4f7d7a4c 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -29,14 +29,15 @@
/*************************************************************************/
#include "texture_loader_dds.h"
+
#include "core/os/file_access.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
+// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
+
enum {
DDS_MAGIC = 0x20534444,
- DDSD_CAPS = 0x00000001,
- DDSD_PIXELFORMAT = 0x00001000,
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
@@ -47,7 +48,6 @@ enum {
};
enum DDSFormat {
-
DDS_DXT1,
DDS_DXT3,
DDS_DXT5,
@@ -128,7 +128,9 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
//validate
- if (magic != DDS_MAGIC || hsize != 124 || !(flags & DDSD_PIXELFORMAT) || !(flags & DDSD_CAPS)) {
+ // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
+ // but non-mandatory when reading (as some writers don't set them)...
+ if (magic != DDS_MAGIC || hsize != 124) {
ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
}
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 4b997e4bfe..7603e7d69d 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -8,7 +8,6 @@ def configure(env):
def get_doc_classes():
return [
- "@NativeScript",
"XRInterfaceGDNative",
"GDNative",
"GDNativeLibrary",
diff --git a/modules/gdnative/doc_classes/@NativeScript.xml b/modules/gdnative/doc_classes/@NativeScript.xml
deleted file mode 100644
index 809b225e21..0000000000
--- a/modules/gdnative/doc_classes/@NativeScript.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@NativeScript" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index 2a9836329e..5896da4640 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -283,7 +283,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platforms["Haiku"] = platform_haiku;
NativePlatformConfig platform_uwp;
- platform_uwp.name = "Windows Universal";
+ platform_uwp.name = "UWP";
platform_uwp.entries.push_back("arm");
platform_uwp.entries.push_back("32");
platform_uwp.entries.push_back("64");
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
index 5fe1060aae..648f4f7cdd 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
@@ -95,13 +95,15 @@ void NavigationMeshEditor::_bind_methods() {
NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox = memnew(HBoxContainer);
- button_bake = memnew(ToolButton);
+ button_bake = memnew(Button);
+ button_bake->set_flat(true);
bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
button_bake->set_text(TTR("Bake NavMesh"));
button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed));
- button_reset = memnew(ToolButton);
+ button_reset = memnew(Button);
+ button_reset->set_flat(true);
bake_hbox->add_child(button_reset);
// No button text, we only use a revert icon which is set when entering the tree.
button_reset->set_tooltip(TTR("Clear the navigation mesh."));
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.h b/modules/gdnavigation/navigation_mesh_editor_plugin.h
index da3a981f8c..728f958eaa 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.h
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.h
@@ -46,8 +46,8 @@ class NavigationMeshEditor : public Control {
AcceptDialog *err_dialog;
HBoxContainer *bake_hbox;
- ToolButton *button_bake;
- ToolButton *button_reset;
+ Button *button_bake;
+ Button *button_reset;
Label *bake_info;
NavigationRegion3D *node;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index bc095ae1f9..5bc9003c29 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1422,6 +1422,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.push_back(0);
codegen.opcodes.write[else_addr] = codegen.opcodes.size();
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(cf->body_else->line);
+ codegen.current_line = cf->body_else->line;
+
Error err2 = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr);
if (err2) {
return err2;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 50d8289fd1..3a5db3687b 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -3093,6 +3093,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
}
+
+ for (int i = 0; i < base_type.class_type->subclasses.size(); i++) {
+ if (base_type.class_type->subclasses[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->subclasses[i]->line;
+ return OK;
+ }
+ }
}
base_type = base_type.class_type->base_type;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index a06ecb1ea8..ca452bf008 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2821,6 +2821,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
#ifdef DEBUG_ENABLED
+ pending_newline = -1; // reset for the new block
+
NewLineNode *nl = alloc_node<NewLineNode>();
nl->line = tokenizer->get_token_line();
@@ -3178,6 +3180,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = tokenizer->get_token_identifier();
+#ifdef DEBUG_ENABLED
+ for (int j = 0; j < current_class->variables.size(); j++) {
+ if (current_class->variables[j].identifier == id->name) {
+ _add_warning(GDScriptWarning::SHADOWED_VARIABLE, id->line, id->name, itos(current_class->variables[j].line));
+ }
+ }
+#endif // DEBUG_ENABLED
BlockNode *check_block = p_block;
while (check_block) {
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 3b0e78546d..1b7ce98721 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1241,14 +1241,16 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed));
search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input));
- mode_thumbnail = memnew(ToolButton);
+ mode_thumbnail = memnew(Button);
+ mode_thumbnail->set_flat(true);
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
mode_thumbnail->set_icon(p_editor->get_gui_base()->get_theme_icon("FileThumbnail", "EditorIcons"));
hb->add_child(mode_thumbnail);
mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
- mode_list = memnew(ToolButton);
+ mode_list = memnew(Button);
+ mode_list->set_flat(true);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
mode_list->set_icon(p_editor->get_gui_base()->get_theme_icon("FileList", "EditorIcons"));
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 19eea18965..0ae9b27833 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -74,8 +74,8 @@ class GridMapEditor : public VBoxContainer {
MenuButton *options;
SpinBox *floor;
double accumulated_floor_delta;
- ToolButton *mode_thumbnail;
- ToolButton *mode_list;
+ Button *mode_thumbnail;
+ Button *mode_list;
LineEdit *search_box;
HSlider *size_slider;
HBoxContainer *spatial_editor_hb;
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 1f9d8c2aa3..501bfff075 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -50,7 +50,7 @@ CryptoKey *CryptoKeyMbedTLS::create() {
return memnew(CryptoKeyMbedTLS);
}
-Error CryptoKeyMbedTLS::load(String p_path) {
+Error CryptoKeyMbedTLS::load(String p_path, bool p_public_only) {
ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Key is in use");
PackedByteArray out;
@@ -59,42 +59,81 @@ Error CryptoKeyMbedTLS::load(String p_path) {
int flen = f->get_len();
out.resize(flen + 1);
- {
- uint8_t *w = out.ptrw();
- f->get_buffer(w, flen);
- w[flen] = 0; //end f string
- }
+ f->get_buffer(out.ptrw(), flen);
+ out.write[flen] = 0; // string terminator
memdelete(f);
- int ret = mbedtls_pk_parse_key(&pkey, out.ptr(), out.size(), nullptr, 0);
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_parse_public_key(&pkey, out.ptr(), out.size());
+ } else {
+ ret = mbedtls_pk_parse_key(&pkey, out.ptr(), out.size(), nullptr, 0);
+ }
// We MUST zeroize the memory for safety!
mbedtls_platform_zeroize(out.ptrw(), out.size());
- ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing private key '" + itos(ret) + "'.");
+ ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing key '" + itos(ret) + "'.");
+ public_only = p_public_only;
return OK;
}
-Error CryptoKeyMbedTLS::save(String p_path) {
+Error CryptoKeyMbedTLS::save(String p_path, bool p_public_only) {
FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot save CryptoKeyMbedTLS file '" + p_path + "'.");
unsigned char w[16000];
memset(w, 0, sizeof(w));
- int ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_write_pubkey_pem(&pkey, w, sizeof(w));
+ } else {
+ ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ }
if (ret != 0) {
memdelete(f);
- memset(w, 0, sizeof(w)); // Zeroize anything we might have written.
+ mbedtls_platform_zeroize(w, sizeof(w)); // Zeroize anything we might have written.
ERR_FAIL_V_MSG(FAILED, "Error writing key '" + itos(ret) + "'.");
}
size_t len = strlen((char *)w);
f->store_buffer(w, len);
memdelete(f);
- memset(w, 0, sizeof(w)); // Zeroize temporary buffer.
+ mbedtls_platform_zeroize(w, sizeof(w)); // Zeroize temporary buffer.
return OK;
}
+Error CryptoKeyMbedTLS::load_from_string(String p_string_key, bool p_public_only) {
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_parse_public_key(&pkey, (unsigned char *)p_string_key.utf8().get_data(), p_string_key.utf8().size());
+ } else {
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)p_string_key.utf8().get_data(), p_string_key.utf8().size(), nullptr, 0);
+ }
+ ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing key '" + itos(ret) + "'.");
+
+ public_only = p_public_only;
+ return OK;
+}
+
+String CryptoKeyMbedTLS::save_to_string(bool p_public_only) {
+ unsigned char w[16000];
+ memset(w, 0, sizeof(w));
+
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_write_pubkey_pem(&pkey, w, sizeof(w));
+ } else {
+ ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ }
+ if (ret != 0) {
+ mbedtls_platform_zeroize(w, sizeof(w));
+ ERR_FAIL_V_MSG("", "Error saving key '" + itos(ret) + "'.");
+ }
+ String s = String::utf8((char *)w);
+ return s;
+}
+
X509Certificate *X509CertificateMbedTLS::create() {
return memnew(X509CertificateMbedTLS);
}
@@ -108,11 +147,8 @@ Error X509CertificateMbedTLS::load(String p_path) {
int flen = f->get_len();
out.resize(flen + 1);
- {
- uint8_t *w = out.ptrw();
- f->get_buffer(w, flen);
- w[flen] = 0; //end f string
- }
+ f->get_buffer(out.ptrw(), flen);
+ out.write[flen] = 0; // string terminator
memdelete(f);
int ret = mbedtls_x509_crt_parse(&cert, out.ptr(), out.size());
@@ -211,9 +247,8 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
// Use builtin certs only if user did not override it in project settings.
PackedByteArray out;
out.resize(_certs_uncompressed_size + 1);
- uint8_t *w = out.ptrw();
- Compression::decompress(w, _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
- w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
+ Compression::decompress(out.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
+ out.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
#ifdef DEBUG_ENABLED
print_verbose("Loaded builtin certs");
#endif
@@ -228,6 +263,7 @@ Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {
int ret = mbedtls_pk_setup(&(out->pkey), mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
ERR_FAIL_COND_V(ret != 0, nullptr);
ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(out->pkey), mbedtls_ctr_drbg_random, &ctr_drbg, p_bytes, 65537);
+ out->public_only = false;
ERR_FAIL_COND_V(ret != 0, nullptr);
return out;
}
@@ -281,3 +317,75 @@ PackedByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) {
mbedtls_ctr_drbg_random(&ctr_drbg, out.ptrw(), p_bytes);
return out;
}
+
+mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
+ switch (p_hash_type) {
+ case HashingContext::HASH_MD5:
+ r_size = 16;
+ return MBEDTLS_MD_MD5;
+ case HashingContext::HASH_SHA1:
+ r_size = 20;
+ return MBEDTLS_MD_SHA1;
+ case HashingContext::HASH_SHA256:
+ r_size = 32;
+ return MBEDTLS_MD_SHA256;
+ default:
+ r_size = 0;
+ ERR_FAIL_V_MSG(MBEDTLS_MD_NONE, "Invalid hash type.");
+ }
+}
+
+Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) {
+ int size;
+ mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector<uint8_t>(), "Invalid hash type.");
+ ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector<uint8_t>(), "Invalid hash provided. Size must be " + itos(size));
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ ERR_FAIL_COND_V_MSG(key->is_public_only(), Vector<uint8_t>(), "Invalid key provided. Cannot sign with public_only keys.");
+ size_t sig_size = 0;
+ unsigned char buf[MBEDTLS_MPI_MAX_SIZE];
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_sign(&(key->pkey), type, p_hash.ptr(), size, buf, &sig_size, mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while signing: " + itos(ret));
+ out.resize(sig_size);
+ copymem(out.ptrw(), buf, sig_size);
+ return out;
+}
+
+bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) {
+ int size;
+ mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type.");
+ ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size));
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), false, "Invalid key provided.");
+ return mbedtls_pk_verify(&(key->pkey), type, p_hash.ptr(), size, p_signature.ptr(), p_signature.size()) == 0;
+}
+
+Vector<uint8_t> CryptoMbedTLS::encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) {
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ uint8_t buf[1024];
+ size_t size;
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_encrypt(&(key->pkey), p_plaintext.ptr(), p_plaintext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while encrypting: " + itos(ret));
+ out.resize(size);
+ copymem(out.ptrw(), buf, size);
+ return out;
+}
+
+Vector<uint8_t> CryptoMbedTLS::decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) {
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ ERR_FAIL_COND_V_MSG(key->is_public_only(), Vector<uint8_t>(), "Invalid key provided. Cannot decrypt using a public_only key.");
+ uint8_t buf[2048];
+ size_t size;
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_decrypt(&(key->pkey), p_ciphertext.ptr(), p_ciphertext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while decrypting: " + itos(ret));
+ out.resize(size);
+ copymem(out.ptrw(), buf, size);
+ return out;
+}
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 48855d082a..2a446f9d48 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -43,15 +43,19 @@ class SSLContextMbedTLS;
class CryptoKeyMbedTLS : public CryptoKey {
private:
mbedtls_pk_context pkey;
- int locks;
+ int locks = 0;
+ bool public_only = true;
public:
static CryptoKey *create();
static void make_default() { CryptoKey::_create = create; }
static void finalize() { CryptoKey::_create = nullptr; }
- virtual Error load(String p_path);
- virtual Error save(String p_path);
+ virtual Error load(String p_path, bool p_public_only);
+ virtual Error save(String p_path, bool p_public_only);
+ virtual String save_to_string(bool p_public_only);
+ virtual Error load_from_string(String p_string_key, bool p_public_only);
+ virtual bool is_public_only() const { return public_only; };
CryptoKeyMbedTLS() {
mbedtls_pk_init(&pkey);
@@ -102,6 +106,7 @@ private:
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
static X509CertificateMbedTLS *default_certs;
+ mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
public:
static Crypto *create();
@@ -113,6 +118,10 @@ public:
virtual PackedByteArray generate_random_bytes(int p_bytes);
virtual Ref<CryptoKey> generate_rsa(int p_bytes);
virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after);
+ virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key);
+ virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key);
+ virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext);
+ virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext);
CryptoMbedTLS();
~CryptoMbedTLS();
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 7391e8790d..3bbbf29d3b 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -15,7 +15,9 @@ def build_godot_tools(source, target, env):
from .solution_builder import build_solution
- build_solution(env, solution_path, build_config)
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
# No need to copy targets. The GodotTools csproj takes care of copying them.
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 23f01b3cca..80e3b59325 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -191,17 +191,16 @@ def configure(env, env_mono):
env.Append(LIBS=["psapi"])
env.Append(LIBS=["version"])
else:
- mono_lib_name = find_name_in_dir_files(
- mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes
- )
+ mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
- if not mono_lib_name:
+ if not mono_lib_file:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + ".lib")
+ env.Append(LINKFLAGS=mono_lib_file)
else:
- env.Append(LIBS=[mono_lib_name])
+ mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
+ env.Append(LINKFLAGS=mono_lib_file_path)
mono_bin_path = os.path.join(mono_root, "bin")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 7980a86cb3..cd659057ef 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -57,7 +57,6 @@ def configure(env):
def get_doc_classes():
return [
- "@C#",
"CSharpScript",
"GodotSharp",
]
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
deleted file mode 100644
index 83a7fbf02c..0000000000
--- a/modules/mono/doc_classes/@C#.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 1eb3404f9e..e1e9d1381f 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSharpScript" inherits="Script" version="4.0">
<brief_description>
+ A script implemented in the C# programming language (Mono-enabled builds only).
</brief_description>
<description>
+ This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds.
+ See also [GodotSharp].
</description>
<tutorials>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
<return type="Variant">
</return>
<description>
+ Returns a new instance of the script.
</description>
</method>
</methods>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 19a08d2036..417f8ac704 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GodotSharp" inherits="Object" version="4.0">
<brief_description>
+ Bridge between Godot and the Mono runtime (Mono-enabled builds only).
</brief_description>
<description>
+ This class is a bridge between Godot and the Mono runtime. It exposes several low-level operations and is only available in Mono-enabled Godot builds.
+ See also [CSharpScript].
</description>
<tutorials>
</tutorials>
@@ -11,26 +14,30 @@
<return type="void">
</return>
<description>
- Attaches the current thread to the mono runtime.
+ Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
<return type="void">
</return>
<description>
- Detaches the current thread from the mono runtime.
+ Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
<return type="int">
</return>
<description>
+ Returns the current MonoDomain ID.
+ [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="get_scripts_domain_id">
<return type="int">
</return>
<description>
+ Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
+ [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="is_domain_finalizing_for_unload">
@@ -39,26 +46,28 @@
<argument index="0" name="domain_id" type="int">
</argument>
<description>
- Returns whether the domain is being finalized.
+ Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_shutting_down">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
</description>
</method>
<method name="is_scripts_domain_loaded">
<return type="bool">
</return>
<description>
- Returns whether the scripts domain is loaded.
+ Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
</description>
</method>
</methods>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 7ab5c5fc59..012b69032e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -25,8 +25,9 @@ namespace GodotTools.Core
bool rooted = path.IsAbsolutePath();
path = path.Replace('\\', '/');
+ path = path[path.Length - 1] == '/' ? path.Substring(0, path.Length - 1) : path;
- string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index d069651dd3..572c541412 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -19,9 +19,12 @@ namespace GodotTools.IdeMessaging
private readonly string identity;
private string MetaFilePath { get; }
+ private DateTime? metaFileModifiedTime;
private GodotIdeMetadata godotIdeMetadata;
private readonly FileSystemWatcher fsWatcher;
+ public string GodotEditorExecutablePath => godotIdeMetadata.EditorExecutablePath;
+
private readonly IMessageHandler messageHandler;
private Peer peer;
@@ -123,7 +126,7 @@ namespace GodotTools.IdeMessaging
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
// FileSystemWatcher requires an existing directory
- if (!File.Exists(projectMetadataDir))
+ if (!Directory.Exists(projectMetadataDir))
Directory.CreateDirectory(projectMetadataDir);
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
@@ -142,6 +145,13 @@ namespace GodotTools.IdeMessaging
if (!File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null && metadata != godotIdeMetadata)
@@ -173,6 +183,13 @@ namespace GodotTools.IdeMessaging
if (IsConnected || !File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null)
@@ -185,7 +202,8 @@ namespace GodotTools.IdeMessaging
private GodotIdeMetadata? ReadMetadataFile()
{
- using (var reader = File.OpenText(MetaFilePath))
+ using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ using (var reader = new StreamReader(fileStream))
{
string portStr = reader.ReadLine();
@@ -272,6 +290,7 @@ namespace GodotTools.IdeMessaging
// ReSharper disable once UnusedMember.Global
public async void Start()
{
+ fsWatcher.Created += OnMetaFileChanged;
fsWatcher.Changed += OnMetaFileChanged;
fsWatcher.Deleted += OnMetaFileDeleted;
fsWatcher.EnableRaisingEvents = true;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index 67815959a6..dad6b9ae7a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.2</LangVersion>
<PackageId>GodotTools.IdeMessaging</PackageId>
- <Version>1.1.0</Version>
+ <Version>1.1.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Authors>Godot Engine contributors</Authors>
<Company />
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index a4e86d6177..10d7e1898e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -105,49 +105,45 @@ namespace GodotTools.IdeMessaging
try
{
- try
+ if (msg.Kind == MessageKind.Request)
{
- if (msg.Kind == MessageKind.Request)
- {
- var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
- await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
- }
- else if (msg.Kind == MessageKind.Response)
- {
- ResponseAwaiter responseAwaiter;
+ var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
+ await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
+ }
+ else if (msg.Kind == MessageKind.Response)
+ {
+ ResponseAwaiter responseAwaiter;
- using (await requestsSem.UseAsync())
+ using (await requestsSem.UseAsync())
+ {
+ if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
{
- if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
- {
- Logger.LogError($"Received unexpected response: {msg.Id}");
- return;
- }
-
- responseAwaiter = queue.Dequeue();
+ Logger.LogError($"Received unexpected response: {msg.Id}");
+ return;
}
- responseAwaiter.SetResult(msg.Content);
- }
- else
- {
- throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
+ responseAwaiter = queue.Dequeue();
}
+
+ responseAwaiter.SetResult(msg.Content);
}
- catch (Exception e)
+ else
{
- Logger.LogError($"Message handler for '{msg}' failed with exception", e);
+ throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
}
}
catch (Exception e)
{
- Logger.LogError($"Exception thrown from message handler. Message: {msg}", e);
+ Logger.LogError($"Message handler for '{msg}' failed with exception", e);
}
}
}
catch (Exception e)
{
- Logger.LogError("Unhandled exception in the peer loop", e);
+ if (!IsDisposed || !(e is SocketException || e.InnerException is SocketException))
+ {
+ Logger.LogError("Unhandled exception in the peer loop", e);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
index 1dd4f852e5..e93db9377b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
@@ -67,6 +67,19 @@ namespace GodotTools.IdeMessaging.Requests
{
}
+ public sealed class StopPlayRequest : Request
+ {
+ public new const string Id = "StopPlay";
+
+ public StopPlayRequest() : base(Id)
+ {
+ }
+ }
+
+ public sealed class StopPlayResponse : Response
+ {
+ }
+
public sealed class DebugPlayRequest : Request
{
public string DebuggerHost { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
new file mode 100644
index 0000000000..5b3ed0b1b7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net472</TargetFramework>
+ <LangVersion>7.2</LangVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="EnvDTE" Version="8.0.2" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
new file mode 100644
index 0000000000..affb2a47e7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text.RegularExpressions;
+using EnvDTE;
+
+namespace GodotTools.OpenVisualStudio
+{
+ internal static class Program
+ {
+ [DllImport("ole32.dll")]
+ private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);
+
+ [DllImport("ole32.dll")]
+ private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
+
+ [DllImport("user32.dll")]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ private static void ShowHelp()
+ {
+ Console.WriteLine("Opens the file(s) in a Visual Studio instance that is editing the specified solution.");
+ Console.WriteLine("If an existing instance for the solution is not found, a new one is created.");
+ Console.WriteLine();
+ Console.WriteLine("Usage:");
+ Console.WriteLine(@" GodotTools.OpenVisualStudio.exe solution [file[;line[;col]]...]");
+ Console.WriteLine();
+ Console.WriteLine("Lines and columns begin at one. Zero or lower will result in an error.");
+ Console.WriteLine("If a line is specified but a column is not, the line is selected in the text editor.");
+ }
+
+ // STAThread needed, otherwise CoRegisterMessageFilter may return CO_E_NOT_SUPPORTED.
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
+ {
+ ShowHelp();
+ return 0;
+ }
+
+ string solutionFile = NormalizePath(args[0]);
+
+ var dte = FindInstanceEditingSolution(solutionFile);
+
+ if (dte == null)
+ {
+ // Open a new instance
+
+ var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
+ dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ dte.UserControl = true;
+
+ try
+ {
+ dte.Solution.Open(solutionFile);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("Solution.Open: Invalid path or file not found");
+ return 1;
+ }
+
+ dte.MainWindow.Visible = true;
+ }
+
+ MessageFilter.Register();
+
+ try
+ {
+ // Open files
+
+ for (int i = 1; i < args.Length; i++)
+ {
+ // Both the line number and the column begin at one
+
+ string[] fileArgumentParts = args[i].Split(';');
+
+ string filePath = NormalizePath(fileArgumentParts[0]);
+
+ try
+ {
+ dte.ItemOperations.OpenFile(filePath);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("ItemOperations.OpenFile: Invalid path or file not found");
+ return 1;
+ }
+
+ if (fileArgumentParts.Length > 1)
+ {
+ if (int.TryParse(fileArgumentParts[1], out int line))
+ {
+ var textSelection = (TextSelection)dte.ActiveDocument.Selection;
+
+ if (fileArgumentParts.Length > 2)
+ {
+ if (int.TryParse(fileArgumentParts[2], out int column))
+ {
+ textSelection.MoveToLineAndOffset(line, column);
+ }
+ else
+ {
+ Console.Error.WriteLine("The column part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ else
+ {
+ textSelection.GotoLine(line, Select: true);
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("The line part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ }
+ }
+ finally
+ {
+ var mainWindow = dte.MainWindow;
+ mainWindow.Activate();
+ SetForegroundWindow(new IntPtr(mainWindow.HWnd));
+
+ MessageFilter.Revoke();
+ }
+
+ return 0;
+ }
+
+ private static DTE FindInstanceEditingSolution(string solutionPath)
+ {
+ if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
+ return null;
+
+ try
+ {
+ pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
+ ppenumMoniker.Reset();
+
+ var moniker = new IMoniker[1];
+
+ while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
+ {
+ string ppszDisplayName;
+
+ CreateBindCtx(0, out IBindCtx ppbc);
+
+ try
+ {
+ moniker[0].GetDisplayName(ppbc, null, out ppszDisplayName);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(ppbc);
+ }
+
+ if (ppszDisplayName == null)
+ continue;
+
+ // The digits after the colon are the process ID
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ continue;
+
+ if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
+ {
+ if (ppunkObject is DTE dte && dte.Solution.FullName.Length > 0)
+ {
+ if (NormalizePath(dte.Solution.FullName) == solutionPath)
+ return dte;
+ }
+ }
+ }
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(pprot);
+ }
+
+ return null;
+ }
+
+ static string NormalizePath(string path)
+ {
+ return new Uri(Path.GetFullPath(path)).LocalPath
+ .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ .ToUpperInvariant();
+ }
+
+ #region MessageFilter. See: http: //msdn.microsoft.com/en-us/library/ms228772.aspx
+
+ private class MessageFilter : IOleMessageFilter
+ {
+ // Class containing the IOleMessageFilter
+ // thread error-handling functions
+
+ private static IOleMessageFilter _oldFilter;
+
+ // Start the filter
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ int ret = CoRegisterMessageFilter(newFilter, out _oldFilter);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ // Done with the filter, close it
+ public static void Revoke()
+ {
+ int ret = CoRegisterMessageFilter(_oldFilter, out _);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ //
+ // IOleMessageFilter functions
+ // Handle incoming thread requests
+ int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
+ {
+ // Return the flag SERVERCALL_ISHANDLED
+ return 0;
+ }
+
+ // Thread call was rejected, so try again.
+ int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // flag = SERVERCALL_RETRYLATER
+ {
+ // Retry the thread call immediately if return >= 0 & < 100
+ return 99;
+ }
+
+ // Too busy; cancel call
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
+ {
+ // Return the flag PENDINGMSG_WAITDEFPROCESS
+ return 2;
+ }
+
+ // Implement the IOleMessageFilter interface
+ [DllImport("ole32.dll")]
+ private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index fb2beb6995..679d5bb444 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -12,6 +12,11 @@ namespace GodotTools.ProjectEditor
private const string CoreApiProjectName = "GodotSharp";
private const string EditorApiProjectName = "GodotSharpEditor";
+ public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
+ public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}";
+
+ public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}";
+
public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
{
string path = Path.Combine(dir, name + ".csproj");
@@ -19,6 +24,7 @@ namespace GodotTools.ProjectEditor
ProjectPropertyGroupElement mainGroup;
var root = CreateLibraryProject(name, "Debug", out mainGroup);
+ mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);
mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 069a1edaa3..8774b4ee31 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -165,6 +165,21 @@ namespace GodotTools.ProjectEditor
return result.ToArray();
}
+ public static void EnsureHasProjectTypeGuids(MSBuildProject project)
+ {
+ var root = project.Root;
+
+ bool found = root.PropertyGroups.Any(pg =>
+ string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids"));
+
+ if (found)
+ return;
+
+ root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids);
+
+ project.HasUnsavedChanges = true;
+ }
+
/// Simple function to make sure the Api assembly references are configured correctly
public static void FixApiHintPath(MSBuildProject project)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index f6147eb5bb..ba5379e562 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj", "{92600954-25F0-4291-8E11-1FEE9FC4BE20}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -37,5 +39,9 @@ Global
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index 3cf495f025..3de3d8d318 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -20,8 +20,8 @@ namespace GodotTools
private ItemList buildTabsList;
private TabContainer buildTabs;
- private ToolButton warningsBtn;
- private ToolButton errorsBtn;
+ private Button warningsBtn;
+ private Button errorsBtn;
private Button viewLogBtn;
private void _UpdateBuildTabsList()
@@ -285,7 +285,7 @@ namespace GodotTools
toolBarHBox.AddSpacer(begin: false);
- warningsBtn = new ToolButton
+ warningsBtn = new Button
{
Text = "Warnings".TTR(),
ToggleMode = true,
@@ -296,7 +296,7 @@ namespace GodotTools
warningsBtn.Toggled += _WarningsToggled;
toolBarHBox.AddChild(warningsBtn);
- errorsBtn = new ToolButton
+ errorsBtn = new Button
{
Text = "Errors".TTR(),
ToggleMode = true,
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
index a1a69334e3..837c8adddb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
@@ -1,6 +1,6 @@
namespace GodotTools.Build
{
- public enum BuildTool
+ public enum BuildTool : long
{
MsBuildMono,
MsBuildVs,
diff --git a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
index bb218c2f19..90d6eb960e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
@@ -1,6 +1,6 @@
namespace GodotTools
{
- public enum ExternalEditorId
+ public enum ExternalEditorId : long
{
None,
VisualStudio, // TODO (Windows-only)
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index c874025be0..f330f9ed2c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -29,7 +30,7 @@ namespace GodotTools
private AcceptDialog aboutDialog;
private CheckBox aboutDialogCheckBox;
- private ToolButton bottomPanelBtn;
+ private Button bottomPanelBtn;
public GodotIdeManager GodotIdeManager { get; private set; }
@@ -238,7 +239,31 @@ namespace GodotTools
// Not an error. Tells the caller to fallback to the global external editor settings or the built-in editor.
return Error.Unavailable;
case ExternalEditorId.VisualStudio:
- throw new NotSupportedException();
+ {
+ string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+ var args = new List<string>
+ {
+ GodotSharpDirs.ProjectSlnPath,
+ line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath
+ };
+
+ string command = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "GodotTools.OpenVisualStudio.exe");
+
+ try
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+
+ OS.RunProcess(command, args);
+ }
+ catch (Exception e)
+ {
+ GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ }
+
+ break;
+ }
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
case ExternalEditorId.Rider:
@@ -458,6 +483,9 @@ namespace GodotTools
// Apply the other fixes only after configurations have been migrated
+ // Make sure the existing project has the ProjectTypeGuids property (for VisualStudio)
+ ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject);
+
// Make sure the existing project has Api assembly references configured correctly
ProjectUtils.FixApiHintPath(msbuildProject);
@@ -485,7 +513,7 @@ namespace GodotTools
menuPopup.IdPressed += _MenuOptionPressed;
- var buildButton = new ToolButton
+ var buildButton = new Button
{
Text = "Build",
HintTooltip = "Build solution",
@@ -501,7 +529,8 @@ namespace GodotTools
if (OS.IsWindows)
{
- settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
+ settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
+ $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index ba527ca3b5..3f14629b11 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -33,5 +33,7 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <!-- Include it if this is an SCons build targeting Windows, or if it's not an SCons build but we're on Windows -->
+ <ProjectReference Include="..\GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) " />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 32f264d100..17f3339560 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -12,6 +12,7 @@ using GodotTools.IdeMessaging;
using GodotTools.IdeMessaging.Requests;
using GodotTools.IdeMessaging.Utils;
using GodotTools.Internals;
+using GodotTools.Utils;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
using File = System.IO.File;
@@ -307,6 +308,11 @@ namespace GodotTools.Ides
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
return await HandleDebugPlay(request);
},
+ [StopPlayRequest.Id] = async (peer, content) =>
+ {
+ var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body);
+ return await HandleStopPlay(request);
+ },
[ReloadScriptsRequest.Id] = async (peer, content) =>
{
_ = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
@@ -343,6 +349,12 @@ namespace GodotTools.Ides
return Task.FromResult<Response>(new DebugPlayResponse());
}
+ private static Task<Response> HandleStopPlay(StopPlayRequest request)
+ {
+ DispatchToMainThread(Internal.EditorRunStop);
+ return Task.FromResult<Response>(new StopPlayResponse());
+ }
+
private static Task<Response> HandleReloadScripts()
{
DispatchToMainThread(Internal.ScriptEditorDebugger_ReloadScripts);
@@ -351,8 +363,13 @@ namespace GodotTools.Ides
private static async Task<Response> HandleCodeCompletionRequest(CodeCompletionRequest request)
{
+ // This is needed if the "resource path" part of the path is case insensitive.
+ // However, it doesn't fix resource loading if the rest of the path is also case insensitive.
+ string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
+
var response = new CodeCompletionResponse {Kind = request.Kind, ScriptFile = request.ScriptFile};
- response.Suggestions = await Task.Run(() => Internal.CodeCompletionRequest(response.Kind, response.ScriptFile));
+ response.Suggestions = await Task.Run(() =>
+ Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
return response;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
index 7fb087467f..569f27649f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
@@ -13,9 +13,9 @@ namespace GodotTools.Internals
public string Name { get; }
public string Namespace { get; }
public bool Nested { get; }
- public int BaseCount { get; }
+ public long BaseCount { get; }
- public ClassDecl(string name, string @namespace, bool nested, int baseCount)
+ public ClassDecl(string name, string @namespace, bool nested, long baseCount)
{
Name = name;
Namespace = @namespace;
@@ -45,7 +45,7 @@ namespace GodotTools.Internals
(string)classDeclDict["name"],
(string)classDeclDict["namespace"],
(bool)classDeclDict["nested"],
- (int)classDeclDict["base_count"]
+ (long)classDeclDict["base_count"]
));
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
new file mode 100644
index 0000000000..c6724ccaf7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+using Godot;
+using GodotTools.Core;
+using JetBrains.Annotations;
+
+namespace GodotTools.Utils
+{
+ public static class FsPathUtils
+ {
+ private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
+
+ private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
+ {
+ // This won't work for Linux/macOS case insensitive file systems, but it's enough for our current problems
+ bool caseSensitive = !OS.IsWindows;
+
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.StartsWith(parentPathNorm,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool PathStartsWith(this string childPath, string parentPath)
+ {
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
+ }
+
+ [CanBeNull]
+ public static string LocalizePathWithCaseChecked(string path)
+ {
+ string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
+ return null;
+
+ string result = "res://" + pathNorm.Substring(resourcePathNorm.Length);
+
+ // Remove the last separator we added
+ return result.Substring(0, result.Length - 1);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
new file mode 100644
index 0000000000..20b11a48dd
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+namespace Godot
+{
+ public partial class SceneTree
+ {
+ public Array<T> GetNodesInGroup<T>(StringName group) where T : class
+ {
+ return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T)));
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index b5ac124c9a..06ec2483c8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -53,6 +53,7 @@
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
+ <Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index ee99a300b9..f6999d01fb 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -35,6 +35,7 @@
#include "gd_glue.h"
#include "nodepath_glue.h"
#include "rid_glue.h"
+#include "scene_tree_glue.h"
#include "string_glue.h"
#include "string_name_glue.h"
@@ -50,6 +51,7 @@ void godot_register_glue_header_icalls() {
godot_register_object_icalls();
godot_register_rid_icalls();
godot_register_string_icalls();
+ godot_register_scene_tree_icalls();
}
// Used by the generated glue
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
new file mode 100644
index 0000000000..bea9544b08
--- /dev/null
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* scene_tree_glue.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_tree_glue.h"
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/class_db.h"
+#include "modules/mono/csharp_script.h"
+#include "modules/mono/mono_gd/gd_mono_utils.h"
+#include "scene/main/node.h"
+
+Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype) {
+ List<Node *> nodes;
+ Array ret;
+
+ // Retrieve all the nodes in the group
+ ptr->get_nodes_in_group(*group, &nodes);
+
+ // No need to bother if the group is empty
+ if (!nodes.empty()) {
+ MonoType *elem_type = mono_reflection_type_get_type(refltype);
+ MonoClass *mono_class = mono_class_from_mono_type(elem_type);
+ GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
+
+ if (klass == GDMonoUtils::get_class_native_base(klass)) {
+ // If we're trying to get native objects, just check the inheritance list
+ StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass);
+ for (int i = 0; i < nodes.size(); ++i) {
+ if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name))
+ ret.push_back(nodes[i]);
+ }
+ } else {
+ // If we're trying to get csharpscript instances, get the mono object and compare the classes
+ for (int i = 0; i < nodes.size(); ++i) {
+ CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
+
+ if (si != nullptr) {
+ MonoObject *obj = si->get_mono_object();
+ if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
+ ret.push_back(nodes[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return memnew(Array(ret));
+}
+
+void godot_register_scene_tree_icalls() {
+ mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic);
+}
+
+#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/scene_tree_glue.h b/modules/mono/glue/scene_tree_glue.h
new file mode 100644
index 0000000000..e9af35a30b
--- /dev/null
+++ b/modules/mono/glue/scene_tree_glue.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* scene_tree_glue.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_TREE_GLUE_H
+#define SCENE_TREE_GLUE_H
+
+#ifdef MONO_GLUE_ENABLED
+
+#include "core/array.h"
+#include "core/string_name.h"
+#include "scene/main/scene_tree.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype);
+
+// Register internal calls
+
+void godot_register_scene_tree_icalls();
+
+#endif // MONO_GLUE_ENABLED
+
+#endif // SCENE_TREE_GLUE_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 085062261d..158742846b 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -624,8 +624,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_BOOLEAN(val);
}
case Variant::INT: {
- int32_t val = p_var->operator signed int();
- return BOX_INT32(val);
+ int64_t val = p_var->operator int64_t();
+ return BOX_INT64(val);
}
case Variant::FLOAT: {
#ifdef REAL_T_IS_DOUBLE
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 1b4fe68582..e0cf916a01 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -75,7 +75,6 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
- Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
index bd459ca344..b15479797c 100644
--- a/modules/visual_script/config.py
+++ b/modules/visual_script/config.py
@@ -8,7 +8,6 @@ def configure(env):
def get_doc_classes():
return [
- "@VisualScript",
"VisualScriptBasicTypeConstant",
"VisualScriptBuiltinFunc",
"VisualScriptClassConstant",
diff --git a/modules/visual_script/doc_classes/@VisualScript.xml b/modules/visual_script/doc_classes/@VisualScript.xml
deleted file mode 100644
index a2b966bfbb..0000000000
--- a/modules/visual_script/doc_classes/@VisualScript.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@VisualScript" version="4.0">
- <brief_description>
- Built-in visual script functions.
- </brief_description>
- <description>
- A list of built-in visual script functions, see [VisualScriptBuiltinFunc] and [VisualScript].
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>