summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gles3/shaders/canvas.glsl42
-rw-r--r--editor/action_map_editor.cpp2
-rw-r--r--editor/editor_node.cpp182
-rw-r--r--editor/editor_node.h5
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/event_listener_line_edit.cpp68
-rw-r--r--editor/event_listener_line_edit.h3
-rw-r--r--editor/input_event_configuration_dialog.cpp85
-rw-r--r--editor/input_event_configuration_dialog.h2
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h2
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp112
-rw-r--r--editor/register_editor_types.cpp221
-rw-r--r--editor/register_editor_types.h37
-rw-r--r--main/main.cpp9
-rw-r--r--modules/gdscript/gdscript.cpp285
-rw-r--r--modules/gdscript/gdscript.h16
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp108
-rw-r--r--modules/gdscript/gdscript_cache.cpp150
-rw-r--r--modules/gdscript/gdscript_cache.h27
-rw-r--r--modules/gdscript/gdscript_compiler.cpp41
-rw-r--r--modules/gdscript/gdscript_function.cpp7
-rw-r--r--modules/gdscript/gdscript_function.h1
-rw-r--r--modules/gdscript/gdscript_parser.cpp12
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd (renamed from modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd)3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out (renamed from modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out)1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs48
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.cpp10
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.h2
-rw-r--r--modules/multiplayer/multiplayer_debugger.cpp26
-rw-r--r--modules/multiplayer/multiplayer_debugger.h2
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp8
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp41
-rw-r--r--modules/multiplayer/scene_multiplayer.h13
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp5
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp21
-rw-r--r--modules/multiplayer/scene_rpc_interface.h5
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp10
-rw-r--r--scene/resources/shape_2d.cpp4
-rw-r--r--scene/resources/shape_3d.cpp4
-rw-r--r--servers/rendering/renderer_canvas_render.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp12
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h9
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas.glsl42
-rw-r--r--servers/rendering/renderer_rd/shaders/skeleton.glsl11
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h1
54 files changed, 1086 insertions, 660 deletions
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index ca806304c5..cdae05a516 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -153,48 +153,6 @@ void main() {
uv += 1e-5;
}
-#ifdef USE_ATTRIBUTES
-#if 0
- if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone
- //skeleton transform
- ivec4 bone_indicesi = ivec4(bone_indices);
-
- uvec2 tex_ofs = bone_indicesi.x * 2;
-
- mat2x4 m;
- m = mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.x;
-
- tex_ofs = bone_indicesi.y * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.y;
-
- tex_ofs = bone_indicesi.z * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.z;
-
- tex_ofs = bone_indicesi.w * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.w;
-
- mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse;
-
- //outvec = bone_matrix * outvec;
- }
-#endif
-#endif
-
vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
vertex_interp = vertex;
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 3b9d6c18eb..c376d5434f 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -443,7 +443,7 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
TreeItem *event_item = action_tree->create_item(action_item);
// First Column - Text
- event_item->set_text(0, event_config_dialog->get_event_text(event, true));
+ event_item->set_text(0, EventListenerLineEdit::get_event_text(event, true));
event_item->set_meta("__event", event);
event_item->set_meta("__index", evnt_idx);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index b1b26e4c74..f277bf6467 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -70,7 +70,6 @@
#include "servers/physics_server_2d.h"
#include "servers/rendering/rendering_device.h"
-#include "editor/animation_track_editor.h"
#include "editor/audio_stream_preview.h"
#include "editor/debugger/debug_adapter/debug_adapter_server.h"
#include "editor/debugger/editor_debugger_node.h"
@@ -82,7 +81,6 @@
#include "editor/editor_data.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_file_dialog.h"
-#include "editor/editor_file_system.h"
#include "editor/editor_folding.h"
#include "editor/editor_help.h"
#include "editor/editor_inspector.h"
@@ -92,12 +90,8 @@
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_property_name_processor.h"
-#include "editor/editor_quick_open.h"
-#include "editor/editor_resource_picker.h"
-#include "editor/editor_resource_preview.h"
#include "editor/editor_run.h"
#include "editor/editor_run_native.h"
-#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_settings_dialog.h"
@@ -123,7 +117,6 @@
#include "editor/import/resource_importer_imagefont.h"
#include "editor/import/resource_importer_layered_texture.h"
#include "editor/import/resource_importer_obj.h"
-#include "editor/import/resource_importer_scene.h"
#include "editor/import/resource_importer_shader_file.h"
#include "editor/import/resource_importer_texture.h"
#include "editor/import/resource_importer_texture_atlas.h"
@@ -133,78 +126,21 @@
#include "editor/multi_node_edit.h"
#include "editor/node_dock.h"
#include "editor/plugin_config_dialog.h"
-#include "editor/plugins/animation_blend_space_1d_editor.h"
-#include "editor/plugins/animation_blend_space_2d_editor.h"
-#include "editor/plugins/animation_blend_tree_editor_plugin.h"
#include "editor/plugins/animation_player_editor_plugin.h"
-#include "editor/plugins/animation_state_machine_editor.h"
-#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
-#include "editor/plugins/audio_stream_randomizer_editor_plugin.h"
-#include "editor/plugins/bit_map_editor_plugin.h"
-#include "editor/plugins/bone_map_editor_plugin.h"
-#include "editor/plugins/camera_3d_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
-#include "editor/plugins/cast_2d_editor_plugin.h"
-#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
-#include "editor/plugins/collision_shape_2d_editor_plugin.h"
-#include "editor/plugins/control_editor_plugin.h"
-#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
-#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
-#include "editor/plugins/curve_editor_plugin.h"
#include "editor/plugins/debugger_editor_plugin.h"
-#include "editor/plugins/editor_debugger_plugin.h"
-#include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
-#include "editor/plugins/font_config_plugin.h"
#include "editor/plugins/gdextension_export_plugin.h"
-#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
-#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
-#include "editor/plugins/gpu_particles_collision_sdf_editor_plugin.h"
-#include "editor/plugins/gradient_editor_plugin.h"
-#include "editor/plugins/gradient_texture_2d_editor_plugin.h"
-#include "editor/plugins/input_event_editor_plugin.h"
-#include "editor/plugins/light_occluder_2d_editor_plugin.h"
-#include "editor/plugins/lightmap_gi_editor_plugin.h"
-#include "editor/plugins/line_2d_editor_plugin.h"
#include "editor/plugins/material_editor_plugin.h"
-#include "editor/plugins/mesh_editor_plugin.h"
-#include "editor/plugins/mesh_instance_3d_editor_plugin.h"
#include "editor/plugins/mesh_library_editor_plugin.h"
-#include "editor/plugins/multimesh_editor_plugin.h"
-#include "editor/plugins/navigation_link_2d_editor_plugin.h"
-#include "editor/plugins/navigation_polygon_editor_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
-#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_translation_parser_plugin.h"
-#include "editor/plugins/path_2d_editor_plugin.h"
-#include "editor/plugins/path_3d_editor_plugin.h"
-#include "editor/plugins/physical_bone_3d_editor_plugin.h"
-#include "editor/plugins/polygon_2d_editor_plugin.h"
-#include "editor/plugins/polygon_3d_editor_plugin.h"
-#include "editor/plugins/resource_preloader_editor_plugin.h"
#include "editor/plugins/root_motion_editor_plugin.h"
-#include "editor/plugins/script_editor_plugin.h"
#include "editor/plugins/script_text_editor.h"
-#include "editor/plugins/shader_editor_plugin.h"
-#include "editor/plugins/shader_file_editor_plugin.h"
-#include "editor/plugins/skeleton_2d_editor_plugin.h"
-#include "editor/plugins/skeleton_3d_editor_plugin.h"
-#include "editor/plugins/skeleton_ik_3d_editor_plugin.h"
-#include "editor/plugins/sprite_2d_editor_plugin.h"
-#include "editor/plugins/sprite_frames_editor_plugin.h"
-#include "editor/plugins/style_box_editor_plugin.h"
-#include "editor/plugins/sub_viewport_preview_editor_plugin.h"
#include "editor/plugins/text_editor.h"
-#include "editor/plugins/texture_3d_editor_plugin.h"
-#include "editor/plugins/texture_editor_plugin.h"
-#include "editor/plugins/texture_layered_editor_plugin.h"
-#include "editor/plugins/texture_region_editor_plugin.h"
-#include "editor/plugins/theme_editor_plugin.h"
-#include "editor/plugins/tiles/tiles_editor_plugin.h"
#include "editor/plugins/version_control_editor_plugin.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
-#include "editor/plugins/voxel_gi_editor_plugin.h"
#include "editor/progress_dialog.h"
#include "editor/project_settings_editor.h"
#include "editor/register_exporters.h"
@@ -4129,63 +4065,6 @@ bool EditorNode::is_scene_in_use(const String &p_path) {
return false;
}
-void EditorNode::register_editor_types() {
- ResourceLoader::set_timestamp_on_load(true);
- ResourceSaver::set_timestamp_on_save(true);
-
- GDREGISTER_CLASS(EditorPaths);
- GDREGISTER_CLASS(EditorPlugin);
- GDREGISTER_CLASS(EditorTranslationParserPlugin);
- GDREGISTER_CLASS(EditorImportPlugin);
- GDREGISTER_CLASS(EditorScript);
- GDREGISTER_CLASS(EditorSelection);
- GDREGISTER_CLASS(EditorFileDialog);
- GDREGISTER_ABSTRACT_CLASS(EditorSettings);
- GDREGISTER_CLASS(EditorNode3DGizmo);
- GDREGISTER_CLASS(EditorNode3DGizmoPlugin);
- GDREGISTER_ABSTRACT_CLASS(EditorResourcePreview);
- GDREGISTER_CLASS(EditorResourcePreviewGenerator);
- GDREGISTER_ABSTRACT_CLASS(EditorFileSystem);
- GDREGISTER_CLASS(EditorFileSystemDirectory);
- GDREGISTER_CLASS(EditorVCSInterface);
- GDREGISTER_ABSTRACT_CLASS(ScriptEditor);
- GDREGISTER_ABSTRACT_CLASS(ScriptEditorBase);
- GDREGISTER_CLASS(EditorSyntaxHighlighter);
- GDREGISTER_ABSTRACT_CLASS(EditorInterface);
- GDREGISTER_CLASS(EditorExportPlugin);
- GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
- GDREGISTER_CLASS(EditorResourceConversionPlugin);
- GDREGISTER_CLASS(EditorSceneFormatImporter);
- GDREGISTER_CLASS(EditorScenePostImportPlugin);
- GDREGISTER_CLASS(EditorInspector);
- GDREGISTER_CLASS(EditorInspectorPlugin);
- GDREGISTER_CLASS(EditorProperty);
- GDREGISTER_CLASS(AnimationTrackEditPlugin);
- GDREGISTER_CLASS(ScriptCreateDialog);
- GDREGISTER_CLASS(EditorFeatureProfile);
- GDREGISTER_CLASS(EditorSpinSlider);
- GDREGISTER_CLASS(EditorResourcePicker);
- GDREGISTER_CLASS(EditorScriptPicker);
- GDREGISTER_ABSTRACT_CLASS(EditorUndoRedoManager);
-
- GDREGISTER_ABSTRACT_CLASS(FileSystemDock);
- GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery);
-
- GDREGISTER_CLASS(EditorScenePostImport);
- GDREGISTER_CLASS(EditorCommandPalette);
- GDREGISTER_CLASS(EditorDebuggerPlugin);
- GDREGISTER_ABSTRACT_CLASS(EditorDebuggerSession);
-}
-
-void EditorNode::unregister_editor_types() {
- _init_callbacks.clear();
- if (EditorPaths::get_singleton()) {
- EditorPaths::free();
- }
-
- EditorResourcePicker::clear_caches();
-}
-
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
if (has_child_process(p_pid)) {
editor_run.stop_child_process(p_pid);
@@ -5235,6 +5114,10 @@ bool EditorNode::immediate_confirmation_dialog(const String &p_text, const Strin
return singleton->immediate_dialog_confirmed;
}
+void EditorNode::cleanup() {
+ _init_callbacks.clear();
+}
+
int EditorNode::get_current_tab() {
return scene_tabs->get_current_tab();
}
@@ -7325,7 +7208,6 @@ EditorNode::EditorNode() {
add_child(audio_preview_gen);
add_editor_plugin(memnew(DebuggerEditorPlugin(debug_menu)));
- add_editor_plugin(memnew(DebugAdapterServer()));
disk_changed = memnew(ConfirmationDialog);
{
@@ -7375,63 +7257,7 @@ EditorNode::EditorNode() {
raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
-
- // This list is alphabetized, and plugins that depend on Node2D are in their own section below.
- add_editor_plugin(memnew(AnimationTreeEditorPlugin));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
- add_editor_plugin(memnew(AudioStreamRandomizerEditorPlugin));
- add_editor_plugin(memnew(BitMapEditorPlugin));
- add_editor_plugin(memnew(BoneMapEditorPlugin));
- add_editor_plugin(memnew(Camera3DEditorPlugin));
- add_editor_plugin(memnew(ControlEditorPlugin));
- add_editor_plugin(memnew(CPUParticles3DEditorPlugin));
- add_editor_plugin(memnew(CurveEditorPlugin));
- add_editor_plugin(memnew(FontEditorPlugin));
- add_editor_plugin(memnew(GPUParticles3DEditorPlugin));
- add_editor_plugin(memnew(GPUParticlesCollisionSDF3DEditorPlugin));
- add_editor_plugin(memnew(GradientEditorPlugin));
- add_editor_plugin(memnew(GradientTexture2DEditorPlugin));
- add_editor_plugin(memnew(InputEventEditorPlugin));
- add_editor_plugin(memnew(LightmapGIEditorPlugin));
- add_editor_plugin(memnew(MaterialEditorPlugin));
- add_editor_plugin(memnew(MeshEditorPlugin));
- add_editor_plugin(memnew(MeshInstance3DEditorPlugin));
- add_editor_plugin(memnew(MeshLibraryEditorPlugin));
- add_editor_plugin(memnew(MultiMeshEditorPlugin));
- add_editor_plugin(memnew(OccluderInstance3DEditorPlugin));
- add_editor_plugin(memnew(Path3DEditorPlugin));
- add_editor_plugin(memnew(PhysicalBone3DEditorPlugin));
- add_editor_plugin(memnew(Polygon3DEditorPlugin));
- add_editor_plugin(memnew(ResourcePreloaderEditorPlugin));
- add_editor_plugin(memnew(ShaderEditorPlugin));
- add_editor_plugin(memnew(ShaderFileEditorPlugin));
- add_editor_plugin(memnew(Skeleton3DEditorPlugin));
- add_editor_plugin(memnew(SkeletonIK3DEditorPlugin));
- add_editor_plugin(memnew(SpriteFramesEditorPlugin));
- add_editor_plugin(memnew(StyleBoxEditorPlugin));
- add_editor_plugin(memnew(SubViewportPreviewEditorPlugin));
- add_editor_plugin(memnew(Texture3DEditorPlugin));
- add_editor_plugin(memnew(TextureEditorPlugin));
- add_editor_plugin(memnew(TextureLayeredEditorPlugin));
- add_editor_plugin(memnew(TextureRegionEditorPlugin));
- add_editor_plugin(memnew(ThemeEditorPlugin));
- add_editor_plugin(memnew(VoxelGIEditorPlugin));
-
- // 2D
- add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin));
- add_editor_plugin(memnew(CollisionShape2DEditorPlugin));
- add_editor_plugin(memnew(CPUParticles2DEditorPlugin));
- add_editor_plugin(memnew(GPUParticles2DEditorPlugin));
- add_editor_plugin(memnew(LightOccluder2DEditorPlugin));
- add_editor_plugin(memnew(Line2DEditorPlugin));
- add_editor_plugin(memnew(NavigationLink2DEditorPlugin));
- add_editor_plugin(memnew(NavigationPolygonEditorPlugin));
- add_editor_plugin(memnew(Path2DEditorPlugin));
- add_editor_plugin(memnew(Polygon2DEditorPlugin));
- add_editor_plugin(memnew(Cast2DEditorPlugin));
- add_editor_plugin(memnew(Skeleton2DEditorPlugin));
- add_editor_plugin(memnew(Sprite2DEditorPlugin));
- add_editor_plugin(memnew(TilesEditorPlugin));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 642f1c5e3f..ff0338a794 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -713,9 +713,6 @@ public:
bool call_build();
- static void register_editor_types();
- static void unregister_editor_types();
-
static EditorNode *get_singleton() { return singleton; }
static EditorLog *get_log() { return singleton->log; }
@@ -749,6 +746,8 @@ public:
static bool immediate_confirmation_dialog(const String &p_text, const String &p_ok_text = TTR("Ok"), const String &p_cancel_text = TTR("Cancel"));
+ static void cleanup();
+
EditorPlugin *get_editor_plugin_screen() { return editor_plugin_screen; }
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; }
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 9c639164f2..d736675faf 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -322,7 +322,7 @@ typedef EditorPlugin *(*EditorPluginCreateFunc)();
class EditorPlugins {
enum {
- MAX_CREATE_FUNCS = 64
+ MAX_CREATE_FUNCS = 128
};
static EditorPluginCreateFunc creation_funcs[MAX_CREATE_FUNCS];
diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp
index e4c35a5b81..1b081f9091 100644
--- a/editor/event_listener_line_edit.cpp
+++ b/editor/event_listener_line_edit.cpp
@@ -30,6 +30,72 @@
#include "editor/event_listener_line_edit.h"
+#include "core/input/input_map.h"
+
+// Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
+static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = {
+ TTRC("Left Stick Left, Joystick 0 Left"),
+ TTRC("Left Stick Right, Joystick 0 Right"),
+ TTRC("Left Stick Up, Joystick 0 Up"),
+ TTRC("Left Stick Down, Joystick 0 Down"),
+ TTRC("Right Stick Left, Joystick 1 Left"),
+ TTRC("Right Stick Right, Joystick 1 Right"),
+ TTRC("Right Stick Up, Joystick 1 Up"),
+ TTRC("Right Stick Down, Joystick 1 Down"),
+ TTRC("Joystick 2 Left"),
+ TTRC("Left Trigger, Sony L2, Xbox LT, Joystick 2 Right"),
+ TTRC("Joystick 2 Up"),
+ TTRC("Right Trigger, Sony R2, Xbox RT, Joystick 2 Down"),
+ TTRC("Joystick 3 Left"),
+ TTRC("Joystick 3 Right"),
+ TTRC("Joystick 3 Up"),
+ TTRC("Joystick 3 Down"),
+ TTRC("Joystick 4 Left"),
+ TTRC("Joystick 4 Right"),
+ TTRC("Joystick 4 Up"),
+ TTRC("Joystick 4 Down"),
+};
+
+String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) {
+ ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent");
+
+ String text = p_event->as_text();
+
+ Ref<InputEventKey> key = p_event;
+ if (key.is_valid() && key->is_command_or_control_autoremap()) {
+#ifdef MACOS_ENABLED
+ text = text.replace("Command", "Command/Ctrl");
+#else
+ text = text.replace("Ctrl", "Command/Ctrl");
+#endif
+ }
+ Ref<InputEventMouse> mouse = p_event;
+ Ref<InputEventJoypadMotion> jp_motion = p_event;
+ Ref<InputEventJoypadButton> jp_button = p_event;
+ if (jp_motion.is_valid()) {
+ // Joypad motion events will display slightly differently than what the event->as_text() provides. See #43660.
+ String desc = TTR("Unknown Joypad Axis");
+ if (jp_motion->get_axis() < JoyAxis::MAX) {
+ desc = RTR(_joy_axis_descriptions[2 * (size_t)jp_motion->get_axis() + (jp_motion->get_axis_value() < 0 ? 0 : 1)]);
+ }
+
+ text = vformat("Joypad Axis %s %s (%s)", itos((int64_t)jp_motion->get_axis()), jp_motion->get_axis_value() < 0 ? "-" : "+", desc);
+ }
+ if (p_include_device && (mouse.is_valid() || jp_button.is_valid() || jp_motion.is_valid())) {
+ String device_string = get_device_string(p_event->get_device());
+ text += vformat(" - %s", device_string);
+ }
+
+ return text;
+}
+
+String EventListenerLineEdit::get_device_string(int p_device) {
+ if (p_device == InputMap::ALL_DEVICES) {
+ return TTR("All Devices");
+ }
+ return TTR("Device") + " " + itos(p_device);
+}
+
bool EventListenerLineEdit::_is_event_allowed(const Ref<InputEvent> &p_event) const {
const Ref<InputEventMouseButton> mb = p_event;
const Ref<InputEventKey> k = p_event;
@@ -71,7 +137,7 @@ void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
event = p_event;
- set_text(event->as_text());
+ set_text(get_event_text(event, false));
emit_signal("event_changed", event);
}
diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h
index c4cd5e4511..487efbc368 100644
--- a/editor/event_listener_line_edit.h
+++ b/editor/event_listener_line_edit.h
@@ -61,6 +61,9 @@ protected:
static void _bind_methods();
public:
+ static String get_event_text(const Ref<InputEvent> &p_event, bool p_include_device);
+ static String get_device_string(int p_device);
+
Ref<InputEvent> get_event() const;
void clear_event();
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index c577c61db7..cb2a8205c2 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -38,63 +38,6 @@
#include "scene/gui/separator.h"
#include "scene/gui/tree.h"
-// Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
-static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = {
- TTRC("Left Stick Left, Joystick 0 Left"),
- TTRC("Left Stick Right, Joystick 0 Right"),
- TTRC("Left Stick Up, Joystick 0 Up"),
- TTRC("Left Stick Down, Joystick 0 Down"),
- TTRC("Right Stick Left, Joystick 1 Left"),
- TTRC("Right Stick Right, Joystick 1 Right"),
- TTRC("Right Stick Up, Joystick 1 Up"),
- TTRC("Right Stick Down, Joystick 1 Down"),
- TTRC("Joystick 2 Left"),
- TTRC("Left Trigger, Sony L2, Xbox LT, Joystick 2 Right"),
- TTRC("Joystick 2 Up"),
- TTRC("Right Trigger, Sony R2, Xbox RT, Joystick 2 Down"),
- TTRC("Joystick 3 Left"),
- TTRC("Joystick 3 Right"),
- TTRC("Joystick 3 Up"),
- TTRC("Joystick 3 Down"),
- TTRC("Joystick 4 Left"),
- TTRC("Joystick 4 Right"),
- TTRC("Joystick 4 Up"),
- TTRC("Joystick 4 Down"),
-};
-
-String InputEventConfigurationDialog::get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) const {
- ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent");
-
- String text = p_event->as_text();
-
- Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_command_or_control_autoremap()) {
-#ifdef MACOS_ENABLED
- text = text.replace("Command", "Command/Ctrl");
-#else
- text = text.replace("Ctrl", "Command/Ctrl");
-#endif
- }
- Ref<InputEventMouse> mouse = p_event;
- Ref<InputEventJoypadMotion> jp_motion = p_event;
- Ref<InputEventJoypadButton> jp_button = p_event;
- if (jp_motion.is_valid()) {
- // Joypad motion events will display slightly differently than what the event->as_text() provides. See #43660.
- String desc = TTR("Unknown Joypad Axis");
- if (jp_motion->get_axis() < JoyAxis::MAX) {
- desc = RTR(_joy_axis_descriptions[2 * (size_t)jp_motion->get_axis() + (jp_motion->get_axis_value() < 0 ? 0 : 1)]);
- }
-
- text = vformat("Joypad Axis %s %s (%s)", itos((int64_t)jp_motion->get_axis()), jp_motion->get_axis_value() < 0 ? "-" : "+", desc);
- }
- if (p_include_device && (mouse.is_valid() || jp_button.is_valid() || jp_motion.is_valid())) {
- String device_string = _get_device_string(p_event->get_device());
- text += vformat(" - %s", device_string);
- }
-
- return text;
-}
-
void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection) {
if (p_event.is_valid()) {
event = p_event;
@@ -107,7 +50,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b
}
// Update Label
- event_as_text->set_text(get_event_text(event, true));
+ event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
Ref<InputEventKey> k = p_event;
Ref<InputEventMouseButton> mb = p_event;
@@ -222,14 +165,7 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven
}
if (joym.is_valid()) {
- float axis_value = joym->get_axis_value();
- if (ABS(axis_value) < 0.9) {
- // Ignore motion below 0.9 magnitude to avoid accidental touches
- return;
- } else {
- // Always make the value 1 or -1 for display consistency
- joym->set_axis_value(SIGN(axis_value));
- }
+ joym->set_axis_value(SIGN(joym->get_axis_value()));
}
if (k.is_valid()) {
@@ -305,7 +241,7 @@ void InputEventConfigurationDialog::_update_input_list() {
Ref<InputEventMouseButton> mb;
mb.instantiate();
mb->set_button_index(mouse_buttons[i]);
- String desc = get_event_text(mb, false);
+ String desc = EventListenerLineEdit::get_event_text(mb, false);
if (!search_term.is_empty() && desc.findn(search_term) == -1) {
continue;
@@ -328,7 +264,7 @@ void InputEventConfigurationDialog::_update_input_list() {
Ref<InputEventJoypadButton> joyb;
joyb.instantiate();
joyb->set_button_index((JoyButton)i);
- String desc = get_event_text(joyb, false);
+ String desc = EventListenerLineEdit::get_event_text(joyb, false);
if (!search_term.is_empty() && desc.findn(search_term) == -1) {
continue;
@@ -354,7 +290,7 @@ void InputEventConfigurationDialog::_update_input_list() {
joym.instantiate();
joym->set_axis((JoyAxis)axis);
joym->set_axis_value(direction);
- String desc = get_event_text(joym, false);
+ String desc = EventListenerLineEdit::get_event_text(joym, false);
if (!search_term.is_empty() && desc.findn(search_term) == -1) {
continue;
@@ -513,7 +449,7 @@ void InputEventConfigurationDialog::_device_selection_changed(int p_option_butto
// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
// and option index 1 corresponds to device 0, etc...
event->set_device(p_option_button_index - 1);
- event_as_text->set_text(get_event_text(event, true));
+ event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));
}
void InputEventConfigurationDialog::_set_current_device(int p_device) {
@@ -524,13 +460,6 @@ int InputEventConfigurationDialog::_get_current_device() const {
return device_id_option->get_selected() - 1;
}
-String InputEventConfigurationDialog::_get_device_string(int p_device) const {
- if (p_device == InputMap::ALL_DEVICES) {
- return TTR("All Devices");
- }
- return TTR("Device") + " " + itos(p_device);
-}
-
void InputEventConfigurationDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -659,7 +588,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
device_id_option = memnew(OptionButton);
device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
for (int i = -1; i < 8; i++) {
- device_id_option->add_item(_get_device_string(i));
+ device_id_option->add_item(EventListenerLineEdit::get_device_string(i));
}
device_id_option->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));
_set_current_device(InputMap::ALL_DEVICES);
diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h
index fc590cba26..eb6eb485ea 100644
--- a/editor/input_event_configuration_dialog.h
+++ b/editor/input_event_configuration_dialog.h
@@ -105,7 +105,6 @@ private:
void _device_selection_changed(int p_option_button_index);
void _set_current_device(int p_device);
int _get_current_device() const;
- String _get_device_string(int p_device) const;
protected:
void _notification(int p_what);
@@ -114,7 +113,6 @@ public:
// Pass an existing event to configure it. Alternatively, pass no event to start with a blank configuration.
void popup_and_configure(const Ref<InputEvent> &p_event = Ref<InputEvent>());
Ref<InputEvent> get_event() const;
- String get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) const;
void set_allowed_input_types(int p_type_masks);
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
index 9ef9fff8cd..658fd287b6 100644
--- a/editor/plugins/animation_tree_editor_plugin.h
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -35,8 +35,6 @@
#include "scene/animation/animation_tree.h"
#include "scene/gui/button.h"
#include "scene/gui/graph_edit.h"
-#include "scene/gui/popup.h"
-#include "scene/gui/tree.h"
class EditorFileDialog;
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
index a38a040814..596b2c0edb 100644
--- a/editor/plugins/root_motion_editor_plugin.cpp
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -47,8 +47,6 @@ void EditorPropertyRootMotion::_confirmed() {
}
void EditorPropertyRootMotion::_node_assign() {
- NodePath current = get_edited_object()->get(get_edited_property());
-
AnimationTree *atree = Object::cast_to<AnimationTree>(get_edited_object());
if (!atree->has_node(atree->get_animation_player())) {
EditorNode::get_singleton()->show_warning(TTR("AnimationTree has no path set to an AnimationPlayer"));
@@ -75,7 +73,10 @@ void EditorPropertyRootMotion::_node_assign() {
for (const StringName &E : animations) {
Ref<Animation> anim = player->get_animation(E);
for (int i = 0; i < anim->get_track_count(); i++) {
- paths.insert(anim->track_get_path(i));
+ String pathname = anim->track_get_path(i).get_concatenated_names();
+ if (!paths.has(pathname)) {
+ paths.insert(pathname);
+ }
}
}
}
@@ -124,66 +125,33 @@ void EditorPropertyRootMotion::_node_assign() {
continue; //no node, can't edit
}
- if (path.get_subname_count()) {
- String concat = path.get_concatenated_subnames();
-
- Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
- if (skeleton && skeleton->find_bone(concat) != -1) {
- //path in skeleton
- const String &bone = concat;
- int idx = skeleton->find_bone(bone);
- List<String> bone_path;
- while (idx != -1) {
- bone_path.push_front(skeleton->get_bone_name(idx));
- idx = skeleton->get_bone_parent(idx);
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ if (skeleton) {
+ HashMap<int, TreeItem *> items;
+ items.insert(-1, ti);
+ Ref<Texture> bone_icon = get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons"));
+ Vector<int> bones_to_process = skeleton->get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
+
+ Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);
+ int child_bone_size = current_bone_child_bones.size();
+ for (int i = 0; i < child_bone_size; i++) {
+ bones_to_process.push_back(current_bone_child_bones[i]);
}
- accum += ":";
- for (List<String>::Element *F = bone_path.front(); F; F = F->next()) {
- if (F != bone_path.front()) {
- accum += "/";
- }
-
- accum += F->get();
- if (!parenthood.has(accum)) {
- ti = filters->create_item(ti);
- parenthood[accum] = ti;
- ti->set_text(0, F->get());
- ti->set_selectable(0, true);
- ti->set_editable(0, false);
- ti->set_icon(0, get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
- ti->set_metadata(0, accum);
- } else {
- ti = parenthood[accum];
- }
- }
+ const int parent_idx = skeleton->get_bone_parent(current_bone_idx);
+ TreeItem *parent_item = items.find(parent_idx)->value;
- ti->set_selectable(0, true);
- ti->set_text(0, concat);
- ti->set_icon(0, get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
- ti->set_metadata(0, path);
- if (path == current) {
- ti->select(0);
- }
+ TreeItem *joint_item = filters->create_item(parent_item);
+ items.insert(current_bone_idx, joint_item);
- } else {
- //just a property
- ti = filters->create_item(ti);
- ti->set_text(0, concat);
- ti->set_selectable(0, true);
- ti->set_metadata(0, path);
- if (path == current) {
- ti->select(0);
- }
- }
- } else {
- if (ti) {
- //just a node, likely call or animation track
- ti->set_selectable(0, true);
- ti->set_metadata(0, path);
- if (path == current) {
- ti->select(0);
- }
+ joint_item->set_text(0, skeleton->get_bone_name(current_bone_idx));
+ joint_item->set_icon(0, bone_icon);
+ joint_item->set_selectable(0, true);
+ joint_item->set_metadata(0, accum + ":" + skeleton->get_bone_name(current_bone_idx));
+ joint_item->set_collapsed(true);
}
}
}
@@ -199,7 +167,6 @@ void EditorPropertyRootMotion::_node_clear() {
void EditorPropertyRootMotion::update_property() {
NodePath p = get_edited_object()->get(get_edited_property());
-
assign->set_tooltip_text(p);
if (p == NodePath()) {
assign->set_icon(Ref<Texture2D>());
@@ -208,26 +175,8 @@ void EditorPropertyRootMotion::update_property() {
return;
}
- Node *base_node = nullptr;
- if (base_hint != NodePath()) {
- if (get_tree()->get_root()->has_node(base_hint)) {
- base_node = get_tree()->get_root()->get_node(base_hint);
- }
- } else {
- base_node = Object::cast_to<Node>(get_edited_object());
- }
-
- if (!base_node || !base_node->has_node(p)) {
- assign->set_icon(Ref<Texture2D>());
- assign->set_text(p);
- return;
- }
-
- Node *target_node = base_node->get_node(p);
- ERR_FAIL_COND(!target_node);
-
- assign->set_text(target_node->get_name());
- assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));
+ assign->set_icon(Ref<Texture2D>());
+ assign->set_text(p);
}
void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) {
@@ -282,9 +231,6 @@ bool EditorInspectorRootMotionPlugin::can_handle(Object *p_object) {
bool EditorInspectorRootMotionPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) {
if (p_path == "root_motion_track" && p_object->is_class("AnimationTree") && p_type == Variant::NODE_PATH) {
EditorPropertyRootMotion *editor = memnew(EditorPropertyRootMotion);
- if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && !p_hint_text.is_empty()) {
- editor->setup(p_hint_text);
- }
add_property_editor(p_path, editor);
return true;
}
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
new file mode 100644
index 0000000000..d3097a694e
--- /dev/null
+++ b/editor/register_editor_types.cpp
@@ -0,0 +1,221 @@
+/*************************************************************************/
+/* register_editor_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_editor_types.h"
+
+#include "core/extension/native_extension_manager.h"
+
+#include "editor/animation_track_editor.h"
+#include "editor/debugger/debug_adapter/debug_adapter_server.h"
+#include "editor/editor_command_palette.h"
+#include "editor/editor_feature_profile.h"
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_file_system.h"
+#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
+#include "editor/editor_resource_picker.h"
+#include "editor/editor_resource_preview.h"
+#include "editor/editor_run_script.h"
+#include "editor/editor_translation_parser.h"
+#include "editor/editor_undo_redo_manager.h"
+#include "editor/filesystem_dock.h"
+#include "editor/import/editor_import_plugin.h"
+#include "editor/import/resource_importer_scene.h"
+#include "editor/plugins/animation_tree_editor_plugin.h"
+#include "editor/plugins/audio_stream_randomizer_editor_plugin.h"
+#include "editor/plugins/bit_map_editor_plugin.h"
+#include "editor/plugins/bone_map_editor_plugin.h"
+#include "editor/plugins/camera_3d_editor_plugin.h"
+#include "editor/plugins/cast_2d_editor_plugin.h"
+#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
+#include "editor/plugins/collision_shape_2d_editor_plugin.h"
+#include "editor/plugins/control_editor_plugin.h"
+#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
+#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
+#include "editor/plugins/curve_editor_plugin.h"
+#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/font_config_plugin.h"
+#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
+#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
+#include "editor/plugins/gpu_particles_collision_sdf_editor_plugin.h"
+#include "editor/plugins/gradient_editor_plugin.h"
+#include "editor/plugins/gradient_texture_2d_editor_plugin.h"
+#include "editor/plugins/input_event_editor_plugin.h"
+#include "editor/plugins/light_occluder_2d_editor_plugin.h"
+#include "editor/plugins/lightmap_gi_editor_plugin.h"
+#include "editor/plugins/line_2d_editor_plugin.h"
+#include "editor/plugins/material_editor_plugin.h"
+#include "editor/plugins/mesh_editor_plugin.h"
+#include "editor/plugins/mesh_instance_3d_editor_plugin.h"
+#include "editor/plugins/mesh_library_editor_plugin.h"
+#include "editor/plugins/multimesh_editor_plugin.h"
+#include "editor/plugins/navigation_link_2d_editor_plugin.h"
+#include "editor/plugins/navigation_polygon_editor_plugin.h"
+#include "editor/plugins/node_3d_editor_gizmos.h"
+#include "editor/plugins/occluder_instance_3d_editor_plugin.h"
+#include "editor/plugins/path_2d_editor_plugin.h"
+#include "editor/plugins/path_3d_editor_plugin.h"
+#include "editor/plugins/physical_bone_3d_editor_plugin.h"
+#include "editor/plugins/polygon_2d_editor_plugin.h"
+#include "editor/plugins/polygon_3d_editor_plugin.h"
+#include "editor/plugins/resource_preloader_editor_plugin.h"
+#include "editor/plugins/script_editor_plugin.h"
+#include "editor/plugins/shader_editor_plugin.h"
+#include "editor/plugins/shader_file_editor_plugin.h"
+#include "editor/plugins/skeleton_2d_editor_plugin.h"
+#include "editor/plugins/skeleton_3d_editor_plugin.h"
+#include "editor/plugins/skeleton_ik_3d_editor_plugin.h"
+#include "editor/plugins/sprite_2d_editor_plugin.h"
+#include "editor/plugins/sprite_frames_editor_plugin.h"
+#include "editor/plugins/style_box_editor_plugin.h"
+#include "editor/plugins/sub_viewport_preview_editor_plugin.h"
+#include "editor/plugins/texture_3d_editor_plugin.h"
+#include "editor/plugins/texture_editor_plugin.h"
+#include "editor/plugins/texture_layered_editor_plugin.h"
+#include "editor/plugins/texture_region_editor_plugin.h"
+#include "editor/plugins/theme_editor_plugin.h"
+#include "editor/plugins/tiles/tiles_editor_plugin.h"
+#include "editor/plugins/version_control_editor_plugin.h"
+#include "editor/plugins/visual_shader_editor_plugin.h"
+#include "editor/plugins/voxel_gi_editor_plugin.h"
+
+void register_editor_types() {
+ ResourceLoader::set_timestamp_on_load(true);
+ ResourceSaver::set_timestamp_on_save(true);
+
+ GDREGISTER_CLASS(EditorPaths);
+ GDREGISTER_CLASS(EditorPlugin);
+ GDREGISTER_CLASS(EditorTranslationParserPlugin);
+ GDREGISTER_CLASS(EditorImportPlugin);
+ GDREGISTER_CLASS(EditorScript);
+ GDREGISTER_CLASS(EditorSelection);
+ GDREGISTER_CLASS(EditorFileDialog);
+ GDREGISTER_ABSTRACT_CLASS(EditorSettings);
+ GDREGISTER_CLASS(EditorNode3DGizmo);
+ GDREGISTER_CLASS(EditorNode3DGizmoPlugin);
+ GDREGISTER_ABSTRACT_CLASS(EditorResourcePreview);
+ GDREGISTER_CLASS(EditorResourcePreviewGenerator);
+ GDREGISTER_ABSTRACT_CLASS(EditorFileSystem);
+ GDREGISTER_CLASS(EditorFileSystemDirectory);
+ GDREGISTER_CLASS(EditorVCSInterface);
+ GDREGISTER_ABSTRACT_CLASS(ScriptEditor);
+ GDREGISTER_ABSTRACT_CLASS(ScriptEditorBase);
+ GDREGISTER_CLASS(EditorSyntaxHighlighter);
+ GDREGISTER_ABSTRACT_CLASS(EditorInterface);
+ GDREGISTER_CLASS(EditorExportPlugin);
+ GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
+ GDREGISTER_CLASS(EditorResourceConversionPlugin);
+ GDREGISTER_CLASS(EditorSceneFormatImporter);
+ GDREGISTER_CLASS(EditorScenePostImportPlugin);
+ GDREGISTER_CLASS(EditorInspector);
+ GDREGISTER_CLASS(EditorInspectorPlugin);
+ GDREGISTER_CLASS(EditorProperty);
+ GDREGISTER_CLASS(AnimationTrackEditPlugin);
+ GDREGISTER_CLASS(ScriptCreateDialog);
+ GDREGISTER_CLASS(EditorFeatureProfile);
+ GDREGISTER_CLASS(EditorSpinSlider);
+ GDREGISTER_CLASS(EditorResourcePicker);
+ GDREGISTER_CLASS(EditorScriptPicker);
+ GDREGISTER_ABSTRACT_CLASS(EditorUndoRedoManager);
+
+ GDREGISTER_ABSTRACT_CLASS(FileSystemDock);
+ GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery);
+
+ GDREGISTER_CLASS(EditorScenePostImport);
+ GDREGISTER_CLASS(EditorCommandPalette);
+ GDREGISTER_CLASS(EditorDebuggerPlugin);
+ GDREGISTER_ABSTRACT_CLASS(EditorDebuggerSession);
+
+ // This list is alphabetized, and plugins that depend on Node2D are in their own section below.
+ EditorPlugins::add_by_type<AnimationTreeEditorPlugin>();
+ EditorPlugins::add_by_type<AudioStreamRandomizerEditorPlugin>();
+ EditorPlugins::add_by_type<BitMapEditorPlugin>();
+ EditorPlugins::add_by_type<BoneMapEditorPlugin>();
+ EditorPlugins::add_by_type<Camera3DEditorPlugin>();
+ EditorPlugins::add_by_type<ControlEditorPlugin>();
+ EditorPlugins::add_by_type<CPUParticles3DEditorPlugin>();
+ EditorPlugins::add_by_type<CurveEditorPlugin>();
+ EditorPlugins::add_by_type<DebugAdapterServer>();
+ EditorPlugins::add_by_type<FontEditorPlugin>();
+ EditorPlugins::add_by_type<GPUParticles3DEditorPlugin>();
+ EditorPlugins::add_by_type<GPUParticlesCollisionSDF3DEditorPlugin>();
+ EditorPlugins::add_by_type<GradientEditorPlugin>();
+ EditorPlugins::add_by_type<GradientTexture2DEditorPlugin>();
+ EditorPlugins::add_by_type<InputEventEditorPlugin>();
+ EditorPlugins::add_by_type<LightmapGIEditorPlugin>();
+ EditorPlugins::add_by_type<MaterialEditorPlugin>();
+ EditorPlugins::add_by_type<MeshEditorPlugin>();
+ EditorPlugins::add_by_type<MeshInstance3DEditorPlugin>();
+ EditorPlugins::add_by_type<MeshLibraryEditorPlugin>();
+ EditorPlugins::add_by_type<MultiMeshEditorPlugin>();
+ EditorPlugins::add_by_type<OccluderInstance3DEditorPlugin>();
+ EditorPlugins::add_by_type<Path3DEditorPlugin>();
+ EditorPlugins::add_by_type<PhysicalBone3DEditorPlugin>();
+ EditorPlugins::add_by_type<Polygon3DEditorPlugin>();
+ EditorPlugins::add_by_type<ResourcePreloaderEditorPlugin>();
+ EditorPlugins::add_by_type<ShaderEditorPlugin>();
+ EditorPlugins::add_by_type<ShaderFileEditorPlugin>();
+ EditorPlugins::add_by_type<Skeleton3DEditorPlugin>();
+ EditorPlugins::add_by_type<SkeletonIK3DEditorPlugin>();
+ EditorPlugins::add_by_type<SpriteFramesEditorPlugin>();
+ EditorPlugins::add_by_type<StyleBoxEditorPlugin>();
+ EditorPlugins::add_by_type<SubViewportPreviewEditorPlugin>();
+ EditorPlugins::add_by_type<Texture3DEditorPlugin>();
+ EditorPlugins::add_by_type<TextureEditorPlugin>();
+ EditorPlugins::add_by_type<TextureLayeredEditorPlugin>();
+ EditorPlugins::add_by_type<TextureRegionEditorPlugin>();
+ EditorPlugins::add_by_type<ThemeEditorPlugin>();
+ EditorPlugins::add_by_type<VoxelGIEditorPlugin>();
+
+ // 2D
+ EditorPlugins::add_by_type<CollisionPolygon2DEditorPlugin>();
+ EditorPlugins::add_by_type<CollisionShape2DEditorPlugin>();
+ EditorPlugins::add_by_type<CPUParticles2DEditorPlugin>();
+ EditorPlugins::add_by_type<GPUParticles2DEditorPlugin>();
+ EditorPlugins::add_by_type<LightOccluder2DEditorPlugin>();
+ EditorPlugins::add_by_type<Line2DEditorPlugin>();
+ EditorPlugins::add_by_type<NavigationLink2DEditorPlugin>();
+ EditorPlugins::add_by_type<NavigationPolygonEditorPlugin>();
+ EditorPlugins::add_by_type<Path2DEditorPlugin>();
+ EditorPlugins::add_by_type<Polygon2DEditorPlugin>();
+ EditorPlugins::add_by_type<Cast2DEditorPlugin>();
+ EditorPlugins::add_by_type<Skeleton2DEditorPlugin>();
+ EditorPlugins::add_by_type<Sprite2DEditorPlugin>();
+ EditorPlugins::add_by_type<TilesEditorPlugin>();
+}
+
+void unregister_editor_types() {
+ EditorNode::cleanup();
+ if (EditorPaths::get_singleton()) {
+ EditorPaths::free();
+ }
+
+ EditorResourcePicker::clear_caches();
+}
diff --git a/editor/register_editor_types.h b/editor/register_editor_types.h
new file mode 100644
index 0000000000..f53e7e3649
--- /dev/null
+++ b/editor/register_editor_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* register_editor_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REGISTER_EDITOR_TYPES_H
+#define REGISTER_EDITOR_TYPES_H
+
+void register_editor_types();
+void unregister_editor_types();
+
+#endif // REGISTER_EDITOR_TYPES_H
diff --git a/main/main.cpp b/main/main.cpp
index 9bd74f8afd..55f7177258 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -95,6 +95,7 @@
#include "editor/progress_dialog.h"
#include "editor/project_converter_3_to_4.h"
#include "editor/project_manager.h"
+#include "editor/register_editor_types.h"
#ifndef NO_EDITOR_SPLASH
#include "main/splash_editor.gen.h"
#endif
@@ -487,7 +488,7 @@ Error Main::test_setup() {
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
- EditorNode::register_editor_types();
+ register_editor_types();
initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR);
@@ -540,7 +541,7 @@ void Main::test_cleanup() {
#ifdef TOOLS_ENABLED
NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
- EditorNode::unregister_editor_types();
+ unregister_editor_types();
#endif
NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE);
@@ -2319,7 +2320,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
- EditorNode::register_editor_types();
+ register_editor_types();
initialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR);
@@ -3319,7 +3320,7 @@ void Main::cleanup(bool p_force) {
#ifdef TOOLS_ENABLED
NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_EDITOR);
uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR);
- EditorNode::unregister_editor_types();
+ unregister_editor_types();
#endif
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index bd6cef0b6e..60230257e0 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -808,6 +808,11 @@ String GDScript::_get_debug_path() const {
}
Error GDScript::reload(bool p_keep_state) {
+ if (reloading) {
+ return OK;
+ }
+ reloading = true;
+
bool has_instances;
{
MutexLock lock(GDScriptLanguage::singleton->mutex);
@@ -830,6 +835,7 @@ Error GDScript::reload(bool p_keep_state) {
// Loading a template, don't parse.
#ifdef TOOLS_ENABLED
if (EditorPaths::get_singleton() && basedir.begins_with(EditorPaths::get_singleton()->get_project_script_templates_dir())) {
+ reloading = false;
return OK;
}
#endif
@@ -839,11 +845,10 @@ Error GDScript::reload(bool p_keep_state) {
if (source_path.is_empty()) {
source_path = get_path();
}
- if (!source_path.is_empty()) {
- MutexLock lock(GDScriptCache::singleton->lock);
- if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
- GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
- }
+ Ref<GDScript> cached_script = GDScriptCache::get_cached_script(source_path);
+ if (!source_path.is_empty() && cached_script.is_null()) {
+ MutexLock lock(GDScriptCache::singleton->mutex);
+ GDScriptCache::singleton->shallow_gdscript_cache[source_path] = Ref<GDScript>(this);
}
}
@@ -856,6 +861,7 @@ Error GDScript::reload(bool p_keep_state) {
}
// TODO: Show all error messages.
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
+ reloading = false;
return ERR_PARSE_ERROR;
}
@@ -872,6 +878,7 @@ Error GDScript::reload(bool p_keep_state) {
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
e = e->next();
}
+ reloading = false;
return ERR_PARSE_ERROR;
}
@@ -886,8 +893,10 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
+ reloading = false;
return ERR_COMPILATION_FAILED;
} else {
+ reloading = false;
return err;
}
}
@@ -900,6 +909,7 @@ Error GDScript::reload(bool p_keep_state) {
}
#endif
+ reloading = false;
return OK;
}
@@ -1006,16 +1016,22 @@ Error GDScript::load_byte_code(const String &p_path) {
}
void GDScript::set_path(const String &p_path, bool p_take_over) {
+ String old_path = path;
if (is_root_script()) {
Script::set_path(p_path, p_take_over);
}
this->path = p_path;
+ GDScriptCache::move_script(old_path, p_path);
for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
kv.value->set_path(p_path, p_take_over);
}
}
Error GDScript::load_source_code(const String &p_path) {
+ if (p_path.is_empty() || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
+ return OK;
+ }
+
Vector<uint8_t> sourcef;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -1133,6 +1149,78 @@ GDScript *GDScript::get_root_script() {
return result;
}
+RBSet<GDScript *> GDScript::get_dependencies() {
+ RBSet<GDScript *> dependencies;
+
+ _get_dependencies(dependencies, this);
+ dependencies.erase(this);
+
+ return dependencies;
+}
+
+RBSet<GDScript *> GDScript::get_inverted_dependencies() {
+ RBSet<GDScript *> inverted_dependencies;
+
+ List<GDScript *> scripts;
+ {
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
+
+ SelfList<GDScript> *elem = GDScriptLanguage::singleton->script_list.first();
+ while (elem) {
+ scripts.push_back(elem->self());
+ elem = elem->next();
+ }
+ }
+
+ for (GDScript *scr : scripts) {
+ if (scr == nullptr || scr == this || scr->destructing) {
+ continue;
+ }
+
+ RBSet<GDScript *> scr_dependencies = scr->get_dependencies();
+ if (scr_dependencies.has(this)) {
+ inverted_dependencies.insert(scr);
+ }
+ }
+
+ return inverted_dependencies;
+}
+
+RBSet<GDScript *> GDScript::get_must_clear_dependencies() {
+ RBSet<GDScript *> dependencies = get_dependencies();
+ RBSet<GDScript *> must_clear_dependencies;
+ HashMap<GDScript *, RBSet<GDScript *>> inverted_dependencies;
+
+ for (GDScript *E : dependencies) {
+ inverted_dependencies.insert(E, E->get_inverted_dependencies());
+ }
+
+ RBSet<GDScript *> cant_clear;
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ for (GDScript *F : E.value) {
+ if (!dependencies.has(F)) {
+ cant_clear.insert(E.key);
+ for (GDScript *G : E.key->get_dependencies()) {
+ cant_clear.insert(G);
+ }
+ break;
+ }
+ }
+ }
+
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ if (cant_clear.has(E.key) || ScriptServer::is_global_class(E.key->get_fully_qualified_name())) {
+ continue;
+ }
+ must_clear_dependencies.insert(E.key);
+ }
+
+ cant_clear.clear();
+ dependencies.clear();
+ inverted_dependencies.clear();
+ return must_clear_dependencies;
+}
+
bool GDScript::has_script_signal(const StringName &p_signal) const {
if (_signals.has(p_signal)) {
return true;
@@ -1194,6 +1282,69 @@ String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript)
return class_name;
}
+GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
+ Variant::Type type = p_variant.get_type();
+ if (type != Variant::Type::OBJECT)
+ return nullptr;
+
+ Object *obj = p_variant;
+ if (obj == nullptr) {
+ return nullptr;
+ }
+
+ return Object::cast_to<GDScript>(obj);
+}
+
+void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
+ if (skip_dependencies || p_dependencies.has(this)) {
+ return;
+ }
+ p_dependencies.insert(this);
+
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ if (E.value == nullptr) {
+ continue;
+ }
+ for (const Variant &V : E.value->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ if (implicit_initializer) {
+ for (const Variant &V : implicit_initializer->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ if (implicit_ready) {
+ for (const Variant &V : implicit_ready->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ if (E.value != p_except) {
+ E.value->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ GDScript *scr = _get_gdscript_from_variant(E.value);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+}
+
GDScript::GDScript() :
script_list(this) {
#ifdef DEBUG_ENABLED
@@ -1253,33 +1404,58 @@ void GDScript::_init_rpc_methods_properties() {
}
}
-GDScript::~GDScript() {
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
+void GDScript::clear() {
+ if (clearing) {
+ return;
+ }
+ clearing = true;
- while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
- // Order matters since clearing the stack may already cause
- // the GDSCriptFunctionState to be destroyed and thus removed from the list.
- pending_func_states.remove(E);
- E->self()->_clear_stack();
+ RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
+ HashMap<GDScript *, ObjectID> must_clear_dependencies_objectids;
+
+ // Log the objectids before clearing, as a cascade of clear could
+ // remove instances that are still in the clear loop
+ for (GDScript *E : must_clear_dependencies) {
+ must_clear_dependencies_objectids.insert(E, E->get_instance_id());
+ }
+
+ for (GDScript *E : must_clear_dependencies) {
+ Object *obj = ObjectDB::get_instance(must_clear_dependencies_objectids[E]);
+ if (obj == nullptr) {
+ continue;
}
+
+ E->skip_dependencies = true;
+ E->clear();
+ E->skip_dependencies = false;
+ GDScriptCache::remove_script(E->get_path());
}
+ RBSet<StringName> member_function_names;
for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- memdelete(E.value);
+ member_function_names.insert(E.key);
+ }
+ for (const StringName &E : member_function_names) {
+ if (member_functions.has(E)) {
+ memdelete(member_functions[E]);
+ }
+ }
+ member_function_names.clear();
+ member_functions.clear();
+
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
}
if (implicit_initializer) {
memdelete(implicit_initializer);
}
+ implicit_initializer = nullptr;
if (implicit_ready) {
memdelete(implicit_ready);
}
-
- if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
- GDScriptCache::remove_script(get_path());
- }
+ implicit_ready = nullptr;
_save_orphaned_subclasses();
@@ -1289,6 +1465,27 @@ GDScript::~GDScript() {
_clear_doc();
}
#endif
+ clearing = false;
+}
+
+GDScript::~GDScript() {
+ if (destructing) {
+ return;
+ }
+ destructing = true;
+
+ clear();
+
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
+
+ while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
+ // Order matters since clearing the stack may already cause
+ // the GDScriptFunctionState to be destroyed and thus removed from the list.
+ pending_func_states.remove(E);
+ E->self()->_clear_stack();
+ }
+ }
#ifdef DEBUG_ENABLED
{
@@ -1297,6 +1494,10 @@ GDScript::~GDScript() {
GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
}
#endif
+
+ if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
+ GDScriptCache::remove_script(get_path());
+ }
}
//////////////////////////////
@@ -2336,26 +2537,27 @@ GDScriptLanguage::~GDScriptLanguage() {
// Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
SelfList<GDScript> *s = script_list.first();
while (s) {
- GDScript *scr = s->self();
// This ensures the current script is not released before we can check what's the next one
// in the list (we can't get the next upfront because we don't know if the reference breaking
// will cause it -or any other after it, for that matter- to be released so the next one
// is not the same as before).
- scr->reference();
-
- for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) {
- GDScriptFunction *func = E.value;
- for (int i = 0; i < func->argument_types.size(); i++) {
- func->argument_types.write[i].script_type_ref = Ref<Script>();
+ Ref<GDScript> scr = s->self();
+ if (scr.is_valid()) {
+ for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) {
+ GDScriptFunction *func = E.value;
+ for (int i = 0; i < func->argument_types.size(); i++) {
+ func->argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ func->return_type.script_type_ref = Ref<Script>();
+ }
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
}
- func->return_type.script_type_ref = Ref<Script>();
- }
- for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
- E.value.data_type.script_type_ref = Ref<Script>();
- }
+ // Clear backup for scripts that could slip out of the cyclic reference check
+ scr->clear();
+ }
s = s->next();
- scr->unreference();
}
singleton = nullptr;
@@ -2379,6 +2581,27 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
return Ref<GDScript>(Object::cast_to<GDScript>(obj));
}
+Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String &p_name) {
+ {
+ MutexLock lock(mutex);
+
+ SelfList<GDScript> *elem = script_list.first();
+ while (elem) {
+ GDScript *scr = elem->self();
+ scr = scr->find_class(p_name);
+ if (scr != nullptr) {
+ return scr;
+ }
+ elem = elem->next();
+ }
+ }
+
+ Ref<GDScript> scr;
+ scr.instantiate();
+ scr->fully_qualified_name = p_name;
+ return scr;
+}
+
/*************** RESOURCE ***************/
Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 61600b1258..2df89d812c 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -61,6 +61,8 @@ class GDScript : public Script {
GDCLASS(GDScript, Script);
bool tool = false;
bool valid = false;
+ bool reloading = false;
+ bool skip_dependencies = false;
struct MemberInfo {
int index = 0;
@@ -124,6 +126,8 @@ class GDScript : public Script {
int subclass_count = 0;
RBSet<Object *> instances;
+ bool destructing = false;
+ bool clearing = false;
//exported members
String source;
String path;
@@ -163,6 +167,9 @@ class GDScript : public Script {
// This method will map the class name from "RefCounted" to "MyClass.InnerClass".
static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
+ GDScript *_get_gdscript_from_variant(const Variant &p_variant);
+ void _get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except);
+
protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -173,6 +180,8 @@ protected:
static void _bind_methods();
public:
+ void clear();
+
virtual bool is_valid() const override { return valid; }
bool inherits_script(const Ref<Script> &p_script) const override;
@@ -193,6 +202,10 @@ public:
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
+ RBSet<GDScript *> get_dependencies();
+ RBSet<GDScript *> get_inverted_dependencies();
+ RBSet<GDScript *> get_must_clear_dependencies();
+
virtual bool has_script_signal(const StringName &p_signal) const override;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
@@ -270,6 +283,7 @@ class GDScriptInstance : public ScriptInstance {
friend class GDScriptLambdaCallable;
friend class GDScriptLambdaSelfCallable;
friend class GDScriptCompiler;
+ friend class GDScriptCache;
friend struct GDScriptUtilityFunctionsDefinitions;
ObjectID owner_id;
@@ -518,6 +532,8 @@ public:
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
+ Ref<GDScript> get_script_by_fully_qualified_name(const String &p_name);
+
GDScriptLanguage();
~GDScriptLanguage();
};
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 3d248e3da7..3a536b42c1 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -40,6 +40,7 @@
#include "core/templates/hash_map.h"
#include "gdscript.h"
#include "gdscript_utility_functions.h"
+#include "scene/resources/packed_scene.h"
static MethodInfo info_from_utility_func(const StringName &p_function) {
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
@@ -3111,7 +3112,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::NATIVE;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
+ if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path);
if (singl_parser.is_valid()) {
Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
@@ -3244,9 +3245,28 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
}
} else {
// TODO: Don't load if validating: use completion cache.
- p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
- if (p_preload->resource.is_null()) {
- push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+
+ // Must load GDScript and PackedScenes separately to permit cyclic references
+ // as ResourceLoader::load() detect and reject those.
+ if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "GDScript") {
+ Error err = OK;
+ Ref<GDScript> res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path);
+ p_preload->resource = res;
+ if (err != OK) {
+ push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ } else if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "PackedScene") {
+ Error err = OK;
+ Ref<PackedScene> res = GDScriptCache::get_packed_scene(p_preload->resolved_path, err, parser->script_path);
+ p_preload->resource = res;
+ if (err != OK) {
+ push_error(vformat(R"(Could not preload resource scene "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ } else {
+ p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
+ if (p_preload->resource.is_null()) {
+ push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+ }
}
}
}
@@ -3288,6 +3308,17 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
// Just try to get it.
bool valid = false;
Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
+
+ // If it's a GDScript instance, try to get the full script. Maybe it's not still completely loaded.
+ Ref<GDScript> gdscr = Ref<GDScript>(p_subscript->base->reduced_value);
+ if (!valid && gdscr.is_valid()) {
+ Error err = OK;
+ GDScriptCache::get_full_script(gdscr->get_path(), err);
+ if (err == OK) {
+ value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
+ }
+ }
+
if (!valid) {
push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
result_type.kind = GDScriptParser::DataType::VARIANT;
@@ -3670,50 +3701,43 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
scr = obj->get_script();
}
if (scr.is_valid()) {
- if (scr->is_valid()) {
- result.script_type = scr;
- result.script_path = scr->get_path();
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- result.kind = GDScriptParser::DataType::CLASS;
- // This might be an inner class, so we want to get the parser for the root.
- // But still get the inner class from that tree.
- GDScript *current = gds.ptr();
- List<StringName> class_chain;
- while (current->_owner) {
- // Push to front so it's in reverse.
- class_chain.push_front(current->name);
- current = current->_owner;
- }
-
- Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
- if (ref.is_null()) {
- push_error("Could not find script in path.", p_source);
- GDScriptParser::DataType error_type;
- error_type.kind = GDScriptParser::DataType::VARIANT;
- return error_type;
- }
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ result.script_type = scr;
+ result.script_path = scr->get_path();
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ result.kind = GDScriptParser::DataType::CLASS;
+ // This might be an inner class, so we want to get the parser for the root.
+ // But still get the inner class from that tree.
+ GDScript *current = gds.ptr();
+ List<StringName> class_chain;
+ while (current->_owner) {
+ // Push to front so it's in reverse.
+ class_chain.push_front(current->name);
+ current = current->_owner;
+ }
- GDScriptParser::ClassNode *found = ref->get_parser()->head;
+ Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
+ if (ref.is_null()) {
+ push_error("Could not find script in path.", p_source);
+ GDScriptParser::DataType error_type;
+ error_type.kind = GDScriptParser::DataType::VARIANT;
+ return error_type;
+ }
+ ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- // It should be okay to assume this exists, since we have a complete script already.
- for (const StringName &E : class_chain) {
- found = found->get_member(E).m_class;
- }
+ GDScriptParser::ClassNode *found = ref->get_parser()->head;
- result.class_type = found;
- result.script_path = ref->get_parser()->script_path;
- } else {
- result.kind = GDScriptParser::DataType::SCRIPT;
+ // It should be okay to assume this exists, since we have a complete script already.
+ for (const StringName &E : class_chain) {
+ found = found->get_member(E).m_class;
}
- result.native_type = scr->get_instance_base_type();
+
+ result.class_type = found;
+ result.script_path = ref->get_parser()->script_path;
} else {
- push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
- result.kind = GDScriptParser::DataType::VARIANT;
- result.type_source = GDScriptParser::DataType::UNDETECTED;
- result.is_meta_type = false;
+ result.kind = GDScriptParser::DataType::SCRIPT;
}
+ result.native_type = scr->get_instance_base_type();
} else {
result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) {
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 03a101b9fc..40681d9771 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -36,6 +36,7 @@
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
+#include "scene/resources/packed_scene.h"
bool GDScriptParserRef::is_valid() const {
return parser != nullptr;
@@ -96,27 +97,88 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
return result;
}
-GDScriptParserRef::~GDScriptParserRef() {
+void GDScriptParserRef::clear() {
+ if (cleared) {
+ return;
+ }
+ cleared = true;
+
if (parser != nullptr) {
memdelete(parser);
}
+
if (analyzer != nullptr) {
memdelete(analyzer);
}
- MutexLock lock(GDScriptCache::singleton->lock);
+}
+
+GDScriptParserRef::~GDScriptParserRef() {
+ clear();
+
+ MutexLock lock(GDScriptCache::singleton->mutex);
GDScriptCache::singleton->parser_map.erase(path);
}
GDScriptCache *GDScriptCache::singleton = nullptr;
+void GDScriptCache::move_script(const String &p_from, const String &p_to) {
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (E.value.has(p_from)) {
+ E.value.insert(p_to);
+ E.value.erase(p_from);
+ }
+ }
+
+ if (singleton->parser_map.has(p_from) && !p_from.is_empty()) {
+ singleton->parser_map[p_to] = singleton->parser_map[p_from];
+ }
+ singleton->parser_map.erase(p_from);
+
+ if (singleton->shallow_gdscript_cache.has(p_from) && !p_from.is_empty()) {
+ singleton->shallow_gdscript_cache[p_to] = singleton->shallow_gdscript_cache[p_from];
+ }
+ singleton->shallow_gdscript_cache.erase(p_from);
+
+ if (singleton->full_gdscript_cache.has(p_from) && !p_from.is_empty()) {
+ singleton->full_gdscript_cache[p_to] = singleton->full_gdscript_cache[p_from];
+ }
+ singleton->full_gdscript_cache.erase(p_from);
+}
+
void GDScriptCache::remove_script(const String &p_path) {
- MutexLock lock(singleton->lock);
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (!E.value.has(p_path)) {
+ continue;
+ }
+ E.value.erase(p_path);
+ }
+
+ GDScriptCache::clear_unreferenced_packed_scenes();
+
+ if (singleton->parser_map.has(p_path)) {
+ singleton->parser_map[p_path]->clear();
+ singleton->parser_map.erase(p_path);
+ }
+
+ singleton->dependencies.erase(p_path);
singleton->shallow_gdscript_cache.erase(p_path);
singleton->full_gdscript_cache.erase(p_path);
}
Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
Ref<GDScriptParserRef> ref;
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
@@ -163,7 +225,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
}
Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
@@ -185,12 +247,12 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e
script->load_source_code(p_path);
GDScriptCompiler::make_scripts(script.ptr(), parser_ref->get_parser()->get_tree(), true);
- singleton->shallow_gdscript_cache[p_path] = script.ptr();
+ singleton->shallow_gdscript_cache[p_path] = script;
return script;
}
Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner, bool p_update_from_disk) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
@@ -220,19 +282,21 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
return script;
}
+ singleton->full_gdscript_cache[p_path] = script;
+ singleton->shallow_gdscript_cache.erase(p_path);
+
r_error = script->reload(true);
if (r_error) {
+ singleton->shallow_gdscript_cache[p_path] = script;
+ singleton->full_gdscript_cache.erase(p_path);
return script;
}
- singleton->full_gdscript_cache[p_path] = script.ptr();
- singleton->shallow_gdscript_cache.erase(p_path);
-
return script;
}
Ref<GDScript> GDScriptCache::get_cached_script(const String &p_path) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
if (singleton->full_gdscript_cache.has(p_path)) {
return singleton->full_gdscript_cache[p_path];
@@ -246,11 +310,11 @@ Ref<GDScript> GDScriptCache::get_cached_script(const String &p_path) {
}
Error GDScriptCache::finish_compiling(const String &p_owner) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
// Mark this as compiled.
Ref<GDScript> script = get_cached_script(p_owner);
- singleton->full_gdscript_cache[p_owner] = script.ptr();
+ singleton->full_gdscript_cache[p_owner] = script;
singleton->shallow_gdscript_cache.erase(p_owner);
HashSet<String> depends = singleton->dependencies[p_owner];
@@ -271,13 +335,73 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
return err;
}
+Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) {
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->packed_scene_cache.has(p_path)) {
+ singleton->packed_scene_dependencies[p_path].insert(p_owner);
+ return singleton->packed_scene_cache[p_path];
+ }
+
+ Ref<PackedScene> scene;
+ scene.instantiate();
+
+ r_error = OK;
+ if (p_path.is_empty()) {
+ r_error = ERR_FILE_BAD_PATH;
+ return scene;
+ }
+
+ scene->set_path(p_path);
+ singleton->packed_scene_cache[p_path] = scene;
+ singleton->packed_scene_dependencies[p_path].insert(p_owner);
+
+ scene->recreate_state();
+ scene->reload_from_file();
+ return scene;
+}
+
+void GDScriptCache::clear_unreferenced_packed_scenes() {
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (E.value.size() > 0 || !ResourceLoader::is_imported(E.key)) {
+ continue;
+ }
+
+ singleton->packed_scene_dependencies.erase(E.key);
+ singleton->packed_scene_cache.erase(E.key);
+ }
+}
+
GDScriptCache::GDScriptCache() {
singleton = this;
}
GDScriptCache::~GDScriptCache() {
+ destructing = true;
+
+ RBSet<Ref<GDScriptParserRef>> parser_map_refs;
+ for (KeyValue<String, GDScriptParserRef *> &E : parser_map) {
+ parser_map_refs.insert(E.value);
+ }
+
+ for (Ref<GDScriptParserRef> &E : parser_map_refs) {
+ if (E.is_valid())
+ E->clear();
+ }
+
+ parser_map_refs.clear();
parser_map.clear();
shallow_gdscript_cache.clear();
full_gdscript_cache.clear();
+
+ packed_scene_cache.clear();
+ packed_scene_dependencies.clear();
+
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index fcd240ba8d..2195932aa3 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -36,6 +36,7 @@
#include "core/templates/hash_map.h"
#include "core/templates/hash_set.h"
#include "gdscript.h"
+#include "scene/resources/packed_scene.h"
class GDScriptAnalyzer;
class GDScriptParser;
@@ -56,6 +57,7 @@ private:
Status status = EMPTY;
Error result = OK;
String path;
+ bool cleared = false;
friend class GDScriptCache;
@@ -64,6 +66,7 @@ public:
Status get_status() const;
GDScriptParser *get_parser() const;
Error raise_status(Status p_new_status);
+ void clear();
GDScriptParserRef() {}
~GDScriptParserRef();
@@ -72,19 +75,25 @@ public:
class GDScriptCache {
// String key is full path.
HashMap<String, GDScriptParserRef *> parser_map;
- HashMap<String, GDScript *> shallow_gdscript_cache;
- HashMap<String, GDScript *> full_gdscript_cache;
+ HashMap<String, Ref<GDScript>> shallow_gdscript_cache;
+ HashMap<String, Ref<GDScript>> full_gdscript_cache;
HashMap<String, HashSet<String>> dependencies;
+ HashMap<String, Ref<PackedScene>> packed_scene_cache;
+ HashMap<String, HashSet<String>> packed_scene_dependencies;
friend class GDScript;
friend class GDScriptParserRef;
+ friend class GDScriptInstance;
static GDScriptCache *singleton;
- Mutex lock;
- static void remove_script(const String &p_path);
+ bool destructing = false;
+
+ Mutex mutex;
public:
+ static void move_script(const String &p_from, const String &p_to);
+ static void remove_script(const String &p_path);
static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String());
static String get_source_code(const String &p_path);
static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String());
@@ -92,6 +101,16 @@ public:
static Ref<GDScript> get_cached_script(const String &p_path);
static Error finish_compiling(const String &p_owner);
+ static Ref<PackedScene> get_packed_scene(const String &p_path, Error &r_error, const String &p_owner = "");
+ static void clear_unreferenced_packed_scenes();
+
+ static bool is_destructing() {
+ if (singleton == nullptr) {
+ return true;
+ }
+ return singleton->destructing;
+ };
+
GDScriptCache();
~GDScriptCache();
};
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 824625b745..f0ceb42f89 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -141,6 +141,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.script_type_ref = script;
}
result.script_type = script.ptr();
+ result.native_type = p_datatype.native_type;
}
} break;
case GDScriptParser::DataType::ENUM:
@@ -354,11 +355,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (class_node->identifier && class_node->identifier->name == identifier) {
res = Ref<GDScript>(main_script);
} else {
- res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
- if (res.is_null()) {
- _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
+ String global_class_path = ScriptServer::get_global_class_path(identifier);
+ if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
+ Error err = OK;
+ res = GDScriptCache::get_full_script(global_class_path, err);
+ if (err != OK) {
+ _set_error("Can't load global class " + String(identifier), p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ } else {
+ res = ResourceLoader::load(global_class_path);
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
}
}
@@ -2172,6 +2184,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
parsing_classes.insert(p_script);
+ p_script->clearing = true;
#ifdef TOOLS_ENABLED
p_script->doc_functions.clear();
p_script->doc_variables.clear();
@@ -2194,10 +2207,24 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
p_script->base = Ref<GDScript>();
p_script->_base = nullptr;
p_script->members.clear();
+
+ // This makes possible to clear script constants and member_functions without heap-use-after-free errors.
+ HashMap<StringName, Variant> constants;
+ for (const KeyValue<StringName, Variant> &E : p_script->constants) {
+ constants.insert(E.key, E.value);
+ }
p_script->constants.clear();
+ constants.clear();
+ HashMap<StringName, GDScriptFunction *> member_functions;
for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
+ member_functions.insert(E.key, E.value);
+ }
+ p_script->member_functions.clear();
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
memdelete(E.value);
}
+ member_functions.clear();
+
if (p_script->implicit_initializer) {
memdelete(p_script->implicit_initializer);
}
@@ -2212,6 +2239,8 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
p_script->implicit_initializer = nullptr;
p_script->implicit_ready = nullptr;
+ p_script->clearing = false;
+
p_script->tool = parser->is_tool();
if (!p_script->name.is_empty()) {
@@ -2454,10 +2483,8 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = inner_class->start_line;
#endif
-
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 98b3e40f1b..24a614b1ad 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -149,10 +149,17 @@ GDScriptFunction::GDScriptFunction() {
}
GDScriptFunction::~GDScriptFunction() {
+ get_script()->member_functions.erase(name);
+
for (int i = 0; i < lambdas.size(); i++) {
memdelete(lambdas[i]);
}
+ for (int i = 0; i < argument_types.size(); i++) {
+ argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ return_type.script_type_ref = Ref<Script>();
+
#ifdef DEBUG_ENABLED
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index e44038d6da..6e5f7a8520 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -430,6 +430,7 @@ public:
};
private:
+ friend class GDScript;
friend class GDScriptCompiler;
friend class GDScriptByteCodeGenerator;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 053d81893d..6fd1362e68 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -648,7 +648,13 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the class name after "class".)")) {
n_class->identifier = parse_identifier();
if (n_class->outer) {
- n_class->fqcn = n_class->outer->fqcn + "::" + n_class->identifier->name;
+ String fqcn = n_class->outer->fqcn;
+ if (fqcn.is_empty()) {
+ fqcn = script_path;
+ }
+ n_class->fqcn = fqcn + "::" + n_class->identifier->name;
+ } else {
+ n_class->fqcn = n_class->identifier->name;
}
}
@@ -1535,7 +1541,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
VariableNode *variable = static_cast<VariableNode *>(statement);
const SuiteNode::Local &local = current_suite->get_local(variable->identifier->name);
if (local.type != SuiteNode::Local::UNDEFINED) {
- push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name), variable->identifier);
}
current_suite->add_local(variable, current_function);
break;
@@ -1550,7 +1556,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
} else {
name = "variable";
}
- push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name), constant->identifier);
}
current_suite->add_local(constant, current_function);
break;
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 1ccbf9d150..7f42643c8f 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -251,7 +251,10 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
return false;
}
} else {
- if (next.get_extension().to_lower() == "gd") {
+ if (next.ends_with(".notest.gd")) {
+ next = dir->get_next();
+ continue;
+ } else if (next.get_extension().to_lower() == "gd") {
#ifndef DEBUG_ENABLED
// On release builds, skip tests marked as debug only.
Error open_err = OK;
@@ -597,6 +600,9 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
}
enable_stdout();
+
+ GDScriptCache::remove_script(script->get_path());
+
return result;
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd
index ea744e3027..c3fc176679 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd
@@ -1,7 +1,4 @@
const A := 42
-func test():
- pass
-
func something():
return "OK"
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd
index 276875dd5a..9d0324ead8 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd
@@ -1,4 +1,4 @@
-const Constants = preload("gdscript_to_preload.gd")
+const Constants = preload("gdscript_to_preload.notest.gd")
func test():
var a := Constants.A
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd
new file mode 100644
index 0000000000..b730453a8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd
@@ -0,0 +1,4 @@
+const A = preload("preload_cyclic_reference_a.notest.gd")
+
+func test():
+ A.test_cyclic_reference()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out
index d73c5eb7cd..14bb971221 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out
@@ -1 +1,2 @@
GDTEST_OK
+godot
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd
new file mode 100644
index 0000000000..7a6035ded1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd
@@ -0,0 +1,12 @@
+const B = preload("preload_cyclic_reference_b.notest.gd")
+
+const WAITING_FOR = "godot"
+
+static func test_cyclic_reference():
+ B.test_cyclic_reference()
+
+static func test_cyclic_reference_2():
+ B.test_cyclic_reference_2()
+
+static func test_cyclic_reference_3():
+ B.test_cyclic_reference_3()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd
new file mode 100644
index 0000000000..3ea5b01156
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd
@@ -0,0 +1,10 @@
+const A = preload("preload_cyclic_reference_a.notest.gd")
+
+static func test_cyclic_reference():
+ A.test_cyclic_reference_2()
+
+static func test_cyclic_reference_2():
+ A.test_cyclic_reference_3()
+
+static func test_cyclic_reference_3():
+ print(A.WAITING_FOR)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
index 5f73064cc0..beabf3d2e5 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
@@ -1,4 +1,4 @@
-const preloaded : GDScript = preload("gdscript_to_preload.gd")
+const preloaded : GDScript = preload("gdscript_to_preload.notest.gd")
func test():
var preloaded_instance: preloaded = preloaded.new()
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
index 1a25d684a0..88c0e71155 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -71,29 +71,29 @@ namespace Godot.SourceGenerators
Expression = 19,
PlaceholderText = 20,
ColorNoAlpha = 21,
- ImageCompressLossy = 22,
- ImageCompressLossless = 23,
- ObjectId = 24,
- TypeString = 25,
- NodePathToEditedNode = 26,
- MethodOfVariantType = 27,
- MethodOfBaseType = 28,
- MethodOfInstance = 29,
- MethodOfScript = 30,
- PropertyOfVariantType = 31,
- PropertyOfBaseType = 32,
- PropertyOfInstance = 33,
- PropertyOfScript = 34,
- ObjectTooBig = 35,
- NodePathValidTypes = 36,
- SaveFile = 37,
- GlobalSaveFile = 38,
- IntIsObjectid = 39,
- IntIsPointer = 41,
- ArrayType = 40,
- LocaleId = 42,
- LocalizableString = 43,
- NodeType = 44,
+ ObjectId = 22,
+ TypeString = 23,
+ NodePathToEditedNode = 24,
+ MethodOfVariantType = 25,
+ MethodOfBaseType = 26,
+ MethodOfInstance = 27,
+ MethodOfScript = 28,
+ PropertyOfVariantType = 29,
+ PropertyOfBaseType = 30,
+ PropertyOfInstance = 31,
+ PropertyOfScript = 32,
+ ObjectTooBig = 33,
+ NodePathValidTypes = 34,
+ SaveFile = 35,
+ GlobalSaveFile = 36,
+ IntIsObjectid = 37,
+ IntIsPointer = 38,
+ ArrayType = 39,
+ LocaleId = 40,
+ LocalizableString = 41,
+ NodeType = 42,
+ HideQuaternionEdit = 43,
+ Password = 44,
Max = 45
}
@@ -128,12 +128,14 @@ namespace Godot.SourceGenerators
DeferredSetResource = 33554432,
EditorInstantiateObject = 67108864,
EditorBasicSetting = 134217728,
+ ReadOnly = 268435456,
Array = 536870912,
Default = 6,
DefaultIntl = 38,
NoEditor = 2
}
+ [Flags]
public enum MethodFlags
{
Normal = 1,
diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp
index a7e5b80b66..c78b32ed49 100644
--- a/modules/multiplayer/editor/editor_network_profiler.cpp
+++ b/modules/multiplayer/editor/editor_network_profiler.cpp
@@ -67,8 +67,8 @@ void EditorNetworkProfiler::_update_frame() {
}
node->set_text(0, E.value.node_path);
- node->set_text(1, E.value.incoming_rpc == 0 ? "-" : itos(E.value.incoming_rpc));
- node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : itos(E.value.outgoing_rpc));
+ node->set_text(1, E.value.incoming_rpc == 0 ? "-" : vformat(TTR("%d (%s)"), E.value.incoming_rpc, String::humanize_size(E.value.incoming_size)));
+ node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : vformat(TTR("%d (%s)"), E.value.outgoing_rpc, String::humanize_size(E.value.outgoing_size)));
}
}
@@ -99,6 +99,12 @@ void EditorNetworkProfiler::add_node_frame_data(const RPCNodeInfo p_frame) {
nodes_data[p_frame.node].incoming_rpc += p_frame.incoming_rpc;
nodes_data[p_frame.node].outgoing_rpc += p_frame.outgoing_rpc;
}
+ if (p_frame.incoming_rpc) {
+ nodes_data[p_frame.node].incoming_size = p_frame.incoming_size / p_frame.incoming_rpc;
+ }
+ if (p_frame.outgoing_rpc) {
+ nodes_data[p_frame.node].outgoing_size = p_frame.outgoing_size / p_frame.outgoing_rpc;
+ }
if (frame_delay->is_stopped()) {
frame_delay->set_wait_time(0.1);
diff --git a/modules/multiplayer/editor/editor_network_profiler.h b/modules/multiplayer/editor/editor_network_profiler.h
index 98d12e3c0a..65d08dcd56 100644
--- a/modules/multiplayer/editor/editor_network_profiler.h
+++ b/modules/multiplayer/editor/editor_network_profiler.h
@@ -66,7 +66,7 @@ protected:
static void _bind_methods();
public:
- void add_node_frame_data(const RPCNodeInfo p_frame);
+ void add_node_frame_data(RPCNodeInfo p_frame);
void set_bandwidth(int p_incoming, int p_outgoing);
bool is_profiling();
diff --git a/modules/multiplayer/multiplayer_debugger.cpp b/modules/multiplayer/multiplayer_debugger.cpp
index 3d22af04dc..f6d22b1637 100644
--- a/modules/multiplayer/multiplayer_debugger.cpp
+++ b/modules/multiplayer/multiplayer_debugger.cpp
@@ -126,12 +126,14 @@ void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_
Array MultiplayerDebugger::RPCFrame::serialize() {
Array arr;
- arr.push_back(infos.size() * 4);
+ arr.push_back(infos.size() * 6);
for (int i = 0; i < infos.size(); ++i) {
arr.push_back(uint64_t(infos[i].node));
arr.push_back(infos[i].node_path);
arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].incoming_size);
arr.push_back(infos[i].outgoing_rpc);
+ arr.push_back(infos[i].outgoing_size);
}
return arr;
}
@@ -139,15 +141,18 @@ Array MultiplayerDebugger::RPCFrame::serialize() {
bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {
ERR_FAIL_COND_V(p_arr.size() < 1, false);
uint32_t size = p_arr[0];
- ERR_FAIL_COND_V(size % 4, false);
+ ERR_FAIL_COND_V(size % 6, false);
ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
- infos.resize(size / 4);
+ infos.resize(size / 6);
int idx = 1;
- for (uint32_t i = 0; i < size / 4; ++i) {
+ for (uint32_t i = 0; i < size / 6; i++) {
infos.write[i].node = uint64_t(p_arr[idx]);
infos.write[i].node_path = p_arr[idx + 1];
infos.write[i].incoming_rpc = p_arr[idx + 2];
- infos.write[i].outgoing_rpc = p_arr[idx + 3];
+ infos.write[i].incoming_size = p_arr[idx + 3];
+ infos.write[i].outgoing_rpc = p_arr[idx + 4];
+ infos.write[i].outgoing_size = p_arr[idx + 5];
+ idx += 6;
}
return true;
}
@@ -159,8 +164,6 @@ void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
rpc_node_data.insert(p_node, RPCNodeInfo());
rpc_node_data[p_node].node = p_node;
rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
- rpc_node_data[p_node].incoming_rpc = 0;
- rpc_node_data[p_node].outgoing_rpc = 0;
}
void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
@@ -168,15 +171,18 @@ void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts
}
void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {
- ERR_FAIL_COND(p_data.size() < 2);
- const ObjectID id = p_data[0];
- const String what = p_data[1];
+ ERR_FAIL_COND(p_data.size() != 3);
+ const String what = p_data[0];
+ const ObjectID id = p_data[1];
+ const int size = p_data[2];
init_node(id);
RPCNodeInfo &info = rpc_node_data[id];
if (what == "rpc_in") {
info.incoming_rpc++;
+ info.incoming_size += size;
} else if (what == "rpc_out") {
info.outgoing_rpc++;
+ info.outgoing_size += size;
}
}
diff --git a/modules/multiplayer/multiplayer_debugger.h b/modules/multiplayer/multiplayer_debugger.h
index 4efd1da016..c8acd2eeb4 100644
--- a/modules/multiplayer/multiplayer_debugger.h
+++ b/modules/multiplayer/multiplayer_debugger.h
@@ -41,7 +41,9 @@ public:
ObjectID node;
String node_path;
int incoming_rpc = 0;
+ int incoming_size = 0;
int outgoing_rpc = 0;
+ int outgoing_size = 0;
};
struct RPCFrame {
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
index 7df9b95b30..f0da4f9dfc 100644
--- a/modules/multiplayer/scene_cache_interface.cpp
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -99,10 +99,6 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
ERR_FAIL_COND(multiplayer_peer.is_null());
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", packet.size());
-#endif
-
multiplayer_peer->set_transfer_channel(0);
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
multiplayer->send_command(p_from, packet.ptr(), packet.size());
@@ -155,10 +151,6 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat
Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG);
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", packet.size() * p_peers.size());
-#endif
-
Error err = OK;
for (int peer_id : p_peers) {
multiplayer_peer->set_transfer_channel(0);
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
index db7c5037cd..93089cd50a 100644
--- a/modules/multiplayer/scene_multiplayer.cpp
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -40,12 +40,12 @@
#endif
#ifdef DEBUG_ENABLED
-void SceneMultiplayer::profile_bandwidth(const String &p_inout, int p_size) {
+_FORCE_INLINE_ void SceneMultiplayer::_profile_bandwidth(const String &p_what, int p_value) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
- values.push_back(p_inout);
+ values.push_back(p_what);
values.push_back(OS::get_singleton()->get_ticks_msec());
- values.push_back(p_size);
+ values.push_back(p_value);
EngineDebugger::profiler_add_frame_data("multiplayer", values);
}
}
@@ -91,6 +91,10 @@ Error SceneMultiplayer::poll() {
Error err = multiplayer_peer->get_packet(&packet, len);
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err));
+#ifdef DEBUG_ENABLED
+ _profile_bandwidth("in", len);
+#endif
+
if (pending_peers.has(sender)) {
if (pending_peers[sender].local) {
// If the auth is over, admit the peer at the first packet.
@@ -220,10 +224,6 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
ERR_FAIL_COND_MSG(root_path.is_empty(), "Multiplayer root was not initialized. If you are using custom multiplayer, remember to set the root path via SceneMultiplayer.set_root_path before using it.");
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
-#ifdef DEBUG_ENABLED
- profile_bandwidth("in", p_packet_len);
-#endif
-
// Extract the `packet_type` from the LSB three bits:
uint8_t packet_type = p_packet[0] & CMD_MASK;
@@ -258,6 +258,13 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
}
}
+#ifdef DEBUG_ENABLED
+_FORCE_INLINE_ Error SceneMultiplayer::_send(const uint8_t *p_packet, int p_packet_len) {
+ _profile_bandwidth("out", p_packet_len);
+ return multiplayer_peer->put_packet(p_packet, p_packet_len);
+}
+#endif
+
Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) {
if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) {
// Send relay packet.
@@ -268,19 +275,19 @@ Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_pa
relay_buffer->put_data(p_packet, p_packet_len);
multiplayer_peer->set_target_peer(1);
const Vector<uint8_t> data = relay_buffer->get_data_array();
- return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ return _send(data.ptr(), relay_buffer->get_position());
}
if (p_to > 0) {
ERR_FAIL_COND_V(!connected_peers.has(p_to), ERR_BUG);
multiplayer_peer->set_target_peer(p_to);
- return multiplayer_peer->put_packet(p_packet, p_packet_len);
+ return _send(p_packet, p_packet_len);
} else {
for (const int &pid : connected_peers) {
if (p_to && pid == -p_to) {
continue;
}
multiplayer_peer->set_target_peer(pid);
- multiplayer_peer->put_packet(p_packet, p_packet_len);
+ _send(p_packet, p_packet_len);
}
return OK;
}
@@ -319,7 +326,7 @@ void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_p
multiplayer_peer->set_transfer_channel(p_channel);
if (peer > 0) {
multiplayer_peer->set_target_peer(peer);
- multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ _send(data.ptr(), relay_buffer->get_position());
} else {
for (const int &P : connected_peers) {
// Not to sender, nor excluded.
@@ -327,7 +334,7 @@ void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_p
continue;
}
multiplayer_peer->set_target_peer(P);
- multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position());
+ _send(data.ptr(), relay_buffer->get_position());
}
}
if (peer == 0 || peer == -1) {
@@ -373,11 +380,11 @@ void SceneMultiplayer::_admit_peer(int p_id) {
// Send new peer to already connected.
encode_uint32(p_id, &buf[2]);
multiplayer_peer->set_target_peer(P);
- multiplayer_peer->put_packet(buf, sizeof(buf));
+ _send(buf, sizeof(buf));
// Send already connected to new peer.
encode_uint32(P, &buf[2]);
multiplayer_peer->set_target_peer(p_id);
- multiplayer_peer->put_packet(buf, sizeof(buf));
+ _send(buf, sizeof(buf));
}
}
@@ -412,7 +419,7 @@ void SceneMultiplayer::_del_peer(int p_id) {
continue;
}
multiplayer_peer->set_target_peer(P);
- multiplayer_peer->put_packet(buf, sizeof(buf));
+ _send(buf, sizeof(buf));
}
}
@@ -468,7 +475,7 @@ Error SceneMultiplayer::send_auth(int p_to, Vector<uint8_t> p_data) {
multiplayer_peer->set_target_peer(p_to);
multiplayer_peer->set_transfer_channel(0);
multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
- return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 2);
+ return _send(packet_cache.ptr(), p_data.size() + 2);
}
Error SceneMultiplayer::complete_auth(int p_peer) {
@@ -478,7 +485,7 @@ Error SceneMultiplayer::complete_auth(int p_peer) {
pending_peers[p_peer].local = true;
// Notify the remote peer that the authentication has completed.
uint8_t buf[2] = { NETWORK_COMMAND_SYS, SYS_COMMAND_AUTH };
- Error err = multiplayer_peer->put_packet(buf, 2);
+ Error err = _send(buf, 2);
// The remote peer already reported the authentication as completed, so admit the peer.
// May generate new packets, so it must happen after sending confirmation.
if (pending_peers[p_peer].remote) {
diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h
index b0ecc48f8c..da8c762134 100644
--- a/modules/multiplayer/scene_multiplayer.h
+++ b/modules/multiplayer/scene_multiplayer.h
@@ -103,6 +103,15 @@ private:
Ref<SceneReplicationInterface> replicator;
Ref<SceneRPCInterface> rpc;
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void _profile_bandwidth(const String &p_what, int p_value);
+ _FORCE_INLINE_ Error _send(const uint8_t *p_packet, int p_packet_len); // Also profiles.
+#else
+ _FORCE_INLINE_ Error _send(const uint8_t *p_packet, int p_packet_len) {
+ return multiplayer_peer->put_packet(p_packet, p_packet_len);
+ }
+#endif
+
protected:
static void _bind_methods();
@@ -163,10 +172,6 @@ public:
Ref<SceneCacheInterface> get_path_cache() { return cache; }
-#ifdef DEBUG_ENABLED
- void profile_bandwidth(const String &p_inout, int p_size);
-#endif
-
SceneMultiplayer();
~SceneMultiplayer();
};
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index f1bab7327a..bc7eaba6d4 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -362,13 +362,8 @@ Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const Obje
Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
-#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", p_size);
-#endif
-
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
peer->set_transfer_channel(0);
peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE);
diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp
index acc113c901..bfb1623077 100644
--- a/modules/multiplayer/scene_rpc_interface.cpp
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -52,16 +52,15 @@
#define BYTE_ONLY_OR_NO_ARGS_FLAG (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT)
#ifdef DEBUG_ENABLED
-_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) {
if (EngineDebugger::is_profiling("rpc")) {
Array values;
- values.push_back(p_id);
values.push_back(p_what);
+ values.push_back(p_id);
+ values.push_back(p_size);
EngineDebugger::profiler_add_frame_data("rpc", values);
}
}
-#else
-_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {}
#endif
// Returns the packet size stripping the node path added when the node is not yet cached.
@@ -277,7 +276,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
argp.resize(argc);
#ifdef DEBUG_ENABLED
- _profile_node_data("rpc_in", p_node->get_instance_id());
+ _profile_node_data("rpc_in", p_node->get_instance_id(), p_packet_len);
#endif
int out;
@@ -399,13 +398,13 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
ERR_FAIL_COND(node_id_compression > 3);
ERR_FAIL_COND(name_id_compression > 1);
- // We can now set the meta
- packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
-
#ifdef DEBUG_ENABLED
- multiplayer->profile_bandwidth("out", ofs);
+ _profile_node_data("rpc_out", p_from->get_instance_id(), ofs);
#endif
+ // We can now set the meta
+ packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
+
// Take chance and set transfer mode, since all send methods will use it.
peer->set_transfer_channel(p_config.channel);
peer->set_transfer_mode(p_config.transfer_mode);
@@ -477,10 +476,6 @@ Error SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_
}
if (p_peer_id != caller_id) {
-#ifdef DEBUG_ENABLED
- _profile_node_data("rpc_out", node->get_instance_id());
-#endif
-
_send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
}
diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h
index aa9be525a2..800293714c 100644
--- a/modules/multiplayer/scene_rpc_interface.h
+++ b/modules/multiplayer/scene_rpc_interface.h
@@ -81,8 +81,11 @@ private:
HashMap<ObjectID, RPCConfigCache> rpc_cache;
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id, int p_size);
+#endif
+
protected:
- _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 5e77902977..2c825e8f7b 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -114,7 +114,7 @@ void Polygon2D::_notification(int p_what) {
ObjectID new_skeleton_id;
- if (skeleton_node) {
+ if (skeleton_node && !invert && bone_weights.size()) {
RS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
new_skeleton_id = skeleton_node->get_instance_id();
} else {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 4c2a761138..9e440405af 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1623,7 +1623,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
Control *control = Object::cast_to<Control>(ci);
if (control) {
gui.dragging = true;
- gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos) - gui.drag_accum);
+ gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
if (gui.drag_data.get_type() != Variant::NIL) {
gui.mouse_focus = nullptr;
gui.forced_mouse_focus = false;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 712a67547b..8b2b7e118c 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -601,9 +601,13 @@ Error ResourceLoaderText::load() {
resource_current++;
int_resources[id] = res; //always assign int resources
- if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
- res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
- res->set_scene_unique_id(id);
+ if (do_assign) {
+ if (cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ res->set_path(path);
+ } else {
+ res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
+ res->set_scene_unique_id(id);
+ }
}
Dictionary missing_resource_properties;
diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp
index af84144591..413670d23e 100644
--- a/scene/resources/shape_2d.cpp
+++ b/scene/resources/shape_2d.cpp
@@ -124,5 +124,7 @@ Shape2D::Shape2D(const RID &p_rid) {
}
Shape2D::~Shape2D() {
- PhysicsServer2D::get_singleton()->free(shape);
+ if (PhysicsServer2D::get_singleton() != nullptr) {
+ PhysicsServer2D::get_singleton()->free(shape);
+ }
}
diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp
index 4423c1d7bb..44f21d2a48 100644
--- a/scene/resources/shape_3d.cpp
+++ b/scene/resources/shape_3d.cpp
@@ -128,5 +128,7 @@ Shape3D::Shape3D(RID p_shape) :
shape(p_shape) {}
Shape3D::~Shape3D() {
- PhysicsServer3D::get_singleton()->free(shape);
+ if (PhysicsServer3D::get_singleton() != nullptr) {
+ PhysicsServer3D::get_singleton()->free(shape);
+ }
}
diff --git a/servers/rendering/renderer_canvas_render.cpp b/servers/rendering/renderer_canvas_render.cpp
index 623f0c647b..07394b49a3 100644
--- a/servers/rendering/renderer_canvas_render.cpp
+++ b/servers/rendering/renderer_canvas_render.cpp
@@ -32,7 +32,7 @@
#include "servers/rendering/rendering_server_globals.h"
const Rect2 &RendererCanvasRender::Item::get_rect() const {
- if (custom_rect || (!rect_dirty && !update_when_visible)) {
+ if (custom_rect || (!rect_dirty && !update_when_visible && skeleton == RID())) {
return rect;
}
@@ -80,7 +80,7 @@ const Rect2 &RendererCanvasRender::Item::get_rect() const {
} break;
case Item::Command::TYPE_MESH: {
const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
- AABB aabb = RSG::mesh_storage->mesh_get_aabb(mesh->mesh, RID());
+ AABB aabb = RSG::mesh_storage->mesh_get_aabb(mesh->mesh, skeleton);
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 7e0070f8b7..7590f76a0c 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -2681,16 +2681,6 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
primitive_arrays.index_array[3] = shader.quad_index_array = RD::get_singleton()->index_array_create(shader.quad_index_buffer, 0, 6);
}
- { //default skeleton buffer
-
- shader.default_skeleton_uniform_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(SkeletonUniform));
- SkeletonUniform su;
- _update_transform_2d_to_mat4(Transform2D(), su.skeleton_inverse);
- _update_transform_2d_to_mat4(Transform2D(), su.skeleton_transform);
- RD::get_singleton()->buffer_update(shader.default_skeleton_uniform_buffer, 0, sizeof(SkeletonUniform), &su);
-
- shader.default_skeleton_texture_buffer = RD::get_singleton()->texture_buffer_create(32, RD::DATA_FORMAT_R32G32B32A32_SFLOAT);
- }
{
//default shadow texture to keep uniform set happy
RD::TextureFormat tf;
@@ -2834,8 +2824,6 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() {
memdelete_arr(state.light_uniforms);
RD::get_singleton()->free(state.lights_uniform_buffer);
- RD::get_singleton()->free(shader.default_skeleton_uniform_buffer);
- RD::get_singleton()->free(shader.default_skeleton_texture_buffer);
}
//shadow rendering
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index d1f3c9ec6a..3fff574098 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -144,10 +144,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
RID quad_index_array;
PipelineVariants pipeline_variants;
- // default_skeleton uniform set
- RID default_skeleton_uniform_buffer;
- RID default_skeleton_texture_buffer;
-
ShaderCompiler compiler;
} shader;
@@ -409,11 +405,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t lights[4];
};
- struct SkeletonUniform {
- float skeleton_transform[16];
- float skeleton_inverse[16];
- };
-
Item *items[MAX_RENDER_ITEMS];
bool using_directional_lights = false;
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index 8593e6b265..eb5f68849e 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -191,48 +191,6 @@ void main() {
uv += 1e-5;
}
-#ifdef USE_ATTRIBUTES
-#if 0
- if (bool(draw_data.flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone
- //skeleton transform
- ivec4 bone_indicesi = ivec4(bone_indices);
-
- uvec2 tex_ofs = bone_indicesi.x * 2;
-
- mat2x4 m;
- m = mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.x;
-
- tex_ofs = bone_indicesi.y * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.y;
-
- tex_ofs = bone_indicesi.z * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.z;
-
- tex_ofs = bone_indicesi.w * 2;
-
- m += mat2x4(
- texelFetch(skeleton_buffer, tex_ofs + 0),
- texelFetch(skeleton_buffer, tex_ofs + 1)) *
- bone_weights.w;
-
- mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse;
-
- //outvec = bone_matrix * outvec;
- }
-#endif
-#endif
-
vertex = (canvas_data.canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
vertex_interp = vertex;
diff --git a/servers/rendering/renderer_rd/shaders/skeleton.glsl b/servers/rendering/renderer_rd/shaders/skeleton.glsl
index 75bea9300b..e66bfb2bcb 100644
--- a/servers/rendering/renderer_rd/shaders/skeleton.glsl
+++ b/servers/rendering/renderer_rd/shaders/skeleton.glsl
@@ -143,8 +143,8 @@ void main() {
uint skin_offset = params.skin_stride * index;
uvec2 bones = uvec2(src_bone_weights.data[skin_offset + 0], src_bone_weights.data[skin_offset + 1]);
- uvec2 bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 3; //pre-add xform offset
- uvec2 bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 3;
+ uvec2 bones_01 = uvec2(bones.x & 0xFFFF, bones.x >> 16) * 2; //pre-add xform offset
+ uvec2 bones_23 = uvec2(bones.y & 0xFFFF, bones.y >> 16) * 2;
skin_offset += params.skin_weight_offset;
@@ -161,6 +161,13 @@ void main() {
//reverse order because its transposed
vertex = (vec4(vertex, 0.0, 1.0) * m).xy;
}
+
+ uint dst_offset = index * params.vertex_stride;
+
+ uvec2 uvertex = floatBitsToUint(vertex);
+ dst_vertices.data[dst_offset + 0] = uvertex.x;
+ dst_vertices.data[dst_offset + 1] = uvertex.y;
+
#else
vec3 vertex;
vec3 normal;
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 1e74d31383..adb882986f 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -616,7 +616,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton);
- if (!skeleton || skeleton->size == 0) {
+ if (!skeleton || skeleton->size == 0 || mesh->skeleton_aabb_version == skeleton->version) {
return mesh->aabb;
}
@@ -708,6 +708,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
}
}
+ mesh->skeleton_aabb_version = skeleton->version;
return aabb;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 6a7400631d..4765475804 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -144,6 +144,7 @@ private:
AABB aabb;
AABB custom_aabb;
+ uint64_t skeleton_aabb_version = 0;
Vector<RID> material_cache;