summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct16
-rw-r--r--core/math/vector4.cpp94
-rw-r--r--core/math/vector4.h25
-rw-r--r--core/variant/variant_call.cpp6
-rw-r--r--doc/classes/Vector4.xml45
-rw-r--r--editor/editor_properties.cpp42
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/project_manager.cpp253
-rw-r--r--editor/project_manager.h2
-rw-r--r--methods.py46
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/msdfgen/config.py3
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.cpp112
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.h18
-rw-r--r--modules/text_server_adv/SCsub4
-rw-r--r--modules/text_server_fb/SCsub4
-rw-r--r--modules/theora/config.py3
-rw-r--r--modules/vorbis/config.py3
-rw-r--r--modules/websocket/emws_server.cpp92
-rw-r--r--modules/websocket/emws_server.h64
-rw-r--r--modules/websocket/register_types.cpp5
-rw-r--r--tests/core/math/test_vector4.h312
-rw-r--r--tests/core/math/test_vector4i.h148
-rw-r--r--tests/test_main.cpp2
24 files changed, 834 insertions, 468 deletions
diff --git a/SConstruct b/SConstruct
index 17184c2e36..d0d03bd8dc 100644
--- a/SConstruct
+++ b/SConstruct
@@ -138,6 +138,7 @@ env_base.__class__.CommandNoCache = methods.CommandNoCache
env_base.__class__.Run = methods.Run
env_base.__class__.disable_warnings = methods.disable_warnings
env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
+env_base.__class__.module_add_dependencies = methods.module_add_dependencies
env_base.__class__.module_check_dependencies = methods.module_check_dependencies
env_base["x86_libtheora_opt_gcc"] = False
@@ -705,6 +706,7 @@ if selected_platform in platform_list:
sys.modules.pop("detect")
modules_enabled = OrderedDict()
+ env.module_dependencies = {}
env.module_icons_paths = []
env.doc_class_path = {}
@@ -716,6 +718,10 @@ if selected_platform in platform_list:
import config
if config.can_build(env, selected_platform):
+ # Disable it if a required dependency is missing.
+ if not env.module_check_dependencies(name):
+ continue
+
config.configure(env)
# Get doc classes paths (if present)
try:
@@ -738,6 +744,7 @@ if selected_platform in platform_list:
sys.modules.pop("config")
env.module_list = modules_enabled
+ methods.sort_module_list(env)
methods.update_version(env.module_version_string)
@@ -800,15 +807,6 @@ if selected_platform in platform_list:
if env["minizip"]:
env.Append(CPPDEFINES=["MINIZIP_ENABLED"])
- editor_module_list = []
- if env["tools"] and not env.module_check_dependencies("tools", editor_module_list):
- print(
- "Build option 'module_"
- + x
- + "_enabled=no' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template)."
- )
- Exit(255)
-
if not env["verbose"]:
methods.no_verbose(sys, env)
diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp
index cc0d0dcf72..4697c311b4 100644
--- a/core/math/vector4.cpp
+++ b/core/math/vector4.cpp
@@ -33,6 +33,40 @@
#include "core/math/basis.h"
#include "core/string/print_string.h"
+void Vector4::set_axis(const int p_axis, const real_t p_value) {
+ ERR_FAIL_INDEX(p_axis, 4);
+ components[p_axis] = p_value;
+}
+
+real_t Vector4::get_axis(const int p_axis) const {
+ ERR_FAIL_INDEX_V(p_axis, 4, 0);
+ return operator[](p_axis);
+}
+
+Vector4::Axis Vector4::min_axis_index() const {
+ uint32_t min_index = 0;
+ real_t min_value = x;
+ for (uint32_t i = 1; i < 4; i++) {
+ if (operator[](i) <= min_value) {
+ min_index = i;
+ min_value = operator[](i);
+ }
+ }
+ return Vector4::Axis(min_index);
+}
+
+Vector4::Axis Vector4::max_axis_index() const {
+ uint32_t max_index = 0;
+ real_t max_value = x;
+ for (uint32_t i = 1; i < 4; i++) {
+ if (operator[](i) > max_value) {
+ max_index = i;
+ max_value = operator[](i);
+ }
+ }
+ return Vector4::Axis(max_index);
+}
+
bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
}
@@ -53,6 +87,16 @@ bool Vector4::is_normalized() const {
return Math::is_equal_approx(length_squared(), 1, (real_t)UNIT_EPSILON); // Use less epsilon.
}
+real_t Vector4::distance_to(const Vector4 &p_to) const {
+ return (p_to - *this).length();
+}
+
+Vector4 Vector4::direction_to(const Vector4 &p_to) const {
+ Vector4 ret(p_to.x - x, p_to.y - y, p_to.z - z, p_to.w - w);
+ ret.normalize();
+ return ret;
+}
+
Vector4 Vector4::abs() const {
return Vector4(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w));
}
@@ -81,32 +125,38 @@ Vector4 Vector4::lerp(const Vector4 &p_to, const real_t p_weight) const {
w + (p_weight * (p_to.w - w)));
}
-Vector4 Vector4::inverse() const {
- return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w);
+Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const {
+ Vector4 res = *this;
+ res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
+ res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
+ res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
+ res.w = Math::cubic_interpolate(res.w, p_b.w, p_pre_a.w, p_post_b.w, p_weight);
+ return res;
}
-Vector4::Axis Vector4::min_axis_index() const {
- uint32_t min_index = 0;
- real_t min_value = x;
- for (uint32_t i = 1; i < 4; i++) {
- if (operator[](i) <= min_value) {
- min_index = i;
- min_value = operator[](i);
- }
- }
- return Vector4::Axis(min_index);
+Vector4 Vector4::posmod(const real_t p_mod) const {
+ return Vector4(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod), Math::fposmod(w, p_mod));
}
-Vector4::Axis Vector4::max_axis_index() const {
- uint32_t max_index = 0;
- real_t max_value = x;
- for (uint32_t i = 1; i < 4; i++) {
- if (operator[](i) > max_value) {
- max_index = i;
- max_value = operator[](i);
- }
- }
- return Vector4::Axis(max_index);
+Vector4 Vector4::posmodv(const Vector4 &p_modv) const {
+ return Vector4(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z), Math::fposmod(w, p_modv.w));
+}
+
+void Vector4::snap(const Vector4 &p_step) {
+ x = Math::snapped(x, p_step.x);
+ y = Math::snapped(y, p_step.y);
+ z = Math::snapped(z, p_step.z);
+ w = Math::snapped(w, p_step.w);
+}
+
+Vector4 Vector4::snapped(const Vector4 &p_step) const {
+ Vector4 v = *this;
+ v.snap(p_step);
+ return v;
+}
+
+Vector4 Vector4::inverse() const {
+ return Vector4(1.0f / x, 1.0f / y, 1.0f / z, 1.0f / w);
}
Vector4 Vector4::clamp(const Vector4 &p_min, const Vector4 &p_max) const {
diff --git a/core/math/vector4.h b/core/math/vector4.h
index 37ddb509d6..373a6a1218 100644
--- a/core/math/vector4.h
+++ b/core/math/vector4.h
@@ -62,6 +62,15 @@ struct _NO_DISCARD_ Vector4 {
DEV_ASSERT((unsigned int)p_axis < 4);
return components[p_axis];
}
+
+ _FORCE_INLINE_ void set_all(const real_t p_value);
+
+ void set_axis(const int p_axis, const real_t p_value);
+ real_t get_axis(const int p_axis) const;
+
+ Vector4::Axis min_axis_index() const;
+ Vector4::Axis max_axis_index() const;
+
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
real_t length() const;
@@ -69,15 +78,21 @@ struct _NO_DISCARD_ Vector4 {
Vector4 normalized() const;
bool is_normalized() const;
+ real_t distance_to(const Vector4 &p_to) const;
+ Vector4 direction_to(const Vector4 &p_to) const;
+
Vector4 abs() const;
Vector4 sign() const;
Vector4 floor() const;
Vector4 ceil() const;
Vector4 round() const;
Vector4 lerp(const Vector4 &p_to, const real_t p_weight) const;
+ Vector4 cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const;
- Vector4::Axis min_axis_index() const;
- Vector4::Axis max_axis_index() const;
+ Vector4 posmod(const real_t p_mod) const;
+ Vector4 posmodv(const Vector4 &p_modv) const;
+ void snap(const Vector4 &p_step);
+ Vector4 snapped(const Vector4 &p_step) const;
Vector4 clamp(const Vector4 &p_min, const Vector4 &p_max) const;
Vector4 inverse() const;
@@ -130,6 +145,10 @@ struct _NO_DISCARD_ Vector4 {
}
};
+void Vector4::set_all(const real_t p_value) {
+ x = y = z = p_value;
+}
+
real_t Vector4::dot(const Vector4 &p_vec4) const {
return x * p_vec4.x + y * p_vec4.y + z * p_vec4.z + w * p_vec4.w;
}
@@ -193,7 +212,7 @@ Vector4 Vector4::operator/(const Vector4 &p_vec4) const {
}
Vector4 Vector4::operator-() const {
- return Vector4(x, y, z, w);
+ return Vector4(-x, -y, -z, -w);
}
Vector4 Vector4::operator*(const real_t &s) const {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index d1f1b83457..5c298f9b3b 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1737,9 +1737,15 @@ static void _register_variant_builtin_methods() {
bind_method(Vector4, ceil, sarray(), varray());
bind_method(Vector4, round, sarray(), varray());
bind_method(Vector4, lerp, sarray("to", "weight"), varray());
+ bind_method(Vector4, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
+ bind_method(Vector4, posmod, sarray("mod"), varray());
+ bind_method(Vector4, posmodv, sarray("modv"), varray());
+ bind_method(Vector4, snapped, sarray("step"), varray());
bind_method(Vector4, clamp, sarray("min", "max"), varray());
bind_method(Vector4, normalized, sarray(), varray());
bind_method(Vector4, is_normalized, sarray(), varray());
+ bind_method(Vector4, direction_to, sarray("to"), varray());
+ bind_method(Vector4, distance_to, sarray("to"), varray());
bind_method(Vector4, dot, sarray("with"), varray());
bind_method(Vector4, inverse, sarray(), varray());
bind_method(Vector4, is_equal_approx, sarray("with"), varray());
diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml
index da0df2672e..ee9d97019e 100644
--- a/doc/classes/Vector4.xml
+++ b/doc/classes/Vector4.xml
@@ -63,6 +63,30 @@
Returns a new vector with all components clamped between the components of [code]min[/code] and [code]max[/code], by running [method @GlobalScope.clamp] on each component.
</description>
</method>
+ <method name="cubic_interpolate" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="b" type="Vector4" />
+ <argument index="1" name="pre_a" type="Vector4" />
+ <argument index="2" name="post_b" type="Vector4" />
+ <argument index="3" name="weight" type="float" />
+ <description>
+ Performs a cubic interpolation between this vector and [code]b[/code] using [code]pre_a[/code] and [code]post_b[/code] as handles, and returns the result at position [code]weight[/code]. [code]weight[/code] is on the range of 0.0 to 1.0, representing the amount of interpolation.
+ </description>
+ </method>
+ <method name="direction_to" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="to" type="Vector4" />
+ <description>
+ Returns the normalized vector pointing from this vector to [code]to[/code]. This is equivalent to using [code](b - a).normalized()[/code].
+ </description>
+ </method>
+ <method name="distance_to" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="to" type="Vector4" />
+ <description>
+ Returns the distance between this vector and [code]to[/code].
+ </description>
+ </method>
<method name="dot" qualifiers="const">
<return type="float" />
<argument index="0" name="with" type="Vector4" />
@@ -133,6 +157,20 @@
Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
+ <method name="posmod" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="mod" type="float" />
+ <description>
+ Returns a vector composed of the [method @GlobalScope.fposmod] of this vector's components and [code]mod[/code].
+ </description>
+ </method>
+ <method name="posmodv" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="modv" type="Vector4" />
+ <description>
+ Returns a vector composed of the [method @GlobalScope.fposmod] of this vector's components and [code]modv[/code]'s components.
+ </description>
+ </method>
<method name="round" qualifiers="const">
<return type="Vector4" />
<description>
@@ -145,6 +183,13 @@
Returns a new vector with each component set to one or negative one, depending on the signs of the components, or zero if the component is zero, by calling [method @GlobalScope.sign] on each component.
</description>
</method>
+ <method name="snapped" qualifiers="const">
+ <return type="Vector4" />
+ <argument index="0" name="step" type="Vector4" />
+ <description>
+ Returns this vector with each component snapped to the nearest multiple of [code]step[/code]. This can also be used to round to an arbitrary number of decimals.
+ </description>
+ </method>
</methods>
<members>
<member name="w" type="float" setter="" getter="" default="0.0">
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index c713fe3df0..5e66d436fc 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -507,13 +507,55 @@ void EditorPropertyPath::_path_focus_exited() {
_path_selected(path->get_text());
}
+void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ const Dictionary drag_data = p_data;
+ if (!drag_data.has("type")) {
+ return;
+ }
+ if (String(drag_data["type"]) != "files") {
+ return;
+ }
+ const Vector<String> filesPaths = drag_data["files"];
+ if (filesPaths.size() == 0) {
+ return;
+ }
+
+ emit_changed(get_edited_property(), filesPaths[0]);
+ update_property();
+}
+
+bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+ const Dictionary drag_data = p_data;
+ if (!drag_data.has("type")) {
+ return false;
+ }
+ if (String(drag_data["type"]) != "files") {
+ return false;
+ }
+ const Vector<String> filesPaths = drag_data["files"];
+ if (filesPaths.size() == 0) {
+ return false;
+ }
+
+ for (const String &extension : extensions) {
+ if (filesPaths[0].ends_with(extension.substr(1, extension.size() - 1))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void EditorPropertyPath::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "position", "data", "from"), &EditorPropertyPath::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw", "position", "data", "from"), &EditorPropertyPath::_drop_data_fw);
}
EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
path = memnew(LineEdit);
+ path->set_drag_forwarding(this);
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
path_hb->add_child(path);
path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected));
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 6a1360d2ca..5c73a53184 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -142,6 +142,8 @@ class EditorPropertyPath : public EditorProperty {
void _path_selected(const String &p_path);
void _path_pressed();
void _path_focus_exited();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
protected:
virtual void _set_read_only(bool p_read_only) override;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 8395fa996a..21891e381b 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -42,6 +42,7 @@
#include "core/string/translation.h"
#include "core/version.h"
#include "editor/editor_file_dialog.h"
+#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "editor/editor_themes.h"
@@ -57,10 +58,6 @@
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
-static inline String get_project_key_from_path(const String &dir) {
- return dir.replace("/", "::");
-}
-
class ProjectDialog : public ConfirmationDialog {
GDCLASS(ProjectDialog, ConfirmationDialog);
@@ -600,9 +597,6 @@ private:
if (dir.ends_with("/")) {
dir = dir.substr(0, dir.length() - 1);
}
- String proj = get_project_key_from_path(dir);
- EditorSettings::get_singleton()->set("projects/" + proj, dir);
- EditorSettings::get_singleton()->save();
hide();
emit_signal(SNAME("project_created"), dir);
@@ -989,7 +983,6 @@ public:
// Can often be passed by copy
struct Item {
- String project_key;
String project_name;
String description;
String path;
@@ -1006,8 +999,7 @@ public:
Item() {}
- Item(const String &p_project,
- const String &p_name,
+ Item(const String &p_name,
const String &p_description,
const String &p_path,
const String &p_icon,
@@ -1018,7 +1010,6 @@ public:
bool p_grayed,
bool p_missing,
int p_version) {
- project_key = p_project;
project_name = p_name;
description = p_description;
path = p_path;
@@ -1034,7 +1025,7 @@ public:
}
_FORCE_INLINE_ bool operator==(const Item &l) const {
- return project_key == l.project_key;
+ return path == l.path;
}
};
@@ -1047,6 +1038,7 @@ public:
void _global_menu_open_project(const Variant &p_tag);
void update_dock_menu();
+ void migrate_config();
void load_projects();
void set_search_term(String p_search_term);
void set_order_option(int p_option);
@@ -1062,6 +1054,8 @@ public:
bool is_any_project_missing() const;
void erase_missing_projects();
int refresh_project(const String &dir_path);
+ void add_project(const String &dir_path, bool favorite);
+ void save_config();
private:
static void _bind_methods();
@@ -1082,12 +1076,15 @@ private:
String _search_term;
FilterOption _order_option;
- HashSet<String> _selected_project_keys;
+ HashSet<String> _selected_project_paths;
String _last_clicked; // Project key
VBoxContainer *_scroll_children;
int _icon_load_index;
Vector<Item> _projects;
+
+ ConfigFile _config;
+ String _config_path;
};
struct ProjectListComparator {
@@ -1103,7 +1100,7 @@ struct ProjectListComparator {
}
switch (order_option) {
case PATH:
- return a.project_key < b.project_key;
+ return a.path < b.path;
case EDIT_DATE:
return a.last_edited > b.last_edited;
default:
@@ -1120,6 +1117,7 @@ ProjectList::ProjectList() {
_icon_load_index = 0;
project_opening_initiated = false;
+ _config_path = EditorPaths::get_singleton()->get_data_dir().plus_file("projects.cfg");
}
ProjectList::~ProjectList() {
@@ -1171,9 +1169,8 @@ void ProjectList::load_project_icon(int p_index) {
}
// Load project data from p_property_key and return it in a ProjectList::Item. p_favorite is passed directly into the Item.
-ProjectList::Item ProjectList::load_project_data(const String &p_property_key, bool p_favorite) {
- String path = EditorSettings::get_singleton()->get(p_property_key);
- String conf = path.plus_file("project.godot");
+ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_favorite) {
+ String conf = p_path.plus_file("project.godot");
bool grayed = false;
bool missing = false;
@@ -1209,7 +1206,7 @@ ProjectList::Item ProjectList::load_project_data(const String &p_property_key, b
// when editing a project (but not when running it).
last_edited = FileAccess::get_modified_time(conf);
- String fscache = path.plus_file(".fscache");
+ String fscache = p_path.plus_file(".fscache");
if (FileAccess::exists(fscache)) {
uint64_t cache_modified = FileAccess::get_modified_time(fscache);
if (cache_modified > last_edited) {
@@ -1222,9 +1219,38 @@ ProjectList::Item ProjectList::load_project_data(const String &p_property_key, b
print_line("Project is missing: " + conf);
}
- const String project_key = p_property_key.get_slice("/", 1);
+ return Item(project_name, description, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+}
+
+void ProjectList::migrate_config() {
+ // Proposal #1637 moved the project list from editor settings to a separate config file
+ // If the new config file doesn't exist, populate it from EditorSettings
+ if (FileAccess::exists(_config_path)) {
+ return;
+ }
+ print_line("Migrating legacy project list");
+
+ List<PropertyInfo> properties;
+ EditorSettings::get_singleton()->get_property_list(&properties);
- return Item(project_key, project_name, description, path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
+ for (const PropertyInfo &E : properties) {
+ // This is actually something like "projects/C:::Documents::Godot::Projects::MyGame"
+ String property_key = E.name;
+ if (!property_key.begins_with("projects/")) {
+ continue;
+ }
+
+ String path = EditorSettings::get_singleton()->get(property_key);
+ String favoriteKey = "favorite_projects/" + property_key.get_slice("/", 1);
+ bool favorite = EditorSettings::get_singleton()->has_setting(favoriteKey);
+ add_project(path, favorite);
+ if (favorite) {
+ EditorSettings::get_singleton()->erase(favoriteKey);
+ }
+ EditorSettings::get_singleton()->erase(property_key);
+ }
+
+ save_config();
}
void ProjectList::load_projects() {
@@ -1239,37 +1265,15 @@ void ProjectList::load_projects() {
}
_projects.clear();
_last_clicked = "";
- _selected_project_keys.clear();
-
- // Load data
- // TODO Would be nice to change how projects and favourites are stored... it complicates things a bit.
- // Use a dictionary associating project path to metadata (like is_favorite).
-
- List<PropertyInfo> properties;
- EditorSettings::get_singleton()->get_property_list(&properties);
-
- HashSet<String> favorites;
- // Find favourites...
- for (const PropertyInfo &E : properties) {
- String property_key = E.name;
- if (property_key.begins_with("favorite_projects/")) {
- favorites.insert(property_key);
- }
- }
-
- for (const PropertyInfo &E : properties) {
- // This is actually something like "projects/C:::Documents::Godot::Projects::MyGame"
- String property_key = E.name;
- if (!property_key.begins_with("projects/")) {
- continue;
- }
-
- String project_key = property_key.get_slice("/", 1);
- bool favorite = favorites.has("favorite_projects/" + project_key);
+ _selected_project_paths.clear();
- Item item = load_project_data(property_key, favorite);
+ List<String> sections;
+ _config.load(_config_path);
+ _config.get_sections(&sections);
- _projects.push_back(item);
+ for (const String &path : sections) {
+ bool favorite = _config.get_value(path, "favorite", false);
+ _projects.push_back(load_project_data(path, favorite));
}
// Create controls
@@ -1496,19 +1500,19 @@ void ProjectList::sort_projects() {
const HashSet<String> &ProjectList::get_selected_project_keys() const {
// Faster if that's all you need
- return _selected_project_keys;
+ return _selected_project_paths;
}
Vector<ProjectList::Item> ProjectList::get_selected_projects() const {
Vector<Item> items;
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
return items;
}
- items.resize(_selected_project_keys.size());
+ items.resize(_selected_project_paths.size());
int j = 0;
for (int i = 0; i < _projects.size(); ++i) {
const Item &item = _projects[i];
- if (_selected_project_keys.has(item.project_key)) {
+ if (_selected_project_paths.has(item.path)) {
items.write[j++] = item;
}
}
@@ -1522,41 +1526,40 @@ void ProjectList::ensure_project_visible(int p_index) {
}
int ProjectList::get_single_selected_index() const {
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
// Default selection
return 0;
}
String key;
- if (_selected_project_keys.size() == 1) {
+ if (_selected_project_paths.size() == 1) {
// Only one selected
- key = *_selected_project_keys.begin();
+ key = *_selected_project_paths.begin();
} else {
// Multiple selected, consider the last clicked one as "main"
key = _last_clicked;
}
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == key) {
+ if (_projects[i].path == key) {
return i;
}
}
return 0;
}
-void ProjectList::remove_project(int p_index, bool p_update_settings) {
+void ProjectList::remove_project(int p_index, bool p_update_config) {
const Item item = _projects[p_index]; // Take a copy
- _selected_project_keys.erase(item.project_key);
+ _selected_project_paths.erase(item.path);
- if (_last_clicked == item.project_key) {
+ if (_last_clicked == item.path) {
_last_clicked = "";
}
memdelete(item.control);
_projects.remove_at(p_index);
- if (p_update_settings) {
- EditorSettings::get_singleton()->erase("projects/" + item.project_key);
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
+ if (p_update_config) {
+ _config.erase_section(item.path);
// Not actually saving the file, in case you are doing more changes to settings
}
@@ -1594,41 +1597,19 @@ void ProjectList::erase_missing_projects() {
}
print_line("Removed " + itos(deleted_count) + " projects from the list, remaining " + itos(remaining_count) + " projects");
-
- EditorSettings::get_singleton()->save();
+ save_config();
}
int ProjectList::refresh_project(const String &dir_path) {
- // Reads editor settings and reloads information about a specific project.
+ // Reloads information about a specific project.
// If it wasn't loaded and should be in the list, it is added (i.e new project).
// If it isn't in the list anymore, it is removed.
// If it is in the list but doesn't exist anymore, it is marked as missing.
- String project_key = get_project_key_from_path(dir_path);
-
- // Read project manager settings
- bool is_favourite = false;
- bool should_be_in_list = false;
- String property_key = "projects/" + project_key;
- {
- List<PropertyInfo> properties;
- EditorSettings::get_singleton()->get_property_list(&properties);
- String favorite_property_key = "favorite_projects/" + project_key;
-
- bool found = false;
- for (const PropertyInfo &E : properties) {
- String prop = E.name;
- if (!found && prop == property_key) {
- found = true;
- } else if (!is_favourite && prop == favorite_property_key) {
- is_favourite = true;
- }
- }
-
- should_be_in_list = found;
- }
+ bool should_be_in_list = _config.has_section(dir_path);
+ bool is_favourite = _config.get_value(dir_path, "favorite", false);
- bool was_selected = _selected_project_keys.has(project_key);
+ bool was_selected = _selected_project_paths.has(dir_path);
// Remove item in any case
for (int i = 0; i < _projects.size(); ++i) {
@@ -1643,7 +1624,7 @@ int ProjectList::refresh_project(const String &dir_path) {
if (should_be_in_list) {
// Recreate it with updated info
- Item item = load_project_data(property_key, is_favourite);
+ Item item = load_project_data(dir_path, is_favourite);
_projects.push_back(item);
create_project_item_control(_projects.size() - 1);
@@ -1651,7 +1632,7 @@ int ProjectList::refresh_project(const String &dir_path) {
sort_projects();
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == project_key) {
+ if (_projects[i].path == dir_path) {
if (was_selected) {
select_project(i);
ensure_project_visible(i);
@@ -1667,13 +1648,23 @@ int ProjectList::refresh_project(const String &dir_path) {
return index;
}
+void ProjectList::add_project(const String &dir_path, bool favorite) {
+ if (!_config.has_section(dir_path)) {
+ _config.set_value(dir_path, "favorite", favorite);
+ }
+}
+
+void ProjectList::save_config() {
+ _config.save(_config_path);
+}
+
int ProjectList::get_project_count() const {
return _projects.size();
}
void ProjectList::select_project(int p_index) {
Vector<Item> previous_selected_items = get_selected_projects();
- _selected_project_keys.clear();
+ _selected_project_paths.clear();
for (int i = 0; i < previous_selected_items.size(); ++i) {
previous_selected_items[i].control->update();
@@ -1695,7 +1686,7 @@ void ProjectList::select_first_visible_project() {
if (!found) {
// Deselect all projects if there are no visible projects in the list.
- _selected_project_keys.clear();
+ _selected_project_paths.clear();
}
}
@@ -1717,24 +1708,23 @@ void ProjectList::select_range(int p_begin, int p_end) {
void ProjectList::toggle_select(int p_index) {
Item &item = _projects.write[p_index];
- if (_selected_project_keys.has(item.project_key)) {
- _selected_project_keys.erase(item.project_key);
+ if (_selected_project_paths.has(item.path)) {
+ _selected_project_paths.erase(item.path);
} else {
- _selected_project_keys.insert(item.project_key);
+ _selected_project_paths.insert(item.path);
}
item.control->update();
}
void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
- if (_selected_project_keys.size() == 0) {
+ if (_selected_project_paths.size() == 0) {
return;
}
for (int i = 0; i < _projects.size(); ++i) {
Item &item = _projects.write[i];
- if (_selected_project_keys.has(item.project_key) && item.control->is_visible()) {
- EditorSettings::get_singleton()->erase("projects/" + item.project_key);
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
+ if (_selected_project_paths.has(item.path) && item.control->is_visible()) {
+ _config.erase_section(item.path);
if (p_delete_project_contents) {
OS::get_singleton()->move_to_trash(item.path);
@@ -1746,9 +1736,8 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
}
}
- EditorSettings::get_singleton()->save();
-
- _selected_project_keys.clear();
+ save_config();
+ _selected_project_paths.clear();
_last_clicked = "";
update_dock_menu();
@@ -1764,9 +1753,9 @@ void ProjectList::_panel_draw(Node *p_hb) {
hb->draw_line(Point2(0, hb->get_size().y + 1), Point2(hb->get_size().x, hb->get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
}
- String key = _projects[p_hb->get_index()].project_key;
+ String key = _projects[p_hb->get_index()].path;
- if (_selected_project_keys.has(key)) {
+ if (_selected_project_paths.has(key)) {
hb->draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), hb->get_size()));
}
}
@@ -1778,11 +1767,11 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
const Item &clicked_project = _projects[clicked_index];
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
- if (mb->is_shift_pressed() && _selected_project_keys.size() > 0 && !_last_clicked.is_empty() && clicked_project.project_key != _last_clicked) {
+ if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
int anchor_index = -1;
for (int i = 0; i < _projects.size(); ++i) {
const Item &p = _projects[i];
- if (p.project_key == _last_clicked) {
+ if (p.path == _last_clicked) {
anchor_index = p.control->get_index();
break;
}
@@ -1794,7 +1783,7 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
toggle_select(clicked_index);
} else {
- _last_clicked = clicked_project.project_key;
+ _last_clicked = clicked_project.path;
select_project(clicked_index);
}
@@ -1816,12 +1805,8 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
item.favorite = !item.favorite;
- if (item.favorite) {
- EditorSettings::get_singleton()->set("favorite_projects/" + item.project_key, item.path);
- } else {
- EditorSettings::get_singleton()->erase("favorite_projects/" + item.project_key);
- }
- EditorSettings::get_singleton()->save();
+ _config.set_value(item.path, "favorite", item.favorite);
+ save_config();
_projects.write[index] = item;
@@ -1831,7 +1816,7 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
if (item.favorite) {
for (int i = 0; i < _projects.size(); ++i) {
- if (_projects[i].project_key == item.project_key) {
+ if (_projects[i].path == item.path) {
ensure_project_visible(i);
break;
}
@@ -2089,6 +2074,8 @@ void ProjectManager::_on_projects_updated() {
}
void ProjectManager::_on_project_created(const String &dir) {
+ _project_list->add_project(dir, false);
+ _project_list->save_config();
search_box->clear();
int i = _project_list->refresh_project(dir);
_project_list->select_project(i);
@@ -2109,9 +2096,7 @@ void ProjectManager::_open_selected_projects() {
const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
- for (const String &E : selected_list) {
- const String &selected = E;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
+ for (const String &path : selected_list) {
String conf = path.plus_file("project.godot");
if (!FileAccess::exists(conf)) {
@@ -2120,7 +2105,7 @@ void ProjectManager::_open_selected_projects() {
return;
}
- print_line("Editing project: " + path + " (" + selected + ")");
+ print_line("Editing project: " + path);
List<String> args;
@@ -2244,8 +2229,7 @@ void ProjectManager::_run_project_confirm() {
continue;
}
- const String &selected = selected_list[i].project_key;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
+ const String &path = selected_list[i].path;
// `.substr(6)` on `ProjectSettings::get_singleton()->get_imported_files_path()` strips away the leading "res://".
if (!DirAccess::exists(path.plus_file(ProjectSettings::get_singleton()->get_imported_files_path().substr(6)))) {
@@ -2254,7 +2238,7 @@ void ProjectManager::_run_project_confirm() {
continue;
}
- print_line("Running project: " + path + " (" + selected + ")");
+ print_line("Running project: " + path);
List<String> args;
@@ -2285,7 +2269,7 @@ void ProjectManager::_run_project() {
}
}
-void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) {
+void ProjectManager::_scan_dir(const String &path) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error error = da->change_dir(path);
ERR_FAIL_COND_MSG(error != OK, "Could not scan directory at: " + path);
@@ -2293,26 +2277,18 @@ void ProjectManager::_scan_dir(const String &path, List<String> *r_projects) {
String n = da->get_next();
while (!n.is_empty()) {
if (da->current_is_dir() && !n.begins_with(".")) {
- _scan_dir(da->get_current_dir().plus_file(n), r_projects);
+ _scan_dir(da->get_current_dir().plus_file(n));
} else if (n == "project.godot") {
- r_projects->push_back(da->get_current_dir());
+ _project_list->add_project(da->get_current_dir(), false);
}
n = da->get_next();
}
da->list_dir_end();
}
-
void ProjectManager::_scan_begin(const String &p_base) {
print_line("Scanning projects at: " + p_base);
- List<String> projects;
- _scan_dir(p_base, &projects);
- print_line("Found " + itos(projects.size()) + " projects.");
-
- for (const String &E : projects) {
- String proj = get_project_key_from_path(E);
- EditorSettings::get_singleton()->set("projects/" + proj, E);
- }
- EditorSettings::get_singleton()->save();
+ _scan_dir(p_base);
+ _project_list->save_config();
_load_recent_projects();
}
@@ -2338,9 +2314,7 @@ void ProjectManager::_rename_project() {
}
for (const String &E : selected_list) {
- const String &selected = E;
- String path = EditorSettings::get_singleton()->get("projects/" + selected);
- npdialog->set_project_path(path);
+ npdialog->set_project_path(E);
npdialog->set_mode(ProjectDialog::MODE_RENAME);
npdialog->show_dialog();
}
@@ -2871,6 +2845,7 @@ ProjectManager::ProjectManager() {
_build_icon_type_cache(get_theme());
}
+ _project_list->migrate_config();
_load_recent_projects();
Ref<DirAccess> dir_access = DirAccess::create(DirAccess::AccessType::ACCESS_FILESYSTEM);
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 28383e4142..10bf25c048 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -125,7 +125,7 @@ class ProjectManager : public Control {
void _on_projects_updated();
void _scan_multiple_folders(PackedStringArray p_files);
void _scan_begin(const String &p_base);
- void _scan_dir(const String &path, List<String> *r_projects);
+ void _scan_dir(const String &path);
void _install_project(const String &p_zip_path, const String &p_title);
diff --git a/methods.py b/methods.py
index b719395ae0..82c19f09e3 100644
--- a/methods.py
+++ b/methods.py
@@ -1,5 +1,6 @@
import os
import re
+import sys
import glob
import subprocess
from collections import OrderedDict
@@ -336,7 +337,20 @@ def disable_module(self):
self.disabled_modules.append(self.current_module)
-def module_check_dependencies(self, module, dependencies, silent=False):
+def module_add_dependencies(self, module, dependencies, optional=False):
+ """
+ Adds dependencies for a given module.
+ Meant to be used in module `can_build` methods.
+ """
+ if module not in self.module_dependencies:
+ self.module_dependencies[module] = [[], []]
+ if optional:
+ self.module_dependencies[module][1].extend(dependencies)
+ else:
+ self.module_dependencies[module][0].extend(dependencies)
+
+
+def module_check_dependencies(self, module):
"""
Checks if module dependencies are enabled for a given module,
and prints a warning if they aren't.
@@ -344,23 +358,41 @@ def module_check_dependencies(self, module, dependencies, silent=False):
Returns a boolean (True if dependencies are satisfied).
"""
missing_deps = []
- for dep in dependencies:
+ required_deps = self.module_dependencies[module][0] if module in self.module_dependencies else []
+ for dep in required_deps:
opt = "module_{}_enabled".format(dep)
if not opt in self or not self[opt]:
missing_deps.append(dep)
if missing_deps != []:
- if not silent:
- print(
- "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
- module, ", ".join(missing_deps)
- )
+ print(
+ "Disabling '{}' module as the following dependencies are not satisfied: {}".format(
+ module, ", ".join(missing_deps)
)
+ )
return False
else:
return True
+def sort_module_list(env):
+ out = OrderedDict()
+ deps = {k: v[0] + list(filter(lambda x: x in env.module_list, v[1])) for k, v in env.module_dependencies.items()}
+
+ frontier = list(env.module_list.keys())
+ explored = []
+ while len(frontier):
+ cur = frontier.pop()
+ deps_list = deps[cur] if cur in deps else []
+ if len(deps_list) and any([d not in explored for d in deps_list]):
+ # Will explore later, after its dependencies
+ frontier.insert(0, cur)
+ continue
+ explored.append(cur)
+ for k in explored:
+ env.module_list.move_to_end(k)
+
+
def use_windows_spawn_fix(self, platform=None):
if os.name != "nt":
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 61ce6185a5..a7d5c406e9 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ env.module_add_dependencies("gdscript", ["jsonrpc", "websocket"], True)
return True
diff --git a/modules/msdfgen/config.py b/modules/msdfgen/config.py
index 653e466a74..631894400d 100644
--- a/modules/msdfgen/config.py
+++ b/modules/msdfgen/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("msdfgen", ["freetype"])
+ env.module_add_dependencies("msdfgen", ["freetype"])
+ return True
def configure(env):
diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor_plugin.cpp
index 1f79b8c3e3..50f1434ad8 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.cpp
+++ b/modules/multiplayer/editor/replication_editor_plugin.cpp
@@ -255,8 +255,6 @@ void ReplicationEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked);
ClassDB::bind_method("_can_drop_data_fw", &ReplicationEditor::_can_drop_data_fw);
ClassDB::bind_method("_drop_data_fw", &ReplicationEditor::_drop_data_fw);
-
- ADD_SIGNAL(MethodInfo("keying_changed"));
}
bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
@@ -322,10 +320,6 @@ void ReplicationEditor::_notification(int p_what) {
add_pick_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
pin->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
} break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- update_keying();
- } break;
}
}
@@ -341,28 +335,15 @@ void ReplicationEditor::_add_pressed() {
return;
}
String np_text = np_line_edit->get_text();
- if (np_text.find(":") == -1) {
- np_text = ":" + np_text;
- }
- NodePath prop = NodePath(np_text);
- if (prop.is_empty()) {
- return;
- }
- UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
- undo_redo->create_action(TTR("Add property"));
- config = current->get_replication_config();
- if (config.is_null()) {
- config.instantiate();
- current->set_replication_config(config);
- undo_redo->add_do_method(current, "set_replication_config", config);
- undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>());
- _update_config();
+ int idx = np_text.find(":");
+ if (idx == -1) {
+ np_text = ".:" + np_text;
+ } else if (idx == 0) {
+ np_text = "." + np_text;
}
- undo_redo->add_do_method(config.ptr(), "add_property", prop);
- undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
- undo_redo->add_do_method(this, "_update_config");
- undo_redo->add_undo_method(this, "_update_config");
- undo_redo->commit_action();
+ NodePath path = NodePath(np_text);
+
+ _add_sync_property(path);
}
void ReplicationEditor::_tree_item_edited() {
@@ -440,32 +421,12 @@ void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bo
}
}
-void ReplicationEditor::update_keying() {
- /// TODO make keying usable.
-#if 0
- bool keying_enabled = false;
- EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
- if (is_visible_in_tree() && config.is_valid() && editor_history->get_path_size() > 0) {
- Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
- keying_enabled = Object::cast_to<Node>(obj) != nullptr;
- }
-
- if (keying_enabled == keying) {
- return;
- }
-
- keying = keying_enabled;
- emit_signal(SNAME("keying_changed"));
-#endif
-}
-
void ReplicationEditor::_update_config() {
deleting = NodePath();
tree->clear();
tree->create_item();
drop_label->set_visible(true);
if (!config.is_valid()) {
- update_keying();
return;
}
TypedArray<NodePath> props = config->get_properties();
@@ -476,7 +437,6 @@ void ReplicationEditor::_update_config() {
const NodePath path = props[i];
_add_property(path, config->property_get_spawn(path), config->property_get_sync(path));
}
- update_keying();
}
void ReplicationEditor::edit(MultiplayerSynchronizer *p_sync) {
@@ -532,43 +492,6 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
item->set_editable(2, true);
}
-void ReplicationEditor::property_keyed(const String &p_property) {
- ERR_FAIL_COND(!current || config.is_null());
- Node *root = current->get_node(current->get_root_path());
- ERR_FAIL_COND(!root);
- EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
- ERR_FAIL_COND(history->get_path_size() == 0);
- Node *node = Object::cast_to<Node>(ObjectDB::get_instance(history->get_path_object(0)));
- ERR_FAIL_COND(!node);
- if (node->is_class("MultiplayerSynchronizer")) {
- error_dialog->set_text(TTR("Properties of 'MultiplayerSynchronizer' cannot be configured for replication."));
- error_dialog->popup_centered();
- return;
- }
- if (history->get_path_size() > 1 || p_property.get_slice_count(":") > 1) {
- error_dialog->set_text(TTR("Subresources cannot yet be configured for replication."));
- error_dialog->popup_centered();
- return;
- }
-
- String path = root->get_path_to(node);
- for (int i = 1; i < history->get_path_size(); i++) {
- String prop = history->get_path_property(i);
- ERR_FAIL_COND(prop == "");
- path += ":" + prop;
- }
- path += ":" + p_property;
-
- NodePath prop = path;
- UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
- undo_redo->create_action(TTR("Add property"));
- undo_redo->add_do_method(config.ptr(), "add_property", prop);
- undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
- undo_redo->add_do_method(this, "_update_config");
- undo_redo->add_undo_method(this, "_update_config");
- undo_redo->commit_action();
-}
-
/// ReplicationEditorPlugin
ReplicationEditorPlugin::ReplicationEditorPlugin() {
repl_editor = memnew(ReplicationEditor);
@@ -580,26 +503,9 @@ ReplicationEditorPlugin::ReplicationEditorPlugin() {
ReplicationEditorPlugin::~ReplicationEditorPlugin() {
}
-void ReplicationEditorPlugin::_keying_changed() {
- // TODO make lock usable.
- //InspectorDock::get_inspector_singleton()->set_keying(repl_editor->has_keying(), this);
-}
-
-void ReplicationEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
- if (!repl_editor->has_keying()) {
- return;
- }
- repl_editor->property_keyed(p_keyed);
-}
-
void ReplicationEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- //Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request));
- InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &ReplicationEditorPlugin::_property_keyed));
- repl_editor->connect("keying_changed", callable_mp(this, &ReplicationEditorPlugin::_keying_changed));
- // TODO make lock usable.
- //InspectorDock::get_inspector_singleton()->connect("object_inspected", callable_mp(repl_editor, &ReplicationEditor::update_keying));
get_tree()->connect("node_removed", callable_mp(this, &ReplicationEditorPlugin::_node_removed));
} break;
}
@@ -635,8 +541,6 @@ bool ReplicationEditorPlugin::handles(Object *p_object) const {
void ReplicationEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- //editor->hide_animation_player_editors();
- //editor->animation_panel_make_visible(true);
button->show();
EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor);
} else if (!repl_editor->get_pin()->is_pressed()) {
diff --git a/modules/multiplayer/editor/replication_editor_plugin.h b/modules/multiplayer/editor/replication_editor_plugin.h
index 5cc2bbe937..e60e49cc25 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.h
+++ b/modules/multiplayer/editor/replication_editor_plugin.h
@@ -61,7 +61,6 @@ private:
Ref<SceneReplicationConfig> config;
NodePath deleting;
Tree *tree = nullptr;
- bool keying = false;
PropertySelector *prop_selector = nullptr;
SceneTreeDialog *pick_node = nullptr;
@@ -98,11 +97,8 @@ protected:
void _notification(int p_what);
public:
- void update_keying();
void edit(MultiplayerSynchronizer *p_object);
- bool has_keying() const { return keying; }
MultiplayerSynchronizer *get_current() const { return current; }
- void property_keyed(const String &p_property);
Button *get_pin() { return pin; }
ReplicationEditor();
@@ -117,8 +113,6 @@ private:
ReplicationEditor *repl_editor = nullptr;
void _node_removed(Node *p_node);
- void _keying_changed();
- void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
void _pinned();
@@ -133,17 +127,5 @@ public:
ReplicationEditorPlugin();
~ReplicationEditorPlugin();
};
-#else
-class ReplicationEditorPlugin : public EditorPlugin {
- GDCLASS(ReplicationEditorPlugin, EditorPlugin);
-
-public:
- virtual void edit(Object *p_object) override {}
- virtual bool handles(Object *p_object) const override { return false; }
- virtual void make_visible(bool p_visible) override {}
-
- ReplicationEditorPlugin() {}
- ~ReplicationEditorPlugin() {}
-};
#endif // REPLICATION_EDITOR_PLUGIN_H
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 81b8e1ea5d..c6678307af 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -36,8 +36,8 @@ def make_icu_data(target, source, env):
# Thirdparty source files
thirdparty_obj = []
-freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
if env["builtin_harfbuzz"]:
env_harfbuzz = env_modules.Clone()
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 8f626f02b8..429d2e1fdc 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -3,8 +3,8 @@
Import("env")
Import("env_modules")
-freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
env_text_server_fb = env_modules.Clone()
diff --git a/modules/theora/config.py b/modules/theora/config.py
index 7f354a8fda..9a27e8e132 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,7 +1,8 @@
def can_build(env, platform):
if env["arch"].startswith("rv"):
return False
- return env.module_check_dependencies("theora", ["ogg", "vorbis"])
+ env.module_add_dependencies("theora", ["ogg", "vorbis"])
+ return True
def configure(env):
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 7ce885a37a..a231ef179d 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("vorbis", ["ogg"])
+ env.module_add_dependencies("vorbis", ["ogg"])
+ return True
def configure(env):
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
deleted file mode 100644
index 2033098cad..0000000000
--- a/modules/websocket/emws_server.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*************************************************************************/
-/* emws_server.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "emws_server.h"
-#include "core/os/os.h"
-
-void EMWSServer::set_extra_headers(const Vector<String> &p_headers) {
-}
-
-Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) {
- return FAILED;
-}
-
-bool EMWSServer::is_listening() const {
- return false;
-}
-
-void EMWSServer::stop() {
-}
-
-bool EMWSServer::has_peer(int p_id) const {
- return false;
-}
-
-Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const {
- return nullptr;
-}
-
-Vector<String> EMWSServer::get_protocols() const {
- Vector<String> out;
-
- return out;
-}
-
-IPAddress EMWSServer::get_peer_address(int p_peer_id) const {
- return IPAddress();
-}
-
-int EMWSServer::get_peer_port(int p_peer_id) const {
- return 0;
-}
-
-void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
-}
-
-void EMWSServer::poll() {
-}
-
-int EMWSServer::get_max_packet_size() const {
- return 0;
-}
-
-Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- return OK;
-}
-
-EMWSServer::EMWSServer() {
-}
-
-EMWSServer::~EMWSServer() {
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
deleted file mode 100644
index 14a9449605..0000000000
--- a/modules/websocket/emws_server.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************/
-/* emws_server.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef EMWS_SERVER_H
-#define EMWS_SERVER_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/object/ref_counted.h"
-#include "emws_peer.h"
-#include "websocket_server.h"
-
-class EMWSServer : public WebSocketServer {
- GDCIIMPL(EMWSServer, WebSocketServer);
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- void set_extra_headers(const Vector<String> &p_headers) override;
- Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override;
- void stop() override;
- bool is_listening() const override;
- bool has_peer(int p_id) const override;
- Ref<WebSocketPeer> get_peer(int p_id) const override;
- IPAddress get_peer_address(int p_peer_id) const override;
- int get_peer_port(int p_peer_id) const override;
- void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override;
- int get_max_packet_size() const override;
- virtual void poll() override;
- virtual Vector<String> get_protocols() const;
-
- EMWSServer();
- ~EMWSServer();
-};
-
-#endif
-
-#endif // EMWS_SERVER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index f562de111f..056111ec92 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -33,11 +33,13 @@
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
+#include "websocket_client.h"
+#include "websocket_server.h"
+
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
#include "emws_peer.h"
-#include "emws_server.h"
#else
#include "wsl_client.h"
#include "wsl_server.h"
@@ -60,7 +62,6 @@ void initialize_websocket_module(ModuleInitializationLevel p_level) {
#ifdef JAVASCRIPT_ENABLED
EMWSPeer::make_default();
EMWSClient::make_default();
- EMWSServer::make_default();
#else
WSLPeer::make_default();
WSLClient::make_default();
diff --git a/tests/core/math/test_vector4.h b/tests/core/math/test_vector4.h
new file mode 100644
index 0000000000..4b8759c0ca
--- /dev/null
+++ b/tests/core/math/test_vector4.h
@@ -0,0 +1,312 @@
+/*************************************************************************/
+/* test_vector4.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR4_H
+#define TEST_VECTOR4_H
+
+#include "core/math/vector4.h"
+#include "tests/test_macros.h"
+
+#define Math_SQRT3 1.7320508075688772935274463415059
+
+namespace TestVector4 {
+
+TEST_CASE("[Vector4] Axis methods") {
+ Vector4 vector = Vector4(1.2, 3.4, 5.6, -0.9);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector4::Axis::AXIS_Z,
+ "Vector4 max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector4::Axis::AXIS_W,
+ "Vector4 min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == (real_t)5.6,
+ "Vector4 get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == (real_t)-0.9,
+ "Vector4 array operator should work as expected.");
+
+ vector.set_axis(Vector4::Axis::AXIS_Y, 4.7);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector4::Axis::AXIS_Y) == (real_t)4.7,
+ "Vector4 set_axis should work as expected.");
+ vector[Vector4::Axis::AXIS_Y] = 3.7;
+ CHECK_MESSAGE(
+ vector[Vector4::Axis::AXIS_Y] == (real_t)3.7,
+ "Vector4 array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector4] Interpolation methods") {
+ const Vector4 vector1 = Vector4(1, 2, 3, 4);
+ const Vector4 vector2 = Vector4(4, 5, 6, 7);
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 0.5) == Vector4(2.5, 3.5, 4.5, 5.5),
+ "Vector4 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector4(2, 3, 4, 5)),
+ "Vector4 lerp should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector4(), Vector4(7, 7, 7, 7), 0.5) == Vector4(2.375, 3.5, 4.625, 5.75),
+ "Vector4 cubic_interpolate should work as expected.");
+ CHECK_MESSAGE(
+ vector1.cubic_interpolate(vector2, Vector4(), Vector4(7, 7, 7, 7), 1.0 / 3.0).is_equal_approx(Vector4(1.851851940155029297, 2.962963104248046875, 4.074074268341064453, 5.185185185185)),
+ "Vector4 cubic_interpolate should work as expected.");
+}
+
+TEST_CASE("[Vector4] Length methods") {
+ const Vector4 vector1 = Vector4(10, 10, 10, 10);
+ const Vector4 vector2 = Vector4(20, 30, 40, 50);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 400,
+ "Vector4 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 20),
+ "Vector4 length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 5400,
+ "Vector4 length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), (real_t)73.484692283495),
+ "Vector4 length should work as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.distance_to(vector2), (real_t)54.772255750517),
+ "Vector4 distance_to should work as expected.");
+}
+
+TEST_CASE("[Vector4] Limiting methods") {
+ const Vector4 vector = Vector4(10, 10, 10, 10);
+ CHECK_MESSAGE(
+ Vector4(-5, 5, 15, -15).clamp(Vector4(), vector) == Vector4(0, 5, 10, 0),
+ "Vector4 clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector4(0, 10, 15, 18), Vector4(5, 10, 20, 25)) == Vector4(5, 10, 15, 18),
+ "Vector4 clamp should work as expected.");
+}
+
+TEST_CASE("[Vector4] Normalization methods") {
+ CHECK_MESSAGE(
+ Vector4(1, 0, 0, 0).is_normalized() == true,
+ "Vector4 is_normalized should return true for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).is_normalized() == false,
+ "Vector4 is_normalized should return false for a non-normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 0, 0, 0).normalized() == Vector4(1, 0, 0, 0),
+ "Vector4 normalized should return the same vector for a normalized vector.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 0, 0).normalized().is_equal_approx(Vector4(Math_SQRT12, Math_SQRT12, 0, 0)),
+ "Vector4 normalized should work as expected.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).normalized().is_equal_approx(Vector4(0.5, 0.5, 0.5, 0.5)),
+ "Vector4 normalized should work as expected.");
+}
+
+TEST_CASE("[Vector4] Operators") {
+ const Vector4 decimal1 = Vector4(2.3, 4.9, 7.8, 3.2);
+ const Vector4 decimal2 = Vector4(1.2, 3.4, 5.6, 1.7);
+ const Vector4 power1 = Vector4(0.75, 1.5, 0.625, 0.125);
+ const Vector4 power2 = Vector4(0.5, 0.125, 0.25, 0.75);
+ const Vector4 int1 = Vector4(4, 5, 9, 2);
+ const Vector4 int2 = Vector4(1, 2, 3, 1);
+
+ CHECK_MESSAGE(
+ -decimal1 == Vector4(-2.3, -4.9, -7.8, -3.2),
+ "Vector4 change of sign should work as expected.");
+ CHECK_MESSAGE(
+ (decimal1 + decimal2).is_equal_approx(Vector4(3.5, 8.3, 13.4, 4.9)),
+ "Vector4 addition should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 + power2) == Vector4(1.25, 1.625, 0.875, 0.875),
+ "Vector4 addition with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 + int2) == Vector4(5, 7, 12, 3),
+ "Vector4 addition with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 - decimal2).is_equal_approx(Vector4(1.1, 1.5, 2.2, 1.5)),
+ "Vector4 subtraction should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 - power2) == Vector4(0.25, 1.375, 0.375, -0.625),
+ "Vector4 subtraction with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 - int2) == Vector4(3, 3, 6, 1),
+ "Vector4 subtraction with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * decimal2).is_equal_approx(Vector4(2.76, 16.66, 43.68, 5.44)),
+ "Vector4 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * power2) == Vector4(0.375, 0.1875, 0.15625, 0.09375),
+ "Vector4 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * int2) == Vector4(4, 10, 27, 2),
+ "Vector4 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / decimal2).is_equal_approx(Vector4(1.91666666666666666, 1.44117647058823529, 1.39285714285714286, 1.88235294118)),
+ "Vector4 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / power2) == Vector4(1.5, 12.0, 2.5, 1.0 / 6.0),
+ "Vector4 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / int2) == Vector4(4, 2.5, 3, 2),
+ "Vector4 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 * 2).is_equal_approx(Vector4(4.6, 9.8, 15.6, 6.4)),
+ "Vector4 multiplication should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 * 2) == Vector4(1.5, 3, 1.25, 0.25),
+ "Vector4 multiplication with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 * 2) == Vector4(8, 10, 18, 4),
+ "Vector4 multiplication with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (decimal1 / 2).is_equal_approx(Vector4(1.15, 2.45, 3.9, 1.6)),
+ "Vector4 division should behave as expected.");
+ CHECK_MESSAGE(
+ (power1 / 2) == Vector4(0.375, 0.75, 0.3125, 0.0625),
+ "Vector4 division with powers of two should give exact results.");
+ CHECK_MESSAGE(
+ (int1 / 2) == Vector4(2, 2.5, 4.5, 1),
+ "Vector4 division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((String)decimal1) == "(2.3, 4.9, 7.8, 3.2)",
+ "Vector4 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)decimal2) == "(1.2, 3.4, 5.6, 1.7)",
+ "Vector4 cast to String should work as expected.");
+ CHECK_MESSAGE(
+ ((String)Vector4(9.7, 9.8, 9.9, -1.8)) == "(9.7, 9.8, 9.9, -1.8)",
+ "Vector4 cast to String should work as expected.");
+#ifdef REAL_T_IS_DOUBLE
+ CHECK_MESSAGE(
+ ((String)Vector4(Math_E, Math_SQRT2, Math_SQRT3, Math_SQRT3)) == "(2.71828182845905, 1.4142135623731, 1.73205080756888, 1.73205080756888)",
+ "Vector4 cast to String should print the correct amount of digits for real_t = double.");
+#else
+ CHECK_MESSAGE(
+ ((String)Vector4(Math_E, Math_SQRT2, Math_SQRT3, Math_SQRT3)) == "(2.718282, 1.414214, 1.732051, 1.732051)",
+ "Vector4 cast to String should print the correct amount of digits for real_t = float.");
+#endif // REAL_T_IS_DOUBLE
+}
+
+TEST_CASE("[Vector4] Other methods") {
+ const Vector4 vector = Vector4(1.2, 3.4, 5.6, 1.6);
+ CHECK_MESSAGE(
+ vector.direction_to(Vector4()).is_equal_approx(-vector.normalized()),
+ "Vector4 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ Vector4(1, 1, 1, 1).direction_to(Vector4(2, 2, 2, 2)).is_equal_approx(Vector4(0.5, 0.5, 0.5, 0.5)),
+ "Vector4 direction_to should work as expected.");
+ CHECK_MESSAGE(
+ vector.inverse().is_equal_approx(Vector4(1 / 1.2, 1 / 3.4, 1 / 5.6, 1 / 1.6)),
+ "Vector4 inverse should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmod(2).is_equal_approx(Vector4(1.2, 1.4, 1.6, 1.6)),
+ "Vector4 posmod should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmod(2).is_equal_approx(Vector4(0.8, 0.6, 0.4, 0.4)),
+ "Vector4 posmod should work as expected.");
+ CHECK_MESSAGE(
+ vector.posmodv(Vector4(1, 2, 3, 4)).is_equal_approx(Vector4(0.2, 1.4, 2.6, 1.6)),
+ "Vector4 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ (-vector).posmodv(Vector4(2, 3, 4, 5)).is_equal_approx(Vector4(0.8, 2.6, 2.4, 3.4)),
+ "Vector4 posmodv should work as expected.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector4(1, 1, 1, 1)) == Vector4(1, 3, 6, 2),
+ "Vector4 snapped to integers should be the same as rounding.");
+ CHECK_MESSAGE(
+ vector.snapped(Vector4(0.25, 0.25, 0.25, 0.25)) == Vector4(1.25, 3.5, 5.5, 1.5),
+ "Vector4 snapped to 0.25 should give exact results.");
+}
+
+TEST_CASE("[Vector4] Rounding methods") {
+ const Vector4 vector1 = Vector4(1.2, 3.4, 5.6, 1.6);
+ const Vector4 vector2 = Vector4(1.2, -3.4, -5.6, -1.6);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector4 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector4 abs should work as expected.");
+ CHECK_MESSAGE(
+ vector1.ceil() == Vector4(2, 4, 6, 2),
+ "Vector4 ceil should work as expected.");
+ CHECK_MESSAGE(
+ vector2.ceil() == Vector4(2, -3, -5, -1),
+ "Vector4 ceil should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.floor() == Vector4(1, 3, 5, 1),
+ "Vector4 floor should work as expected.");
+ CHECK_MESSAGE(
+ vector2.floor() == Vector4(1, -4, -6, -2),
+ "Vector4 floor should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.round() == Vector4(1, 3, 6, 2),
+ "Vector4 round should work as expected.");
+ CHECK_MESSAGE(
+ vector2.round() == Vector4(1, -3, -6, -2),
+ "Vector4 round should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector4(1, 1, 1, 1),
+ "Vector4 sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector4(1, -1, -1, -1),
+ "Vector4 sign should work as expected.");
+}
+
+TEST_CASE("[Vector4] Linear algebra methods") {
+ const Vector4 vector_x = Vector4(1, 0, 0, 0);
+ const Vector4 vector_y = Vector4(0, 1, 0, 0);
+ const Vector4 vector1 = Vector4(1.7, 2.3, 1, 9.1);
+ const Vector4 vector2 = Vector4(-8.2, -16, 3, 2.4);
+
+ CHECK_MESSAGE(
+ vector_x.dot(vector_y) == 0.0,
+ "Vector4 dot product of perpendicular vectors should be zero.");
+ CHECK_MESSAGE(
+ vector_x.dot(vector_x) == 1.0,
+ "Vector4 dot product of identical unit vectors should be one.");
+ CHECK_MESSAGE(
+ (vector_x * 10).dot(vector_x * 10) == 100.0,
+ "Vector4 dot product of same direction vectors should behave as expected.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx((vector1 * 2).dot(vector2 * 4), (real_t)-25.9 * 8),
+ "Vector4 dot product should work as expected.");
+}
+} // namespace TestVector4
+
+#endif // TEST_VECTOR4_H
diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h
new file mode 100644
index 0000000000..ac63001b24
--- /dev/null
+++ b/tests/core/math/test_vector4i.h
@@ -0,0 +1,148 @@
+/*************************************************************************/
+/* test_vector4i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_VECTOR4I_H
+#define TEST_VECTOR4I_H
+
+#include "core/math/vector4i.h"
+#include "tests/test_macros.h"
+
+namespace TestVector4i {
+
+TEST_CASE("[Vector4i] Axis methods") {
+ Vector4i vector = Vector4i(1, 2, 3, 4);
+ CHECK_MESSAGE(
+ vector.max_axis_index() == Vector4i::Axis::AXIS_W,
+ "Vector4i max_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.min_axis_index() == Vector4i::Axis::AXIS_X,
+ "Vector4i min_axis_index should work as expected.");
+ CHECK_MESSAGE(
+ vector.get_axis(vector.max_axis_index()) == 4,
+ "Vector4i get_axis should work as expected.");
+ CHECK_MESSAGE(
+ vector[vector.min_axis_index()] == 1,
+ "Vector4i array operator should work as expected.");
+
+ vector.set_axis(Vector4i::Axis::AXIS_Y, 5);
+ CHECK_MESSAGE(
+ vector.get_axis(Vector4i::Axis::AXIS_Y) == 5,
+ "Vector4i set_axis should work as expected.");
+ vector[Vector4i::Axis::AXIS_Y] = 5;
+ CHECK_MESSAGE(
+ vector[Vector4i::Axis::AXIS_Y] == 5,
+ "Vector4i array operator setter should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Clamp method") {
+ const Vector4i vector = Vector4i(10, 10, 10, 10);
+ CHECK_MESSAGE(
+ Vector4i(-5, 5, 15, INT_MAX).clamp(Vector4i(), vector) == Vector4i(0, 5, 10, 10),
+ "Vector4i clamp should work as expected.");
+ CHECK_MESSAGE(
+ vector.clamp(Vector4i(0, 10, 15, -10), Vector4i(5, 10, 20, -5)) == Vector4i(5, 10, 15, -5),
+ "Vector4i clamp should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Length methods") {
+ const Vector4i vector1 = Vector4i(10, 10, 10, 10);
+ const Vector4i vector2 = Vector4i(20, 30, 40, 50);
+ CHECK_MESSAGE(
+ vector1.length_squared() == 400,
+ "Vector4i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector1.length(), 20),
+ "Vector4i length should work as expected.");
+ CHECK_MESSAGE(
+ vector2.length_squared() == 5400,
+ "Vector4i length_squared should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ Math::is_equal_approx(vector2.length(), 73.4846922835),
+ "Vector4i length should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Operators") {
+ const Vector4i vector1 = Vector4i(4, 5, 9, 2);
+ const Vector4i vector2 = Vector4i(1, 2, 3, 4);
+
+ CHECK_MESSAGE(
+ -vector1 == Vector4i(-4, -5, -9, -2),
+ "Vector4i change of sign should work as expected.");
+ CHECK_MESSAGE(
+ (vector1 + vector2) == Vector4i(5, 7, 12, 6),
+ "Vector4i addition with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 - vector2) == Vector4i(3, 3, 6, -2),
+ "Vector4i subtraction with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 * vector2) == Vector4i(4, 10, 27, 8),
+ "Vector4i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / vector2) == Vector4i(4, 2, 3, 0),
+ "Vector4i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ (vector1 * 2) == Vector4i(8, 10, 18, 4),
+ "Vector4i multiplication with integers should give exact results.");
+ CHECK_MESSAGE(
+ (vector1 / 2) == Vector4i(2, 2, 4, 1),
+ "Vector4i division with integers should give exact results.");
+
+ CHECK_MESSAGE(
+ ((Vector4)vector1) == Vector4(4, 5, 9, 2),
+ "Vector4i cast to Vector4 should work as expected.");
+ CHECK_MESSAGE(
+ ((Vector4)vector2) == Vector4(1, 2, 3, 4),
+ "Vector4i cast to Vector4 should work as expected.");
+ CHECK_MESSAGE(
+ Vector4i(Vector4(1.1, 2.9, 3.9, 100.5)) == Vector4i(1, 2, 3, 100),
+ "Vector4i constructed from Vector4 should work as expected.");
+}
+
+TEST_CASE("[Vector4i] Abs and sign methods") {
+ const Vector4i vector1 = Vector4i(1, 3, 5, 7);
+ const Vector4i vector2 = Vector4i(1, -3, -5, 7);
+ CHECK_MESSAGE(
+ vector1.abs() == vector1,
+ "Vector4i abs should work as expected.");
+ CHECK_MESSAGE(
+ vector2.abs() == vector1,
+ "Vector4i abs should work as expected.");
+
+ CHECK_MESSAGE(
+ vector1.sign() == Vector4i(1, 1, 1, 1),
+ "Vector4i sign should work as expected.");
+ CHECK_MESSAGE(
+ vector2.sign() == Vector4i(1, -1, -1, 1),
+ "Vector4i sign should work as expected.");
+}
+} // namespace TestVector4i
+
+#endif // TEST_VECTOR4I_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 628b9cbc3c..3d186711cb 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -58,6 +58,8 @@
#include "tests/core/math/test_vector2i.h"
#include "tests/core/math/test_vector3.h"
#include "tests/core/math/test_vector3i.h"
+#include "tests/core/math/test_vector4.h"
+#include "tests/core/math/test_vector4i.h"
#include "tests/core/object/test_class_db.h"
#include "tests/core/object/test_method_bind.h"
#include "tests/core/object/test_object.h"