diff options
76 files changed, 1713 insertions, 163 deletions
diff --git a/.travis.yml b/.travis.yml index a5217c1d9c..a8bc8289e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ matrix: - mono packages: - &linux_deps [libasound2-dev, libfreetype6-dev, libgl1-mesa-dev, libglu1-mesa-dev, libx11-dev, libxcursor-dev, libxi-dev, libxinerama-dev, libxrandr-dev] - - &linux_mono_deps [mono-devel, msbuild] + - &linux_mono_deps [mono-devel, msbuild, nuget] coverity_scan: project: diff --git a/SConstruct b/SConstruct index f8c1c82961..999021e49a 100644 --- a/SConstruct +++ b/SConstruct @@ -544,7 +544,7 @@ if 'env' in locals(): [os.remove(f) for f in files] def file_list(self): - if self.path == None: + if self.path is None: # Nothing to do return [] # Gather a list of (filename, (size, atime)) within the @@ -569,7 +569,7 @@ if 'env' in locals(): if sum > self.limit: mark = i break - if mark == None: + if mark is None: return [] else: return [x[0] for x in file_stat[mark:]] diff --git a/core/color.cpp b/core/color.cpp index 55dd1ec6b9..ac314417ec 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -468,7 +468,7 @@ String Color::to_html(bool p_alpha) const { return txt; } -Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { p_h = Math::fmod(p_h * 360.0f, 360.0f); if (p_h < 0.0) diff --git a/core/color.h b/core/color.h index add61343ff..d6ad5f91c5 100644 --- a/core/color.h +++ b/core/color.h @@ -194,7 +194,7 @@ struct Color { static bool html_is_valid(const String &p_color); static Color named(const String &p_name); String to_html(bool p_alpha = true) const; - Color from_hsv(float p_h, float p_s, float p_v, float p_a); + Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; static Color from_rgbe9995(uint32_t p_color); _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys diff --git a/doc/tools/doc_merge.py b/doc/tools/doc_merge.py index 57ac4bdcdd..496d5dcb74 100644 --- a/doc/tools/doc_merge.py +++ b/doc/tools/doc_merge.py @@ -82,7 +82,7 @@ def find_signal_descr(old_class, name): def find_constant_descr(old_class, name): - if (old_class == None): + if (old_class is None): return None constants = old_class.find("constants") if(constants != None and len(list(constants)) > 0): diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index b3d6d32c26..7b7cc52547 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -375,7 +375,7 @@ def make_method( event=False, pp=None ): - if (declare or pp == None): + if (declare or pp is None): t = '- ' else: t = "" @@ -405,7 +405,7 @@ def make_method( t += 'void' t += ' ' - if declare or pp == None: + if declare or pp is None: s = '**' + method_data.attrib['name'] + '** ' else: @@ -577,7 +577,7 @@ def make_rst_class(node): make_method(f, name, m, True, True) f.write('\n') d = m.find('description') - if d == None or d.text.strip() == '': + if d is None or d.text.strip() == '': continue f.write(rstize_text(d.text.strip(), name)) f.write("\n\n") @@ -676,7 +676,7 @@ def make_rst_class(node): make_method(f, name, m, True) f.write('\n') d = m.find('description') - if d == None or d.text.strip() == '': + if d is None or d.text.strip() == '': continue f.write(rstize_text(d.text.strip(), name)) f.write("\n\n") diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index b990384949..79d4eb2243 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -148,7 +148,10 @@ void main() { vec4 color = color_interp; +#if !defined(COLOR_USED) + //default behavior, texture by color color *= texture2D(color_texture, uv_interp); +#endif #ifdef SCREEN_UV_USED vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 1a6188862f..218063566a 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -144,6 +144,17 @@ String EditorExportPreset::get_include_filter() const { return include_filter; } +void EditorExportPreset::set_export_path(const String &p_path) { + + export_path = p_path; + EditorExport::singleton->save_presets(); +} + +String EditorExportPreset::get_export_path() const { + + return export_path; +} + void EditorExportPreset::set_exclude_filter(const String &p_exclude) { exclude_filter = p_exclude; @@ -213,6 +224,7 @@ String EditorExportPreset::get_custom_features() const { EditorExportPreset::EditorExportPreset() { + export_path = ""; export_filter = EXPORT_ALL_RESOURCES; runnable = false; } @@ -1034,6 +1046,7 @@ void EditorExport::_save() { } config->set_value(section, "include_filter", preset->get_include_filter()); config->set_value(section, "exclude_filter", preset->get_exclude_filter()); + config->set_value(section, "export_path", preset->get_export_path()); config->set_value(section, "patch_list", preset->get_patches()); String option_section = "preset." + itos(i) + ".options"; @@ -1189,6 +1202,7 @@ void EditorExport::load_config() { preset->set_include_filter(config->get_value(section, "include_filter")); preset->set_exclude_filter(config->get_value(section, "exclude_filter")); + preset->set_export_path(config->get_value(section, "export_path", "")); Vector<String> patch_list = config->get_value(section, "patch_list"); diff --git a/editor/editor_export.h b/editor/editor_export.h index b4ee5b89e7..d1165fd042 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -57,6 +57,7 @@ private: ExportFilter export_filter; String include_filter; String exclude_filter; + String export_path; String exporter; Set<String> selected_files; @@ -114,6 +115,9 @@ public: void set_custom_features(const String &p_custom_features); String get_custom_features() const; + void set_export_path(const String &p_path); + String get_export_path() const; + const List<PropertyInfo> &get_properties() const { return properties; } EditorExportPreset(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 3899fcf83f..7155905472 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4972,7 +4972,7 @@ EditorNode::EditorNode() { dock_vb->add_child(dock_hb); dock_select = memnew(Control); - dock_select->set_custom_minimum_size(Size2(64, 0) * EDSCALE); + dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE); dock_select->connect("gui_input", this, "_dock_select_input"); dock_select->connect("draw", this, "_dock_select_draw"); dock_select->connect("mouse_exited", this, "_dock_popup_exit"); @@ -4983,7 +4983,7 @@ EditorNode::EditorNode() { dock_select_rect_over = -1; dock_popup_selected = -1; for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->set_custom_minimum_size(Size2(0, 64) * EDSCALE); + dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE); dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL); dock_slot[i]->set_popup(dock_select_popup); dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i)); @@ -5499,8 +5499,8 @@ EditorNode::EditorNode() { right_r_vsplit->hide(); // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize - left_r_hsplit->set_split_offset(100 * EDSCALE); - main_hsplit->set_split_offset(-100 * EDSCALE); + left_r_hsplit->set_split_offset(70 * EDSCALE); + main_hsplit->set_split_offset(-70 * EDSCALE); // Define corresponding default layout @@ -5515,8 +5515,8 @@ EditorNode::EditorNode() { for (int i = 0; i < vsplits.size(); i++) default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0); default_layout->set_value(docks_section, "dock_hsplit_1", 0); - default_layout->set_value(docks_section, "dock_hsplit_2", 40 * EDSCALE); - default_layout->set_value(docks_section, "dock_hsplit_3", -40 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_2", 70 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_3", -70 * EDSCALE); default_layout->set_value(docks_section, "dock_hsplit_4", 0); _update_layouts_menu(); diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index e65a697857..40e3bb5be2 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -108,7 +108,7 @@ void BakedLightmapEditorPlugin::_bind_methods() { BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake Lightmaps")); bake->hide(); diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h index a32b573851..8d3b5b1dd6 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.h +++ b/editor/plugins/baked_lightmap_editor_plugin.h @@ -42,7 +42,7 @@ class BakedLightmapEditorPlugin : public EditorPlugin { BakedLightmap *lightmap; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp index 06da64b181..5fc5cad1ef 100644 --- a/editor/plugins/gi_probe_editor_plugin.cpp +++ b/editor/plugins/gi_probe_editor_plugin.cpp @@ -90,7 +90,7 @@ void GIProbeEditorPlugin::_bind_methods() { GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake GI Probe")); bake->hide(); diff --git a/editor/plugins/gi_probe_editor_plugin.h b/editor/plugins/gi_probe_editor_plugin.h index 017e9bd743..1b3b63f227 100644 --- a/editor/plugins/gi_probe_editor_plugin.h +++ b/editor/plugins/gi_probe_editor_plugin.h @@ -42,7 +42,7 @@ class GIProbeEditorPlugin : public EditorPlugin { GIProbe *gi_probe; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index 186e66f980..8df40232b0 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -350,8 +350,6 @@ ItemListEditor::ItemListEditor() { selected_idx = -1; - add_child(memnew(VSeparator)); - toolbar_button = memnew(ToolButton); toolbar_button->set_text(TTR("Items")); add_child(toolbar_button); diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 29a780961e..4a98aa411d 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -39,7 +39,7 @@ void BackgroundProgress::_add_task(const String &p_task, const String &p_label, _THREAD_SAFE_METHOD_ ERR_FAIL_COND(tasks.has(p_task)); - Task t; + BackgroundProgress::Task t; t.hb = memnew(HBoxContainer); Label *l = memnew(Label); l->set_text(p_label + " "); @@ -112,7 +112,7 @@ void BackgroundProgress::add_task(const String &p_task, const String &p_label, i void BackgroundProgress::task_step(const String &p_task, int p_step) { //this code is weird, but it prevents deadlock. - bool no_updates; + bool no_updates = true; { _THREAD_SAFE_METHOD_ no_updates = updates.empty(); @@ -167,7 +167,7 @@ void ProgressDialog::_popup() { void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { ERR_FAIL_COND(tasks.has(p_task)); - Task t; + ProgressDialog::Task t; t.vb = memnew(VBoxContainer); VBoxContainer *vb2 = memnew(VBoxContainer); t.vb->add_margin_child(p_label, vb2); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 3ec49e187f..d2dccdb425 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -50,6 +50,7 @@ void ProjectExportDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); connect("confirmed", this, "_export_pck_zip"); custom_feature_display->get_parent_control()->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -58,6 +59,7 @@ void ProjectExportDialog::_notification(int p_what) { EditorSettings::get_singleton()->set("interface/dialogs/export_bounds", get_rect()); } break; case NOTIFICATION_THEME_CHANGED: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); Control *panel = custom_feature_display->get_parent_control(); if (panel) @@ -171,6 +173,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { runnable->set_disabled(true); parameters->edit(NULL); presets->unselect_all(); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); patches->clear(); @@ -188,6 +191,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { sections->show(); name->set_editable(true); + duplicate_preset->set_disabled(false); delete_preset->set_disabled(false); name->set_text(current->get_name()); runnable->set_disabled(false); @@ -428,6 +432,61 @@ void ProjectExportDialog::_name_changed(const String &p_string) { _update_presets(); } +void ProjectExportDialog::_duplicate_preset() { + + Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); + if (current.is_null()) + return; + + Ref<EditorExportPreset> preset = current->get_platform()->create_preset(); + ERR_FAIL_COND(!preset.is_valid()); + + String name = current->get_name() + "" + itos(1); + bool make_runnable = true; + int attempt = 2; + while (true) { + + bool valid = true; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); + if (p->get_platform() == preset->get_platform() && p->is_runnable()) { + make_runnable = false; + } + if (p->get_name() == name) { + valid = false; + break; + } + } + + if (valid) + break; + + attempt++; + name = current->get_name() + " " + itos(attempt); + } + + preset->set_name(name); + if (make_runnable) + preset->set_runnable(make_runnable); + preset->set_export_filter(current->get_export_filter()); + preset->set_include_filter(current->get_include_filter()); + preset->set_exclude_filter(current->get_exclude_filter()); + Vector<String> list = current->get_patches(); + for (int i = 0; i < list.size(); i++) { + preset->add_patch(list[i]); + } + preset->set_custom_features(current->get_custom_features()); + + for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) { + preset->set(E->get().name, current->get(E->get().name)); + } + + EditorExport::get_singleton()->add_export_preset(preset); + _update_presets(); + _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1); +} + void ProjectExportDialog::_delete_preset() { Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); @@ -745,12 +804,16 @@ void ProjectExportDialog::_export_project() { export_project->set_access(FileDialog::ACCESS_FILESYSTEM); export_project->clear_filters(); - String extension = platform->get_binary_extension(current); - if (extension != String()) { - export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export"); - export_project->set_current_file(default_filename + "." + extension); + if (current->get_export_path() != "") { + export_project->set_current_path(current->get_export_path()); } else { - export_project->set_current_file(default_filename); + String extension = platform->get_binary_extension(current); + if (extension != String()) { + export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export"); + export_project->set_current_file(default_filename + "." + extension); + } else { + export_project->set_current_file(default_filename); + } } // Ensure that signal is connected if previous attempt left it disconnected with _validate_export_path @@ -772,6 +835,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { ERR_FAIL_COND(current.is_null()); Ref<EditorExportPlatform> platform = current->get_platform(); ERR_FAIL_COND(platform.is_null()); + current->set_export_path(p_path); Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); if (err != OK) { @@ -789,6 +853,7 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters); ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed); ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed); + ClassDB::bind_method("_duplicate_preset", &ProjectExportDialog::_duplicate_preset); ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset); ClassDB::bind_method("_delete_preset_confirm", &ProjectExportDialog::_delete_preset_confirm); ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw); @@ -842,6 +907,9 @@ ProjectExportDialog::ProjectExportDialog() { presets->set_drag_forwarding(this); mc->add_child(presets); presets->connect("item_selected", this, "_edit_preset"); + duplicate_preset = memnew(ToolButton); + preset_hb->add_child(duplicate_preset); + duplicate_preset->connect("pressed", this, "_duplicate_preset"); delete_preset = memnew(ToolButton); preset_hb->add_child(delete_preset); delete_preset->connect("pressed", this, "_delete_preset"); @@ -949,6 +1017,7 @@ ProjectExportDialog::ProjectExportDialog() { //disable by default name->set_editable(false); runnable->set_disabled(true); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); parameters->edit(NULL); diff --git a/editor/project_export.h b/editor/project_export.h index 552c6d7faf..a4bca843a8 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -61,6 +61,7 @@ private: TabContainer *sections; MenuButton *add_preset; + Button *duplicate_preset; Button *delete_preset; ItemList *presets; @@ -108,6 +109,7 @@ private: void _name_changed(const String &p_string); void _add_preset(int p_platform); void _edit_preset(int p_index); + void _duplicate_preset(); void _delete_preset(); void _delete_preset_confirm(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index ebc005ff26..f992d4d2e0 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2370,6 +2370,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->set_h_size_flags(SIZE_EXPAND_FILL); filter->set_placeholder(TTR("Filter nodes")); filter_hbc->add_child(filter); + filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); tb = memnew(ToolButton); diff --git a/gles_builders.py b/gles_builders.py index 1e63a53f1a..f9fd6d3fa2 100644 --- a/gles_builders.py +++ b/gles_builders.py @@ -59,11 +59,11 @@ def include_file_in_legacygl_header(filename, header_data, depth): included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": header_data.vertex_included_files += [included_file] - if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None: + if include_file_in_legacygl_header(included_file, header_data, depth + 1) is None: print("Error in file '" + filename + "': #include " + includeline + "could not be found!") elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": header_data.fragment_included_files += [included_file] - if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None: + if include_file_in_legacygl_header(included_file, header_data, depth + 1) is None: print("Error in file '" + filename + "': #include " + includeline + "could not be found!") line = fs.readline() diff --git a/main/input_default.cpp b/main/input_default.cpp index 33c422190d..e8015400af 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -429,6 +429,7 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = p_event->is_action_pressed(E->key()); + action.strength = 0.f; action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); @@ -562,6 +563,7 @@ void InputDefault::action_press(const StringName &p_action) { action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = true; + action.strength = 0.f; action_state[p_action] = action; } @@ -573,6 +575,7 @@ void InputDefault::action_release(const StringName &p_action) { action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.idle_frame = Engine::get_singleton()->get_idle_frames(); action.pressed = false; + action.strength = 0.f; action_state[p_action] = action; } diff --git a/methods.py b/methods.py index 0cf05751a8..111a2347be 100644 --- a/methods.py +++ b/methods.py @@ -330,7 +330,7 @@ def split_lib(self, libname, src_list = None, env_lib = None): list = [] lib_list = [] - if src_list == None: + if src_list is None: src_list = getattr(env, libname + "_sources") if type(env_lib) == type(None): diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index ecde73ae1c..283e9b3bc8 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -306,6 +306,13 @@ bool GDNative::initialize() { #elif defined(UWP_ENABLED) // On UWP we use a relative path from the app String path = lib_path.replace("res://", ""); +#elif defined(OSX_ENABLED) + // On OSX the exported libraries are located under the Frameworks directory. + // So we need to replace the library path. + String path = ProjectSettings::get_singleton()->globalize_path(lib_path); + if (!FileAccess::exists(path)) { + path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file()); + } #else String path = ProjectSettings::get_singleton()->globalize_path(lib_path); #endif diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp index 1fb0ff0500..a30cc09bf6 100644 --- a/modules/gdnative/gdnative/array.cpp +++ b/modules/gdnative/gdnative/array.cpp @@ -318,6 +318,38 @@ void GDAPI godot_array_destroy(godot_array *p_self) { ((Array *)p_self)->~Array(); } +godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep) { + const Array *self = (const Array *)p_self; + godot_array res; + Array *val = (Array *)&res; + memnew_placement(val, Array); + *val = self->duplicate(p_deep); + return res; +} + +godot_variant GDAPI godot_array_max(const godot_array *p_self) { + const Array *self = (const Array *)p_self; + godot_variant v; + Variant *val = (Variant *)&v; + memnew_placement(val, Variant); + *val = self->max(); + return v; +} + +godot_variant GDAPI godot_array_min(const godot_array *p_self) { + const Array *self = (const Array *)p_self; + godot_variant v; + Variant *val = (Variant *)&v; + memnew_placement(val, Variant); + *val = self->min(); + return v; +} + +void GDAPI godot_array_shuffle(godot_array *p_self) { + Array *self = (Array *)p_self; + self->shuffle(); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp index 70d2814577..d88499ade1 100644 --- a/modules/gdnative/gdnative/basis.cpp +++ b/modules/gdnative/gdnative/basis.cpp @@ -282,6 +282,15 @@ godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self return raw_dest; } +godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t) { + godot_basis raw_dest; + Basis *dest = (Basis *)&raw_dest; + const Basis *self = (const Basis *)p_self; + const Basis *b = (const Basis *)p_b; + *dest = self->slerp(*b, p_t); + return raw_dest; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp index 4089f4458a..79f0c71b5e 100644 --- a/modules/gdnative/gdnative/color.cpp +++ b/modules/gdnative/gdnative/color.cpp @@ -116,6 +116,26 @@ godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) { return self->to_rgba32(); } +godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_abgr32(); +} + +godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_abgr64(); +} + +godot_int GDAPI godot_color_to_argb64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_argb64(); +} + +godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self) { + const Color *self = (const Color *)p_self; + return self->to_rgba64(); +} + godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) { const Color *self = (const Color *)p_self; return self->to_argb32(); @@ -156,6 +176,27 @@ godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color return dest; } +godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->darkened(p_amount); + return dest; +} + +godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->from_hsv(p_h, p_s, p_v, p_a); + return dest; +} + +godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount) { + godot_color dest; + const Color *self = (const Color *)p_self; + *((Color *)&dest) = self->lightened(p_amount); + return dest; +} + godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha) { godot_string dest; const Color *self = (const Color *)p_self; diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp index f24facaae8..cf82940e09 100644 --- a/modules/gdnative/gdnative/node_path.cpp +++ b/modules/gdnative/gdnative/node_path.cpp @@ -110,6 +110,15 @@ godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, c return *self == *b; } +godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self) { + const NodePath *self = (const NodePath *)p_self; + godot_node_path res; + NodePath *val = (NodePath *)&res; + memnew_placement(val, NodePath); + *val = self->get_as_property_path(); + return res; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp index ddec77edcd..2594759508 100644 --- a/modules/gdnative/gdnative/quat.cpp +++ b/modules/gdnative/gdnative/quat.cpp @@ -225,6 +225,12 @@ godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self) { return raw_dest; } +void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle) { + Quat *self = (Quat *)p_self; + const Vector3 *axis = (const Vector3 *)p_axis; + self->set_axis_angle(*axis, p_angle); +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp index 54b98fc4e5..5cbc2712c3 100644 --- a/modules/gdnative/gdnative/rect2.cpp +++ b/modules/gdnative/gdnative/rect2.cpp @@ -109,6 +109,27 @@ godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p return dest; } +godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom); + return dest; +} + +godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by); + return dest; +} + +godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self) { + godot_rect2 dest; + const Rect2 *self = (const Rect2 *)p_self; + *((Rect2 *)&dest) = self->abs(); + return dest; +} + godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to) { godot_rect2 dest; const Rect2 *self = (const Rect2 *)p_self; diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 8ca57392a3..0996633b70 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -1285,6 +1285,64 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self) { return self->is_valid_ip_address(); } +godot_string GDAPI godot_string_dedent(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_string result; + String return_value = self->dedent(); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix) { + const String *self = (const String *)p_self; + String *prefix = (String *)p_prefix; + godot_string result; + String return_value = self->trim_prefix(*prefix); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix) { + const String *self = (const String *)p_self; + String *suffix = (String *)p_suffix; + godot_string result; + String return_value = self->trim_suffix(*suffix); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) { + const String *self = (const String *)p_self; + String *chars = (String *)p_chars; + godot_string result; + String return_value = self->rstrip(*chars); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, + const godot_bool p_allow_empty, const godot_int p_maxsplit) { + const String *self = (const String *)p_self; + String *divisor = (String *)p_divisor; + + godot_pool_string_array result; + memnew_placement(&result, PoolStringArray); + PoolStringArray *proxy = (PoolStringArray *)&result; + PoolStringArray::Write proxy_writer = proxy->write(); + Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit); + proxy->resize(tmp_result.size()); + + for (int i = 0; i < tmp_result.size(); i++) { + proxy_writer[i] = tmp_result[i]; + } + + return result; +} + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 16a34a9a33..c5a1fa139e 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -14,6 +14,183 @@ "next": null, "api": [ { + "name": "godot_color_to_abgr32", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_abgr64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_argb64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_to_rgba64", + "return_type": "godot_int", + "arguments": [ + ["const godot_color *", "p_self"] + ] + }, + { + "name": "godot_color_darkened", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_amount"] + ] + }, + { + "name": "godot_color_from_hsv", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_h"], + ["const godot_real", "p_s"], + ["const godot_real", "p_v"], + ["const godot_real", "p_a"] + ] + }, + { + "name": "godot_color_lightened", + "return_type": "godot_color", + "arguments": [ + ["const godot_color *", "p_self"], + ["const godot_real", "p_amount"] + ] + }, + { + "name": "godot_array_duplicate", + "return_type": "godot_array", + "arguments": [ + ["const godot_array *", "p_self"], + ["const godot_bool", "p_deep"] + ] + }, + { + "name": "godot_array_max", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_min", + "return_type": "godot_variant", + "arguments": [ + ["const godot_array *", "p_self"] + ] + }, + { + "name": "godot_array_shuffle", + "return_type": "void", + "arguments": [ + ["godot_array *", "p_self"] + ] + }, + { + "name": "godot_basis_slerp", + "return_type": "godot_basis", + "arguments": [ + ["const godot_basis *", "p_self"], + ["const godot_basis *", "p_b"], + ["const godot_real", "p_t"] + ] + }, + { + "name": "godot_node_path_get_as_property_path", + "return_type": "godot_node_path", + "arguments": [ + ["const godot_node_path *", "p_self"] + ] + }, + { + "name": "godot_quat_set_axis_angle", + "return_type": "void", + "arguments": [ + ["godot_quat *", "p_self"], + ["const godot_vector3 *", "p_axis"], + ["const godot_real", "p_angle"] + ] + }, + { + "name": "godot_rect2_grow_individual", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_real", "p_left"], + ["const godot_real", "p_top"], + ["const godot_real", "p_right"], + ["const godot_real", "p_bottom"] + ] + }, + { + "name": "godot_rect2_grow_margin", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"], + ["const godot_int", "p_margin"], + ["const godot_real", "p_by"] + ] + }, + { + "name": "godot_rect2_abs", + "return_type": "godot_rect2", + "arguments": [ + ["const godot_rect2 *", "p_self"] + ] + }, + { + "name": "godot_string_dedent", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_trim_prefix", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_prefix"] + ] + }, + { + "name": "godot_string_trim_suffix", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_suffix"] + ] + }, + { + "name": "godot_string_rstrip", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_chars"] + ] + }, + { + "name": "godot_string_rsplit", + "return_type": "godot_pool_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_divisor"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, + { "name": "godot_basis_get_quat", "return_type": "godot_quat", "arguments": [ diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h index 1e66d133b9..876b8f8e8f 100644 --- a/modules/gdnative/include/gdnative/array.h +++ b/modules/gdnative/include/gdnative/array.h @@ -130,6 +130,14 @@ godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_vari void GDAPI godot_array_destroy(godot_array *p_self); +godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep); + +godot_variant GDAPI godot_array_max(const godot_array *p_self); + +godot_variant GDAPI godot_array_min(const godot_array *p_self); + +void GDAPI godot_array_shuffle(godot_array *p_self); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h index ebe2b1125b..6128bf3ac3 100644 --- a/modules/gdnative/include/gdnative/basis.h +++ b/modules/gdnative/include/gdnative/basis.h @@ -127,6 +127,8 @@ godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b); +godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h index 1f0ac8354d..3007dbc6e3 100644 --- a/modules/gdnative/include/gdnative/color.h +++ b/modules/gdnative/include/gdnative/color.h @@ -81,6 +81,14 @@ godot_string GDAPI godot_color_as_string(const godot_color *p_self); godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self); +godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self); + +godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self); + +godot_int GDAPI godot_color_to_argb64(const godot_color *p_self); + +godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self); + godot_int GDAPI godot_color_to_argb32(const godot_color *p_self); godot_real GDAPI godot_color_gray(const godot_color *p_self); @@ -93,6 +101,12 @@ godot_color GDAPI godot_color_linear_interpolate(const godot_color *p_self, cons godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over); +godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount); + +godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a); + +godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount); + godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha); godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b); diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h index 2b55e01d13..48fe5b4d3d 100644 --- a/modules/gdnative/include/gdnative/node_path.h +++ b/modules/gdnative/include/gdnative/node_path.h @@ -80,6 +80,8 @@ godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self); godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b); +godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h index b1290f745c..634f486e66 100644 --- a/modules/gdnative/include/gdnative/quat.h +++ b/modules/gdnative/include/gdnative/quat.h @@ -109,6 +109,8 @@ godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self); +void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h index 4adcb73e3d..47c15c80bd 100644 --- a/modules/gdnative/include/gdnative/rect2.h +++ b/modules/gdnative/include/gdnative/rect2.h @@ -77,6 +77,12 @@ godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_ve godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by); +godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom); + +godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by); + +godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self); + godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to); godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b); diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index 73245160c1..95ae42a9ec 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -246,6 +246,12 @@ godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self); +godot_string GDAPI godot_string_dedent(const godot_string *p_self); +godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix); +godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix); +godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars); +godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit); + void GDAPI godot_string_destroy(godot_string *p_self); #ifdef __cplusplus diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 9a62b0fdc9..0e5dd9b4cf 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -44,7 +44,7 @@ def make_cs_files_header(src, dst, version_dst): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel + '", ' \ + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' @@ -102,6 +102,75 @@ env_mono = conf.Finish() import os +def find_nuget_unix(): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(): + import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + def find_msbuild_unix(filename): import os.path import sys @@ -176,14 +245,17 @@ def mono_build_solution(source, target, env): import mono_reg_utils as monoreg from shutil import copyfile - framework_path = '' + sln_path = os.path.abspath(str(source[0])) + target_path = os.path.abspath(str(target[0])) + framework_path = '' msbuild_env = os.environ.copy() # Needed when running from Developer Command Prompt for VS if 'PLATFORM' in msbuild_env: del msbuild_env['PLATFORM'] + # Find MSBuild if os.name == 'nt': msbuild_info = find_msbuild_windows() if msbuild_info is None: @@ -213,11 +285,27 @@ def mono_build_solution(source, target, env): print('MSBuild path: ' + msbuild_path) + # Find NuGet + nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + + try: + subprocess.check_call([nuget_path, 'restore', sln_path]) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: NuGet restore failed') + + # Build solution + build_config = 'Release' msbuild_args = [ msbuild_path, - os.path.abspath(str(source[0])), + sln_path, '/p:Configuration=' + build_config, ] @@ -227,20 +315,24 @@ def mono_build_solution(source, target, env): try: subprocess.check_call(msbuild_args, env=msbuild_env) except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools build failed') + raise RuntimeError('GodotSharpTools: Build failed') + + # Copy files - src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) + asm_file = 'GodotSharpTools.dll' if not os.path.isdir(dst_dir): if os.path.exists(dst_dir): raise RuntimeError('Target directory is a file') os.makedirs(dst_dir) - asm_file = 'GodotSharpTools.dll' - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) + # Dependencies + copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) + if env['tools']: output_dir = Dir('#bin').abspath editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 6b3dc01306..a048baf5d7 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -32,6 +32,7 @@ #include <mono/metadata/threads.h> +#include "core/io/json.h" #include "core/os/file_access.h" #include "core/os/os.h" #include "core/os/thread.h" @@ -42,7 +43,6 @@ #include "editor/csharp_project.h" #include "editor/editor_node.h" #include "editor/godotsharp_editor.h" -#include "utils/string_utils.h" #endif #include "godotsharp_dirs.h" @@ -50,6 +50,7 @@ #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" +#include "utils/string_utils.h" #include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -370,6 +371,7 @@ bool CSharpLanguage::supports_builtin_mode() const { return false; } +#ifdef TOOLS_ENABLED static String variant_type_to_managed_name(const String &p_var_type_name) { if (p_var_type_name.empty()) @@ -432,8 +434,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return p_var_type_name; } -String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { -#ifdef TOOLS_ENABLED +String CSharpLanguage::make_function(const String &, const String &p_name, const PoolStringArray &p_args) const { // FIXME // - Due to Godot's API limitation this just appends the function to the end of the file // - Use fully qualified name if there is ambiguity @@ -449,10 +450,12 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name s += ")\n{\n // Replace with function body.\n}\n"; return s; +} #else +String CSharpLanguage::make_function(const String &, const String &, const PoolStringArray &) const { return String(); -#endif } +#endif String CSharpLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED @@ -822,6 +825,49 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { } #endif +void CSharpLanguage::project_assembly_loaded() { + + scripts_metadata.clear(); + + String scripts_metadata_filename = "scripts_metadata."; + +#ifdef TOOLS_ENABLED + scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player"; +#else +#ifdef DEBUG_ENABLED + scripts_metadata_filename += "debug"; +#else + scripts_metadata_filename += "release"; +#endif +#endif + + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename); + + if (FileAccess::exists(scripts_metadata_path)) { + String old_json; + + Error ferr = read_all_file_utf8(scripts_metadata_path, old_json); + ERR_FAIL_COND(ferr != OK); + + Variant old_dict_var; + String err_str; + int err_line; + Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line); + if (json_err != OK) { + ERR_PRINTS("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ")"); + return; + } + + scripts_metadata = old_dict_var.operator Dictionary(); + + print_verbose("Successfully loaded scripts metadata"); + } else { + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_PRINT("Missing scripts metadata file"); + } + } +} + void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("cs"); @@ -2210,10 +2256,27 @@ bool CSharpScript::can_instance() const { #endif #ifdef TOOLS_ENABLED - return valid && (tool || ScriptServer::is_scripting_enabled()); + bool extra_cond = tool || ScriptServer::is_scripting_enabled(); #else - return valid; + bool extra_cond = true; #endif + + // FIXME Need to think this through better. + // For tool scripts, this will never fire if the class is not found. That's because we + // don't know if it's a tool script if we can't find the class to access the attributes. + if (extra_cond && !script_class) { + if (GDMono::get_singleton()->get_project_assembly() == NULL) { + // The project assembly is not loaded + ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); + ERR_FAIL_V(NULL); + } else { + // The project assembly is loaded, but the class could not found + ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); + ERR_FAIL_V(NULL); + } + } + + return valid && extra_cond; } StringName CSharpScript::get_instance_base_type() const { @@ -2319,20 +2382,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { CRASH_COND(!valid); #endif - if (!script_class) { - if (GDMono::get_singleton()->get_project_assembly() == NULL) { - // The project assembly is not loaded - ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); - ERR_FAIL_V(NULL); - } else { - // The project assembly is loaded, but the class could not found - ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); - ERR_FAIL_V(NULL); - } - } - - ERR_FAIL_COND_V(!valid, NULL); - if (native) { String native_name = native->get_name(); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { @@ -2420,7 +2469,23 @@ Error CSharpScript::reload(bool p_keep_state) { GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly(); if (project_assembly) { - script_class = project_assembly->get_object_derived_class(name); + const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path()); + if (script_metadata_var) { + Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"]; + const Variant *namespace_ = script_metadata.getptr("namespace"); + const Variant *class_name = script_metadata.getptr("class_name"); + ERR_FAIL_NULL_V(namespace_, ERR_BUG); + ERR_FAIL_NULL_V(class_name, ERR_BUG); + GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String()); + if (klass) { + bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass); + ERR_FAIL_COND_V(!obj_type, ERR_BUG); + script_class = klass; + } + } else { + // Missing script metadata. Fallback to legacy method + script_class = project_assembly->get_object_derived_class(name); + } valid = script_class != NULL; @@ -2548,29 +2613,14 @@ int CSharpScript::get_member_line(const StringName &p_member) const { Error CSharpScript::load_source_code(const String &p_path) { - PoolVector<uint8_t> sourcef; - Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, err); - - int len = f->get_len(); - sourcef.resize(len + 1); - PoolVector<uint8_t>::Write w = sourcef.write(); - int r = f->get_buffer(w.ptr(), len); - f->close(); - memdelete(f); - ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); - w[len] = 0; - - String s; - if (s.parse_utf8((const char *)w.ptr())) { - - ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); - ERR_FAIL_V(ERR_INVALID_DATA); + Error ferr = read_all_file_utf8(p_path, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + } + ERR_FAIL_V(ferr); } - source = s; - #ifdef TOOLS_ENABLED source_changed_cache = true; #endif diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 8b0a095890..fc604df2a0 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -67,7 +67,7 @@ class CSharpScript : public Script { friend class CSharpInstance; friend class CSharpLanguage; - friend class CSharpScriptDepSort; + friend struct CSharpScriptDepSort; bool tool; bool valid; @@ -275,6 +275,8 @@ class CSharpLanguage : public ScriptLanguage { int lang_idx; + Dictionary scripts_metadata; + public: StringNameCache string_names; @@ -295,6 +297,10 @@ public: void reload_assemblies_if_needed(bool p_soft_reload); #endif + void project_assembly_loaded(); + + _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; } + virtual String get_name() const; /* LANGUAGE FUNCTIONS */ diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index fceb732426..f9e9f41977 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -31,6 +31,9 @@ <Reference Include="System" /> <Reference Include="Microsoft.Build" /> <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL"> + <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="StringExtensions.cs" /> @@ -43,5 +46,8 @@ <Compile Include="Utils\OS.cs" /> <Compile Include="Editor\GodotSharpExport.cs" /> </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs index f00ec5a2ad..647d9ac81d 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs @@ -1,4 +1,5 @@ using System; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -7,7 +8,10 @@ namespace GodotSharpTools.Project { public static bool HasItem(this ProjectRootElement root, string itemType, string include) { - string includeNormalized = include.NormalizePath(); + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + string normalizedInclude = include.NormalizePath(); foreach (var itemGroup in root.ItemGroups) { @@ -16,10 +20,14 @@ namespace GodotSharpTools.Project foreach (var item in itemGroup.Items) { - if (item.ItemType == itemType) + if (item.ItemType != itemType) + continue; + + var glob = Glob.Parse(item.Include.NormalizePath(), globOptions); + + if (glob.IsMatch(normalizedInclude)) { - if (item.Include.NormalizePath() == includeNormalized) - return true; + return true; } } } diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 1d863e6f61..574b711b29 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -6,6 +6,9 @@ namespace GodotSharpTools.Project { public static class ProjectGenerator { + const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; + const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; + public static string GenCoreApiProject(string dir, string[] compileItems) { string path = Path.Combine(dir, CoreApiProject + ".csproj"); @@ -15,6 +18,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); GenAssemblyInfoFile(root, dir, CoreApiProject, new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" }, @@ -39,6 +43,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); GenAssemblyInfoFile(root, dir, EditorApiProject); diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs index 6889ea715f..a13f4fd6ef 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs @@ -1,5 +1,6 @@ -using System; +using System.Collections.Generic; using System.IO; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -10,8 +11,61 @@ namespace GodotSharpTools.Project { var dir = Directory.GetParent(projectPath).FullName; var root = ProjectRootElement.Open(projectPath); - if (root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\"))) + var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\"); + + if (root.AddItemChecked(itemType, normalizedInclude)) root.Save(); } + + private static string[] GetAllFilesRecursive(string rootDirectory, string mask) + { + string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); + + // We want relative paths + for (int i = 0; i < files.Length; i++) { + files[i] = files[i].RelativeToPath(rootDirectory); + } + + return files; + } + + public static string[] GetIncludeFiles(string projectPath, string itemType) + { + var result = new List<string>(); + var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs"); + + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + var root = ProjectRootElement.Open(projectPath); + + foreach (var itemGroup in root.ItemGroups) + { + if (itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + string normalizedInclude = item.Include.NormalizePath(); + + var glob = Glob.Parse(normalizedInclude, globOptions); + + // TODO Check somehow if path has no blog to avoid the following loop... + + foreach (var existingFile in existingFiles) + { + if (glob.IsMatch(existingFile)) + { + result.Add(existingFile); + } + } + } + } + + return result.ToArray(); + } } } diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotSharpTools/StringExtensions.cs index b66c86f8ce..b0436d2f18 100644 --- a/modules/mono/editor/GodotSharpTools/StringExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/StringExtensions.cs @@ -36,7 +36,9 @@ namespace GodotSharpTools public static bool IsAbsolutePath(this string path) { - return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot); + return path.StartsWith("/", StringComparison.Ordinal) || + path.StartsWith("\\", StringComparison.Ordinal) || + path.StartsWith(driveRoot, StringComparison.Ordinal); } public static string CsvEscape(this string value, char delimiter = ',') diff --git a/modules/mono/editor/GodotSharpTools/packages.config b/modules/mono/editor/GodotSharpTools/packages.config new file mode 100644 index 0000000000..2c7cb0bd4b --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 6f223b75a3..ab96356d6d 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -30,11 +30,15 @@ #include "csharp_project.h" +#include "core/io/json.h" #include "core/os/os.h" #include "core/project_settings.h" +#include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" +#include "../utils/string_utils.h" +#include "script_class_parser.h" namespace CSharpProject { @@ -118,4 +122,110 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str ERR_FAIL(); } } + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); + + void *args[2] = { + GDMonoMarshal::mono_string_from_godot(p_project_path), + GDMonoMarshal::mono_string_from_godot("Compile") + }; + + MonoException *exc = NULL; + MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + ERR_FAIL_V(FAILED); + } + + PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret); + PoolStringArray::Read r = project_files.read(); + + Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata(); + Dictionary new_dict; + + for (int i = 0; i < project_files.size(); i++) { + const String &project_file = ("res://" + r[i]).simplify_path(); + + uint64_t modified_time = FileAccess::get_modified_time(project_file); + + const Variant *old_file_var = old_dict.getptr(project_file); + if (old_file_var) { + Dictionary old_file_dict = old_file_var->operator Dictionary(); + + if (old_file_dict["modified_time"].operator uint64_t() == modified_time) { + // No changes so no need to parse again + new_dict[project_file] = old_file_dict; + continue; + } + } + + ScriptClassParser scp; + Error err = scp.parse_file(project_file); + if (err != OK) { + ERR_PRINTS("Parse error: " + scp.get_error()); + ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file); + ERR_FAIL_V(err); + } + + Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes(); + + bool found = false; + Dictionary class_dict; + + String search_name = project_file.get_file().get_basename(); + + for (int j = 0; j < classes.size(); j++) { + const ScriptClassParser::ClassDecl &class_decl = classes[j]; + + if (class_decl.base.size() == 0) + continue; // Does not inherit nor implement anything, so it can't be a script class + + String class_cmp; + + if (class_decl.nested) { + class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1); + } else { + class_cmp = class_decl.name; + } + + if (class_cmp != search_name) + continue; + + class_dict["namespace"] = class_decl.namespace_; + class_dict["class_name"] = class_decl.name; + class_dict["nested"] = class_decl.nested; + + found = true; + break; + } + + if (found) { + Dictionary file_dict; + file_dict["modified_time"] = modified_time; + file_dict["class"] = class_dict; + new_dict[project_file] = file_dict; + } + } + + if (new_dict.size()) { + String json = JSON::print(new_dict, "", false); + + Error ferr; + FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr); + ERR_EXPLAIN("Cannot open file for writing: " + p_output_path); + ERR_FAIL_COND_V(ferr != OK, ferr); + f->store_string(json); + f->flush(); + f->close(); + memdelete(f); + } + + return OK; +} + } // namespace CSharpProject diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h index d852139de0..aeeeff50f0 100644 --- a/modules/mono/editor/csharp_project.h +++ b/modules/mono/editor/csharp_project.h @@ -40,6 +40,9 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>()); void add_item(const String &p_project_path, const String &p_item_type, const String &p_include); + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path); + } // namespace CSharpProject #endif // CSHARP_PROJECT_H diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index b504cfe712..6216213716 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -39,6 +39,7 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" +#include "csharp_project.h" #include "godotsharp_editor.h" #define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)" @@ -268,7 +269,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) @@ -280,8 +281,6 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & Error err = da->copy(assembly_src, assembly_dst); - memdelete(da); - if (err != OK) { show_build_error_dialog("Failed to copy " + assembly_file); return false; @@ -398,6 +397,18 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { bool GodotSharpBuilds::editor_build_callback() { + String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player"); + + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); + ERR_FAIL_COND_V(metadata_err != OK, false); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); + + ERR_EXPLAIN("Failed to copy scripts metadata file"); + ERR_FAIL_COND_V(copy_err != OK, false); + return build_project_blocking("Tools"); } diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 933ede93dc..509ec961fb 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -37,6 +37,7 @@ #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" +#include "csharp_project.h" #include "godotsharp_builds.h" static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() { @@ -86,6 +87,12 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug String build_config = p_debug ? "Debug" : "Release"; + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); + + ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); // Add dependency assemblies @@ -124,7 +131,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { String depend_src_path = E->value(); String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); - ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path)); + ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); } // Mono specific export template extras (data dir) @@ -155,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug } } -bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) { +bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) { FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); ERR_FAIL_COND_V(!f, false); @@ -164,7 +171,7 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d data.resize(f->get_len()); f->get_buffer(data.ptrw(), data.size()); - add_file(p_dst_path, data, false); + add_file(p_dst_path, data, p_remap); return true; } @@ -191,21 +198,21 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c if (has_extension) { path = search_dir.plus_file(ref_name); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true); if (ref_assembly != NULL) break; } } else { path = search_dir.plus_file(ref_name + ".dll"); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); if (ref_assembly != NULL) break; } path = search_dir.plus_file(ref_name + ".exe"); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); if (ref_assembly != NULL) break; } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index f007016578..10d4375567 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -41,7 +41,7 @@ class GodotSharpExport : public EditorExportPlugin { MonoAssemblyName *aname_prealloc; - bool _add_assembly(const String &p_src_path, const String &p_dst_path); + bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false); Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies); diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 0ac59a1be8..d7bfa54aba 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -31,6 +31,8 @@ #include "mono_bottom_panel.h" #include "../csharp_script.h" +#include "../godotsharp_dirs.h" +#include "csharp_project.h" #include "godotsharp_editor.h" MonoBottomPanel *MonoBottomPanel::singleton = NULL; @@ -148,6 +150,10 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); + GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); MonoReloadNode::get_singleton()->restart_reload_timer(); diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/net_solution.cpp index 8bbd376c9a..a000debe52 100644 --- a/modules/mono/editor/net_solution.cpp +++ b/modules/mono/editor/net_solution.cpp @@ -52,7 +52,7 @@ #define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject" -#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU" +#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU" #define PROJECT_PLATFORMS_CONFIG \ "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp new file mode 100644 index 0000000000..bc8eed81cf --- /dev/null +++ b/modules/mono/editor/script_class_parser.cpp @@ -0,0 +1,542 @@ +#include "script_class_parser.h" + +#include "core/map.h" +#include "core/os/os.h" + +#include "../utils/string_utils.h" + +const char *ScriptClassParser::token_names[ScriptClassParser::TK_MAX] = { + "[", + "]", + "{", + "}", + ".", + ":", + ",", + "Symbol", + "Identifier", + "String", + "Number", + "<", + ">", + "EOF", + "Error" +}; + +String ScriptClassParser::get_token_name(ScriptClassParser::Token p_token) { + + ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>"); + return token_names[p_token]; +} + +ScriptClassParser::Token ScriptClassParser::get_token() { + + while (true) { + switch (code[idx]) { + case '\n': { + line++; + idx++; + break; + }; + case 0: { + return TK_EOF; + } break; + case '{': { + idx++; + return TK_CURLY_BRACKET_OPEN; + }; + case '}': { + idx++; + return TK_CURLY_BRACKET_CLOSE; + }; + case '[': { + idx++; + return TK_BRACKET_OPEN; + }; + case ']': { + idx++; + return TK_BRACKET_CLOSE; + }; + case '<': { + idx++; + return TK_OP_LESS; + }; + case '>': { + idx++; + return TK_OP_GREATER; + }; + case ':': { + idx++; + return TK_COLON; + }; + case ',': { + idx++; + return TK_COMMA; + }; + case '.': { + idx++; + return TK_PERIOD; + }; + case '#': { + //compiler directive + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + continue; + } break; + case '/': { + switch (code[idx + 1]) { + case '*': { // block comment + idx += 2; + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated comment"; + error = true; + return TK_ERROR; + } else if (code[idx] == '*' && code[idx + 1] == '/') { + idx += 2; + break; + } else if (code[idx] == '\n') { + line++; + } + + idx++; + } + + } break; + case '/': { // line comment skip + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + + } break; + default: { + value = "/"; + idx++; + return TK_SYMBOL; + } + } + + continue; // a comment + } break; + case '\'': + case '"': { + bool verbatim = idx != 0 && code[idx - 1] == '@'; + + CharType begin_str = code[idx]; + idx++; + String tk_string = String(); + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated String"; + error = true; + return TK_ERROR; + } else if (code[idx] == begin_str) { + if (verbatim && code[idx + 1] == '"') { // `""` is verbatim string's `\"` + idx += 2; // skip next `"` as well + continue; + } + + idx += 1; + break; + } else if (code[idx] == '\\' && !verbatim) { + //escaped characters... + idx++; + CharType next = code[idx]; + if (next == 0) { + error_str = "Unterminated String"; + error = true; + return TK_ERROR; + } + CharType res = 0; + + switch (next) { + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': + res = 13; + break; + case '\"': res = '\"'; break; + case '\\': + res = '\\'; + break; + default: { + res = next; + } break; + } + + tk_string += res; + + } else { + if (code[idx] == '\n') + line++; + tk_string += code[idx]; + } + idx++; + } + + value = tk_string; + + return TK_STRING; + } break; + default: { + if (code[idx] <= 32) { + idx++; + break; + } + + if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) { + value = String::chr(code[idx]); + idx++; + return TK_SYMBOL; + } + + if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { + //a number + const CharType *rptr; + double number = String::to_double(&code[idx], &rptr); + idx += (rptr - &code[idx]); + value = number; + return TK_NUMBER; + + } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) { + String id; + + id += code[idx]; + idx++; + + while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) { + id += code[idx]; + idx++; + } + + value = id; + return TK_IDENTIFIER; + } else if (code[idx] == '@' && code[idx + 1] == '"') { + // begin of verbatim string + idx++; + } else { + error_str = "Unexpected character."; + error = true; + return TK_ERROR; + } + } + } + } +} + +Error ScriptClassParser::_skip_generic_type_params() { + + Token tk; + + while (true) { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + tk = get_token(); + + if (tk == TK_PERIOD) { + while (true) { + tk = get_token(); + + if (tk != TK_IDENTIFIER) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk != TK_PERIOD) + break; + } + } + + if (tk == TK_OP_LESS) { + Error err = _skip_generic_type_params(); + if (err) + return err; + continue; + } else if (tk != TK_COMMA) { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } else if (tk == TK_OP_LESS) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found " + get_token_name(TK_OP_LESS); + error = true; + return ERR_PARSE_ERROR; + } else if (tk == TK_OP_GREATER) { + return OK; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } +} + +Error ScriptClassParser::_parse_type_full_name(String &r_full_name) { + + Token tk = get_token(); + + if (tk != TK_IDENTIFIER) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + r_full_name += String(value); + + if (code[idx] != '.') // We only want to take the next token if it's a period + return OK; + + tk = get_token(); + + CRASH_COND(tk != TK_PERIOD); // Assertion + + r_full_name += "."; + + return _parse_type_full_name(r_full_name); +} + +Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { + + String name; + + Error err = _parse_type_full_name(name); + if (err) + return err; + + Token tk = get_token(); + + if (tk == TK_OP_LESS) { + // We don't add it to the base list if it's generic + Error err = _skip_generic_type_params(); + if (err) + return err; + } else if (tk == TK_COMMA) { + Error err = _parse_class_base(r_base); + if (err) + return err; + r_base.push_back(name); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + r_base.push_back(name); + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) { + + Token tk = get_token(); + + if (tk == TK_IDENTIFIER) { + r_name += String(value); + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk == TK_PERIOD) { + r_name += "."; + return _parse_namespace_name(r_name, r_curly_stack); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + r_curly_stack++; + return OK; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } +} + +Error ScriptClassParser::parse(const String &p_code) { + + code = p_code; + idx = 0; + line = 0; + error_str = String(); + error = false; + value = Variant(); + classes.clear(); + + Token tk = get_token(); + + Map<int, NameDecl> name_stack; + int curly_stack = 0; + int type_curly_stack = 0; + + while (!error && tk != TK_EOF) { + if (tk == TK_IDENTIFIER && String(value) == "class") { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + String name = value; + int at_level = type_curly_stack; + + ClassDecl class_decl; + + for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) { + const NameDecl &name_decl = E->value(); + + if (name_decl.type == NameDecl::NAMESPACE_DECL) { + if (E != name_stack.front()) + class_decl.namespace_ += "."; + class_decl.namespace_ += name_decl.name; + } else { + class_decl.name += name_decl.name + "."; + } + } + + class_decl.name += name; + class_decl.nested = type_curly_stack > 0; + + bool generic = false; + + while (true) { + tk = get_token(); + + if (tk == TK_COLON) { + Error err = _parse_class_base(class_decl.base); + if (err) + return err; + + curly_stack++; + type_curly_stack++; + + break; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_OP_LESS && !generic) { + generic = true; + + Error err = _skip_generic_type_params(); + if (err) + return err; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::CLASS_DECL; + name_stack[at_level] = name_decl; + + if (!generic) { // no generics, thanks + classes.push_back(class_decl); + } else if (OS::get_singleton()->is_stdout_verbose()) { + String full_name = class_decl.namespace_; + if (full_name.length()) + full_name += "."; + full_name += class_decl.name; + OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8()); + } + } + } else if (tk == TK_IDENTIFIER && String(value) == "struct") { + String name; + int at_level = type_curly_stack; + while (true) { + tk = get_token(); + if (tk == TK_IDENTIFIER && name.empty()) { + name = String(value); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + if (name.empty()) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + " after keyword `struct`, found " + get_token_name(TK_CURLY_BRACKET_OPEN); + error = true; + return ERR_PARSE_ERROR; + } + + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_EOF) { + error_str = "Expected " + get_token_name(TK_CURLY_BRACKET_OPEN) + " after struct decl, found " + get_token_name(TK_EOF); + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::STRUCT_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_IDENTIFIER && String(value) == "namespace") { + if (type_curly_stack > 0) { + error_str = "Found namespace nested inside type."; + error = true; + return ERR_PARSE_ERROR; + } + + String name; + int at_level = curly_stack; + + Error err = _parse_namespace_name(name, curly_stack); + if (err) + return err; + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::NAMESPACE_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + } else if (tk == TK_CURLY_BRACKET_CLOSE) { + curly_stack--; + if (name_stack.has(curly_stack)) { + if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL) + type_curly_stack--; + name_stack.erase(curly_stack); + } + } + + tk = get_token(); + } + + if (!error && tk == TK_EOF && curly_stack > 0) { + error_str = "Reached EOF with missing close curly brackets."; + error = true; + } + + if (error) + return ERR_PARSE_ERROR; + + return OK; +} + +Error ScriptClassParser::parse_file(const String &p_filepath) { + + String source; + + Error ferr = read_all_file_utf8(p_filepath, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + ERR_EXPLAIN("File '" + p_filepath + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + } + ERR_FAIL_V(ferr); + } + + return parse(source); +} + +String ScriptClassParser::get_error() { + return error_str; +} + +Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() { + return classes; +} diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h new file mode 100644 index 0000000000..1e174c28a9 --- /dev/null +++ b/modules/mono/editor/script_class_parser.h @@ -0,0 +1,79 @@ +#ifndef SCRIPT_CLASS_PARSER_H +#define SCRIPT_CLASS_PARSER_H + +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vector.h" + +class ScriptClassParser { + +public: + struct NameDecl { + enum Type { + NAMESPACE_DECL, + CLASS_DECL, + STRUCT_DECL + }; + + String name; + Type type; + }; + + struct ClassDecl { + String name; + String namespace_; + Vector<String> base; + bool nested; + bool has_script_attr; + }; + +private: + String code; + int idx; + int line; + String error_str; + bool error; + Variant value; + + Vector<ClassDecl> classes; + + enum Token { + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_PERIOD, + TK_COLON, + TK_COMMA, + TK_SYMBOL, + TK_IDENTIFIER, + TK_STRING, + TK_NUMBER, + TK_OP_LESS, + TK_OP_GREATER, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_names[TK_MAX]; + static String get_token_name(Token p_token); + + Token get_token(); + + Error _skip_generic_type_params(); + + Error _parse_type_full_name(String &r_full_name); + Error _parse_class_base(Vector<String> &r_base); + Error _parse_namespace_name(String &r_name, int &r_curly_stack); + +public: + Error parse(const String &p_code); + Error parse_file(const String &p_filepath); + + String get_error(); + + Vector<ClassDecl> get_classes(); +}; + +#endif // SCRIPT_CLASS_PARSER_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 4d88cca6f1..c3feafee28 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -377,34 +377,24 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { - return load_assembly_from(p_name, String(), r_assembly, p_refonly); -} - -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { - - return load_assembly_from(p_name, String(), p_aname, r_assembly, p_refonly); -} - -bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly) { - CRASH_COND(!r_assembly); MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - bool result = load_assembly_from(p_name, p_basedir, aname, r_assembly, p_refonly); + bool result = load_assembly(p_name, aname, r_assembly, p_refonly); mono_assembly_name_free(aname); mono_free(aname); return result; } -bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { CRASH_COND(!r_assembly); print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); MonoImageOpenStatus status = MONO_IMAGE_OK; - MonoAssembly *assembly = mono_assembly_load_full(p_aname, p_basedir.length() ? p_basedir.utf8().get_data() : NULL, &status, p_refonly); + MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly); if (!assembly) return false; @@ -425,6 +415,32 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, M return true; } +bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) { + + CRASH_COND(!r_assembly); + + print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + + GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly); + + if (!assembly) + return false; + +#ifdef DEBUG_ENABLED + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); + + ERR_FAIL_COND_V(stored_assembly == NULL, false); + ERR_FAIL_COND_V(*stored_assembly != assembly, false); +#endif + + *r_assembly = assembly; + + print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); + + return true; +} + APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { APIAssembly::Version api_assembly_version; @@ -480,7 +496,14 @@ bool GDMono::_load_core_api_assembly() { } #endif - bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(API_ASSEMBLY_NAME, + assembly_path, + &core_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -510,7 +533,14 @@ bool GDMono::_load_editor_api_assembly() { return false; } - bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME, + assembly_path, + &editor_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -551,6 +581,8 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + + CSharpLanguage::get_singleton()->project_assembly_loaded(); } else { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load project assembly"); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 745dcc34eb..fd9551b6de 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -199,8 +199,7 @@ public: bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false); Error finalize_and_unload_domain(MonoDomain *p_domain); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 1067c11e0e..72b0204672 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -457,6 +457,20 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) return match; } +GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { + + GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); + if (loaded_asm) + return *loaded_asm; +#ifdef DEBUG_ENABLED + CRASH_COND(!FileAccess::exists(p_path)); +#endif + no_search = true; + GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly); + no_search = false; + return res; +} + GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { loaded = false; diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 4c9b1cb10d..2af40ec901 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -128,6 +128,8 @@ public: static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String()); + static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); + GDMonoAssembly(const String &p_name, const String &p_path = String()); ~GDMonoAssembly(); }; diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index b4c78df538..fa1fbebb16 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -141,7 +141,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (exc) { GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(); + ERR_FAIL(); } } } diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 8691932f9a..6900866725 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -30,6 +30,8 @@ #include "string_utils.h" +#include "core/os/file_access.h" + namespace { int sfind(const String &p_text, int p_from) { @@ -128,6 +130,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const return new_string; } +#ifdef TOOLS_ENABLED bool is_csharp_keyword(const String &p_name) { // Reserved keywords @@ -156,3 +159,28 @@ bool is_csharp_keyword(const String &p_name) { String escape_csharp_keyword(const String &p_name) { return is_csharp_keyword(p_name) ? "@" + p_name : p_name; } +#endif + +Error read_all_file_utf8(const String &p_path, String &r_content) { + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, err); + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String source; + if (source.parse_utf8((const char *)w.ptr())) { + ERR_FAIL_V(ERR_INVALID_DATA); + } + + r_content = source; + return OK; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index f2df2340ae..ee803bd720 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -42,4 +42,6 @@ bool is_csharp_keyword(const String &p_name); String escape_csharp_keyword(const String &p_name); #endif +Error read_all_file_utf8(const String &p_path, String &r_content); + #endif // STRING_FORMAT_H diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index 98351fbaee..d121a82d9e 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -103,24 +103,25 @@ void NavigationMeshEditor::_bind_methods() { NavigationMeshEditor::NavigationMeshEditor() { bake_hbox = memnew(HBoxContainer); + button_bake = memnew(ToolButton); - button_bake->set_text(TTR("Bake!")); + bake_hbox->add_child(button_bake); button_bake->set_toggle_mode(true); - button_reset = memnew(Button); - button_bake->set_tooltip(TTR("Bake the navigation mesh.") + "\n"); + button_bake->set_text(TTR("Bake NavMesh")); + button_bake->connect("pressed", this, "_bake_pressed"); - bake_info = memnew(Label); - bake_hbox->add_child(button_bake); + button_reset = memnew(ToolButton); 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.")); + button_reset->connect("pressed", this, "_clear_pressed"); + + bake_info = memnew(Label); bake_hbox->add_child(bake_info); err_dialog = memnew(AcceptDialog); add_child(err_dialog); node = NULL; - - button_bake->connect("pressed", this, "_bake_pressed"); - button_reset->connect("pressed", this, "_clear_pressed"); - button_reset->set_tooltip(TTR("Clear the navigation mesh.")); } NavigationMeshEditor::~NavigationMeshEditor() { diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h index 4f3e222002..914d9ab8b9 100644 --- a/modules/recast/navigation_mesh_editor_plugin.h +++ b/modules/recast/navigation_mesh_editor_plugin.h @@ -43,8 +43,8 @@ class NavigationMeshEditor : public Control { AcceptDialog *err_dialog; HBoxContainer *bake_hbox; - Button *button_bake; - Button *button_reset; + ToolButton *button_bake; + ToolButton *button_reset; Label *bake_info; NavigationMeshInstance *node; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7fdd7fe446..a80a015ed3 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3018,11 +3018,15 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) { void VisualScriptEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_READY) { + if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) { + node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); node_filter->set_clear_button_enabled(true); - variable_editor->connect("changed", this, "_update_members"); - signal_editor->connect("changed", this, "_update_members"); + + if (p_what == NOTIFICATION_READY) { + variable_editor->connect("changed", this, "_update_members"); + signal_editor->connect("changed", this, "_update_members"); + } Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); @@ -3056,8 +3060,12 @@ void VisualScriptEditor::_notification(int p_what) { node_styles[E->get().first] = frame_style; } } - } - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (is_visible_in_tree()) { + _update_members(); + _update_graph(); + } + } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { left_vsplit->set_visible(is_visible_in_tree()); } } diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 1603ea70d9..18ffc74fc3 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -14,9 +14,9 @@ apply plugin: 'com.android.application' allprojects { repositories { - jcenter() mavenCentral() google() + jcenter() $$GRADLE_REPOSITORY_URLS$$ } } diff --git a/platform/android/detect.py b/platform/android/detect.py index 953a2fa6d2..e4cab2fb23 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -186,7 +186,7 @@ def configure(env): env.PrependENVPath('PATH', tools_path) ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = compiler_path + '/clang' env['CXX'] = compiler_path + '/clang++' else: @@ -293,7 +293,7 @@ def configure(env): # Return NDK version string in source.properties (adapted from the Chromium project). def get_ndk_version(path): - if path == None: + if path is None: return None prop_file_path = os.path.join(path, "source.properties") try: diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index b13a1e9643..417571f6f3 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -87,7 +87,7 @@ def configure(env): s_compiler_path = '$IPHONEPATH/Developer/usr/bin/' ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = compiler_path + 'clang' env['CXX'] = compiler_path + 'clang++' env['S_compiler'] = s_compiler_path + 'gcc' diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 8a0883eca3..c5bd64b15c 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -89,7 +89,7 @@ def configure(env): basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" ccache_path = os.environ.get("CCACHE") - if ccache_path == None: + if ccache_path is None: env['CC'] = basecmd + "cc" env['CXX'] = basecmd + "c++" else: diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 559f23ca5b..f25b9ba9cd 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -17,7 +17,7 @@ def can_build(): # building natively on windows! if (os.getenv("VSINSTALLDIR")): - if (os.getenv("ANGLE_SRC_PATH") == None): + if (os.getenv("ANGLE_SRC_PATH") is None): return False return True diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index bb36852cf9..9f1687262f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -220,13 +220,14 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & case ALIGN_LEFT: l.offset_caches.push_back(0); break; \ case ALIGN_CENTER: l.offset_caches.push_back(((p_width - margin) - used) / 2); break; \ case ALIGN_RIGHT: l.offset_caches.push_back(((p_width - margin) - used)); break; \ - case ALIGN_FILL: l.offset_caches.push_back((p_width - margin) - used /*+spaces_size*/); break; \ + case ALIGN_FILL: l.offset_caches.push_back(line_wrapped ? ((p_width - margin) - used) : 0); break; \ } \ l.height_caches.push_back(line_height); \ l.ascent_caches.push_back(line_ascent); \ l.descent_caches.push_back(line_descent); \ l.space_caches.push_back(spaces); \ } \ + line_wrapped = false; \ y += line_height + get_constant(SceneStringNames::get_singleton()->line_separation); \ line_height = 0; \ line_ascent = 0; \ @@ -254,6 +255,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & l.minimum_width = MAX(l.minimum_width, m_width); \ } \ if (wofs + m_width > p_width) { \ + line_wrapped = true; \ if (p_mode == PROCESS_CACHE) { \ if (spaces > 0) \ spaces -= 1; \ @@ -298,6 +300,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int rchar = 0; int lh = 0; bool line_is_blank = true; + bool line_wrapped = false; int fh = 0; while (it) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index bb56eea98e..d996132960 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1429,9 +1429,6 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect()); - if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -1443,9 +1440,6 @@ void TextEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->hide_virtual_keyboard(); - if (raised_from_completion) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - } } break; } } @@ -5666,16 +5660,12 @@ void TextEdit::_confirm_completion() { void TextEdit::_cancel_code_hint() { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - raised_from_completion = false; completion_hint = ""; update(); } void TextEdit::_cancel_completion() { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 0); - raised_from_completion = false; if (!completion_active) return; @@ -5833,8 +5823,6 @@ void TextEdit::query_code_comple() { void TextEdit::set_code_hint(const String &p_hint) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - raised_from_completion = true; completion_hint = p_hint; completion_hint_offset = -0xFFFF; update(); @@ -5842,8 +5830,6 @@ void TextEdit::set_code_hint(const String &p_hint) { void TextEdit::code_complete(const Vector<String> &p_strings, bool p_forced) { - VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1); - raised_from_completion = true; completion_strings = p_strings; completion_active = true; completion_forced = p_forced; @@ -6346,8 +6332,6 @@ TextEdit::TextEdit() { target_v_scroll = 0; v_scroll_speed = 80; - raised_from_completion = false; - context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index f0c18ad047..8a508a8738 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -306,8 +306,6 @@ private: float target_v_scroll; float v_scroll_speed; - bool raised_from_completion; - String highlighted_word; uint64_t last_dblclk; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 274c74a9a2..c3030ee741 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -614,7 +614,7 @@ void SpatialMaterial::_update_shader() { code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n"; code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n"; - code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames),particle_frame / float(particles_anim_h_frames) / float(particles_anim_v_frames));\n"; + code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames), floor(particle_frame / float(particles_anim_h_frames)) / float(particles_anim_v_frames));\n"; } break; } diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index d52e121a13..e2db89e699 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -2498,7 +2498,7 @@ bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block, CompletionT identifier = StringName(); - TkPos pos; + TkPos pos = { 0, 0 }; Token tk = _get_token(); |