diff options
Diffstat (limited to 'modules')
72 files changed, 1230 insertions, 497 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 44044d2750..4eb798de11 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -598,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 ef86ccae14..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()) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 2ce92f340d..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 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 97ac6f7de6..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); + } } } @@ -7080,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/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 a048baf5d7..700e518cfc 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2770,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/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs index 574b711b29..2ce7837a27 100644 --- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs +++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs @@ -6,22 +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) @@ -31,34 +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) @@ -82,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); @@ -187,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/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 03db765c2e..416108603b 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -31,6 +31,8 @@ #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" @@ -62,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); @@ -106,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"); @@ -127,6 +132,14 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou _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] = { @@ -167,6 +180,7 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou 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); } diff --git a/modules/mono/editor/net_solution.cpp b/modules/mono/editor/dotnet_solution.cpp index a000debe52..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" @@ -58,27 +58,26 @@ "\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 6216213716..0f12fc5243 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -227,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; } } @@ -302,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(); @@ -317,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; @@ -377,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); @@ -403,11 +387,13 @@ bool GodotSharpBuilds::editor_build_callback() { Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor); ERR_FAIL_COND_V(metadata_err != OK, false); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player); + 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); + 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 509ec961fb..34c710320a 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -117,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); diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 43de8df2b2..bc8eed81cf 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -5,6 +5,30 @@ #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) { @@ -203,7 +227,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { } } -Error ScriptClassParser::_skip_type_parameters() { +Error ScriptClassParser::_skip_generic_type_params() { Token tk; @@ -213,68 +237,100 @@ Error ScriptClassParser::_skip_type_parameters() { 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_type_parameters(); + Error err = _skip_generic_type_params(); if (err) return err; continue; } else if (tk != TK_COMMA) { - error_str = "Unexpected token: " + itos(tk); + error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } } else if (tk == TK_OP_LESS) { - error_str = "Expected identifier before `<`."; + 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: " + itos(tk); + error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } } } -Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { +Error ScriptClassParser::_parse_type_full_name(String &r_full_name) { - Token tk; + Token tk = get_token(); - 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; + } - if (tk == TK_IDENTIFIER) { - bool generic = false; + r_full_name += String(value); - String name = value; + if (code[idx] != '.') // We only want to take the next token if it's a period + return OK; - tk = get_token(); + tk = get_token(); - if (tk == TK_OP_LESS) { - generic = true; - Error err = _skip_type_parameters(); - if (err) - return err; - } else if (tk == TK_COMMA) { - Error err = _parse_class_base(r_base); - if (err) - return err; - } else if (tk != TK_CURLY_BRACKET_OPEN) { - error_str = "Unexpected token: " + itos(tk); - error = true; - return ERR_PARSE_ERROR; - } + CRASH_COND(tk != TK_PERIOD); // Assertion - r_base.push_back(!generic ? name : String()); // no generics, please + r_full_name += "."; - return OK; - } else { - error_str = "Unexpected token: " + itos(tk); - error = true; - return ERR_PARSE_ERROR; - } + 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) { @@ -284,7 +340,7 @@ Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stac if (tk == TK_IDENTIFIER) { r_name += String(value); } else { - error_str = "Unexpected token: " + itos(tk); + error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } @@ -298,7 +354,7 @@ Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stac r_curly_stack++; return OK; } else { - error_str = "Unexpected token: " + itos(tk); + error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } @@ -366,11 +422,11 @@ Error ScriptClassParser::parse(const String &p_code) { } else if (tk == TK_OP_LESS && !generic) { generic = true; - Error err = _skip_type_parameters(); + Error err = _skip_generic_type_params(); if (err) return err; } else { - error_str = "Unexpected token: " + itos(tk); + error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } @@ -400,7 +456,7 @@ Error ScriptClassParser::parse(const String &p_code) { name = String(value); } else if (tk == TK_CURLY_BRACKET_OPEN) { if (name.empty()) { - error_str = "Expected identifier after keyword `struct`. Found `{`."; + 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; } @@ -409,7 +465,7 @@ Error ScriptClassParser::parse(const String &p_code) { type_curly_stack++; break; } else if (tk == TK_EOF) { - error_str = "Expected `{` after struct decl. Found `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; } diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h index 11cf1853e2..1e174c28a9 100644 --- a/modules/mono/editor/script_class_parser.h +++ b/modules/mono/editor/script_class_parser.h @@ -52,13 +52,18 @@ private: TK_OP_LESS, TK_OP_GREATER, TK_EOF, - TK_ERROR + TK_ERROR, + TK_MAX }; + static const char *token_names[TK_MAX]; + static String get_token_name(Token p_token); + Token get_token(); - Error _skip_type_parameters(); + 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); 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 c3feafee28..a80155bd89 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -496,12 +496,12 @@ bool GDMono::_load_core_api_assembly() { } #endif - String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(API_ASSEMBLY_NAME ".dll"); + 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(API_ASSEMBLY_NAME, + bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly); @@ -635,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)); @@ -666,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_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/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/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_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 { |