summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/csg_shape.cpp9
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml2
-rw-r--r--modules/freetype/SCsub2
-rw-r--r--modules/gdnative/gdnative.cpp19
-rw-r--r--modules/gdnative/gdnative/array.cpp32
-rw-r--r--modules/gdnative/gdnative/basis.cpp9
-rw-r--r--modules/gdnative/gdnative/color.cpp41
-rw-r--r--modules/gdnative/gdnative/node_path.cpp9
-rw-r--r--modules/gdnative/gdnative/quat.cpp6
-rw-r--r--modules/gdnative/gdnative/rect2.cpp21
-rw-r--r--modules/gdnative/gdnative/string.cpp58
-rw-r--r--modules/gdnative/gdnative_api.json177
-rw-r--r--modules/gdnative/include/gdnative/array.h8
-rw-r--r--modules/gdnative/include/gdnative/basis.h2
-rw-r--r--modules/gdnative/include/gdnative/color.h14
-rw-r--r--modules/gdnative/include/gdnative/node_path.h2
-rw-r--r--modules/gdnative/include/gdnative/quat.h2
-rw-r--r--modules/gdnative/include/gdnative/rect2.h6
-rw-r--r--modules/gdnative/include/gdnative/string.h6
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp8
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp12
-rw-r--r--modules/gdscript/gdscript_compiler.cpp20
-rw-r--r--modules/gdscript/gdscript_compiler.h10
-rw-r--r--modules/gdscript/gdscript_editor.cpp60
-rw-r--r--modules/gdscript/gdscript_function.cpp49
-rw-r--r--modules/gdscript/gdscript_function.h10
-rw-r--r--modules/gdscript/gdscript_functions.cpp51
-rw-r--r--modules/gdscript/gdscript_functions.h3
-rw-r--r--modules/gdscript/gdscript_parser.cpp87
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml2
-rw-r--r--modules/gridmap/grid_map.cpp2
-rw-r--r--modules/jpg/image_loader_jpegd.cpp22
-rw-r--r--modules/mono/SCsub108
-rw-r--r--modules/mono/config.py6
-rw-r--r--modules/mono/csharp_script.cpp139
-rw-r--r--modules/mono/csharp_script.h8
-rw-r--r--modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj6
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs16
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs41
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs58
-rw-r--r--modules/mono/editor/GodotSharpTools/StringExtensions.cs4
-rw-r--r--modules/mono/editor/GodotSharpTools/packages.config4
-rw-r--r--modules/mono/editor/bindings_generator.cpp140
-rw-r--r--modules/mono/editor/bindings_generator.h6
-rw-r--r--modules/mono/editor/csharp_project.cpp129
-rw-r--r--modules/mono/editor/csharp_project.h3
-rw-r--r--modules/mono/editor/dotnet_solution.cpp (renamed from modules/mono/editor/net_solution.cpp)63
-rw-r--r--modules/mono/editor/dotnet_solution.h (renamed from modules/mono/editor/net_solution.h)23
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp83
-rw-r--r--modules/mono/editor/godotsharp_builds.h4
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp24
-rw-r--r--modules/mono/editor/godotsharp_export.cpp21
-rw-r--r--modules/mono/editor/godotsharp_export.h2
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp6
-rw-r--r--modules/mono/editor/script_class_parser.cpp542
-rw-r--r--modules/mono/editor/script_class_parser.h79
-rw-r--r--modules/mono/glue/Managed/Files/GD.cs16
-rw-r--r--modules/mono/glue/gd_glue.cpp10
-rw-r--r--modules/mono/godotsharp_defs.h3
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp66
-rw-r--r--modules/mono/mono_gd/gd_mono.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp20
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h10
-rw-r--r--modules/mono/signal_awaiter_utils.cpp2
-rw-r--r--modules/mono/utils/path_utils.cpp10
-rw-r--r--modules/mono/utils/string_utils.cpp28
-rw-r--r--modules/mono/utils/string_utils.h2
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml6
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp19
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.h4
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml2
-rw-r--r--modules/visual_script/visual_script.cpp4
-rw-r--r--modules/visual_script/visual_script_editor.cpp21
-rw-r--r--modules/visual_script/visual_script_editor.h2
-rw-r--r--modules/websocket/emws_client.cpp14
-rw-r--r--modules/websocket/emws_client.h3
-rw-r--r--modules/websocket/emws_peer.cpp57
-rw-r--r--modules/websocket/emws_peer.h22
-rw-r--r--modules/websocket/emws_server.cpp4
-rw-r--r--modules/websocket/emws_server.h1
-rw-r--r--modules/websocket/lws_client.cpp25
-rw-r--r--modules/websocket/lws_client.h7
-rw-r--r--modules/websocket/lws_peer.cpp110
-rw-r--r--modules/websocket/lws_peer.h24
-rw-r--r--modules/websocket/lws_server.cpp11
-rw-r--r--modules/websocket/lws_server.h5
-rw-r--r--modules/websocket/packet_buffer.h122
-rw-r--r--modules/websocket/register_types.cpp17
-rw-r--r--modules/websocket/websocket_client.cpp2
-rw-r--r--modules/websocket/websocket_macros.h10
-rw-r--r--modules/websocket/websocket_multiplayer.cpp7
-rw-r--r--modules/websocket/websocket_multiplayer.h6
-rw-r--r--modules/websocket/websocket_peer.h1
97 files changed, 2368 insertions, 604 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index d1ef08dc83..4eb798de11 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -521,6 +521,11 @@ CSGBrush *CSGMesh::_build_brush() {
Array arrays = mesh->surface_get_arrays(i);
+ if (arrays.size() == 0) {
+ _make_dirty();
+ ERR_FAIL_COND_V(arrays.size() == 0, NULL);
+ }
+
PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
if (avertices.size() == 0)
continue;
@@ -593,8 +598,8 @@ CSGBrush *CSGMesh::_build_brush() {
mw[j / 3] = mat;
}
} else {
- int is = vertices.size();
- int as = avertices.size();
+ int as = vertices.size();
+ int is = avertices.size();
vertices.resize(as + is);
smooth.resize((as + is) / 3);
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 6d990f6f6f..e057a435ac 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -7,7 +7,7 @@
A PacketPeer implementation that should be passed to [method SceneTree.set_network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/networking/high_level_multiplayer.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
<link>http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<demos>
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 7ca40c1b8b..3e2068b8db 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -59,6 +59,8 @@ if env['builtin_freetype']:
if env['platform'] == 'uwp':
# Include header for UWP to fix build issues
env_freetype.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
+ # Globally too, as freetype is used in scene (see bottom)
+ env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
sfnt = thirdparty_dir + 'src/sfnt/sfnt.c'
if env['platform'] == 'javascript':
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index ecde73ae1c..f07fdef488 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -243,12 +243,12 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reloadable"), "set_reloadable", "is_reloadable");
}
GDNative::GDNative() {
@@ -268,7 +268,7 @@ void GDNative::_bind_methods() {
ClassDB::bind_method(D_METHOD("call_native", "calling_type", "procedure_name", "arguments"), &GDNative::call_native);
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
}
void GDNative::set_library(Ref<GDNativeLibrary> p_library) {
@@ -306,6 +306,13 @@ bool GDNative::initialize() {
#elif defined(UWP_ENABLED)
// On UWP we use a relative path from the app
String path = lib_path.replace("res://", "");
+#elif defined(OSX_ENABLED)
+ // On OSX the exported libraries are located under the Frameworks directory.
+ // So we need to replace the library path.
+ String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
+ if (!FileAccess::exists(path)) {
+ path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file());
+ }
#else
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
#endif
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index 1fb0ff0500..a30cc09bf6 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -318,6 +318,38 @@ void GDAPI godot_array_destroy(godot_array *p_self) {
((Array *)p_self)->~Array();
}
+godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep) {
+ const Array *self = (const Array *)p_self;
+ godot_array res;
+ Array *val = (Array *)&res;
+ memnew_placement(val, Array);
+ *val = self->duplicate(p_deep);
+ return res;
+}
+
+godot_variant GDAPI godot_array_max(const godot_array *p_self) {
+ const Array *self = (const Array *)p_self;
+ godot_variant v;
+ Variant *val = (Variant *)&v;
+ memnew_placement(val, Variant);
+ *val = self->max();
+ return v;
+}
+
+godot_variant GDAPI godot_array_min(const godot_array *p_self) {
+ const Array *self = (const Array *)p_self;
+ godot_variant v;
+ Variant *val = (Variant *)&v;
+ memnew_placement(val, Variant);
+ *val = self->min();
+ return v;
+}
+
+void GDAPI godot_array_shuffle(godot_array *p_self) {
+ Array *self = (Array *)p_self;
+ self->shuffle();
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 70d2814577..d88499ade1 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -282,6 +282,15 @@ godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self
return raw_dest;
}
+godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t) {
+ godot_basis raw_dest;
+ Basis *dest = (Basis *)&raw_dest;
+ const Basis *self = (const Basis *)p_self;
+ const Basis *b = (const Basis *)p_b;
+ *dest = self->slerp(*b, p_t);
+ return raw_dest;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp
index 4089f4458a..79f0c71b5e 100644
--- a/modules/gdnative/gdnative/color.cpp
+++ b/modules/gdnative/gdnative/color.cpp
@@ -116,6 +116,26 @@ godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) {
return self->to_rgba32();
}
+godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_abgr32();
+}
+
+godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_abgr64();
+}
+
+godot_int GDAPI godot_color_to_argb64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_argb64();
+}
+
+godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self) {
+ const Color *self = (const Color *)p_self;
+ return self->to_rgba64();
+}
+
godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) {
const Color *self = (const Color *)p_self;
return self->to_argb32();
@@ -156,6 +176,27 @@ godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color
return dest;
}
+godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->darkened(p_amount);
+ return dest;
+}
+
+godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->from_hsv(p_h, p_s, p_v, p_a);
+ return dest;
+}
+
+godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount) {
+ godot_color dest;
+ const Color *self = (const Color *)p_self;
+ *((Color *)&dest) = self->lightened(p_amount);
+ return dest;
+}
+
godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha) {
godot_string dest;
const Color *self = (const Color *)p_self;
diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp
index f24facaae8..cf82940e09 100644
--- a/modules/gdnative/gdnative/node_path.cpp
+++ b/modules/gdnative/gdnative/node_path.cpp
@@ -110,6 +110,15 @@ godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, c
return *self == *b;
}
+godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self) {
+ const NodePath *self = (const NodePath *)p_self;
+ godot_node_path res;
+ NodePath *val = (NodePath *)&res;
+ memnew_placement(val, NodePath);
+ *val = self->get_as_property_path();
+ return res;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
index ddec77edcd..2594759508 100644
--- a/modules/gdnative/gdnative/quat.cpp
+++ b/modules/gdnative/gdnative/quat.cpp
@@ -225,6 +225,12 @@ godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self) {
return raw_dest;
}
+void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle) {
+ Quat *self = (Quat *)p_self;
+ const Vector3 *axis = (const Vector3 *)p_axis;
+ self->set_axis_angle(*axis, p_angle);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index 54b98fc4e5..5cbc2712c3 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -109,6 +109,27 @@ godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p
return dest;
}
+godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom);
+ return dest;
+}
+
+godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by);
+ return dest;
+}
+
+godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self) {
+ godot_rect2 dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2 *)&dest) = self->abs();
+ return dest;
+}
+
godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to) {
godot_rect2 dest;
const Rect2 *self = (const Rect2 *)p_self;
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 8ca57392a3..0996633b70 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -1285,6 +1285,64 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self) {
return self->is_valid_ip_address();
}
+godot_string GDAPI godot_string_dedent(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ String return_value = self->dedent();
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix) {
+ const String *self = (const String *)p_self;
+ String *prefix = (String *)p_prefix;
+ godot_string result;
+ String return_value = self->trim_prefix(*prefix);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix) {
+ const String *self = (const String *)p_self;
+ String *suffix = (String *)p_suffix;
+ godot_string result;
+ String return_value = self->trim_suffix(*suffix);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
+ const String *self = (const String *)p_self;
+ String *chars = (String *)p_chars;
+ godot_string result;
+ String return_value = self->rstrip(*chars);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor,
+ const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+ const String *self = (const String *)p_self;
+ String *divisor = (String *)p_divisor;
+
+ godot_pool_string_array result;
+ memnew_placement(&result, PoolStringArray);
+ PoolStringArray *proxy = (PoolStringArray *)&result;
+ PoolStringArray::Write proxy_writer = proxy->write();
+ Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit);
+ proxy->resize(tmp_result.size());
+
+ for (int i = 0; i < tmp_result.size(); i++) {
+ proxy_writer[i] = tmp_result[i];
+ }
+
+ return result;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 16a34a9a33..c5a1fa139e 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -14,6 +14,183 @@
"next": null,
"api": [
{
+ "name": "godot_color_to_abgr32",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_abgr64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_argb64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_rgba64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_darkened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_color_from_hsv",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_h"],
+ ["const godot_real", "p_s"],
+ ["const godot_real", "p_v"],
+ ["const godot_real", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_color_lightened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_array_duplicate",
+ "return_type": "godot_array",
+ "arguments": [
+ ["const godot_array *", "p_self"],
+ ["const godot_bool", "p_deep"]
+ ]
+ },
+ {
+ "name": "godot_array_max",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_array_min",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_array_shuffle",
+ "return_type": "void",
+ "arguments": [
+ ["godot_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_basis_slerp",
+ "return_type": "godot_basis",
+ "arguments": [
+ ["const godot_basis *", "p_self"],
+ ["const godot_basis *", "p_b"],
+ ["const godot_real", "p_t"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_as_property_path",
+ "return_type": "godot_node_path",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_set_axis_angle",
+ "return_type": "void",
+ "arguments": [
+ ["godot_quat *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["const godot_real", "p_angle"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_individual",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_real", "p_left"],
+ ["const godot_real", "p_top"],
+ ["const godot_real", "p_right"],
+ ["const godot_real", "p_bottom"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_margin",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_int", "p_margin"],
+ ["const godot_real", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2_abs",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_dedent",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_trim_prefix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_prefix"]
+ ]
+ },
+ {
+ "name": "godot_string_trim_suffix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_suffix"]
+ ]
+ },
+ {
+ "name": "godot_string_rstrip",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_chars"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit",
+ "return_type": "godot_pool_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_divisor"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
"name": "godot_basis_get_quat",
"return_type": "godot_quat",
"arguments": [
diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h
index 1e66d133b9..876b8f8e8f 100644
--- a/modules/gdnative/include/gdnative/array.h
+++ b/modules/gdnative/include/gdnative/array.h
@@ -130,6 +130,14 @@ godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_vari
void GDAPI godot_array_destroy(godot_array *p_self);
+godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep);
+
+godot_variant GDAPI godot_array_max(const godot_array *p_self);
+
+godot_variant GDAPI godot_array_min(const godot_array *p_self);
+
+void GDAPI godot_array_shuffle(godot_array *p_self);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index ebe2b1125b..6128bf3ac3 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -127,6 +127,8 @@ godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self
godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b);
+godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h
index 1f0ac8354d..3007dbc6e3 100644
--- a/modules/gdnative/include/gdnative/color.h
+++ b/modules/gdnative/include/gdnative/color.h
@@ -81,6 +81,14 @@ godot_string GDAPI godot_color_as_string(const godot_color *p_self);
godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self);
+godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_argb64(const godot_color *p_self);
+
+godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self);
+
godot_int GDAPI godot_color_to_argb32(const godot_color *p_self);
godot_real GDAPI godot_color_gray(const godot_color *p_self);
@@ -93,6 +101,12 @@ godot_color GDAPI godot_color_linear_interpolate(const godot_color *p_self, cons
godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over);
+godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount);
+
+godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a);
+
+godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount);
+
godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha);
godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b);
diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h
index 2b55e01d13..48fe5b4d3d 100644
--- a/modules/gdnative/include/gdnative/node_path.h
+++ b/modules/gdnative/include/gdnative/node_path.h
@@ -80,6 +80,8 @@ godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self);
godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b);
+godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h
index b1290f745c..634f486e66 100644
--- a/modules/gdnative/include/gdnative/quat.h
+++ b/modules/gdnative/include/gdnative/quat.h
@@ -109,6 +109,8 @@ godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot
godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self);
+void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h
index 4adcb73e3d..47c15c80bd 100644
--- a/modules/gdnative/include/gdnative/rect2.h
+++ b/modules/gdnative/include/gdnative/rect2.h
@@ -77,6 +77,12 @@ godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_ve
godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by);
+godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom);
+
+godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by);
+
+godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self);
+
godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to);
godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b);
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 73245160c1..95ae42a9ec 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -246,6 +246,12 @@ godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self);
+godot_string GDAPI godot_string_dedent(const godot_string *p_self);
+godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix);
+godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix);
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars);
+godot_pool_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
void GDAPI godot_string_destroy(godot_string *p_self);
#ifdef __cplusplus
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 3e56275396..eb133502c4 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -72,11 +72,11 @@ void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_signal_documentation", "signal_name"), &NativeScript::get_signal_documentation);
ClassDB::bind_method(D_METHOD("get_property_documentation", "path"), &NativeScript::get_property_documentation);
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "class_name"), "set_class_name", "get_class_name");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "library", PROPERTY_HINT_RESOURCE_TYPE, "GDNativeLibrary"), "set_library", "get_library");
ADD_GROUP("Script Class", "script_class_");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name");
- ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_name"), "set_script_class_name", "get_script_class_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "script_class_icon_path", PROPERTY_HINT_FILE), "set_script_class_icon_path", "get_script_class_icon_path");
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &NativeScript::_new, MethodInfo(Variant::OBJECT, "new"));
}
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 632970f8c0..4cefdbd7cb 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 48c1760662..159085df34 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -469,8 +469,15 @@ bool GDScript::_update_exports() {
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
E->get()->set_build_failed(true);
}
+ return false;
}
} else {
+ if (!valid) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
+ return false;
+ }
}
if (base_cache.is_valid()) {
@@ -2065,12 +2072,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 +2088,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_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 310c4e21f2..45319c59e7 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1603,13 +1603,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
return OK;
}
-Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
+Error GDScriptCompiler::_parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
Vector<int> bytecode;
CodeGen codegen;
codegen.class_node = p_class;
- codegen.script = p_script;
+ codegen.script = p_script.ptr();
codegen.function_node = p_func;
codegen.stack_max = 0;
codegen.current_line = 0;
@@ -1853,7 +1853,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
if (p_class->owner && p_class->owner->owner) {
// Owner is not root
@@ -1887,7 +1887,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->initializer = NULL;
p_script->subclasses.clear();
- p_script->_owner = p_owner;
+ p_script->_owner = p_owner.ptr();
p_script->tool = p_class->tool;
p_script->name = p_class->name;
@@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
StringName name = p_class->_signals[i].name;
- GDScript *c = p_script;
+ GDScript *c = p_script.ptr();
while (c) {
@@ -2054,7 +2054,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
return OK;
}
-Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
bool has_initializer = false;
@@ -2159,7 +2159,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
return OK;
}
-void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::_make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
@@ -2178,20 +2178,20 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
subclass.instance();
}
- subclass->_owner = const_cast<GDScript *>(p_script);
+ subclass->_owner = const_cast<GDScript *>(p_script.ptr());
class_map.insert(name, subclass);
_make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
}
}
-Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
+Error GDScriptCompiler::compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
error = "";
parser = p_parser;
- main_script = p_script;
+ main_script = p_script.ptr();
const GDScriptParser::Node *root = parser->get_parse_tree();
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 55f5e2b48e..23c6450aa7 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -148,17 +148,17 @@ class GDScriptCompiler {
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
- Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
- Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
+ Error _parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ void _make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;
String error;
public:
- Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false);
+ Error compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state = false);
String get_error() const;
int get_error_line() const;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index ddd9e6b01c..068e8d2d92 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1308,37 +1308,38 @@ static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, c
return false;
}
- // Check ClassDB
- if (ClassDB::class_exists(p_identifier)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ for (int i = 0; i < 2; i++) {
+ StringName target_id;
+ switch (i) {
+ case 0:
+ // Check ClassDB
+ target_id = p_identifier;
+ break;
+ case 1:
+ // ClassDB again for underscore-prefixed classes
+ target_id = String("_") + p_identifier;
+ break;
}
- return true;
- }
- // ClassDB again for underscore-prefixed classes
- StringName under_id = String("_") + p_identifier;
- if (ClassDB::class_exists(under_id)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = p_identifier;
- if (Engine::get_singleton()->has_singleton(p_identifier)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
- } else {
- r_type.type.is_meta_type = true;
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ if (ClassDB::class_exists(target_id)) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = target_id;
+ if (Engine::get_singleton()->has_singleton(target_id)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(target_id);
+ } else {
+ r_type.type.is_meta_type = true;
+ const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id);
+ // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript
+ if (!target_elem) {
+ return false;
+ }
+ int idx = target_elem->get();
+ r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ }
+ return true;
}
- return true;
}
// Check autoload singletons
@@ -1999,7 +2000,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/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index f09ff224e8..31115a4bd9 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -275,7 +275,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
uint32_t alloca_size = 0;
- GDScript *_class;
+ GDScript *script;
int ip = 0;
int line = _initial_line;
@@ -286,7 +286,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
line = p_state->line;
ip = p_state->ip;
alloca_size = p_state->stack.size();
- _class = p_state->_class;
+ script = p_state->script.ptr();
p_instance = p_state->instance;
defarg = p_state->defarg;
self = p_state->self;
@@ -368,9 +368,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else {
self = p_instance->owner;
}
- _class = p_instance->script.ptr();
+ script = p_instance->script.ptr();
} else {
- _class = _script;
+ script = this->_script.ptr();
}
}
@@ -395,7 +395,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text); \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text); \
if (unlikely(!m_v)) \
OPCODE_BREAK;
@@ -404,7 +404,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space)
#define GET_VARIANT_PTR(m_v, m_code_ofs) \
Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, _class, self, stack, err_text);
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, stack, err_text);
#endif
@@ -1185,7 +1185,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(dst, argc + 3);
- const GDScript *gds = _script;
+ const GDScript *gds = _script.ptr();
const Map<StringName, GDScriptFunction *>::Element *E = NULL;
while (gds->base.ptr()) {
@@ -1256,11 +1256,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
gdfs->state.alloca_size = alloca_size;
- gdfs->state._class = _class;
+ gdfs->state.script = _script;
gdfs->state.ip = ip + ipofs;
gdfs->state.line = line;
gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0;
- gdfs->state.script_id = _class->get_instance_id();
//gdfs->state.result_pos=ip+ipofs-1;
gdfs->state.defarg = defarg;
gdfs->state.instance = p_instance;
@@ -1549,8 +1548,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
String err_file;
if (p_instance)
err_file = p_instance->script->path;
- else if (_class)
- err_file = _class->path;
+ else if (script)
+ err_file = script->path;
if (err_file == "")
err_file = "<built-in>";
String err_func = name;
@@ -1765,15 +1764,20 @@ GDScriptFunction::~GDScriptFunction() {
Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
#ifdef DEBUG_ENABLED
- if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
- ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ // Checking this first since it's faster
+ if (!state.script.is_valid()) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
ERR_FAIL_V(Variant());
}
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
}
+#else
+ if (!is_valid()) {
+ return Variant();
+ }
#endif
Variant arg;
@@ -1841,12 +1845,12 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
return false;
if (p_extended_check) {
+ //script gone? (checking this first since it's faster)
+ if (!state.script.is_valid())
+ return false;
//class instance gone?
if (state.instance_id && !ObjectDB::get_instance(state.instance_id))
return false;
- //script gone?
- if (state.script_id && !ObjectDB::get_instance(state.script_id))
- return false;
}
return true;
@@ -1856,13 +1860,14 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
ERR_FAIL_COND_V(!function, Variant());
#ifdef DEBUG_ENABLED
- if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
- ERR_EXPLAIN("Resumed after yield, but class instance is gone");
+ // Checking this first since it's faster
+ if (!state.script.is_valid()) {
+ ERR_EXPLAIN("Resumed after yield, but script is gone");
ERR_FAIL_V(Variant());
}
- if (state.script_id && !ObjectDB::get_instance(state.script_id)) {
- ERR_EXPLAIN("Resumed after yield, but script is gone");
+ if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) {
+ ERR_EXPLAIN("Resumed after yield, but class instance is gone");
ERR_FAIL_V(Variant());
}
#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index bfb6d673f1..a47070de4f 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -225,7 +225,7 @@ private:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
- GDScript *_script;
+ Ref<GDScript> _script;
StringName name;
Vector<Variant> constants;
@@ -272,15 +272,13 @@ private:
public:
struct CallState {
- ObjectID instance_id; //by debug only
- ObjectID script_id;
-
+ ObjectID instance_id;
GDScriptInstance *instance;
Vector<uint8_t> stack;
int stack_size;
Variant self;
uint32_t alloca_size;
- GDScript *_class;
+ Ref<GDScript> script;
int ip;
int line;
int defarg;
@@ -299,7 +297,7 @@ public:
int get_default_argument_addr(int p_idx) const;
GDScriptDataType get_return_type() const;
GDScriptDataType get_argument_type(int p_idx) const;
- GDScript *get_script() const { return _script; }
+ GDScript *get_script() const { return const_cast<GDScript *>(_script.ptr()); }
StringName get_source() const { return source; }
void debug_get_stack_member_state(int p_line, List<Pair<StringName, int> > *r_stackvars) const;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 5af9bbc05f..2f31d59c46 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -106,6 +106,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"printerr",
"printraw",
"print_debug",
+ "push_error",
+ "push_warning",
"var2str",
"str2var",
"var2bytes",
@@ -707,13 +709,40 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
ScriptLanguage *script = GDScriptLanguage::get_singleton();
if (script->debug_get_stack_level_count() > 0) {
- str += "\n\t";
- str += "At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)); // + " in function '" + script->debug_get_stack_level_function(0) + "'";
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
}
print_line(str);
r_ret = Variant();
} break;
+ case PUSH_ERROR: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ ERR_PRINTS(message);
+ r_ret = Variant();
+ } break;
+ case PUSH_WARNING: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ r_ret = Variant();
+ break;
+ }
+
+ String message = *p_args[0];
+ WARN_PRINTS(message);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -1754,11 +1783,25 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case PUSH_ERROR: {
+
+ MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
+ case PUSH_WARNING: {
+
+ MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
+ mi.return_val.type = Variant::NIL;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
+
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
-
} break;
case STR_TO_VAR: {
@@ -1768,10 +1811,10 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
+
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi;
-
} break;
case BYTES_TO_VAR: {
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index e920dd4ece..33d5f27230 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -97,6 +97,8 @@ public:
TEXT_PRINTERR,
TEXT_PRINTRAW,
TEXT_PRINT_DEBUG,
+ PUSH_ERROR,
+ PUSH_WARNING,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
@@ -117,7 +119,6 @@ public:
LEN,
IS_INSTANCE_VALID,
FUNC_MAX
-
};
static const char *get_func_name(Function p_func);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 5f2655eb92..09b3a5631f 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -4353,6 +4353,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.type = Variant::INT;
current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
+ current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
Dictionary enum_values = constant;
List<Variant> keys;
@@ -4839,6 +4840,21 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
if (tokenizer->is_token_literal(0, true)) {
enum_name = tokenizer->get_token_literal();
+
+ if (current_class->constant_expressions.has(enum_name)) {
+ _set_error("A constant named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[enum_name].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == enum_name) {
+ _set_error("A variable named '" + String(enum_name) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) {
@@ -4865,26 +4881,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
} else { // tokenizer->is_token_literal(0, true)
- ClassNode::Constant constant;
-
StringName const_id = tokenizer->get_token_literal();
- if (current_class->constant_expressions.has(const_id)) {
- _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " +
- itos(current_class->constant_expressions[const_id].expression->line) + ").");
- return;
- }
-
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == const_id) {
- _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
-
tokenizer->advance();
+ ConstantNode *enum_value_expr;
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
tokenizer->advance();
@@ -4901,23 +4903,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
+ enum_value_expr = static_cast<ConstantNode *>(subexpr);
- if (subexpr_const->value.get_type() != Variant::INT) {
+ if (enum_value_expr->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum");
return;
}
- last_assign = subexpr_const->value;
-
- constant.expression = subexpr_const;
+ last_assign = enum_value_expr->value;
} else {
last_assign = last_assign + 1;
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = last_assign;
- cn->datatype = _type_from_variant(cn->value);
- constant.expression = cn;
+ enum_value_expr = alloc_node<ConstantNode>();
+ enum_value_expr->value = last_assign;
+ enum_value_expr->datatype = _type_from_variant(enum_value_expr->value);
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
@@ -4925,14 +4924,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
if (enum_name != "") {
- const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression);
- enum_dict[const_id] = cn->value;
- }
+ enum_dict[const_id] = enum_value_expr->value;
+ } else {
+ if (current_class->constant_expressions.has(const_id)) {
+ _set_error("A constant named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->constant_expressions[const_id].expression->line) + ").");
+ return;
+ }
- constant.type.has_type = true;
- constant.type.kind = DataType::BUILTIN;
- constant.type.builtin_type = Variant::INT;
- p_class->constant_expressions.insert(const_id, constant);
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == const_id) {
+ _set_error("A variable named '" + String(const_id) + "' already exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
+ ClassNode::Constant constant;
+ constant.type.has_type = true;
+ constant.type.kind = DataType::BUILTIN;
+ constant.type.builtin_type = Variant::INT;
+ constant.expression = enum_value_expr;
+ p_class->constant_expressions.insert(const_id, constant);
+ }
}
}
@@ -6709,9 +6723,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
}
}
+ bool rets = false;
return_type.has_type = true;
return_type.kind = DataType::BUILTIN;
- return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name);
+ return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets);
+ // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type.
+ // At least make sure we know that it returns
+ if (rets && return_type.builtin_type == Variant::NIL) {
+ return_type.has_type = false;
+ }
break;
}
@@ -7074,7 +7094,6 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
DataType member_type;
for (int i = 0; i < current_class->variables.size(); i++) {
- ClassNode::Member m = current_class->variables[i];
if (current_class->variables[i].identifier == p_identifier) {
member_type = current_class->variables[i].data_type;
current_class->variables.write[i].usages += 1;
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index f13479940d..9b9088dd82 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -10,7 +10,7 @@
A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html</link>
+ <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
</tutorials>
<demos>
</demos>
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index a8fdf8cf1f..274a2f0249 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -892,7 +892,7 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1));
#ifndef DISABLE_DEPRECATED
- ADD_PROPERTYNO(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary", 0), "set_theme", "get_theme");
#endif // DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library");
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index a49137ae73..24d40111cf 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -48,9 +48,9 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
const int image_width = decoder.get_width();
const int image_height = decoder.get_height();
- int comps = decoder.get_num_components();
- if (comps == 3)
- comps = 4; //weird
+ const int comps = decoder.get_num_components();
+ if (comps != 1 && comps != 3)
+ return ERR_FILE_CORRUPT;
if (decoder.begin_decoding() != jpgd::JPGD_SUCCESS)
return ERR_FILE_CORRUPT;
@@ -73,7 +73,19 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
}
jpgd::uint8 *pDst = pImage_data + y * dst_bpl;
- memcpy(pDst, pScan_line, dst_bpl);
+
+ if (comps == 1) {
+ memcpy(pDst, pScan_line, dst_bpl);
+ } else {
+ // For images with more than 1 channel pScan_line will always point to a buffer
+ // containing 32-bit RGBA pixels. Alpha is always 255 and we ignore it.
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x * 4 + 0];
+ pDst[1] = pScan_line[x * 4 + 1];
+ pDst[2] = pScan_line[x * 4 + 2];
+ pDst += 3;
+ }
+ }
}
//all good
@@ -82,7 +94,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
if (comps == 1)
fmt = Image::FORMAT_L8;
else
- fmt = Image::FORMAT_RGBA8;
+ fmt = Image::FORMAT_RGB8;
dw = PoolVector<uint8_t>::Write();
p_image->create(image_width, image_height, 0, fmt, data);
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/config.py b/modules/mono/config.py
index 8427103ee7..189699cca8 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -265,11 +265,7 @@ def make_template_dir(env, mono_root):
template_dir_name = ''
- if platform == 'windows':
- template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
- elif platform == 'osx':
- template_dir_name = 'data.mono.%s.%s' % (platform, target)
- elif platform == 'x11':
+ if platform in ['windows', 'osx', 'x11']:
template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target)
else:
assert False
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 5160d42367..700e518cfc 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
@@ -2718,7 +2770,8 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
"Compile",
ProjectSettings::get_singleton()->globalize_path(p_path));
} else {
- ERR_PRINTS("Cannot add " + p_path + " to the C# project because it could not be created.");
+ ERR_PRINTS("Failed to create C# project");
+ ERR_PRINTS("Cannot add " + p_path + " to the C# project");
}
}
#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..2ce7837a27 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -6,18 +6,24 @@ namespace GodotSharpTools.Project
{
public static class ProjectGenerator
{
+ public const string CoreApiProjectName = "GodotSharp";
+ public const string EditorApiProjectName = "GodotSharpEditor";
+ 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");
+ string path = Path.Combine(dir, CoreApiProjectName + ".csproj");
ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(CoreApiProject, out mainGroup);
+ var root = CreateLibraryProject(CoreApiProjectName, out mainGroup);
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 + "\")]" },
+ GenAssemblyInfoFile(root, dir, CoreApiProjectName,
+ new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" },
new string[] { "System.Runtime.CompilerServices" });
foreach (var item in compileItems)
@@ -27,33 +33,33 @@ namespace GodotSharpTools.Project
root.Save(path);
- return root.GetGuid().ToString().ToUpper();
+ return CoreApiProjectGuid;
}
- public static string GenEditorApiProject(string dir, string coreApiHintPath, string[] compileItems)
+ public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems)
{
- string path = Path.Combine(dir, EditorApiProject + ".csproj");
+ string path = Path.Combine(dir, EditorApiProjectName + ".csproj");
ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(EditorApiProject, out mainGroup);
+ var root = CreateLibraryProject(EditorApiProjectName, out mainGroup);
mainGroup.AddProperty("DocumentationFile", Path.Combine("$(OutputPath)", "$(AssemblyName).xml"));
mainGroup.SetProperty("RootNamespace", "Godot");
+ mainGroup.SetProperty("ProjectGuid", EditorApiProjectGuid);
- GenAssemblyInfoFile(root, dir, EditorApiProject);
+ GenAssemblyInfoFile(root, dir, EditorApiProjectName);
foreach (var item in compileItems)
{
root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
}
- var coreApiRef = root.AddItem("Reference", CoreApiProject);
- coreApiRef.AddMetadata("HintPath", coreApiHintPath);
+ var coreApiRef = root.AddItem("ProjectReference", coreApiProjPath.Replace("/", "\\"));
coreApiRef.AddMetadata("Private", "False");
root.Save(path);
- return root.GetGuid().ToString().ToUpper();
+ return EditorApiProjectGuid;
}
public static string GenGameProject(string dir, string name, string[] compileItems)
@@ -77,13 +83,13 @@ namespace GodotSharpTools.Project
toolsGroup.AddProperty("WarningLevel", "4");
toolsGroup.AddProperty("ConsolePause", "false");
- var coreApiRef = root.AddItem("Reference", CoreApiProject);
- coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProject + ".dll"));
+ var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
+ coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll"));
coreApiRef.AddMetadata("Private", "False");
- var editorApiRef = root.AddItem("Reference", EditorApiProject);
+ var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
- editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProject + ".dll"));
+ editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll"));
editorApiRef.AddMetadata("Private", "False");
GenAssemblyInfoFile(root, dir, name);
@@ -182,9 +188,6 @@ namespace GodotSharpTools.Project
}
}
- public const string CoreApiProject = "GodotSharp";
- public const string EditorApiProject = "GodotSharpEditor";
-
private const string assemblyInfoTemplate =
@"using System.Reflection;{0}
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/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 710682d3aa..166b3e1324 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -47,7 +47,6 @@
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
#include "csharp_project.h"
-#include "net_solution.h"
#define CS_INDENT " " // 4 whitespaces
@@ -401,32 +400,29 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
p_output.push_back(CLOSE_BLOCK); // end of namespace
}
-Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bool p_verbose_output) {
+Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
+ String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
+
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
- if (!DirAccess::exists(p_output_dir)) {
- Error err = da->make_dir_recursive(p_output_dir);
+ if (!DirAccess::exists(proj_dir)) {
+ Error err = da->make_dir_recursive(proj_dir);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
- da->change_dir(p_output_dir);
+ da->change_dir(proj_dir);
da->make_dir("Core");
da->make_dir("ObjectType");
- String core_dir = path_join(p_output_dir, "Core");
- String obj_type_dir = path_join(p_output_dir, "ObjectType");
+ String core_dir = path_join(proj_dir, "Core");
+ String obj_type_dir = path_join(proj_dir, "ObjectType");
Vector<String> compile_items;
- NETSolution solution(API_ASSEMBLY_NAME);
-
- if (!solution.set_path(p_output_dir))
- return ERR_FILE_NOT_FOUND;
-
// Generate source file for global scope constants and enums
{
List<String> constants_source;
@@ -530,15 +526,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
compile_items.push_back(internal_methods_file);
- String guid = CSharpProject::generate_core_api_project(p_output_dir, compile_items);
+ String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items);
- solution.add_new_project(API_ASSEMBLY_NAME, guid);
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj");
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
- Error sln_error = solution.save();
- if (sln_error != OK) {
- ERR_PRINT("Could not to save .NET solution.");
- return sln_error;
- }
+ r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
if (verbose_output)
OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n");
@@ -546,32 +542,29 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
return OK;
}
-Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output) {
+Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) {
verbose_output = p_verbose_output;
+ String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
+
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
- if (!DirAccess::exists(p_output_dir)) {
- Error err = da->make_dir_recursive(p_output_dir);
+ if (!DirAccess::exists(proj_dir)) {
+ Error err = da->make_dir_recursive(proj_dir);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
}
- da->change_dir(p_output_dir);
+ da->change_dir(proj_dir);
da->make_dir("Core");
da->make_dir("ObjectType");
- String core_dir = path_join(p_output_dir, "Core");
- String obj_type_dir = path_join(p_output_dir, "ObjectType");
+ String core_dir = path_join(proj_dir, "Core");
+ String obj_type_dir = path_join(proj_dir, "ObjectType");
Vector<String> compile_items;
- NETSolution solution(EDITOR_API_ASSEMBLY_NAME);
-
- if (!solution.set_path(p_output_dir))
- return ERR_FILE_NOT_FOUND;
-
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
const TypeInterface &itype = E.get();
@@ -632,19 +625,57 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
compile_items.push_back(internal_methods_file);
- String guid = CSharpProject::generate_editor_api_project(p_output_dir, p_core_dll_path, compile_items);
+ String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items);
+
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj");
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
+
+ r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
+
+ if (verbose_output)
+ OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
+
+ return OK;
+}
- solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, guid);
+Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) {
+
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
+
+ if (!DirAccess::exists(p_output_dir)) {
+ Error err = da->make_dir_recursive(p_output_dir);
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ }
+
+ DotNetSolution solution(API_SOLUTION_NAME);
+
+ if (!solution.set_path(p_output_dir))
+ return ERR_FILE_NOT_FOUND;
+
+ Error proj_err;
+
+ proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output);
+ if (proj_err != OK) {
+ ERR_PRINT("Generation of the Core API C# project failed");
+ return proj_err;
+ }
+
+ proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output);
+ if (proj_err != OK) {
+ ERR_PRINT("Generation of the Editor API C# project failed");
+ return proj_err;
+ }
Error sln_error = solution.save();
if (sln_error != OK) {
- ERR_PRINT("Could not to save .NET solution.");
+ ERR_PRINT("Failed to save API solution");
return sln_error;
}
- if (verbose_output)
- OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n");
-
return OK;
}
@@ -2368,12 +2399,11 @@ void BindingsGenerator::initialize() {
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- const int NUM_OPTIONS = 3;
+ const int NUM_OPTIONS = 2;
int options_left = NUM_OPTIONS;
String mono_glue_option = "--generate-mono-glue";
- String cs_core_api_option = "--generate-cs-core-api";
- String cs_editor_api_option = "--generate-cs-editor-api";
+ String cs_api_option = "--generate-cs-api";
verbose_output = true;
@@ -2387,42 +2417,24 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
if (path_elem) {
if (get_singleton()->generate_glue(path_elem->get()) != OK)
- ERR_PRINT("Mono glue generation failed");
+ ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
elem = elem->next();
} else {
- ERR_PRINTS("--generate-mono-glue: No output directory specified");
+ ERR_PRINTS(mono_glue_option + ": No output directory specified");
}
--options_left;
- } else if (elem->get() == cs_core_api_option) {
+ } else if (elem->get() == cs_api_option) {
const List<String>::Element *path_elem = elem->next();
if (path_elem) {
- if (get_singleton()->generate_cs_core_project(path_elem->get()) != OK)
- ERR_PRINT("Generation of solution and C# project for the Core API failed");
+ if (get_singleton()->generate_cs_api(path_elem->get()) != OK)
+ ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
elem = elem->next();
} else {
- ERR_PRINTS(cs_core_api_option + ": No output directory specified");
- }
-
- --options_left;
-
- } else if (elem->get() == cs_editor_api_option) {
-
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- if (path_elem->next()) {
- if (get_singleton()->generate_cs_editor_project(path_elem->get(), path_elem->next()->get()) != OK)
- ERR_PRINT("Generation of solution and C# project for the Editor API failed");
- elem = path_elem->next();
- } else {
- ERR_PRINTS(cs_editor_api_option + ": No hint path for the Core API dll specified");
- }
- } else {
- ERR_PRINTS(cs_editor_api_option + ": No output directory specified");
+ ERR_PRINTS(cs_api_option + ": No output directory specified");
}
--options_left;
@@ -2434,7 +2446,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
verbose_output = false;
if (options_left != NUM_OPTIONS)
- exit(0);
+ ::exit(0);
}
#endif
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 38cf99c294..91c474c4f0 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -32,6 +32,7 @@
#define BINDINGS_GENERATOR_H
#include "core/class_db.h"
+#include "dotnet_solution.h"
#include "editor/doc/doc_data.h"
#include "editor/editor_help.h"
@@ -556,8 +557,9 @@ class BindingsGenerator {
static BindingsGenerator *singleton;
public:
- Error generate_cs_core_project(const String &p_output_dir, bool p_verbose_output = true);
- Error generate_cs_editor_project(const String &p_output_dir, const String &p_core_dll_path, bool p_verbose_output = true);
+ Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
+ Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true);
+ Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true);
Error generate_glue(const String &p_output_dir);
static uint32_t get_version();
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index 6f223b75a3..416108603b 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -30,11 +30,17 @@
#include "csharp_project.h"
+#include "core/io/json.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.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 {
@@ -58,16 +64,16 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
}
-String generate_editor_api_project(const String &p_dir, const String &p_core_dll_path, const Vector<String> &p_files) {
+String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) {
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
Variant dir = p_dir;
- Variant core_dll_path = p_core_dll_path;
+ Variant core_proj_path = p_core_proj_path;
Variant compile_items = p_files;
- const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
+ const Variant *args[3] = { &dir, &core_proj_path, &compile_items };
MonoException *exc = NULL;
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
@@ -102,6 +108,9 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
+ if (!GLOBAL_DEF("mono/project/auto_update_project", true))
+ return;
+
_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
@@ -118,4 +127,118 @@ 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)
+
+ if (FileAccess::exists(p_output_path)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ Error rm_err = da->remove(p_output_path);
+
+ ERR_EXPLAIN("Failed to remove old scripts metadata file");
+ ERR_FAIL_COND_V(rm_err != OK, rm_err);
+ }
+
+ GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
+
+ void *args[2] = {
+ GDMonoMarshal::mono_string_from_godot(p_project_path),
+ GDMonoMarshal::mono_string_from_godot("Compile")
+ };
+
+ MonoException *exc = NULL;
+ MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc);
+
+ if (exc) {
+ GDMonoUtils::debug_print_unhandled_exception(exc);
+ ERR_FAIL_V(FAILED);
+ }
+
+ PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
+ PoolStringArray::Read r = project_files.read();
+
+ Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata();
+ Dictionary new_dict;
+
+ for (int i = 0; i < project_files.size(); i++) {
+ const String &project_file = ("res://" + r[i]).simplify_path();
+
+ uint64_t modified_time = FileAccess::get_modified_time(project_file);
+
+ const Variant *old_file_var = old_dict.getptr(project_file);
+ if (old_file_var) {
+ Dictionary old_file_dict = old_file_var->operator Dictionary();
+
+ if (old_file_dict["modified_time"].operator uint64_t() == modified_time) {
+ // No changes so no need to parse again
+ new_dict[project_file] = old_file_dict;
+ continue;
+ }
+ }
+
+ ScriptClassParser scp;
+ Error err = scp.parse_file(project_file);
+ if (err != OK) {
+ ERR_PRINTS("Parse error: " + scp.get_error());
+ ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file);
+ ERR_FAIL_V(err);
+ }
+
+ Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes();
+
+ bool found = false;
+ Dictionary class_dict;
+
+ String search_name = project_file.get_file().get_basename();
+
+ for (int j = 0; j < classes.size(); j++) {
+ const ScriptClassParser::ClassDecl &class_decl = classes[j];
+
+ if (class_decl.base.size() == 0)
+ continue; // Does not inherit nor implement anything, so it can't be a script class
+
+ String class_cmp;
+
+ if (class_decl.nested) {
+ class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1);
+ } else {
+ class_cmp = class_decl.name;
+ }
+
+ if (class_cmp != search_name)
+ continue;
+
+ class_dict["namespace"] = class_decl.namespace_;
+ class_dict["class_name"] = class_decl.name;
+ class_dict["nested"] = class_decl.nested;
+
+ found = true;
+ break;
+ }
+
+ if (found) {
+ Dictionary file_dict;
+ file_dict["modified_time"] = modified_time;
+ file_dict["class"] = class_dict;
+ new_dict[project_file] = file_dict;
+ }
+ }
+
+ if (new_dict.size()) {
+ String json = JSON::print(new_dict, "", false);
+
+ Error ferr;
+ FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr);
+ ERR_EXPLAIN("Cannot open file for writing: " + p_output_path);
+ ERR_FAIL_COND_V(ferr != OK, ferr);
+ f->store_string(json);
+ f->flush();
+ f->close();
+ memdelete(f);
+ }
+
+ return OK;
+}
+
} // namespace CSharpProject
diff --git a/modules/mono/editor/csharp_project.h b/modules/mono/editor/csharp_project.h
index d852139de0..aeeeff50f0 100644
--- a/modules/mono/editor/csharp_project.h
+++ b/modules/mono/editor/csharp_project.h
@@ -40,6 +40,9 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>());
void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
+
+Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path);
+
} // namespace CSharpProject
#endif // CSHARP_PROJECT_H
diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/dotnet_solution.cpp
index 8bbd376c9a..ab92e2e378 100644
--- a/modules/mono/editor/net_solution.cpp
+++ b/modules/mono/editor/dotnet_solution.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* net_solution.cpp */
+/* dotnet_solution.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "net_solution.h"
+#include "dotnet_solution.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
@@ -52,33 +52,32 @@
#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" \
"\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU"
-void NETSolution::add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs) {
- if (projects.has(p_name))
- WARN_PRINT("Overriding existing project.");
-
- ProjectInfo procinfo;
- procinfo.guid = p_guid;
+void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) {
+ projects[p_name] = p_project_info;
+}
- procinfo.configs.push_back("Debug");
- procinfo.configs.push_back("Release");
+bool DotNetSolution::has_project(const String &p_name) const {
+ return projects.find(p_name) != NULL;
+}
- for (int i = 0; i < p_extra_configs.size(); i++) {
- procinfo.configs.push_back(p_extra_configs[i]);
- }
+const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const {
+ return projects[p_name];
+}
- projects[p_name] = procinfo;
+bool DotNetSolution::remove_project(const String &p_name) {
+ return projects.erase(p_name);
}
-Error NETSolution::save() {
+Error DotNetSolution::save() {
bool dir_exists = DirAccess::exists(path);
ERR_EXPLAIN("The directory does not exist.");
- ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH);
+ ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND);
String projs_decl;
String sln_platform_cfg;
@@ -86,34 +85,40 @@ Error NETSolution::save() {
for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) {
const String &name = E->key();
- const ProjectInfo &procinfo = E->value();
+ const ProjectInfo &proj_info = E->value();
- projs_decl += sformat(PROJECT_DECLARATION, name, name + ".csproj", procinfo.guid);
+ bool is_front = E == projects.front();
- for (int i = 0; i < procinfo.configs.size(); i++) {
- const String &config = procinfo.configs[i];
+ if (!is_front)
+ projs_decl += "\n";
- if (i != 0) {
+ projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid);
+
+ for (int i = 0; i < proj_info.configs.size(); i++) {
+ const String &config = proj_info.configs[i];
+
+ if (i != 0 || !is_front) {
sln_platform_cfg += "\n";
proj_platform_cfg += "\n";
}
sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config);
- proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, procinfo.guid, config);
+ proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config);
}
}
String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg);
- FileAccessRef file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
- ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
+ FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
+ ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE);
file->store_string(content);
file->close();
+ memdelete(file);
return OK;
}
-bool NETSolution::set_path(const String &p_existing_path) {
+bool DotNetSolution::set_path(const String &p_existing_path) {
if (p_existing_path.is_abs_path()) {
path = p_existing_path;
} else {
@@ -126,6 +131,10 @@ bool NETSolution::set_path(const String &p_existing_path) {
return true;
}
-NETSolution::NETSolution(const String &p_name) {
+String DotNetSolution::get_path() {
+ return path;
+}
+
+DotNetSolution::DotNetSolution(const String &p_name) {
name = p_name;
}
diff --git a/modules/mono/editor/net_solution.h b/modules/mono/editor/dotnet_solution.h
index bdff24af0b..629605ad18 100644
--- a/modules/mono/editor/net_solution.h
+++ b/modules/mono/editor/dotnet_solution.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* net_solution.h */
+/* dotnet_solution.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,23 +34,28 @@
#include "core/map.h"
#include "core/ustring.h"
-struct NETSolution {
+struct DotNetSolution {
String name;
- void add_new_project(const String &p_name, const String &p_guid, const Vector<String> &p_extra_configs = Vector<String>());
+ struct ProjectInfo {
+ String guid;
+ String relpath; // Must be relative to the solution directory
+ Vector<String> configs;
+ };
+
+ void add_new_project(const String &p_name, const ProjectInfo &p_project_info);
+ bool has_project(const String &p_name) const;
+ const ProjectInfo &get_project_info(const String &p_name) const;
+ bool remove_project(const String &p_name);
Error save();
bool set_path(const String &p_existing_path);
+ String get_path();
- NETSolution(const String &p_name);
+ DotNetSolution(const String &p_name);
private:
- struct ProjectInfo {
- String guid;
- Vector<String> configs;
- };
-
String path;
Map<String, ProjectInfo> projects;
};
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index b504cfe712..0f12fc5243 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)"
@@ -226,20 +227,24 @@ void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
MonoBottomPanel::get_singleton()->show_build_tab();
}
-bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config) {
+bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) {
- String api_sln_file = p_api_sln_dir.plus_file(p_name + ".sln");
- String api_assembly_dir = p_api_sln_dir.plus_file("bin").plus_file(p_config);
- String api_assembly_file = api_assembly_dir.plus_file(p_name + ".dll");
+ String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
- if (!FileAccess::exists(api_assembly_file)) {
+ String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
+ String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+
+ String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
+ String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) {
MonoBuildInfo api_build_info(api_sln_file, p_config);
// TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
// once we start to actively document manually maintained C# classes
api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
- show_build_error_dialog("Failed to build " + p_name + " solution.");
+ show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution.");
return false;
}
}
@@ -268,7 +273,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 +285,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;
@@ -303,9 +306,9 @@ String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
"_" + String::num_uint64(CS_GLUE_VERSION);
}
-bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
+bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
- String api_name = p_api_type == APIAssembly::API_CORE ? API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+ String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
@@ -318,55 +321,35 @@ bool GodotSharpBuilds::make_api_sln(APIAssembly::Type p_api_type) {
String api_build_config = "Release";
- EditorProgress pr("mono_build_release_" + api_name, "Building " + api_name + " solution...", 3);
+ EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3);
- pr.step("Generating " + api_name + " solution", 0);
+ pr.step("Generating " API_SOLUTION_NAME " solution", 0);
- String core_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
- .plus_file(_api_folder_name(APIAssembly::API_CORE))
- .plus_file(API_ASSEMBLY_NAME);
- String editor_api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
- .plus_file(_api_folder_name(APIAssembly::API_EDITOR))
- .plus_file(EDITOR_API_ASSEMBLY_NAME);
+ String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
+ .plus_file(_api_folder_name(APIAssembly::API_CORE));
- String api_sln_dir = p_api_type == APIAssembly::API_CORE ? core_api_sln_dir : editor_api_sln_dir;
- String api_sln_file = api_sln_dir.plus_file(api_name + ".sln");
+ String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
- String core_api_assembly;
-
- if (p_api_type == APIAssembly::API_EDITOR) {
- core_api_assembly = core_api_sln_dir.plus_file("bin")
- .plus_file(api_build_config)
- .plus_file(API_ASSEMBLY_NAME ".dll");
- }
-
-#ifndef DEBUG_METHODS_ENABLED
-#error "How am I supposed to generate the bindings?"
-#endif
-
BindingsGenerator *gen = BindingsGenerator::get_singleton();
bool gen_verbose = OS::get_singleton()->is_stdout_verbose();
- Error err = p_api_type == APIAssembly::API_CORE ?
- gen->generate_cs_core_project(api_sln_dir, gen_verbose) :
- gen->generate_cs_editor_project(api_sln_dir, core_api_assembly, gen_verbose);
-
+ Error err = gen->generate_cs_api(api_sln_dir, gen_verbose);
if (err != OK) {
- show_build_error_dialog("Failed to generate " + api_name + " solution. Error: " + itos(err));
+ show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
return false;
}
}
- pr.step("Building " + api_name + " solution", 1);
+ pr.step("Building " API_SOLUTION_NAME " solution", 1);
- if (!GodotSharpBuilds::build_api_sln(api_name, api_sln_dir, api_build_config))
+ if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config))
return false;
pr.step("Copying " + api_name + " assembly", 2);
// Copy the built assembly to the assemblies directory
- String api_assembly_dir = api_sln_dir.plus_file("bin").plus_file(api_build_config);
+ String api_assembly_dir = api_sln_dir.plus_file(api_name).plus_file("bin").plus_file(api_build_config);
if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
return false;
@@ -378,10 +361,10 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
return false;
EditorProgress pr("mono_project_debug_build", "Building project solution...", 1);
@@ -398,6 +381,20 @@ 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);
+
+ if (FileAccess::exists(scripts_metadata_path_editor)) {
+ 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_builds.h b/modules/mono/editor/godotsharp_builds.h
index c6dc6b6236..7f38b0aa49 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -84,10 +84,10 @@ public:
bool build(const MonoBuildInfo &p_build_info);
bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
- static bool build_api_sln(const String &p_name, const String &p_api_sln_dir, const String &p_config);
+ static bool build_api_sln(const String &p_api_sln_dir, const String &p_config);
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
- static bool make_api_sln(APIAssembly::Type p_api_type);
+ static bool make_api_assembly(APIAssembly::Type p_api_type);
static bool build_project_blocking(const String &p_config);
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 9df4e10266..f27511ad5e 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -42,8 +42,8 @@
#include "../utils/path_utils.h"
#include "bindings_generator.h"
#include "csharp_project.h"
+#include "dotnet_solution.h"
#include "godotsharp_export.h"
-#include "net_solution.h"
#ifdef OSX_ENABLED
#include "../utils/osx_utils.h"
@@ -71,17 +71,21 @@ bool GodotSharpEditor::_create_project_solution() {
if (guid.length()) {
- NETSolution solution(name);
+ DotNetSolution solution(name);
if (!solution.set_path(path)) {
show_error_dialog(TTR("Failed to create solution."));
return false;
}
- Vector<String> extra_configs;
- extra_configs.push_back("Tools");
+ DotNetSolution::ProjectInfo proj_info;
+ proj_info.guid = guid;
+ proj_info.relpath = name + ".csproj";
+ proj_info.configs.push_back("Debug");
+ proj_info.configs.push_back("Release");
+ proj_info.configs.push_back("Tools");
- solution.add_new_project(name, guid, extra_configs);
+ solution.add_new_project(name, proj_info);
Error sln_error = solution.save();
@@ -90,10 +94,10 @@ bool GodotSharpEditor::_create_project_solution() {
return false;
}
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
return false;
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
return false;
pr.step(TTR("Done"));
@@ -122,15 +126,15 @@ void GodotSharpEditor::_make_api_solutions_if_needed_impl() {
// If the project has a solution and C# project make sure the API assemblies are present and up to date
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
- if (!FileAccess::exists(res_assemblies_dir.plus_file(API_ASSEMBLY_NAME ".dll")) ||
+ if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) ||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_CORE))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
return;
}
if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) ||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
- if (!GodotSharpBuilds::make_api_sln(APIAssembly::API_EDITOR))
+ if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
return; // Redundant? I don't think so
}
}
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 933ede93dc..34c710320a 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
@@ -110,7 +117,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
GDMonoAssembly *scripts_assembly = NULL;
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
- project_dll_src_dir, &scripts_assembly, /* refonly: */ true);
+ project_dll_src_path, &scripts_assembly, /* refonly: */ true);
ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name);
ERR_FAIL_COND(!load_success);
@@ -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/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
new file mode 100644
index 0000000000..bc8eed81cf
--- /dev/null
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -0,0 +1,542 @@
+#include "script_class_parser.h"
+
+#include "core/map.h"
+#include "core/os/os.h"
+
+#include "../utils/string_utils.h"
+
+const char *ScriptClassParser::token_names[ScriptClassParser::TK_MAX] = {
+ "[",
+ "]",
+ "{",
+ "}",
+ ".",
+ ":",
+ ",",
+ "Symbol",
+ "Identifier",
+ "String",
+ "Number",
+ "<",
+ ">",
+ "EOF",
+ "Error"
+};
+
+String ScriptClassParser::get_token_name(ScriptClassParser::Token p_token) {
+
+ ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>");
+ return token_names[p_token];
+}
+
+ScriptClassParser::Token ScriptClassParser::get_token() {
+
+ while (true) {
+ switch (code[idx]) {
+ case '\n': {
+ line++;
+ idx++;
+ break;
+ };
+ case 0: {
+ return TK_EOF;
+ } break;
+ case '{': {
+ idx++;
+ return TK_CURLY_BRACKET_OPEN;
+ };
+ case '}': {
+ idx++;
+ return TK_CURLY_BRACKET_CLOSE;
+ };
+ case '[': {
+ idx++;
+ return TK_BRACKET_OPEN;
+ };
+ case ']': {
+ idx++;
+ return TK_BRACKET_CLOSE;
+ };
+ case '<': {
+ idx++;
+ return TK_OP_LESS;
+ };
+ case '>': {
+ idx++;
+ return TK_OP_GREATER;
+ };
+ case ':': {
+ idx++;
+ return TK_COLON;
+ };
+ case ',': {
+ idx++;
+ return TK_COMMA;
+ };
+ case '.': {
+ idx++;
+ return TK_PERIOD;
+ };
+ case '#': {
+ //compiler directive
+ while (code[idx] != '\n' && code[idx] != 0) {
+ idx++;
+ }
+ continue;
+ } break;
+ case '/': {
+ switch (code[idx + 1]) {
+ case '*': { // block comment
+ idx += 2;
+ while (true) {
+ if (code[idx] == 0) {
+ error_str = "Unterminated comment";
+ error = true;
+ return TK_ERROR;
+ } else if (code[idx] == '*' && code[idx + 1] == '/') {
+ idx += 2;
+ break;
+ } else if (code[idx] == '\n') {
+ line++;
+ }
+
+ idx++;
+ }
+
+ } break;
+ case '/': { // line comment skip
+ while (code[idx] != '\n' && code[idx] != 0) {
+ idx++;
+ }
+
+ } break;
+ default: {
+ value = "/";
+ idx++;
+ return TK_SYMBOL;
+ }
+ }
+
+ continue; // a comment
+ } break;
+ case '\'':
+ case '"': {
+ bool verbatim = idx != 0 && code[idx - 1] == '@';
+
+ CharType begin_str = code[idx];
+ idx++;
+ String tk_string = String();
+ while (true) {
+ if (code[idx] == 0) {
+ error_str = "Unterminated String";
+ error = true;
+ return TK_ERROR;
+ } else if (code[idx] == begin_str) {
+ if (verbatim && code[idx + 1] == '"') { // `""` is verbatim string's `\"`
+ idx += 2; // skip next `"` as well
+ continue;
+ }
+
+ idx += 1;
+ break;
+ } else if (code[idx] == '\\' && !verbatim) {
+ //escaped characters...
+ idx++;
+ CharType next = code[idx];
+ if (next == 0) {
+ error_str = "Unterminated String";
+ error = true;
+ return TK_ERROR;
+ }
+ CharType res = 0;
+
+ switch (next) {
+ case 'b': res = 8; break;
+ case 't': res = 9; break;
+ case 'n': res = 10; break;
+ case 'f': res = 12; break;
+ case 'r':
+ res = 13;
+ break;
+ case '\"': res = '\"'; break;
+ case '\\':
+ res = '\\';
+ break;
+ default: {
+ res = next;
+ } break;
+ }
+
+ tk_string += res;
+
+ } else {
+ if (code[idx] == '\n')
+ line++;
+ tk_string += code[idx];
+ }
+ idx++;
+ }
+
+ value = tk_string;
+
+ return TK_STRING;
+ } break;
+ default: {
+ if (code[idx] <= 32) {
+ idx++;
+ break;
+ }
+
+ if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) {
+ value = String::chr(code[idx]);
+ idx++;
+ return TK_SYMBOL;
+ }
+
+ if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
+ //a number
+ const CharType *rptr;
+ double number = String::to_double(&code[idx], &rptr);
+ idx += (rptr - &code[idx]);
+ value = number;
+ return TK_NUMBER;
+
+ } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) {
+ String id;
+
+ id += code[idx];
+ idx++;
+
+ while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) {
+ id += code[idx];
+ idx++;
+ }
+
+ value = id;
+ return TK_IDENTIFIER;
+ } else if (code[idx] == '@' && code[idx + 1] == '"') {
+ // begin of verbatim string
+ idx++;
+ } else {
+ error_str = "Unexpected character.";
+ error = true;
+ return TK_ERROR;
+ }
+ }
+ }
+ }
+}
+
+Error ScriptClassParser::_skip_generic_type_params() {
+
+ Token tk;
+
+ while (true) {
+ tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ tk = get_token();
+
+ if (tk == TK_PERIOD) {
+ while (true) {
+ tk = get_token();
+
+ if (tk != TK_IDENTIFIER) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = get_token();
+
+ if (tk != TK_PERIOD)
+ break;
+ }
+ }
+
+ if (tk == TK_OP_LESS) {
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ continue;
+ } else if (tk != TK_COMMA) {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ } else if (tk == TK_OP_LESS) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found " + get_token_name(TK_OP_LESS);
+ error = true;
+ return ERR_PARSE_ERROR;
+ } else if (tk == TK_OP_GREATER) {
+ return OK;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+}
+
+Error ScriptClassParser::_parse_type_full_name(String &r_full_name) {
+
+ Token tk = get_token();
+
+ if (tk != TK_IDENTIFIER) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ r_full_name += String(value);
+
+ if (code[idx] != '.') // We only want to take the next token if it's a period
+ return OK;
+
+ tk = get_token();
+
+ CRASH_COND(tk != TK_PERIOD); // Assertion
+
+ r_full_name += ".";
+
+ return _parse_type_full_name(r_full_name);
+}
+
+Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
+
+ String name;
+
+ Error err = _parse_type_full_name(name);
+ if (err)
+ return err;
+
+ Token tk = get_token();
+
+ if (tk == TK_OP_LESS) {
+ // We don't add it to the base list if it's generic
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ } else if (tk == TK_COMMA) {
+ Error err = _parse_class_base(r_base);
+ if (err)
+ return err;
+ r_base.push_back(name);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ r_base.push_back(name);
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
+Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) {
+
+ Token tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ r_name += String(value);
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = get_token();
+
+ if (tk == TK_PERIOD) {
+ r_name += ".";
+ return _parse_namespace_name(r_name, r_curly_stack);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ r_curly_stack++;
+ return OK;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+}
+
+Error ScriptClassParser::parse(const String &p_code) {
+
+ code = p_code;
+ idx = 0;
+ line = 0;
+ error_str = String();
+ error = false;
+ value = Variant();
+ classes.clear();
+
+ Token tk = get_token();
+
+ Map<int, NameDecl> name_stack;
+ int curly_stack = 0;
+ int type_curly_stack = 0;
+
+ while (!error && tk != TK_EOF) {
+ if (tk == TK_IDENTIFIER && String(value) == "class") {
+ tk = get_token();
+
+ if (tk == TK_IDENTIFIER) {
+ String name = value;
+ int at_level = type_curly_stack;
+
+ ClassDecl class_decl;
+
+ for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) {
+ const NameDecl &name_decl = E->value();
+
+ if (name_decl.type == NameDecl::NAMESPACE_DECL) {
+ if (E != name_stack.front())
+ class_decl.namespace_ += ".";
+ class_decl.namespace_ += name_decl.name;
+ } else {
+ class_decl.name += name_decl.name + ".";
+ }
+ }
+
+ class_decl.name += name;
+ class_decl.nested = type_curly_stack > 0;
+
+ bool generic = false;
+
+ while (true) {
+ tk = get_token();
+
+ if (tk == TK_COLON) {
+ Error err = _parse_class_base(class_decl.base);
+ if (err)
+ return err;
+
+ curly_stack++;
+ type_curly_stack++;
+
+ break;
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ curly_stack++;
+ type_curly_stack++;
+ break;
+ } else if (tk == TK_OP_LESS && !generic) {
+ generic = true;
+
+ Error err = _skip_generic_type_params();
+ if (err)
+ return err;
+ } else {
+ error_str = "Unexpected token: " + get_token_name(tk);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::CLASS_DECL;
+ name_stack[at_level] = name_decl;
+
+ if (!generic) { // no generics, thanks
+ classes.push_back(class_decl);
+ } else if (OS::get_singleton()->is_stdout_verbose()) {
+ String full_name = class_decl.namespace_;
+ if (full_name.length())
+ full_name += ".";
+ full_name += class_decl.name;
+ OS::get_singleton()->print(String("Ignoring generic class declaration: " + class_decl.name).utf8());
+ }
+ }
+ } else if (tk == TK_IDENTIFIER && String(value) == "struct") {
+ String name;
+ int at_level = type_curly_stack;
+ while (true) {
+ tk = get_token();
+ if (tk == TK_IDENTIFIER && name.empty()) {
+ name = String(value);
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ if (name.empty()) {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + " after keyword `struct`, found " + get_token_name(TK_CURLY_BRACKET_OPEN);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ curly_stack++;
+ type_curly_stack++;
+ break;
+ } else if (tk == TK_EOF) {
+ error_str = "Expected " + get_token_name(TK_CURLY_BRACKET_OPEN) + " after struct decl, found " + get_token_name(TK_EOF);
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::STRUCT_DECL;
+ name_stack[at_level] = name_decl;
+ } else if (tk == TK_IDENTIFIER && String(value) == "namespace") {
+ if (type_curly_stack > 0) {
+ error_str = "Found namespace nested inside type.";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+
+ String name;
+ int at_level = curly_stack;
+
+ Error err = _parse_namespace_name(name, curly_stack);
+ if (err)
+ return err;
+
+ NameDecl name_decl;
+ name_decl.name = name;
+ name_decl.type = NameDecl::NAMESPACE_DECL;
+ name_stack[at_level] = name_decl;
+ } else if (tk == TK_CURLY_BRACKET_OPEN) {
+ curly_stack++;
+ } else if (tk == TK_CURLY_BRACKET_CLOSE) {
+ curly_stack--;
+ if (name_stack.has(curly_stack)) {
+ if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL)
+ type_curly_stack--;
+ name_stack.erase(curly_stack);
+ }
+ }
+
+ tk = get_token();
+ }
+
+ if (!error && tk == TK_EOF && curly_stack > 0) {
+ error_str = "Reached EOF with missing close curly brackets.";
+ error = true;
+ }
+
+ if (error)
+ return ERR_PARSE_ERROR;
+
+ return OK;
+}
+
+Error ScriptClassParser::parse_file(const String &p_filepath) {
+
+ String source;
+
+ Error ferr = read_all_file_utf8(p_filepath, source);
+ if (ferr != OK) {
+ if (ferr == ERR_INVALID_DATA) {
+ ERR_EXPLAIN("File '" + p_filepath + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
+ }
+ ERR_FAIL_V(ferr);
+ }
+
+ return parse(source);
+}
+
+String ScriptClassParser::get_error() {
+ return error_str;
+}
+
+Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() {
+ return classes;
+}
diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h
new file mode 100644
index 0000000000..1e174c28a9
--- /dev/null
+++ b/modules/mono/editor/script_class_parser.h
@@ -0,0 +1,79 @@
+#ifndef SCRIPT_CLASS_PARSER_H
+#define SCRIPT_CLASS_PARSER_H
+
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vector.h"
+
+class ScriptClassParser {
+
+public:
+ struct NameDecl {
+ enum Type {
+ NAMESPACE_DECL,
+ CLASS_DECL,
+ STRUCT_DECL
+ };
+
+ String name;
+ Type type;
+ };
+
+ struct ClassDecl {
+ String name;
+ String namespace_;
+ Vector<String> base;
+ bool nested;
+ bool has_script_attr;
+ };
+
+private:
+ String code;
+ int idx;
+ int line;
+ String error_str;
+ bool error;
+ Variant value;
+
+ Vector<ClassDecl> classes;
+
+ enum Token {
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_PERIOD,
+ TK_COLON,
+ TK_COMMA,
+ TK_SYMBOL,
+ TK_IDENTIFIER,
+ TK_STRING,
+ TK_NUMBER,
+ TK_OP_LESS,
+ TK_OP_GREATER,
+ TK_EOF,
+ TK_ERROR,
+ TK_MAX
+ };
+
+ static const char *token_names[TK_MAX];
+ static String get_token_name(Token p_token);
+
+ Token get_token();
+
+ Error _skip_generic_type_params();
+
+ Error _parse_type_full_name(String &r_full_name);
+ Error _parse_class_base(Vector<String> &r_base);
+ Error _parse_namespace_name(String &r_name, int &r_curly_stack);
+
+public:
+ Error parse(const String &p_code);
+ Error parse_file(const String &p_filepath);
+
+ String get_error();
+
+ Vector<ClassDecl> get_classes();
+};
+
+#endif // SCRIPT_CLASS_PARSER_H
diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs
index e4818e186c..75a35a9eea 100644
--- a/modules/mono/glue/Managed/Files/GD.cs
+++ b/modules/mono/glue/Managed/Files/GD.cs
@@ -70,6 +70,16 @@ namespace Godot
return ResourceLoader.Load<T>(path);
}
+ public static void PushError(string message)
+ {
+ godot_icall_GD_pusherror(message);
+ }
+
+ public static void PushWarning(string message)
+ {
+ godot_icall_GD_pushwarning(message);
+ }
+
public static void Print(params object[] what)
{
godot_icall_GD_print(what);
@@ -238,5 +248,11 @@ namespace Godot
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static string godot_icall_GD_var2str(object var);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_pusherror(string type);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_GD_pushwarning(string type);
}
}
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 051f42b966..9f5bcecdd9 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -157,6 +157,14 @@ bool godot_icall_GD_type_exists(MonoString *p_type) {
return ClassDB::class_exists(GDMonoMarshal::mono_string_to_godot(p_type));
}
+void godot_icall_GD_pusherror(MonoString *p_str) {
+ ERR_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str));
+}
+
+void godot_icall_GD_pushwarning(MonoString *p_str) {
+ WARN_PRINTS(GDMonoMarshal::mono_string_to_godot(p_str));
+}
+
MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var) {
Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
@@ -186,6 +194,8 @@ void godot_register_gd_icalls() {
mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert);
mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash);
mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning);
mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print);
mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr);
mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw);
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 39d608de9f..5a6a2d1742 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -36,7 +36,8 @@
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "ptr"
#define BINDINGS_NATIVE_NAME_FIELD "nativeName"
-#define API_ASSEMBLY_NAME "GodotSharp"
+#define API_SOLUTION_NAME "GodotSharp"
+#define CORE_API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 4d88cca6f1..a80155bd89 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(CORE_API_ASSEMBLY_NAME ".dll");
+
+ if (!FileAccess::exists(assembly_path))
+ return false;
+
+ bool success = load_assembly_from(CORE_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");
@@ -603,7 +635,7 @@ void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type,
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
- API_ASSEMBLY_NAME ".dll" :
+ CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
ERR_FAIL_COND(!FileAccess::exists(assembly_path));
@@ -634,7 +666,7 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type)
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
- API_ASSEMBLY_NAME ".dll" :
+ CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(assembly_path))
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/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index fe41722af0..5d9b9213e7 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -223,6 +223,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
ERR_FAIL();
}
}
+
+ break;
}
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + tclass->get_name());
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index b97a24b09c..fe2c09799c 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -631,36 +631,36 @@ void set_pending_exception(MonoException *p_exc) {
_THREAD_LOCAL_(int)
current_invoke_count = 0;
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) {
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) {
+MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) {
+MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc);
+ MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
}
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
GD_MONO_END_RUNTIME_INVOKE;
return ret;
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index ec3a57eb46..f00680ff03 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -233,13 +233,13 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
return current_invoke_count;
}
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc);
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc);
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
+MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc);
-MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc);
+MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc);
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc);
+void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
+MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error);
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/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index ea942a9a8e..e663ee3c4a 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -88,7 +88,7 @@ void fix_path(const String &p_path, String &r_out) {
bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
#ifdef WINDOWS_ENABLED
CharType ret[_MAX_PATH];
- if (_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
+ if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
String abspath = String(ret).replace("\\", "/");
int pos = abspath.find(":/");
if (pos != -1) {
@@ -99,10 +99,12 @@ bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
return true;
}
#else
- char ret[PATH_MAX];
- if (realpath(p_existing_path.utf8().get_data(), ret)) {
+ char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL);
+ if (resolved_path) {
String retstr;
- if (!retstr.parse_utf8(ret)) {
+ bool success = !retstr.parse_utf8(resolved_path);
+ ::free(resolved_path);
+ if (success) {
r_abs_path = retstr;
return true;
}
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/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index 28764aca40..70849c5a80 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- <link>http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html</link>
+ <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
</tutorials>
<demos>
</demos>
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/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 7fdd7fe446..c5f2070963 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -765,6 +765,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
}
void VisualScriptEditor::_update_members() {
+ ERR_FAIL_COND(!script.is_valid());
updating_members = true;
@@ -3018,11 +3019,15 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) {
void VisualScriptEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_READY) {
+ if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) {
+
node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons"));
node_filter->set_clear_button_enabled(true);
- variable_editor->connect("changed", this, "_update_members");
- signal_editor->connect("changed", this, "_update_members");
+
+ if (p_what == NOTIFICATION_READY) {
+ variable_editor->connect("changed", this, "_update_members");
+ signal_editor->connect("changed", this, "_update_members");
+ }
Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
@@ -3056,8 +3061,12 @@ void VisualScriptEditor::_notification(int p_what) {
node_styles[E->get().first] = frame_style;
}
}
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+
+ if (is_visible_in_tree() && script.is_valid()) {
+ _update_members();
+ _update_graph();
+ }
+ } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
left_vsplit->set_visible(is_visible_in_tree());
}
}
@@ -3644,7 +3653,7 @@ VisualScriptEditor::VisualScriptEditor() {
new_virtual_method_select = memnew(VisualScriptPropertySelector);
add_child(new_virtual_method_select);
new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method");
- new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method");
+ new_virtual_method_select->get_cancel();
member_popup = memnew(PopupMenu);
add_child(member_popup);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 97c75dae94..ce3245bc28 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -176,7 +176,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _cancel_connect_node();
void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point);
- void _selected_new_virtual_method(const String &p_text = String(""), const String &p_category = String(""), const bool p_connecting = true);
+ void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
int error_line;
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 8255ed7116..82a577790e 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -31,6 +31,7 @@
#include "emws_client.h"
#include "core/io/ip.h"
+#include "core/project_settings.h"
#include "emscripten.h"
extern "C" {
@@ -43,8 +44,9 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
- static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
- client->_on_peer_packet();
+ Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
+ if (err == OK)
+ client->_on_peer_packet();
}
EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
@@ -159,7 +161,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
/* clang-format on */
- static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock);
+ static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size);
return OK;
};
@@ -198,7 +200,13 @@ uint16_t EMWSClient::get_connected_port() const {
return 1025;
};
+int EMWSClient::get_max_packet_size() const {
+ return (1 << _in_buf_size) - PROTO_SIZE;
+}
+
EMWSClient::EMWSClient() {
+ _in_buf_size = GLOBAL_GET(WSC_IN_BUF);
+ _in_pkt_size = GLOBAL_GET(WSC_IN_PKT);
_is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
/* clang-format off */
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index b20633baff..a21090a1a3 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -41,6 +41,8 @@ class EMWSClient : public WebSocketClient {
GDCIIMPL(EMWSClient, WebSocketClient);
private:
+ int _in_buf_size;
+ int _in_pkt_size;
int _js_id;
public:
@@ -52,6 +54,7 @@ public:
IP_Address get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
+ int get_max_packet_size() const;
virtual void poll();
EMWSClient();
~EMWSClient();
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 68f41165eb..bb97934824 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -32,11 +32,11 @@
#include "emws_peer.h"
#include "core/io/ip.h"
-void EMWSPeer::set_sock(int p_sock) {
+void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) {
peer_sock = p_sock;
- in_buffer.clear();
- queue_count = 0;
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _packet_buffer.resize((1 << p_in_buf_size));
}
void EMWSPeer::set_write_mode(WriteMode p_mode) {
@@ -47,18 +47,10 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
return write_mode;
}
-void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
-
- if (in_buffer.space_left() < p_size + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL();
- }
+Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
uint8_t is_string = p_is_string ? 1 : 0;
- in_buffer.write((uint8_t *)&p_size, 4);
- in_buffer.write((uint8_t *)&is_string, 1);
- in_buffer.write(p_data, p_size);
- queue_count++;
+ return _in_buffer.write_packet(p_data, p_size, &is_string);
}
Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
@@ -89,40 +81,28 @@ Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- if (queue_count == 0)
+ if (_in_buffer.packets_left() == 0)
return ERR_UNAVAILABLE;
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- in_buffer.read((uint8_t *)&to_read, 4);
- --queue_count;
- left = in_buffer.data_left();
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ int read = 0;
+ Error err = _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
+ ERR_FAIL_COND_V(err != OK, err);
- if (left < to_read + 1) {
- in_buffer.advance_read(left);
- return FAILED;
- }
-
- in_buffer.read(&is_string, 1);
- _was_string = is_string == 1;
- in_buffer.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
return OK;
};
int EMWSPeer::get_available_packet_count() const {
- return queue_count;
+ return _in_buffer.packets_left();
};
bool EMWSPeer::was_string_packet() const {
- return _was_string;
+ return _is_string;
};
bool EMWSPeer::is_connected_to_host() const {
@@ -143,9 +123,9 @@ void EMWSPeer::close(int p_code, String p_reason) {
}, peer_sock, p_code, p_reason.utf8().get_data());
/* clang-format on */
}
+ _is_string = 0;
+ _in_buffer.clear();
peer_sock = -1;
- queue_count = 0;
- in_buffer.clear();
};
IP_Address EMWSPeer::get_connected_host() const {
@@ -162,15 +142,12 @@ uint16_t EMWSPeer::get_connected_port() const {
EMWSPeer::EMWSPeer() {
peer_sock = -1;
- queue_count = 0;
- _was_string = false;
- in_buffer.resize(16);
write_mode = WRITE_MODE_BINARY;
+ close();
};
EMWSPeer::~EMWSPeer() {
- in_buffer.resize(0);
close();
};
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index a4b2c8f50b..4beb86d45b 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -36,6 +36,7 @@
#include "core/io/packet_peer.h"
#include "core/ring_buffer.h"
#include "emscripten.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
class EMWSPeer : public WebSocketPeer {
@@ -43,25 +44,20 @@ class EMWSPeer : public WebSocketPeer {
GDCIIMPL(EMWSPeer, WebSocketPeer);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
- };
-
int peer_sock;
WriteMode write_mode;
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- RingBuffer<uint8_t> in_buffer;
- int queue_count;
- bool _was_string;
+ PoolVector<uint8_t> _packet_buffer;
+ PacketBuffer<uint8_t> _in_buffer;
+ uint8_t _is_string;
public:
- void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
- void set_sock(int sock);
+ Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size);
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
@@ -72,10 +68,6 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
- Error read_wsi(void *in, size_t len);
- Error write_wsi();
-
EMWSPeer();
~EMWSPeer();
};
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index db02162699..09f9c1ceec 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -74,6 +74,10 @@ 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;
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index 74b689a29b..2dc455c389 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -49,6 +49,7 @@ public:
IP_Address get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
+ int get_max_packet_size() const;
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index d71d091720..fa0bb4cfb2 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -32,6 +32,7 @@
#include "lws_client.h"
#include "core/io/ip.h"
#include "core/io/stream_peer_ssl.h"
+#include "core/project_settings.h"
#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@@ -90,12 +91,13 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
i.ssl_connection = 0;
}
- // This String needs to survive till we call lws_client_connect_via_info
- String addr_str = (String)addr;
-
- i.address = addr_str.ascii().get_data();
- i.host = p_host.utf8().get_data();
- i.path = p_path.utf8().get_data();
+ // These CharStrings needs to survive till we call lws_client_connect_via_info
+ CharString addr_ch = ((String)addr).ascii();
+ CharString host_ch = p_host.utf8();
+ CharString path_ch = p_path.utf8();
+ i.address = addr_ch.get_data();
+ i.host = host_ch.get_data();
+ i.path = path_ch.get_data();
i.port = p_port;
lws_client_connect_via_info(&i);
@@ -103,6 +105,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
return OK;
};
+int LWSClient::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
void LWSClient::poll() {
_lws_poll();
@@ -123,7 +129,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
} break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
- peer->set_wsi(wsi);
+ peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
peer_data->peer_id = 0;
peer_data->force_close = false;
peer_data->clean_close = false;
@@ -206,6 +212,11 @@ uint16_t LWSClient::get_connected_port() const {
};
LWSClient::LWSClient() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1);
+
context = NULL;
_lws_ref = NULL;
_peer = Ref<LWSPeer>(memnew(LWSPeer));
diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h
index 1bbc19f352..fdecb99925 100644
--- a/modules/websocket/lws_client.h
+++ b/modules/websocket/lws_client.h
@@ -43,8 +43,15 @@ class LWSClient : public WebSocketClient {
LWS_HELPER(LWSClient);
+private:
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+
public:
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
+ int get_max_packet_size() const;
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
IP_Address get_connected_host() const;
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index b5c130b308..04e6e7c951 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -41,11 +41,12 @@
#include "drivers/unix/net_socket_posix.h"
-void LWSPeer::set_wsi(struct lws *p_wsi) {
+void LWSPeer::set_wsi(struct lws *p_wsi, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) {
ERR_FAIL_COND(wsi != NULL);
- rbw.resize(16);
- rbr.resize(16);
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _out_buffer.resize(p_out_pkt_size, p_out_buf_size);
+ _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE);
wsi = p_wsi;
};
@@ -61,24 +62,29 @@ Error LWSPeer::read_wsi(void *in, size_t len) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- uint32_t size = in_size;
- uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
+ if (lws_is_first_fragment(wsi))
+ _in_size = 0;
+ else if (_in_size == -1) // Trash this frame
+ return ERR_FILE_CORRUPT;
- if (rbr.space_left() < len + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL_V(FAILED);
+ Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL);
+
+ if (err != OK) {
+ _in_buffer.discard_payload(_in_size);
+ _in_size = -1;
+ ERR_FAIL_V(err);
}
- copymem(&(input_buffer[size]), in, len);
- size += len;
+ _in_size += len;
- in_size = size;
if (lws_is_final_fragment(wsi)) {
- rbr.write((uint8_t *)&size, 4);
- rbr.write((uint8_t *)&is_string, 1);
- rbr.write(input_buffer, size);
- in_count++;
- in_size = 0;
+ uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
+ err = _in_buffer.write_packet(NULL, _in_size, &is_string);
+ if (err != OK) {
+ _in_buffer.discard_payload(_in_size);
+ _in_size = -1;
+ ERR_FAIL_V(err);
+ }
}
return OK;
@@ -89,26 +95,20 @@ Error LWSPeer::write_wsi() {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
PoolVector<uint8_t> tmp;
- int left = rbw.data_left();
- uint32_t to_write = 0;
+ int count = _out_buffer.packets_left();
- if (left == 0 || out_count == 0)
+ if (count == 0)
return OK;
- rbw.read((uint8_t *)&to_write, 4);
- out_count--;
-
- if (left < to_write) {
- rbw.advance_read(left);
- return FAILED;
- }
+ int read = 0;
+ uint8_t is_string;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read);
- tmp.resize(LWS_PRE + to_write);
- rbw.read(&(tmp.write()[LWS_PRE]), to_write);
- lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode);
- tmp.resize(0);
+ enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY;
+ lws_write(wsi, &(rw[LWS_PRE]), read, mode);
- if (out_count > 0)
+ if (count > 1)
lws_callback_on_writable(wsi); // we want to write more!
return OK;
@@ -118,40 +118,27 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- rbw.write((uint8_t *)&p_buffer_size, 4);
- rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left()));
- out_count++;
-
+ uint8_t is_string = write_mode == WRITE_MODE_TEXT;
+ _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string);
lws_callback_on_writable(wsi); // notify that we want to write
return OK;
};
Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ r_buffer_size = 0;
+
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- if (in_count == 0)
+ if (_in_buffer.packets_left() == 0)
return ERR_UNAVAILABLE;
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- rbr.read((uint8_t *)&to_read, 4);
- in_count--;
- left = rbr.data_left();
-
- if (left < to_read + 1) {
- rbr.advance_read(left);
- return FAILED;
- }
+ int read = 0;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
- rbr.read(&is_string, 1);
- rbr.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
- _was_string = is_string;
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
return OK;
};
@@ -161,12 +148,12 @@ int LWSPeer::get_available_packet_count() const {
if (!is_connected_to_host())
return 0;
- return in_count;
+ return _in_buffer.packets_left();
};
bool LWSPeer::was_string_packet() const {
- return _was_string;
+ return _is_string;
};
bool LWSPeer::is_connected_to_host() const {
@@ -219,12 +206,11 @@ void LWSPeer::close(int p_code, String p_reason) {
close_reason = "";
}
wsi = NULL;
- rbw.resize(0);
- rbr.resize(0);
- in_count = 0;
- in_size = 0;
- out_count = 0;
- _was_string = false;
+ _in_buffer.clear();
+ _out_buffer.clear();
+ _in_size = 0;
+ _is_string = 0;
+ _packet_buffer.resize(0);
};
IP_Address LWSPeer::get_connected_host() const {
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h
index 571445db01..3ded3810d1 100644
--- a/modules/websocket/lws_peer.h
+++ b/modules/websocket/lws_peer.h
@@ -37,6 +37,7 @@
#include "core/ring_buffer.h"
#include "libwebsockets.h"
#include "lws_config.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
class LWSPeer : public WebSocketPeer {
@@ -44,14 +45,16 @@ class LWSPeer : public WebSocketPeer {
GDCIIMPL(LWSPeer, WebSocketPeer);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type
- };
+ int _in_size;
+ uint8_t _is_string;
+ // Our packet info is just a boolean (is_string), using uint8_t for it.
+ PacketBuffer<uint8_t> _in_buffer;
+ PacketBuffer<uint8_t> _out_buffer;
+
+ PoolVector<uint8_t> _packet_buffer;
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
struct lws *wsi;
WriteMode write_mode;
- bool _was_string;
int close_code;
String close_reason;
@@ -63,17 +66,10 @@ public:
bool clean_close;
};
- RingBuffer<uint8_t> rbw;
- RingBuffer<uint8_t> rbr;
- uint8_t input_buffer[PACKET_BUFFER_SIZE];
- uint32_t in_size;
- int in_count;
- int out_count;
-
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
@@ -84,7 +80,7 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
+ void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size);
Error read_wsi(void *in, size_t len);
Error write_wsi();
void send_close_status(struct lws *wsi);
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 58fa043346..0e551eb318 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -31,6 +31,7 @@
#include "lws_server.h"
#include "core/os/os.h"
+#include "core/project_settings.h"
Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
@@ -67,6 +68,10 @@ bool LWSServer::is_listening() const {
return context != NULL;
}
+int LWSServer::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
@@ -85,7 +90,7 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
int32_t id = _gen_unique_id();
Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer));
- peer->set_wsi(wsi);
+ peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
_peer_map[id] = peer;
peer_data->peer_id = id;
@@ -192,6 +197,10 @@ void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
}
LWSServer::LWSServer() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1);
context = NULL;
_lws_ref = NULL;
}
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index 346773ebc4..c43044f194 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -45,11 +45,16 @@ class LWSServer : public WebSocketServer {
private:
Map<int, Ref<LWSPeer> > peer_map;
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
public:
Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
void stop();
bool is_listening() const;
+ int get_max_packet_size() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
IP_Address get_peer_address(int p_peer_id) const;
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
new file mode 100644
index 0000000000..a3af7f728a
--- /dev/null
+++ b/modules/websocket/packet_buffer.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* packet_buffer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 PACKET_BUFFER_H
+#define PACKET_BUFFER_H
+
+#include "core/os/copymem.h"
+#include "core/ring_buffer.h"
+
+template <class T>
+class PacketBuffer {
+
+private:
+ typedef struct {
+ uint32_t size;
+ T info;
+ } _Packet;
+
+ RingBuffer<_Packet> _packets;
+ RingBuffer<uint8_t> _payload;
+
+public:
+ Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) {
+#ifdef TOOLS_ENABLED
+ // Verbose buffer warnings
+ if (p_payload && _payload.space_left() < p_size) {
+ ERR_PRINT("Buffer payload full! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+ if (p_info && _packets.space_left() < 1) {
+ ERR_PRINT("Too many packets in queue! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+#else
+ ERR_FAIL_COND_V(p_payload && _payload.space_left() < p_size, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY);
+#endif
+
+ // If p_info is NULL, only the payload is written
+ if (p_info) {
+ _Packet p;
+ p.size = p_size;
+ copymem(&p.info, p_info, sizeof(T));
+ _packets.write(p);
+ }
+
+ // If p_payload is NULL, only the packet information is written.
+ if (p_payload) {
+ _payload.write((const uint8_t *)p_payload, p_size);
+ }
+
+ return OK;
+ }
+
+ Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) {
+ ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE);
+ _Packet p;
+ _packets.read(&p, 1);
+ ERR_FAIL_COND_V(_payload.data_left() < p.size, ERR_BUG);
+ ERR_FAIL_COND_V(p_bytes < p.size, ERR_OUT_OF_MEMORY);
+
+ r_read = p.size;
+ copymem(r_info, &p.info, sizeof(T));
+ _payload.read(r_payload, p.size);
+ return OK;
+ }
+
+ void discard_payload(int p_size) {
+ _packets.decrease_write(p_size);
+ }
+
+ void resize(int p_pkt_shift, int p_buf_shift) {
+ _packets.resize(p_pkt_shift);
+ _payload.resize(p_buf_shift);
+ }
+
+ int packets_left() const {
+ return _packets.data_left();
+ }
+
+ void clear() {
+ _payload.resize(0);
+ _packets.resize(0);
+ }
+
+ PacketBuffer() {
+ clear();
+ }
+
+ ~PacketBuffer() {
+ clear();
+ }
+};
+
+#endif // PACKET_BUFFER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 538cd40454..8946faffa9 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "register_types.h"
#include "core/error_macros.h"
+#include "core/project_settings.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
@@ -41,6 +42,22 @@
#endif
void register_websocket_types() {
+#define _SET_HINT(NAME, _VAL_, _MAX_) \
+ GLOBAL_DEF(NAME, _VAL_); \
+ ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
+
+ // Client buffers project settings
+ _SET_HINT(WSC_IN_BUF, 64, 4096);
+ _SET_HINT(WSC_IN_PKT, 1024, 16384);
+ _SET_HINT(WSC_OUT_BUF, 64, 4096);
+ _SET_HINT(WSC_OUT_PKT, 1024, 16384);
+
+ // Server buffers project settings
+ _SET_HINT(WSS_IN_BUF, 64, 4096);
+ _SET_HINT(WSS_IN_PKT, 1024, 16384);
+ _SET_HINT(WSS_OUT_BUF, 64, 4096);
+ _SET_HINT(WSS_OUT_PKT, 1024, 16384);
+
#ifdef JAVASCRIPT_ENABLED
EM_ASM({
var IDHandler = {};
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index f9b94dc519..6c5018bb79 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -136,7 +136,7 @@ void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index d27fb4d778..45dd30d0ce 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -30,6 +30,16 @@
#ifndef WEBSOCKETMACTOS_H
#define WEBSOCKETMACTOS_H
+#define WSC_IN_BUF "network/limits/websocket_client/max_in_buffer_kb"
+#define WSC_IN_PKT "network/limits/websocket_client/max_in_packets"
+#define WSC_OUT_BUF "network/limits/websocket_client/max_out_buffer_kb"
+#define WSC_OUT_PKT "network/limits/websocket_client/max_out_packets"
+
+#define WSS_IN_BUF "network/limits/websocket_server/max_in_buffer_kb"
+#define WSS_IN_PKT "network/limits/websocket_server/max_in_packets"
+#define WSS_OUT_BUF "network/limits/websocket_server/max_out_buffer_kb"
+#define WSS_OUT_PKT "network/limits/websocket_server/max_out_packets"
+
/* clang-format off */
#define GDCICLASS(CNAME) \
public:\
diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp
index 873658559a..9a95c17e47 100644
--- a/modules/websocket/websocket_multiplayer.cpp
+++ b/modules/websocket/websocket_multiplayer.cpp
@@ -100,13 +100,6 @@ int WebSocketMultiplayerPeer::get_available_packet_count() const {
return _incoming_packets.size();
}
-int WebSocketMultiplayerPeer::get_max_packet_size() const {
-
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
-
- return MAX_PACKET_SIZE;
-}
-
Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
r_buffer_size = 0;
diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h
index 8edfc5296e..3cba0011fc 100644
--- a/modules/websocket/websocket_multiplayer.h
+++ b/modules/websocket/websocket_multiplayer.h
@@ -51,9 +51,7 @@ protected:
SYS_DEL = 2,
SYS_ID = 3,
- PROTO_SIZE = 9,
- SYS_PACKET_SIZE = 13,
- MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer
+ PROTO_SIZE = 9
};
struct Packet {
@@ -93,7 +91,7 @@ public:
/* PacketPeer */
virtual int get_available_packet_count() const;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const = 0;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 5918fda3c2..4966cdfc72 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -32,7 +32,6 @@
#include "core/error_list.h"
#include "core/io/packet_peer.h"
-#include "core/ring_buffer.h"
#include "websocket_macros.h"
class WebSocketPeer : public PacketPeer {