diff options
94 files changed, 1388 insertions, 278 deletions
diff --git a/.travis.yml b/.travis.yml index a5217c1d9c..a8bc8289e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ matrix: - mono packages: - &linux_deps [libasound2-dev, libfreetype6-dev, libgl1-mesa-dev, libglu1-mesa-dev, libx11-dev, libxcursor-dev, libxi-dev, libxinerama-dev, libxrandr-dev] - - &linux_mono_deps [mono-devel, msbuild] + - &linux_mono_deps [mono-devel, msbuild, nuget] coverity_scan: project: diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 6b6856dcc8..b9544ac166 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -500,8 +500,9 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { void FileAccessNetwork::configure() { GLOBAL_DEF("network/remote_fs/page_size", 65536); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero GLOBAL_DEF("network/remote_fs/page_read_ahead", 4); - GLOBAL_DEF("network/remote_fs/max_pages", 20); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_read_ahead", PropertyInfo(Variant::INT, "network/remote_fs/page_read_ahead", PROPERTY_HINT_RANGE, "0,8,1,or_greater")); } FileAccessNetwork::FileAccessNetwork() { @@ -519,7 +520,6 @@ FileAccessNetwork::FileAccessNetwork() { nc->unlock_mutex(); page_size = GLOBAL_GET("network/remote_fs/page_size"); read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead"); - max_pages = GLOBAL_GET("network/remote_fs/max_pages"); last_activity_val = 0; waiting_on_page = -1; last_page = -1; diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index e32dcea990..c929e8446d 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -98,7 +98,6 @@ class FileAccessNetwork : public FileAccess { int page_size; int read_ahead; - int max_pages; mutable int waiting_on_page; mutable int last_activity_val; diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 7f788c90a7..abfc73407a 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -338,6 +338,7 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater")); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 99a23bbee1..407bb78375 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -288,9 +288,28 @@ void ProjectSettings::_convert_to_last_version() { } } +/* + * This method is responsible for loading a project.godot file and/or data file + * using the following merit order: + * - If using NetworkClient, try to lookup project file or fail. + * - If --main-pack was passed by the user (`p_main_pack`), load it or fail. + * - Search for .pck file matching binary name. There are two possibilities: + * o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') + * o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck') + * For each tentative, if the file exists, load it or fail. + * - On relevant platforms (Android/iOS), lookup project file in OS resource path. + * If found, load it or fail. + * - Lookup project file in passed `p_path` (--path passed by the user), i.e. we + * are running from source code. + * If not found and `p_upwards` is true (--upwards passed by the user), look for + * project files in parent folders up to the system root (used to run a game + * from command line while in a subfolder). + * If a project file is found, load it or fail. + * If nothing was found, error out. + */ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) { - //If looking for files in network, just use network! + // If looking for files in a network client, use it directly if (FileAccessNetworkClient::get_singleton()) { @@ -302,9 +321,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - String exec_path = OS::get_singleton()->get_executable_path(); - - //Attempt with a passed main pack first + // Attempt with a user-defined main pack first if (p_main_pack != "") { @@ -320,25 +337,39 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Attempt with execname.pck + // Attempt with exec_name.pck + // (This is the usual case when distributing a Godot game.) + + // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) + // or the exec path's basename + '.pck' (Windows). + // We need to test both possibilities as extensions for Linux binaries are optional + // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). + + String exec_path = OS::get_singleton()->get_executable_path(); + if (exec_path != "") { bool found = false; - // get our filename without our path (note, using exec_path.get_file before get_basename anymore because not all file systems have dots in their file names!) - String filebase_name = exec_path.get_file().get_basename(); + String exec_dir = exec_path.get_base_dir(); + String exec_filename = exec_path.get_file(); + String exec_basename = exec_filename.get_basename(); + + // Try to load data pack at the location of the executable + // As mentioned above, we have two potential names to attempt - // try to open at the location of executable - String datapack_name = exec_path.get_base_dir().plus_file(filebase_name) + ".pck"; - if (_load_resource_pack(datapack_name)) { + if (_load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) || + _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"))) { found = true; } else { - datapack_name = filebase_name + ".pck"; - if (_load_resource_pack(datapack_name)) { + // If we couldn't find them next to the executable, we attempt + // the current working directory. Same story, two tests. + if (_load_resource_pack(exec_basename + ".pck") || + _load_resource_pack(exec_filename + ".pck")) { found = true; } } - // if we opened our package, try and load our project... + // If we opened our package, try and load our project if (found) { Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -350,17 +381,15 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } } - //Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) - if (OS::get_singleton()->get_resource_dir() != "") { - //OS will call Globals->get_resource_path which will be empty if not overridden! - //if the OS would rather use somewhere else, then it will not be empty. + // Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) + if (OS::get_singleton()->get_resource_dir() != "") { + // OS will call ProjectSettings->get_resource_path which will be empty if not overridden! + // If the OS would rather use a specific location, then it will not be empty. resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/"); - if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') + if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') { resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end - - // data.pck and data.zip are deprecated and no longer supported, apologies. - // make sure this is loaded from the resource path + } Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -371,21 +400,19 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Nothing was found, try to find a project.godot somewhere! + // Nothing was found, try to find a project file in provided path (`p_path`) + // or, if requested (`p_upwards`) in parent directories. DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!d, ERR_CANT_CREATE); - d->change_dir(p_path); - String candidate = d->get_current_dir(); String current_dir = d->get_current_dir(); - + String candidate = current_dir; bool found = false; Error err; while (true) { - err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); if (err == OK) { // Optional, we don't mind if it fails @@ -396,10 +423,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } if (p_upwards) { - // Try to load settings ascending through dirs shape! + // Try to load settings ascending through parent directories d->change_dir(".."); if (d->get_current_dir() == current_dir) - break; //not doing anything useful + break; // not doing anything useful current_dir = d->get_current_dir(); } else { break; @@ -416,6 +443,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + // If we're loading a project.godot from source code, we can operate some + // ProjectSettings conversions if need be. _convert_to_last_version(); return OK; @@ -1133,6 +1162,7 @@ ProjectSettings::ProjectSettings() { custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects"); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); + custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); //assigning here, because using GLOBAL_GET on every block for compressing can be slow Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index b0023b4c26..430004bb96 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -199,6 +199,7 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); } void register_core_singletons() { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 3e984fae32..621a94ab1a 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -1100,7 +1100,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : eh.userdata = this; add_error_handler(&eh); - profile_info.resize(CLAMP(int(ProjectSettings::get_singleton()->get("debug/settings/profiler/max_functions")), 128, 65535)); + profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); profile_info_ptrs.resize(profile_info.size()); } diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 37756bcfd8..8f654441b2 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -385,6 +385,8 @@ <constant name="SPEAKER_MODE_STEREO" value="0" enum="SpeakerMode"> Two or fewer speakers are detected. </constant> + <constant name="SPEAKER_SURROUND_31" value="1" enum="SpeakerMode"> + </constant> <constant name="SPEAKER_SURROUND_51" value="2" enum="SpeakerMode"> A 5.1 channel surround setup detected. </constant> diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml index 8a8a9a2d24..346ab9d357 100644 --- a/doc/classes/GridContainer.xml +++ b/doc/classes/GridContainer.xml @@ -11,16 +11,6 @@ <demos> </demos> <methods> - <method name="get_child_control_at_cell"> - <return type="Control"> - </return> - <argument index="0" name="row" type="int"> - </argument> - <argument index="1" name="column" type="int"> - </argument> - <description> - </description> - </method> </methods> <members> <member name="columns" type="int" setter="set_columns" getter="get_columns"> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 10ec15b99d..0e6e54c8fd 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -123,7 +123,7 @@ <argument index="0" name="button_index" type="int"> </argument> <description> - Receives a [code]JOY_BUTTON_*[/code] Enum and returns it's equivalent name as a string. + Receives a [code]JOY_BUTTON_*[/code] Enum and returns its equivalent name as a string. </description> </method> <method name="get_joy_guid" qualifiers="const"> diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index ef42726ca9..bcc9a7cad9 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -41,6 +41,12 @@ Returns the [Material] for a surface of the [Mesh] resource. </description> </method> + <method name="get_surface_material_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> <method name="set_surface_material"> <return type="void"> </return> diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml index ea6fe6d11c..3b76861afd 100644 --- a/doc/classes/NetworkedMultiplayerPeer.xml +++ b/doc/classes/NetworkedMultiplayerPeer.xml @@ -92,7 +92,7 @@ </signals> <constants> <constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode"> - Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters. + Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters. </constant> <constant name="TRANSFER_MODE_UNRELIABLE_ORDERED" value="1" enum="TransferMode"> Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [code]TRANSFER_MODE_RELIABLE[/code]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index a33ee5c363..19aa2e9f5e 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -179,6 +179,15 @@ If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through script, because those scenes don't have an owner. </description> </method> + <method name="find_parent" qualifiers="const"> + <return type="Node"> + </return> + <argument index="0" name="mask" type="String"> + </argument> + <description> + Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names. + </description> + </method> <method name="get_child" qualifiers="const"> <return type="Node"> </return> @@ -267,15 +276,6 @@ Returns the parent node of the current node, or an empty [code]Node[/code] if the node lacks a parent. </description> </method> - <method name="find_parent" qualifiers="const"> - <return type="Node"> - </return> - <argument index="0" name="mask" type="String"> - </argument> - <description> - Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case sensitive, but '*' matches zero or more characters and '?' matches any single character except '.'). Note that it does not match against the full path, just against individual node names. - </description> - </method> <method name="get_path" qualifiers="const"> <return type="NodePath"> </return> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 08df3f0ad6..19f85fae4c 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -6,10 +6,23 @@ <description> A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself. Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). Note that the node doesn't need to own itself. - Example of saving a node: + Example of saving a node with different owners: The following example creates 3 objects: [code]Node2D[/code] ([code]node[/code]), [code]RigidBody2D[/code] ([code]rigid[/code]) and [code]CollisionObject2D[/code] ([code]collision[/code]). [code]collision[/code] is a child of [code]rigid[/code] which is a child of [code]node[/code]. Only [code]rigid[/code] is owned by [code]node[/code] and [code]pack[/code] will therefore only save those two nodes, but not [code]collision[/code]. [codeblock] + # create the objects + var node = Node2D.new() + var rigid = RigidBody2D.new() + var collision = CollisionShape2D.new() + + # create the object hierachy + rigid.add_child(collision) + node.add_child(rigid) + + # change owner of rigid, but not of collision + rigid.set_owner(node) + var scene = PackedScene.new() - var result = scene.pack(child) + # only node and rigid are now packed + var result = scene.pack(node) if result == OK: ResourceSaver.save("res://path/name.scn", scene) # or user://... [/codeblock] diff --git a/doc/classes/PathFollow2D.xml b/doc/classes/PathFollow2D.xml index f9940dab2f..515c921d0d 100644 --- a/doc/classes/PathFollow2D.xml +++ b/doc/classes/PathFollow2D.xml @@ -23,6 +23,7 @@ The node's offset along the curve. </member> <member name="lookahead" type="float" setter="set_lookahead" getter="get_lookahead"> + How far to look ahead of the curve to calculate the tangent if the node is rotating. E.g. shorter lookaheads will lead to faster rotations. Default value: [code]4[/code]. </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop"> If [code]true[/code], any offset outside the path's length will wrap around, instead of stopping at the ends. Use it for cyclic paths. diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index f79baea0be..c836414dd2 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -1260,7 +1260,7 @@ The higher, the stronger. </constant> <constant name="PIN_JOINT_IMPULSE_CLAMP" value="2" enum="PinJointParam"> - If above 0, this value is the maximum value for an impulse that this Joint puts on it's ends. + If above 0, this value is the maximum value for an impulse that this Joint puts on its ends. </constant> <constant name="HINGE_JOINT_BIAS" value="0" enum="HingeJointParam"> The speed with which the two bodies get pulled together when they move in different directions. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 9efc05dfd5..3b7356ab39 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -182,7 +182,7 @@ Name of the project. It is used from both project manager and by the exporters. Overriding this as name.locale allows setting it in multiple languages. </member> <member name="application/config/use_custom_user_dir" type="bool" setter="" getter=""> - Allow the project to save to it's own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect. + Allow the project to save to its own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect. </member> <member name="application/run/disable_stderr" type="bool" setter="" getter=""> Disable printing to stderr on exported build. @@ -213,6 +213,8 @@ <member name="audio/mix_rate" type="int" setter="" getter=""> Mix rate used for audio. In general, it's better to not touch this and leave it to the host operating system. </member> + <member name="audio/output_latency" type="int" setter="" getter=""> + </member> <member name="audio/video_delay_compensation_ms" type="int" setter="" getter=""> Setting to hardcode audio delay when playing video. Best to leave this untouched unless you know what you are doing. </member> @@ -230,6 +232,8 @@ </member> <member name="compression/formats/zstd/window_log_size" type="int" setter="" getter=""> </member> + <member name="debug/gdscript/completion/autocomplete_setters_and_getters" type="bool" setter="" getter=""> + </member> <member name="debug/gdscript/warnings/constant_used_as_function" type="bool" setter="" getter=""> </member> <member name="debug/gdscript/warnings/deprecated_keyword" type="bool" setter="" getter=""> @@ -372,7 +376,7 @@ <member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter=""> Timer setting for incremental search in Tree, IntemList, etc. controls. </member> - <member name="gui/timers/text_edit_idle_detect_sec" type="int" setter="" getter=""> + <member name="gui/timers/text_edit_idle_detect_sec" type="float" setter="" getter=""> Timer for detecting idle in the editor. </member> <member name="input/ui_accept" type="Dictionary" setter="" getter=""> @@ -604,9 +608,6 @@ <member name="network/limits/packet_peer_stream/max_buffer_po2" type="int" setter="" getter=""> Default size of packet peer stream for deserializing godot data. Over this size, data is dropped. </member> - <member name="network/remote_fs/max_pages" type="int" setter="" getter=""> - Maximum amount of pages used for remote filesystem (used by debugging). - </member> <member name="network/remote_fs/page_read_ahead" type="int" setter="" getter=""> Amount of read ahead used by remote filesystem. Improves latency. </member> @@ -652,7 +653,7 @@ <member name="rendering/limits/rendering/max_renderable_elements" type="int" setter="" getter=""> Max amount of elements renderable in a frame. If more than this are visible per frame, they will be dropped. Keep in mind elements refer to mesh surfaces and not mesh themselves. </member> - <member name="rendering/limits/time/time_rollover_secs" type="int" setter="" getter=""> + <member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter=""> Shaders have a time variable that constantly increases. At some point it needs to be rolled back to zero to avoid numerical errors on shader animations. This setting specifies when. </member> <member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter=""> diff --git a/doc/classes/ResourceSaver.xml b/doc/classes/ResourceSaver.xml index bf3ea95bce..f388667891 100644 --- a/doc/classes/ResourceSaver.xml +++ b/doc/classes/ResourceSaver.xml @@ -47,5 +47,7 @@ </constant> <constant name="FLAG_COMPRESS" value="32" enum="SaverFlags"> </constant> + <constant name="FLAG_REPLACE_SUBRESOURCE_PATHS" value="64" enum="SaverFlags"> + </constant> </constants> </class> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 4f9ac68e47..2e5d5d81c9 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -149,7 +149,7 @@ <argument index="2" name="transform" type="Transform"> </argument> <description> - Append vertices from a given [Mesh] surface onto the current vertex array with specified [Transform]. + Append vertices from a given [Mesh] surface onto the current vertex array with specified [Transform]. </description> </method> <method name="begin"> @@ -187,7 +187,7 @@ <argument index="1" name="surface" type="int"> </argument> <description> - Creates a vertex array from an existing [Mesh]. + Creates a vertex array from an existing [Mesh]. </description> </method> <method name="deindex"> diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 18b5dd3483..9227c04e71 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -1111,6 +1111,7 @@ void RasterizerCanvasGLES2::initialize() { // polygon buffer { uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); poly_size *= 1024; poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); @@ -1122,6 +1123,7 @@ void RasterizerCanvasGLES2::initialize() { glBindBuffer(GL_ARRAY_BUFFER, 0); uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); index_size *= 1024; // kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 7e6d969ccb..c4b6c607a2 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -3046,6 +3046,7 @@ void RasterizerSceneGLES2::initialize() { { uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); glGenBuffers(1, &state.immediate_buffer); glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index a1f0c2680c..2a781605d1 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1307,8 +1307,7 @@ LIGHT_SHADER_CODE #define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, texture2D(p_shadow, p_pos).r) #define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, texture2DProj(p_shadow, p_pos).r) -float sample_shadow( - highp sampler2D shadow, highp vec4 spos) { +float sample_shadow(highp sampler2D shadow, highp vec4 spos) { #ifdef SHADOW_MODE_PCF_13 @@ -1923,12 +1922,12 @@ FRAGMENT_SHADER_CODE highp vec4 splane = shadow_coord; splane.xyz /= splane.w; - float shadow = sample_shadow(light_shadow_atlas, splane.xy, splane.z); + float shadow = sample_shadow(light_shadow_atlas, splane); light_att *= shadow; } #endif -#endif +#endif // LIGHT_MODE_SPOT #ifdef USE_VERTEX_LIGHTING //vertex lighting diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index a9b46baf53..f0932c2f80 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -2027,6 +2027,7 @@ void RasterizerCanvasGLES3::initialize() { { uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); poly_size *= 1024; //kb poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); @@ -2074,6 +2075,7 @@ void RasterizerCanvasGLES3::initialize() { glGenVertexArrays(1, &data.polygon_buffer_pointer_array); uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); index_size *= 1024; //kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index d3cd01a903..2f6a556773 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -436,6 +436,7 @@ void RasterizerGLES3::register_config() { GLOBAL_DEF("rendering/quality/filters/anisotropic_filter_level", 4); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/anisotropic_filter_level", PropertyInfo(Variant::INT, "rendering/quality/filters/anisotropic_filter_level", PROPERTY_HINT_RANGE, "1,16,1")); GLOBAL_DEF("rendering/limits/time/time_rollover_secs", 3600); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/time/time_rollover_secs", PropertyInfo(Variant::REAL, "rendering/limits/time/time_rollover_secs", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); } RasterizerGLES3::RasterizerGLES3() { diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index fcb68f44fe..0d4c83e0db 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -4852,10 +4852,7 @@ void RasterizerSceneGLES3::initialize() { glBindBuffer(GL_UNIFORM_BUFFER, 0); render_list.max_elements = GLOBAL_DEF_RST("rendering/limits/rendering/max_renderable_elements", (int)RenderList::DEFAULT_MAX_ELEMENTS); - if (render_list.max_elements > 1000000) - render_list.max_elements = 1000000; - if (render_list.max_elements < 1024) - render_list.max_elements = 1024; + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/rendering/max_renderable_elements", PropertyInfo(Variant::INT, "rendering/limits/rendering/max_renderable_elements", PROPERTY_HINT_RANGE, "1024,1000000,1")); { //quad buffers @@ -5054,6 +5051,7 @@ void RasterizerSceneGLES3::initialize() { { uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); glGenBuffers(1, &state.immediate_buffer); glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index a9aa152f10..406ef6af78 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -7686,6 +7686,8 @@ void RasterizerStorageGLES3::initialize() { { //transform feedback buffers uint32_t xf_feedback_size = GLOBAL_DEF_RST("rendering/limits/buffers/blend_shape_max_buffer_size_kb", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/blend_shape_max_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/blend_shape_max_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); + for (int i = 0; i < 2; i++) { glGenBuffers(1, &resources.transform_feedback_buffers[i]); diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index 9353128333..bc6ceb1e7e 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -114,11 +114,12 @@ Error AudioDriverRtAudio::init() { unsigned int buffer_frames = closest_power_of_2(latency * mix_rate / 1000); print_verbose("Audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); - short int tries = 2; + short int tries = 4; - while (tries >= 0) { + while (tries > 0) { switch (speaker_mode) { case SPEAKER_MODE_STEREO: parameters.nChannels = 2; break; + case SPEAKER_SURROUND_31: parameters.nChannels = 4; break; case SPEAKER_SURROUND_51: parameters.nChannels = 6; break; case SPEAKER_SURROUND_71: parameters.nChannels = 8; break; }; @@ -133,7 +134,9 @@ Error AudioDriverRtAudio::init() { ERR_PRINT("Unable to open audio, retrying with fewer channels..."); switch (speaker_mode) { - case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_MODE_STEREO; break; + case SPEAKER_MODE_STEREO: break; // Required to silence unhandled enum value warning. + case SPEAKER_SURROUND_31: speaker_mode = SPEAKER_MODE_STEREO; break; + case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_SURROUND_31; break; case SPEAKER_SURROUND_71: speaker_mode = SPEAKER_SURROUND_51; break; } diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index cdf0e4b829..2f2840192a 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -47,7 +47,9 @@ void EditorAbout::_notification(int p_what) { Control *base = EditorNode::get_singleton()->get_gui_base(); Ref<Font> font = base->get_font("source", "EditorFonts"); _tpl_text->add_font_override("normal_font", font); + _tpl_text->add_constant_override("line_separation", 6 * EDSCALE); _license_text->add_font_override("normal_font", font); + _license_text->add_constant_override("line_separation", 6 * EDSCALE); _logo->set_texture(base->get_icon("Logo", "EditorIcons")); } break; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d01543198a..3899fcf83f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4972,7 +4972,7 @@ EditorNode::EditorNode() { dock_vb->add_child(dock_hb); dock_select = memnew(Control); - dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE); + dock_select->set_custom_minimum_size(Size2(64, 0) * EDSCALE); dock_select->connect("gui_input", this, "_dock_select_input"); dock_select->connect("draw", this, "_dock_select_draw"); dock_select->connect("mouse_exited", this, "_dock_popup_exit"); @@ -4983,7 +4983,7 @@ EditorNode::EditorNode() { dock_select_rect_over = -1; dock_popup_selected = -1; for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->set_custom_minimum_size(Size2(230, 220) * EDSCALE); + dock_slot[i]->set_custom_minimum_size(Size2(0, 64) * EDSCALE); dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL); dock_slot[i]->set_popup(dock_select_popup); dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i)); @@ -5499,8 +5499,8 @@ EditorNode::EditorNode() { right_r_vsplit->hide(); // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize - left_r_hsplit->set_split_offset(40 * EDSCALE); - main_hsplit->set_split_offset(-40 * EDSCALE); + left_r_hsplit->set_split_offset(100 * EDSCALE); + main_hsplit->set_split_offset(-100 * EDSCALE); // Define corresponding default layout diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 96d482f47e..5148c12160 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1860,6 +1860,12 @@ void EditorPropertyNodePath::update_property() { Node *target_node = base_node->get_node(p); ERR_FAIL_COND(!target_node); + if (String(target_node->get_name()).find("@") != -1) { + assign->set_icon(Ref<Texture>()); + assign->set_text(p); + return; + } + assign->set_text(target_node->get_name()); assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 1e97920f7e..6eaa2560db 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -357,7 +357,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/theme/color_theme", "Adaptive"); hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom"); - _initial_set("text_editor/theme/line_spacing", 4); + _initial_set("text_editor/theme/line_spacing", 6); _initial_set("text_editor/theme/selection_color", Color::html("40808080")); _load_default_text_editor_theme(); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 26bc43c540..ef960f74ea 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -225,7 +225,7 @@ void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool updating_tree = false; } -void FileSystemDock::_update_display_mode() { +void FileSystemDock::_update_display_mode(bool p_force) { // Compute the new display mode DisplayMode new_display_mode; if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { @@ -234,7 +234,7 @@ void FileSystemDock::_update_display_mode() { new_display_mode = DISPLAY_MODE_SPLIT; } - if (new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) { + if (p_force || new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) { display_mode = new_display_mode; old_display_mode_setting = display_mode_setting; button_toggle_display_mode->set_pressed(display_mode_setting == DISPLAY_MODE_SETTING_SPLIT ? true : false); @@ -352,6 +352,11 @@ void FileSystemDock::_notification(int p_what) { tree->set_drop_mode_flags(0); } break; + case NOTIFICATION_THEME_CHANGED: { + if (tree->is_visible_in_tree()) { + _update_display_mode(true); + } + } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { // Update icons String ei = "EditorIcons"; @@ -360,6 +365,11 @@ void FileSystemDock::_notification(int p_what) { button_tree->set_icon(get_icon("Filesystem", ei)); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); + if (button_file_list_display_mode->is_pressed()) { + button_file_list_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); + } else { + button_file_list_display_mode->set_icon(get_icon("FileList", "EditorIcons")); + } tree_search_box->set_right_icon(get_icon("Search", ei)); tree_search_box->set_clear_button_enabled(true); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index d964515572..2aa79b1ddd 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -268,7 +268,7 @@ private: void _file_list_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); void _tree_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata); - void _update_display_mode(); + void _update_display_mode(bool p_force = false); Vector<String> _tree_get_selected(bool remove_self_inclusion = true); diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index e65a697857..40e3bb5be2 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -108,7 +108,7 @@ void BakedLightmapEditorPlugin::_bind_methods() { BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake Lightmaps")); bake->hide(); diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h index a32b573851..8d3b5b1dd6 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.h +++ b/editor/plugins/baked_lightmap_editor_plugin.h @@ -42,7 +42,7 @@ class BakedLightmapEditorPlugin : public EditorPlugin { BakedLightmap *lightmap; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp index 06da64b181..5fc5cad1ef 100644 --- a/editor/plugins/gi_probe_editor_plugin.cpp +++ b/editor/plugins/gi_probe_editor_plugin.cpp @@ -90,7 +90,7 @@ void GIProbeEditorPlugin::_bind_methods() { GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) { editor = p_node; - bake = memnew(Button); + bake = memnew(ToolButton); bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons")); bake->set_text(TTR("Bake GI Probe")); bake->hide(); diff --git a/editor/plugins/gi_probe_editor_plugin.h b/editor/plugins/gi_probe_editor_plugin.h index 017e9bd743..1b3b63f227 100644 --- a/editor/plugins/gi_probe_editor_plugin.h +++ b/editor/plugins/gi_probe_editor_plugin.h @@ -42,7 +42,7 @@ class GIProbeEditorPlugin : public EditorPlugin { GIProbe *gi_probe; - Button *bake; + ToolButton *bake; EditorNode *editor; static EditorProgress *tmp_progress; diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index 186e66f980..8df40232b0 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -350,8 +350,6 @@ ItemListEditor::ItemListEditor() { selected_idx = -1; - add_child(memnew(VSeparator)); - toolbar_button = memnew(ToolButton); toolbar_button->set_text(TTR("Items")); add_child(toolbar_button); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 080d3ff894..07a7e7952a 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -490,7 +490,7 @@ void ScriptEditor::_open_recent_script(int p_idx) { edit(text_file, true); return; } - // if it's a path then its most likely a deleted file not help + // if it's a path then it's most likely a deleted file not help } else if (path.find("::") != -1) { // built-in script Ref<Script> script = ResourceLoader::load(path); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 362b8543d4..27f5910d94 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -179,7 +179,7 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_color_override("search_result_border_color", search_result_border_color); text_edit->add_color_override("symbol_color", symbol_color); - text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4)); + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); colors_cache.symbol_color = symbol_color; colors_cache.keyword_color = keyword_color; diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 0dda95832c..ab89d170da 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -952,8 +952,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (b->is_pressed()) { int mod = _get_key_modifier(b); - if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { - set_freelook_active(true); + if (!orthogonal) { + if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) { + set_freelook_active(true); + } } } else { set_freelook_active(false); @@ -1134,7 +1136,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (clicked) { _select_clicked(clicked_wants_append, true); - //clickd processing was deferred + // Processing was deferred. clicked = 0; } @@ -1241,7 +1243,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (!clicked_includes_current) { _select_clicked(clicked_wants_append, true); - //clickd processing was deferred + // Processing was deferred. } _compute_edit(_edit.mouse_pos); @@ -1339,7 +1341,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { List<Node *> &selection = editor_selection->get_selected_node_list(); - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); float snap = 0; if (_edit.snap || spatial_editor->is_snap_enabled()) { @@ -1468,7 +1471,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { List<Node *> &selection = editor_selection->get_selected_node_list(); - bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW + // Disable local transformation for TRANSFORM_VIEW + bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); float snap = 0; if (_edit.snap || spatial_editor->is_snap_enabled()) { @@ -1643,6 +1647,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { nav_mode = NAVIGATION_ZOOM; } else if (freelook_active) { nav_mode = NAVIGATION_LOOK; + } else if (orthogonal) { + nav_mode = NAVIGATION_PAN; } } else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) { @@ -1793,7 +1799,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) { _menu_option(VIEW_CENTER_TO_SELECTION); } - if (ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { + // Orthgonal mode doesn't work in freelook. + if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) { _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL); _update_name(); } @@ -1823,7 +1830,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { set_message(TTR("Animation Key Inserted.")); } - if (ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { + // Freelook doesn't work in orthogonal mode. + if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) { set_freelook_active(!is_freelook_active()); } else if (k->get_scancode() == KEY_ESCAPE) { @@ -1911,37 +1919,38 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { - // Freelook only works properly in perspective. - // It could technically work in ortho, but it's terrible for a user due to FOV being a fixed width. - if (!orthogonal) { - real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); - - // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". - Transform prev_camera_transform = to_camera_transform(cursor); + if (orthogonal) { + _nav_pan(p_event, p_relative); + return; + } - if (invert_y_axis) { - cursor.x_rot -= p_relative.y * radians_per_pixel; - } else { - cursor.x_rot += p_relative.y * radians_per_pixel; - } - cursor.y_rot += p_relative.x * radians_per_pixel; - if (cursor.x_rot > Math_PI / 2.0) - cursor.x_rot = Math_PI / 2.0; - if (cursor.x_rot < -Math_PI / 2.0) - cursor.x_rot = -Math_PI / 2.0; + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); + real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); - // Look is like the opposite of Orbit: the focus point rotates around the camera - Transform camera_transform = to_camera_transform(cursor); - Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); - Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); - Vector3 diff = prev_pos - pos; - cursor.pos += diff; + // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". + Transform prev_camera_transform = to_camera_transform(cursor); - name = ""; - _update_name(); + if (invert_y_axis) { + cursor.x_rot -= p_relative.y * radians_per_pixel; + } else { + cursor.x_rot += p_relative.y * radians_per_pixel; } + cursor.y_rot += p_relative.x * radians_per_pixel; + if (cursor.x_rot > Math_PI / 2.0) + cursor.x_rot = Math_PI / 2.0; + if (cursor.x_rot < -Math_PI / 2.0) + cursor.x_rot = -Math_PI / 2.0; + + // Look is like the opposite of Orbit: the focus point rotates around the camera + Transform camera_transform = to_camera_transform(cursor); + Vector3 pos = camera_transform.xform(Vector3(0, 0, 0)); + Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0)); + Vector3 diff = prev_pos - pos; + cursor.pos += diff; + + name = ""; + _update_name(); } void SpatialEditorViewport::set_freelook_active(bool active_now) { @@ -3495,7 +3504,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); preview_camera = memnew(CheckBox); - preview_camera->set_position(Point2(10 * EDSCALE, 15 * EDSCALE + view_menu->get_size().height)); + preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton. preview_camera->set_text(TTR("Preview")); surface->add_child(preview_camera); preview_camera->hide(); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 4ff7046a35..1a43b16f3e 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -131,7 +131,7 @@ void TextEditor::_load_theme_settings() { text_edit->add_color_override("search_result_border_color", search_result_border_color); text_edit->add_color_override("symbol_color", symbol_color); - text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4)); + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6)); colors_cache.font_color = text_color; colors_cache.symbol_color = symbol_color; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 45c20121da..f908641bab 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -53,6 +53,13 @@ void SceneTreeDock::_nodes_drag_begin() { } } +void SceneTreeDock::_quick_open() { + Vector<String> files = quick_open->get_selected_files(); + for (int i = 0; i < files.size(); i++) { + instance(files[i]); + } +} + void SceneTreeDock::_input(Ref<InputEvent> p_event) { Ref<InputEventMouseButton> mb = p_event; @@ -319,16 +326,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } - file->set_mode(EditorFileDialog::MODE_OPEN_FILE); - List<String> extensions; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions); - file->clear_filters(); - for (int i = 0; i < extensions.size(); i++) { - - file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - } - - file->popup_centered_ratio(); + quick_open->popup_dialog("PackedScene", true); + quick_open->set_title(TTR("Instance Child Scene")); } break; case TOOL_REPLACE: { @@ -1307,6 +1306,13 @@ bool SceneTreeDock::_validate_no_foreign() { return false; } + // When edited_scene inherits from another one the root Node will be the parent Scene, + // we don't want to consider that Node a foreign one otherwise we would not be able to + // delete it + if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene == E->get()) { + continue; + } + if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get())) >= 0) { accept->set_text(TTR("Can't operate on nodes the current scene inherits from!")); @@ -2296,6 +2302,7 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_new_scene_from"), &SceneTreeDock::_new_scene_from); ClassDB::bind_method(D_METHOD("_nodes_dragged"), &SceneTreeDock::_nodes_dragged); ClassDB::bind_method(D_METHOD("_files_dropped"), &SceneTreeDock::_files_dropped); + ClassDB::bind_method(D_METHOD("_quick_open"), &SceneTreeDock::_quick_open); ClassDB::bind_method(D_METHOD("_script_dropped"), &SceneTreeDock::_script_dropped); ClassDB::bind_method(D_METHOD("_tree_rmb"), &SceneTreeDock::_tree_rmb); ClassDB::bind_method(D_METHOD("_filter_changed"), &SceneTreeDock::_filter_changed); @@ -2445,9 +2452,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel accept = memnew(AcceptDialog); add_child(accept); - file = memnew(EditorFileDialog); - add_child(file); - file->connect("file_selected", this, "instance"); + quick_open = memnew(EditorQuickOpen); + add_child(quick_open); + quick_open->connect("quick_open", this, "_quick_open"); set_process_unhandled_key_input(true); delete_dialog = memnew(ConfirmationDialog); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index eea34a3ec5..3939f4f361 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_sub_scene.h" #include "editor/groups_editor.h" +#include "editor/quick_open.h" #include "editor/rename_dialog.h" #include "editor/reparent_dialog.h" #include "editor/script_create_dialog.h" @@ -125,7 +126,7 @@ class SceneTreeDock : public VBoxContainer { ConfirmationDialog *editable_instance_remove_dialog; ReparentDialog *reparent_dialog; - EditorFileDialog *file; + EditorQuickOpen *quick_open; EditorSubScene *import_subscene_dialog; EditorFileDialog *new_scene_from_dialog; @@ -194,6 +195,7 @@ class SceneTreeDock : public VBoxContainer { void _nodes_dragged(Array p_nodes, NodePath p_to, int p_type); void _files_dropped(Vector<String> p_files, NodePath p_to, int p_type); void _script_dropped(String p_file, NodePath p_to); + void _quick_open(); void _tree_rmb(const Vector2 &p_menu_pos); diff --git a/main/main.cpp b/main/main.cpp index e9cd183d8a..6310281ff8 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -738,9 +738,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/multithreaded_server/rid_pool_prealloc", PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1")); // No negative and limit to 500 due to crashes GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_chars_per_second", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_chars_per_second", PROPERTY_HINT_RANGE, "0, 4096, 1, or_greater")); GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_messages_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_messages_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater")); GLOBAL_DEF("network/limits/debugger_stdout/max_errors_per_frame", 10); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/debugger_stdout/max_errors_per_frame", PropertyInfo(Variant::INT, "network/limits/debugger_stdout/max_errors_per_frame", PROPERTY_HINT_RANGE, "0, 20, 1, or_greater")); if (debug_mode == "remote") { @@ -813,6 +817,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph GLOBAL_DEF("logging/file_logging/enable_file_logging", false); GLOBAL_DEF("logging/file_logging/log_path", "user://logs/log.txt"); GLOBAL_DEF("logging/file_logging/max_log_files", 10); + ProjectSettings::get_singleton()->set_custom_property_info("logging/file_logging/max_log_files", PropertyInfo(Variant::INT, "logging/file_logging/max_log_files", PROPERTY_HINT_RANGE, "0,20,1,or_greater")); //no negative numbers if (FileAccess::get_create_func(FileAccess::ACCESS_USERDATA) && GLOBAL_GET("logging/file_logging/enable_file_logging")) { String base_path = GLOBAL_GET("logging/file_logging/log_path"); int max_files = GLOBAL_GET("logging/file_logging/max_log_files"); @@ -1003,6 +1008,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_iterations_per_second(GLOBAL_DEF("physics/common/physics_fps", 60)); Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); Engine::get_singleton()->set_target_fps(GLOBAL_DEF("debug/settings/fps/force_fps", 0)); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/fps/force_fps", PropertyInfo(Variant::INT, "debug/settings/fps/force_fps", PROPERTY_HINT_RANGE, "0,120,1,or_greater")); GLOBAL_DEF("debug/settings/stdout/print_fps", false); @@ -1011,10 +1017,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (frame_delay == 0) { frame_delay = GLOBAL_DEF("application/run/frame_delay_msec", 0); + ProjectSettings::get_singleton()->set_custom_property_info("application/run/frame_delay_msec", PropertyInfo(Variant::INT, "application/run/frame_delay_msec", PROPERTY_HINT_RANGE, "0,100,1,or_greater")); // No negative numbers } OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(GLOBAL_DEF("application/run/low_processor_mode_sleep_usec", 8000)); + ProjectSettings::get_singleton()->set_custom_property_info("application/run/low_processor_mode_sleep_usec", PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater")); // No negative numbers Engine::get_singleton()->set_frame_delay(frame_delay); @@ -1643,7 +1651,7 @@ bool Main::start() { GLOBAL_DEF("display/window/stretch/aspect", "ignore"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand")); GLOBAL_DEF("display/window/stretch/shrink", 1); - ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::STRING, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1")); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::REAL, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1")); sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true)); sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true)); GLOBAL_DEF("gui/common/snap_controls_to_pixels", true); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 48c1760662..ef86ccae14 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2065,12 +2065,12 @@ GDScriptLanguage::GDScriptLanguage() { _debug_call_stack_pos = 0; int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 + if (ScriptDebugger::get_singleton()) { //debugging enabled! _debug_max_call_stack = dmcs; - if (_debug_max_call_stack < 1024) - _debug_max_call_stack = 1024; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); } else { @@ -2081,6 +2081,7 @@ GDScriptLanguage::GDScriptLanguage() { #ifdef DEBUG_ENABLED GLOBAL_DEF("debug/gdscript/warnings/enable", true); GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); + GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false); for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_")); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index ddd9e6b01c..2ce92f340d 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1999,7 +1999,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (!_static) { List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods, false, true); + bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize(); + ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) { continue; diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 9a62b0fdc9..0e5dd9b4cf 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -44,7 +44,7 @@ def make_cs_files_header(src, dst, version_dst): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) - inserted_files += '\tr_files.insert("' + filepath_src_rel + '", ' \ + inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ 'CompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' @@ -102,6 +102,75 @@ env_mono = conf.Finish() import os +def find_nuget_unix(): + import os.path + import sys + + hint_dirs = ['/opt/novell/mono/bin'] + if sys.platform == 'darwin': + hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs + + for hint_dir in hint_dirs: + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path): + return hint_path + elif os.path.isfile(hint_path + '.exe'): + return hint_path + '.exe' + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + if os.path.isfile(hint_path + '.exe') and os.access(hint_path + '.exe', os.X_OK): + return hint_path + '.exe' + + return None + + +def find_nuget_windows(): + import mono_reg_utils as monoreg + + mono_root = '' + bits = env['bits'] + + if bits == '32': + if os.getenv('MONO32_PREFIX'): + mono_root = os.getenv('MONO32_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + else: + if os.getenv('MONO64_PREFIX'): + mono_root = os.getenv('MONO64_PREFIX') + else: + mono_root = monoreg.find_mono_root_dir(bits) + + if mono_root: + mono_bin_dir = os.path.join(mono_root, 'bin') + nuget_mono = os.path.join(mono_bin_dir, 'nuget.bat') + + if os.path.isfile(nuget_mono): + return nuget_mono + + # Standalone NuGet + + for hint_dir in os.environ['PATH'].split(os.pathsep): + hint_dir = hint_dir.strip('"') + hint_path = os.path.join(hint_dir, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + + return None + + def find_msbuild_unix(filename): import os.path import sys @@ -176,14 +245,17 @@ def mono_build_solution(source, target, env): import mono_reg_utils as monoreg from shutil import copyfile - framework_path = '' + sln_path = os.path.abspath(str(source[0])) + target_path = os.path.abspath(str(target[0])) + framework_path = '' msbuild_env = os.environ.copy() # Needed when running from Developer Command Prompt for VS if 'PLATFORM' in msbuild_env: del msbuild_env['PLATFORM'] + # Find MSBuild if os.name == 'nt': msbuild_info = find_msbuild_windows() if msbuild_info is None: @@ -213,11 +285,27 @@ def mono_build_solution(source, target, env): print('MSBuild path: ' + msbuild_path) + # Find NuGet + nuget_path = find_nuget_windows() if os.name == 'nt' else find_nuget_unix() + if nuget_path is None: + raise RuntimeError('Cannot find NuGet executable') + + print('NuGet path: ' + nuget_path) + + # Do NuGet restore + + try: + subprocess.check_call([nuget_path, 'restore', sln_path]) + except subprocess.CalledProcessError: + raise RuntimeError('GodotSharpTools: NuGet restore failed') + + # Build solution + build_config = 'Release' msbuild_args = [ msbuild_path, - os.path.abspath(str(source[0])), + sln_path, '/p:Configuration=' + build_config, ] @@ -227,20 +315,24 @@ def mono_build_solution(source, target, env): try: subprocess.check_call(msbuild_args, env=msbuild_env) except subprocess.CalledProcessError: - raise RuntimeError('GodotSharpTools build failed') + raise RuntimeError('GodotSharpTools: Build failed') + + # Copy files - src_dir = os.path.abspath(os.path.join(str(source[0]), os.pardir, 'bin', build_config)) - dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir)) + src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config)) + dst_dir = os.path.abspath(os.path.join(target_path, os.pardir)) + asm_file = 'GodotSharpTools.dll' if not os.path.isdir(dst_dir): if os.path.exists(dst_dir): raise RuntimeError('Target directory is a file') os.makedirs(dst_dir) - asm_file = 'GodotSharpTools.dll' - copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file)) + # Dependencies + copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll")) + if env['tools']: output_dir = Dir('#bin').abspath editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 5160d42367..a048baf5d7 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -32,6 +32,7 @@ #include <mono/metadata/threads.h> +#include "core/io/json.h" #include "core/os/file_access.h" #include "core/os/os.h" #include "core/os/thread.h" @@ -42,7 +43,6 @@ #include "editor/csharp_project.h" #include "editor/editor_node.h" #include "editor/godotsharp_editor.h" -#include "utils/string_utils.h" #endif #include "godotsharp_dirs.h" @@ -50,6 +50,7 @@ #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" +#include "utils/string_utils.h" #include "utils/thread_local.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -370,6 +371,7 @@ bool CSharpLanguage::supports_builtin_mode() const { return false; } +#ifdef TOOLS_ENABLED static String variant_type_to_managed_name(const String &p_var_type_name) { if (p_var_type_name.empty()) @@ -432,8 +434,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return p_var_type_name; } -String CSharpLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const { -#ifdef TOOLS_ENABLED +String CSharpLanguage::make_function(const String &, const String &p_name, const PoolStringArray &p_args) const { // FIXME // - Due to Godot's API limitation this just appends the function to the end of the file // - Use fully qualified name if there is ambiguity @@ -449,10 +450,12 @@ String CSharpLanguage::make_function(const String &p_class, const String &p_name s += ")\n{\n // Replace with function body.\n}\n"; return s; +} #else +String CSharpLanguage::make_function(const String &, const String &, const PoolStringArray &) const { return String(); -#endif } +#endif String CSharpLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED @@ -822,6 +825,49 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { } #endif +void CSharpLanguage::project_assembly_loaded() { + + scripts_metadata.clear(); + + String scripts_metadata_filename = "scripts_metadata."; + +#ifdef TOOLS_ENABLED + scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player"; +#else +#ifdef DEBUG_ENABLED + scripts_metadata_filename += "debug"; +#else + scripts_metadata_filename += "release"; +#endif +#endif + + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename); + + if (FileAccess::exists(scripts_metadata_path)) { + String old_json; + + Error ferr = read_all_file_utf8(scripts_metadata_path, old_json); + ERR_FAIL_COND(ferr != OK); + + Variant old_dict_var; + String err_str; + int err_line; + Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line); + if (json_err != OK) { + ERR_PRINTS("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ")"); + return; + } + + scripts_metadata = old_dict_var.operator Dictionary(); + + print_verbose("Successfully loaded scripts metadata"); + } else { + if (!Engine::get_singleton()->is_editor_hint()) { + ERR_PRINT("Missing scripts metadata file"); + } + } +} + void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("cs"); @@ -1407,6 +1453,8 @@ bool CSharpInstance::_unreference_owner_unsafe() { if (!unsafe_referenced) return false; // Already unreferenced + unsafe_referenced = false; + // Called from CSharpInstance::mono_object_disposed() or ~CSharpInstance() // Unsafe refcount decrement. The managed instance also counts as a reference. @@ -2208,10 +2256,27 @@ bool CSharpScript::can_instance() const { #endif #ifdef TOOLS_ENABLED - return valid && (tool || ScriptServer::is_scripting_enabled()); + bool extra_cond = tool || ScriptServer::is_scripting_enabled(); #else - return valid; + bool extra_cond = true; #endif + + // FIXME Need to think this through better. + // For tool scripts, this will never fire if the class is not found. That's because we + // don't know if it's a tool script if we can't find the class to access the attributes. + if (extra_cond && !script_class) { + if (GDMono::get_singleton()->get_project_assembly() == NULL) { + // The project assembly is not loaded + ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); + ERR_FAIL_V(NULL); + } else { + // The project assembly is loaded, but the class could not found + ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); + ERR_FAIL_V(NULL); + } + } + + return valid && extra_cond; } StringName CSharpScript::get_instance_base_type() const { @@ -2317,20 +2382,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { CRASH_COND(!valid); #endif - if (!script_class) { - if (GDMono::get_singleton()->get_project_assembly() == NULL) { - // The project assembly is not loaded - ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); - ERR_FAIL_V(NULL); - } else { - // The project assembly is loaded, but the class could not found - ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); - ERR_FAIL_V(NULL); - } - } - - ERR_FAIL_COND_V(!valid, NULL); - if (native) { String native_name = native->get_name(); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { @@ -2418,7 +2469,23 @@ Error CSharpScript::reload(bool p_keep_state) { GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly(); if (project_assembly) { - script_class = project_assembly->get_object_derived_class(name); + const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path()); + if (script_metadata_var) { + Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"]; + const Variant *namespace_ = script_metadata.getptr("namespace"); + const Variant *class_name = script_metadata.getptr("class_name"); + ERR_FAIL_NULL_V(namespace_, ERR_BUG); + ERR_FAIL_NULL_V(class_name, ERR_BUG); + GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String()); + if (klass) { + bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass); + ERR_FAIL_COND_V(!obj_type, ERR_BUG); + script_class = klass; + } + } else { + // Missing script metadata. Fallback to legacy method + script_class = project_assembly->get_object_derived_class(name); + } valid = script_class != NULL; @@ -2546,29 +2613,14 @@ int CSharpScript::get_member_line(const StringName &p_member) const { Error CSharpScript::load_source_code(const String &p_path) { - PoolVector<uint8_t> sourcef; - Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V(err != OK, err); - - int len = f->get_len(); - sourcef.resize(len + 1); - PoolVector<uint8_t>::Write w = sourcef.write(); - int r = f->get_buffer(w.ptr(), len); - f->close(); - memdelete(f); - ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); - w[len] = 0; - - String s; - if (s.parse_utf8((const char *)w.ptr())) { - - ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); - ERR_FAIL_V(ERR_INVALID_DATA); + Error ferr = read_all_file_utf8(p_path, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + } + ERR_FAIL_V(ferr); } - source = s; - #ifdef TOOLS_ENABLED source_changed_cache = true; #endif diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 8b0a095890..fc604df2a0 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -67,7 +67,7 @@ class CSharpScript : public Script { friend class CSharpInstance; friend class CSharpLanguage; - friend class CSharpScriptDepSort; + friend struct CSharpScriptDepSort; bool tool; bool valid; @@ -275,6 +275,8 @@ class CSharpLanguage : public ScriptLanguage { int lang_idx; + Dictionary scripts_metadata; + public: StringNameCache string_names; @@ -295,6 +297,10 @@ public: void reload_assemblies_if_needed(bool p_soft_reload); #endif + void project_assembly_loaded(); + + _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; } + virtual String get_name() const; /* LANGUAGE FUNCTIONS */ diff --git a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj index fceb732426..f9e9f41977 100644 --- a/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj +++ b/modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj @@ -31,6 +31,9 @@ <Reference Include="System" /> <Reference Include="Microsoft.Build" /> <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL"> + <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="StringExtensions.cs" /> @@ -43,5 +46,8 @@ <Compile Include="Utils\OS.cs" /> <Compile Include="Editor\GodotSharpExport.cs" /> </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs index f00ec5a2ad..647d9ac81d 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs @@ -1,4 +1,5 @@ using System; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -7,7 +8,10 @@ namespace GodotSharpTools.Project { public static bool HasItem(this ProjectRootElement root, string itemType, string include) { - string includeNormalized = include.NormalizePath(); + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + string normalizedInclude = include.NormalizePath(); foreach (var itemGroup in root.ItemGroups) { @@ -16,10 +20,14 @@ namespace GodotSharpTools.Project foreach (var item in itemGroup.Items) { - if (item.ItemType == itemType) + if (item.ItemType != itemType) + continue; + + var glob = Glob.Parse(item.Include.NormalizePath(), globOptions); + + if (glob.IsMatch(normalizedInclude)) { - if (item.Include.NormalizePath() == includeNormalized) - return true; + return true; } } } diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 1d863e6f61..574b711b29 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -6,6 +6,9 @@ namespace GodotSharpTools.Project { public static class ProjectGenerator { + const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"; + const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}"; + public static string GenCoreApiProject(string dir, string[] compileItems) { string path = Path.Combine(dir, CoreApiProject + ".csproj"); @@ -15,6 +18,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", CoreApiProjectGuid); GenAssemblyInfoFile(root, dir, CoreApiProject, new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProject + "\")]" }, @@ -39,6 +43,7 @@ namespace GodotSharpTools.Project mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml")); mainGroup.SetProperty("RootNamespace", "Godot"); + mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid); GenAssemblyInfoFile(root, dir, EditorApiProject); diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs index 6889ea715f..a13f4fd6ef 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs @@ -1,5 +1,6 @@ -using System; +using System.Collections.Generic; using System.IO; +using DotNet.Globbing; using Microsoft.Build.Construction; namespace GodotSharpTools.Project @@ -10,8 +11,61 @@ namespace GodotSharpTools.Project { var dir = Directory.GetParent(projectPath).FullName; var root = ProjectRootElement.Open(projectPath); - if (root.AddItemChecked(itemType, include.RelativeToPath(dir).Replace("/", "\\"))) + var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\"); + + if (root.AddItemChecked(itemType, normalizedInclude)) root.Save(); } + + private static string[] GetAllFilesRecursive(string rootDirectory, string mask) + { + string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories); + + // We want relative paths + for (int i = 0; i < files.Length; i++) { + files[i] = files[i].RelativeToPath(rootDirectory); + } + + return files; + } + + public static string[] GetIncludeFiles(string projectPath, string itemType) + { + var result = new List<string>(); + var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs"); + + GlobOptions globOptions = new GlobOptions(); + globOptions.Evaluation.CaseInsensitive = false; + + var root = ProjectRootElement.Open(projectPath); + + foreach (var itemGroup in root.ItemGroups) + { + if (itemGroup.Condition.Length != 0) + continue; + + foreach (var item in itemGroup.Items) + { + if (item.ItemType != itemType) + continue; + + string normalizedInclude = item.Include.NormalizePath(); + + var glob = Glob.Parse(normalizedInclude, globOptions); + + // TODO Check somehow if path has no blog to avoid the following loop... + + foreach (var existingFile in existingFiles) + { + if (glob.IsMatch(existingFile)) + { + result.Add(existingFile); + } + } + } + } + + return result.ToArray(); + } } } diff --git a/modules/mono/editor/GodotSharpTools/StringExtensions.cs b/modules/mono/editor/GodotSharpTools/StringExtensions.cs index b66c86f8ce..b0436d2f18 100644 --- a/modules/mono/editor/GodotSharpTools/StringExtensions.cs +++ b/modules/mono/editor/GodotSharpTools/StringExtensions.cs @@ -36,7 +36,9 @@ namespace GodotSharpTools public static bool IsAbsolutePath(this string path) { - return path.StartsWith("/") || path.StartsWith("\\") || path.StartsWith(driveRoot); + return path.StartsWith("/", StringComparison.Ordinal) || + path.StartsWith("\\", StringComparison.Ordinal) || + path.StartsWith(driveRoot, StringComparison.Ordinal); } public static string CsvEscape(this string value, char delimiter = ',') diff --git a/modules/mono/editor/GodotSharpTools/packages.config b/modules/mono/editor/GodotSharpTools/packages.config new file mode 100644 index 0000000000..2c7cb0bd4b --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index 6f223b75a3..03db765c2e 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -30,11 +30,15 @@ #include "csharp_project.h" +#include "core/io/json.h" #include "core/os/os.h" #include "core/project_settings.h" +#include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" +#include "../utils/string_utils.h" +#include "script_class_parser.h" namespace CSharpProject { @@ -118,4 +122,109 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str ERR_FAIL(); } } + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) { + + _GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN) + + GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils"); + + void *args[2] = { + GDMonoMarshal::mono_string_from_godot(p_project_path), + GDMonoMarshal::mono_string_from_godot("Compile") + }; + + MonoException *exc = NULL; + MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc); + + if (exc) { + GDMonoUtils::debug_print_unhandled_exception(exc); + ERR_FAIL_V(FAILED); + } + + PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret); + PoolStringArray::Read r = project_files.read(); + + Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata(); + Dictionary new_dict; + + for (int i = 0; i < project_files.size(); i++) { + const String &project_file = ("res://" + r[i]).simplify_path(); + + uint64_t modified_time = FileAccess::get_modified_time(project_file); + + const Variant *old_file_var = old_dict.getptr(project_file); + if (old_file_var) { + Dictionary old_file_dict = old_file_var->operator Dictionary(); + + if (old_file_dict["modified_time"].operator uint64_t() == modified_time) { + // No changes so no need to parse again + new_dict[project_file] = old_file_dict; + continue; + } + } + + ScriptClassParser scp; + Error err = scp.parse_file(project_file); + if (err != OK) { + ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file); + ERR_FAIL_V(err); + } + + Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes(); + + bool found = false; + Dictionary class_dict; + + String search_name = project_file.get_file().get_basename(); + + for (int j = 0; j < classes.size(); j++) { + const ScriptClassParser::ClassDecl &class_decl = classes[j]; + + if (class_decl.base.size() == 0) + continue; // Does not inherit nor implement anything, so it can't be a script class + + String class_cmp; + + if (class_decl.nested) { + class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1); + } else { + class_cmp = class_decl.name; + } + + if (class_cmp != search_name) + continue; + + class_dict["namespace"] = class_decl.namespace_; + class_dict["class_name"] = class_decl.name; + class_dict["nested"] = class_decl.nested; + + found = true; + break; + } + + if (found) { + Dictionary file_dict; + file_dict["modified_time"] = modified_time; + file_dict["class"] = class_dict; + new_dict[project_file] = file_dict; + } + } + + if (new_dict.size()) { + String json = JSON::print(new_dict, "", false); + + Error ferr; + FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr); + ERR_EXPLAIN("Cannot open file for writing: " + p_output_path); + ERR_FAIL_COND_V(ferr != OK, ferr); + f->store_string(json); + f->flush(); + f->close(); + memdelete(f); + } + + return OK; +} + } // namespace CSharpProject diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h index d852139de0..aeeeff50f0 100644 --- a/modules/mono/editor/csharp_project.h +++ b/modules/mono/editor/csharp_project.h @@ -40,6 +40,9 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>()); void add_item(const String &p_project_path, const String &p_item_type, const String &p_include); + +Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path); + } // namespace CSharpProject #endif // CSHARP_PROJECT_H diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index b504cfe712..6216213716 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -39,6 +39,7 @@ #include "../mono_gd/gd_mono_marshal.h" #include "../utils/path_utils.h" #include "bindings_generator.h" +#include "csharp_project.h" #include "godotsharp_editor.h" #define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)" @@ -268,7 +269,7 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & if (!FileAccess::exists(assembly_dst) || FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String xml_file = p_assembly_name + ".xml"; if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) @@ -280,8 +281,6 @@ bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String & Error err = da->copy(assembly_src, assembly_dst); - memdelete(da); - if (err != OK) { show_build_error_dialog("Failed to copy " + assembly_file); return false; @@ -398,6 +397,18 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) { bool GodotSharpBuilds::editor_build_callback() { + String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player"); + + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); + ERR_FAIL_COND_V(metadata_err != OK, false); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); + + ERR_EXPLAIN("Failed to copy scripts metadata file"); + ERR_FAIL_COND_V(copy_err != OK, false); + return build_project_blocking("Tools"); } diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 933ede93dc..509ec961fb 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -37,6 +37,7 @@ #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_marshal.h" +#include "csharp_project.h" #include "godotsharp_builds.h" static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() { @@ -86,6 +87,12 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug String build_config = p_debug ? "Debug" : "Release"; + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release")); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); + + ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path)); + ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config)); // Add dependency assemblies @@ -124,7 +131,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) { String depend_src_path = E->value(); String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file()); - ERR_FAIL_COND(!_add_assembly(depend_src_path, depend_dst_path)); + ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path)); } // Mono specific export template extras (data dir) @@ -155,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug } } -bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_dst_path) { +bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) { FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ); ERR_FAIL_COND_V(!f, false); @@ -164,7 +171,7 @@ bool GodotSharpExport::_add_assembly(const String &p_src_path, const String &p_d data.resize(f->get_len()); f->get_buffer(data.ptrw(), data.size()); - add_file(p_dst_path, data, false); + add_file(p_dst_path, data, p_remap); return true; } @@ -191,21 +198,21 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c if (has_extension) { path = search_dir.plus_file(ref_name); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name.get_basename(), path, &ref_assembly, true); if (ref_assembly != NULL) break; } } else { path = search_dir.plus_file(ref_name + ".dll"); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); if (ref_assembly != NULL) break; } path = search_dir.plus_file(ref_name + ".exe"); if (FileAccess::exists(path)) { - GDMono::get_singleton()->load_assembly_from(ref_name, search_dir, ref_aname, &ref_assembly, true); + GDMono::get_singleton()->load_assembly_from(ref_name, path, &ref_assembly, true); if (ref_assembly != NULL) break; } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index f007016578..10d4375567 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -41,7 +41,7 @@ class GodotSharpExport : public EditorExportPlugin { MonoAssemblyName *aname_prealloc; - bool _add_assembly(const String &p_src_path, const String &p_dst_path); + bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false); Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies); diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index 0ac59a1be8..d7bfa54aba 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -31,6 +31,8 @@ #include "mono_bottom_panel.h" #include "../csharp_script.h" +#include "../godotsharp_dirs.h" +#include "csharp_project.h" #include "godotsharp_editor.h" MonoBottomPanel *MonoBottomPanel::singleton = NULL; @@ -148,6 +150,10 @@ void MonoBottomPanel::_errors_toggled(bool p_pressed) { void MonoBottomPanel::_build_project_pressed() { + String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor"); + Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); + ERR_FAIL_COND(metadata_err != OK); + GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); MonoReloadNode::get_singleton()->restart_reload_timer(); diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/net_solution.cpp index 8bbd376c9a..a000debe52 100644 --- a/modules/mono/editor/net_solution.cpp +++ b/modules/mono/editor/net_solution.cpp @@ -52,7 +52,7 @@ #define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject" -#define SOLUTION_PLATFORMS_CONFIG "\t\%0|Any CPU = %0|Any CPU" +#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU" #define PROJECT_PLATFORMS_CONFIG \ "\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \ diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp new file mode 100644 index 0000000000..43de8df2b2 --- /dev/null +++ b/modules/mono/editor/script_class_parser.cpp @@ -0,0 +1,486 @@ +#include "script_class_parser.h" + +#include "core/map.h" +#include "core/os/os.h" + +#include "../utils/string_utils.h" + +ScriptClassParser::Token ScriptClassParser::get_token() { + + while (true) { + switch (code[idx]) { + case '\n': { + line++; + idx++; + break; + }; + case 0: { + return TK_EOF; + } break; + case '{': { + idx++; + return TK_CURLY_BRACKET_OPEN; + }; + case '}': { + idx++; + return TK_CURLY_BRACKET_CLOSE; + }; + case '[': { + idx++; + return TK_BRACKET_OPEN; + }; + case ']': { + idx++; + return TK_BRACKET_CLOSE; + }; + case '<': { + idx++; + return TK_OP_LESS; + }; + case '>': { + idx++; + return TK_OP_GREATER; + }; + case ':': { + idx++; + return TK_COLON; + }; + case ',': { + idx++; + return TK_COMMA; + }; + case '.': { + idx++; + return TK_PERIOD; + }; + case '#': { + //compiler directive + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + continue; + } break; + case '/': { + switch (code[idx + 1]) { + case '*': { // block comment + idx += 2; + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated comment"; + error = true; + return TK_ERROR; + } else if (code[idx] == '*' && code[idx + 1] == '/') { + idx += 2; + break; + } else if (code[idx] == '\n') { + line++; + } + + idx++; + } + + } break; + case '/': { // line comment skip + while (code[idx] != '\n' && code[idx] != 0) { + idx++; + } + + } break; + default: { + value = "/"; + idx++; + return TK_SYMBOL; + } + } + + continue; // a comment + } break; + case '\'': + case '"': { + bool verbatim = idx != 0 && code[idx - 1] == '@'; + + CharType begin_str = code[idx]; + idx++; + String tk_string = String(); + while (true) { + if (code[idx] == 0) { + error_str = "Unterminated String"; + error = true; + return TK_ERROR; + } else if (code[idx] == begin_str) { + if (verbatim && code[idx + 1] == '"') { // `""` is verbatim string's `\"` + idx += 2; // skip next `"` as well + continue; + } + + idx += 1; + break; + } else if (code[idx] == '\\' && !verbatim) { + //escaped characters... + idx++; + CharType next = code[idx]; + if (next == 0) { + error_str = "Unterminated String"; + error = true; + return TK_ERROR; + } + CharType res = 0; + + switch (next) { + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': + res = 13; + break; + case '\"': res = '\"'; break; + case '\\': + res = '\\'; + break; + default: { + res = next; + } break; + } + + tk_string += res; + + } else { + if (code[idx] == '\n') + line++; + tk_string += code[idx]; + } + idx++; + } + + value = tk_string; + + return TK_STRING; + } break; + default: { + if (code[idx] <= 32) { + idx++; + break; + } + + if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) { + value = String::chr(code[idx]); + idx++; + return TK_SYMBOL; + } + + if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { + //a number + const CharType *rptr; + double number = String::to_double(&code[idx], &rptr); + idx += (rptr - &code[idx]); + value = number; + return TK_NUMBER; + + } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) { + String id; + + id += code[idx]; + idx++; + + while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) { + id += code[idx]; + idx++; + } + + value = id; + return TK_IDENTIFIER; + } else if (code[idx] == '@' && code[idx + 1] == '"') { + // begin of verbatim string + idx++; + } else { + error_str = "Unexpected character."; + error = true; + return TK_ERROR; + } + } + } + } +} + +Error ScriptClassParser::_skip_type_parameters() { + + Token tk; + + while (true) { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + tk = get_token(); + + if (tk == TK_OP_LESS) { + Error err = _skip_type_parameters(); + if (err) + return err; + continue; + } else if (tk != TK_COMMA) { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + } else if (tk == TK_OP_LESS) { + error_str = "Expected identifier before `<`."; + error = true; + return ERR_PARSE_ERROR; + } else if (tk == TK_OP_GREATER) { + return OK; + } else { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + } +} + +Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { + + Token tk; + + while (true) { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + bool generic = false; + + String name = value; + + tk = get_token(); + + if (tk == TK_OP_LESS) { + generic = true; + Error err = _skip_type_parameters(); + if (err) + return err; + } else if (tk == TK_COMMA) { + Error err = _parse_class_base(r_base); + if (err) + return err; + } else if (tk != TK_CURLY_BRACKET_OPEN) { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + + r_base.push_back(!generic ? name : String()); // no generics, please + + return OK; + } else { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + } +} + +Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) { + + Token tk = get_token(); + + if (tk == TK_IDENTIFIER) { + r_name += String(value); + } else { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk == TK_PERIOD) { + r_name += "."; + return _parse_namespace_name(r_name, r_curly_stack); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + r_curly_stack++; + return OK; + } else { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } +} + +Error ScriptClassParser::parse(const String &p_code) { + + code = p_code; + idx = 0; + line = 0; + error_str = String(); + error = false; + value = Variant(); + classes.clear(); + + Token tk = get_token(); + + Map<int, NameDecl> name_stack; + int curly_stack = 0; + int type_curly_stack = 0; + + while (!error && tk != TK_EOF) { + if (tk == TK_IDENTIFIER && String(value) == "class") { + tk = get_token(); + + if (tk == TK_IDENTIFIER) { + String name = value; + int at_level = type_curly_stack; + + ClassDecl class_decl; + + for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) { + const NameDecl &name_decl = E->value(); + + if (name_decl.type == NameDecl::NAMESPACE_DECL) { + if (E != name_stack.front()) + class_decl.namespace_ += "."; + class_decl.namespace_ += name_decl.name; + } else { + class_decl.name += name_decl.name + "."; + } + } + + class_decl.name += name; + class_decl.nested = type_curly_stack > 0; + + bool generic = false; + + while (true) { + tk = get_token(); + + if (tk == TK_COLON) { + Error err = _parse_class_base(class_decl.base); + if (err) + return err; + + curly_stack++; + type_curly_stack++; + + break; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_OP_LESS && !generic) { + generic = true; + + Error err = _skip_type_parameters(); + if (err) + return err; + } else { + error_str = "Unexpected token: " + itos(tk); + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::CLASS_DECL; + name_stack[at_level] = name_decl; + + if (!generic) { // no generics, thanks + classes.push_back(class_decl); + } else if (OS::get_singleton()->is_stdout_verbose()) { + String full_name = class_decl.namespace_; + if (full_name.length()) + full_name += "."; + full_name += class_decl.name; + OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8()); + } + } + } else if (tk == TK_IDENTIFIER && String(value) == "struct") { + String name; + int at_level = type_curly_stack; + while (true) { + tk = get_token(); + if (tk == TK_IDENTIFIER && name.empty()) { + name = String(value); + } else if (tk == TK_CURLY_BRACKET_OPEN) { + if (name.empty()) { + error_str = "Expected identifier after keyword `struct`. Found `{`."; + error = true; + return ERR_PARSE_ERROR; + } + + curly_stack++; + type_curly_stack++; + break; + } else if (tk == TK_EOF) { + error_str = "Expected `{` after struct decl. Found `EOF`."; + error = true; + return ERR_PARSE_ERROR; + } + } + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::STRUCT_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_IDENTIFIER && String(value) == "namespace") { + if (type_curly_stack > 0) { + error_str = "Found namespace nested inside type."; + error = true; + return ERR_PARSE_ERROR; + } + + String name; + int at_level = curly_stack; + + Error err = _parse_namespace_name(name, curly_stack); + if (err) + return err; + + NameDecl name_decl; + name_decl.name = name; + name_decl.type = NameDecl::NAMESPACE_DECL; + name_stack[at_level] = name_decl; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + curly_stack++; + } else if (tk == TK_CURLY_BRACKET_CLOSE) { + curly_stack--; + if (name_stack.has(curly_stack)) { + if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL) + type_curly_stack--; + name_stack.erase(curly_stack); + } + } + + tk = get_token(); + } + + if (!error && tk == TK_EOF && curly_stack > 0) { + error_str = "Reached EOF with missing close curly brackets."; + error = true; + } + + if (error) + return ERR_PARSE_ERROR; + + return OK; +} + +Error ScriptClassParser::parse_file(const String &p_filepath) { + + String source; + + Error ferr = read_all_file_utf8(p_filepath, source); + if (ferr != OK) { + if (ferr == ERR_INVALID_DATA) { + ERR_EXPLAIN("File '" + p_filepath + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + } + ERR_FAIL_V(ferr); + } + + return parse(source); +} + +String ScriptClassParser::get_error() { + return error_str; +} + +Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() { + return classes; +} diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h new file mode 100644 index 0000000000..11cf1853e2 --- /dev/null +++ b/modules/mono/editor/script_class_parser.h @@ -0,0 +1,74 @@ +#ifndef SCRIPT_CLASS_PARSER_H +#define SCRIPT_CLASS_PARSER_H + +#include "core/ustring.h" +#include "core/variant.h" +#include "core/vector.h" + +class ScriptClassParser { + +public: + struct NameDecl { + enum Type { + NAMESPACE_DECL, + CLASS_DECL, + STRUCT_DECL + }; + + String name; + Type type; + }; + + struct ClassDecl { + String name; + String namespace_; + Vector<String> base; + bool nested; + bool has_script_attr; + }; + +private: + String code; + int idx; + int line; + String error_str; + bool error; + Variant value; + + Vector<ClassDecl> classes; + + enum Token { + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_PERIOD, + TK_COLON, + TK_COMMA, + TK_SYMBOL, + TK_IDENTIFIER, + TK_STRING, + TK_NUMBER, + TK_OP_LESS, + TK_OP_GREATER, + TK_EOF, + TK_ERROR + }; + + Token get_token(); + + Error _skip_type_parameters(); + + Error _parse_class_base(Vector<String> &r_base); + Error _parse_namespace_name(String &r_name, int &r_curly_stack); + +public: + Error parse(const String &p_code); + Error parse_file(const String &p_filepath); + + String get_error(); + + Vector<ClassDecl> get_classes(); +}; + +#endif // SCRIPT_CLASS_PARSER_H diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 4d88cca6f1..c3feafee28 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -377,34 +377,24 @@ GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { - return load_assembly_from(p_name, String(), r_assembly, p_refonly); -} - -bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { - - return load_assembly_from(p_name, String(), p_aname, r_assembly, p_refonly); -} - -bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly) { - CRASH_COND(!r_assembly); MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8()); - bool result = load_assembly_from(p_name, p_basedir, aname, r_assembly, p_refonly); + bool result = load_assembly(p_name, aname, r_assembly, p_refonly); mono_assembly_name_free(aname); mono_free(aname); return result; } -bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { +bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) { CRASH_COND(!r_assembly); print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); MonoImageOpenStatus status = MONO_IMAGE_OK; - MonoAssembly *assembly = mono_assembly_load_full(p_aname, p_basedir.length() ? p_basedir.utf8().get_data() : NULL, &status, p_refonly); + MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly); if (!assembly) return false; @@ -425,6 +415,32 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_basedir, M return true; } +bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) { + + CRASH_COND(!r_assembly); + + print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); + + GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly); + + if (!assembly) + return false; + +#ifdef DEBUG_ENABLED + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); + + ERR_FAIL_COND_V(stored_assembly == NULL, false); + ERR_FAIL_COND_V(*stored_assembly != assembly, false); +#endif + + *r_assembly = assembly; + + print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); + + return true; +} + APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) { APIAssembly::Version api_assembly_version; @@ -480,7 +496,14 @@ bool GDMono::_load_core_api_assembly() { } #endif - bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(API_ASSEMBLY_NAME, + assembly_path, + &core_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -510,7 +533,14 @@ bool GDMono::_load_editor_api_assembly() { return false; } - bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); + String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + + if (!FileAccess::exists(assembly_path)) + return false; + + bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME, + assembly_path, + &editor_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -551,6 +581,8 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); + + CSharpLanguage::get_singleton()->project_assembly_loaded(); } else { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load project assembly"); diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 745dcc34eb..fd9551b6de 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -199,8 +199,7 @@ public: bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false); bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly_from(const String &p_name, const String &p_basedir, GDMonoAssembly **r_assembly, bool p_refonly = false); - bool load_assembly_from(const String &p_name, const String &p_basedir, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false); + bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false); Error finalize_and_unload_domain(MonoDomain *p_domain); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 1067c11e0e..72b0204672 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -457,6 +457,20 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) return match; } +GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) { + + GDMonoAssembly **loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); + if (loaded_asm) + return *loaded_asm; +#ifdef DEBUG_ENABLED + CRASH_COND(!FileAccess::exists(p_path)); +#endif + no_search = true; + GDMonoAssembly *res = _load_assembly_from(p_name, p_path, p_refonly); + no_search = false; + return res; +} + GDMonoAssembly::GDMonoAssembly(const String &p_name, const String &p_path) { loaded = false; diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 4c9b1cb10d..2af40ec901 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -128,6 +128,8 @@ public: static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String()); + static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); + GDMonoAssembly(const String &p_name, const String &p_path = String()); ~GDMonoAssembly(); }; diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index b4c78df538..fa1fbebb16 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -141,7 +141,7 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { if (exc) { GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(); + ERR_FAIL(); } } } diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index 8691932f9a..6900866725 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -30,6 +30,8 @@ #include "string_utils.h" +#include "core/os/file_access.h" + namespace { int sfind(const String &p_text, int p_from) { @@ -128,6 +130,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const return new_string; } +#ifdef TOOLS_ENABLED bool is_csharp_keyword(const String &p_name) { // Reserved keywords @@ -156,3 +159,28 @@ bool is_csharp_keyword(const String &p_name) { String escape_csharp_keyword(const String &p_name) { return is_csharp_keyword(p_name) ? "@" + p_name : p_name; } +#endif + +Error read_all_file_utf8(const String &p_path, String &r_content) { + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + ERR_FAIL_COND_V(err != OK, err); + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String source; + if (source.parse_utf8((const char *)w.ptr())) { + ERR_FAIL_V(ERR_INVALID_DATA); + } + + r_content = source; + return OK; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index f2df2340ae..ee803bd720 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -42,4 +42,6 @@ bool is_csharp_keyword(const String &p_name); String escape_csharp_keyword(const String &p_name); #endif +Error read_all_file_utf8(const String &p_path, String &r_content); + #endif // STRING_FORMAT_H diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index d88abb3180..ba54160a90 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -17,6 +17,9 @@ <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap"> If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map. </member> + <member name="height" type="int" setter="set_height" getter="get_height"> + Height of the generated texture. + </member> <member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise"> The [OpenSimplexNoise] instance used to generate the noise. </member> @@ -26,9 +29,6 @@ <member name="width" type="int" setter="set_width" getter="get_width"> Width of the generated texture. </member> - <member name="height" type="int" setter="set_height" getter="get_height"> - Height of the generated texture. - </member> </members> <constants> </constants> diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index 98351fbaee..d121a82d9e 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -103,24 +103,25 @@ void NavigationMeshEditor::_bind_methods() { NavigationMeshEditor::NavigationMeshEditor() { bake_hbox = memnew(HBoxContainer); + button_bake = memnew(ToolButton); - button_bake->set_text(TTR("Bake!")); + bake_hbox->add_child(button_bake); button_bake->set_toggle_mode(true); - button_reset = memnew(Button); - button_bake->set_tooltip(TTR("Bake the navigation mesh.") + "\n"); + button_bake->set_text(TTR("Bake NavMesh")); + button_bake->connect("pressed", this, "_bake_pressed"); - bake_info = memnew(Label); - bake_hbox->add_child(button_bake); + button_reset = memnew(ToolButton); bake_hbox->add_child(button_reset); + // No button text, we only use a revert icon which is set when entering the tree. + button_reset->set_tooltip(TTR("Clear the navigation mesh.")); + button_reset->connect("pressed", this, "_clear_pressed"); + + bake_info = memnew(Label); bake_hbox->add_child(bake_info); err_dialog = memnew(AcceptDialog); add_child(err_dialog); node = NULL; - - button_bake->connect("pressed", this, "_bake_pressed"); - button_reset->connect("pressed", this, "_clear_pressed"); - button_reset->set_tooltip(TTR("Clear the navigation mesh.")); } NavigationMeshEditor::~NavigationMeshEditor() { diff --git a/modules/recast/navigation_mesh_editor_plugin.h b/modules/recast/navigation_mesh_editor_plugin.h index 4f3e222002..914d9ab8b9 100644 --- a/modules/recast/navigation_mesh_editor_plugin.h +++ b/modules/recast/navigation_mesh_editor_plugin.h @@ -43,8 +43,8 @@ class NavigationMeshEditor : public Control { AcceptDialog *err_dialog; HBoxContainer *bake_hbox; - Button *button_bake; - Button *button_reset; + ToolButton *button_bake; + ToolButton *button_reset; Label *bake_info; NavigationMeshInstance *node; diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 52a30baaec..186e9e63b1 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2699,11 +2699,11 @@ VisualScriptLanguage::VisualScriptLanguage() { _debug_parse_err_file = ""; _debug_call_stack_pos = 0; int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024); + ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024 + if (ScriptDebugger::get_singleton()) { //debugging enabled! _debug_max_call_stack = dmcs; - if (_debug_max_call_stack < 1024) - _debug_max_call_stack = 1024; _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1); } else { diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 30bc413459..8e050c1d27 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -586,7 +586,7 @@ Error OS_Android::shell_open(String p_uri) { String OS_Android::get_resource_dir() const { - return "/"; //android has it's own filesystem for resources inside the APK + return "/"; //android has its own filesystem for resources inside the APK } String OS_Android::get_locale() const { diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index fd7cd69c37..2d70b00dda 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -92,9 +92,9 @@ Error ContextGL_Win::initialize() { PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, (BYTE)PFD_TYPE_RGBA, - OS::get_singleton()->is_layered_allowed() ? (BYTE)32 : (BYTE)24, + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24), (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored - OS::get_singleton()->is_layered_allowed() ? (BYTE)8 : (BYTE)0, // Alpha Buffer + (BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer (BYTE)0, // Shift Bit Ignored (BYTE)0, // No Accumulation Buffer (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored diff --git a/platform/windows/joypad.cpp b/platform/windows/joypad.cpp index 99ac0214e0..7201714fb8 100644 --- a/platform/windows/joypad.cpp +++ b/platform/windows/joypad.cpp @@ -172,7 +172,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { joy->di_joy->SetDataFormat(&c_dfDIJoystick2); joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND); - joy->di_joy->EnumObjects(objectsCallback, this, NULL); + joy->di_joy->EnumObjects(objectsCallback, this, 0); joy->joy_axis.sort(); joy->guid = instance->guidInstance; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ac130ac4f2..3bbffd8fb7 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -763,7 +763,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RECT r; GetWindowRect(hWnd, &r); - dib_size = Size2(r.right - r.left, r.bottom - r.top); + dib_size = Size2i(r.right - r.left, r.bottom - r.top); BITMAPINFO bmi; ZeroMemory(&bmi, sizeof(BITMAPINFO)); @@ -773,7 +773,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4; + bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4; hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0); SelectObject(hDC_dib, hBitmap); @@ -1050,7 +1050,6 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau UINT x = 0, y = 0; HRESULT hr = E_FAIL; - bool bSet = false; if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) { hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y); if (SUCCEEDED(hr) && (x > 0) && (y > 0)) { @@ -1229,7 +1228,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int printf("Error setting WNDPROC: %li\n", le); }; - LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC); + GetWindowLongPtr(hWnd, GWLP_WNDPROC); RECT rect; if (!GetClientRect(hWnd, &rect)) { @@ -1728,14 +1727,18 @@ void OS_Windows::set_window_position(const Point2 &p_position) { Size2 OS_Windows::get_window_size() const { RECT r; - GetClientRect(hWnd, &r); - return Vector2(r.right - r.left, r.bottom - r.top); + if (GetClientRect(hWnd, &r)) { // Only area inside of window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); } Size2 OS_Windows::get_real_window_size() const { RECT r; - GetWindowRect(hWnd, &r); - return Vector2(r.right - r.left, r.bottom - r.top); + if (GetWindowRect(hWnd, &r)) { // Includes area of the window border + return Size2(r.right - r.left, r.bottom - r.top); + } + return Size2(); } void OS_Windows::set_window_size(const Size2 p_size) { @@ -2274,7 +2277,6 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap ERR_FAIL_COND(!image.is_valid()); UINT image_size = texture_size.width * texture_size.height; - UINT size = sizeof(UINT) * image_size; // Create the BITMAP with alpha channel COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); @@ -2746,11 +2748,6 @@ void OS_Windows::run() { main_loop->init(); - uint64_t last_ticks = get_ticks_usec(); - - int frames = 0; - uint64_t frame = 0; - while (!force_quit) { process_events(); // get rid of pending events diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 7c4c8f0eff..88c2c8aec6 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2095,7 +2095,7 @@ void OS_X11::process_xevents() { last_timestamp = event.xkey.time; // key event is a little complex, so - // it will be handled in it's own function. + // it will be handled in its own function. handle_key_event((XKeyEvent *)&event); } break; case SelectionRequest: { diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 4bff26a200..2dc500f7ab 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -38,14 +38,14 @@ void ARVRCamera::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - // need to find our ARVROrigin parent and let it know we're it's camera! + // need to find our ARVROrigin parent and let it know we're its camera! ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent()); if (origin != NULL) { origin->set_tracked_camera(this); } }; break; case NOTIFICATION_EXIT_TREE: { - // need to find our ARVROrigin parent and let it know we're no longer it's camera! + // need to find our ARVROrigin parent and let it know we're no longer its camera! ARVROrigin *origin = Object::cast_to<ARVROrigin>(get_parent()); if (origin != NULL) { origin->clear_tracked_camera_if(this); diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 67fb658562..d6690676cc 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -62,7 +62,7 @@ public: }; /* - ARVRController is a helper node that automatically updates it's position based on tracker data. + ARVRController is a helper node that automatically updates its position based on tracker data. It must be a child node of our ARVROrigin node */ @@ -102,7 +102,7 @@ public: }; /* - ARVRAnchor is a helper node that automatically updates it's position based on anchor data, it represents a real world location. + ARVRAnchor is a helper node that automatically updates its position based on anchor data, it represents a real world location. It must be a child node of our ARVROrigin node */ diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index 541f6047d9..0eccbbc8f9 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -261,7 +261,7 @@ static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector3 *p_normal, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) { if (p_level == cell_subdiv - 1) { - //plot the face by guessing it's albedo and emission value + //plot the face by guessing its albedo and emission value //find best axis to map to, for scanning values int closest_axis = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 0d5fbee9ee..494995fb85 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1524,6 +1524,7 @@ void ItemList::_bind_methods() { ADD_SIGNAL(MethodInfo("nothing_selected")); GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers } ItemList::ItemList() { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index f8a5c4cea3..bb56eea98e 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -6232,6 +6232,7 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_MAX); GLOBAL_DEF("gui/timers/text_edit_idle_detect_sec", 3); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/text_edit_idle_detect_sec", PropertyInfo(Variant::REAL, "gui/timers/text_edit_idle_detect_sec", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater")); // No negative numbers } TextEdit::TextEdit() { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index fdbe3b57f0..f7dec77ce4 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1944,6 +1944,7 @@ SceneTree::SceneTree() { debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4)); collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); + ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative tree_version = 1; physics_process_time = 1; @@ -1977,7 +1978,9 @@ SceneTree::SceneTree() { current_scene = NULL; int ref_atlas_size = GLOBAL_DEF("rendering/quality/reflections/atlas_size", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_size", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_size", PROPERTY_HINT_RANGE, "0,8192,or_greater")); //next_power_of_2 will return a 0 as min value int ref_atlas_subdiv = GLOBAL_DEF("rendering/quality/reflections/atlas_subdiv", 8); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/reflections/atlas_subdiv", PropertyInfo(Variant::INT, "rendering/quality/reflections/atlas_subdiv", PROPERTY_HINT_RANGE, "0,32,or_greater")); //next_power_of_2 will return a 0 as min value int msaa_mode = GLOBAL_DEF("rendering/quality/filters/msaa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/filters/msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x")); root->set_msaa(Viewport::MSAA(msaa_mode)); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index bb379ff4af..6bf1d12086 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -629,10 +629,8 @@ Rect2 Viewport::get_visible_rect() const { Rect2 r; if (size == Size2()) { - - r = Rect2(Point2(), Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height)); + r = Rect2(Point2(), OS::get_singleton()->get_window_size()); } else { - r = Rect2(Point2(), size); } @@ -2945,6 +2943,7 @@ Viewport::Viewport() { //gui.tooltip_timer->force_parent_owned(); gui.tooltip_delay = GLOBAL_DEF("gui/timers/tooltip_delay_sec", 0.7); + ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::REAL, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers gui.tooltip = NULL; gui.tooltip_label = NULL; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 086fb83af9..42ce9d10cd 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -264,7 +264,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { } if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { - //if node was not part of instance, must set it's name, parenthood and ownership + //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { parent->_add_child_nocheck(node, snames[n.name]); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 37454d9253..b41fcac8fa 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -934,6 +934,7 @@ void AudioServer::init() { channel_disable_threshold_db = GLOBAL_DEF_RST("audio/channel_disable_threshold_db", -60.0); channel_disable_frames = float(GLOBAL_DEF_RST("audio/channel_disable_time", 2.0)) * get_mix_rate(); + ProjectSettings::get_singleton()->set_custom_property_info("audio/channel_disable_time", PropertyInfo(Variant::REAL, "audio/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); buffer_size = 1024; //hardcoded for now init_channels_and_buffers(); diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp index 731749b8ce..feb96d8054 100644 --- a/servers/physics/space_sw.cpp +++ b/servers/physics/space_sw.cpp @@ -1183,6 +1183,7 @@ SpaceSW::SpaceSW() { body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI)); body_time_to_sleep = GLOBAL_DEF("physics/3d/time_before_sleep", 0.5); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); body_angular_velocity_damp_ratio = 10; broadphase = BroadPhaseSW::create_func(); diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp index 950f0f9d24..95195c8fff 100644 --- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp +++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp @@ -635,11 +635,15 @@ BroadPhase2DSW *BroadPhase2DHashGrid::_create() { BroadPhase2DHashGrid::BroadPhase2DHashGrid() { hash_table_size = GLOBAL_DEF("physics/2d/bp_hash_table_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/bp_hash_table_size", PropertyInfo(Variant::INT, "physics/2d/bp_hash_table_size", PROPERTY_HINT_RANGE, "0,8192,1,or_greater")); hash_table_size = Math::larger_prime(hash_table_size); hash_table = memnew_arr(PosBin *, hash_table_size); cell_size = GLOBAL_DEF("physics/2d/cell_size", 128); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/cell_size", PropertyInfo(Variant::INT, "physics/2d/cell_size", PROPERTY_HINT_RANGE, "0,512,1,or_greater")); + large_object_min_surface = GLOBAL_DEF("physics/2d/large_object_surface_threshold_in_cells", 512); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/large_object_surface_threshold_in_cells", PropertyInfo(Variant::INT, "physics/2d/large_object_surface_threshold_in_cells", PROPERTY_HINT_RANGE, "0,1024,1,or_greater")); for (uint32_t i = 0; i < hash_table_size; i++) hash_table[i] = NULL; diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index f36328c985..adcc99aae8 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -1233,6 +1233,7 @@ Space2DSW::Space2DSW() { body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", (8.0 / 180.0 * Math_PI)); body_time_to_sleep = GLOBAL_DEF("physics/2d/time_before_sleep", 0.5); + ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/time_before_sleep", PropertyInfo(Variant::REAL, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); broadphase = BroadPhase2DSW::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 9d684d33d6..164be132b8 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2370,6 +2370,7 @@ VisualServer::VisualServer() { GLOBAL_DEF("rendering/quality/directional_shadow/size", 4096); GLOBAL_DEF("rendering/quality/directional_shadow/size.mobile", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/quality/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); GLOBAL_DEF("rendering/quality/shadow_atlas/size", 4096); GLOBAL_DEF("rendering/quality/shadow_atlas/size.mobile", 2048); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/shadow_atlas/size", PropertyInfo(Variant::INT, "rendering/quality/shadow_atlas/size", PROPERTY_HINT_RANGE, "256,16384")); |