summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/bmp/image_loader_bmp.cpp2
-rw-r--r--modules/bullet/area_bullet.cpp10
-rw-r--r--modules/bullet/bullet_physics_server.cpp238
-rw-r--r--modules/bullet/bullet_physics_server.h54
-rw-r--r--modules/bullet/collision_object_bullet.cpp18
-rw-r--r--modules/bullet/cone_twist_joint_bullet.cpp27
-rw-r--r--modules/bullet/cone_twist_joint_bullet.h8
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp67
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.h4
-rw-r--r--modules/bullet/godot_result_callbacks.cpp68
-rw-r--r--modules/bullet/godot_result_callbacks.h44
-rw-r--r--modules/bullet/hinge_joint_bullet.cpp12
-rw-r--r--modules/bullet/pin_joint_bullet.cpp3
-rw-r--r--modules/bullet/register_types.cpp9
-rw-r--r--modules/bullet/rigid_body_bullet.cpp38
-rw-r--r--modules/bullet/rigid_body_bullet.h5
-rw-r--r--modules/bullet/shape_bullet.cpp87
-rw-r--r--modules/bullet/shape_bullet.h42
-rw-r--r--modules/bullet/soft_body_bullet.cpp474
-rw-r--r--modules/bullet/soft_body_bullet.h103
-rw-r--r--modules/bullet/space_bullet.cpp225
-rw-r--r--modules/bullet/space_bullet.h25
-rw-r--r--modules/csg/csg.cpp74
-rw-r--r--modules/csg/csg.h2
-rw-r--r--modules/csg/csg_gizmos.cpp93
-rw-r--r--modules/csg/csg_gizmos.h24
-rw-r--r--modules/csg/csg_shape.cpp120
-rw-r--r--modules/csg/csg_shape.h12
-rw-r--r--modules/csg/doc_classes/CSGBox.xml6
-rw-r--r--modules/csg/doc_classes/CSGCombiner.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder.xml8
-rw-r--r--modules/csg/doc_classes/CSGMesh.xml3
-rw-r--r--modules/csg/doc_classes/CSGPolygon.xml27
-rw-r--r--modules/csg/doc_classes/CSGPrimitive.xml2
-rw-r--r--modules/csg/doc_classes/CSGShape.xml8
-rw-r--r--modules/csg/doc_classes/CSGSphere.xml7
-rw-r--r--modules/csg/doc_classes/CSGTorus.xml8
-rw-r--r--modules/cvtt/SCsub21
-rw-r--r--modules/cvtt/config.py5
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp394
-rw-r--r--modules/cvtt/image_compress_cvtt.h39
-rw-r--r--modules/cvtt/register_types.cpp45
-rw-r--r--modules/cvtt/register_types.h34
-rw-r--r--modules/dds/texture_loader_dds.cpp2
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp2
-rw-r--r--modules/etc/image_etc.cpp33
-rw-r--r--modules/gdnative/SCsub258
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.cpp2
-rw-r--r--modules/gdnative/arvr/arvr_interface_gdnative.h2
-rw-r--r--modules/gdnative/config.py3
-rw-r--r--modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml15
-rw-r--r--modules/gdnative/doc_classes/NativeScript.xml4
-rw-r--r--modules/gdnative/doc_classes/PacketPeerGDNative.xml (renamed from modules/webm/doc_classes/ResourceImporterWebm.xml)2
-rw-r--r--modules/gdnative/doc_classes/StreamPeerGDNative.xml (renamed from modules/theora/doc_classes/ResourceImporterTheora.xml)2
-rw-r--r--modules/gdnative/gdnative.cpp10
-rw-r--r--modules/gdnative/gdnative.h6
-rw-r--r--modules/gdnative/gdnative/basis.cpp34
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp6
-rw-r--r--modules/gdnative/gdnative/gdnative.cpp4
-rw-r--r--modules/gdnative/gdnative/pool_arrays.cpp2
-rw-r--r--modules/gdnative/gdnative/quat.cpp12
-rw-r--r--modules/gdnative/gdnative/rect2.cpp2
-rw-r--r--modules/gdnative/gdnative/string.cpp14
-rw-r--r--modules/gdnative/gdnative/transform.cpp6
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp2
-rw-r--r--modules/gdnative/gdnative/vector2.cpp2
-rw-r--r--modules/gdnative/gdnative_api.json137
-rw-r--r--modules/gdnative/gdnative_builders.py310
-rw-r--r--modules/gdnative/include/gdnative/basis.h13
-rw-r--r--modules/gdnative/include/gdnative/dictionary.h2
-rw-r--r--modules/gdnative/include/gdnative/gdnative.h4
-rw-r--r--modules/gdnative/include/gdnative/quat.h2
-rw-r--r--modules/gdnative/include/gdnative/transform.h1
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h5
-rw-r--r--modules/gdnative/include/net/godot_net.h118
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp2
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp4
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp254
-rw-r--r--modules/gdnative/nativescript/nativescript.h36
-rw-r--r--modules/gdnative/net/SCsub12
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp124
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h77
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.cpp (renamed from modules/webm/resource_importer_webm.cpp)69
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.h (renamed from modules/webm/resource_importer_webm.h)41
-rw-r--r--modules/gdnative/net/register_types.cpp43
-rw-r--r--modules/gdnative/net/register_types.h32
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.cpp (renamed from modules/theora/resource_importer_theora.cpp)65
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.h (renamed from modules/theora/resource_importer_theora.h)47
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h2
-rw-r--r--modules/gdnative/register_types.cpp11
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp30
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h4
-rw-r--r--modules/gdscript/gdscript.cpp391
-rw-r--r--modules/gdscript/gdscript.h63
-rw-r--r--modules/gdscript/gdscript_compiler.cpp733
-rw-r--r--modules/gdscript/gdscript_compiler.h11
-rw-r--r--modules/gdscript/gdscript_editor.cpp4134
-rw-r--r--modules/gdscript/gdscript_function.cpp290
-rw-r--r--modules/gdscript/gdscript_function.h102
-rw-r--r--modules/gdscript/gdscript_functions.cpp79
-rw-r--r--modules/gdscript/gdscript_functions.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp4042
-rw-r--r--modules/gdscript/gdscript_parser.h278
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp71
-rw-r--r--modules/gdscript/gdscript_tokenizer.h32
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml5
-rw-r--r--modules/gridmap/grid_map.cpp86
-rw-r--r--modules/gridmap/grid_map.h7
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp176
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h18
-rw-r--r--modules/jpg/image_loader_jpegd.cpp4
-rwxr-xr-xmodules/mbedtls/SCsub7
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp80
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h6
-rw-r--r--modules/mobile_vr/config.py3
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp (renamed from modules/mobile_vr/mobile_interface.cpp)109
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h (renamed from modules/mobile_vr/mobile_interface.h)6
-rw-r--r--modules/mobile_vr/register_types.cpp2
-rw-r--r--modules/mobile_vr/shaders/lens_distorted.glsl67
-rw-r--r--modules/mono/SCsub44
-rw-r--r--modules/mono/config.py116
-rw-r--r--modules/mono/csharp_script.cpp334
-rw-r--r--modules/mono/csharp_script.h8
-rw-r--r--modules/mono/editor/bindings_generator.cpp92
-rw-r--r--modules/mono/editor/bindings_generator.h11
-rw-r--r--modules/mono/editor/csharp_project.cpp32
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp31
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp10
-rw-r--r--modules/mono/editor/monodevelop_instance.cpp16
-rw-r--r--modules/mono/glue/collections_glue.cpp240
-rw-r--r--modules/mono/glue/collections_glue.h100
-rw-r--r--modules/mono/glue/cs_files/AABB.cs200
-rw-r--r--modules/mono/glue/cs_files/Array.cs340
-rw-r--r--modules/mono/glue/cs_files/Attributes/ExportAttribute.cs17
-rw-r--r--modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs (renamed from modules/mono/glue/cs_files/GodotMethodAttribute.cs)0
-rw-r--r--modules/mono/glue/cs_files/Attributes/RPCAttributes.cs (renamed from modules/mono/glue/cs_files/RPCAttributes.cs)0
-rw-r--r--modules/mono/glue/cs_files/Attributes/SignalAttribute.cs (renamed from modules/mono/glue/cs_files/SignalAttribute.cs)0
-rw-r--r--modules/mono/glue/cs_files/Attributes/ToolAttribute.cs7
-rw-r--r--modules/mono/glue/cs_files/Basis.cs20
-rw-r--r--modules/mono/glue/cs_files/Color.cs88
-rw-r--r--modules/mono/glue/cs_files/Dictionary.cs406
-rw-r--r--modules/mono/glue/cs_files/ExportAttribute.cs17
-rw-r--r--modules/mono/glue/cs_files/Extensions/NodeExtensions.cs45
-rw-r--r--modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs17
-rw-r--r--modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs10
-rw-r--r--modules/mono/glue/cs_files/GD.cs43
-rw-r--r--modules/mono/glue/cs_files/IAwaiter.cs18
-rw-r--r--modules/mono/glue/cs_files/Interfaces/IAwaitable.cs (renamed from modules/mono/glue/cs_files/IAwaitable.cs)0
-rw-r--r--modules/mono/glue/cs_files/Interfaces/IAwaiter.cs18
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs30
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs40
-rw-r--r--modules/mono/glue/cs_files/Plane.cs9
-rw-r--r--modules/mono/glue/cs_files/Quat.cs97
-rw-r--r--modules/mono/glue/cs_files/Rect2.cs122
-rw-r--r--modules/mono/glue/cs_files/StringExtensions.cs14
-rw-r--r--modules/mono/glue/cs_files/ToolAttribute.cs7
-rw-r--r--modules/mono/glue/cs_files/Transform.cs13
-rw-r--r--modules/mono/glue/cs_files/Transform2D.cs21
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt1
-rw-r--r--modules/mono/glue/cs_files/Vector2.cs44
-rw-r--r--modules/mono/glue/cs_files/Vector3.cs46
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/godotsharp_defs.h1
-rw-r--r--modules/mono/mono_gc_handle.cpp14
-rw-r--r--modules/mono/mono_gc_handle.h15
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp97
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp76
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp38
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h10
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp45
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h3
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp3
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp171
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp24
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp27
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp250
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h70
-rw-r--r--modules/mono/mono_reg_utils.py4
-rw-r--r--modules/mono/signal_awaiter_utils.cpp27
-rw-r--r--modules/mono/signal_awaiter_utils.h2
-rw-r--r--modules/mono/tls_configure.py36
-rw-r--r--modules/mono/utils/macros.h59
-rw-r--r--modules/mono/utils/thread_local.cpp107
-rw-r--r--modules/mono/utils/thread_local.h171
-rw-r--r--modules/pvr/texture_loader_pvr.cpp22
-rw-r--r--modules/recast/navigation_mesh_generator.cpp6
-rw-r--r--modules/regex/SCsub3
-rw-r--r--modules/regex/regex.cpp14
-rw-r--r--modules/squish/image_compress_squish.cpp51
-rw-r--r--modules/squish/image_compress_squish.h2
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp2
-rw-r--r--modules/tga/image_loader_tga.cpp3
-rw-r--r--modules/theora/register_types.cpp16
-rw-r--r--modules/theora/video_stream_theora.cpp53
-rw-r--r--modules/theora/video_stream_theora.h9
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp81
-rw-r--r--modules/visual_script/visual_script.cpp21
-rw-r--r--modules/visual_script/visual_script.h3
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp5
-rw-r--r--modules/visual_script/visual_script_editor.cpp493
-rw-r--r--modules/visual_script/visual_script_editor.h48
-rw-r--r--modules/visual_script/visual_script_expression.cpp28
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp4
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp9
-rw-r--r--modules/visual_script/visual_script_nodes.cpp32
-rw-r--r--modules/visual_script/visual_script_nodes.h4
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp733
-rw-r--r--modules/visual_script/visual_script_property_selector.h93
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp2
-rw-r--r--modules/webm/libvpx/SCsub12
-rw-r--r--modules/webm/register_types.cpp16
-rw-r--r--modules/webm/video_stream_webm.cpp43
-rw-r--r--modules/webm/video_stream_webm.h9
-rw-r--r--modules/webp/image_loader_webp.cpp67
-rw-r--r--modules/websocket/SCsub165
-rw-r--r--modules/websocket/emws_client.cpp19
-rw-r--r--modules/websocket/lws_client.cpp24
-rw-r--r--modules/websocket/lws_helper.h59
-rw-r--r--modules/websocket/lws_peer.cpp71
-rw-r--r--modules/websocket/lws_peer.h13
-rw-r--r--modules/websocket/lws_server.cpp12
229 files changed, 16162 insertions, 5278 deletions
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index 769119a0dc..919731b52b 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -53,7 +53,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
err = FAILED;
}
- if (bits_per_pixel != 24 || bits_per_pixel != 32) {
+ if (!(bits_per_pixel == 24 || bits_per_pixel == 32)) {
err = FAILED;
}
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index bfb452d109..3668088590 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -80,7 +80,7 @@ void AreaBullet::dispatch_callbacks() {
// Reverse order because I've to remove EXIT objects
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- OverlappingObjectData &otherObj = overlappingObjects[i];
+ OverlappingObjectData &otherObj = overlappingObjects.write[i];
switch (otherObj.state) {
case OVERLAP_STATE_ENTER:
@@ -199,13 +199,13 @@ void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
void AreaBullet::put_overlap_as_exit(int p_index) {
scratch();
- overlappingObjects[p_index].state = OVERLAP_STATE_EXIT;
+ overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT;
}
void AreaBullet::put_overlap_as_inside(int p_index) {
// This check is required to be sure this body was inside
if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
- overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE;
+ overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE;
}
}
@@ -236,7 +236,7 @@ void AreaBullet::set_param(PhysicsServer::AreaParameter p_param, const Variant &
set_spOv_gravityPointAttenuation(p_value);
break;
default:
- print_line("The Bullet areas doesn't suppot this param: " + itos(p_param));
+ WARN_PRINTS("Area doesn't support this parameter in the Bullet backend: " + itos(p_param));
}
}
@@ -259,7 +259,7 @@ Variant AreaBullet::get_param(PhysicsServer::AreaParameter p_param) const {
case PhysicsServer::AREA_PARAM_GRAVITY_POINT_ATTENUATION:
return spOv_gravityPointAttenuation;
default:
- print_line("The Bullet areas doesn't suppot this param: " + itos(p_param));
+ WARN_PRINTS("Area doesn't support this parameter in the Bullet backend: " + itos(p_param));
return Variant();
}
}
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 6246a295ec..dbd27a3564 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -113,6 +113,10 @@ RID BulletPhysicsServer::shape_create(ShapeType p_shape) {
shape = bulletnew(CapsuleShapeBullet);
} break;
+ case SHAPE_CYLINDER: {
+
+ shape = bulletnew(CylinderShapeBullet);
+ } break;
case SHAPE_CONVEX_POLYGON: {
shape = bulletnew(ConvexPolygonShapeBullet);
@@ -159,13 +163,25 @@ Variant BulletPhysicsServer::shape_get_data(RID p_shape) const {
return shape->get_data();
}
+void BulletPhysicsServer::shape_set_margin(RID p_shape, real_t p_margin) {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_margin(p_margin);
+}
+
+real_t BulletPhysicsServer::shape_get_margin(RID p_shape) const {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape, 0.0);
+ return shape->get_margin();
+}
+
real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
//WARN_PRINT("Bias not supported by Bullet physics engine");
return 0.;
}
RID BulletPhysicsServer::space_create() {
- SpaceBullet *space = bulletnew(SpaceBullet(false));
+ SpaceBullet *space = bulletnew(SpaceBullet);
CreateThenReturnRID(space_owner, space);
}
@@ -563,9 +579,6 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) {
void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) {
CollisionObjectBullet *body = get_collisin_object(p_body);
- if (!body) {
- body = soft_body_owner.get(p_body);
- }
ERR_FAIL_COND(!body);
body->set_instance_id(p_ID);
@@ -706,6 +719,34 @@ Vector3 BulletPhysicsServer::body_get_applied_torque(RID p_body) const {
return body->get_applied_torque();
}
+void BulletPhysicsServer::body_add_central_force(RID p_body, const Vector3 &p_force) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_central_force(p_force);
+}
+
+void BulletPhysicsServer::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_force(p_force, p_pos);
+}
+
+void BulletPhysicsServer::body_add_torque(RID p_body, const Vector3 &p_torque) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_torque(p_torque);
+}
+
+void BulletPhysicsServer::body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->apply_central_impulse(p_impulse);
+}
+
void BulletPhysicsServer::body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) {
RigidBodyBullet *body = rigid_body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -832,12 +873,20 @@ PhysicsDirectBodyState *BulletPhysicsServer::body_get_direct_state(RID p_body) {
return BulletPhysicsDirectBodyState::get_singleton(body);
}
-bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result) {
+bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) {
RigidBodyBullet *body = rigid_body_owner.get(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes);
+}
+
+int BulletPhysicsServer::body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) {
+ RigidBodyBullet *body = rigid_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0);
+ ERR_FAIL_COND_V(!body->get_space(), 0);
+
+ return body->get_space()->test_ray_separation(body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin);
}
RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
@@ -849,6 +898,13 @@ RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) {
CreateThenReturnRID(soft_body_owner, body);
}
+void BulletPhysicsServer::soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+
+ body->update_visual_server(p_visual_server_handler);
+}
+
void BulletPhysicsServer::soft_body_set_space(RID p_body, RID p_space) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
@@ -875,11 +931,11 @@ RID BulletPhysicsServer::soft_body_get_space(RID p_body) const {
return space->get_self();
}
-void BulletPhysicsServer::soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void BulletPhysicsServer::soft_body_set_mesh(RID p_body, const REF &p_mesh) {
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_trimesh_body_shape(p_indices, p_vertices, p_triangles_num);
+ body->set_soft_mesh(p_mesh);
}
void BulletPhysicsServer::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
@@ -945,11 +1001,13 @@ void BulletPhysicsServer::soft_body_get_collision_exceptions(RID p_body, List<RI
}
void BulletPhysicsServer::soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {
- print_line("TODO MUST BE IMPLEMENTED");
+ // FIXME: Must be implemented.
+ WARN_PRINT("soft_body_state is not implemented yet in Bullet backend.");
}
Variant BulletPhysicsServer::soft_body_get_state(RID p_body, BodyState p_state) const {
- print_line("TODO MUST BE IMPLEMENTED");
+ // FIXME: Must be implemented.
+ WARN_PRINT("soft_body_state is not implemented yet in Bullet backend.");
return Variant();
}
@@ -957,14 +1015,16 @@ void BulletPhysicsServer::soft_body_set_transform(RID p_body, const Transform &p
SoftBodyBullet *body = soft_body_owner.get(p_body);
ERR_FAIL_COND(!body);
- body->set_transform(p_transform);
+ body->set_soft_transform(p_transform);
}
-Transform BulletPhysicsServer::soft_body_get_transform(RID p_body) const {
+Vector3 BulletPhysicsServer::soft_body_get_vertex_position(RID p_body, int vertex_index) const {
const SoftBodyBullet *body = soft_body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, Transform());
+ Vector3 pos;
+ ERR_FAIL_COND_V(!body, pos);
- return body->get_transform();
+ body->get_node_position(vertex_index, pos);
+ return pos;
}
void BulletPhysicsServer::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
@@ -979,6 +1039,154 @@ bool BulletPhysicsServer::soft_body_is_ray_pickable(RID p_body) const {
return body->is_ray_pickable();
}
+void BulletPhysicsServer::soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_simulation_precision(p_simulation_precision);
+}
+
+int BulletPhysicsServer::soft_body_get_simulation_precision(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_simulation_precision();
+}
+
+void BulletPhysicsServer::soft_body_set_total_mass(RID p_body, real_t p_total_mass) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_total_mass(p_total_mass);
+}
+
+real_t BulletPhysicsServer::soft_body_get_total_mass(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_total_mass();
+}
+
+void BulletPhysicsServer::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_linear_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_linear_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_linear_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_areaAngular_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_areaAngular_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_areaAngular_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_volume_stiffness(p_stiffness);
+}
+
+real_t BulletPhysicsServer::soft_body_get_volume_stiffness(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_volume_stiffness();
+}
+
+void BulletPhysicsServer::soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_pressure_coefficient(p_pressure_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pressure_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pressure_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ return body->set_pose_matching_coefficient(p_pose_matching_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_pose_matching_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_pose_matching_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_damping_coefficient(p_damping_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_damping_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_damping_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_drag_coefficient(p_drag_coefficient);
+}
+
+real_t BulletPhysicsServer::soft_body_get_drag_coefficient(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_drag_coefficient();
+}
+
+void BulletPhysicsServer::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_position(p_point_index, p_global_position);
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_global_position(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3(0., 0., 0.));
+ Vector3 pos;
+ body->get_node_position(p_point_index, pos);
+ return pos;
+}
+
+Vector3 BulletPhysicsServer::soft_body_get_point_offset(RID p_body, int p_point_index) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, Vector3());
+ Vector3 res;
+ body->get_node_offset(p_point_index, res);
+ return res;
+}
+
+void BulletPhysicsServer::soft_body_remove_all_pinned_points(RID p_body) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->reset_all_node_mass();
+}
+
+void BulletPhysicsServer::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
+ body->set_node_mass(p_point_index, p_pin ? 0 : 1);
+}
+
+bool BulletPhysicsServer::soft_body_is_point_pinned(RID p_body, int p_point_index) {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, 0.f);
+ return body->get_node_mass(p_point_index);
+}
+
PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const {
JointBullet *joint = joint_owner.get(p_joint);
ERR_FAIL_COND_V(!joint, JOINT_PIN);
@@ -1233,7 +1441,7 @@ RID BulletPhysicsServer::joint_create_generic_6dof(RID p_body_A, const Transform
ERR_FAIL_COND_V(body_A == body_B, RID());
- JointBullet *joint = bulletnew(Generic6DOFJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B, true));
+ JointBullet *joint = bulletnew(Generic6DOFJointBullet(body_A, body_B, p_local_frame_A, p_local_frame_B));
AddJointToSpace(body_A, joint);
CreateThenReturnRID(joint_owner, joint);
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index e931915bba..e9c568d605 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -99,6 +99,9 @@ public:
virtual ShapeType shape_get_type(RID p_shape) const;
virtual Variant shape_get_data(RID p_shape) const;
+ virtual void shape_set_margin(RID p_shape, real_t p_margin);
+ virtual real_t shape_get_margin(RID p_shape) const;
+
/// Not supported
virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
/// Not supported
@@ -225,6 +228,11 @@ public:
virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque);
virtual Vector3 body_get_applied_torque(RID p_body) const;
+ virtual void body_add_central_force(RID p_body, const Vector3 &p_force);
+ virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos);
+ virtual void body_add_torque(RID p_body, const Vector3 &p_torque);
+
+ virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse);
virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse);
virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse);
virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity);
@@ -253,16 +261,19 @@ public:
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body);
- virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL);
+ virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true);
+ virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001);
/* SOFT BODY API */
virtual RID soft_body_create(bool p_init_sleeping = false);
+ virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler);
+
virtual void soft_body_set_space(RID p_body, RID p_space);
virtual RID soft_body_get_space(RID p_body) const;
- virtual void soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh);
virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer);
virtual uint32_t soft_body_get_collision_layer(RID p_body) const;
@@ -277,12 +288,49 @@ public:
virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const;
+ /// Special function. This function has bad performance
virtual void soft_body_set_transform(RID p_body, const Transform &p_transform);
- virtual Transform soft_body_get_transform(RID p_body) const;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const;
virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable);
virtual bool soft_body_is_ray_pickable(RID p_body) const;
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision);
+ virtual int soft_body_get_simulation_precision(RID p_body);
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass);
+ virtual real_t soft_body_get_total_mass(RID p_body);
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_linear_stiffness(RID p_body);
+
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body);
+
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness);
+ virtual real_t soft_body_get_volume_stiffness(RID p_body);
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient);
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body);
+
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient);
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body);
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient);
+ virtual real_t soft_body_get_damping_coefficient(RID p_body);
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient);
+ virtual real_t soft_body_get_drag_coefficient(RID p_body);
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position);
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index);
+
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body);
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin);
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index);
+
/* JOINT API */
virtual JointType joint_get_type(RID p_joint) const;
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 57e4db708e..271cdb0223 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -111,6 +111,8 @@ void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_coll
void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
exceptions.insert(p_ignoreCollisionObject->get_self());
+ if (!bt_collision_object)
+ return;
bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, true);
if (space)
space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher());
@@ -221,7 +223,7 @@ void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform
}
void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) {
- ShapeWrapper &shp = shapes[p_index];
+ ShapeWrapper &shp = shapes.write[p_index];
shp.shape->remove_owner(this);
p_shape->add_owner(this);
shp.shape = p_shape;
@@ -231,8 +233,8 @@ void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) {
void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) {
ERR_FAIL_INDEX(p_index, get_shape_count());
- shapes[p_index].set_transform(p_transform);
- on_shape_changed(shapes[p_index].shape);
+ shapes.write[p_index].set_transform(p_transform);
+ on_shape_changed(shapes.write[p_index].shape);
}
void RigidCollisionObjectBullet::remove_shape(ShapeBullet *p_shape) {
@@ -285,7 +287,7 @@ void RigidCollisionObjectBullet::on_shape_changed(const ShapeBullet *const p_sha
const int size = shapes.size();
for (int i = 0; i < size; ++i) {
if (shapes[i].shape == p_shape) {
- bulletdelete(shapes[i].bt_shape);
+ bulletdelete(shapes.write[i].bt_shape);
}
}
on_shapes_changed();
@@ -305,7 +307,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() {
// Reset shape if required
if (force_shape_reset) {
for (i = 0; i < shapes_size; ++i) {
- shpWrapper = &shapes[i];
+ shpWrapper = &shapes.write[i];
bulletdelete(shpWrapper->bt_shape);
}
force_shape_reset = false;
@@ -314,7 +316,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() {
// Insert all shapes
btVector3 body_scale(get_bt_body_scale());
for (i = 0; i < shapes_size; ++i) {
- shpWrapper = &shapes[i];
+ shpWrapper = &shapes.write[i];
if (shpWrapper->active) {
if (!shpWrapper->bt_shape) {
shpWrapper->bt_shape = shpWrapper->shape->create_bt_shape(shpWrapper->scale * body_scale);
@@ -332,7 +334,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() {
}
void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) {
- shapes[p_index].active = !p_disabled;
+ shapes.write[p_index].active = !p_disabled;
on_shapes_changed();
}
@@ -346,7 +348,7 @@ void RigidCollisionObjectBullet::on_body_scale_changed() {
}
void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) {
- ShapeWrapper &shp = shapes[p_index];
+ ShapeWrapper &shp = shapes.write[p_index];
shp.shape->remove_owner(this, p_permanentlyFromThisBody);
bulletdelete(shp.bt_shape);
}
diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp
index 472ad3b52c..ecacce0bee 100644
--- a/modules/bullet/cone_twist_joint_bullet.cpp
+++ b/modules/bullet/cone_twist_joint_bullet.cpp
@@ -64,26 +64,6 @@ ConeTwistJointBullet::ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet
setup(coneConstraint);
}
-void ConeTwistJointBullet::set_angular_only(bool angularOnly) {
- coneConstraint->setAngularOnly(angularOnly);
-}
-
-void ConeTwistJointBullet::set_limit(real_t _swingSpan1, real_t _swingSpan2, real_t _twistSpan, real_t _softness, real_t _biasFactor, real_t _relaxationFactor) {
- coneConstraint->setLimit(_swingSpan1, _swingSpan2, _twistSpan, _softness, _biasFactor, _relaxationFactor);
-}
-
-int ConeTwistJointBullet::get_solve_twist_limit() {
- return coneConstraint->getSolveTwistLimit();
-}
-
-int ConeTwistJointBullet::get_solve_swing_limit() {
- return coneConstraint->getSolveSwingLimit();
-}
-
-real_t ConeTwistJointBullet::get_twist_limit_sign() {
- return coneConstraint->getTwistLimitSign();
-}
-
void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param, real_t p_value) {
switch (p_param) {
case PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN:
@@ -103,7 +83,9 @@ void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param,
coneConstraint->setLimit(coneConstraint->getSwingSpan1(), coneConstraint->getSwingSpan2(), coneConstraint->getTwistSpan(), coneConstraint->getLimitSoftness(), coneConstraint->getBiasFactor(), p_value);
break;
default:
- WARN_PRINT("This parameter is not supported by Bullet engine");
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED
+ break;
}
}
@@ -120,7 +102,8 @@ real_t ConeTwistJointBullet::get_param(PhysicsServer::ConeTwistJointParam p_para
case PhysicsServer::CONE_TWIST_JOINT_RELAXATION:
return coneConstraint->getRelaxationFactor();
default:
- WARN_PRINT("This parameter is not supported by Bullet engine");
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED;
return 0;
}
}
diff --git a/modules/bullet/cone_twist_joint_bullet.h b/modules/bullet/cone_twist_joint_bullet.h
index bd6eb49196..d6040fd6ec 100644
--- a/modules/bullet/cone_twist_joint_bullet.h
+++ b/modules/bullet/cone_twist_joint_bullet.h
@@ -47,14 +47,6 @@ public:
virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_CONE_TWIST; }
- void set_angular_only(bool angularOnly);
-
- void set_limit(real_t _swingSpan1, real_t _swingSpan2, real_t _twistSpan, real_t _softness = 0.8f, real_t _biasFactor = 0.3f, real_t _relaxationFactor = 1.0f);
- int get_solve_twist_limit();
-
- int get_solve_swing_limit();
- real_t get_twist_limit_sign();
-
void set_param(PhysicsServer::ConeTwistJointParam p_param, real_t p_value);
real_t get_param(PhysicsServer::ConeTwistJointParam p_param) const;
};
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index adfad7803f..a36f1123bc 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -34,13 +34,13 @@
#include "bullet_utilities.h"
#include "rigid_body_bullet.h"
-#include <BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h>
+#include <BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h>
/**
@author AndreaCatania
*/
-Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB, bool useLinearReferenceFrameA) :
+Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB) :
JointBullet() {
Transform scaled_AFrame(frameInA.scaled(rbA->get_body_scale()));
@@ -58,9 +58,9 @@ Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBu
btTransform btFrameB;
G_TO_B(scaled_BFrame, btFrameB);
- sixDOFConstraint = bulletnew(btGeneric6DofConstraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB, useLinearReferenceFrameA));
+ sixDOFConstraint = bulletnew(btGeneric6DofSpring2Constraint(*rbA->get_bt_rigid_body(), *rbB->get_bt_rigid_body(), btFrameA, btFrameB));
} else {
- sixDOFConstraint = bulletnew(btGeneric6DofConstraint(*rbA->get_bt_rigid_body(), btFrameA, useLinearReferenceFrameA));
+ sixDOFConstraint = bulletnew(btGeneric6DofSpring2Constraint(*rbA->get_bt_rigid_body(), btFrameA));
}
setup(sixDOFConstraint);
@@ -123,20 +123,11 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
switch (p_param) {
case PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT:
limits_lower[0][p_axis] = p_value;
- set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter
+ set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT]); // Reload bullet parameter
break;
case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT:
limits_upper[0][p_axis] = p_value;
- set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter
- break;
- case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS:
- sixDOFConstraint->getTranslationalLimitMotor()->m_limitSoftness = p_value;
- break;
- case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION:
- sixDOFConstraint->getTranslationalLimitMotor()->m_restitution = p_value;
- break;
- case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
- sixDOFConstraint->getTranslationalLimitMotor()->m_damping = p_value;
+ set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT]); // Reload bullet parameter
break;
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY:
sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis] = p_value;
@@ -146,23 +137,11 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT:
limits_lower[1][p_axis] = p_value;
- set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter
+ set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter
break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
limits_upper[1][p_axis] = p_value;
- set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter
- break;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS:
- sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_limitSoftness = p_value;
- break;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING:
- sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_damping = p_value;
- break;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION:
- sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce = p_value;
- break;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT:
- sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce = p_value;
+ set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter
break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP:
sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP = p_value;
@@ -171,10 +150,12 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO
sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity = p_value;
break;
case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
- sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce = p_value;
+ sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce = p_value;
break;
default:
- WARN_PRINT("This parameter is not supported");
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED
+ break;
}
}
@@ -185,12 +166,6 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6
return limits_lower[0][p_axis];
case PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT:
return limits_upper[0][p_axis];
- case PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS:
- return sixDOFConstraint->getTranslationalLimitMotor()->m_limitSoftness;
- case PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION:
- return sixDOFConstraint->getTranslationalLimitMotor()->m_restitution;
- case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING:
- return sixDOFConstraint->getTranslationalLimitMotor()->m_damping;
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY:
return sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis];
case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT:
@@ -199,23 +174,18 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6
return limits_lower[1][p_axis];
case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT:
return limits_upper[1][p_axis];
- case PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS:
- return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_limitSoftness;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING:
- return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_damping;
case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION:
return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce;
- case PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT:
- return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce;
case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP:
return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP;
case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY:
return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity;
case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT:
- return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxLimitForce;
+ return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce;
default:
- WARN_PRINT("This parameter is not supported");
- return 0.;
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED;
+ return 0;
}
}
@@ -246,8 +216,9 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF
sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag];
break;
default:
- WARN_PRINT("This flag is not supported by Bullet engine");
- return;
+ ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated");
+ WARN_DEPRECATED
+ break;
}
}
diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h
index ad06582eac..176127ed6c 100644
--- a/modules/bullet/generic_6dof_joint_bullet.h
+++ b/modules/bullet/generic_6dof_joint_bullet.h
@@ -40,7 +40,7 @@
class RigidBodyBullet;
class Generic6DOFJointBullet : public JointBullet {
- class btGeneric6DofConstraint *sixDOFConstraint;
+ class btGeneric6DofSpring2Constraint *sixDOFConstraint;
// First is linear second is angular
Vector3 limits_lower[2];
@@ -48,7 +48,7 @@ class Generic6DOFJointBullet : public JointBullet {
bool flags[3][PhysicsServer::G6DOF_JOINT_FLAG_MAX];
public:
- Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB, bool useLinearReferenceFrameA);
+ Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB);
virtual PhysicsServer::JointType get_type() const { return PhysicsServer::JOINT_6DOF; }
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 197550d686..534034d707 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -30,6 +30,7 @@
#include "godot_result_callbacks.h"
+#include "area_bullet.h"
#include "bullet_types_converter.h"
#include "collision_object_bullet.h"
#include "rigid_body_bullet.h"
@@ -51,11 +52,23 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
- if (m_pickRay && gObj->is_ray_pickable()) {
- return true;
- } else if (m_exclude->has(gObj->get_self())) {
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
+ if (m_pickRay && !gObj->is_ray_pickable()) {
+ return false;
+ }
+
+ if (m_exclude->has(gObj->get_self())) {
return false;
}
+
return true;
} else {
return false;
@@ -124,6 +137,15 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0)
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -144,6 +166,15 @@ bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -189,6 +220,15 @@ bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *pr
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -218,6 +258,15 @@ bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -260,10 +309,19 @@ void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3
if (m_penetration_distance > depth) { // Has penetration?
- bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
+ const bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
m_penetration_distance = depth;
m_other_compound_shape_index = isSwapped ? m_index0 : m_index1;
- m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB;
+
+ const btCollisionObjectWrapper *bw0 = m_body0Wrap;
+ if (isSwapped)
+ bw0 = m_body1Wrap;
+
+ if (bw0->getCollisionShape()->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) {
+ m_pointNormalWorld = bw0->m_worldTransform.getBasis().transpose() * btVector3(0, 0, 1);
+ } else {
+ m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
+ }
}
}
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 363051f24c..3948f43c00 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -56,12 +56,17 @@ struct GodotClosestRayResultCallback : public btCollisionWorld::ClosestRayResult
bool m_pickRay;
int m_shapeId;
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
public:
- GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude) :
+ GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld),
m_exclude(p_exclude),
m_pickRay(false),
- m_shapeId(0) {}
+ m_shapeId(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -108,9 +113,14 @@ public:
const Set<RID> *m_exclude;
int m_shapeId;
- GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
- m_exclude(p_exclude) {}
+ m_exclude(p_exclude),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -125,12 +135,17 @@ public:
int m_count;
const Set<RID> *m_exclude;
- GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
m_exclude(p_exclude),
m_resultMax(p_resultMax),
- m_count(0) {}
+ m_count(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -146,12 +161,17 @@ public:
int m_count;
const Set<RID> *m_exclude;
- GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
m_exclude(p_exclude),
m_resultMax(p_resultMax),
- m_count(0) {}
+ m_count(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -167,13 +187,17 @@ public:
const btCollisionObject *m_rest_info_collision_object;
btVector3 m_rest_info_bt_point;
const Set<RID> *m_exclude;
+ bool collide_with_bodies;
+ bool collide_with_areas;
- GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeRestInfo *p_result, const Set<RID> *p_exclude) :
+ GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeRestInfo *p_result, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_result(p_result),
m_exclude(p_exclude),
m_collided(false),
- m_min_distance(0) {}
+ m_min_distance(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp
index 97ea7ca3df..86c6a632cd 100644
--- a/modules/bullet/hinge_joint_bullet.cpp
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -95,11 +95,6 @@ real_t HingeJointBullet::get_hinge_angle() {
void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t p_value) {
switch (p_param) {
- case PhysicsServer::HINGE_JOINT_BIAS:
- if (0 < p_value) {
- print_line("The Bullet Hinge Joint doesn't support bias, So it's always 0");
- }
- break;
case PhysicsServer::HINGE_JOINT_LIMIT_UPPER:
hingeConstraint->setLimit(hingeConstraint->getLowerLimit(), p_value, hingeConstraint->getLimitSoftness(), hingeConstraint->getLimitBiasFactor(), hingeConstraint->getLimitRelaxationFactor());
break;
@@ -122,7 +117,9 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t
hingeConstraint->setMaxMotorImpulse(p_value);
break;
default:
- WARN_PRINTS("The Bullet Hinge Joint doesn't support this parameter: " + itos(p_param) + ", value: " + itos(p_value));
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED
+ break;
}
}
@@ -146,7 +143,8 @@ real_t HingeJointBullet::get_param(PhysicsServer::HingeJointParam p_param) const
case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE:
return hingeConstraint->getMaxMotorImpulse();
default:
- WARN_PRINTS("The Bullet Hinge Joint doesn't support this parameter: " + itos(p_param));
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED;
return 0;
}
}
diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp
index c4e5b8cdbe..183a7e75b9 100644
--- a/modules/bullet/pin_joint_bullet.cpp
+++ b/modules/bullet/pin_joint_bullet.cpp
@@ -85,7 +85,8 @@ real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const {
case PhysicsServer::PIN_JOINT_IMPULSE_CLAMP:
return p2pConstraint->m_setting.m_impulseClamp;
default:
- WARN_PRINTS("This get parameter is not supported");
+ ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated");
+ WARN_DEPRECATED
return 0;
}
}
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
index b119b7720f..a76b0438b4 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -32,19 +32,26 @@
#include "bullet_physics_server.h"
#include "class_db.h"
+#include "project_settings.h"
/**
@author AndreaCatania
*/
+#ifndef _3D_DISABLED
PhysicsServer *_createBulletPhysicsCallback() {
return memnew(BulletPhysicsServer);
}
+#endif
void register_bullet_types() {
-
+#ifndef _3D_DISABLED
PhysicsServerManager::register_server("Bullet", &_createBulletPhysicsCallback);
PhysicsServerManager::set_default_server("Bullet", 1);
+
+ GLOBAL_DEF("physics/3d/active_soft_world", true);
+ ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/active_soft_world", PropertyInfo(Variant::BOOL, "physics/3d/active_soft_world"));
+#endif
}
void unregister_bullet_types() {
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2494063c22..9c0e802be5 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -126,6 +126,10 @@ void BulletPhysicsDirectBodyState::add_torque(const Vector3 &p_torque) {
body->apply_torque(p_torque);
}
+void BulletPhysicsDirectBodyState::apply_central_impulse(const Vector3 &p_j) {
+ body->apply_central_impulse(p_j);
+}
+
void BulletPhysicsDirectBodyState::apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) {
body->apply_impulse(p_pos, p_j);
}
@@ -154,6 +158,10 @@ Vector3 BulletPhysicsDirectBodyState::get_contact_local_normal(int p_contact_idx
return body->collisions[p_contact_idx].hitNormal;
}
+float BulletPhysicsDirectBodyState::get_contact_impulse(int p_contact_idx) const {
+ return body->collisions[p_contact_idx].appliedImpulse;
+}
+
int BulletPhysicsDirectBodyState::get_contact_local_shape(int p_contact_idx) const {
return body->collisions[p_contact_idx].local_shape;
}
@@ -175,7 +183,7 @@ int BulletPhysicsDirectBodyState::get_contact_collider_shape(int p_contact_idx)
}
Vector3 BulletPhysicsDirectBodyState::get_contact_collider_velocity_at_position(int p_contact_idx) const {
- RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx];
+ RigidBodyBullet::CollisionData &colDat = body->collisions.write[p_contact_idx];
btVector3 hitLocation;
G_TO_B(colDat.hitLocalLocation, hitLocation);
@@ -220,19 +228,20 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
continue;
}
- shapes[i].transform = shape_wrapper->transform;
- shapes[i].transform.getOrigin() *= owner_scale;
+ shapes.write[i].transform = shape_wrapper->transform;
+ shapes.write[i].transform.getOrigin() *= owner_scale;
switch (shape_wrapper->shape->get_type()) {
case PhysicsServer::SHAPE_SPHERE:
case PhysicsServer::SHAPE_BOX:
case PhysicsServer::SHAPE_CAPSULE:
+ case PhysicsServer::SHAPE_CYLINDER:
case PhysicsServer::SHAPE_CONVEX_POLYGON:
case PhysicsServer::SHAPE_RAY: {
- shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
+ shapes.write[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
} break;
default:
WARN_PRINT("This shape is not supported to be kinematic!");
- shapes[i].shape = NULL;
+ shapes.write[i].shape = NULL;
}
}
}
@@ -240,7 +249,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) {
for (int i = shapes.size() - 1; 0 <= i; --i) {
if (shapes[i].shape) {
- bulletdelete(shapes[i].shape);
+ bulletdelete(shapes.write[i].shape);
}
}
shapes.resize(new_size);
@@ -280,7 +289,7 @@ RigidBodyBullet::RigidBodyBullet() :
areasWhereIam.resize(maxAreasWhereIam);
for (int i = areasWhereIam.size() - 1; 0 <= i; --i) {
- areasWhereIam[i] = NULL;
+ areasWhereIam.write[i] = NULL;
}
btBody->setSleepingThresholds(0.2, 0.2);
}
@@ -400,17 +409,18 @@ void RigidBodyBullet::on_collision_checker_start() {
collisionsCount = 0;
}
-bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index) {
+bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) {
if (collisionsCount >= maxCollisionsDetection) {
return false;
}
- CollisionData &cd = collisions[collisionsCount];
+ CollisionData &cd = collisions.write[collisionsCount];
cd.hitLocalLocation = p_hitLocalLocation;
cd.otherObject = p_otherObject;
cd.hitWorldLocation = p_hitWorldLocation;
cd.hitNormal = p_hitNormal;
+ cd.appliedImpulse = p_appliedImpulse;
cd.other_object_shape = p_other_shape_index;
cd.local_shape = p_local_shape_index;
@@ -794,15 +804,15 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) {
if (NULL == areasWhereIam[i]) {
// This area has the highest priority
- areasWhereIam[i] = p_area;
+ areasWhereIam.write[i] = p_area;
break;
} else {
if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) {
// The position was found, just shift all elements
for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam[j + 1] = areasWhereIam[j];
+ areasWhereIam.write[j + 1] = areasWhereIam[j];
}
- areasWhereIam[i] = p_area;
+ areasWhereIam.write[i] = p_area;
break;
}
}
@@ -826,7 +836,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
if (p_area == areasWhereIam[i]) {
// The area was fount, just shift down all elements
for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam[j] = areasWhereIam[j + 1];
+ areasWhereIam.write[j] = areasWhereIam[j + 1];
}
wasTheAreaFound = true;
break;
@@ -839,7 +849,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
}
--areaWhereIamCount;
- areasWhereIam[areaWhereIamCount] = NULL; // Even if this is not required, I clear the last element to be safe
+ areasWhereIam.write[areaWhereIamCount] = NULL; // Even if this is not required, I clear the last element to be safe
if (PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) {
scratch_space_override_modificator();
}
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index b9511243c7..f03009bce9 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -113,6 +113,7 @@ public:
virtual void add_central_force(const Vector3 &p_force);
virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos);
virtual void add_torque(const Vector3 &p_torque);
+ virtual void apply_central_impulse(const Vector3 &p_impulse);
virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j);
virtual void apply_torque_impulse(const Vector3 &p_j);
@@ -123,6 +124,7 @@ public:
virtual Vector3 get_contact_local_position(int p_contact_idx) const;
virtual Vector3 get_contact_local_normal(int p_contact_idx) const;
+ virtual float get_contact_impulse(int p_contact_idx) const;
virtual int get_contact_local_shape(int p_contact_idx) const;
virtual RID get_contact_collider(int p_contact_idx) const;
@@ -149,6 +151,7 @@ public:
Vector3 hitLocalLocation;
Vector3 hitWorldLocation;
Vector3 hitNormal;
+ float appliedImpulse;
};
struct ForceIntegrationCallback {
@@ -248,7 +251,7 @@ public:
}
bool can_add_collision() { return collisionsCount < maxCollisionsDetection; }
- bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index);
+ bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index);
void assert_no_constraints();
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 76d9614465..cc7d106496 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -44,19 +44,20 @@
@author AndreaCatania
*/
-ShapeBullet::ShapeBullet() {}
+ShapeBullet::ShapeBullet() :
+ margin(0.04) {}
ShapeBullet::~ShapeBullet() {}
-btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 s;
G_TO_B(p_implicit_scale, s);
- return create_bt_shape(s, p_margin);
+ return create_bt_shape(s, p_extra_edge);
}
btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
p_btShape->setUserPointer(const_cast<ShapeBullet *>(this));
- p_btShape->setMargin(0.);
+ p_btShape->setMargin(margin);
return p_btShape;
}
@@ -93,6 +94,15 @@ const Map<ShapeOwnerBullet *, int> &ShapeBullet::get_owners() const {
return owners;
}
+void ShapeBullet::set_margin(real_t p_margin) {
+ margin = p_margin;
+ notifyShapeChanged();
+}
+
+real_t ShapeBullet::get_margin() const {
+ return margin;
+}
+
btEmptyShape *ShapeBullet::create_shape_empty() {
return bulletnew(btEmptyShape);
}
@@ -113,6 +123,10 @@ btCapsuleShapeZ *ShapeBullet::create_shape_capsule(btScalar radius, btScalar hei
return bulletnew(btCapsuleShapeZ(radius, height));
}
+btCylinderShape *ShapeBullet::create_shape_cylinder(btScalar radius, btScalar height) {
+ return bulletnew(btCylinderShape(btVector3(radius, height / 2.0, radius)));
+}
+
btConvexPointCloudShape *ShapeBullet::create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling) {
return bulletnew(btConvexPointCloudShape(&p_vertices[0], p_vertices.size(), p_local_scaling));
}
@@ -162,7 +176,7 @@ void PlaneShapeBullet::setup(const Plane &p_plane) {
notifyShapeChanged();
}
-btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 btPlaneNormal;
G_TO_B(plane.normal, btPlaneNormal);
return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
@@ -190,8 +204,8 @@ void SphereShapeBullet::setup(real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_margin));
+btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_extra_edge));
}
/* Box */
@@ -217,8 +231,8 @@ void BoxShapeBullet::setup(const Vector3 &p_half_extents) {
notifyShapeChanged();
}
-btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_margin, p_margin, p_margin)));
+btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_extra_edge, p_extra_edge, p_extra_edge)));
}
/* Capsule */
@@ -250,8 +264,41 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
+btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge));
+}
+
+/* Cylinder */
+
+CylinderShapeBullet::CylinderShapeBullet() :
+ ShapeBullet() {}
+
+void CylinderShapeBullet::set_data(const Variant &p_data) {
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("radius"));
+ ERR_FAIL_COND(!d.has("height"));
+ setup(d["height"], d["radius"]);
+}
+
+Variant CylinderShapeBullet::get_data() const {
+ Dictionary d;
+ d["radius"] = radius;
+ d["height"] = height;
+ return d;
+}
+
+PhysicsServer::ShapeType CylinderShapeBullet::get_type() const {
+ return PhysicsServer::SHAPE_CYLINDER;
+}
+
+void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) {
+ radius = p_radius;
+ height = p_height;
+ notifyShapeChanged();
+}
+
+btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+ return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
}
/* Convex polygon */
@@ -267,7 +314,7 @@ void ConvexPolygonShapeBullet::get_vertices(Vector<Vector3> &out_vertices) {
const int n_of_vertices = vertices.size();
out_vertices.resize(n_of_vertices);
for (int i = n_of_vertices - 1; 0 <= i; --i) {
- B_TO_G(vertices[i], out_vertices[i]);
+ B_TO_G(vertices[i], out_vertices.write[i]);
}
}
@@ -292,11 +339,13 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) {
notifyShapeChanged();
}
-btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ if (!vertices.size())
+ // This is necessary since 0 vertices
+ return prepare(ShapeBullet::create_shape_empty());
btCollisionShape *cs(ShapeBullet::create_shape_convex(vertices));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -365,14 +414,13 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) {
notifyShapeChanged();
}
-btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape);
if (!cs)
// This is necessary since if 0 faces the creation of concave return NULL
cs = ShapeBullet::create_shape_empty();
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -458,11 +506,10 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
notifyShapeChanged();
}
-btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -496,6 +543,6 @@ void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) {
notifyShapeChanged();
}
-btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin, slips_on_slope));
+btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_extra_edge, slips_on_slope));
}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index abeea0f9ce..638e044e6a 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -52,6 +52,7 @@ class btBvhTriangleMeshShape;
class ShapeBullet : public RIDBullet {
Map<ShapeOwnerBullet *, int> owners;
+ real_t margin;
protected:
/// return self
@@ -62,14 +63,17 @@ public:
ShapeBullet();
virtual ~ShapeBullet();
- btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_margin = 0);
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0) = 0;
+ btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
void add_owner(ShapeOwnerBullet *p_owner);
void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false);
bool is_owner(ShapeOwnerBullet *p_owner) const;
const Map<ShapeOwnerBullet *, int> &get_owners() const;
+ void set_margin(real_t p_margin);
+ real_t get_margin() const;
+
/// Setup the shape
virtual void set_data(const Variant &p_data) = 0;
virtual Variant get_data() const = 0;
@@ -82,6 +86,7 @@ public:
static class btSphereShape *create_shape_sphere(btScalar radius);
static class btBoxShape *create_shape_box(const btVector3 &boxHalfExtents);
static class btCapsuleShapeZ *create_shape_capsule(btScalar radius, btScalar height);
+ static class btCylinderShape *create_shape_cylinder(btScalar radius, btScalar height);
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
@@ -99,7 +104,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Plane &p_plane);
@@ -116,7 +121,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_radius);
@@ -133,7 +138,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector3 &p_half_extents);
@@ -152,6 +157,25 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+
+private:
+ void setup(real_t p_height, real_t p_radius);
+};
+
+class CylinderShapeBullet : public ShapeBullet {
+
+ real_t height;
+ real_t radius;
+
+public:
+ CylinderShapeBullet();
+
+ _FORCE_INLINE_ real_t get_height() { return height; }
+ _FORCE_INLINE_ real_t get_radius() { return radius; }
+ virtual void set_data(const Variant &p_data);
+ virtual Variant get_data() const;
+ virtual PhysicsServer::ShapeType get_type() const;
virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
@@ -169,7 +193,7 @@ public:
void get_vertices(Vector<Vector3> &out_vertices);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector<Vector3> &p_vertices);
@@ -187,7 +211,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(PoolVector<Vector3> p_faces);
@@ -207,7 +231,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
@@ -224,7 +248,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_length, bool p_slips_on_slope);
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 5c20eb73f1..9fc7230f91 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -32,42 +32,24 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "scene/3d/immediate_geometry.h"
+#include "scene/3d/soft_body.h"
#include "space_bullet.h"
-/**
- @author AndreaCatania
-*/
-
SoftBodyBullet::SoftBodyBullet() :
CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY),
- mass(1),
+ total_mass(1),
simulation_precision(5),
- stiffness(0.5f),
- pressure_coefficient(50),
- damping_coefficient(0.005),
- drag_coefficient(0.005),
+ linear_stiffness(0.5),
+ areaAngular_stiffness(0.5),
+ volume_stiffness(0.5),
+ pressure_coefficient(0.),
+ pose_matching_coefficient(0.),
+ damping_coefficient(0.01),
+ drag_coefficient(0.),
bt_soft_body(NULL),
- soft_shape_type(SOFT_SHAPETYPE_NONE),
- isScratched(false),
- soft_body_shape_data(NULL) {
-
- test_geometry = memnew(ImmediateGeometry);
-
- red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial));
- red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
- red_mat->set_line_width(20.0);
- red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- red_mat->set_albedo(Color(1, 0, 0, 1));
- test_geometry->set_material_override(red_mat);
-
- test_is_in_scene = false;
-}
+ isScratched(false) {}
SoftBodyBullet::~SoftBodyBullet() {
- bulletdelete(soft_body_shape_data);
}
void SoftBodyBullet::reload_body() {
@@ -80,8 +62,6 @@ void SoftBodyBullet::reload_body() {
void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
isScratched = false;
-
- // Remove this object from the physics world
space->remove_soft_body(this);
}
@@ -90,86 +70,181 @@ void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
space->add_soft_body(this);
}
-
- reload_soft_body();
}
-void SoftBodyBullet::dispatch_callbacks() {
- if (!bt_soft_body) {
+void SoftBodyBullet::dispatch_callbacks() {}
+
+void SoftBodyBullet::on_collision_filters_change() {}
+
+void SoftBodyBullet::on_collision_checker_start() {}
+
+void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {}
+
+void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_server_handler) {
+ if (!bt_soft_body)
return;
+
+ /// Update visual server vertices
+ const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes);
+ const int nodes_count = nodes.size();
+
+ const Vector<int> *vs_indices;
+ const void *vertex_position;
+ const void *vertex_normal;
+
+ for (int vertex_index = 0; vertex_index < nodes_count; ++vertex_index) {
+ vertex_position = reinterpret_cast<const void *>(&nodes[vertex_index].m_x);
+ vertex_normal = reinterpret_cast<const void *>(&nodes[vertex_index].m_n);
+
+ vs_indices = &indices_table[vertex_index];
+
+ const int vs_indices_size(vs_indices->size());
+ for (int x = 0; x < vs_indices_size; ++x) {
+ p_visual_server_handler->set_vertex((*vs_indices)[x], vertex_position);
+ p_visual_server_handler->set_normal((*vs_indices)[x], vertex_normal);
+ }
}
- if (!test_is_in_scene) {
- test_is_in_scene = true;
- SceneTree::get_singleton()->get_current_scene()->add_child(test_geometry);
+ /// Generate AABB
+ btVector3 aabb_min;
+ btVector3 aabb_max;
+ bt_soft_body->getAabb(aabb_min, aabb_max);
+
+ btVector3 size(aabb_max - aabb_min);
+
+ AABB aabb;
+ B_TO_G(aabb_min, aabb.position);
+ B_TO_G(size, aabb.size);
+
+ p_visual_server_handler->set_aabb(aabb);
+}
+
+void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) {
+
+ if (p_mesh.is_null())
+ soft_mesh.unref();
+ else
+ soft_mesh = p_mesh;
+
+ if (soft_mesh.is_null()) {
+
+ destroy_soft_body();
+ return;
}
- test_geometry->clear();
- test_geometry->begin(Mesh::PRIMITIVE_LINES, NULL);
- bool first = true;
- Vector3 pos;
- for (int i = 0; i < bt_soft_body->m_nodes.size(); ++i) {
- const btSoftBody::Node &n = bt_soft_body->m_nodes[i];
- B_TO_G(n.m_x, pos);
- test_geometry->add_vertex(pos);
- if (!first) {
- test_geometry->add_vertex(pos);
- } else {
- first = false;
- }
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & VS::ARRAY_FORMAT_INDEX));
+ set_trimesh_body_shape(arrays[VS::ARRAY_INDEX], arrays[VS::ARRAY_VERTEX]);
+}
+
+void SoftBodyBullet::destroy_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+
+ if (space) {
+ /// Remove from world before deletion
+ space->remove_soft_body(this);
}
- test_geometry->end();
+
+ destroyBulletCollisionObject();
+ bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::set_soft_transform(const Transform &p_transform) {
+ reset_all_node_positions();
+ move_all_nodes(p_transform);
}
-void SoftBodyBullet::on_collision_filters_change() {
+void SoftBodyBullet::move_all_nodes(const Transform &p_transform) {
+ if (!bt_soft_body)
+ return;
+ btTransform bt_transf;
+ G_TO_B(p_transform, bt_transf);
+ bt_soft_body->transform(bt_transf);
}
-void SoftBodyBullet::on_collision_checker_start() {
+void SoftBodyBullet::set_node_position(int p_node_index, const Vector3 &p_global_position) {
+ btVector3 bt_pos;
+ G_TO_B(p_global_position, bt_pos);
+ set_node_position(p_node_index, bt_pos);
}
-void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {
+void SoftBodyBullet::set_node_position(int p_node_index, const btVector3 &p_global_position) {
+ if (bt_soft_body) {
+ bt_soft_body->m_nodes[p_node_index].m_x = p_global_position;
+ }
}
-void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {
+void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) const {
+ if (bt_soft_body) {
+ B_TO_G(bt_soft_body->m_nodes[p_node_index].m_x, r_position);
+ }
}
-void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) {
+void SoftBodyBullet::get_node_offset(int p_node_index, Vector3 &r_offset) const {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vertices(arrays[VS::ARRAY_VERTEX]);
- TrimeshSoftShapeData *shape_data = bulletnew(TrimeshSoftShapeData);
- shape_data->m_triangles_indices = p_indices;
- shape_data->m_vertices = p_vertices;
- shape_data->m_triangles_num = p_triangles_num;
+ if (0 <= p_node_index && vertices.size() > p_node_index) {
+ r_offset = vertices[p_node_index];
+ }
+}
- set_body_shape_data(shape_data, SOFT_SHAPE_TYPE_TRIMESH);
- reload_soft_body();
+void SoftBodyBullet::get_node_offset(int p_node_index, btVector3 &r_offset) const {
+ Vector3 off;
+ get_node_offset(p_node_index, off);
+ G_TO_B(off, r_offset);
}
-void SoftBodyBullet::set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type) {
- bulletdelete(soft_body_shape_data);
- soft_body_shape_data = p_soft_shape_data;
- soft_shape_type = p_type;
+void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) {
+ if (0 >= p_mass) {
+ pin_node(node_index);
+ } else {
+ unpin_node(node_index);
+ }
+ if (bt_soft_body) {
+ bt_soft_body->setMass(node_index, p_mass);
+ }
}
-void SoftBodyBullet::set_transform(const Transform &p_transform) {
- transform = p_transform;
+btScalar SoftBodyBullet::get_node_mass(int node_index) const {
if (bt_soft_body) {
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- //bt_soft_body->transform(bt_trans);
+ return bt_soft_body->getMass(node_index);
+ } else {
+ return -1 == search_node_pinned(node_index) ? 1 : 0;
}
}
-const Transform &SoftBodyBullet::get_transform() const {
- return transform;
+void SoftBodyBullet::reset_all_node_mass() {
+ if (bt_soft_body) {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 1);
+ }
+ }
+ pinned_nodes.resize(0);
}
-void SoftBodyBullet::get_first_node_origin(btVector3 &p_out_origin) const {
- if (bt_soft_body && bt_soft_body->m_nodes.size()) {
- p_out_origin = bt_soft_body->m_nodes[0].m_x;
- } else {
- p_out_origin.setZero();
+void SoftBodyBullet::reset_all_node_positions() {
+ if (soft_mesh.is_null())
+ return;
+
+ Array arrays = soft_mesh->surface_get_arrays(0);
+ PoolVector<Vector3> vs_vertices(arrays[VS::ARRAY_VERTEX]);
+ PoolVector<Vector3>::Read vs_vertices_read = vs_vertices.read();
+
+ for (int vertex_index = bt_soft_body->m_nodes.size() - 1; 0 <= vertex_index; --vertex_index) {
+
+ G_TO_B(vs_vertices_read[indices_table[vertex_index][0]], bt_soft_body->m_nodes[vertex_index].m_x);
+
+ bt_soft_body->m_nodes[vertex_index].m_q = bt_soft_body->m_nodes[vertex_index].m_x;
+ bt_soft_body->m_nodes[vertex_index].m_v = btVector3(0, 0, 0);
+ bt_soft_body->m_nodes[vertex_index].m_f = btVector3(0, 0, 0);
}
}
@@ -181,22 +256,34 @@ void SoftBodyBullet::set_activation_state(bool p_active) {
}
}
-void SoftBodyBullet::set_mass(real_t p_val) {
+void SoftBodyBullet::set_total_mass(real_t p_val) {
if (0 >= p_val) {
p_val = 1;
}
- mass = p_val;
+ total_mass = p_val;
if (bt_soft_body) {
- bt_soft_body->setTotalMass(mass);
+ bt_soft_body->setTotalMass(total_mass);
}
}
-void SoftBodyBullet::set_stiffness(real_t p_val) {
- stiffness = p_val;
+void SoftBodyBullet::set_linear_stiffness(real_t p_val) {
+ linear_stiffness = p_val;
if (bt_soft_body) {
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ mat0->m_kLST = linear_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_areaAngular_stiffness(real_t p_val) {
+ areaAngular_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kAST = areaAngular_stiffness;
+ }
+}
+
+void SoftBodyBullet::set_volume_stiffness(real_t p_val) {
+ volume_stiffness = p_val;
+ if (bt_soft_body) {
+ mat0->m_kVST = volume_stiffness;
}
}
@@ -204,6 +291,9 @@ void SoftBodyBullet::set_simulation_precision(int p_val) {
simulation_precision = p_val;
if (bt_soft_body) {
bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
}
}
@@ -214,6 +304,13 @@ void SoftBodyBullet::set_pressure_coefficient(real_t p_val) {
}
}
+void SoftBodyBullet::set_pose_matching_coefficient(real_t p_val) {
+ pose_matching_coefficient = p_val;
+ if (bt_soft_body) {
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ }
+}
+
void SoftBodyBullet::set_damping_coefficient(real_t p_val) {
damping_coefficient = p_val;
if (bt_soft_body) {
@@ -228,89 +325,156 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) {
}
}
-void SoftBodyBullet::reload_soft_body() {
-
+void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices) {
+ /// Assert the current soft body is destroyed
destroy_soft_body();
- create_soft_body();
- if (bt_soft_body) {
+ /// Parse visual server indices to physical indices.
+ /// Merge all overlapping vertices and create a map of physical vertices to visual server
- // TODO the softbody set new transform considering the current transform as center of world
- // like if it's local transform, so I must fix this by setting nwe transform considering the old
- btTransform bt_trans;
- G_TO_B(transform, bt_trans);
- bt_soft_body->transform(bt_trans);
+ {
+ /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
+ Vector<int> vs_indices_to_physics_table;
- bt_soft_body->generateBendingConstraints(2, mat0);
- mat0->m_kAST = stiffness;
- mat0->m_kLST = stiffness;
- mat0->m_kVST = stiffness;
+ { // Map vertices
+ indices_table.resize(0);
- bt_soft_body->m_cfg.piterations = simulation_precision;
- bt_soft_body->m_cfg.kDP = damping_coefficient;
- bt_soft_body->m_cfg.kDG = drag_coefficient;
- bt_soft_body->m_cfg.kPR = pressure_coefficient;
- bt_soft_body->setTotalMass(mass);
- }
- if (space) {
- // TODO remove this please
- space->add_soft_body(this);
- }
-}
+ int index = 0;
+ Map<Vector3, int> unique_vertices;
-void SoftBodyBullet::create_soft_body() {
- if (!space || !soft_body_shape_data) {
- return;
- }
- ERR_FAIL_COND(!space->is_using_soft_world());
- switch (soft_shape_type) {
- case SOFT_SHAPE_TYPE_TRIMESH: {
- TrimeshSoftShapeData *trimesh_data = static_cast<TrimeshSoftShapeData *>(soft_body_shape_data);
-
- Vector<int> indices;
- Vector<btScalar> vertices;
-
- int i;
- const int indices_size = trimesh_data->m_triangles_indices.size();
- const int vertices_size = trimesh_data->m_vertices.size();
- indices.resize(indices_size);
- vertices.resize(vertices_size * 3);
-
- PoolVector<int>::Read i_r = trimesh_data->m_triangles_indices.read();
- for (i = 0; i < indices_size; ++i) {
- indices[i] = i_r[i];
+ const int vs_vertices_size(p_vertices.size());
+
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int vs_vertex_index = 0; vs_vertex_index < vs_vertices_size; ++vs_vertex_index) {
+
+ Map<Vector3, int>::Element *e = unique_vertices.find(p_vertices_read[vs_vertex_index]);
+ int vertex_id;
+ if (e) {
+ // Already rxisting
+ vertex_id = e->value();
+ } else {
+ // Create new one
+ unique_vertices[p_vertices_read[vs_vertex_index]] = vertex_id = index++;
+ indices_table.push_back(Vector<int>());
+ }
+
+ indices_table.write[vertex_id].push_back(vs_vertex_index);
+ vs_indices_to_physics_table.push_back(vertex_id);
+ }
+ }
+
+ const int indices_map_size(indices_table.size());
+
+ Vector<btScalar> bt_vertices;
+
+ { // Parse vertices to bullet
+
+ bt_vertices.resize(indices_map_size * 3);
+ PoolVector<Vector3>::Read p_vertices_read = p_vertices.read();
+
+ for (int i = 0; i < indices_map_size; ++i) {
+ bt_vertices.write[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
+ bt_vertices.write[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
+ bt_vertices.write[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
}
- i_r = PoolVector<int>::Read();
+ }
+
+ Vector<int> bt_triangles;
+ const int triangles_size(p_indices.size() / 3);
+
+ { // Parse indices
+
+ bt_triangles.resize(triangles_size * 3);
+
+ PoolVector<int>::Read p_indices_read = p_indices.read();
- PoolVector<Vector3>::Read f_r = trimesh_data->m_vertices.read();
- for (int j = i = 0; i < vertices_size; ++i, j += 3) {
- vertices[j + 0] = f_r[i][0];
- vertices[j + 1] = f_r[i][1];
- vertices[j + 2] = f_r[i][2];
+ for (int i = 0; i < triangles_size; ++i) {
+ bt_triangles.write[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
+ bt_triangles.write[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
+ bt_triangles.write[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
}
- f_r = PoolVector<Vector3>::Read();
+ }
- bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(*space->get_soft_body_world_info(), vertices.ptr(), indices.ptr(), trimesh_data->m_triangles_num);
- } break;
- default:
- ERR_PRINT("Shape type not supported");
- return;
+ btSoftBodyWorldInfo fake_world_info;
+ bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false);
+ setup_soft_body();
}
+}
+
+void SoftBodyBullet::setup_soft_body() {
+
+ if (!bt_soft_body)
+ return;
+ // Soft body setup
setupBulletCollisionObject(bt_soft_body);
- bt_soft_body->getCollisionShape()->setMargin(0.001f);
+ bt_soft_body->m_worldInfo = NULL; // Remove fake world info
+ bt_soft_body->getCollisionShape()->setMargin(0.01);
bt_soft_body->setCollisionFlags(bt_soft_body->getCollisionFlags() & (~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)));
+
+ // Space setup
+ if (space) {
+ space->add_soft_body(this);
+ }
+
mat0 = bt_soft_body->appendMaterial();
+
+ // Assign soft body data
+ bt_soft_body->generateBendingConstraints(2, mat0);
+
+ mat0->m_kLST = linear_stiffness;
+ mat0->m_kAST = areaAngular_stiffness;
+ mat0->m_kVST = volume_stiffness;
+
+ // Clusters allow to have Soft vs Soft collision but doesn't work well right now
+
+ //bt_soft_body->m_cfg.kSRHR_CL = 1;// Soft vs rigid hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSKHR_CL = 1;// Soft vs kinematic hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSSHR_CL = 1;// Soft vs soft hardness [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSR_SPLT_CL = 1; // Soft vs rigid impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSK_SPLT_CL = 1; // Soft vs kinematic impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.kSS_SPLT_CL = 1; // Soft vs Soft impulse split [0,1] (cluster only)
+ //bt_soft_body->m_cfg.collisions = btSoftBody::fCollision::CL_SS + btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::VF_SS;
+ //bt_soft_body->generateClusters(64);
+
+ bt_soft_body->m_cfg.piterations = simulation_precision;
+ bt_soft_body->m_cfg.viterations = simulation_precision;
+ bt_soft_body->m_cfg.diterations = simulation_precision;
+ bt_soft_body->m_cfg.citerations = simulation_precision;
+ bt_soft_body->m_cfg.kDP = damping_coefficient;
+ bt_soft_body->m_cfg.kDG = drag_coefficient;
+ bt_soft_body->m_cfg.kPR = pressure_coefficient;
+ bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
+ bt_soft_body->setTotalMass(total_mass);
+
+ btSoftBodyHelpers::ReoptimizeLinkOrder(bt_soft_body);
+ bt_soft_body->updateBounds();
+
+ // Set pinned nodes
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ bt_soft_body->setMass(pinned_nodes[i], 0);
+ }
}
-void SoftBodyBullet::destroy_soft_body() {
- if (space) {
- /// This step is required to assert that the body is not into the world during deletion
- /// This step is required since to change the body shape the body must be re-created.
- /// Here is handled the case when the body is assigned into a world and the body
- /// shape is changed.
- space->remove_soft_body(this);
+void SoftBodyBullet::pin_node(int p_node_index) {
+ if (-1 == search_node_pinned(p_node_index)) {
+ pinned_nodes.push_back(p_node_index);
}
- destroyBulletCollisionObject();
- bt_soft_body = NULL;
+}
+
+void SoftBodyBullet::unpin_node(int p_node_index) {
+ const int id = search_node_pinned(p_node_index);
+ if (-1 != id) {
+ pinned_nodes.remove(id);
+ }
+}
+
+int SoftBodyBullet::search_node_pinned(int p_node_index) const {
+ for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
+ if (p_node_index == pinned_nodes[i]) {
+ return i;
+ }
+ }
+ return -1;
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index 9895643b84..c775193584 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -40,7 +40,10 @@
#define x11_None 0L
#endif
-#include <BulletSoftBody/btSoftBodyHelpers.h>
+#include "BulletSoftBody/btSoftBodyHelpers.h"
+#include "collision_object_bullet.h"
+#include "scene/resources/mesh.h"
+#include "servers/physics_server.h"
#ifdef x11_None
/// This is required to re add the macro None defined by x11 compiler
@@ -52,39 +55,34 @@
@author AndreaCatania
*/
-struct SoftShapeData {};
-struct TrimeshSoftShapeData : public SoftShapeData {
- PoolVector<int> m_triangles_indices;
- PoolVector<Vector3> m_vertices;
- int m_triangles_num;
-};
-
class SoftBodyBullet : public CollisionObjectBullet {
-public:
- enum SoftShapeType {
- SOFT_SHAPETYPE_NONE = 0,
- SOFT_SHAPE_TYPE_TRIMESH
- };
private:
btSoftBody *bt_soft_body;
+ Vector<Vector<int> > indices_table;
btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
- SoftShapeType soft_shape_type;
bool isScratched;
- SoftShapeData *soft_body_shape_data;
+ Ref<Mesh> soft_mesh;
- Transform transform;
int simulation_precision;
- real_t mass;
- real_t stiffness; // [0,1]
+ real_t total_mass;
+ real_t linear_stiffness; // [0,1]
+ real_t areaAngular_stiffness; // [0,1]
+ real_t volume_stiffness; // [0,1]
real_t pressure_coefficient; // [-inf,+inf]
+ real_t pose_matching_coefficient; // [0,1]
real_t damping_coefficient; // [0,1]
real_t drag_coefficient; // [0,1]
+ Vector<int> pinned_nodes;
- class ImmediateGeometry *test_geometry; // TODO remove this please
- Ref<SpatialMaterial> red_mat; // TODO remove this please
- bool test_is_in_scene; // TODO remove this please
+ // Other property to add
+ //btScalar kVC; // Volume conversation coefficient [0,+inf]
+ //btScalar kDF; // Dynamic friction coefficient [0,1]
+ //btScalar kMT; // Pose matching coefficient [0,1]
+ //btScalar kCHR; // Rigid contacts hardness [0,1]
+ //btScalar kKHR; // Kinetic contacts hardness [0,1]
+ //btScalar kSHR; // Soft contacts hardness [0,1]
public:
SoftBodyBullet();
@@ -101,39 +99,64 @@ public:
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
- void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num);
- void set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type);
+ void update_visual_server(class SoftBodyVisualServerHandler *p_visual_server_handler);
- void set_transform(const Transform &p_transform);
- /// This function doesn't return the exact COM transform.
- /// It returns the origin only of first node (vertice) of current soft body
- /// ---
- /// The soft body doesn't have a fixed center of mass, but is a group of nodes (vertices)
- /// that each has its own position in the world.
- /// For this reason return the correct COM is not so simple and must be calculate
- /// Check this to improve this function http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=8803
- const Transform &get_transform() const;
- void get_first_node_origin(btVector3 &p_out_origin) const;
+ void set_soft_mesh(const Ref<Mesh> &p_mesh);
+ void destroy_soft_body();
+
+ // Special function. This function has bad performance
+ void set_soft_transform(const Transform &p_transform);
+
+ void move_all_nodes(const Transform &p_transform);
+ void set_node_position(int node_index, const Vector3 &p_global_position);
+ void set_node_position(int node_index, const btVector3 &p_global_position);
+ void get_node_position(int node_index, Vector3 &r_position) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, Vector3 &r_offset) const;
+ // Heavy function, Please cache this info
+ void get_node_offset(int node_index, btVector3 &r_offset) const;
+
+ void set_node_mass(int node_index, btScalar p_mass);
+ btScalar get_node_mass(int node_index) const;
+ void reset_all_node_mass();
+ void reset_all_node_positions();
void set_activation_state(bool p_active);
- void set_mass(real_t p_val);
- _FORCE_INLINE_ real_t get_mass() const { return mass; }
- void set_stiffness(real_t p_val);
- _FORCE_INLINE_ real_t get_stiffness() const { return stiffness; }
+ void set_total_mass(real_t p_val);
+ _FORCE_INLINE_ real_t get_total_mass() const { return total_mass; }
+
+ void set_linear_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_linear_stiffness() const { return linear_stiffness; }
+
+ void set_areaAngular_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_areaAngular_stiffness() const { return areaAngular_stiffness; }
+
+ void set_volume_stiffness(real_t p_val);
+ _FORCE_INLINE_ real_t get_volume_stiffness() const { return volume_stiffness; }
+
void set_simulation_precision(int p_val);
_FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; }
+
void set_pressure_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; }
+
+ void set_pose_matching_coefficient(real_t p_val);
+ _FORCE_INLINE_ real_t get_pose_matching_coefficient() const { return pose_matching_coefficient; }
+
void set_damping_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; }
+
void set_drag_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; }
private:
- void reload_soft_body();
- void create_soft_body();
- void destroy_soft_body();
+ void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices);
+ void setup_soft_body();
+
+ void pin_node(int p_node_index);
+ void unpin_node(int p_node_index);
+ int search_node_pinned(int p_node_index) const;
};
#endif // SOFT_BODY_BULLET_H
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 971fd39509..e8e6fc4d07 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -36,6 +36,7 @@
#include "constraint_bullet.h"
#include "godot_collision_configuration.h"
#include "godot_collision_dispatcher.h"
+#include "project_settings.h"
#include "rigid_body_bullet.h"
#include "servers/physics_server.h"
#include "soft_body_bullet.h"
@@ -60,7 +61,7 @@ BulletPhysicsDirectSpaceState::BulletPhysicsDirectSpaceState(SpaceBullet *p_spac
PhysicsDirectSpaceState(),
space(p_space) {}
-int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -68,13 +69,13 @@ int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, Shape
btVector3 bt_point;
G_TO_B(p_point, bt_point);
- btSphereShape sphere_point(0.f);
+ btSphereShape sphere_point(0.001f);
btCollisionObject collision_object_point;
collision_object_point.setCollisionShape(&sphere_point);
collision_object_point.setWorldTransform(btTransform(btQuaternion::getIdentity(), bt_point));
// Setup query
- GodotAllContactResultCallback btResult(&collision_object_point, r_results, p_result_max, &p_exclude);
+ GodotAllContactResultCallback btResult(&collision_object_point, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
space->dynamicsWorld->contactTest(&collision_object_point, btResult);
@@ -83,7 +84,7 @@ int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, Shape
return btResult.m_count;
}
-bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_pick_ray) {
+bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_ray) {
btVector3 btVec_from;
btVector3 btVec_to;
@@ -92,7 +93,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
G_TO_B(p_to, btVec_to);
// setup query
- GodotClosestRayResultCallback btResult(btVec_from, btVec_to, &p_exclude);
+ GodotClosestRayResultCallback btResult(btVec_from, btVec_to, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
btResult.m_pickRay = p_pick_ray;
@@ -116,7 +117,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
}
}
-int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -138,7 +139,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
+ GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -149,7 +150,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, ShapeRestInfo *r_info) {
+bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
@@ -170,14 +171,16 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
btTransform bt_xform_to(bt_xform_from);
bt_xform_to.getOrigin() += bt_motion;
- GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude);
+ GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, 0.002);
if (btResult.hasHit()) {
- p_closest_safe = p_closest_unsafe = btResult.m_closestHitFraction;
+ const btScalar l = bt_motion.length();
+ p_closest_unsafe = btResult.m_closestHitFraction;
+ p_closest_safe = MAX(p_closest_unsafe - (1 - ((l - 0.01) / l)), 0);
if (r_info) {
if (btCollisionObject::CO_RIGID_BODY == btResult.m_hitCollisionObject->getInternalType()) {
B_TO_G(static_cast<const btRigidBody *>(btResult.m_hitCollisionObject)->getVelocityInLocalPoint(btResult.m_hitPointWorld), r_info->linear_velocity);
@@ -196,7 +199,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
}
/// Returns the list of contacts pairs in this order: Local contact, other body contact
-bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -218,7 +221,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotContactPairContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
+ GodotContactPairContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -230,7 +233,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
@@ -250,7 +253,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotRestInfoContactResultCallback btQuery(&collision_object, r_info, &p_exclude);
+ GodotRestInfoContactResultCallback btQuery(&collision_object, r_info, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -325,7 +328,7 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_
}
}
-SpaceBullet::SpaceBullet(bool p_create_soft_world) :
+SpaceBullet::SpaceBullet() :
broadphase(NULL),
dispatcher(NULL),
solver(NULL),
@@ -338,7 +341,7 @@ SpaceBullet::SpaceBullet(bool p_create_soft_world) :
gravityMagnitude(10),
contactDebugCount(0) {
- create_empty_world(p_create_soft_world);
+ create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true));
direct_access = memnew(BulletPhysicsDirectSpaceState(this));
}
@@ -355,6 +358,7 @@ void SpaceBullet::flush_queries() {
}
void SpaceBullet::step(real_t p_delta_time) {
+ delta_time = p_delta_time;
dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
}
@@ -483,6 +487,7 @@ void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
void SpaceBullet::add_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
+ p_body->get_bt_soft_body()->m_worldInfo = get_soft_body_world_info();
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->addSoftBody(p_body->get_bt_soft_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
}
} else {
@@ -494,6 +499,7 @@ void SpaceBullet::remove_soft_body(SoftBodyBullet *p_body) {
if (is_using_soft_world()) {
if (p_body->get_bt_soft_body()) {
static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->removeSoftBody(p_body->get_bt_soft_body());
+ p_body->get_bt_soft_body()->m_worldInfo = NULL;
}
}
}
@@ -549,7 +555,13 @@ BulletPhysicsDirectSpaceState *SpaceBullet::get_direct_state() {
}
btScalar calculateGodotCombinedRestitution(const btCollisionObject *body0, const btCollisionObject *body1) {
- return MAX(body0->getRestitution(), body1->getRestitution());
+
+ return CLAMP(body0->getRestitution() + body1->getRestitution(), 0, 1);
+}
+
+btScalar calculateGodotCombinedFriction(const btCollisionObject *body0, const btCollisionObject *body1) {
+
+ return ABS(MIN(body0->getFriction(), body1->getFriction()));
}
void SpaceBullet::create_empty_world(bool p_create_soft_world) {
@@ -585,6 +597,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) {
ghostPairCallback = bulletnew(btGhostPairCallback);
godotFilterCallback = bulletnew(GodotFilterCallback);
gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution;
+ gCalculateCombinedFrictionCallback = &calculateGodotCombinedFriction;
dynamicsWorld->setWorldUserInfo(this);
@@ -629,7 +642,7 @@ void SpaceBullet::destroy_world() {
void SpaceBullet::check_ghost_overlaps() {
/// Algorithm support variables
- btConvexShape *other_body_shape;
+ btCollisionShape *other_body_shape;
btConvexShape *area_shape;
btGjkPairDetector::ClosestPointInput gjk_input;
AreaBullet *area;
@@ -645,7 +658,7 @@ void SpaceBullet::check_ghost_overlaps() {
/// 1. Reset all states
for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
- AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i];
+ AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i];
// This check prevent the overwrite of ENTER state
// if this function is called more times before dispatchCallbacks
if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) {
@@ -681,20 +694,42 @@ void SpaceBullet::check_ghost_overlaps() {
// For each other object shape
for (z = otherObject->get_compound_shape()->getNumChildShapes() - 1; 0 <= z; --z) {
- if (!otherObject->get_compound_shape()->getChildShape(z)->isConvex())
- continue;
-
- other_body_shape = static_cast<btConvexShape *>(otherObject->get_compound_shape()->getChildShape(z));
+ other_body_shape = static_cast<btCollisionShape *>(otherObject->get_compound_shape()->getChildShape(z));
gjk_input.m_transformB = otherObject->get_transform__bullet() * otherObject->get_compound_shape()->getChildTransform(z);
- btPointCollector result;
- btGjkPairDetector gjk_pair_detector(area_shape, other_body_shape, gjk_simplex_solver, gjk_epa_pen_solver);
- gjk_pair_detector.getClosestPoints(gjk_input, result, 0);
+ if (other_body_shape->isConvex()) {
+
+ btPointCollector result;
+ btGjkPairDetector gjk_pair_detector(area_shape, static_cast<btConvexShape *>(other_body_shape), gjk_simplex_solver, gjk_epa_pen_solver);
+ gjk_pair_detector.getClosestPoints(gjk_input, result, 0);
+
+ if (0 >= result.m_distance) {
+ hasOverlap = true;
+ goto collision_found;
+ }
+
+ } else {
+
+ btCollisionObjectWrapper obA(NULL, area_shape, area->get_bt_ghost(), gjk_input.m_transformA, -1, y);
+ btCollisionObjectWrapper obB(NULL, other_body_shape, otherObject->get_bt_collision_object(), gjk_input.m_transformB, -1, z);
+
+ btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS);
+
+ if (!algorithm)
+ continue;
- if (0 >= result.m_distance) {
- hasOverlap = true;
- goto collision_found;
+ GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB);
+ algorithm->processCollision(&obA, &obB, dynamicsWorld->getDispatchInfo(), &contactPointResult);
+
+ algorithm->~btCollisionAlgorithm();
+ dispatcher->freeCollisionAlgorithm(algorithm);
+
+ if (contactPointResult.hasHit()) {
+ hasOverlap = true;
+ goto collision_found;
+ }
}
+
} // ~For each other object shape
} // ~For each area shape
@@ -754,19 +789,20 @@ void SpaceBullet::check_body_collision() {
Vector3 collisionWorldPosition;
Vector3 collisionLocalPosition;
Vector3 normalOnB;
+ float appliedImpulse = pt.m_appliedImpulse;
B_TO_G(pt.m_normalWorldOnB, normalOnB);
if (bodyA->can_add_collision()) {
B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition);
/// pt.m_localPointB Doesn't report the exact point in local space
B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition);
- bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, pt.m_index1, pt.m_index0);
+ bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0);
}
if (bodyB->can_add_collision()) {
B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition);
/// pt.m_localPointA Doesn't report the exact point in local space
B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition);
- bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, pt.m_index0, pt.m_index1);
+ bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1);
}
#ifdef DEBUG_ENABLED
@@ -807,7 +843,7 @@ static Ref<SpatialMaterial> red_mat;
static Ref<SpatialMaterial> blue_mat;
#endif
-bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result) {
+bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes) {
#if debug_test_motion
/// Yes I know this is not good, but I've used it as fast debugging hack.
@@ -882,6 +918,12 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
// Skip no convex shape
continue;
}
+
+ if (p_exclude_raycast_shapes && p_body->get_bt_shape(shIndex)->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) {
+ // Skip rayshape in order to implement custom separation process
+ continue;
+ }
+
btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex)));
btTransform shape_world_from = body_transform * p_body->get_kinematic_utilities()->shapes[shIndex].transform;
@@ -912,11 +954,11 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
btVector3 __rec(0, 0, 0);
RecoverResult r_recover_result;
- has_penetration = recover_from_penetration(p_body, body_transform, 0, p_infinite_inertia, __rec, &r_recover_result);
+ has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result);
// Parse results
if (r_result) {
- B_TO_G(motion + initial_recover_motion, r_result->motion);
+ B_TO_G(motion + initial_recover_motion + __rec, r_result->motion);
if (has_penetration) {
@@ -952,6 +994,39 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
return has_penetration;
}
+int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, float p_margin) {
+
+ btTransform body_transform;
+ G_TO_B(p_transform, body_transform);
+ UNSCALE_BT_BASIS(body_transform);
+
+ btVector3 recover_motion(0, 0, 0);
+
+ int rays_found = 0;
+
+ for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
+ int last_ray_index = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max, recover_motion, r_results);
+
+ rays_found = MAX(last_ray_index, rays_found);
+ if (!rays_found) {
+ break;
+ } else {
+ body_transform.getOrigin() += recover_motion;
+ }
+ }
+
+ //optimize results (remove non colliding)
+ for (int i = 0; i < rays_found; i++) {
+ if (r_results[i].collision_depth >= 0) {
+ rays_found--;
+ SWAP(r_results[i], r_results[rays_found]);
+ }
+ }
+
+ B_TO_G(recover_motion, r_recover_motion);
+ return rays_found;
+}
+
struct RecoverPenetrationBroadPhaseCallback : public btBroadphaseAabbCallback {
private:
const btCollisionObject *self_collision_object;
@@ -1008,6 +1083,11 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
continue;
}
+ if (kin_shape.shape->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) {
+ // Skip rayshape in order to implement custom separation process
+ continue;
+ }
+
body_shape_position = p_body_position * kin_shape.transform;
body_shape_position_recovered = body_shape_position;
body_shape_position_recovered.getOrigin() += r_delta_recover_movement;
@@ -1110,7 +1190,6 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
if (contactPointResult.hasHit()) {
r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1 * p_recover_movement_scale);
-
if (r_recover_result) {
if (contactPointResult.m_penetration_distance < r_recover_result->penetration_distance) {
r_recover_result->hasPenetration = true;
@@ -1126,3 +1205,79 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
}
return false;
}
+
+int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) {
+
+ RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask());
+
+ btTransform body_shape_position;
+ btTransform body_shape_position_recovered;
+
+ // Broad phase support
+ btVector3 minAabb, maxAabb;
+
+ int ray_index = 0;
+
+ // For each shape
+ for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
+
+ recover_broad_result.reset();
+
+ if (ray_index >= p_result_max) {
+ break;
+ }
+
+ const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]);
+ if (!kin_shape.is_active()) {
+ continue;
+ }
+
+ if (kin_shape.shape->getShapeType() != CUSTOM_CONVEX_SHAPE_TYPE) {
+ continue;
+ }
+
+ body_shape_position = p_body_position * kin_shape.transform;
+ body_shape_position_recovered = body_shape_position;
+ body_shape_position_recovered.getOrigin() += r_delta_recover_movement;
+
+ kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb);
+ dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result);
+
+ for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
+ btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
+ if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
+ otherObject->activate(); // Force activation of hitten rigid, soft body
+ continue;
+ } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
+ continue;
+
+ if (otherObject->getCollisionShape()->isCompound()) {
+
+ // Each convex shape
+ btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape());
+ for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) {
+
+ RecoverResult r_recover_result;
+ if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, &r_recover_result)) {
+
+ const btRigidBody *btRigid = static_cast<const btRigidBody *>(otherObject);
+ CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(otherObject->getUserPointer());
+
+ r_results[ray_index].collision_depth = r_recover_result.penetration_distance;
+ B_TO_G(r_recover_result.pointWorld, r_results[ray_index].collision_point);
+ B_TO_G(r_recover_result.normal, r_results[ray_index].collision_normal);
+ B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_results[ray_index].collider_velocity);
+ r_results[ray_index].collision_local_shape = kinIndex;
+ r_results[ray_index].collider_id = collisionObject->get_instance_id();
+ r_results[ray_index].collider = collisionObject->get_self();
+ r_results[ray_index].collider_shape = r_recover_result.other_compound_shape_index;
+ }
+ }
+ }
+ }
+
+ ++ray_index;
+ }
+
+ return ray_index;
+}
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index a6c2786878..517ec67ffa 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -73,18 +73,18 @@ private:
public:
BulletPhysicsDirectSpaceState(SpaceBullet *p_space);
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_pick_ray = false);
- virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, ShapeRestInfo *r_info = NULL);
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false);
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL);
/// Returns the list of contacts pairs in this order: Local contact, other body contact
- virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const;
};
class SpaceBullet : public RIDBullet {
-private:
+
friend class AreaBullet;
friend void onBulletTickCallback(btDynamicsWorld *world, btScalar timeStep);
friend class BulletPhysicsDirectSpaceState;
@@ -109,12 +109,14 @@ private:
Vector<Vector3> contactDebug;
int contactDebugCount;
+ real_t delta_time;
public:
- SpaceBullet(bool p_create_soft_world);
+ SpaceBullet();
virtual ~SpaceBullet();
void flush_queries();
+ real_t get_delta_time() { return delta_time; }
void step(real_t p_delta_time);
_FORCE_INLINE_ btBroadphaseInterface *get_broadphase() { return broadphase; }
@@ -162,7 +164,7 @@ public:
contactDebugCount = 0;
}
_FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) {
- if (contactDebugCount < contactDebug.size()) contactDebug[contactDebugCount++] = p_contact;
+ if (contactDebugCount < contactDebug.size()) contactDebug.write[contactDebugCount++] = p_contact;
}
_FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; }
_FORCE_INLINE_ int get_debug_contact_count() { return contactDebugCount; }
@@ -172,7 +174,8 @@ public:
void update_gravity();
- bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result);
+ bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes);
+ int test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, float p_margin);
private:
void create_empty_world(bool p_create_soft_world);
@@ -206,5 +209,7 @@ private:
/// This is an API that recover a kinematic object from penetration
/// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm
bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
+
+ int recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results);
};
#endif
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 4e6e701bfd..87c2caec0d 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -62,7 +62,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo
faces.resize(p_vertices.size() / 3);
for (int i = 0; i < faces.size(); i++) {
- Face &f = faces[i];
+ Face &f = faces.write[i];
f.vertices[0] = rv[i * 3 + 0];
f.vertices[1] = rv[i * 3 + 1];
f.vertices[2] = rv[i * 3 + 2];
@@ -101,7 +101,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo
materials.resize(material_map.size());
for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) {
- materials[E->get()] = E->key();
+ materials.write[E->get()] = E->key();
}
_regen_face_aabbs();
@@ -111,10 +111,10 @@ void CSGBrush::_regen_face_aabbs() {
for (int i = 0; i < faces.size(); i++) {
- faces[i].aabb.position = faces[i].vertices[0];
- faces[i].aabb.expand_to(faces[i].vertices[1]);
- faces[i].aabb.expand_to(faces[i].vertices[2]);
- faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros
+ faces.write[i].aabb.position = faces[i].vertices[0];
+ faces.write[i].aabb.expand_to(faces[i].vertices[1]);
+ faces.write[i].aabb.expand_to(faces[i].vertices[2]);
+ faces.write[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros
}
}
@@ -125,7 +125,7 @@ void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
for (int i = 0; i < faces.size(); i++) {
for (int j = 0; j < 3; j++) {
- faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]);
+ faces.write[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]);
}
}
@@ -341,7 +341,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_
new_edge.points[0] = edges[i].points[0];
new_edge.points[1] = point_idx;
new_edge.outer = edges[i].outer;
- edges[i].points[0] = point_idx;
+ edges.write[i].points[0] = point_idx;
edges.insert(i, new_edge);
i++; //skip newly inserted edge
base_edges++; //will need an extra one in the base triangle
@@ -637,7 +637,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in
int to_point = e.edge_point;
int current_edge = e.edge;
- edge_process[e.edge] = true; //mark as processed
+ edge_process.write[e.edge] = true; //mark as processed
int limit = p_poly.points.size() * 4; //avoid infinite recursion
@@ -708,7 +708,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in
prev_point = to_point;
to_point = next_point;
- edge_process[next_edge] = true; //mark this edge as processed
+ edge_process.write[next_edge] = true; //mark this edge as processed
current_edge = next_edge;
limit--;
@@ -792,13 +792,13 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build
//none processed by default
for (int i = 0; i < edge_process.size(); i++) {
- edge_process[i] = false;
+ edge_process.write[i] = false;
}
//put edges in points, so points can go through them
for (int i = 0; i < p_poly.edges.size(); i++) {
- vertex_process[p_poly.edges[i].points[0]].push_back(i);
- vertex_process[p_poly.edges[i].points[1]].push_back(i);
+ vertex_process.write[p_poly.edges[i].points[0]].push_back(i);
+ vertex_process.write[p_poly.edges[i].points[1]].push_back(i);
}
Vector<PolyPoints> polys;
@@ -854,7 +854,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build
_add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline);
if (outline.size() > 1) {
- polys[intersect_poly].holes.push_back(outline);
+ polys.write[intersect_poly].holes.push_back(outline);
}
}
_add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys);
@@ -953,18 +953,18 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build
//duplicate point
int insert_at = with_outline_vertex;
- polys[i].points.insert(insert_at, polys[i].points[insert_at]);
+ polys.write[i].points.insert(insert_at, polys[i].points[insert_at]);
insert_at++;
//insert all others, outline should be backwards (must check)
int holesize = polys[i].holes[j].size();
for (int k = 0; k <= holesize; k++) {
int idx = (from_hole_vertex + k) % holesize;
- polys[i].points.insert(insert_at, polys[i].holes[j][idx]);
+ polys.write[i].points.insert(insert_at, polys[i].holes[j][idx]);
insert_at++;
}
added_hole = true;
- polys[i].holes.remove(j);
+ polys.write[i].holes.remove(j);
break; //got rid of hole, break and continue
}
}
@@ -980,7 +980,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build
Vector<Vector2> vertices;
vertices.resize(polys[i].points.size());
for (int j = 0; j < vertices.size(); j++) {
- vertices[j] = p_poly.points[polys[i].points[j]].point;
+ vertices.write[j] = p_poly.points[polys[i].points[j]].point;
}
Vector<int> indices = Geometry::triangulate_polygon(vertices);
@@ -1267,7 +1267,7 @@ void CSGBrushOperation::MeshMerge::mark_inside_faces() {
int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i);
if (intersections & 1) {
- faces[i].inside = true;
+ faces.write[i].inside = true;
}
}
}
@@ -1419,13 +1419,13 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A
if (mesh_merge.faces[i].inside)
continue;
for (int j = 0; j < 3; j++) {
- result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ result.faces.write[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces.write[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- result.faces[outside_count].smooth = mesh_merge.faces[i].smooth;
- result.faces[outside_count].invert = mesh_merge.faces[i].invert;
- result.faces[outside_count].material = mesh_merge.faces[i].material_idx;
+ result.faces.write[outside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces.write[outside_count].invert = mesh_merge.faces[i].invert;
+ result.faces.write[outside_count].material = mesh_merge.faces[i].material_idx;
outside_count++;
}
@@ -1451,13 +1451,13 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A
if (!mesh_merge.faces[i].inside)
continue;
for (int j = 0; j < 3; j++) {
- result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ result.faces.write[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces.write[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
- result.faces[inside_count].smooth = mesh_merge.faces[i].smooth;
- result.faces[inside_count].invert = mesh_merge.faces[i].invert;
- result.faces[inside_count].material = mesh_merge.faces[i].material_idx;
+ result.faces.write[inside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces.write[inside_count].invert = mesh_merge.faces[i].invert;
+ result.faces.write[inside_count].material = mesh_merge.faces[i].material_idx;
inside_count++;
}
@@ -1489,19 +1489,19 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A
continue;
for (int j = 0; j < 3; j++) {
- result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
- result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ result.faces.write[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces.write[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
}
if (mesh_merge.faces[i].from_b) {
//invert facing of insides of B
- SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]);
- SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]);
+ SWAP(result.faces.write[face_count].vertices[1], result.faces.write[face_count].vertices[2]);
+ SWAP(result.faces.write[face_count].uvs[1], result.faces.write[face_count].uvs[2]);
}
- result.faces[face_count].smooth = mesh_merge.faces[i].smooth;
- result.faces[face_count].invert = mesh_merge.faces[i].invert;
- result.faces[face_count].material = mesh_merge.faces[i].material_idx;
+ result.faces.write[face_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces.write[face_count].invert = mesh_merge.faces[i].invert;
+ result.faces.write[face_count].material = mesh_merge.faces[i].material_idx;
face_count++;
}
@@ -1513,6 +1513,6 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A
//updatelist of materials
result.materials.resize(mesh_merge.materials.size());
for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
- result.materials[E->get()] = E->key();
+ result.materials.write[E->get()] = E->key();
}
}
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 53303a6533..2e07c23e28 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -34,9 +34,9 @@
#include "aabb.h"
#include "dvector.h"
#include "map.h"
-#include "math_2d.h"
#include "oa_hash_map.h"
#include "plane.h"
+#include "rect2.h"
#include "scene/resources/material.h"
#include "transform.h"
#include "vector3.h"
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index 2150320c4a..07c33ed670 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -32,7 +32,16 @@
///////////
-String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const {
+CSGShapeSpatialGizmoPlugin::CSGShapeSpatialGizmoPlugin() {
+
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1));
+ create_material("shape_material", gizmo_color);
+ create_handle_material("handles");
+}
+
+String CSGShapeSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere>(cs)) {
@@ -57,7 +66,9 @@ String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const {
return "";
}
-Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const {
+Variant CSGShapeSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere>(cs)) {
@@ -89,10 +100,12 @@ Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const {
return Variant();
}
-void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
Transform gt = cs->get_global_transform();
- gt.orthonormalize();
+ //gt.orthonormalize();
Transform gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
@@ -126,9 +139,9 @@ void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2
d = 0.001;
switch (p_idx) {
- case 0: s->set_width(d); break;
- case 1: s->set_height(d); break;
- case 2: s->set_depth(d); break;
+ case 0: s->set_width(d * 2); break;
+ case 1: s->set_height(d * 2); break;
+ case 2: s->set_depth(d * 2); break;
}
}
@@ -170,7 +183,9 @@ void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2
s->set_outer_radius(d);
}
}
-void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+void CSGShapeSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
@@ -260,12 +275,26 @@ void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bo
ur->commit_action();
}
}
-void CSGShapeSpatialGizmo::redraw() {
+bool CSGShapeSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
+ return Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial);
+}
+
+String CSGShapeSpatialGizmoPlugin::get_name() const {
+ return "CSGShapes";
+}
+
+bool CSGShapeSpatialGizmoPlugin::is_selectable_when_hidden() const {
+ return true;
+}
+
+void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
- clear();
+ p_gizmo->clear();
- Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
- Ref<Material> material = create_material("shape_material", gizmo_color);
+ Ref<Material> material = get_material("shape_material", p_gizmo);
+ Ref<Material> handles_material = get_material("handles");
PoolVector<Vector3> faces = cs->get_brush_faces();
@@ -278,14 +307,14 @@ void CSGShapeSpatialGizmo::redraw() {
int f = i / 6;
for (int j = 0; j < 3; j++) {
int j_n = (j + 1) % 3;
- lines[i + j * 2 + 0] = r[f * 3 + j];
- lines[i + j * 2 + 1] = r[f * 3 + j_n];
+ lines.write[i + j * 2 + 0] = r[f * 3 + j];
+ lines.write[i + j * 2 + 1] = r[f * 3 + j_n];
}
}
}
- add_lines(lines, material);
- add_collision_segments(lines);
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
if (Object::cast_to<CSGSphere>(cs)) {
CSGSphere *s = Object::cast_to<CSGSphere>(cs);
@@ -293,17 +322,17 @@ void CSGShapeSpatialGizmo::redraw() {
float r = s->get_radius();
Vector<Vector3> handles;
handles.push_back(Vector3(r, 0, 0));
- add_handles(handles);
+ p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGBox>(cs)) {
CSGBox *s = Object::cast_to<CSGBox>(cs);
Vector<Vector3> handles;
- handles.push_back(Vector3(s->get_width(), 0, 0));
- handles.push_back(Vector3(0, s->get_height(), 0));
- handles.push_back(Vector3(0, 0, s->get_depth()));
- add_handles(handles);
+ handles.push_back(Vector3(s->get_width() * 0.5, 0, 0));
+ handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
+ handles.push_back(Vector3(0, 0, s->get_depth() * 0.5));
+ p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGCylinder>(cs)) {
@@ -312,7 +341,7 @@ void CSGShapeSpatialGizmo::redraw() {
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_radius(), 0, 0));
handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
- add_handles(handles);
+ p_gizmo->add_handles(handles, handles_material);
}
if (Object::cast_to<CSGTorus>(cs)) {
@@ -321,25 +350,11 @@ void CSGShapeSpatialGizmo::redraw() {
Vector<Vector3> handles;
handles.push_back(Vector3(s->get_inner_radius(), 0, 0));
handles.push_back(Vector3(s->get_outer_radius(), 0, 0));
- add_handles(handles);
+ p_gizmo->add_handles(handles, handles_material);
}
}
-CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) {
-
- cs = p_cs;
- set_spatial_node(p_cs);
-}
-
-Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) {
- if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) {
- Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial)));
- return csg;
- }
-
- return Ref<SpatialEditorGizmo>();
-}
EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) {
-
- EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1));
+ Ref<CSGShapeSpatialGizmoPlugin> gizmo_plugin = Ref<CSGShapeSpatialGizmoPlugin>(memnew(CSGShapeSpatialGizmoPlugin));
+ SpatialEditor::get_singleton()->register_gizmo_plugin(gizmo_plugin);
}
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
index 68e916823b..d65d1f58c1 100644
--- a/modules/csg/csg_gizmos.h
+++ b/modules/csg/csg_gizmos.h
@@ -35,25 +35,27 @@
#include "editor/editor_plugin.h"
#include "editor/spatial_editor_gizmos.h"
-class CSGShapeSpatialGizmo : public EditorSpatialGizmo {
+class CSGShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
- GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo);
-
- CSGShape *cs;
+ GDCLASS(CSGShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
public:
- virtual String get_handle_name(int p_idx) const;
- virtual Variant get_handle_value(int p_idx) const;
- virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
- virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
- void redraw();
- CSGShapeSpatialGizmo(CSGShape *p_cs = NULL);
+ bool has_gizmo(Spatial *p_spatial);
+ String get_name() const;
+ bool is_selectable_when_hidden() const;
+ void redraw(EditorSpatialGizmo *p_gizmo);
+
+ String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const;
+ Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const;
+ void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point);
+ void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel);
+
+ CSGShapeSpatialGizmoPlugin();
};
class EditorPluginCSG : public EditorPlugin {
GDCLASS(EditorPluginCSG, EditorPlugin)
public:
- virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
EditorPluginCSG(EditorNode *p_editor);
};
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index ba27d6839d..714be16db7 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -161,8 +161,6 @@ CSGBrush *CSGShape::_get_brush() {
void CSGShape::_update_shape() {
- //print_line("updating shape for " + String(get_path()));
-
if (parent)
return;
@@ -177,7 +175,7 @@ void CSGShape::_update_shape() {
Vector<int> face_count;
face_count.resize(n->materials.size() + 1);
for (int i = 0; i < face_count.size(); i++) {
- face_count[i] = 0;
+ face_count.write[i] = 0;
}
for (int i = 0; i < n->faces.size(); i++) {
@@ -200,7 +198,7 @@ void CSGShape::_update_shape() {
}
}
- face_count[idx]++;
+ face_count.write[idx]++;
}
Vector<ShapeUpdateSurface> surfaces;
@@ -210,18 +208,18 @@ void CSGShape::_update_shape() {
//create arrays
for (int i = 0; i < surfaces.size(); i++) {
- surfaces[i].vertices.resize(face_count[i] * 3);
- surfaces[i].normals.resize(face_count[i] * 3);
- surfaces[i].uvs.resize(face_count[i] * 3);
- surfaces[i].last_added = 0;
+ surfaces.write[i].vertices.resize(face_count[i] * 3);
+ surfaces.write[i].normals.resize(face_count[i] * 3);
+ surfaces.write[i].uvs.resize(face_count[i] * 3);
+ surfaces.write[i].last_added = 0;
if (i != surfaces.size() - 1) {
- surfaces[i].material = n->materials[i];
+ surfaces.write[i].material = n->materials[i];
}
- surfaces[i].verticesw = surfaces[i].vertices.write();
- surfaces[i].normalsw = surfaces[i].normals.write();
- surfaces[i].uvsw = surfaces[i].uvs.write();
+ surfaces.write[i].verticesw = surfaces.write[i].vertices.write();
+ surfaces.write[i].normalsw = surfaces.write[i].normals.write();
+ surfaces.write[i].uvsw = surfaces.write[i].uvs.write();
}
//fill arrays
@@ -281,7 +279,7 @@ void CSGShape::_update_shape() {
surfaces[idx].normalsw[last + order[j]] = normal;
}
- surfaces[idx].last_added += 3;
+ surfaces.write[idx].last_added += 3;
}
}
@@ -290,9 +288,9 @@ void CSGShape::_update_shape() {
for (int i = 0; i < surfaces.size(); i++) {
- surfaces[i].verticesw = PoolVector<Vector3>::Write();
- surfaces[i].normalsw = PoolVector<Vector3>::Write();
- surfaces[i].uvsw = PoolVector<Vector2>::Write();
+ surfaces.write[i].verticesw = PoolVector<Vector3>::Write();
+ surfaces.write[i].normalsw = PoolVector<Vector3>::Write();
+ surfaces.write[i].uvsw = PoolVector<Vector2>::Write();
if (surfaces[i].last_added == 0)
continue;
@@ -372,7 +370,6 @@ void CSGShape::_notification(int p_what) {
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
- //print_line("local xform changed");
if (parent) {
parent->_make_dirty();
}
@@ -641,7 +638,6 @@ CSGBrush *CSGMesh::_build_brush() {
}
}
- //print_line("total vertices? " + itos(vertices.size()));
if (vertices.size() == 0)
return NULL;
@@ -921,7 +917,7 @@ CSGBrush *CSGBox::_build_brush() {
int face = 0;
- Vector3 vertex_mul(width, height, depth);
+ Vector3 vertex_mul(width * 0.5, height * 0.5, depth * 0.5);
{
@@ -1055,9 +1051,9 @@ Ref<Material> CSGBox::get_material() const {
CSGBox::CSGBox() {
// defaults
- width = 1.0;
- height = 1.0;
- depth = 1.0;
+ width = 2.0;
+ height = 2.0;
+ depth = 2.0;
}
///////////////
@@ -1585,7 +1581,11 @@ CSGBrush *CSGPolygon::_build_brush() {
case MODE_PATH: {
float bl = curve->get_baked_length();
int splits = MAX(2, Math::ceil(bl / path_interval));
- face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ if (path_joined) {
+ face_count = splits * final_polygon.size() * 2;
+ } else {
+ face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ }
} break;
}
@@ -1793,8 +1793,14 @@ CSGBrush *CSGPolygon::_build_brush() {
float bl = curve->get_baked_length();
int splits = MAX(2, Math::ceil(bl / path_interval));
+ float u1 = 0.0;
+ float u2 = path_continuous_u ? 0.0 : 1.0;
- Transform path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
+ Transform path_to_this;
+ if (!path_local) {
+ // center on paths origin
+ path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
+ }
Transform prev_xf;
@@ -1812,6 +1818,9 @@ CSGBrush *CSGPolygon::_build_brush() {
for (int i = 0; i <= splits; i++) {
float ofs = i * path_interval;
+ if (i == splits && path_joined) {
+ ofs = 0.0;
+ }
Transform xf;
xf.origin = curve->interpolate_baked(ofs);
@@ -1836,6 +1845,11 @@ CSGBrush *CSGPolygon::_build_brush() {
xf = path_to_this * xf;
if (i > 0) {
+ if (path_continuous_u) {
+ u1 = u2;
+ u2 += (prev_xf.origin - xf.origin).length();
+ };
+
//put triangles where they belong
//add triangles for depth
for (int j = 0; j < final_polygon.size(); j++) {
@@ -1850,10 +1864,10 @@ CSGBrush *CSGPolygon::_build_brush() {
};
Vector2 u[4] = {
- Vector2(0, 0),
- Vector2(0, 1),
- Vector2(1, 1),
- Vector2(1, 0)
+ Vector2(u1, 0),
+ Vector2(u1, 1),
+ Vector2(u2, 1),
+ Vector2(u2, 0)
};
// face 1
@@ -1888,7 +1902,7 @@ CSGBrush *CSGPolygon::_build_brush() {
}
}
- if (i == 0) {
+ if (i == 0 && !path_joined) {
for (int j = 0; j < triangles.size(); j += 3) {
for (int k = 0; k < 3; k++) {
@@ -1905,7 +1919,7 @@ CSGBrush *CSGPolygon::_build_brush() {
}
}
- if (i == splits) {
+ if (i == splits && !path_joined) {
for (int j = 0; j < triangles.size(); j += 3) {
for (int k = 0; k < 3; k++) {
@@ -2003,6 +2017,15 @@ void CSGPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation);
ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation);
+ ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon::set_path_local);
+ ClassDB::bind_method(D_METHOD("is_path_local"), &CSGPolygon::is_path_local);
+
+ ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon::set_path_continuous_u);
+ ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon::is_path_continuous_u);
+
+ ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon::set_path_joined);
+ ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon::is_path_joined);
+
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material);
@@ -2020,9 +2043,12 @@ void CSGPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node"), "set_path_node", "get_path_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path"), "set_path_node", "get_path_node");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_path_interval", "get_path_interval");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
@@ -2067,6 +2093,15 @@ float CSGPolygon::get_depth() const {
return depth;
}
+void CSGPolygon::set_path_continuous_u(bool p_enable) {
+ path_continuous_u = p_enable;
+ _make_dirty();
+}
+
+bool CSGPolygon::is_path_continuous_u() const {
+ return path_continuous_u;
+}
+
void CSGPolygon::set_spin_degrees(const float p_spin_degrees) {
ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
spin_degrees = p_spin_degrees;
@@ -2119,6 +2154,26 @@ CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const {
return path_rotation;
}
+void CSGPolygon::set_path_local(bool p_enable) {
+ path_local = p_enable;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGPolygon::is_path_local() const {
+ return path_local;
+}
+
+void CSGPolygon::set_path_joined(bool p_enable) {
+ path_joined = p_enable;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGPolygon::is_path_joined() const {
+ return path_joined;
+}
+
void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) {
smooth_faces = p_smooth_faces;
_make_dirty();
@@ -2160,5 +2215,8 @@ CSGPolygon::CSGPolygon() {
smooth_faces = false;
path_interval = 1;
path_rotation = PATH_ROTATION_PATH;
+ path_local = false;
+ path_continuous_u = false;
+ path_joined = false;
path_cache = NULL;
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index cbb5c7e041..6898cdaf64 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -334,10 +334,13 @@ private:
NodePath path_node;
float path_interval;
PathRotation path_rotation;
+ bool path_local;
Node *path_cache;
bool smooth_faces;
+ bool path_continuous_u;
+ bool path_joined;
bool _is_editable_3d_polygon() const;
bool _has_editable_3d_polygon_no_depth() const;
@@ -375,6 +378,15 @@ public:
void set_path_rotation(PathRotation p_rotation);
PathRotation get_path_rotation() const;
+ void set_path_local(bool p_enable);
+ bool is_path_local() const;
+
+ void set_path_continuous_u(bool p_enable);
+ bool is_path_continuous_u() const;
+
+ void set_path_joined(bool p_enable);
+ bool is_path_joined() const;
+
void set_smooth_faces(bool p_smooth_faces);
bool get_smooth_faces() const;
diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml
index 80455fda80..5ec7b5089d 100644
--- a/modules/csg/doc_classes/CSGBox.xml
+++ b/modules/csg/doc_classes/CSGBox.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGBox" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Box shape.
</brief_description>
<description>
+ This node allows you to create a box for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,12 +14,16 @@
</methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth">
+ Depth of the box measured from the center of the box.
</member>
<member name="height" type="float" setter="set_height" getter="get_height">
+ Height of the box measured from the center of the box.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the box.
</member>
<member name="width" type="float" setter="set_width" getter="get_width">
+ Width of the box measured from the center of the box.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml
index b2265d7703..1cfaa74b7d 100644
--- a/modules/csg/doc_classes/CSGCombiner.xml
+++ b/modules/csg/doc_classes/CSGCombiner.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCombiner" inherits="CSGShape" category="Core" version="3.1">
<brief_description>
+ A CSG node that allows you to combine other CSG modifiers.
</brief_description>
<description>
+ For complex arrangements of shapes it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as their input to create the final shape.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml
index 0cab26ad3d..92b170ed1f 100644
--- a/modules/csg/doc_classes/CSGCylinder.xml
+++ b/modules/csg/doc_classes/CSGCylinder.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGCylinder" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Cylinder shape.
</brief_description>
<description>
+ This node allows you to create a cylinder (or cone) for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,16 +14,22 @@
</methods>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone">
+ If true a cone is created, the [member radius] will only apply to one side.
</member>
<member name="height" type="float" setter="set_height" getter="get_height">
+ The height of the cylinder.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the cylinder.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius">
+ The radius of the cylinder.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides">
+ The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. When false the cylinder will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml
index e5c3e5ccf3..419214b7e6 100644
--- a/modules/csg/doc_classes/CSGMesh.xml
+++ b/modules/csg/doc_classes/CSGMesh.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGMesh" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
+ This CSG node allows you to use any mesh resource as a CSG shape provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
</description>
<tutorials>
</tutorials>
@@ -12,6 +14,7 @@
</methods>
<members>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
+ The mesh resource to use as a CSG shape.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml
index 379c512d6a..a33e5557cb 100644
--- a/modules/csg/doc_classes/CSGPolygon.xml
+++ b/modules/csg/doc_classes/CSGPolygon.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPolygon" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
+ This node takes a 2D polygon shape and extrudes it to create a 3D mesh.
</description>
<tutorials>
</tutorials>
@@ -12,38 +14,63 @@
</methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth">
+ Extrusion depth when [member mode] is [constant MODE_DEPTH].
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ Material to use for the resulting mesh.
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon.Mode">
+ Extrusion mode.
+ </member>
+ <member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u">
+ If true the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH].
</member>
<member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
+ Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH].
+ </member>
+ <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined">
+ If true the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH].
+ </member>
+ <member name="path_local" type="bool" setter="set_path_local" getter="is_path_local">
+ If false we extrude centered on our path, if true we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH].
</member>
<member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
+ The [Shape] object containing the path along which we extrude when [member mode] is [constant MODE_PATH].
</member>
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon.PathRotation">
+ The method by which each slice is rotated along the path when [member mode] is [constant MODE_PATH].
</member>
<member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon">
+ Point array that defines the shape that we'll extrude.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ Generates smooth normals so smooth shading is applied to our mesh.
</member>
<member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
+ Degrees to rotate our extrusion for each slice when [member mode] is [constant MODE_SPIN].
</member>
<member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
+ Number of extrusion when [member mode] is [constant MODE_SPIN].
</member>
</members>
<constants>
<constant name="MODE_DEPTH" value="0" enum="Mode">
+ Shape is extruded to [member depth].
</constant>
<constant name="MODE_SPIN" value="1" enum="Mode">
+ Shape is extruded by rotating it around an axis.
</constant>
<constant name="MODE_PATH" value="2" enum="Mode">
+ Shape is extruded along a path set by a [Shape] set in [member path_node].
</constant>
<constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
+ Slice is not rotated.
</constant>
<constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
+ Slice is rotated around the up vector of the path.
</constant>
<constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
+ Slice is rotate to match the path exactly.
</constant>
</constants>
</class>
diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml
index bf41c40f22..2591bab7e3 100644
--- a/modules/csg/doc_classes/CSGPrimitive.xml
+++ b/modules/csg/doc_classes/CSGPrimitive.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGPrimitive" inherits="CSGShape" category="Core" version="3.1">
<brief_description>
+ Base class for CSG primitives.
</brief_description>
<description>
</description>
@@ -12,6 +13,7 @@
</methods>
<members>
<member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces">
+ Invert the faces of the mesh.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
index cf236a4207..90621b94f4 100644
--- a/modules/csg/doc_classes/CSGShape.xml
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.1">
<brief_description>
+ The CSG base class.
</brief_description>
<description>
+ This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
</description>
<tutorials>
</tutorials>
@@ -13,23 +15,29 @@
<return type="bool">
</return>
<description>
+ Returns true if this is a root shape and is thus the object that is rendered.
</description>
</method>
</methods>
<members>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
+ The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap">
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision">
+ Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden.
</member>
</members>
<constants>
<constant name="OPERATION_UNION" value="0" enum="Operation">
+ Geometry of both primitives is merged, intersecting geometry is removed.
</constant>
<constant name="OPERATION_INTERSECTION" value="1" enum="Operation">
+ Only intersecting geometry remains, the rest is removed.
</constant>
<constant name="OPERATION_SUBTRACTION" value="2" enum="Operation">
+ The second shape is susbtracted from the first, leaving a dent with it's shape.
</constant>
</constants>
</class>
diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml
index 520368506e..a0069879cb 100644
--- a/modules/csg/doc_classes/CSGSphere.xml
+++ b/modules/csg/doc_classes/CSGSphere.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGSphere" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Sphere shape.
</brief_description>
<description>
+ This node allows you to create a sphere for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,14 +14,19 @@
</methods>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the sphere.
</member>
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments">
+ Number of vertical slices for the sphere.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius">
+ Radius of the sphere.
</member>
<member name="rings" type="int" setter="set_rings" getter="get_rings">
+ Number of horizontal slices for the sphere.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the sphere are set to give a smooth effect making the sphere seem rounded. When false the sphere will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml
index 58bbef2600..187d71a2fa 100644
--- a/modules/csg/doc_classes/CSGTorus.xml
+++ b/modules/csg/doc_classes/CSGTorus.xml
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSGTorus" inherits="CSGPrimitive" category="Core" version="3.1">
<brief_description>
+ A CSG Torus shape.
</brief_description>
<description>
+ This node allows you to create a torus for use with the CSG system.
</description>
<tutorials>
</tutorials>
@@ -12,16 +14,22 @@
</methods>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius">
+ The inner radius of the torus.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
+ The material used to render the torus.
</member>
<member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius">
+ The outer radius of the torus.
</member>
<member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides">
+ The number of edges each ring of the torus is constructed of.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides">
+ The number of slices the torus is constructed of.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces">
+ If true the normals of the torus are set to give a smooth effect making the torus seem rounded. When false the torus will have a flat shaded look.
</member>
</members>
<constants>
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
new file mode 100644
index 0000000000..5c396482aa
--- /dev/null
+++ b/modules/cvtt/SCsub
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_cvtt = env_modules.Clone()
+
+# Thirdparty source files
+if env['builtin_squish']:
+ thirdparty_dir = "#thirdparty/cvtt/"
+ thirdparty_sources = [
+ "ConvectionKernels.cpp"
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_cvtt.add_source_files(env.modules_sources, thirdparty_sources)
+ env_cvtt.Append(CPPPATH=[thirdparty_dir])
+
+# Godot source files
+env_cvtt.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/cvtt/config.py b/modules/cvtt/config.py
new file mode 100644
index 0000000000..098f1eafa9
--- /dev/null
+++ b/modules/cvtt/config.py
@@ -0,0 +1,5 @@
+def can_build(env, platform):
+ return env['tools']
+
+def configure(env):
+ pass
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
new file mode 100644
index 0000000000..af92861352
--- /dev/null
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -0,0 +1,394 @@
+/*************************************************************************/
+/* image_compress_cvtt.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "image_compress_cvtt.h"
+
+#include "os/os.h"
+#include "os/thread.h"
+#include "print_string.h"
+
+#include <ConvectionKernels.h>
+
+struct CVTTCompressionJobParams {
+ bool is_hdr;
+ bool is_signed;
+ int bytes_per_pixel;
+
+ cvtt::Options options;
+};
+
+struct CVTTCompressionRowTask {
+ const uint8_t *in_mm_bytes;
+ uint8_t *out_mm_bytes;
+ int y_start;
+ int width;
+ int height;
+};
+
+struct CVTTCompressionJobQueue {
+ CVTTCompressionJobParams job_params;
+ const CVTTCompressionRowTask *job_tasks;
+ uint32_t num_tasks;
+ uint32_t current_task;
+};
+
+static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const CVTTCompressionRowTask &p_row_task) {
+ const uint8_t *in_bytes = p_row_task.in_mm_bytes;
+ uint8_t *out_bytes = p_row_task.out_mm_bytes;
+ int w = p_row_task.width;
+ int h = p_row_task.height;
+
+ int y_start = p_row_task.y_start;
+ int y_end = y_start + 4;
+
+ int bytes_per_pixel = p_job_params.bytes_per_pixel;
+ bool is_hdr = p_job_params.is_hdr;
+ bool is_signed = p_job_params.is_signed;
+
+ cvtt::PixelBlockU8 input_blocks_ldr[cvtt::NumParallelBlocks];
+ cvtt::PixelBlockF16 input_blocks_hdr[cvtt::NumParallelBlocks];
+
+ for (int x_start = 0; x_start < w; x_start += 4 * cvtt::NumParallelBlocks) {
+ int x_end = x_start + 4 * cvtt::NumParallelBlocks;
+
+ for (int y = y_start; y < y_end; y++) {
+ int first_input_element = (y - y_start) * 4;
+ const uint8_t *row_start;
+ if (y >= h) {
+ row_start = in_bytes + (h - 1) * (w * bytes_per_pixel);
+ } else {
+ row_start = in_bytes + y * (w * bytes_per_pixel);
+ }
+
+ for (int x = x_start; x < x_end; x++) {
+ const uint8_t *pixel_start;
+ if (x >= w) {
+ pixel_start = row_start + (w - 1) * bytes_per_pixel;
+ } else {
+ pixel_start = row_start + x * bytes_per_pixel;
+ }
+
+ int block_index = (x - x_start) / 4;
+ int block_element = (x - x_start) % 4 + first_input_element;
+ if (is_hdr) {
+ memcpy(input_blocks_hdr[block_index].m_pixels[block_element], pixel_start, bytes_per_pixel);
+ input_blocks_hdr[block_index].m_pixels[block_element][3] = 0x3c00; // 1.0 (unused)
+ } else {
+ memcpy(input_blocks_ldr[block_index].m_pixels[block_element], pixel_start, bytes_per_pixel);
+ }
+ }
+ }
+
+ uint8_t output_blocks[16 * cvtt::NumParallelBlocks];
+
+ if (is_hdr) {
+ if (is_signed) {
+ cvtt::Kernels::EncodeBC6HS(output_blocks, input_blocks_hdr, p_job_params.options);
+ } else {
+ cvtt::Kernels::EncodeBC6HU(output_blocks, input_blocks_hdr, p_job_params.options);
+ }
+ } else {
+ cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options);
+ }
+
+ int num_real_blocks = ((w - x_start) + 3) / 4;
+ if (num_real_blocks > cvtt::NumParallelBlocks) {
+ num_real_blocks = cvtt::NumParallelBlocks;
+ }
+
+ memcpy(out_bytes, output_blocks, 16 * num_real_blocks);
+ out_bytes += 16 * num_real_blocks;
+ }
+}
+
+static void _digest_job_queue(void *p_job_queue) {
+ CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue);
+
+ for (int next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) {
+ _digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]);
+ }
+}
+
+void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressSource p_source) {
+
+ if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA)
+ return; //do not compress, already compressed
+
+ int w = p_image->get_width();
+ int h = p_image->get_height();
+
+ bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8);
+ bool is_hdr = (p_image->get_format() == Image::FORMAT_RGBH);
+
+ if (!is_ldr && !is_hdr) {
+ return; // Not a usable source format
+ }
+
+ cvtt::Options options;
+ uint32_t flags = cvtt::Flags::Fastest;
+
+ if (p_lossy_quality > 0.85)
+ flags = cvtt::Flags::Ultra;
+ else if (p_lossy_quality > 0.75)
+ flags = cvtt::Flags::Better;
+ else if (p_lossy_quality > 0.55)
+ flags = cvtt::Flags::Default;
+ else if (p_lossy_quality > 0.35)
+ flags = cvtt::Flags::Fast;
+ else if (p_lossy_quality > 0.15)
+ flags = cvtt::Flags::Faster;
+
+ flags |= cvtt::Flags::BC7_RespectPunchThrough;
+
+ if (p_source == Image::COMPRESS_SOURCE_NORMAL) {
+ flags |= cvtt::Flags::Uniform;
+ }
+
+ Image::Format target_format = Image::FORMAT_BPTC_RGBA;
+
+ bool is_signed = false;
+ if (is_hdr) {
+ PoolVector<uint8_t>::Read rb = p_image->get_data().read();
+
+ const uint16_t *source_data = reinterpret_cast<const uint16_t *>(&rb[0]);
+ int pixel_element_count = w * h * 3;
+ for (int i = 0; i < pixel_element_count; i++) {
+ if ((source_data[i] & 0x8000) != 0 && (source_data[i] & 0x7fff) != 0) {
+ is_signed = true;
+ break;
+ }
+ }
+
+ target_format = is_signed ? Image::FORMAT_BPTC_RGBF : Image::FORMAT_BPTC_RGBFU;
+ } else {
+ p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
+ }
+
+ PoolVector<uint8_t>::Read rb = p_image->get_data().read();
+
+ PoolVector<uint8_t> data;
+ int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
+ int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
+ data.resize(target_size);
+ int shift = Image::get_format_pixel_rshift(target_format);
+
+ PoolVector<uint8_t>::Write wb = data.write();
+
+ int dst_ofs = 0;
+
+ CVTTCompressionJobQueue job_queue;
+ job_queue.job_params.is_hdr = is_hdr;
+ job_queue.job_params.is_signed = is_signed;
+ job_queue.job_params.options = options;
+ job_queue.job_params.bytes_per_pixel = is_hdr ? 6 : 4;
+
+#ifdef NO_THREADS
+ int num_job_threads = 0;
+#else
+ int num_job_threads = OS::get_singleton()->can_use_threads() ? (OS::get_singleton()->get_processor_count() - 1) : 0;
+#endif
+
+ PoolVector<CVTTCompressionRowTask> tasks;
+
+ for (int i = 0; i <= mm_count; i++) {
+
+ int bw = w % 4 != 0 ? w + (4 - w % 4) : w;
+ int bh = h % 4 != 0 ? h + (4 - h % 4) : h;
+
+ int src_ofs = p_image->get_mipmap_offset(i);
+
+ const uint8_t *in_bytes = &rb[src_ofs];
+ uint8_t *out_bytes = &wb[dst_ofs];
+
+ for (int y_start = 0; y_start < h; y_start += 4) {
+ int y_end = y_start + 4;
+
+ CVTTCompressionRowTask row_task;
+ row_task.width = w;
+ row_task.height = h;
+ row_task.y_start = y_start;
+ row_task.in_mm_bytes = in_bytes;
+ row_task.out_mm_bytes = out_bytes;
+
+ if (num_job_threads > 0) {
+ tasks.push_back(row_task);
+ } else {
+ _digest_row_task(job_queue.job_params, row_task);
+ }
+
+ out_bytes += 16 * (bw / 4);
+ }
+
+ dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift;
+ w = MAX(w / 2, 1);
+ h = MAX(h / 2, 1);
+ }
+
+ if (num_job_threads > 0) {
+ PoolVector<Thread *> threads;
+ threads.resize(num_job_threads);
+
+ PoolVector<Thread *>::Write threads_wb = threads.write();
+
+ PoolVector<CVTTCompressionRowTask>::Read tasks_rb = tasks.read();
+
+ job_queue.job_tasks = &tasks_rb[0];
+ job_queue.current_task = 0;
+ job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
+
+ for (int i = 0; i < num_job_threads; i++) {
+ threads_wb[i] = Thread::create(_digest_job_queue, &job_queue);
+ }
+ _digest_job_queue(&job_queue);
+
+ for (int i = 0; i < num_job_threads; i++) {
+ Thread::wait_to_finish(threads_wb[i]);
+ memdelete(threads_wb[i]);
+ }
+ }
+
+ p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+}
+
+void image_decompress_cvtt(Image *p_image) {
+
+ Image::Format target_format;
+ bool is_signed = false;
+ bool is_hdr = false;
+
+ Image::Format input_format = p_image->get_format();
+
+ switch (input_format) {
+ case Image::FORMAT_BPTC_RGBA:
+ target_format = Image::FORMAT_RGBA8;
+ break;
+ case Image::FORMAT_BPTC_RGBF:
+ case Image::FORMAT_BPTC_RGBFU:
+ target_format = Image::FORMAT_RGBH;
+ is_signed = (input_format == Image::FORMAT_BPTC_RGBF);
+ is_hdr = true;
+ break;
+ default:
+ return; // Invalid input format
+ };
+
+ int w = p_image->get_width();
+ int h = p_image->get_height();
+
+ PoolVector<uint8_t>::Read rb = p_image->get_data().read();
+
+ PoolVector<uint8_t> data;
+ int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
+ int mm_count = p_image->get_mipmap_count();
+ data.resize(target_size);
+ int shift = Image::get_format_pixel_rshift(target_format);
+
+ PoolVector<uint8_t>::Write wb = data.write();
+
+ int bytes_per_pixel = is_hdr ? 6 : 4;
+
+ int dst_ofs = 0;
+
+ for (int i = 0; i <= mm_count; i++) {
+
+ int src_ofs = p_image->get_mipmap_offset(i);
+
+ const uint8_t *in_bytes = &rb[src_ofs];
+ uint8_t *out_bytes = &wb[dst_ofs];
+
+ cvtt::PixelBlockU8 output_blocks_ldr[cvtt::NumParallelBlocks];
+ cvtt::PixelBlockF16 output_blocks_hdr[cvtt::NumParallelBlocks];
+
+ for (int y_start = 0; y_start < h; y_start += 4) {
+ int y_end = y_start + 4;
+
+ for (int x_start = 0; x_start < w; x_start += 4 * cvtt::NumParallelBlocks) {
+ int x_end = x_start + 4 * cvtt::NumParallelBlocks;
+
+ uint8_t input_blocks[16 * cvtt::NumParallelBlocks];
+ memset(input_blocks, 0, sizeof(input_blocks));
+
+ int num_real_blocks = ((w - x_start) + 3) / 4;
+ if (num_real_blocks > cvtt::NumParallelBlocks) {
+ num_real_blocks = cvtt::NumParallelBlocks;
+ }
+
+ memcpy(input_blocks, in_bytes, 16 * num_real_blocks);
+ in_bytes += 16 * num_real_blocks;
+
+ if (is_hdr) {
+ if (is_signed) {
+ cvtt::Kernels::DecodeBC6HS(output_blocks_hdr, input_blocks);
+ } else {
+ cvtt::Kernels::DecodeBC6HU(output_blocks_hdr, input_blocks);
+ }
+ } else {
+ cvtt::Kernels::DecodeBC7(output_blocks_ldr, input_blocks);
+ }
+
+ for (int y = y_start; y < y_end; y++) {
+ int first_input_element = (y - y_start) * 4;
+ uint8_t *row_start;
+ if (y >= h) {
+ row_start = out_bytes + (h - 1) * (w * bytes_per_pixel);
+ } else {
+ row_start = out_bytes + y * (w * bytes_per_pixel);
+ }
+
+ for (int x = x_start; x < x_end; x++) {
+ uint8_t *pixel_start;
+ if (x >= w) {
+ pixel_start = row_start + (w - 1) * bytes_per_pixel;
+ } else {
+ pixel_start = row_start + x * bytes_per_pixel;
+ }
+
+ int block_index = (x - x_start) / 4;
+ int block_element = (x - x_start) % 4 + first_input_element;
+ if (is_hdr) {
+ memcpy(pixel_start, output_blocks_hdr[block_index].m_pixels[block_element], bytes_per_pixel);
+ } else {
+ memcpy(pixel_start, output_blocks_ldr[block_index].m_pixels[block_element], bytes_per_pixel);
+ }
+ }
+ }
+ }
+ }
+
+ dst_ofs += w * h * bytes_per_pixel;
+ w >>= 1;
+ h >>= 1;
+ }
+
+ rb = PoolVector<uint8_t>::Read();
+ wb = PoolVector<uint8_t>::Write();
+
+ p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+}
diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h
new file mode 100644
index 0000000000..0e18b247e5
--- /dev/null
+++ b/modules/cvtt/image_compress_cvtt.h
@@ -0,0 +1,39 @@
+/*************************************************************************/
+/* image_compress_cvtt.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 IMAGE_COMPRESS_CVTT_H
+#define IMAGE_COMPRESS_CVTT_H
+
+#include "image.h"
+
+void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressSource p_source);
+void image_decompress_cvtt(Image *p_image);
+
+#endif // IMAGE_COMPRESS_CVTT_H
diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp
new file mode 100644
index 0000000000..c96fbbf340
--- /dev/null
+++ b/modules/cvtt/register_types.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "image_compress_cvtt.h"
+
+void register_cvtt_types() {
+
+ Image::set_compress_bptc_func(image_compress_cvtt);
+ Image::_image_decompress_bptc = image_decompress_cvtt;
+}
+
+void unregister_cvtt_types() {}
+
+#endif
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
new file mode 100644
index 0000000000..73de9728d3
--- /dev/null
+++ b/modules/cvtt/register_types.h
@@ -0,0 +1,34 @@
+/*************************************************************************/
+/* register_types.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. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+void register_cvtt_types();
+void unregister_cvtt_types();
+#endif
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 9424080b6d..3cb24d0407 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -217,8 +217,6 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
if (!(flags & DDSD_MIPMAPCOUNT))
mipmaps = 1;
- //print_line("found format: "+String(dds_format_info[dds_format].name));
-
PoolVector<uint8_t> src_data;
const DDSFormatInfo &info = dds_format_info[dds_format];
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 88768829d7..25b7f2472d 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -666,7 +666,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *
while (total) {
for (size_t i = 0; i < inBufferCount; i++) {
int to_copy = MIN(total, int(inBuffers[i].dataLength));
- copymem(&enet->src_compressor_mem[ofs], inBuffers[i].data, to_copy);
+ copymem(&enet->src_compressor_mem.write[ofs], inBuffers[i].data, to_copy);
ofs += to_copy;
total -= to_copy;
}
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
index 8a674bc8c1..f5c817c816 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_etc.cpp
@@ -98,6 +98,33 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
Image::Format img_format = p_img->get_format();
Image::DetectChannels detected_channels = p_img->get_detected_channels();
+ if (p_source == Image::COMPRESS_SOURCE_LAYERED) {
+ //keep what comes in
+ switch (p_img->get_format()) {
+ case Image::FORMAT_L8: {
+ detected_channels = Image::DETECTED_L;
+ } break;
+ case Image::FORMAT_LA8: {
+ detected_channels = Image::DETECTED_LA;
+ } break;
+ case Image::FORMAT_R8: {
+ detected_channels = Image::DETECTED_R;
+ } break;
+ case Image::FORMAT_RG8: {
+ detected_channels = Image::DETECTED_RG;
+ } break;
+ case Image::FORMAT_RGB8: {
+ detected_channels = Image::DETECTED_RGB;
+ } break;
+ case Image::FORMAT_RGBA8:
+ case Image::FORMAT_RGBA4444:
+ case Image::FORMAT_RGBA5551: {
+ detected_channels = Image::DETECTED_RGBA;
+ } break;
+ default: {}
+ }
+ }
+
if (p_source == Image::COMPRESS_SOURCE_SRGB && (detected_channels == Image::DETECTED_R || detected_channels == Image::DETECTED_RG)) {
//R and RG do not support SRGB
detected_channels = Image::DETECTED_RGB;
@@ -147,7 +174,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
PoolVector<uint8_t>::Read r = img->get_data().read();
- int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps() ? -1 : 0);
+ int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps());
int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0);
PoolVector<uint8_t> dst_data;
@@ -172,7 +199,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
int wofs = 0;
- print_line("begin encoding, format: " + Image::get_format_name(etc_format));
+ print_verbose("ETC: Begin encoding, format: " + Image::get_format_name(etc_format));
uint64_t t = OS::get_singleton()->get_ticks_msec();
for (int i = 0; i < mmc; i++) {
// convert source image to internal etc2comp format (which is equivalent to Image::FORMAT_RGBAF)
@@ -200,7 +227,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
delete[] src_rgba_f;
}
- print_line("time encoding: " + rtos(OS::get_singleton()->get_ticks_msec() - t));
+ print_verbose("ETC: Time encoding: " + rtos(OS::get_singleton()->get_ticks_msec() - t));
p_img->create(imgw, imgh, p_img->has_mipmaps(), etc_format, dst_data);
}
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index a3434995aa..46b2a832f1 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -13,271 +13,23 @@ gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cp
gdn_env.Append(CPPPATH=['#modules/gdnative/include/'])
+SConscript("net/SCsub")
SConscript("arvr/SCsub")
SConscript("pluginscript/SCsub")
-def _spaced(e):
- return e if e[-1] == '*' else e + ' '
+from platform_methods import run_in_subprocess
+import gdnative_builders
-def _build_gdnative_api_struct_header(api):
- gdnative_api_init_macro = [
- '\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;'
- ]
-
- for ext in api['extensions']:
- name = ext['name']
- gdnative_api_init_macro.append(
- '\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name))
-
- gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;')
- gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ')
- gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {')
-
- for ext in api['extensions']:
- name = ext['name']
- gdnative_api_init_macro.append(
- '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type'])
- gdnative_api_init_macro.append(
- '\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)'
- ' _gdnative_wrapper_api_struct->extensions[i];'.format(name))
- gdnative_api_init_macro.append('\t\t\t\tbreak;')
- gdnative_api_init_macro.append('\t\t}')
- gdnative_api_init_macro.append('\t}')
-
- out = [
- '/* THIS FILE IS GENERATED DO NOT EDIT */',
- '#ifndef GODOT_GDNATIVE_API_STRUCT_H',
- '#define GODOT_GDNATIVE_API_STRUCT_H',
- '',
- '#include <gdnative/gdnative.h>',
- '#include <android/godot_android.h>',
- '#include <arvr/godot_arvr.h>',
- '#include <nativescript/godot_nativescript.h>',
- '#include <pluginscript/godot_pluginscript.h>',
- '',
- '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)',
- '',
- '#ifdef __cplusplus',
- 'extern "C" {',
- '#endif',
- '',
- 'enum GDNATIVE_API_TYPES {',
- '\tGDNATIVE_' + api['core']['type'] + ','
- ]
-
- for ext in api['extensions']:
- out += ['\tGDNATIVE_EXT_' + ext['type'] + ',']
-
- out += ['};', '']
-
-
- def generate_extension_struct(name, ext, include_version=True):
- ret_val = []
- if ext['next']:
- ret_val += generate_extension_struct(name, ext['next'])
-
- ret_val += [
- 'typedef struct godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct {',
- '\tunsigned int type;',
- '\tgodot_gdnative_api_version version;',
- '\tconst godot_gdnative_api_struct *next;'
- ]
-
- for funcdef in ext['api']:
- args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
- ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
-
- ret_val += ['} godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct;', '']
-
- return ret_val
-
-
- for ext in api['extensions']:
- name = ext['name']
- out += generate_extension_struct(name, ext, False)
-
- out += [
- 'typedef struct godot_gdnative_core_api_struct {',
- '\tunsigned int type;',
- '\tgodot_gdnative_api_version version;',
- '\tconst godot_gdnative_api_struct *next;',
- '\tunsigned int num_extensions;',
- '\tconst godot_gdnative_api_struct **extensions;',
- ]
-
- for funcdef in api['core']['api']:
- args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
- out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
-
- out += [
- '} godot_gdnative_core_api_struct;',
- '',
- '#ifdef __cplusplus',
- '}',
- '#endif',
- '',
- '#endif // GODOT_GDNATIVE_API_STRUCT_H',
- ''
- ]
- return '\n'.join(out)
-
-def _build_gdnative_api_struct_source(api):
- out = [
- '/* THIS FILE IS GENERATED DO NOT EDIT */',
- '',
- '#include <gdnative_api_struct.gen.h>',
- ''
- ]
-
- def get_extension_struct_name(name, ext, include_version=True):
- return 'godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct'
-
- def get_extension_struct_instance_name(name, ext, include_version=True):
- return 'api_extension_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_struct'
-
- def get_extension_struct_definition(name, ext, include_version=True):
-
- ret_val = []
-
- if ext['next']:
- ret_val += get_extension_struct_definition(name, ext['next'])
-
- ret_val += [
- 'extern const ' + get_extension_struct_name(name, ext, include_version) + ' ' + get_extension_struct_instance_name(name, ext, include_version) + ' = {',
- '\tGDNATIVE_EXT_' + ext['type'] + ',',
- '\t{' + str(ext['version']['major']) + ', ' + str(ext['version']['minor']) + '},',
- '\t' + ('NULL' if not ext['next'] else ('(const godot_gdnative_api_struct *)&' + get_extension_struct_instance_name(name, ext['next']))) + ','
- ]
-
- for funcdef in ext['api']:
- ret_val.append('\t%s,' % funcdef['name'])
-
- ret_val += ['};\n']
-
- return ret_val
-
- for ext in api['extensions']:
- name = ext['name']
- out += get_extension_struct_definition(name, ext, False)
-
- out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {']
-
- for ext in api['extensions']:
- name = ext['name']
- out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,']
-
- out += ['};\n']
-
- out += [
- 'extern const godot_gdnative_core_api_struct api_struct = {',
- '\tGDNATIVE_' + api['core']['type'] + ',',
- '\t{' + str(api['core']['version']['major']) + ', ' + str(api['core']['version']['minor']) + '},',
- '\tNULL,',
- '\t' + str(len(api['extensions'])) + ',',
- '\tgdnative_extensions_pointers,',
- ]
-
- for funcdef in api['core']['api']:
- out.append('\t%s,' % funcdef['name'])
- out.append('};\n')
-
- return '\n'.join(out)
-
-def build_gdnative_api_struct(target, source, env):
- import json
- from collections import OrderedDict
-
- with open(source[0].path, 'r') as fd:
- api = json.load(fd)
-
- header, source = target
- with open(header.path, 'w') as fd:
- fd.write(_build_gdnative_api_struct_header(api))
- with open(source.path, 'w') as fd:
- fd.write(_build_gdnative_api_struct_source(api))
_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'],
- 'gdnative_api.json', build_gdnative_api_struct)
+ 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_api_struct))
gdn_env.add_source_files(env.modules_sources, [gensource])
env.use_ptrcall = True
-def _build_gdnative_wrapper_code(api):
- out = [
- '/* THIS FILE IS GENERATED DO NOT EDIT */',
- '',
- '#include <gdnative/gdnative.h>',
- '#include <nativescript/godot_nativescript.h>',
- '#include <pluginscript/godot_pluginscript.h>',
- '#include <arvr/godot_arvr.h>',
- '',
- '#include <gdnative_api_struct.gen.h>',
- '',
- '#ifdef __cplusplus',
- 'extern "C" {',
- '#endif',
- '',
- 'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;',
- ]
-
- for ext in api['extensions']:
- name = ext['name']
- out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;')
-
- out += ['']
-
- for funcdef in api['core']['api']:
- args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
- out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
-
- args = ', '.join(['%s' % n for t, n in funcdef['arguments']])
-
- return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t'
- return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');'
-
- out.append(return_line)
- out.append('}')
- out.append('')
-
- for ext in api['extensions']:
- name = ext['name']
- for funcdef in ext['api']:
- args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
- out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
-
- args = ', '.join(['%s' % n for t, n in funcdef['arguments']])
-
- return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t'
- return_line += '_gdnative_wrapper_' + name + '_api_struct->' + funcdef['name'] + '(' + args + ');'
-
- out.append(return_line)
- out.append('}')
- out.append('')
-
- out += [
- '#ifdef __cplusplus',
- '}',
- '#endif'
- ]
-
- return '\n'.join(out)
-
-
-def build_gdnative_wrapper_code(target, source, env):
- import json
- with open(source[0].path, 'r') as fd:
- api = json.load(fd)
-
- wrapper_file = target[0]
- with open(wrapper_file.path, 'w') as fd:
- fd.write(_build_gdnative_wrapper_code(api))
-
-
-
if ARGUMENTS.get('gdnative_wrapper', False):
-#build wrapper code
- gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code)
+ gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code))
gd_wrapper_env = env.Clone()
gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/'])
diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
index 385c020a78..b525725107 100644
--- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp
+++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp
@@ -125,7 +125,7 @@ bool ARVRInterfaceGDNative::is_stereo() {
return stereo;
}
-bool ARVRInterfaceGDNative::is_initialized() {
+bool ARVRInterfaceGDNative::is_initialized() const {
bool initialized;
ERR_FAIL_COND_V(interface == NULL, false);
diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h
index e50be6e196..26d0cdf9f4 100644
--- a/modules/gdnative/arvr/arvr_interface_gdnative.h
+++ b/modules/gdnative/arvr/arvr_interface_gdnative.h
@@ -59,7 +59,7 @@ public:
virtual StringName get_name() const;
virtual int get_capabilities() const;
- virtual bool is_initialized();
+ virtual bool is_initialized() const;
virtual bool initialize();
virtual void uninitialize();
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 626e9239f8..701a13d32f 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -9,8 +9,11 @@ def get_doc_classes():
"ARVRInterfaceGDNative",
"GDNative",
"GDNativeLibrary",
+ "MultiplayerPeerGDNative",
"NativeScript",
+ "PacketPeerGDNative",
"PluginScript",
+ "StreamPeerGDNative",
]
def get_doc_path():
diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml
new file mode 100644
index 0000000000..4433179726
--- /dev/null
+++ b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerPeerGDNative" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml
index 1d3053244b..37d5b79e7a 100644
--- a/modules/gdnative/doc_classes/NativeScript.xml
+++ b/modules/gdnative/doc_classes/NativeScript.xml
@@ -57,6 +57,10 @@
</member>
<member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library">
</member>
+ <member name="script_class_icon_path" type="String" setter="set_script_class_icon_path" getter="get_script_class_icon_path">
+ </member>
+ <member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name">
+ </member>
</members>
<constants>
</constants>
diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml
index 0cfab1baf0..0ae54bc9c7 100644
--- a/modules/webm/doc_classes/ResourceImporterWebm.xml
+++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1">
+<class name="PacketPeerGDNative" inherits="PacketPeer" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml
index 5fc40a6eba..d86cd2c25a 100644
--- a/modules/theora/doc_classes/ResourceImporterTheora.xml
+++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1">
+<class name="StreamPeerGDNative" inherits="StreamPeer" category="Core" version="3.1">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index e7ebcc73af..0acd6c27d8 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -277,7 +277,7 @@ void GDNative::set_library(Ref<GDNativeLibrary> p_library) {
library = p_library;
}
-Ref<GDNativeLibrary> GDNative::get_library() {
+Ref<GDNativeLibrary> GDNative::get_library() const {
return library;
}
@@ -373,7 +373,7 @@ bool GDNative::initialize() {
if (library->should_load_once() && !GDNativeLibrary::loaded_libraries->has(lib_path)) {
Vector<Ref<GDNative> > gdnatives;
gdnatives.resize(1);
- gdnatives[0] = Ref<GDNative>(this);
+ gdnatives.write[0] = Ref<GDNative>(this);
GDNativeLibrary::loaded_libraries->insert(lib_path, gdnatives);
}
@@ -428,7 +428,7 @@ bool GDNative::terminate() {
return true;
}
-bool GDNative::is_initialized() {
+bool GDNative::is_initialized() const {
return initialized;
}
@@ -442,7 +442,7 @@ Vector<StringName> GDNativeCallRegistry::get_native_call_types() {
size_t idx = 0;
for (Map<StringName, native_call_cb>::Element *E = native_calls.front(); E; E = E->next(), idx++) {
- call_types[idx] = E->key();
+ call_types.write[idx] = E->key();
}
return call_types;
@@ -474,7 +474,7 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced
return res;
}
-Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) {
+Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) const {
if (!initialized) {
ERR_PRINT("No valid library handle, can't get symbol from GDNative object");
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index b17bb94f1c..148f85723e 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -148,16 +148,16 @@ public:
static void _bind_methods();
void set_library(Ref<GDNativeLibrary> p_library);
- Ref<GDNativeLibrary> get_library();
+ Ref<GDNativeLibrary> get_library() const;
- bool is_initialized();
+ bool is_initialized() const;
bool initialize();
bool terminate();
Variant call_native(StringName p_native_call_type, StringName p_procedure_name, Array p_arguments = Array());
- Error get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional = true);
+ Error get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional = true) const;
};
class GDNativeLibraryResourceLoader : public ResourceFormatLoader {
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 372bdf3fb1..70d2814577 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -113,6 +113,40 @@ godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self) {
return dest;
}
+godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self) {
+ godot_quat dest;
+ const Basis *self = (const Basis *)p_self;
+ *((Quat *)&dest) = self->get_quat();
+ return dest;
+}
+
+void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat) {
+ Basis *self = (Basis *)p_self;
+ const Quat *quat = (const Quat *)p_quat;
+ self->set_quat(*quat);
+}
+
+void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale) {
+ Basis *self = (Basis *)p_self;
+ const Vector3 *axis = (const Vector3 *)p_axis;
+ const Vector3 *scale = (const Vector3 *)p_scale;
+ self->set_axis_angle_scale(*axis, p_phi, *scale);
+}
+
+void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale) {
+ Basis *self = (Basis *)p_self;
+ const Vector3 *euler = (const Vector3 *)p_euler;
+ const Vector3 *scale = (const Vector3 *)p_scale;
+ self->set_euler_scale(*euler, *scale);
+}
+
+void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale) {
+ Basis *self = (Basis *)p_self;
+ const Quat *quat = (const Quat *)p_quat;
+ const Vector3 *scale = (const Vector3 *)p_scale;
+ self->set_quat_scale(*quat, *scale);
+}
+
godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self) {
godot_vector3 dest;
const Basis *self = (const Basis *)p_self;
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index 786e614158..34cc91129e 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -155,6 +155,12 @@ godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self) {
return raw_dest;
}
+godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key) {
+ Dictionary *self = (Dictionary *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ return self->erase(*key);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp
index 041990e137..73fca655a8 100644
--- a/modules/gdnative/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative/gdnative.cpp
@@ -166,6 +166,10 @@ void _gdnative_report_loading_error(const godot_object *p_library, const char *p
_err_print_error("gdnative_init", library->get_current_library_path().utf8().ptr(), 0, message.utf8().ptr());
}
+bool GDAPI godot_is_instance_valid(const godot_object *p_object) {
+ return ObjectDB::instance_validate((Object *)p_object);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/pool_arrays.cpp b/modules/gdnative/gdnative/pool_arrays.cpp
index 6688be1a0d..2b6b7a823a 100644
--- a/modules/gdnative/gdnative/pool_arrays.cpp
+++ b/modules/gdnative/gdnative/pool_arrays.cpp
@@ -35,7 +35,7 @@
#include "dvector.h"
#include "core/color.h"
-#include "core/math/math_2d.h"
+#include "core/math/vector2.h"
#include "core/math/vector3.h"
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
index 56ff7fe3a8..ddec77edcd 100644
--- a/modules/gdnative/gdnative/quat.cpp
+++ b/modules/gdnative/gdnative/quat.cpp
@@ -49,6 +49,18 @@ void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector
*dest = Quat(*axis, p_angle);
}
+void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis) {
+ const Basis *basis = (const Basis *)p_basis;
+ Quat *dest = (Quat *)r_dest;
+ *dest = Quat(*basis);
+}
+
+void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler) {
+ const Vector3 *euler = (const Vector3 *)p_euler;
+ Quat *dest = (Quat *)r_dest;
+ *dest = Quat(*euler);
+}
+
godot_real GDAPI godot_quat_get_x(const godot_quat *p_self) {
const Quat *self = (const Quat *)p_self;
return self->x;
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index 83c58db520..54b98fc4e5 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -30,7 +30,7 @@
#include "gdnative/rect2.h"
-#include "core/math/math_2d.h"
+#include "core/math/transform_2d.h"
#include "core/variant.h"
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 7f5dbc12be..8ca57392a3 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -207,7 +207,7 @@ godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_arra
Array *keys_proxy = (Array *)p_keys;
keys.resize(keys_proxy->size());
for (int i = 0; i < keys_proxy->size(); i++) {
- keys[i] = (*keys_proxy)[i];
+ keys.write[i] = (*keys_proxy)[i];
}
return self->findmk(keys);
@@ -220,7 +220,7 @@ godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot
Array *keys_proxy = (Array *)p_keys;
keys.resize(keys_proxy->size());
for (int i = 0; i < keys_proxy->size(); i++) {
- keys[i] = (*keys_proxy)[i];
+ keys.write[i] = (*keys_proxy)[i];
}
return self->findmk(keys, p_from);
@@ -233,7 +233,7 @@ godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, co
Array *keys_proxy = (Array *)p_keys;
keys.resize(keys_proxy->size());
for (int i = 0; i < keys_proxy->size(); i++) {
- keys[i] = (*keys_proxy)[i];
+ keys.write[i] = (*keys_proxy)[i];
}
return self->findmk(keys, p_from, r_key);
@@ -696,7 +696,7 @@ godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const
Array *splitter_proxy = (Array *)p_splitters;
splitters.resize(splitter_proxy->size());
for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters[i] = (*splitter_proxy)[i];
+ splitters.write[i] = (*splitter_proxy)[i];
}
godot_array result;
@@ -719,7 +719,7 @@ godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *
Array *splitter_proxy = (Array *)p_splitters;
splitters.resize(splitter_proxy->size());
for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters[i] = (*splitter_proxy)[i];
+ splitters.write[i] = (*splitter_proxy)[i];
}
godot_array result;
@@ -774,7 +774,7 @@ godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const g
Array *splitter_proxy = (Array *)p_splitters;
splitters.resize(splitter_proxy->size());
for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters[i] = (*splitter_proxy)[i];
+ splitters.write[i] = (*splitter_proxy)[i];
}
godot_array result;
@@ -797,7 +797,7 @@ godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_
Array *splitter_proxy = (Array *)p_splitters;
splitters.resize(splitter_proxy->size());
for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters[i] = (*splitter_proxy)[i];
+ splitters.write[i] = (*splitter_proxy)[i];
}
godot_array result;
diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp
index 715f2e3c08..ee6140c7d0 100644
--- a/modules/gdnative/gdnative/transform.cpp
+++ b/modules/gdnative/gdnative/transform.cpp
@@ -56,6 +56,12 @@ void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_bas
*dest = Transform(*basis, *origin);
}
+void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat) {
+ const Quat *quat = (const Quat *)p_quat;
+ Transform *dest = (Transform *)r_dest;
+ *dest = Transform(*quat);
+}
+
godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self) {
godot_basis dest;
const Transform *self = (const Transform *)p_self;
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index c69607a18a..fa0e15d9d2 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -30,7 +30,7 @@
#include "gdnative/transform2d.h"
-#include "core/math/math_2d.h"
+#include "core/math/transform_2d.h"
#include "core/variant.h"
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index 9e40b42373..c7902e06ee 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -30,7 +30,7 @@
#include "gdnative/vector2.h"
-#include "core/math/math_2d.h"
+#include "core/math/vector2.h"
#include "core/variant.h"
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 217fd87c3e..dce3d7e96b 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5,7 +5,98 @@
"major": 1,
"minor": 0
},
- "next": null,
+ "next": {
+ "type": "CORE",
+ "version": {
+ "major": 1,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_basis_get_quat",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_basis *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_quat",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_quat *", "p_quat"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_axis_angle_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["godot_real", "p_phi"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_euler_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_vector3 *", "p_euler"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_quat_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_quat *", "p_quat"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_erase_with_return",
+ "return_type": "bool",
+ "arguments": [
+ ["godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_is_instance_valid",
+ "return_type": "bool",
+ "arguments": [
+ ["const godot_object *", "p_object"]
+ ]
+ },
+ {
+ "name": "godot_quat_new_with_basis",
+ "return_type": "void",
+ "arguments": [
+ ["godot_quat *", "r_dest"],
+ ["const godot_basis *", "p_basis"]
+ ]
+ },
+ {
+ "name": "godot_quat_new_with_euler",
+ "return_type": "void",
+ "arguments": [
+ ["godot_quat *", "r_dest"],
+ ["const godot_vector3 *", "p_euler"]
+ ]
+ },
+ {
+ "name": "godot_transform_new_with_quat",
+ "return_type": "void",
+ "arguments": [
+ ["godot_transform *", "r_dest"],
+ ["const godot_quat *", "p_quat"]
+ ]
+ }
+ ]
+ },
"api": [
{
"name": "godot_color_new_rgba",
@@ -4484,7 +4575,7 @@
]
},
{
- "name": "godot_string_wide_str",
+ "name": "godot_string_wide_str",
"return_type": "const wchar_t *",
"arguments": [
["const godot_string *", "p_self"]
@@ -5253,21 +5344,21 @@
"name": "godot_string_ascii",
"return_type": "godot_char_string",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
"name": "godot_string_ascii_extended",
"return_type": "godot_char_string",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
"name": "godot_string_utf8",
"return_type": "godot_char_string",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
@@ -5765,15 +5856,15 @@
"minor": 0
},
"next": {
- "type": "NATIVESCRIPT",
- "version": {
- "major": 1,
- "minor": 1
- },
- "next": null,
- "api": [
+ "type": "NATIVESCRIPT",
+ "version": {
+ "major": 1,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
{
- "name": "godot_nativescript_set_method_argument_information",
+ "name": "godot_nativescript_set_method_argument_information",
"return_type": "void",
"arguments": [
["void *", "p_gdnative_handle"],
@@ -5784,7 +5875,7 @@
]
},
{
- "name": "godot_nativescript_set_class_documentation",
+ "name": "godot_nativescript_set_class_documentation",
"return_type": "void",
"arguments": [
["void *", "p_gdnative_handle"],
@@ -5793,7 +5884,7 @@
]
},
{
- "name": "godot_nativescript_set_method_documentation",
+ "name": "godot_nativescript_set_method_documentation",
"return_type": "void",
"arguments": [
["void *", "p_gdnative_handle"],
@@ -5803,7 +5894,7 @@
]
},
{
- "name": "godot_nativescript_set_property_documentation",
+ "name": "godot_nativescript_set_property_documentation",
"return_type": "void",
"arguments": [
["void *", "p_gdnative_handle"],
@@ -5813,7 +5904,7 @@
]
},
{
- "name": "godot_nativescript_set_signal_documentation",
+ "name": "godot_nativescript_set_signal_documentation",
"return_type": "void",
"arguments": [
["void *", "p_gdnative_handle"],
@@ -5874,10 +5965,18 @@
"return_type": "void *",
"arguments": [
["int", "p_idx"],
- ["godot_object *", "p_object"]
+ ["godot_object *", "p_object"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_profiling_add_data",
+ "return_type": "void",
+ "arguments": [
+ ["const char *", "p_signature"],
+ ["uint64_t", "p_line"]
]
}
- ]
+ ]
},
"api": [
{
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
new file mode 100644
index 0000000000..f9d1ed9dc5
--- /dev/null
+++ b/modules/gdnative/gdnative_builders.py
@@ -0,0 +1,310 @@
+"""Functions used to generate source files during build time
+
+All such functions are invoked in a subprocess on Windows to prevent build flakiness.
+
+"""
+import json
+from platform_methods import subprocess_main
+
+
+def _spaced(e):
+ return e if e[-1] == '*' else e + ' '
+
+
+def _build_gdnative_api_struct_header(api):
+ gdnative_api_init_macro = [
+ '\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;'
+ ]
+
+ for ext in api['extensions']:
+ name = ext['name']
+ gdnative_api_init_macro.append(
+ '\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name))
+
+ gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;')
+ gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ')
+ gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {')
+
+ for ext in api['extensions']:
+ name = ext['name']
+ gdnative_api_init_macro.append(
+ '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type'])
+ gdnative_api_init_macro.append(
+ '\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)'
+ ' _gdnative_wrapper_api_struct->extensions[i];'.format(name))
+ gdnative_api_init_macro.append('\t\t\t\tbreak;')
+ gdnative_api_init_macro.append('\t\t}')
+ gdnative_api_init_macro.append('\t}')
+
+ out = [
+ '/* THIS FILE IS GENERATED DO NOT EDIT */',
+ '#ifndef GODOT_GDNATIVE_API_STRUCT_H',
+ '#define GODOT_GDNATIVE_API_STRUCT_H',
+ '',
+ '#include <gdnative/gdnative.h>',
+ '#include <android/godot_android.h>',
+ '#include <arvr/godot_arvr.h>',
+ '#include <nativescript/godot_nativescript.h>',
+ '#include <pluginscript/godot_pluginscript.h>',
+ '',
+ '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)',
+ '',
+ '#ifdef __cplusplus',
+ 'extern "C" {',
+ '#endif',
+ '',
+ 'enum GDNATIVE_API_TYPES {',
+ '\tGDNATIVE_' + api['core']['type'] + ','
+ ]
+
+ for ext in api['extensions']:
+ out += ['\tGDNATIVE_EXT_' + ext['type'] + ',']
+
+ out += ['};', '']
+
+ def generate_extension_struct(name, ext, include_version=True):
+ ret_val = []
+ if ext['next']:
+ ret_val += generate_extension_struct(name, ext['next'])
+
+ ret_val += [
+ 'typedef struct godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct {',
+ '\tunsigned int type;',
+ '\tgodot_gdnative_api_version version;',
+ '\tconst godot_gdnative_api_struct *next;'
+ ]
+
+ for funcdef in ext['api']:
+ args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
+ ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ ret_val += ['} godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct;', '']
+
+ return ret_val
+
+
+ def generate_core_extension_struct(core):
+ ret_val = []
+ if core['next']:
+ ret_val += generate_core_extension_struct(core['next'])
+
+ ret_val += [
+ 'typedef struct godot_gdnative_core_' + ('{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + '_api_struct {',
+ '\tunsigned int type;',
+ '\tgodot_gdnative_api_version version;',
+ '\tconst godot_gdnative_api_struct *next;',
+ ]
+
+ for funcdef in core['api']:
+ args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
+ ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ ret_val += ['} godot_gdnative_core_' + '{0}_{1}'.format(core['version']['major'], core['version']['minor']) + '_api_struct;', '']
+
+ return ret_val
+
+
+ for ext in api['extensions']:
+ name = ext['name']
+ out += generate_extension_struct(name, ext, False)
+
+ if api['core']['next']:
+ out += generate_core_extension_struct(api['core']['next'])
+
+ out += [
+ 'typedef struct godot_gdnative_core_api_struct {',
+ '\tunsigned int type;',
+ '\tgodot_gdnative_api_version version;',
+ '\tconst godot_gdnative_api_struct *next;',
+ '\tunsigned int num_extensions;',
+ '\tconst godot_gdnative_api_struct **extensions;',
+ ]
+
+ for funcdef in api['core']['api']:
+ args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
+ out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ out += [
+ '} godot_gdnative_core_api_struct;',
+ '',
+ '#ifdef __cplusplus',
+ '}',
+ '#endif',
+ '',
+ '#endif // GODOT_GDNATIVE_API_STRUCT_H',
+ ''
+ ]
+ return '\n'.join(out)
+
+
+def _build_gdnative_api_struct_source(api):
+ out = [
+ '/* THIS FILE IS GENERATED DO NOT EDIT */',
+ '',
+ '#include <gdnative_api_struct.gen.h>',
+ ''
+ ]
+
+ def get_extension_struct_name(name, ext, include_version=True):
+ return 'godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct'
+
+ def get_extension_struct_instance_name(name, ext, include_version=True):
+ return 'api_extension_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_struct'
+
+ def get_extension_struct_definition(name, ext, include_version=True):
+
+ ret_val = []
+
+ if ext['next']:
+ ret_val += get_extension_struct_definition(name, ext['next'])
+
+ ret_val += [
+ 'extern const ' + get_extension_struct_name(name, ext, include_version) + ' ' + get_extension_struct_instance_name(name, ext, include_version) + ' = {',
+ '\tGDNATIVE_EXT_' + ext['type'] + ',',
+ '\t{' + str(ext['version']['major']) + ', ' + str(ext['version']['minor']) + '},',
+ '\t' + ('NULL' if not ext['next'] else ('(const godot_gdnative_api_struct *)&' + get_extension_struct_instance_name(name, ext['next']))) + ','
+ ]
+
+ for funcdef in ext['api']:
+ ret_val.append('\t%s,' % funcdef['name'])
+
+ ret_val += ['};\n']
+
+ return ret_val
+
+
+ def get_core_struct_definition(core):
+ ret_val = []
+
+ if core['next']:
+ ret_val += get_core_struct_definition(core['next'])
+
+ ret_val += [
+ 'extern const godot_gdnative_core_' + ('{0}_{1}_api_struct api_{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + ' = {',
+ '\tGDNATIVE_' + core['type'] + ',',
+ '\t{' + str(core['version']['major']) + ', ' + str(core['version']['minor']) + '},',
+ '\t' + ('NULL' if not core['next'] else ('(const godot_gdnative_api_struct *)& api_{0}_{1}'.format(core['version']['major'], core['version']['minor']))) + ','
+ ]
+
+ for funcdef in core['api']:
+ ret_val.append('\t%s,' % funcdef['name'])
+
+ ret_val += ['};\n']
+
+ return ret_val
+
+ for ext in api['extensions']:
+ name = ext['name']
+ out += get_extension_struct_definition(name, ext, False)
+
+ out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {']
+
+ for ext in api['extensions']:
+ name = ext['name']
+ out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,']
+
+ out += ['};\n']
+
+ if api['core']['next']:
+ out += get_core_struct_definition(api['core']['next'])
+
+ out += [
+ 'extern const godot_gdnative_core_api_struct api_struct = {',
+ '\tGDNATIVE_' + api['core']['type'] + ',',
+ '\t{' + str(api['core']['version']['major']) + ', ' + str(api['core']['version']['minor']) + '},',
+ '\tNULL,',
+ '\t' + str(len(api['extensions'])) + ',',
+ '\tgdnative_extensions_pointers,',
+ ]
+
+ for funcdef in api['core']['api']:
+ out.append('\t%s,' % funcdef['name'])
+ out.append('};\n')
+
+ return '\n'.join(out)
+
+
+def build_gdnative_api_struct(target, source, env):
+
+ with open(source[0], 'r') as fd:
+ api = json.load(fd)
+
+ header, source = target
+ with open(header, 'w') as fd:
+ fd.write(_build_gdnative_api_struct_header(api))
+ with open(source, 'w') as fd:
+ fd.write(_build_gdnative_api_struct_source(api))
+
+
+def _build_gdnative_wrapper_code(api):
+ out = [
+ '/* THIS FILE IS GENERATED DO NOT EDIT */',
+ '',
+ '#include <gdnative/gdnative.h>',
+ '#include <nativescript/godot_nativescript.h>',
+ '#include <pluginscript/godot_pluginscript.h>',
+ '#include <arvr/godot_arvr.h>',
+ '',
+ '#include <gdnative_api_struct.gen.h>',
+ '',
+ '#ifdef __cplusplus',
+ 'extern "C" {',
+ '#endif',
+ '',
+ 'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;',
+ ]
+
+ for ext in api['extensions']:
+ name = ext['name']
+ out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;')
+
+ out += ['']
+
+ for funcdef in api['core']['api']:
+ args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
+ out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ args = ', '.join(['%s' % n for t, n in funcdef['arguments']])
+
+ return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t'
+ return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');'
+
+ out.append(return_line)
+ out.append('}')
+ out.append('')
+
+ for ext in api['extensions']:
+ name = ext['name']
+ for funcdef in ext['api']:
+ args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']])
+ out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args))
+
+ args = ', '.join(['%s' % n for t, n in funcdef['arguments']])
+
+ return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t'
+ return_line += '_gdnative_wrapper_' + name + '_api_struct->' + funcdef['name'] + '(' + args + ');'
+
+ out.append(return_line)
+ out.append('}')
+ out.append('')
+
+ out += [
+ '#ifdef __cplusplus',
+ '}',
+ '#endif'
+ ]
+
+ return '\n'.join(out)
+
+
+def build_gdnative_wrapper_code(target, source, env):
+ with open(source[0], 'r') as fd:
+ api = json.load(fd)
+
+ wrapper_file = target[0]
+ with open(wrapper_file, 'w') as fd:
+ fd.write(_build_gdnative_wrapper_code(api))
+
+
+if __name__ == '__main__':
+ subprocess_main(globals())
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index 53e950b4a2..ebe2b1125b 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -62,6 +62,7 @@ extern "C" {
void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis);
void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi);
void GDAPI godot_basis_new_with_euler(godot_basis *r_dest, const godot_vector3 *p_euler);
+void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler);
godot_string GDAPI godot_basis_as_string(const godot_basis *p_self);
@@ -81,6 +82,16 @@ godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self);
godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self);
+godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self);
+
+void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat);
+
+void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale);
+
+void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale);
+
+void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale);
+
godot_real GDAPI godot_basis_tdotx(const godot_basis *p_self, const godot_vector3 *p_with);
godot_real GDAPI godot_basis_tdoty(const godot_basis *p_self, const godot_vector3 *p_with);
@@ -95,8 +106,6 @@ godot_int GDAPI godot_basis_get_orthogonal_index(const godot_basis *p_self);
void GDAPI godot_basis_new(godot_basis *r_dest);
-void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler);
-
// p_elements is a pointer to an array of 3 (!!) vector3
void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements);
diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h
index a86d60dc72..faace818ee 100644
--- a/modules/gdnative/include/gdnative/dictionary.h
+++ b/modules/gdnative/include/gdnative/dictionary.h
@@ -94,6 +94,8 @@ godot_bool GDAPI godot_dictionary_operator_equal(const godot_dictionary *p_self,
godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self);
+godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h
index 4cf6e99b06..616c305f25 100644
--- a/modules/gdnative/include/gdnative/gdnative.h
+++ b/modules/gdnative/include/gdnative/gdnative.h
@@ -282,6 +282,10 @@ void GDAPI godot_print_error(const char *p_description, const char *p_function,
void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line);
void GDAPI godot_print(const godot_string *p_message);
+// GDNATIVE CORE 1.0.1
+
+bool GDAPI godot_is_instance_valid(const godot_object *p_object);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h
index 4e86960aaf..b1290f745c 100644
--- a/modules/gdnative/include/gdnative/quat.h
+++ b/modules/gdnative/include/gdnative/quat.h
@@ -60,6 +60,8 @@ extern "C" {
void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w);
void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle);
+void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis);
+void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler);
godot_real GDAPI godot_quat_get_x(const godot_quat *p_self);
void GDAPI godot_quat_set_x(godot_quat *p_self, const godot_real val);
diff --git a/modules/gdnative/include/gdnative/transform.h b/modules/gdnative/include/gdnative/transform.h
index a646da146a..880f21c88a 100644
--- a/modules/gdnative/include/gdnative/transform.h
+++ b/modules/gdnative/include/gdnative/transform.h
@@ -62,6 +62,7 @@ extern "C" {
void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin);
void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin);
+void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat);
godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self);
void GDAPI godot_transform_set_basis(godot_transform *p_self, const godot_basis *p_v);
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index f28ba352ab..d6a729be47 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -68,6 +68,7 @@ typedef enum {
GODOT_PROPERTY_HINT_GLOBAL_DIR, ///< a directort path must be passed
GODOT_PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type
GODOT_PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines
+ GODOT_PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties
GODOT_PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSY,
GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS,
@@ -228,6 +229,8 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object);
typedef struct {
GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *);
GDCALLINGCONV void (*free_instance_binding_data)(void *, void *);
+ GDCALLINGCONV void (*refcount_incremented_instance_binding)(void *, godot_object *);
+ GDCALLINGCONV bool (*refcount_decremented_instance_binding)(void *, godot_object *);
void *data;
GDCALLINGCONV void (*free_func)(void *);
} godot_instance_binding_functions;
@@ -237,6 +240,8 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i
void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object);
+void GDAPI godot_nativescript_profiling_add_data(const char *p_signature, uint64_t p_time);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
new file mode 100644
index 0000000000..bfa688592d
--- /dev/null
+++ b/modules/gdnative/include/net/godot_net.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* godot_net.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 GODOT_NATIVENET_H
+#define GODOT_NATIVENET_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For future versions of the API we should only add new functions at the end of the structure and use the
+// version info to detect whether a call is available
+
+// Use these to populate version in your plugin
+#define GODOT_NET_API_MAJOR 3
+#define GODOT_NET_API_MINOR 1
+
+typedef struct {
+
+ godot_gdnative_api_version version; /* version of our API */
+ godot_object *data; /* User reference */
+
+ /* This is StreamPeer */
+ godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes);
+ godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int &r_received);
+ godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes);
+ godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int &r_sent);
+
+ int (*get_available_bytes)(const void *user);
+
+ void *next; /* For extension? */
+} godot_net_stream_peer;
+
+/* Binds a StreamPeerGDNative to the provided interface */
+void godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ void *next; /* For extension? */
+} godot_net_packet_peer;
+
+/* Binds a PacketPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *);
+
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int &);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ /* This is NetworkedMultiplayerPeer */
+ void (*set_transfer_mode)(void *, godot_int);
+ godot_int (*get_transfer_mode)(const void *);
+ // 0 = broadcast, 1 = server, <0 = all but abs(value)
+ void (*set_target_peer)(void *, godot_int);
+ godot_int (*get_packet_peer)(const void *);
+ godot_bool (*is_server)(const void *);
+ void (*poll)(void *);
+ // Must be > 0, 1 is for server
+ int32_t (*get_unique_id)(const void *);
+ void (*set_refuse_new_connections)(void *, godot_bool);
+ godot_bool (*is_refusing_new_connections)(const void *);
+ godot_int (*get_connection_status)(const void *);
+
+ void *next; /* For extension? Or maybe not... */
+} godot_net_multiplayer_peer;
+
+/* Binds a MultiplayerPeerGDNative to the provided interface */
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 4012e821bb..70ca8d68b8 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -110,7 +110,6 @@ struct ClassAPI {
bool is_singleton;
bool is_instanciable;
// @Unclear
- bool is_creatable;
bool is_reference;
List<MethodAPI> methods;
@@ -385,7 +384,6 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n");
source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n");
// @Unclear
- // source.push_back(String("\t\t\"createable\": ") + (api.is_creatable ? "true" : "false") + ",\n");
source.push_back("\t\t\"constants\": {\n");
for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) {
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index ace2ecac5c..72606c8340 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -365,6 +365,10 @@ void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object
return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object);
}
+void GDAPI godot_nativescript_profiling_add_data(const char *p_signature, uint64_t p_time) {
+ NativeScriptLanguage::get_singleton()->profiling_add_data(StringName(p_signature), p_time);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index d6abbc1bcf..3f88fabebd 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -62,6 +62,11 @@ void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_library", "library"), &NativeScript::set_library);
ClassDB::bind_method(D_METHOD("get_library"), &NativeScript::get_library);
+ ClassDB::bind_method(D_METHOD("set_script_class_name", "class_name"), &NativeScript::set_script_class_name);
+ ClassDB::bind_method(D_METHOD("get_script_class_name"), &NativeScript::get_script_class_name);
+ ClassDB::bind_method(D_METHOD("set_script_class_icon_path", "icon_path"), &NativeScript::set_script_class_icon_path);
+ ClassDB::bind_method(D_METHOD("get_script_class_icon_path"), &NativeScript::get_script_class_icon_path);
+
ClassDB::bind_method(D_METHOD("get_class_documentation"), &NativeScript::get_class_documentation);
ClassDB::bind_method(D_METHOD("get_method_documentation", "method"), &NativeScript::get_method_documentation);
ClassDB::bind_method(D_METHOD("get_signal_documentation", "signal_name"), &NativeScript::get_signal_documentation);
@@ -69,6 +74,9 @@ void NativeScript::_bind_methods() {
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_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");
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &NativeScript::_new, MethodInfo(Variant::OBJECT, "new"));
}
@@ -131,6 +139,22 @@ Ref<GDNativeLibrary> NativeScript::get_library() const {
return library;
}
+void NativeScript::set_script_class_name(String p_type) {
+ script_class_name = p_type;
+}
+
+String NativeScript::get_script_class_name() const {
+ return script_class_name;
+}
+
+void NativeScript::set_script_class_icon_path(String p_icon_path) {
+ script_class_icon_path = p_icon_path;
+}
+
+String NativeScript::get_script_class_icon_path() const {
+ return script_class_icon_path;
+}
+
bool NativeScript::can_instance() const {
NativeScriptDesc *script_data = get_script_desc();
@@ -149,7 +173,10 @@ Ref<Script> NativeScript::get_base_script() const {
if (!script_data)
return Ref<Script>();
- Ref<NativeScript> ns = Ref<NativeScript>(NSL->create_script());
+ NativeScript *script = (NativeScript *)NSL->create_script();
+ Ref<NativeScript> ns = Ref<NativeScript>(script);
+ ERR_FAIL_COND_V(!ns.is_valid(), Ref<Script>());
+
ns->set_class_name(script_data->base);
ns->set_library(get_library());
return ns;
@@ -553,12 +580,17 @@ bool NativeScriptInstance::set(const StringName &p_name, const Variant &p_value)
Variant name = p_name;
const Variant *args[2] = { &name, &p_value };
- E->get().method.method((godot_object *)owner,
+ godot_variant result;
+ result = E->get().method.method((godot_object *)owner,
E->get().method.method_data,
userdata,
2,
(godot_variant **)args);
- return true;
+ bool handled = *(Variant *)&result;
+ godot_variant_destroy(&result);
+ if (handled) {
+ return true;
+ }
}
script_data = script_data->base_data;
@@ -593,10 +625,9 @@ bool NativeScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
(godot_variant **)args);
r_ret = *(Variant *)&result;
godot_variant_destroy(&result);
- if (r_ret.get_type() == Variant::NIL) {
- return false;
+ if (r_ret.get_type() != Variant::NIL) {
+ return true;
}
- return true;
}
script_data = script_data->base_data;
@@ -697,11 +728,21 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
if (E) {
godot_variant result;
+
+#ifdef DEBUG_ENABLED
+ current_method_call = p_method;
+#endif
+
result = E->get().method.method((godot_object *)owner,
E->get().method.method_data,
userdata,
p_argcount,
(godot_variant **)p_args);
+
+#ifdef DEBUG_ENABLED
+ current_method_call = "";
+#endif
+
Variant res = *(Variant *)&result;
godot_variant_destroy(&result);
r_error.error = Variant::CallError::CALL_OK;
@@ -716,6 +757,15 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
}
void NativeScriptInstance::notification(int p_notification) {
+#ifdef DEBUG_ENABLED
+ if (p_notification == MainLoop::NOTIFICATION_CRASH) {
+ if (current_method_call != StringName("")) {
+ ERR_PRINTS("NativeScriptInstance detected crash on method: " + current_method_call);
+ current_method_call = "";
+ }
+ }
+#endif
+
Variant value = p_notification;
const Variant *args[1] = { &value };
call_multilevel("_notification", args, 1);
@@ -960,6 +1010,10 @@ NativeScriptLanguage::NativeScriptLanguage() {
has_objects_to_register = false;
mutex = Mutex::create();
#endif
+
+#ifdef DEBUG_ENABLED
+ profiling = false;
+#endif
}
NativeScriptLanguage::~NativeScriptLanguage() {
@@ -1034,7 +1088,7 @@ Ref<Script> NativeScriptLanguage::get_template(const String &p_class_name, const
s->set_class_name(p_class_name);
return Ref<NativeScript>(s);
}
-bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
return true;
}
@@ -1102,17 +1156,105 @@ void NativeScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_
}
void NativeScriptLanguage::profiling_start() {
+#ifdef DEBUG_ENABLED
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+
+ profile_data.clear();
+ profiling = true;
+#endif
}
void NativeScriptLanguage::profiling_stop() {
+#ifdef DEBUG_ENABLED
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+
+ profiling = false;
+#endif
}
int NativeScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) {
+#ifdef DEBUG_ENABLED
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+ int current = 0;
+
+ for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
+ if (current >= p_info_max)
+ break;
+
+ p_info_arr[current].call_count = d->get().call_count;
+ p_info_arr[current].self_time = d->get().self_time;
+ p_info_arr[current].total_time = d->get().total_time;
+ p_info_arr[current].signature = d->get().signature;
+ current++;
+ }
+
+ return current;
+#else
return 0;
+#endif
}
int NativeScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) {
+#ifdef DEBUG_ENABLED
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+ int current = 0;
+
+ for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
+ if (current >= p_info_max)
+ break;
+
+ if (d->get().last_frame_call_count) {
+ p_info_arr[current].call_count = d->get().last_frame_call_count;
+ p_info_arr[current].self_time = d->get().last_frame_self_time;
+ p_info_arr[current].total_time = d->get().last_frame_total_time;
+ p_info_arr[current].signature = d->get().signature;
+ current++;
+ }
+ }
+
+ return current;
+#else
return 0;
+#endif
+}
+
+void NativeScriptLanguage::profiling_add_data(StringName p_signature, uint64_t p_time) {
+#ifdef DEBUG_ENABLED
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+
+ Map<StringName, ProfileData>::Element *d = profile_data.find(p_signature);
+ if (d) {
+ d->get().call_count += 1;
+ d->get().total_time += p_time;
+ d->get().frame_call_count += 1;
+ d->get().frame_total_time += p_time;
+ } else {
+ ProfileData data;
+
+ data.signature = p_signature;
+ data.call_count = 1;
+ data.self_time = 0;
+ data.total_time = p_time;
+ data.frame_call_count = 1;
+ data.frame_self_time = 0;
+ data.frame_total_time = p_time;
+ data.last_frame_call_count = 0;
+ data.last_frame_self_time = 0;
+ data.last_frame_total_time = 0;
+
+ profile_data.insert(p_signature, data);
+ }
+#endif
}
int NativeScriptLanguage::register_binding_functions(godot_instance_binding_functions p_binding_functions) {
@@ -1135,8 +1277,8 @@ int NativeScriptLanguage::register_binding_functions(godot_instance_binding_func
}
// set the functions
- binding_functions[idx].first = true;
- binding_functions[idx].second = p_binding_functions;
+ binding_functions.write[idx].first = true;
+ binding_functions.write[idx].second = p_binding_functions;
return idx;
}
@@ -1151,7 +1293,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) {
binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]);
}
- binding_functions[p_idx].first = false;
+ binding_functions.write[p_idx].first = false;
if (binding_functions[p_idx].second.free_func)
binding_functions[p_idx].second.free_func(binding_functions[p_idx].second.data);
@@ -1177,7 +1319,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
binding_data->resize(p_idx + 1);
for (int i = old_size; i <= p_idx; i++) {
- (*binding_data)[i] = NULL;
+ (*binding_data).write[i] = NULL;
}
}
@@ -1186,7 +1328,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
// no binding data yet, soooooo alloc new one \o/
- (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
+ (*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
}
return (*binding_data)[p_idx];
@@ -1199,7 +1341,7 @@ void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) {
binding_data->resize(binding_functions.size());
for (int i = 0; i < binding_functions.size(); i++) {
- (*binding_data)[i] = NULL;
+ (*binding_data).write[i] = NULL;
}
binding_instances.insert(binding_data);
@@ -1228,6 +1370,54 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
delete &binding_data;
}
+void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_object) {
+
+ void *data = p_object->get_script_instance_binding(lang_idx);
+
+ if (!data)
+ return;
+
+ Vector<void *> &binding_data = *(Vector<void *> *)data;
+
+ for (int i = 0; i < binding_data.size(); i++) {
+ if (!binding_data[i])
+ continue;
+
+ if (!binding_functions[i].first)
+ continue;
+
+ if (binding_functions[i].second.refcount_incremented_instance_binding) {
+ binding_functions[i].second.refcount_incremented_instance_binding(binding_data[i], p_object);
+ }
+ }
+}
+
+bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_object) {
+
+ void *data = p_object->get_script_instance_binding(lang_idx);
+
+ if (!data)
+ return true;
+
+ Vector<void *> &binding_data = *(Vector<void *> *)data;
+
+ bool can_die = true;
+
+ for (int i = 0; i < binding_data.size(); i++) {
+ if (!binding_data[i])
+ continue;
+
+ if (!binding_functions[i].first)
+ continue;
+
+ if (binding_functions[i].second.refcount_decremented_instance_binding) {
+ can_die = can_die && binding_functions[i].second.refcount_decremented_instance_binding(binding_data[i], p_object);
+ }
+ }
+
+ return can_die;
+}
+
void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) {
if (!global_type_tags.has(p_idx)) {
global_type_tags.insert(p_idx, HashMap<StringName, const void *>());
@@ -1355,6 +1545,24 @@ void NativeScriptLanguage::frame() {
has_objects_to_register = false;
}
#endif
+
+#ifdef DEBUG_ENABLED
+ {
+#ifndef NO_THREADS
+ MutexLock lock(mutex);
+#endif
+
+ for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
+ d->get().last_frame_call_count = d->get().frame_call_count;
+ d->get().last_frame_self_time = d->get().frame_self_time;
+ d->get().last_frame_total_time = d->get().frame_total_time;
+ d->get().frame_call_count = 0;
+ d->get().frame_self_time = 0;
+ d->get().frame_total_time = 0;
+ }
+ }
+#endif
+
call_libraries_cb(_frame_call_name);
}
@@ -1370,6 +1578,26 @@ void NativeScriptLanguage::thread_exit() {
#endif // NO_THREADS
+bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const {
+ return p_type == "NativeScript";
+}
+
+String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
+ Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript");
+ if (script.is_valid()) {
+ if (r_base_type)
+ *r_base_type = script->get_instance_base_type();
+ if (r_icon_path)
+ *r_icon_path = script->get_script_class_icon_path();
+ return script->get_script_class_name();
+ }
+ if (r_base_type)
+ *r_base_type = String();
+ if (r_icon_path)
+ *r_icon_path = String();
+ return String();
+}
+
void NativeReloadNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_notification"), &NativeReloadNode::_notification);
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index b47962dc37..ebfe36fd94 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -118,6 +118,9 @@ class NativeScript : public Script {
String class_name;
+ String script_class_name;
+ String script_class_icon_path;
+
#ifndef NO_THREADS
Mutex *owners_lock;
#endif
@@ -135,6 +138,11 @@ public:
void set_library(Ref<GDNativeLibrary> p_library);
Ref<GDNativeLibrary> get_library() const;
+ void set_script_class_name(String p_type);
+ String get_script_class_name() const;
+ void set_script_class_icon_path(String p_icon_path);
+ String get_script_class_icon_path() const;
+
virtual bool can_instance() const;
virtual Ref<Script> get_base_script() const; //for script inheritance
@@ -181,6 +189,9 @@ class NativeScriptInstance : public ScriptInstance {
Object *owner;
Ref<NativeScript> script;
+#ifdef DEBUG_ENABLED
+ StringName current_method_call;
+#endif
void _ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount);
@@ -243,6 +254,22 @@ private:
Map<int, HashMap<StringName, const void *> > global_type_tags;
+ struct ProfileData {
+ StringName signature;
+ uint64_t call_count;
+ uint64_t self_time;
+ uint64_t total_time;
+ uint64_t frame_call_count;
+ uint64_t frame_self_time;
+ uint64_t frame_total_time;
+ uint64_t last_frame_call_count;
+ uint64_t last_frame_self_time;
+ uint64_t last_frame_total_time;
+ };
+
+ Map<StringName, ProfileData> profile_data;
+ bool profiling;
+
public:
// These two maps must only be touched on the main thread
Map<String, Map<StringName, NativeScriptDesc> > library_classes;
@@ -292,7 +319,7 @@ public:
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -326,9 +353,16 @@ public:
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
+ virtual void refcount_incremented_instance_binding(Object *p_object);
+ virtual bool refcount_decremented_instance_binding(Object *p_object);
void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag);
const void *get_global_type_tag(int p_idx, StringName p_class_name) const;
+
+ virtual bool handles_global_class_type(const String &p_type) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const;
+
+ void profiling_add_data(StringName p_signature, uint64_t p_time);
};
inline NativeScriptDesc *NativeScript::get_script_desc() const {
diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub
new file mode 100644
index 0000000000..53f9271128
--- /dev/null
+++ b/modules/gdnative/net/SCsub
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+import os
+import methods
+
+Import('env')
+Import('env_modules')
+
+env_net_gdnative = env_modules.Clone()
+
+env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/'])
+env_net_gdnative.add_source_files(env.modules_sources, '*.cpp')
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
new file mode 100644
index 0000000000..e2d710b5ad
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "multiplayer_peer_gdnative.h"
+
+MultiplayerPeerGDNative::MultiplayerPeerGDNative() {
+ interface = NULL;
+}
+
+MultiplayerPeerGDNative::~MultiplayerPeerGDNative() {
+}
+
+void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) {
+ interface = p_interface;
+}
+
+Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
+}
+
+Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
+}
+
+int MultiplayerPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
+}
+
+/* NetworkedMultiplayerPeer */
+void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_transfer_mode(interface->data, (godot_int)p_mode);
+}
+
+NetworkedMultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
+ ERR_FAIL_COND_V(interface == NULL, TRANSFER_MODE_UNRELIABLE);
+ return (TransferMode)interface->get_transfer_mode(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_target_peer(interface->data, p_peer_id);
+}
+
+int MultiplayerPeerGDNative::get_packet_peer() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_packet_peer(interface->data);
+}
+
+bool MultiplayerPeerGDNative::is_server() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->is_server(interface->data);
+}
+
+void MultiplayerPeerGDNative::poll() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->poll(interface->data);
+}
+
+int MultiplayerPeerGDNative::get_unique_id() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_unique_id(interface->data);
+}
+
+void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_refuse_new_connections(interface->data, p_enable);
+}
+
+bool MultiplayerPeerGDNative::is_refusing_new_connections() const {
+ ERR_FAIL_COND_V(interface == NULL, true);
+ return interface->is_refusing_new_connections(interface->data);
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const {
+ ERR_FAIL_COND_V(interface == NULL, CONNECTION_DISCONNECTED);
+ return (ConnectionStatus)interface->get_connection_status(interface->data);
+}
+
+void MultiplayerPeerGDNative::_bind_methods() {
+}
+
+extern "C" {
+
+void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) {
+
+ ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl);
+}
+}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
new file mode 100644
index 0000000000..c8c95b3dd7
--- /dev/null
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* multiplayer_peer_gdnative.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 MULTIPLAYER_PEER_GDNATIVE_H
+#define MULTIPLAYER_PEER_GDNATIVE_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
+
+class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer {
+ GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer)
+
+protected:
+ static void _bind_methods();
+ const godot_net_multiplayer_peer *interface;
+
+public:
+ MultiplayerPeerGDNative();
+ ~MultiplayerPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
+
+ /* Specific to PacketPeer */
+ 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;
+ virtual int get_available_packet_count() const;
+
+ /* Specific to NetworkedMultiplayerPeer */
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual TransferMode get_transfer_mode() const;
+ virtual void set_target_peer(int p_peer_id);
+
+ virtual int get_packet_peer() const;
+
+ virtual bool is_server() const;
+
+ virtual void poll();
+
+ virtual int get_unique_id() const;
+
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual ConnectionStatus get_connection_status() const;
+};
+
+#endif // MULTIPLAYER_PEER_GDNATIVE_H
diff --git a/modules/webm/resource_importer_webm.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp
index 7124a503e8..ceae79edc0 100644
--- a/modules/webm/resource_importer_webm.cpp
+++ b/modules/gdnative/net/packet_peer_gdnative.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.cpp */
+/* packet_peer_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,69 +28,46 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_webm.h"
+#include "packet_peer_gdnative.h"
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-#include "video_stream_webm.h"
-
-String ResourceImporterWebm::get_importer_name() const {
-
- return "Webm";
+PacketPeerGDNative::PacketPeerGDNative() {
+ interface = NULL;
}
-String ResourceImporterWebm::get_visible_name() const {
-
- return "Webm";
+PacketPeerGDNative::~PacketPeerGDNative() {
}
-void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("webm");
+void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) {
+ interface = p_impl;
}
-String ResourceImporterWebm::get_save_extension() const {
- return "webmstr";
+void PacketPeerGDNative::_bind_methods() {
}
-String ResourceImporterWebm::get_resource_type() const {
-
- return "VideoStreamWebm";
+Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->get_packet(interface->data, r_buffer, r_buffer_size);
}
-bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
+Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
}
-int ResourceImporterWebm::get_preset_count() const {
- return 0;
+int PacketPeerGDNative::get_max_packet_size() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_max_packet_size(interface->data);
}
-String ResourceImporterWebm::get_preset_name(int p_idx) const {
- return String();
+int PacketPeerGDNative::get_available_packet_count() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_packet_count(interface->data);
}
-void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+extern "C" {
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
-}
+void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) {
-Error ResourceImporterWebm::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) {
-
- FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ);
- if (!f) {
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
- }
- memdelete(f);
-
- VideoStreamWebm *stream = memnew(VideoStreamWebm);
- stream->set_file(p_source_file);
-
- Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
-
- return ResourceSaver::save(p_save_path + ".webmstr", webm_stream);
+ ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl);
}
-
-ResourceImporterWebm::ResourceImporterWebm() {
}
diff --git a/modules/webm/resource_importer_webm.h b/modules/gdnative/net/packet_peer_gdnative.h
index d61e6e2a93..71814177ed 100644
--- a/modules/webm/resource_importer_webm.h
+++ b/modules/gdnative/net/packet_peer_gdnative.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_webm.h */
+/* packet_peer_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,29 +28,32 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTERWEBM_H
-#define RESOURCEIMPORTERWEBM_H
+#ifndef PACKET_PEER_GDNATIVE_H
+#define PACKET_PEER_GDNATIVE_H
-#include "io/resource_import.h"
+#include "core/io/packet_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
-class ResourceImporterWebm : public ResourceImporter {
- GDCLASS(ResourceImporterWebm, ResourceImporter)
-public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual String get_save_extension() const;
- virtual String get_resource_type() const;
+class PacketPeerGDNative : public PacketPeer {
+ GDCLASS(PacketPeerGDNative, PacketPeer)
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+protected:
+ static void _bind_methods();
+ const godot_net_packet_peer *interface;
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+public:
+ PacketPeerGDNative();
+ ~PacketPeerGDNative();
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL);
+ /* Sets the interface implementation from GDNative */
+ void set_native_packet_peer(const godot_net_packet_peer *p_impl);
- ResourceImporterWebm();
+ /* Specific to PacketPeer */
+ 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;
+ virtual int get_available_packet_count() const;
};
-#endif // RESOURCEIMPORTERWEBM_H
+#endif // PACKET_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp
new file mode 100644
index 0000000000..c3fb4d8008
--- /dev/null
+++ b/modules/gdnative/net/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "multiplayer_peer_gdnative.h"
+#include "packet_peer_gdnative.h"
+#include "stream_peer_gdnative.h"
+
+void register_net_types() {
+ ClassDB::register_class<MultiplayerPeerGDNative>();
+ ClassDB::register_class<PacketPeerGDNative>();
+ ClassDB::register_class<StreamPeerGDNative>();
+}
+
+void unregister_net_types() {
+}
diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h
new file mode 100644
index 0000000000..9545a2ba8f
--- /dev/null
+++ b/modules/gdnative/net/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.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. */
+/*************************************************************************/
+
+void register_net_types();
+void unregister_net_types();
diff --git a/modules/theora/resource_importer_theora.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp
index ee9bab74a7..4d1f2ec9a5 100644
--- a/modules/theora/resource_importer_theora.cpp
+++ b/modules/gdnative/net/stream_peer_gdnative.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_theora.cpp */
+/* stream_peer_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,63 +28,50 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_theora.h"
+#include "stream_peer_gdnative.h"
-#include "io/resource_saver.h"
-#include "os/file_access.h"
-#include "scene/resources/texture.h"
-
-String ResourceImporterTheora::get_importer_name() const {
-
- return "Theora";
+StreamPeerGDNative::StreamPeerGDNative() {
+ interface = NULL;
}
-String ResourceImporterTheora::get_visible_name() const {
-
- return "Theora";
+StreamPeerGDNative::~StreamPeerGDNative() {
}
-void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ogv");
- p_extensions->push_back("ogm");
+void StreamPeerGDNative::set_native_stream_peer(godot_net_stream_peer *p_interface) {
+ interface = p_interface;
}
-String ResourceImporterTheora::get_save_extension() const {
- return "ogvstr";
+void StreamPeerGDNative::_bind_methods() {
}
-String ResourceImporterTheora::get_resource_type() const {
-
- return "VideoStreamTheora";
+Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_data(interface->data, p_data, p_bytes));
}
-bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
-
- return true;
+Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, r_sent));
}
-int ResourceImporterTheora::get_preset_count() const {
- return 0;
+Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_data(interface->data, p_buffer, p_bytes));
}
-String ResourceImporterTheora::get_preset_name(int p_idx) const {
- return String();
+Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, r_received));
}
-void ResourceImporterTheora::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+int StreamPeerGDNative::get_available_bytes() const {
+ ERR_FAIL_COND_V(interface == NULL, 0);
+ return interface->get_available_bytes(interface->data);
}
-Error ResourceImporterTheora::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) {
-
- VideoStreamTheora *stream = memnew(VideoStreamTheora);
- stream->set_file(p_source_file);
+extern "C" {
- Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
-
- return ResourceSaver::save(p_save_path + ".ogvstr", ogv_stream);
+void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, godot_net_stream_peer *p_interface) {
+ ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface);
}
-
-ResourceImporterTheora::ResourceImporterTheora() {
}
diff --git a/modules/theora/resource_importer_theora.h b/modules/gdnative/net/stream_peer_gdnative.h
index e3c79287ad..654234e6ab 100644
--- a/modules/theora/resource_importer_theora.h
+++ b/modules/gdnative/net/stream_peer_gdnative.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_importer_theora.h */
+/* stream_peer_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,31 +28,34 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTEROGGTHEORA_H
-#define RESOURCEIMPORTEROGGTHEORA_H
+#ifndef STREAM_PEER_GDNATIVE_H
+#define STREAM_PEER_GDNATIVE_H
-#include "video_stream_theora.h"
+#include "core/io/stream_peer.h"
+#include "modules/gdnative/gdnative.h"
+#include "modules/gdnative/include/net/godot_net.h"
-#include "core/io/resource_import.h"
+class StreamPeerGDNative : public StreamPeer {
-class ResourceImporterTheora : public ResourceImporter {
- GDCLASS(ResourceImporterTheora, ResourceImporter)
-public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual String get_save_extension() const;
- virtual String get_resource_type() const;
-
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+ GDCLASS(StreamPeerGDNative, StreamPeer);
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+protected:
+ static void _bind_methods();
+ godot_net_stream_peer *interface;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL);
-
- ResourceImporterTheora();
+public:
+ StreamPeerGDNative();
+ ~StreamPeerGDNative();
+
+ /* Sets the interface implementation from GDNative */
+ void set_native_stream_peer(godot_net_stream_peer *p_interface);
+
+ /* Specific to StreamPeer */
+ Error put_data(const uint8_t *p_data, int p_bytes);
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
+ Error get_data(uint8_t *p_buffer, int p_bytes);
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
+ int get_available_bytes() const;
};
-#endif // RESOURCEIMPORTEROGGTHEORA_H
+#endif // STREAM_PEER_GDNATIVE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index 8018178bd5..2b538c4a36 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -108,7 +108,7 @@ Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const
return script;
}
-bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
PoolStringArray functions;
if (_desc.validate) {
bool ret = _desc.validate(
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 709345885b..c4df6f3a33 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -74,7 +74,7 @@ public:
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index a0b6fbeb75..ae1a218392 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -38,6 +38,7 @@
#include "arvr/register_types.h"
#include "nativescript/register_types.h"
+#include "net/register_types.h"
#include "pluginscript/register_types.h"
#include "core/engine.h"
@@ -321,6 +322,7 @@ void register_gdnative_types() {
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
+ register_net_types();
register_arvr_types();
register_nativescript_types();
register_pluginscript_types();
@@ -339,10 +341,10 @@ void register_gdnative_types() {
Ref<GDNativeLibrary> lib = ResourceLoader::load(path);
- singleton_gdnatives[i].instance();
- singleton_gdnatives[i]->set_library(lib);
+ singleton_gdnatives.write[i].instance();
+ singleton_gdnatives.write[i]->set_library(lib);
- if (!singleton_gdnatives[i]->initialize()) {
+ if (!singleton_gdnatives.write[i]->initialize()) {
// Can't initialize. Don't make a native_call then
continue;
}
@@ -372,13 +374,14 @@ void unregister_gdnative_types() {
continue;
}
- singleton_gdnatives[i]->terminate();
+ singleton_gdnatives.write[i]->terminate();
}
singleton_gdnatives.clear();
unregister_pluginscript_types();
unregister_nativescript_types();
unregister_arvr_types();
+ unregister_net_types();
memdelete(GDNativeCallRegistry::singleton);
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index fedc510f01..f2a1a5b50c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -75,9 +75,11 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_keyword = false;
bool in_word = false;
bool in_function_name = false;
+ bool in_variable_declaration = false;
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
+ bool expect_type = false;
Color keyword_color;
Color color;
@@ -205,6 +207,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (str[k] == '(') {
in_function_name = true;
+ } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_VAR)) {
+ in_variable_declaration = true;
}
}
@@ -222,6 +226,28 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (is_symbol) {
in_function_name = false;
in_member_variable = false;
+
+ if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') {
+ expect_type = false;
+ }
+ if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
+ expect_type = true;
+ }
+
+ if (in_variable_declaration || previous_text == "(" || previous_text == ",") {
+ int k = j;
+ // Skip space
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == ':') {
+ // has type hint
+ expect_type = true;
+ }
+ }
+
+ in_variable_declaration = false;
}
if (!in_node_path && in_region == -1 && str[j] == '$') {
@@ -256,6 +282,9 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
} else if (is_number) {
next_type = NUMBER;
color = number_color;
+ } else if (expect_type) {
+ next_type = TYPE;
+ color = type_color;
} else {
next_type = IDENTIFIER;
}
@@ -330,6 +359,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
+ type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
}
SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 0296ab7652..b8cb4a65e9 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -44,7 +44,8 @@ private:
FUNCTION,
KEYWORD,
MEMBER,
- IDENTIFIER
+ IDENTIFIER,
+ TYPE,
};
// colours
@@ -56,6 +57,7 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
+ Color type_color;
public:
static SyntaxHighlighter *create();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index f23e7854a5..33d9c011f2 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -126,7 +126,10 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
GDScriptLanguage::singleton->lock->unlock();
#endif
- ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing
+ if (r_error.error != Variant::CallError::CALL_OK) {
+ memdelete(instance);
+ ERR_FAIL_COND_V(r_error.error != Variant::CallError::CALL_OK, NULL); //error constructing
+ }
}
//@TODO make thread safe
@@ -181,7 +184,11 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Variant::CallErro
bool GDScript::can_instance() const {
- return valid || (!tool && !ScriptServer::is_scripting_enabled());
+#ifdef TOOLS_ENABLED
+ return valid && (tool || ScriptServer::is_scripting_enabled());
+#else
+ return valid;
+#endif
}
Ref<Script> GDScript::get_base_script() const {
@@ -220,16 +227,14 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
+ GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- PropertyInfo arg;
- arg.type = Variant::NIL; //variant
- arg.name = E->get()->get_argument_name(i);
- mi.arguments.push_back(arg);
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ mi.arguments.push_back(func->get_argument_type(i));
}
- mi.return_val.name = "Variant";
+ mi.return_val = func->get_return_type();
p_list->push_back(mi);
}
}
@@ -277,16 +282,14 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
if (!E)
return MethodInfo();
+ GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- PropertyInfo arg;
- arg.type = Variant::NIL; //variant
- arg.name = E->get()->get_argument_name(i);
- mi.arguments.push_back(arg);
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ mi.arguments.push_back(func->get_argument_type(i));
}
- mi.return_val.name = "Variant";
+ mi.return_val = func->get_return_type();
return mi;
}
@@ -294,11 +297,6 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant
#ifdef TOOLS_ENABLED
- /*
- for (const Map<StringName,Variant>::Element *I=member_default_values.front();I;I=I->next()) {
- print_line("\t"+String(String(I->key())+":"+String(I->get())));
- }
- */
const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property);
if (E) {
r_value = E->get();
@@ -314,27 +312,6 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant
ScriptInstance *GDScript::instance_create(Object *p_this) {
- if (!tool && !ScriptServer::is_scripting_enabled()) {
-
-#ifdef TOOLS_ENABLED
-
- //instance a fake script for editing the values
- //plist.invert();
-
- /*print_line("CREATING PLACEHOLDER");
- for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
- print_line(E->get().name);
- }*/
- PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
- placeholders.insert(si);
- //_update_placeholder(si);
- _update_exports();
- return si;
-#else
- return NULL;
-#endif
- }
-
GDScript *top = this;
while (top->_base)
top = top->_base;
@@ -353,6 +330,18 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
Variant::CallError unchecked_error;
return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error);
}
+
+PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) {
+#ifdef TOOLS_ENABLED
+ PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
+ placeholders.insert(si);
+ _update_exports();
+ return si;
+#else
+ return NULL;
+#endif
+}
+
bool GDScript::instance_has(const Object *p_this) const {
#ifndef NO_THREADS
@@ -409,7 +398,6 @@ bool GDScript::_update_exports() {
bool changed = false;
if (source_changed_cache) {
- //print_line("updating source for "+get_path());
source_changed_cache = false;
changed = true;
@@ -456,11 +444,8 @@ bool GDScript::_update_exports() {
if (bf.is_valid()) {
- //print_line("parent is: "+bf->get_path());
base_cache = bf;
bf->inheriters_cache.insert(get_instance_id());
-
- //bf->_update_exports(p_instances,true,false);
}
} else {
ERR_PRINT(("Path extending itself in " + path).utf8().get_data());
@@ -475,7 +460,6 @@ bool GDScript::_update_exports() {
continue;
members_cache.push_back(c->variables[i]._export);
- //print_line("found "+c->variables[i]._export.name);
member_default_values_cache[c->variables[i].identifier] = c->variables[i].default_value;
}
@@ -484,9 +468,12 @@ bool GDScript::_update_exports() {
for (int i = 0; i < c->_signals.size(); i++) {
_signals[c->_signals[i].name] = c->_signals[i].arguments;
}
+ } else {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
}
} else {
- //print_line("unchanged is "+get_path());
}
if (base_cache.is_valid()) {
@@ -495,17 +482,15 @@ bool GDScript::_update_exports() {
}
}
- if (/*changed &&*/ placeholders.size()) { //hm :(
-
- //print_line("updating placeholders for "+get_path());
+ if (placeholders.size()) { //hm :(
- //update placeholders if any
+ // update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
-
+ E->get()->set_build_failed(false);
E->get()->update(propnames, values);
}
}
@@ -525,7 +510,6 @@ void GDScript::update_exports() {
Set<ObjectID> copy = inheriters_cache; //might get modified
- //print_line("update exports for "+get_path()+" ic: "+itos(copy.size()));
for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) {
Object *id = ObjectDB::get_instance(E->get());
GDScript *s = Object::cast_to<GDScript>(id);
@@ -600,6 +584,15 @@ Error GDScript::reload(bool p_keep_state) {
return err;
}
}
+#if DEBUG_ENABLED
+ for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
+ const GDScriptWarning &warning = E->get();
+ if (ScriptDebugger::get_singleton()) {
+ Vector<ScriptLanguage::StackInfo> si;
+ ScriptDebugger::get_singleton()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+ }
+ }
+#endif
valid = true;
@@ -729,22 +722,36 @@ Error GDScript::load_byte_code(const String &p_path) {
FileAccess *fa = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V(!fa, ERR_CANT_OPEN);
+
FileAccessEncrypted *fae = memnew(FileAccessEncrypted);
ERR_FAIL_COND_V(!fae, ERR_CANT_OPEN);
+
Vector<uint8_t> key;
key.resize(32);
for (int i = 0; i < key.size(); i++) {
- key[i] = script_encryption_key[i];
+ key.write[i] = script_encryption_key[i];
}
+
Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_READ);
- ERR_FAIL_COND_V(err, err);
+
+ if (err) {
+ fa->close();
+ memdelete(fa);
+ memdelete(fae);
+
+ ERR_FAIL_COND_V(err, err);
+ }
+
bytecode.resize(fae->get_len());
fae->get_buffer(bytecode.ptrw(), bytecode.size());
+ fae->close();
memdelete(fae);
+
} else {
bytecode = FileAccess::get_file_as_array(p_path);
}
+
ERR_FAIL_COND_V(bytecode.size() == 0, ERR_PARSE_ERROR);
path = p_path;
@@ -812,7 +819,6 @@ Error GDScript::load_source_code(const String &p_path) {
#ifdef TOOLS_ENABLED
source_changed_cache = true;
#endif
- //print_line("LSC :"+get_path());
path = p_path;
return OK;
}
@@ -941,8 +947,12 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (err.error == Variant::CallError::CALL_OK) {
return true; //function exists, call was successful
}
- } else
- members[E->get().index] = p_value;
+ } else {
+ if (!E->get().data_type.is_type(p_value)) {
+ return false; // Type mismatch
+ }
+ members.write[E->get().index] = p_value;
+ }
return true;
}
}
@@ -1270,7 +1280,7 @@ void GDScriptInstance::reload_members() {
if (member_indices_cache.has(E->key())) {
Variant value = members[member_indices_cache[E->key()]];
- new_members[E->get().index] = value;
+ new_members.write[E->get().index] = value;
}
}
@@ -1320,7 +1330,7 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va
if (globals.has(p_name)) {
//overwrite existing
- global_array[globals[p_name]] = p_value;
+ global_array.write[globals[p_name]] = p_value;
return;
}
globals[p_name] = global_array.size();
@@ -1490,7 +1500,6 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time;
p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time;
p_info_arr[current].signature = elem->self()->profile.signature;
- //print_line(String(elem->self()->profile.signature)+": "+itos(elem->self()->profile.last_frame_call_count));
current++;
}
elem = elem->next();
@@ -1529,7 +1538,7 @@ struct GDScriptDepSort {
void GDScriptLanguage::reload_all_scripts() {
#ifdef DEBUG_ENABLED
- print_line("RELOAD ALL SCRIPTS");
+ print_verbose("GDScript: Reloading all scripts");
if (lock) {
lock->lock();
}
@@ -1539,7 +1548,7 @@ void GDScriptLanguage::reload_all_scripts() {
SelfList<GDScript> *elem = script_list.first();
while (elem) {
if (elem->self()->get_path().is_resource_file()) {
- print_line("FOUND: " + elem->self()->get_path());
+ print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
elem = elem->next();
@@ -1555,7 +1564,7 @@ void GDScriptLanguage::reload_all_scripts() {
for (List<Ref<GDScript> >::Element *E = scripts.front(); E; E = E->next()) {
- print_line("RELOADING: " + E->get()->get_path());
+ print_verbose("GDScript: Reloading: " + E->get()->get_path());
E->get()->load_source_code(E->get()->get_path());
E->get()->reload(true);
}
@@ -1686,7 +1695,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
void GDScriptLanguage::frame() {
- //print_line("calls: "+itos(calls));
calls = 0;
#ifdef DEBUG_ENABLED
@@ -1735,10 +1743,13 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"NAN",
"self",
"true",
+ "void",
// functions
+ "as",
"assert",
"breakpoint",
"class",
+ "class_name",
"extends",
"is",
"func",
@@ -1788,6 +1799,244 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
+
+ return p_type == "GDScript";
+}
+
+String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
+
+ PoolVector<uint8_t> sourcef;
+ Error err;
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (err) {
+ return String();
+ }
+
+ int len = f->get_len();
+ sourcef.resize(len + 1);
+ PoolVector<uint8_t>::Write w = sourcef.write();
+ int r = f->get_buffer(w.ptr(), len);
+ f->close();
+ memdelete(f);
+ ERR_FAIL_COND_V(r != len, String());
+ w[len] = 0;
+
+ String s;
+ if (s.parse_utf8((const char *)w.ptr())) {
+ return String();
+ }
+
+ GDScriptParser parser;
+
+ parser.parse(s, p_path.get_base_dir(), true, p_path);
+
+ if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
+
+ const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
+ if (r_base_type) {
+ GDScriptParser::DataType base_type;
+ if (c->base_type.has_type) {
+ base_type = c->base_type;
+ while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ } else {
+ base_type = GDScriptParser::DataType();
+ }
+ } break;
+ default: {
+ base_type = GDScriptParser::DataType();
+ } break;
+ }
+ }
+ }
+ if (base_type.has_type) {
+ *r_base_type = base_type.native_type;
+ } else {
+ // Fallback
+ if (c->extends_used && c->extends_class.size() == 1) {
+ *r_base_type = c->extends_class[0];
+ } else if (!c->extends_used) {
+ *r_base_type = "Reference";
+ }
+ }
+ }
+ if (r_icon_path) {
+ if (c->icon_path.empty() || c->icon_path.is_abs_path())
+ *r_icon_path = c->icon_path;
+ else if (c->icon_path.is_rel_path())
+ *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
+ }
+ return c->name;
+ }
+
+ return String();
+}
+
+#ifdef DEBUG_ENABLED
+String GDScriptWarning::get_message() const {
+
+#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
+
+ switch (code) {
+ case UNASSIGNED_VARIABLE_OP_ASSIGN: {
+ CHECK_SYMBOLS(1);
+ return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value.";
+ } break;
+ case UNASSIGNED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The variable '" + symbols[0] + "' was used but never assigned a value.";
+ } break;
+ case UNUSED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The local variable '" + symbols[0] + "' is declared but never used in the block.";
+ } break;
+ case UNUSED_CLASS_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
+ } break;
+ case UNUSED_ARGUMENT: {
+ CHECK_SYMBOLS(2);
+ return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'.";
+ } break;
+ case UNREACHABLE_CODE: {
+ CHECK_SYMBOLS(1);
+ return "Unreachable code (statement after return) in function '" + symbols[0] + "()'.";
+ } break;
+ case STANDALONE_EXPRESSION: {
+ return "Standalone expression (the line has no effect).";
+ } break;
+ case VOID_ASSIGNMENT: {
+ CHECK_SYMBOLS(1);
+ return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
+ } break;
+ case NARROWING_CONVERSION: {
+ return "Narrowing coversion (float is converted to int and lose precision).";
+ } break;
+ case FUNCTION_MAY_YIELD: {
+ CHECK_SYMBOLS(1);
+ return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead.";
+ } break;
+ case VARIABLE_CONFLICTS_FUNCTION: {
+ CHECK_SYMBOLS(1);
+ return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name.";
+ } break;
+ case FUNCTION_CONFLICTS_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name.";
+ } break;
+ case FUNCTION_CONFLICTS_CONSTANT: {
+ CHECK_SYMBOLS(1);
+ return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name.";
+ } break;
+ case INCOMPATIBLE_TERNARY: {
+ return "Values of the ternary conditional are not mutually compatible.";
+ } break;
+ case UNUSED_SIGNAL: {
+ CHECK_SYMBOLS(1);
+ return "The signal '" + symbols[0] + "' is declared but never emitted.";
+ } break;
+ case RETURN_VALUE_DISCARDED: {
+ CHECK_SYMBOLS(1);
+ return "The function '" + symbols[0] + "()' returns a value, but this value is never used.";
+ } break;
+ case PROPERTY_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?";
+ } break;
+ case CONSTANT_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?";
+ } break;
+ case FUNCTION_USED_AS_PROPERTY: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?";
+ } break;
+ case INTEGER_DIVISION: {
+ return "Integer division, decimal part will be discarded.";
+ } break;
+ case UNSAFE_PROPERTY_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_METHOD_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_CAST: {
+ CHECK_SYMBOLS(1);
+ return "The value is cast to '" + symbols[0] + "' but has an unkown type.";
+ } break;
+ case UNSAFE_CALL_ARGUMENT: {
+ CHECK_SYMBOLS(4);
+ return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
+ } break;
+ }
+ ERR_EXPLAIN("Invalid GDScript waring code: " + get_name_from_code(code));
+ ERR_FAIL_V(String());
+
+#undef CHECK_SYMBOLS
+}
+
+String GDScriptWarning::get_name() const {
+ return get_name_from_code(code);
+}
+
+String GDScriptWarning::get_name_from_code(Code p_code) {
+ ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String());
+
+ static const char *names[] = {
+ "UNASSIGNED_VARIABLE",
+ "UNASSIGNED_VARIABLE_OP_ASSIGN",
+ "UNUSED_VARIABLE",
+ "UNUSED_CLASS_VARIABLE",
+ "UNUSED_ARGUMENT",
+ "UNREACHABLE_CODE",
+ "STANDALONE_EXPRESSION",
+ "VOID_ASSIGNMENT",
+ "NARROWING_CONVERSION",
+ "FUNCTION_MAY_YIELD",
+ "VARIABLE_CONFLICTS_FUNCTION",
+ "FUNCTION_CONFLICTS_VARIABLE",
+ "FUNCTION_CONFLICTS_CONSTANT",
+ "INCOMPATIBLE_TERNARY",
+ "UNUSED_SIGNAL",
+ "RETURN_VALUE_DISCARDED",
+ "PROPERTY_USED_AS_FUNCTION",
+ "CONSTANT_USED_AS_FUNCTION",
+ "FUNCTION_USED_AS_PROPERTY",
+ "INTEGER_DIVISION",
+ "UNSAFE_PROPERTY_ACCESS",
+ "UNSAFE_METHOD_ACCESS",
+ "UNSAFE_CAST",
+ "UNSAFE_CALL_ARGUMENT",
+ NULL
+ };
+
+ return names[(int)p_code];
+}
+
+GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
+ for (int i = 0; i < WARNING_MAX; i++) {
+ if (get_name_from_code((Code)i) == p_name) {
+ return (Code)i;
+ }
+ }
+
+ ERR_EXPLAIN("Invalid GDScript waring name: " + p_name);
+ ERR_FAIL_V(WARNING_MAX);
+}
+
+#endif // DEBUG_ENABLED
+
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
@@ -1824,6 +2073,15 @@ GDScriptLanguage::GDScriptLanguage() {
_debug_max_call_stack = 0;
_call_stack = NULL;
}
+
+#ifdef DEBUG_ENABLED
+ GLOBAL_DEF("debug/gdscript/warnings/enable", true);
+ GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
+ for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
+ String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
+ GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_"));
+ }
+#endif // DEBUG_ENABLED
}
GDScriptLanguage::~GDScriptLanguage() {
@@ -1856,7 +2114,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
Error err = script->load_byte_code(p_path);
if (err != OK) {
-
+ memdelete(script);
ERR_FAIL_COND_V(err != OK, RES());
}
@@ -1864,7 +2122,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
Error err = script->load_source_code(p_path);
if (err != OK) {
-
+ memdelete(script);
ERR_FAIL_COND_V(err != OK, RES());
}
@@ -1879,6 +2137,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
return scriptres;
}
+
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index a35b0a10d5..d400230f43 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -64,6 +64,7 @@ class GDScript : public Script {
StringName setter;
StringName getter;
MultiplayerAPI::RPCMode rpc_mode;
+ GDScriptDataType data_type;
};
friend class GDScriptInstance;
@@ -145,8 +146,13 @@ public:
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; }
const Set<StringName> &get_members() const { return members; }
+ const GDScriptDataType &get_member_type(const StringName &p_member) const {
+ ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType());
+ return member_indices[p_member].data_type;
+ }
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
+ const String &get_script_class_name() const { return name; }
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
@@ -165,6 +171,7 @@ public:
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance *instance_create(Object *p_this);
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
@@ -255,6 +262,49 @@ public:
~GDScriptInstance();
};
+#ifdef DEBUG_ENABLED
+struct GDScriptWarning {
+ enum Code {
+ UNASSIGNED_VARIABLE, // Variable used but never assigned
+ UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc)
+ UNUSED_VARIABLE, // Local variable is declared but never used
+ UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file
+ UNUSED_ARGUMENT, // Function argument is never used
+ UNREACHABLE_CODE, // Code after a return statement
+ STANDALONE_EXPRESSION, // Expression not assigned to a variable
+ VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable
+ NARROWING_CONVERSION, // Float value into an integer slot, precision is lost
+ FUNCTION_MAY_YIELD, // Typed assign of function call that yields (it may return a function state)
+ VARIABLE_CONFLICTS_FUNCTION, // Variable has the same name of a function
+ FUNCTION_CONFLICTS_VARIABLE, // Function has the same name of a variable
+ FUNCTION_CONFLICTS_CONSTANT, // Function has the same name of a constant
+ INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible
+ UNUSED_SIGNAL, // Signal is defined but never emitted
+ RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used
+ PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name
+ CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name
+ FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name
+ INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded
+ UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes)
+ UNSAFE_METHOD_ACCESS, // Fucntion not found in the detected type (but can be in subtypes)
+ UNSAFE_CAST, // Cast used in an unknown type
+ UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument
+ WARNING_MAX,
+ } code;
+ Vector<String> symbols;
+ int line;
+
+ String get_name() const;
+ String get_message() const;
+ static String get_name_from_code(Code p_code);
+ static Code get_code_from_name(const String &p_name);
+
+ GDScriptWarning() :
+ line(-1),
+ code(WARNING_MAX) {}
+};
+#endif // DEBUG_ENABLED
+
class GDScriptLanguage : public ScriptLanguage {
static GDScriptLanguage *singleton;
@@ -349,10 +399,10 @@ public:
Vector<StackInfo> csi;
csi.resize(_debug_call_stack_pos);
for (int i = 0; i < _debug_call_stack_pos; i++) {
- csi[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
+ csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
if (_call_stack[i].function)
- csi[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
- csi[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path();
+ csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
+ csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path();
}
return csi;
}
@@ -391,7 +441,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -439,6 +489,11 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ /* GLOBAL CLASSES */
+
+ virtual bool handles_global_class_type(const String &p_type) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
+
GDScriptLanguage();
~GDScriptLanguage();
};
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5c834966c5..006fbece53 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -34,7 +34,7 @@
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
- if (!codegen.function_node || codegen.function_node->_static)
+ if (codegen.function_node && codegen.function_node->_static)
return false;
if (codegen.stack_identifiers.has(p_name))
@@ -111,23 +111,50 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
return true;
}
-/*
-int GDScriptCompiler::_parse_subexpression(CodeGen& codegen,const GDScriptParser::Node *p_expression) {
-
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
+ if (!p_datatype.has_type) {
+ return GDScriptDataType();
+ }
- int ret = _parse_expression(codegen,p_expression);
- if (ret<0)
- return ret;
+ GDScriptDataType result;
+ result.has_type = true;
- if (ret&(GDScriptFunction::ADDR_TYPE_STACK<<GDScriptFunction::ADDR_BITS)) {
- codegen.stack_level++;
- codegen.check_max_stack_level();
- //stack was used, keep value
+ switch (p_datatype.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ result.kind = GDScriptDataType::BUILTIN;
+ result.builtin_type = p_datatype.builtin_type;
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ result.kind = GDScriptDataType::NATIVE;
+ result.native_type = p_datatype.native_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ result.kind = GDScriptDataType::SCRIPT;
+ result.script_type = p_datatype.script_type;
+ result.native_type = result.script_type->get_instance_base_type();
+ }
+ case GDScriptParser::DataType::GDSCRIPT: {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = p_datatype.script_type;
+ result.native_type = result.script_type->get_instance_base_type();
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ if (!p_datatype.class_type->owner) {
+ result.script_type = Ref<GDScript>(main_script);
+ } else {
+ result.script_type = class_map[p_datatype.class_type->name];
+ }
+ result.native_type = result.script_type->get_instance_base_type();
+ } break;
+ default: {
+ ERR_PRINT("Parser bug: converting unresolved type.");
+ result.has_type = false;
+ }
}
- return ret;
+ return result;
}
-*/
int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
@@ -263,21 +290,47 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
owner = owner->_owner;
}
- /*
- handled in constants now
- if (codegen.script->subclasses.has(identifier)) {
- //same with a subclass, make it a local constant.
- int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]);
- return idx|(GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
-
- }*/
-
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
}
+ /* TRY GLOBAL CLASSES */
+
+ if (ScriptServer::is_global_class(identifier)) {
+
+ const GDScriptParser::ClassNode *class_node = codegen.class_node;
+ while (class_node->owner) {
+ class_node = class_node->owner;
+ }
+
+ if (class_node->name == identifier) {
+ _set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression);
+ return -1;
+ }
+
+ RES res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ return -1;
+ }
+
+ Variant key = res;
+ int idx;
+
+ if (!codegen.constant_map.has(key)) {
+
+ idx = codegen.constant_map.size();
+ codegen.constant_map[key] = idx;
+
+ } else {
+ idx = codegen.constant_map[key];
+ }
+
+ return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ }
+
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
@@ -395,6 +448,83 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return dst_addr;
} break;
+ case GDScriptParser::Node::TYPE_CAST: {
+ const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
+
+ int slevel = p_stack_level;
+ int src_addr = _parse_expression(codegen, cn->source_node, slevel);
+ if (src_addr < 0)
+ return src_addr;
+ if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ switch (cn->cast_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
+ codegen.opcodes.push_back(cn->cast_type.builtin_type);
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (!cn->cast_type.class_type->owner) {
+ script = codegen.script;
+ } else {
+ StringName name = cn->cast_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = cn->cast_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ } break;
+ default: {
+ _set_error("Parser bug: unresolved data type.", cn);
+ return -1;
+ }
+ }
+
+ codegen.opcodes.push_back(src_addr); // source adddress
+ int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
+ } break;
case GDScriptParser::Node::TYPE_OPERATOR: {
//hell breaks loose
@@ -655,8 +785,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes[jump_fail_pos] = codegen.opcodes.size();
- codegen.opcodes[jump_fail_pos2] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_fail_pos2] = codegen.opcodes.size();
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE);
codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
@@ -688,8 +818,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes[jump_success_pos] = codegen.opcodes.size();
- codegen.opcodes[jump_success_pos2] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_success_pos] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_success_pos2] = codegen.opcodes.size();
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE);
codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
@@ -720,7 +850,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
int jump_past_pos = codegen.opcodes.size();
codegen.opcodes.push_back(0);
- codegen.opcodes[jump_fail_pos] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
res = _parse_expression(codegen, on->arguments[2], p_stack_level);
if (res < 0)
return res;
@@ -729,7 +859,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
codegen.opcodes.push_back(res);
- codegen.opcodes[jump_past_pos] = codegen.opcodes.size();
+ codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size();
return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
@@ -747,14 +877,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) return -1;
} break;
- case GDScriptParser::OperatorNode::OP_PREINC: {
- } break; //?
- case GDScriptParser::OperatorNode::OP_PREDEC: {
- } break;
- case GDScriptParser::OperatorNode::OP_INC: {
- } break;
- case GDScriptParser::OperatorNode::OP_DEC: {
- } break;
//binary operators (in precedence order)
case GDScriptParser::OperatorNode::OP_IN: {
if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) return -1;
@@ -828,7 +950,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (on->arguments[0]->type == GDScriptParser::Node::TYPE_OPERATOR && (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX || static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED)) {
- // SET (chained) MODE!
+ // SET (chained) MODE!
#ifdef DEBUG_ENABLED
if (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
const GDScriptParser::OperatorNode *inon = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
@@ -887,8 +1009,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
return prev_pos;
int retval = prev_pos;
- //print_line("retval: "+itos(retval));
-
if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
slevel++;
codegen.alloc_stack(slevel);
@@ -1029,12 +1149,87 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (src_address_b < 0)
return -1;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
+
+ if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
+ // Typed assignment
+ switch (assign_type.kind) {
+ case GDScriptParser::DataType::BUILTIN: {
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
+ codegen.opcodes.push_back(assign_type.builtin_type); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int class_idx;
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
+
+ class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ } else {
+ _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
+ return -1;
+ }
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
+ codegen.opcodes.push_back(class_idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+
+ Variant script;
+ int idx = -1;
+ if (!assign_type.class_type->owner) {
+ script = codegen.script;
+ } else {
+ StringName name = assign_type.class_type->name;
+ if (class_map[name] == codegen.script->subclasses[name]) {
+ idx = codegen.get_name_map_pos(name);
+ idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
+ } else {
+ script = class_map[name];
+ }
+ }
+
+ if (idx < 0) {
+ idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+ }
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+
+ Variant script = assign_type.script_type;
+ int idx = codegen.get_constant_pos(script);
+ idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
+ codegen.opcodes.push_back(idx); // variable type
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
+ }
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ }
return dst_address_a; //if anything, returns wathever was assigned or correct stack position
}
-
} break;
case GDScriptParser::OperatorNode::OP_IS: {
@@ -1058,6 +1253,25 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
} break;
+ case GDScriptParser::OperatorNode::OP_IS_BUILTIN: {
+ ERR_FAIL_COND_V(on->arguments.size() != 2, false);
+ ERR_FAIL_COND_V(on->arguments[1]->type != GDScriptParser::Node::TYPE_TYPE, false);
+
+ int slevel = p_stack_level;
+
+ int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
+ if (src_address_a < 0)
+ return -1;
+
+ if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)
+ slevel++; //uses stack for return, increase stack
+
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]);
+
+ codegen.opcodes.push_back(GDScriptFunction::OPCODE_IS_BUILTIN); // perform operator
+ codegen.opcodes.push_back(src_address_a); // argument 1
+ codegen.opcodes.push_back((int)tn->vtype); // argument 2 (unary only takes one parameter)
+ } break;
default: {
ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression.");
@@ -1127,6 +1341,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
int ret = _parse_expression(codegen, op, p_stack_level);
if (ret < 0) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
@@ -1146,6 +1362,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
// compile the condition
int ret = _parse_expression(codegen, branch.compiled_pattern, p_stack_level);
if (ret < 0) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
@@ -1158,16 +1376,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr);
if (err) {
+ memdelete(id);
+ memdelete(op);
return ERR_PARSE_ERROR;
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
codegen.opcodes.push_back(break_addr);
- codegen.opcodes[continue_addr + 1] = codegen.opcodes.size();
+ codegen.opcodes.write[continue_addr + 1] = codegen.opcodes.size();
}
- codegen.opcodes[break_addr + 1] = codegen.opcodes.size();
+ codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+
+ memdelete(id);
+ memdelete(op);
} break;
@@ -1196,16 +1419,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
int end_addr = codegen.opcodes.size();
codegen.opcodes.push_back(0);
- codegen.opcodes[else_addr] = codegen.opcodes.size();
+ codegen.opcodes.write[else_addr] = codegen.opcodes.size();
Error err = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr);
if (err)
return err;
- codegen.opcodes[end_addr] = codegen.opcodes.size();
+ codegen.opcodes.write[end_addr] = codegen.opcodes.size();
} else {
//end without else
- codegen.opcodes[else_addr] = codegen.opcodes.size();
+ codegen.opcodes.write[else_addr] = codegen.opcodes.size();
}
} break;
@@ -1256,7 +1479,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
codegen.opcodes.push_back(continue_pos);
- codegen.opcodes[break_pos + 1] = codegen.opcodes.size();
+ codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size();
codegen.pop_stack_identifiers();
@@ -1282,7 +1505,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
codegen.opcodes.push_back(continue_addr);
- codegen.opcodes[break_addr + 1] = codegen.opcodes.size();
+ codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
} break;
case GDScriptParser::ControlFlowNode::CF_SWITCH: {
@@ -1490,6 +1713,18 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) {
gdfunc->_static = p_func->_static;
gdfunc->rpc_mode = p_func->rpc_mode;
+ gdfunc->argument_types.resize(p_func->argument_types.size());
+ for (int i = 0; i < p_func->argument_types.size(); i++) {
+ gdfunc->argument_types.write[i] = _gdtype_from_datatype(p_func->argument_types[i]);
+ }
+ gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
+ } else {
+ gdfunc->_static = false;
+ gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ gdfunc->return_type = GDScriptDataType();
+ gdfunc->return_type.has_type = true;
+ gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
+ gdfunc->return_type.builtin_type = Variant::NIL;
}
#ifdef TOOLS_ENABLED
@@ -1499,11 +1734,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (codegen.constant_map.size()) {
gdfunc->_constant_count = codegen.constant_map.size();
gdfunc->constants.resize(codegen.constant_map.size());
- gdfunc->_constants_ptr = &gdfunc->constants[0];
+ gdfunc->_constants_ptr = gdfunc->constants.ptrw();
const Variant *K = NULL;
while ((K = codegen.constant_map.next(K))) {
int idx = codegen.constant_map[*K];
- gdfunc->constants[idx] = *K;
+ gdfunc->constants.write[idx] = *K;
}
} else {
@@ -1517,7 +1752,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->_global_names_ptr = &gdfunc->global_names[0];
for (Map<StringName, int>::Element *E = codegen.name_map.front(); E; E = E->next()) {
- gdfunc->global_names[E->get()] = E->key();
+ gdfunc->global_names.write[E->get()] = E->key();
}
gdfunc->_global_names_count = gdfunc->global_names.size();
@@ -1532,7 +1767,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
gdfunc->named_globals.resize(codegen.named_globals.size());
gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
for (int i = 0; i < codegen.named_globals.size(); i++) {
- gdfunc->named_globals[i] = codegen.named_globals[i];
+ gdfunc->named_globals.write[i] = codegen.named_globals[i];
}
gdfunc->_named_globals_count = gdfunc->named_globals.size();
}
@@ -1618,12 +1853,23 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
-Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- Map<StringName, Ref<GDScript> > old_subclasses;
-
- if (p_keep_state) {
- old_subclasses = p_script->subclasses;
+ if (p_class->owner && p_class->owner->owner) {
+ // Owner is not root
+ StringName owner_name = p_class->owner->name;
+ if (!parsed_classes.has(owner_name)) {
+ if (parsing_classes.has(owner_name)) {
+ _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ parsing_classes.insert(owner_name);
+ Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state);
+ if (err) {
+ return err;
+ }
+ parsing_classes.erase(owner_name);
+ }
}
p_script->native = Ref<GDScriptNativeClass>();
@@ -1647,236 +1893,100 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
Ref<GDScriptNativeClass> native;
- if (p_class->extends_used) {
- //do inheritance
- String path = p_class->extends_file;
-
- Ref<GDScript> script;
-
- if (path != "") {
- //path (and optionally subclasses)
-
- if (path.is_rel_path()) {
-
- String base;
-
- if (p_owner) {
- GDScript *current_class = p_owner;
- while (current_class != NULL) {
- base = current_class->get_path();
- if (base == "")
- current_class = current_class->_owner;
- else
- break;
- }
- } else {
- base = p_script->get_path();
- }
-
- if (base == "" || base.is_rel_path()) {
- _set_error("Could not resolve relative path for parent class: " + path, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- path = base.get_base_dir().plus_file(path).simplify_path();
- }
- script = ResourceLoader::load(path);
- if (script.is_null()) {
- _set_error("Could not load base class: " + path, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- if (!script->valid) {
-
- _set_error("Script not fully loaded (cyclic preload?): " + path, p_class);
- return ERR_BUSY;
- }
- //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
-
- if (p_class->extends_class.size()) {
-
- for (int i = 0; i < p_class->extends_class.size(); i++) {
-
- String sub = p_class->extends_class[i];
- if (script->subclasses.has(sub)) {
-
- Ref<Script> subclass = script->subclasses[sub]; //avoid reference from disappearing
- script = subclass;
- } else {
-
- _set_error("Could not find subclass: " + sub, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- }
- }
-
- } else {
-
- ERR_FAIL_COND_V(p_class->extends_class.size() == 0, ERR_BUG);
- //look around for the subclasses
-
- String base = p_class->extends_class[0];
- GDScript *p = p_owner;
- Ref<GDScript> base_class;
-
- while (p) {
-
- if (p->subclasses.has(base)) {
-
- base_class = p->subclasses[base];
- break;
- }
-
- if (p->constants.has(base)) {
-
- base_class = p->constants[base];
- if (base_class.is_null()) {
- _set_error("Constant is not a class: " + base, p_class);
- return ERR_SCRIPT_FAILED;
- }
- break;
- }
-
- p = p->_owner;
- }
-
- if (base_class.is_valid()) {
-
- String ident = base;
-
- for (int i = 1; i < p_class->extends_class.size(); i++) {
-
- String subclass = p_class->extends_class[i];
-
- ident += ("." + subclass);
-
- if (base_class->subclasses.has(subclass)) {
-
- base_class = base_class->subclasses[subclass];
- } else if (base_class->constants.has(subclass)) {
-
- Ref<GDScript> new_base_class = base_class->constants[subclass];
- if (new_base_class.is_null()) {
- _set_error("Constant is not a class: " + ident, p_class);
- return ERR_SCRIPT_FAILED;
- }
- base_class = new_base_class;
- } else {
-
- _set_error("Could not find subclass: " + ident, p_class);
- return ERR_FILE_NOT_FOUND;
- }
- }
-
- script = base_class;
-
- } else {
-
- if (p_class->extends_class.size() > 1) {
-
- _set_error("Invalid inheritance (unknown class+subclasses)", p_class);
- return ERR_FILE_NOT_FOUND;
+ // Inheritance
+ switch (p_class->base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ StringName base_name = p_class->base_type.class_type->name;
+ // Make sure dependency is parsed first
+ if (!parsed_classes.has(base_name)) {
+ if (parsing_classes.has(base_name)) {
+ _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
+ return ERR_PARSE_ERROR;
}
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
-
- _set_error("Unknown class: '" + base + "'", p_class);
- return ERR_FILE_NOT_FOUND;
- }
-
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- if (!native.is_valid()) {
-
- _set_error("Global not a class: '" + base + "'", p_class);
-
- return ERR_FILE_NOT_FOUND;
+ parsing_classes.insert(base_name);
+ Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
}
+ parsing_classes.erase(base_name);
}
- }
-
- if (script.is_valid()) {
-
- p_script->base = script;
+ Ref<GDScript> base = class_map[base_name];
+ p_script->base = base;
p_script->_base = p_script->base.ptr();
- p_script->member_indices = script->member_indices;
-
- } else if (native.is_valid()) {
-
+ p_script->member_indices = base->member_indices;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> base = p_class->base_type.script_type;
+ p_script->base = base;
+ p_script->_base = p_script->base.ptr();
+ p_script->member_indices = base->member_indices;
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
+ ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
- } else {
-
- _set_error("Could not determine inheritance", p_class);
- return ERR_FILE_NOT_FOUND;
- }
-
- } else {
- // without extends, implicitly extend Reference
- int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"];
- native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
- ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
- p_script->native = native;
+ } break;
+ default: {
+ _set_error("Parser bug: invalid inheritance.", p_class);
+ return ERR_BUG;
+ } break;
}
- //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
-
for (int i = 0; i < p_class->variables.size(); i++) {
StringName name = p_class->variables[i].identifier;
- if (p_script->member_indices.has(name)) {
- _set_error("Member '" + name + "' already exists (in current or parent class)", p_class);
- return ERR_ALREADY_EXISTS;
- }
- if (_is_class_member_property(p_script, name)) {
- _set_error("Member '" + name + "' already exists as a class property.", p_class);
- return ERR_ALREADY_EXISTS;
- }
- if (p_class->variables[i]._export.type != Variant::NIL) {
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+ minfo.setter = p_class->variables[i].setter;
+ minfo.getter = p_class->variables[i].getter;
+ minfo.rpc_mode = p_class->variables[i].rpc_mode;
+ minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
+
+ PropertyInfo prop_info = minfo.data_type;
+ prop_info.name = name;
+ PropertyInfo export_info = p_class->variables[i]._export;
- p_script->member_info[name] = p_class->variables[i]._export;
+ if (export_info.type != Variant::NIL) {
+
+ if (!minfo.data_type.has_type) {
+ prop_info.type = export_info.type;
+ prop_info.class_name = export_info.class_name;
+ }
+ prop_info.hint = export_info.hint;
+ prop_info.hint_string = export_info.hint_string;
+ prop_info.usage = export_info.usage;
#ifdef TOOLS_ENABLED
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
-
p_script->member_default_values[name] = p_class->variables[i].default_value;
}
#endif
} else {
-
- p_script->member_info[name] = PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+ prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
- //int new_idx = p_script->member_indices.size();
- GDScript::MemberInfo minfo;
- minfo.index = p_script->member_indices.size();
- minfo.setter = p_class->variables[i].setter;
- minfo.getter = p_class->variables[i].getter;
- minfo.rpc_mode = p_class->variables[i].rpc_mode;
-
+ p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = p_class->variables[i].line;
#endif
}
- for (int i = 0; i < p_class->constant_expressions.size(); i++) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- StringName name = p_class->constant_expressions[i].identifier;
- ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
+ StringName name = E->key();
- if (_is_class_member_property(p_script, name)) {
- _set_error("Member '" + name + "' already exists as a class property.", p_class);
- return ERR_ALREADY_EXISTS;
- }
+ ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
- GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(p_class->constant_expressions[i].expression);
+ GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
p_script->constants.insert(name, constant->value);
-//p_script->constants[constant->value].make_const();
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->constant_expressions[i].expression->line;
+ p_script->member_lines[name] = E->get().expression->line;
#endif
}
@@ -1909,23 +2019,29 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->_signals[name] = p_class->_signals[i].arguments;
}
+
+ if (p_class->owner) {
+ parsed_classes.insert(p_class->name);
+ if (parsing_classes.has(p_class->name)) {
+ parsing_classes.erase(p_class->name);
+ }
+ }
+
//parse sub-classes
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
- Ref<GDScript> subclass;
+ Ref<GDScript> subclass = class_map[name];
- if (old_subclasses.has(name)) {
- subclass = old_subclasses[name];
- } else {
- subclass.instance();
+ // Subclass might still be parsing, just skip it
+ if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
+ parsing_classes.insert(name);
+ Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
+ if (err)
+ return err;
}
- Error err = _parse_class(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
- if (err)
- return err;
-
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = p_class->subclasses[i]->line;
@@ -1935,6 +2051,10 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->subclasses.insert(name, subclass);
}
+ return OK;
+}
+
+Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
bool has_initializer = false;
@@ -1975,50 +2095,10 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#ifdef DEBUG_ENABLED
- //validate setters/getters if debug is enabled
- for (int i = 0; i < p_class->variables.size(); i++) {
-
- if (p_class->variables[i].setter) {
- const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].setter);
- if (!E) {
- _set_error("Setter function '" + String(p_class->variables[i].setter) + "' not found in class.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
-
- if (E->get()->is_static()) {
-
- _set_error("Setter function '" + String(p_class->variables[i].setter) + "' is static.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
- }
- if (p_class->variables[i].getter) {
- const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].getter);
- if (!E) {
- _set_error("Getter function '" + String(p_class->variables[i].getter) + "' not found in class.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
-
- if (E->get()->is_static()) {
-
- _set_error("Getter function '" + String(p_class->variables[i].getter) + "' is static.", NULL);
- err_line = p_class->variables[i].line;
- err_column = 0;
- return ERR_PARSE_ERROR;
- }
- }
- }
//validate instances if keeping state
if (p_keep_state) {
-
- print_line("RELOAD KEEP " + p_script->path);
for (Set<Object *>::Element *E = p_script->instances.front(); E;) {
Set<Object *>::Element *N = E->next();
@@ -2065,22 +2145,67 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#endif
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ StringName name = p_class->subclasses[i]->name;
+ Ref<GDScript> subclass = class_map[name];
+
+ Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ if (err) {
+ return err;
+ }
+ }
+
p_script->valid = true;
return OK;
}
+void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+
+ Map<StringName, Ref<GDScript> > old_subclasses;
+
+ if (p_keep_state) {
+ old_subclasses = p_script->subclasses;
+ }
+
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ StringName name = p_class->subclasses[i]->name;
+
+ Ref<GDScript> subclass;
+
+ if (old_subclasses.has(name)) {
+ subclass = old_subclasses[name];
+ } else {
+ subclass.instance();
+ }
+
+ subclass->_owner = const_cast<GDScript *>(p_script);
+ class_map.insert(name, subclass);
+
+ _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ }
+}
+
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
error = "";
parser = p_parser;
+ main_script = p_script;
const GDScriptParser::Node *root = parser->get_parse_tree();
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
source = p_script->get_path();
- Error err = _parse_class(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ // Create scripts for subclasses beforehand so they can be referenced
+ _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+
+ Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+
+ if (err)
+ return err;
+
+ err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 237b0de9e7..55f5e2b48e 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,12 +31,17 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
+#include "core/set.h"
#include "gdscript.h"
#include "gdscript_parser.h"
class GDScriptCompiler {
const GDScriptParser *parser;
+ Map<StringName, Ref<GDScript> > class_map;
+ Set<StringName> parsed_classes;
+ Set<StringName> parsing_classes;
+ GDScript *main_script;
struct CodeGen {
GDScript *script;
@@ -138,11 +143,15 @@ class GDScriptCompiler {
bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
+
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
- Error _parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 0e4724fec2..d3068fb6d0 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -37,6 +37,7 @@
#include "os/file_access.h"
#ifdef TOOLS_ENABLED
+#include "core/reference.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#include "engine.h"
@@ -45,29 +46,53 @@
void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("#");
- p_delimiters->push_back("\"\"\" \"\"\"");
}
void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("\" \"");
p_delimiters->push_back("' '");
+ p_delimiters->push_back("\"\"\" \"\"\"");
}
Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
+#ifdef TOOLS_ENABLED
+ bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false);
+#else
+ bool th = false;
+#endif
- String _template = String() +
- "extends %BASE%\n\n" +
- "# class member variables go here, for example:\n" +
- "# var a = 2\n" +
- "# var b = \"textvar\"\n\n" +
- "func _ready():\n" +
- "%TS%# Called when the node is added to the scene for the first time.\n" +
- "%TS%# Initialization here.\n" +
- "%TS%pass\n\n" +
- "#func _process(delta):\n" +
- "#%TS%# Called every frame. Delta is time since last frame.\n" +
- "#%TS%# Update game logic here.\n" +
+ String _template = "extends %BASE%\n"
+ "\n"
+ "# Declare member variables here. Examples:\n"
+ "# var a%INT_TYPE% = 2\n"
+ "# var b%STRING_TYPE% = \"text\"\n"
+ "\n"
+ "# Called when the node enters the scene tree for the first time.\n"
+ "func _ready()%VOID_RETURN%:\n"
+ "%TS%pass # Replace with function body.\n"
+ "\n"
+ "# Called every frame. 'delta' is the elapsed time since the previous frame.\n"
+ "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n"
"#%TS%pass\n";
+#ifdef TOOLS_ENABLED
+ if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) {
+ _template = _template.replace("%INT_TYPE%", ": int");
+ _template = _template.replace("%STRING_TYPE%", ": String");
+ _template = _template.replace("%FLOAT_TYPE%", ": float");
+ _template = _template.replace("%VOID_RETURN%", " -> void");
+ } else {
+ _template = _template.replace("%INT_TYPE%", "");
+ _template = _template.replace("%STRING_TYPE%", "");
+ _template = _template.replace("%FLOAT_TYPE%", "");
+ _template = _template.replace("%VOID_RETURN%", "");
+ }
+#else
+ _template = _template.replace("%INT_TYPE%", "");
+ _template = _template.replace("%STRING_TYPE%", "");
+ _template = _template.replace("%FLOAT_TYPE%", "");
+ _template = _template.replace("%VOID_RETURN%", "");
+#endif
+
_template = _template.replace("%BASE%", p_base_class_name);
_template = _template.replace("%TS%", _get_indentation());
@@ -91,11 +116,24 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p
p_script->set_source_code(src);
}
-bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
GDScriptParser parser;
- Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path);
+ Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines);
+#ifdef DEBUG_ENABLED
+ if (r_warnings) {
+ for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
+ const GDScriptWarning &warn = E->get();
+ ScriptLanguage::Warning w;
+ w.line = warn.line;
+ w.code = (int)warn.code;
+ w.string_code = GDScriptWarning::get_name_from_code(warn.code);
+ w.message = warn.get_message();
+ r_warnings->push_back(w);
+ }
+ }
+#endif
if (err) {
r_line_error = parser.get_error_line();
r_col_error = parser.get_error_column();
@@ -154,7 +192,6 @@ int GDScriptLanguage::find_function(const String &p_function, const String &p_co
if (tokenizer.get_token() == GDScriptTokenizer::TK_NEWLINE) {
indent = tokenizer.get_token_line_indent();
}
- //print_line("TOKEN: "+String(GDScriptTokenizer::get_token_name(tokenizer.get_token())));
if (indent == 0 && tokenizer.get_token() == GDScriptTokenizer::TK_PR_FUNCTION && tokenizer.get_token(1) == GDScriptTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer.get_token_identifier(1);
@@ -163,7 +200,6 @@ int GDScriptLanguage::find_function(const String &p_function, const String &p_co
}
}
tokenizer.advance();
- //print_line("NEXT: "+String(GDScriptTokenizer::get_token_name(tokenizer.get_token())));
}
return -1;
}
@@ -415,63 +451,34 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_cons
String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+#ifdef TOOLS_ENABLED
+ bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");
+#else
+ bool th = false;
+#endif
+
String s = "func " + p_name + "(";
if (p_args.size()) {
for (int i = 0; i < p_args.size(); i++) {
if (i > 0)
s += ", ";
s += p_args[i].get_slice(":", 0);
+ if (th) {
+ String type = p_args[i].get_slice(":", 1);
+ if (!type.empty() && type != "var") {
+ s += ": " + type;
+ }
+ }
}
}
- s += "):\n" + _get_indentation() + "pass # Replace with function body.\n";
+ s += String(")") + (th ? " -> void" : "") + ":\n" + _get_indentation() + "pass # Replace with function body.\n";
return s;
}
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
+//////// COMPLETION //////////
-struct GDScriptCompletionIdentifier {
-
- String enumeration;
- StringName obj_type;
- Ref<GDScript> script;
- Variant::Type type;
- Variant value; //im case there is a value, also return it
-
- GDScriptCompletionIdentifier() :
- type(Variant::NIL) {}
-};
-
-static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) {
-
- GDScriptCompletionIdentifier t;
- t.type = p_variant.get_type();
- t.value = p_variant;
- if (p_variant.get_type() == Variant::OBJECT) {
- Object *obj = p_variant;
- if (obj) {
-
- if (p_allow_gdnative_class && Object::cast_to<GDScriptNativeClass>(obj)) {
- t.obj_type = Object::cast_to<GDScriptNativeClass>(obj)->get_name();
- t.value = Variant();
- } else {
-
- t.obj_type = obj->get_class();
- }
- }
- }
- return t;
-}
-
-static GDScriptCompletionIdentifier _get_type_from_pinfo(const PropertyInfo &p_info) {
-
- GDScriptCompletionIdentifier t;
- t.type = p_info.type;
- if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- t.obj_type = p_info.hint_string;
- }
- return t;
-}
+#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
struct GDScriptCompletionContext {
@@ -480,1043 +487,1612 @@ struct GDScriptCompletionContext {
const GDScriptParser::BlockNode *block;
Object *base;
String base_path;
-};
-
-static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) {
-
- if (context._class->extends_used) {
- //do inheritance
- String path = context._class->extends_file;
-
- Ref<GDScript> script;
- Ref<GDScriptNativeClass> native;
-
- if (path != "") {
- //path (and optionally subclasses)
+ int line;
- if (path.is_rel_path()) {
-
- path = context.base_path.plus_file(path);
- }
-
- if (ScriptCodeCompletionCache::get_singleton())
- script = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
- else
- script = ResourceLoader::load(path);
+ GDScriptCompletionContext() :
+ _class(NULL),
+ function(NULL),
+ block(NULL),
+ base(NULL) {}
+};
- if (script.is_null()) {
- return REF();
- }
- if (!script->is_valid()) {
+struct GDScriptCompletionIdentifier {
+ GDScriptParser::DataType type;
+ String enumeration;
+ Variant value;
+ const GDScriptParser::Node *assigned_expression;
- return REF();
- }
- //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
+ GDScriptCompletionIdentifier() :
+ assigned_expression(NULL) {}
+};
- if (context._class->extends_class.size()) {
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) {
- for (int i = 0; i < context._class->extends_class.size(); i++) {
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ r_list.insert("\"" + p_dir->get_file_path(i) + "\"");
+ }
- String sub = context._class->extends_class[i];
- if (script->get_subclasses().has(sub)) {
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _get_directory_contents(p_dir->get_subdir(i), r_list);
+ }
+}
- script = script->get_subclasses()[sub];
- } else {
+static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = true) {
- return REF();
- }
- }
- }
+ if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ String enum_name = p_info.class_name;
+ if (enum_name.find(".") == -1) {
+ return enum_name;
+ }
+ return enum_name.get_slice(".", 1);
+ }
- if (script.is_valid())
- return script;
+ String n = p_info.name;
+ int idx = n.find(":");
+ if (idx != -1) {
+ return n.substr(idx + 1, n.length());
+ }
+ if (p_info.type == Variant::OBJECT) {
+ if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ return p_info.hint_string;
} else {
+ return p_info.class_name.operator String();
+ }
+ }
+ if (p_info.type == Variant::NIL) {
+ if (p_isarg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ return "var";
+ } else {
+ return "void";
+ }
+ }
- if (context._class->extends_class.size() == 0) {
- ERR_PRINT("BUG");
- return REF();
- }
-
- String base = context._class->extends_class[0];
-
- if (context._class->extends_class.size() > 1) {
-
- return REF();
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
+ return Variant::get_type_name(p_info.type);
+}
- return REF();
+static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
+ GDScriptCompletionIdentifier ci;
+ ci.value = p_value;
+ ci.type.is_constant = true;
+ ci.type.has_type = true;
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ ci.type.builtin_type = p_value.get_type();
+
+ if (ci.type.builtin_type == Variant::OBJECT) {
+ Object *obj = p_value.operator Object *();
+ if (!obj) {
+ return ci;
+ }
+ ci.type.native_type = obj->get_class_name();
+ Ref<Script> scr = p_value;
+ if (scr.is_valid()) {
+ ci.type.is_meta_type = true;
+ } else {
+ ci.type.is_meta_type = false;
+ scr = obj->get_script();
+ }
+ if (scr.is_valid()) {
+ ci.type.script_type = scr;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } else {
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
}
-
- int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
- native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
- return native;
+ ci.type.native_type = scr->get_instance_base_type();
+ } else {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
}
}
- return Ref<Reference>();
+ return ci;
}
-static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) {
+static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) {
+ GDScriptCompletionIdentifier ci;
- GDScriptCompletionIdentifier id;
-
- REF pc = _get_parent_class(context);
- if (!pc.is_valid()) {
- return id;
+ if (p_property.type == Variant::NIL) {
+ // Variant
+ return ci;
}
- Ref<GDScriptNativeClass> nc = pc;
- Ref<GDScript> s = pc;
- if (s.is_null() && nc.is_null()) {
- return id;
- }
- while (!s.is_null()) {
- nc = s->get_native();
- s = s->get_base();
- }
- if (nc.is_null()) {
- return id;
+ if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ ci.enumeration = p_property.class_name;
}
- id.type = Variant::OBJECT;
- if (context.base)
- id.value = context.base;
- id.obj_type = nc->get_name();
- return id;
+ ci.type.has_type = true;
+ ci.type.builtin_type = p_property.type;
+ if (p_property.type == Variant::OBJECT) {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
+ ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ } else {
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ }
+ return ci;
}
-static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type, bool p_for_indexing);
-
-static bool _guess_expression_type(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, GDScriptCompletionIdentifier &r_type, bool p_for_indexing = false) {
-
- if (p_node->type == GDScriptParser::Node::TYPE_CONSTANT) {
-
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_node);
+static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_gdtype) {
+ GDScriptCompletionIdentifier ci;
+ if (!p_gdtype.has_type) {
+ return ci;
+ }
- r_type = _get_type_from_variant(cn->value);
+ ci.type.has_type = true;
+ ci.type.builtin_type = p_gdtype.builtin_type;
+ ci.type.native_type = p_gdtype.native_type;
+ ci.type.script_type = p_gdtype.script_type;
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_DICTIONARY) {
-
- r_type.type = Variant::DICTIONARY;
+ switch (p_gdtype.kind) {
+ case GDScriptDataType::BUILTIN: {
+ ci.type.kind = GDScriptParser::DataType::BUILTIN;
+ } break;
+ case GDScriptDataType::NATIVE: {
+ ci.type.kind = GDScriptParser::DataType::NATIVE;
+ } break;
+ case GDScriptDataType::GDSCRIPT: {
+ ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } break;
+ case GDScriptDataType::SCRIPT: {
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
+ } break;
+ }
+ return ci;
+}
- //what the heck, fill it anyway
- const GDScriptParser::DictionaryNode *an = static_cast<const GDScriptParser::DictionaryNode *>(p_node);
- Dictionary d;
- for (int i = 0; i < an->elements.size(); i++) {
- GDScriptCompletionIdentifier k;
- if (_guess_expression_type(context, an->elements[i].key, p_line, k) && k.value.get_type() != Variant::NIL) {
- GDScriptCompletionIdentifier v;
- if (_guess_expression_type(context, an->elements[i].value, p_line, v)) {
- d[k.value] = v.value;
+static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
+
+static bool _guess_expression_type(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) {
+ bool found = false;
+ switch (p_expression->type) {
+ case GDScriptParser::Node::TYPE_CONSTANT: {
+ const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression);
+ r_type = _type_from_variant(cn->value);
+ found = true;
+ } break;
+ case GDScriptParser::Node::TYPE_SELF: {
+ if (p_context._class) {
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ r_type.type.is_constant = true;
+ r_type.value = p_context.base;
+ found = true;
+ }
+ } break;
+ case GDScriptParser::Node::TYPE_IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
+ found = _guess_identifier_type(p_context, id->name, r_type);
+ } break;
+ case GDScriptParser::Node::TYPE_DICTIONARY: {
+ // Try to recreate the dictionary
+ const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
+ Dictionary d;
+ bool full = true;
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (_guess_expression_type(p_context, dn->elements[i].key, key)) {
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, dn->elements[i].value, value)) {
+ if (!value.type.is_constant) {
+ full = false;
+ break;
+ }
+ d[key.value] = value.value;
+ } else {
+ full = false;
+ break;
+ }
+ } else {
+ full = false;
+ break;
}
}
- }
- r_type.value = d;
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_ARRAY) {
-
- r_type.type = Variant::ARRAY;
- //what the heck, fill it anyway
- const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_node);
- Array arr;
- arr.resize(an->elements.size());
- for (int i = 0; i < an->elements.size(); i++) {
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, an->elements[i], p_line, ci)) {
- arr[i] = ci.value;
+ if (full) {
+ // If not fully constant, setting this value is detrimental to the inference
+ r_type.value = d;
+ r_type.type.is_constant = true;
}
- }
- r_type.value = arr;
- return true;
-
- } else if (p_node->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
-
- MethodInfo mi = GDScriptFunctions::get_info(static_cast<const GDScriptParser::BuiltInFunctionNode *>(p_node)->function);
- r_type = _get_type_from_pinfo(mi.return_val);
-
- return true;
- } else if (p_node->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- return _guess_identifier_type(context, p_line - 1, static_cast<const GDScriptParser::IdentifierNode *>(p_node)->name, r_type, p_for_indexing);
- } else if (p_node->type == GDScriptParser::Node::TYPE_SELF) {
- //eeh...
-
- r_type = _get_native_class(context);
- return r_type.type != Variant::NIL;
-
- } else if (p_node->type == GDScriptParser::Node::TYPE_OPERATOR) {
-
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->op == GDScriptParser::OperatorNode::OP_CALL) {
-
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
-
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
- r_type.type = tn->vtype;
- return true;
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
-
- const GDScriptParser::BuiltInFunctionNode *bin = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- return _guess_expression_type(context, bin, p_line, r_type);
-
- } else if (op->arguments.size() > 1 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
-
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && String(id) == "new") {
-
- //shortcut
- StringName identifier = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
-
- if (ClassDB::class_exists(identifier)) {
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = identifier;
- return true;
- }
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::DICTIONARY;
+ } break;
+ case GDScriptParser::Node::TYPE_ARRAY: {
+ // Try to recreate the array
+ const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
+ Array a;
+ bool full = true;
+ a.resize(an->elements.size());
+ for (int i = 0; i < an->elements.size(); i++) {
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, an->elements[i], value)) {
+ a[i] = value.value;
+ } else {
+ full = false;
+ break;
}
-
- GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, op->arguments[0], p_line, base))
- return false;
-
- if (base.type == Variant::OBJECT) {
-
- if (id.operator String() == "new" && base.value.get_type() == Variant::OBJECT) {
-
- Object *obj = base.value;
- if (obj && Object::cast_to<GDScriptNativeClass>(obj)) {
- GDScriptNativeClass *gdnc = Object::cast_to<GDScriptNativeClass>(obj);
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = gdnc->get_name();
- return true;
- } else {
-
- if (base.obj_type != StringName()) {
-
- r_type.type = Variant::OBJECT;
- r_type.value = Variant();
- r_type.obj_type = base.obj_type;
- return true;
- }
+ }
+ if (full) {
+ // If not fully constant, setting this value is detrimental to the inference
+ r_type.value = a;
+ }
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::ARRAY;
+ } break;
+ case GDScriptParser::Node::TYPE_OPERATOR: {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_expression);
+ switch (op->op) {
+ case GDScriptParser::OperatorNode::OP_CALL: {
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
+ r_type.type.has_type = true;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = tn->vtype;
+ found = true;
+ break;
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
+ const GDScriptParser::BuiltInFunctionNode *bin = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(bin->function);
+ r_type = _type_from_property(mi.return_val);
+ found = true;
+ break;
+ } else if (op->arguments.size() >= 2 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
+
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- }
-
- if (ClassDB::has_method(base.obj_type, id)) {
-
-#ifdef TOOLS_ENABLED
- MethodBind *mb = ClassDB::get_method(base.obj_type, id);
- PropertyInfo pi = mb->get_return_info();
- //try calling the function if constant and all args are constant, should not crash..
- Object *baseptr = base.value;
+ // Try call if constant methods with constant arguments
+ if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) {
+ GDScriptParser::DataType native_type = base.type;
- if (mb->is_const() && pi.type == Variant::OBJECT) {
-
- bool all_valid = true;
- Vector<Variant> args;
- for (int i = 2; i < op->arguments.size(); i++) {
- GDScriptCompletionIdentifier arg;
+ while (native_type.kind == GDScriptParser::DataType::CLASS) {
+ native_type = native_type.class_type->base_type;
+ }
- if (_guess_expression_type(context, op->arguments[i], p_line, arg)) {
- if (arg.value.get_type() != Variant::NIL && arg.value.get_type() != Variant::OBJECT) { // calling with object seems dangerous, i don' t know
- args.push_back(arg.value);
+ while (native_type.kind == GDScriptParser::DataType::GDSCRIPT || native_type.kind == GDScriptParser::DataType::SCRIPT) {
+ if (native_type.script_type.is_valid()) {
+ Ref<Script> parent = native_type.script_type->get_base_script();
+ if (parent.is_valid()) {
+ native_type.script_type = parent;
} else {
- all_valid = false;
- break;
+ native_type.kind = GDScriptParser::DataType::NATIVE;
+ native_type.native_type = native_type.script_type->get_instance_base_type();
+ if (!ClassDB::class_exists(native_type.native_type)) {
+ native_type.native_type = String("_") + native_type.native_type;
+ if (!ClassDB::class_exists(native_type.native_type)) {
+ native_type.has_type = false;
+ }
+ }
}
- } else {
- all_valid = false;
}
}
- if (all_valid && String(id) == "get_node" && ClassDB::is_parent_class(base.obj_type, "Node") && args.size()) {
-
- String arg1 = args[0];
- if (arg1.begins_with("/root/")) {
- String which = arg1.get_slice("/", 2);
- if (which != "") {
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- //print_line("find singleton");
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
- String s = E->get().name;
- if (!s.begins_with("autoload/"))
- continue;
- //print_line("found "+s);
- String name = s.get_slice("/", 1);
- //print_line("name: "+name+", which: "+which);
- if (name == which) {
- String script = ProjectSettings::get_singleton()->get(s);
-
- if (!script.begins_with("res://")) {
- script = "res://" + script;
- }
-
- if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
- script = script.get_basename() + ".gd";
- }
-
- if (FileAccess::exists(script)) {
-
- //print_line("is a script");
-
- Ref<Script> scr;
- if (ScriptCodeCompletionCache::get_singleton())
- scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
- else
- scr = ResourceLoader::load(script);
-
- r_type.obj_type = "Node";
- r_type.type = Variant::OBJECT;
- r_type.script = scr;
- r_type.value = Variant();
-
- return true;
- }
+ if (native_type.has_type && native_type.kind == GDScriptParser::DataType::NATIVE) {
+ MethodBind *mb = ClassDB::get_method(native_type.native_type, id);
+ if (mb && mb->is_const()) {
+ bool all_is_const = true;
+ Vector<Variant> args;
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+ for (int i = 2; all_is_const && i < op->arguments.size(); i++) {
+ GDScriptCompletionIdentifier arg;
+
+ if (_guess_expression_type(c, op->arguments[i], arg)) {
+ if (arg.type.has_type && arg.type.is_constant && arg.value.get_type() != Variant::OBJECT) {
+ args.push_back(arg.value);
+ } else {
+ all_is_const = false;
}
+ } else {
+ all_is_const = false;
}
}
- }
- }
- if (baseptr) {
-
- if (all_valid) {
- Vector<const Variant *> argptr;
- for (int i = 0; i < args.size(); i++) {
- argptr.push_back(&args[i]);
+ Object *baseptr = base.value;
+
+ if (all_is_const && String(id) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
+
+ String arg1 = args[0];
+ if (arg1.begins_with("/root/")) {
+ String which = arg1.get_slice("/", 2);
+ if (which != "") {
+ // Try singletons first
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
+ found = true;
+ } else {
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ if (name == which) {
+ String script = ProjectSettings::get_singleton()->get(s);
+
+ if (script.begins_with("*")) {
+ script = script.right(1);
+ }
+
+ if (!script.begins_with("res://")) {
+ script = "res://" + script;
+ }
+
+ if (!script.ends_with(".gd")) {
+ //not a script, try find the script anyway,
+ //may have some success
+ script = script.get_basename() + ".gd";
+ }
+
+ if (FileAccess::exists(script)) {
+ Ref<Script> scr;
+ if (ScriptCodeCompletionCache::get_singleton()) {
+ scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
+ } else {
+ scr = ResourceLoader::load(script);
+ }
+ if (scr.is_valid()) {
+ r_type.type.has_type = true;
+ r_type.type.script_type = scr;
+ r_type.type.is_constant = false;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ r_type.type.kind = GDScriptParser::DataType::GDSCRIPT;
+ } else {
+ r_type.type.kind = GDScriptParser::DataType::SCRIPT;
+ }
+ r_type.value = Variant();
+ found = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
}
- Variant::CallError ce;
- Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
-
- if (ce.error == Variant::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ if (!found && all_is_const && baseptr) {
+ Vector<const Variant *> argptr;
+ for (int i = 0; i < args.size(); i++) {
+ argptr.push_back(&args[i]);
+ }
- if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != NULL) {
+ Variant::CallError ce;
+ Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
- r_type = _get_type_from_variant(ret);
- return true;
+ if (ce.error == Variant::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != NULL) {
+ r_type = _type_from_variant(ret);
+ found = true;
+ }
}
}
}
}
}
- r_type.type = pi.type;
- if (pi.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- r_type.obj_type = pi.hint_string;
+ if (!found) {
+ found = _guess_method_return_type_from_base(c, base, id, r_type);
}
-
- return true;
-#else
- return false;
-#endif
- } else {
- return false;
}
- } else {
- //method for some variant..
- Variant::CallError ce;
- Variant v = Variant::construct(base.type, NULL, 0, ce);
- List<MethodInfo> mi;
- v.get_method_list(&mi);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
-
- if (!E->get().name.begins_with("_") && E->get().name == id.operator String()) {
-
- MethodInfo mi = E->get();
- r_type.type = mi.return_val.type;
- if (mi.return_val.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- r_type.obj_type = mi.return_val.hint_string;
- }
- return true;
- }
+ } break;
+ case GDScriptParser::OperatorNode::OP_PARENT_CALL: {
+ if (!p_context._class || !op->arguments.size() || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ break;
}
- }
- }
- } else if (op->op == GDScriptParser::OperatorNode::OP_INDEX || op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- GDScriptCompletionIdentifier p1;
- GDScriptCompletionIdentifier p2;
+ StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
- if (op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
+ GDScriptCompletionIdentifier base;
+ base.value = p_context.base;
+ base.type = p_context._class->base_type;
- if (op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- String id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
- p2.type = Variant::STRING;
- p2.value = id;
- }
-
- } else {
- if (op->arguments[1]) {
- if (!_guess_expression_type(context, op->arguments[1], p_line, p2)) {
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- return false;
+ found = _guess_method_return_type_from_base(c, base, id, r_type);
+ } break;
+ case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ found = false;
+ break;
}
- }
- }
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_ARRAY) {
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(op->arguments[0]);
- if (p2.value.is_num()) {
- int index = p2.value;
- if (index < 0 || index >= an->elements.size())
- return false;
- return _guess_expression_type(context, an->elements[index], p_line, r_type);
- }
-
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
-
- const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
-
- if (p2.value.get_type() == Variant::NIL)
- return false;
-
- for (int i = 0; i < dn->elements.size(); i++) {
-
- GDScriptCompletionIdentifier k;
-
- if (!_guess_expression_type(context, dn->elements[i].key, p_line, k)) {
-
- return false;
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- if (k.value.get_type() == Variant::NIL)
- return false;
+ if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(id->name))) {
+ Variant value = base.value.operator Dictionary()[String(id->name)];
+ r_type = _type_from_variant(value);
+ found = true;
+ break;
+ }
- if (k.value == p2.value) {
+ const GDScriptParser::DictionaryNode *dn = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
+ }
- return _guess_expression_type(context, dn->elements[i].value, p_line, r_type);
+ if (dn) {
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (!_guess_expression_type(c, dn->elements[i].key, key)) {
+ continue;
+ }
+ if (key.value == String(id->name)) {
+ r_type.assigned_expression = dn->elements[i].value;
+ found = _guess_expression_type(c, dn->elements[i].value, r_type);
+ break;
+ }
+ }
}
- }
- } else {
+ if (!found) {
+ found = _guess_identifier_type_from_base(c, base, id->name, r_type);
+ }
+ } break;
+ case GDScriptParser::OperatorNode::OP_INDEX: {
+ if (op->arguments.size() < 2) {
+ found = false;
+ break;
+ }
- if (op->arguments[0]) {
- if (!_guess_expression_type(context, op->arguments[0], p_line, p1)) {
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
- return false;
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(c, op->arguments[0], base)) {
+ found = false;
+ break;
}
- }
-
- if (p1.value.get_type() == Variant::OBJECT) {
- //??
- if (p1.obj_type != StringName() && p2.type == Variant::STRING) {
+ GDScriptCompletionIdentifier index;
+ if (!_guess_expression_type(c, op->arguments[1], index)) {
+ found = false;
+ break;
+ }
- StringName base_type = p1.obj_type;
+ if (base.value.in(index.value)) {
+ Variant value = base.value.get(index.value);
+ r_type = _type_from_variant(value);
+ found = true;
+ break;
+ }
- if (p1.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = p1.value;
- if (gdn.is_valid()) {
+ // Look if it is a dictionary node
+ const GDScriptParser::DictionaryNode *dn = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
+ }
- base_type = gdn->get_name();
+ if (dn) {
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (!_guess_expression_type(c, dn->elements[i].key, key)) {
+ continue;
}
- }
- StringName index = p2.value;
- bool valid;
- Variant::Type t = ClassDB::get_property_type(base_type, index, &valid);
- if (t != Variant::NIL && valid) {
- r_type.type = t;
- if (t == Variant::INT || t == Variant::OBJECT) {
-//check for enum!
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-
- StringName getter = ClassDB::get_property_getter(base_type, index);
- if (getter != StringName()) {
- MethodBind *mb = ClassDB::get_method(base_type, getter);
- if (mb) {
- PropertyInfo rt = mb->get_return_info();
- if ((rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM) && t == Variant::INT) {
- r_type.enumeration = rt.class_name;
- } else if (t == Variant::OBJECT) {
-
- r_type.obj_type = rt.class_name;
- }
- }
- }
-#endif
+ if (key.value == index.value) {
+ r_type.assigned_expression = dn->elements[i].value;
+ found = _guess_expression_type(p_context, dn->elements[i].value, r_type);
+ break;
}
-
- return true;
}
}
- } else if (p1.value.get_type() != Variant::NIL) {
- bool valid;
- Variant ret = p1.value.get(p2.value, &valid);
- if (valid) {
- r_type = _get_type_from_variant(ret);
- return true;
+ // Look if it is an array node
+ if (!found && index.value.is_num()) {
+ int idx = index.value;
+ const GDScriptParser::ArrayNode *an = NULL;
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_ARRAY) {
+ an = static_cast<const GDScriptParser::ArrayNode *>(op->arguments[0]);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_ARRAY) {
+ an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);
+ }
+
+ if (an && idx >= 0 && an->elements.size() > idx) {
+ r_type.assigned_expression = an->elements[idx];
+ found = _guess_expression_type(c, an->elements[idx], r_type);
+ break;
+ }
}
- } else {
- if (p1.type != Variant::NIL) {
- Variant::CallError ce;
- Variant base = Variant::construct(p1.type, NULL, 0, ce);
- bool valid;
- Variant ret = base.get(p2.value, &valid);
+ // Look for valid indexing in other types
+ if (!found && (index.value.get_type() == Variant::STRING || index.value.get_type() == Variant::NODE_PATH)) {
+ StringName id = index.value;
+ found = _guess_identifier_type_from_base(c, base, id, r_type);
+ } else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
+ Variant::CallError err;
+ Variant base_val = Variant::construct(base.type.builtin_type, NULL, 0, err);
+ bool valid = false;
+ Variant res = base_val.get(index.value, &valid);
if (valid) {
- r_type = _get_type_from_variant(ret);
- return true;
+ r_type = _type_from_variant(res);
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ found = true;
}
}
- }
- }
+ } break;
+ default: {
+ if (op->arguments.size() < 2) {
+ found = false;
+ break;
+ }
- } else {
+ Variant::Operator vop = Variant::OP_MAX;
+ switch (op->op) {
+ case GDScriptParser::OperatorNode::OP_ADD: vop = Variant::OP_ADD; break;
+ case GDScriptParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBTRACT; break;
+ case GDScriptParser::OperatorNode::OP_MUL: vop = Variant::OP_MULTIPLY; break;
+ case GDScriptParser::OperatorNode::OP_DIV: vop = Variant::OP_DIVIDE; break;
+ case GDScriptParser::OperatorNode::OP_MOD: vop = Variant::OP_MODULE; break;
+ case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: vop = Variant::OP_SHIFT_LEFT; break;
+ case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: vop = Variant::OP_SHIFT_RIGHT; break;
+ case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break;
+ case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break;
+ case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break;
+ default: {}
+ }
- Variant::Operator vop = Variant::OP_MAX;
- switch (op->op) {
- case GDScriptParser::OperatorNode::OP_ADD: vop = Variant::OP_ADD; break;
- case GDScriptParser::OperatorNode::OP_SUB: vop = Variant::OP_SUBTRACT; break;
- case GDScriptParser::OperatorNode::OP_MUL: vop = Variant::OP_MULTIPLY; break;
- case GDScriptParser::OperatorNode::OP_DIV: vop = Variant::OP_DIVIDE; break;
- case GDScriptParser::OperatorNode::OP_MOD: vop = Variant::OP_MODULE; break;
- case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: vop = Variant::OP_SHIFT_LEFT; break;
- case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: vop = Variant::OP_SHIFT_RIGHT; break;
- case GDScriptParser::OperatorNode::OP_BIT_AND: vop = Variant::OP_BIT_AND; break;
- case GDScriptParser::OperatorNode::OP_BIT_OR: vop = Variant::OP_BIT_OR; break;
- case GDScriptParser::OperatorNode::OP_BIT_XOR: vop = Variant::OP_BIT_XOR; break;
- default: {}
- }
-
- if (vop == Variant::OP_MAX)
- return false;
+ if (vop == Variant::OP_MAX) {
+ break;
+ }
- GDScriptCompletionIdentifier p1;
- GDScriptCompletionIdentifier p2;
+ GDScriptCompletionContext context = p_context;
+ context.line = op->line;
- if (op->arguments[0]) {
- if (!_guess_expression_type(context, op->arguments[0], p_line, p1)) {
+ GDScriptCompletionIdentifier p1;
+ GDScriptCompletionIdentifier p2;
- return false;
- }
- }
+ if (!_guess_expression_type(context, op->arguments[0], p1)) {
+ found = false;
+ break;
+ }
- if (op->arguments.size() > 1) {
- if (!_guess_expression_type(context, op->arguments[1], p_line, p2)) {
+ if (!_guess_expression_type(context, op->arguments[1], p2)) {
+ found = false;
+ break;
+ }
- return false;
- }
- }
+ Variant::CallError ce;
+ bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
+ Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, NULL, 0, ce);
+ bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
+ Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, NULL, 0, ce);
+ // avoid potential invalid ops
+ if ((vop == Variant::OP_DIVIDE || vop == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
+ v2 = 1;
+ v2_use_value = false;
+ }
+ if (vop == Variant::OP_DIVIDE && v2.get_type() == Variant::REAL) {
+ v2 = 1.0;
+ v2_use_value = false;
+ }
- Variant::CallError ce;
- bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type, NULL, 0, ce);
- bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type, NULL, 0, ce);
- // avoid potential invalid ops
- if ((vop == Variant::OP_DIVIDE || vop == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
- v2 = 1;
- v2_use_value = false;
- }
- if (vop == Variant::OP_DIVIDE && v2.get_type() == Variant::REAL) {
- v2 = 1.0;
- v2_use_value = false;
+ Variant res;
+ bool valid;
+ Variant::evaluate(vop, v1, v2, res, valid);
+ if (!valid) {
+ found = false;
+ break;
+ }
+ r_type = _type_from_variant(res);
+ if (!v1_use_value || !v2_use_value) {
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ }
+
+ found = true;
+ } break;
}
+ } break;
+ }
- Variant r;
- bool valid;
- Variant::evaluate(vop, v1, v2, r, valid);
- if (!valid)
- return false;
- r_type.type = r.get_type();
- if (v1_use_value && v2_use_value)
- r_type.value = r;
+ // It may have found a null, but that's never useful
+ if (found && r_type.type.has_type && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {
+ found = false;
+ }
- return true;
+ // Check type hint last. For collections we want chance to get the actual value first
+ // This way we can detect types from the content of dictionaries and arrays
+ if (!found && p_expression->get_datatype().has_type) {
+ r_type.type = p_expression->get_datatype();
+ if (!r_type.assigned_expression) {
+ r_type.assigned_expression = p_expression;
}
+ found = true;
}
- return false;
+ return found;
}
-static bool _guess_identifier_type_in_block(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type(const GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
- if (context.block->if_condition && context.block->if_condition->type == GDScriptParser::Node::TYPE_OPERATOR && static_cast<const GDScriptParser::OperatorNode *>(context.block->if_condition)->op == GDScriptParser::OperatorNode::OP_IS) {
- //is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
- //super dirty hack, but very useful
- //credit: Zylann
- //TODO: this could be hacked to detect ANDed conditions too..
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(context.block->if_condition);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name == p_identifier) {
- //bingo
- if (_guess_expression_type(context, op->arguments[1], op->line, r_type)) {
- return true;
+ // Look in blocks first
+ const GDScriptParser::BlockNode *blk = p_context.block;
+ int last_assign_line = -1;
+ const GDScriptParser::Node *last_assigned_expression = NULL;
+ GDScriptParser::DataType var_type;
+ while (blk) {
+ if (blk->variables.has(p_identifier)) {
+ if (blk->variables[p_identifier]->line > p_context.line) {
+ return false;
}
- }
- }
- GDScriptCompletionIdentifier gdi = _get_native_class(context);
- if (gdi.obj_type != StringName()) {
- bool valid;
- Variant::Type t = ClassDB::get_property_type(gdi.obj_type, p_identifier, &valid);
- if (t != Variant::NIL && valid) {
- r_type.type = t;
- if (t == Variant::INT) {
-//check for enum!
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
+ var_type = blk->variables[p_identifier]->datatype;
- StringName getter = ClassDB::get_property_getter(gdi.obj_type, p_identifier);
- if (getter != StringName()) {
- MethodBind *mb = ClassDB::get_method(gdi.obj_type, getter);
- if (mb) {
- PropertyInfo rt = mb->get_return_info();
- if (rt.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
- r_type.enumeration = rt.class_name;
- }
- }
+ if (!last_assigned_expression && blk->variables[p_identifier]->assign && blk->variables[p_identifier]->assign->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->variables[p_identifier]->assign);
+ if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN && op->arguments.size() >= 2) {
+ last_assign_line = op->line;
+ last_assigned_expression = op->arguments[1];
}
-#endif
}
- return true;
}
- }
-
- const GDScriptParser::Node *last_assign = NULL;
- int last_assign_line = -1;
-
- for (int i = 0; i < context.block->statements.size(); i++) {
-
- if (context.block->statements[i]->line > p_line)
- continue;
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
-
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]);
+ for (const List<GDScriptParser::Node *>::Element *E = blk->statements.front(); E; E = E->next()) {
+ const GDScriptParser::Node *expr = E->get();
+ if (expr->line > p_context.line || expr->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ continue;
+ }
- if (lv->assign && lv->name == p_identifier) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(expr);
+ if (op->op != GDScriptParser::OperatorNode::OP_ASSIGN || op->line < last_assign_line) {
+ continue;
+ }
- last_assign = lv->assign;
- last_assign_line = context.block->statements[i]->line;
+ if (op->arguments.size() >= 2 && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
+ if (id->name == p_identifier) {
+ last_assign_line = op->line;
+ last_assigned_expression = op->arguments[1];
+ }
}
}
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(context.block->statements[i]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
-
- if (op->arguments.size() && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
-
- if (id->name == p_identifier) {
-
- last_assign = op->arguments[1];
- last_assign_line = context.block->statements[i]->line;
- }
+ if (blk->if_condition && blk->if_condition->type == GDScriptParser::Node::TYPE_OPERATOR && static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition)->op == GDScriptParser::OperatorNode::OP_IS) {
+ //is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
+ //super dirty hack, but very useful
+ //credit: Zylann
+ //TODO: this could be hacked to detect ANDed conditions too..
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition);
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name == p_identifier) {
+ //bingo
+ GDScriptCompletionContext c = p_context;
+ c.line = op->line;
+ c.block = blk;
+ if (_guess_expression_type(p_context, op->arguments[1], r_type)) {
+ return true;
}
}
}
- }
- //use the last assignment, (then backwards?)
- if (last_assign && last_assign_line != p_line) {
-
- return _guess_expression_type(context, last_assign, last_assign_line, r_type);
+ blk = blk->parent_block;
}
- return false;
-}
-
-static bool _guess_identifier_from_assignment_in_function(GDScriptCompletionContext &context, int p_src_line, const StringName &p_identifier, const StringName &p_function, GDScriptCompletionIdentifier &r_type) {
-
- const GDScriptParser::FunctionNode *func = NULL;
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_function) {
- func = context._class->functions[i];
- break;
+ if (last_assigned_expression && last_assign_line != p_context.line) {
+ GDScriptCompletionContext c = p_context;
+ c.line = last_assign_line;
+ r_type.assigned_expression = last_assigned_expression;
+ if (_guess_expression_type(c, last_assigned_expression, r_type)) {
+ return true;
}
}
- if (!func)
- return false;
+ if (var_type.has_type) {
+ r_type.type = var_type;
+ return true;
+ }
- for (int i = 0; i < func->body->statements.size(); i++) {
+ if (p_context.function) {
+ for (int i = 0; i < p_context.function->arguments.size(); i++) {
+ if (p_context.function->arguments[i] == p_identifier) {
+ if (p_context.function->argument_types[i].has_type) {
+ r_type.type = p_context.function->argument_types[i];
+ return true;
+ }
- if (func->body->statements[i]->line == p_src_line) {
- break;
+ int def_from = p_context.function->arguments.size() - p_context.function->default_values.size();
+ if (i >= def_from) {
+ int def_idx = def_from - i;
+ if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]);
+ if (op->arguments.size() < 2) {
+ return false;
+ }
+ GDScriptCompletionContext c = p_context;
+ c.function = NULL;
+ c.block = NULL;
+ return _guess_expression_type(c, op->arguments[1], r_type);
+ }
+ }
+ break;
+ }
}
- if (func->body->statements[i]->type == GDScriptParser::BlockNode::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(func->body->statements[i]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
-
- if (op->arguments.size() && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
-
- if (id->name == p_identifier) {
-
- return _guess_expression_type(context, op->arguments[1], func->body->statements[i]->line, r_type);
+ GDScriptParser::DataType base_type = p_context._class->base_type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid() && gds->has_method(p_context.function->name)) {
+ GDScriptFunction *func = gds->get_member_functions()[p_context.function->name];
+ if (func) {
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (func->get_argument_name(i) == p_identifier) {
+ r_type = _type_from_gdtype(func->get_argument_type(i));
+ return true;
+ }
+ }
+ }
+ Ref<GDScript> base_gds = gds->get_base_script();
+ if (base_gds.is_valid()) {
+ base_type.kind = GDScriptParser::DataType::GDSCRIPT;
+ base_type.script_type = base_gds;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
}
- }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(base_type.native_type, &methods);
+ ClassDB::get_virtual_methods(base_type.native_type, &methods);
+
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_context.function->name) {
+ MethodInfo &mi = E->get();
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ if (E->get().name == p_identifier) {
+ r_type = _type_from_property(E->get());
+ return true;
+ }
+ }
+ }
+ }
+ base_type.has_type = false;
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
}
- return false;
-}
-
-static bool _guess_identifier_type(GDScriptCompletionContext &context, int p_line, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type, bool p_for_indexing) {
-
- //go to block first
-
- const GDScriptParser::BlockNode *block = context.block;
+ // Check current class (including inheritance)
+ if (p_context._class) {
+ GDScriptCompletionIdentifier context_base;
+ context_base.value = p_context.base;
+ context_base.type.has_type = true;
+ context_base.type.kind = GDScriptParser::DataType::CLASS;
+ context_base.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ context_base.type.is_meta_type = p_context.function && p_context.function->_static;
- while (block) {
-
- GDScriptCompletionContext c = context;
- c.block = block;
-
- if (_guess_identifier_type_in_block(c, p_line, p_identifier, r_type)) {
+ if (_guess_identifier_type_from_base(p_context, context_base, p_identifier, r_type)) {
return true;
}
-
- block = block->parent_block;
}
- //guess from argument if virtual
- if (context.function && context.function->name != StringName()) {
-
- int argindex = -1;
+ // Check named scripts
+ if (ScriptServer::is_global_class(p_identifier)) {
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ if (scr.is_valid()) {
+ r_type = _type_from_variant(scr);
+ r_type.type.is_meta_type = true;
+ return true;
+ }
+ return false;
+ }
- for (int i = 0; i < context.function->arguments.size(); i++) {
+ // 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];
+ }
+ return true;
+ }
- if (context.function->arguments[i] == p_identifier) {
- argindex = i;
- break;
- }
+ // 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];
}
+ return true;
+ }
- if (argindex != -1) {
- GDScriptCompletionIdentifier id = _get_native_class(context);
- if (id.type == Variant::OBJECT && id.obj_type != StringName()) {
- //this kinda sucks but meh
+ // Check autoload singletons
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
+ return true;
+ }
- List<MethodInfo> vmethods;
- ClassDB::get_virtual_methods(id.obj_type, &vmethods);
- for (List<MethodInfo>::Element *E = vmethods.front(); E; E = E->next()) {
+ return false;
+}
- if (E->get().name == context.function->name && argindex < E->get().arguments.size()) {
+static bool _guess_identifier_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type->constant_expressions.has(p_identifier)) {
+ GDScriptParser::ClassNode::Constant c = base_type.class_type->constant_expressions[p_identifier];
+ r_type.type = c.type;
+ if (c.expression->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ r_type.value = static_cast<const GDScriptParser::ConstantNode *>(c.expression)->value;
+ }
+ return true;
+ }
- PropertyInfo arg = E->get().arguments[argindex];
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->variables.size(); i++) {
+ GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i];
+ if (m.identifier == p_identifier) {
+ if (m.data_type.has_type) {
+ r_type.type = m.data_type;
+ return true;
+ }
+ if (m.expression) {
+ if (_guess_expression_type(p_context, m.expression, r_type)) {
+ return true;
+ }
+ if (m.expression->get_datatype().has_type) {
+ r_type.type = m.expression->get_datatype();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if (gds->get_constants().has(p_identifier)) {
+ r_type = _type_from_variant(gds->get_constants()[p_identifier]);
+ return true;
+ }
+ if (!_static) {
+ const Set<StringName>::Element *m = gds->get_members().find(p_identifier);
+ if (m) {
+ r_type = _type_from_gdtype(gds->get_member_type(p_identifier));
+ return true;
+ }
+ }
+ Ref<GDScript> parent = gds->get_base_script();
+ if (parent.is_valid()) {
+ base_type.script_type = parent;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ if (constants.has(p_identifier)) {
+ r_type = _type_from_variant(constants[p_identifier]);
+ return true;
+ }
- int scp = String(arg.name).find(":");
- if (scp != -1) {
+ if (!_static) {
+ List<PropertyInfo> members;
+ scr->get_script_property_list(&members);
+ for (const List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == p_identifier) {
+ r_type = _type_from_property(prop);
+ return true;
+ }
+ }
+ }
+ Ref<Script> parent = scr->get_base_script();
+ if (parent.is_valid()) {
+ base_type.script_type = parent;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ return false;
+ }
+ }
- r_type.type = Variant::OBJECT;
- r_type.obj_type = String(arg.name).substr(scp + 1, String(arg.name).length());
- return true;
+ // Skip constants since they're all integers. Type does not matter because int has no members
+ List<PropertyInfo> props;
+ ClassDB::get_property_list(class_name, &props);
+ for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == p_identifier) {
+ StringName getter = ClassDB::get_property_getter(class_name, p_identifier);
+ if (getter != StringName()) {
+ MethodBind *g = ClassDB::get_method(class_name, getter);
+ if (g) {
+ r_type = _type_from_property(g->get_return_info());
+ return true;
+ }
} else {
-
- r_type.type = arg.type;
- if (arg.hint == PROPERTY_HINT_RESOURCE_TYPE)
- r_type.obj_type = arg.hint_string;
+ r_type = _type_from_property(prop);
return true;
}
+ break;
}
}
- }
+ return false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ if (err.error != Variant::CallError::CALL_OK) {
+ return false;
+ }
+ bool valid = false;
+ Variant res = tmp.get(p_identifier, &valid);
+ if (valid) {
+ r_type = _type_from_variant(res);
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ return true;
+ }
+ return false;
+ } break;
+ default: {
+ return false;
+ } break;
}
}
- //guess type in constant
+ return false;
+}
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
+static bool _find_last_return_in_block(const GDScriptCompletionContext &p_context, int &r_last_return_line, const GDScriptParser::Node **r_last_returned_value) {
+ if (!p_context.block) {
+ return false;
+ }
- if (context._class->constant_expressions[i].identifier == p_identifier) {
+ for (int i = 0; i < p_context.block->statements.size(); i++) {
+ if (p_context.block->statements[i]->line < r_last_return_line) {
+ continue;
+ }
+ if (p_context.block->statements[i]->type != GDScriptParser::Node::TYPE_CONTROL_FLOW) {
+ continue;
+ }
- ERR_FAIL_COND_V(context._class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT, false);
- r_type = _get_type_from_variant(static_cast<const GDScriptParser::ConstantNode *>(context._class->constant_expressions[i].expression)->value);
- return true;
+ const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(p_context.block->statements[i]);
+ if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_RETURN && cf->arguments.size() > 0) {
+ if (cf->line > r_last_return_line) {
+ r_last_return_line = cf->line;
+ *r_last_returned_value = cf->arguments[0];
+ }
}
}
- if (!(context.function && context.function->_static)) {
+ // Recurse into subblocks
+ for (int i = 0; i < p_context.block->sub_blocks.size(); i++) {
+ GDScriptCompletionContext c = p_context;
+ c.block = p_context.block->sub_blocks[i];
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ }
- for (int i = 0; i < context._class->variables.size(); i++) {
+ return false;
+}
- if (context._class->variables[i].identifier == p_identifier) {
+static bool _guess_method_return_type_from_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
- if (context._class->variables[i]._export.type != Variant::NIL) {
+ if (_static && p_method == "new") {
+ r_type.type = base_type;
+ r_type.type.is_meta_type = false;
+ r_type.type.is_constant = false;
+ return true;
+ }
- r_type = _get_type_from_pinfo(context._class->variables[i]._export);
- return true;
- } else if (context._class->variables[i].expression) {
- if (p_line <= context._class->variables[i].line)
- return false;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (!base_type.class_type) {
+ base_type.has_type = false;
+ break;
+ }
- bool rtype = _guess_expression_type(context, context._class->variables[i].expression, context._class->variables[i].line, r_type);
- if (rtype && r_type.type != Variant::NIL)
- return true;
- //return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type);
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_method) {
+ int last_return_line = -1;
+ const GDScriptParser::Node *last_returned_value = NULL;
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.function = base_type.class_type->static_functions[i];
+ c.block = c.function->body;
+
+ _find_last_return_in_block(c, last_return_line, &last_returned_value);
+ if (last_returned_value) {
+ c.line = c.block->end_line;
+ return _guess_expression_type(c, last_returned_value, r_type);
+ }
+ }
+ }
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_method) {
+ int last_return_line = -1;
+ const GDScriptParser::Node *last_returned_value = NULL;
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.function = base_type.class_type->functions[i];
+ c.block = c.function->body;
+
+ _find_last_return_in_block(c, last_return_line, &last_returned_value);
+ if (last_returned_value) {
+ c.line = c.block->end_line;
+ return _guess_expression_type(c, last_returned_value, r_type);
+ }
+ }
+ }
}
- //try to guess from assignment in constructor or _ready
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_ready", r_type))
- return true;
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_enter_tree", r_type))
- return true;
- if (_guess_identifier_from_assignment_in_function(context, p_line + 1, p_identifier, "_init", r_type))
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if (gds->get_member_functions().has(p_method)) {
+ r_type = _type_from_gdtype(gds->get_member_functions()[p_method]->get_return_type());
+ return true;
+ }
+ Ref<GDScript> base_script = gds->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ List<MethodInfo> methods;
+ scr->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+ if (mi.name == p_method) {
+ r_type = _type_from_property(mi.return_val);
+ return true;
+ }
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName native = base_type.native_type;
+ if (!ClassDB::class_exists(native)) {
+ native = String("_") + native;
+ if (!ClassDB::class_exists(native)) {
+ return false;
+ }
+ }
+ MethodBind *mb = ClassDB::get_method(native, p_method);
+ if (mb) {
+ r_type = _type_from_property(mb->get_return_info());
return true;
+ }
+ return false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return false;
+ }
+ List<MethodInfo> methods;
+ tmp.get_method_list(&methods);
+
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+ if (mi.name == p_method) {
+ r_type = _type_from_property(mi.return_val);
+ return true;
+ }
+ }
+ return false;
+ } break;
+ default: {
return false;
}
}
}
+ return false;
+}
- //autoloads as singletons
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
- String s = E->get().name;
- if (!s.begins_with("autoload/"))
- continue;
- String name = s.get_slice("/", 1);
- if (name == String(p_identifier)) {
+static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx) {
- String path = ProjectSettings::get_singleton()->get(s);
- if (path.begins_with("*")) {
- String script = path.substr(1, path.length());
+ String arghint = _get_visual_datatype(p_info.return_val, false) + " " + p_info.name + "(";
- if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
- script = script.get_basename() + ".gd";
- }
+ int def_args = p_info.arguments.size() - p_info.default_arguments.size();
+ int i = 0;
+ for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
+ if (i > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
+ }
- if (FileAccess::exists(script)) {
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += _get_visual_datatype(E->get(), true) + " " + E->get().name;
- //print_line("is a script");
+ if (i - def_args >= 0) {
+ arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
+ }
- Ref<Script> scr;
- if (ScriptCodeCompletionCache::get_singleton())
- scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
- else
- scr = ResourceLoader::load(script);
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
- r_type.obj_type = "Node";
- r_type.type = Variant::OBJECT;
- r_type.script = scr;
- r_type.value = Variant();
+ i++;
+ }
- return true;
- }
- }
+ if (p_info.flags & METHOD_FLAG_VARARG) {
+ if (p_info.arguments.size() > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
+ }
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += "...";
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
}
}
+ if (p_info.arguments.size() > 0 || (p_info.flags & METHOD_FLAG_VARARG)) {
+ arghint += " ";
+ }
- //global
- for (Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
- if (E->key() == p_identifier) {
+ arghint += ")";
- r_type = _get_type_from_variant(GDScriptLanguage::get_singleton()->get_global_array()[E->get()], !p_for_indexing);
- return true;
- }
- }
- return false;
+ return arghint;
}
-static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p_line, bool p_only_functions, Set<String> &result) {
+static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
- if (p_only_functions)
- return;
+ String arghint = p_function->return_type.to_string() + " " + p_function->name.operator String() + "(";
- for (int i = 0; i < context.block->statements.size(); i++) {
+ int def_args = p_function->arguments.size() - p_function->default_values.size();
+ for (int i = 0; i < p_function->arguments.size(); i++) {
+ if (i > 0) {
+ arghint += ", ";
+ } else {
+ arghint += " ";
+ }
- GDScriptParser::Node *statement = context.block->statements[i];
- if (statement->line > p_line)
- continue;
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += p_function->argument_types[i].to_string() + " " + p_function->arguments[i].operator String();
+
+ if (i - def_args >= 0) {
+ String def_val = "<unknown>";
+ if (p_function->default_values[i - def_args] && p_function->default_values[i - def_args]->type == GDScriptParser::Node::TYPE_OPERATOR) {
+ const GDScriptParser::OperatorNode *assign = static_cast<const GDScriptParser::OperatorNode *>(p_function->default_values[i - def_args]);
+
+ if (assign->arguments.size() >= 2) {
+ if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(assign->arguments[1]);
+ def_val = cn->value.get_construct_string();
+ } else if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->arguments[1]);
+ def_val = id->name.operator String();
+ }
+ }
+ }
+ arghint += " = " + def_val;
+ }
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ }
- GDScriptParser::BlockNode::Type statementType = statement->type;
- if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
+ if (p_function->arguments.size() > 0) {
+ arghint += " ";
+ }
+ arghint += ")";
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement);
- result.insert(lv->name.operator String());
- } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) {
+ return arghint;
+}
- const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
- if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) {
+static void _find_enumeration_candidates(const String p_enum_hint, Set<String> &r_result) {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]);
- result.insert(id->name.operator String());
+ if (p_enum_hint.find(".") == -1) {
+ // Global constant
+ StringName current_enum = p_enum_hint;
+ for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
+ if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
+ r_result.insert(GlobalConstants::get_global_constant_name(i));
}
}
- }
-}
-
-static void _find_identifiers_in_class(GDScriptCompletionContext &context, bool p_static, bool p_only_functions, Set<String> &result) {
+ } else {
+ String class_name = p_enum_hint.get_slice(".", 0);
+ String enum_name = p_enum_hint.get_slice(".", 1);
- if (!p_static && !p_only_functions) {
+ if (!ClassDB::class_exists(class_name)) {
+ return;
+ }
- for (int i = 0; i < context._class->variables.size(); i++) {
- result.insert(context._class->variables[i].identifier);
+ List<StringName> enum_constants;
+ ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);
+ for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
+ String candidate = class_name + "." + E->get();
+ r_result.insert(candidate);
}
}
- if (!p_only_functions) {
+}
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
- result.insert(context._class->constant_expressions[i].identifier);
+static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Set<String> &r_result) {
+ for (Map<StringName, GDScriptParser::LocalVarNode *>::Element *E = p_context.block->variables.front(); E; E = E->next()) {
+ if (E->get()->line < p_context.line) {
+ r_result.insert(E->key().operator String());
}
+ }
+ if (p_context.block->parent_block) {
+ GDScriptCompletionContext c = p_context;
+ c.block = p_context.block->parent_block;
+ _find_identifiers_in_block(c, r_result);
+ }
+}
+
+static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result);
- for (int i = 0; i < context._class->subclasses.size(); i++) {
- result.insert(context._class->subclasses[i]->name);
+static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Set<String> &r_result) {
+ if (!p_parent_only) {
+ if (!p_static && !p_only_functions) {
+ for (int i = 0; i < p_context._class->variables.size(); i++) {
+ r_result.insert(p_context._class->variables[i].identifier);
+ }
}
- }
- for (int i = 0; i < context._class->static_functions.size(); i++) {
- if (context._class->static_functions[i]->arguments.size())
- result.insert(context._class->static_functions[i]->name.operator String() + "(");
- else
- result.insert(context._class->static_functions[i]->name.operator String() + "()");
- }
+ if (!p_only_functions) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_context._class->constant_expressions.front(); E; E = E->next()) {
+ r_result.insert(E->key());
+ }
+ for (int i = 0; i < p_context._class->subclasses.size(); i++) {
+ r_result.insert(p_context._class->subclasses[i]->name);
+ }
+ }
- if (!p_static) {
+ for (int i = 0; i < p_context._class->static_functions.size(); i++) {
+ if (p_context._class->static_functions[i]->arguments.size()) {
+ r_result.insert(p_context._class->static_functions[i]->name.operator String() + "(");
+ } else {
+ r_result.insert(p_context._class->static_functions[i]->name.operator String() + "()");
+ }
+ }
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->arguments.size())
- result.insert(context._class->functions[i]->name.operator String() + "(");
- else
- result.insert(context._class->functions[i]->name.operator String() + "()");
+ if (!p_static) {
+ for (int i = 0; i < p_context._class->functions.size(); i++) {
+ if (p_context._class->functions[i]->arguments.size()) {
+ r_result.insert(p_context._class->functions[i]->name.operator String() + "(");
+ } else {
+ r_result.insert(p_context._class->functions[i]->name.operator String() + "()");
+ }
+ }
}
}
- //globals
-
- Ref<Reference> base = _get_parent_class(context);
+ // Parents
+ GDScriptCompletionIdentifier base_type;
+ base_type.type = p_context._class->base_type;
+ base_type.type.is_meta_type = p_static;
+ base_type.value = p_context.base;
- while (true) {
+ GDScriptCompletionContext c = p_context;
+ c.block = NULL;
+ c.function = NULL;
- Ref<GDScript> script = base;
- Ref<GDScriptNativeClass> nc = base;
- if (script.is_valid()) {
+ _find_identifiers_in_base(c, base_type, p_only_functions, r_result);
+}
- if (!p_static && !p_only_functions) {
- for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) {
- result.insert(E->get().operator String());
+static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result) {
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
+
+ if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ r_result.insert("new(");
+ }
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ GDScriptCompletionContext c = p_context;
+ c._class = base_type.class_type;
+ c.block = NULL;
+ c.function = NULL;
+ _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> script = base_type.script_type;
+ if (script.is_valid()) {
+ if (!_static && !p_only_functions) {
+ for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) {
+ r_result.insert(E->get().operator String());
+ }
+ }
+ if (!p_only_functions) {
+ for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
+ for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
+ if (!_static || E->get()->is_static()) {
+ if (E->get()->get_argument_count()) {
+ r_result.insert(E->key().operator String() + "(");
+ } else {
+ r_result.insert(E->key().operator String() + "()");
+ }
+ }
+ }
+ if (!p_only_functions) {
+ for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
+ base_type = GDScriptParser::DataType();
+ if (script->get_base().is_valid()) {
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::GDSCRIPT;
+ base_type.script_type = script->get_base();
+ } else {
+ base_type.has_type = script->get_instance_base_type() != StringName();
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.script_type = script->get_instance_base_type();
+ }
+ } else {
+ return;
}
- }
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ if (!_static && !p_only_functions) {
+ List<PropertyInfo> members;
+ scr->get_script_property_list(&members);
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ r_result.insert(E->get().name);
+ }
+ }
+ if (!p_only_functions) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
+ }
+ }
- if (!p_only_functions) {
- for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
- }
- }
+ List<MethodInfo> methods;
+ scr->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
+ }
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
- if (!p_static || E->get()->is_static()) {
- if (E->get()->get_argument_count())
- result.insert(E->key().operator String() + "(");
- else
- result.insert(E->key().operator String() + "()");
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return;
}
- }
-
- if (!p_only_functions) {
- for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName type = base_type.native_type;
+ if (!ClassDB::class_exists(type)) {
+ type = String("_") + type;
+ if (!ClassDB::class_exists(type)) {
+ return;
+ }
}
- }
- base = script->get_base();
- if (base.is_null())
- base = script->get_native();
- } else if (nc.is_valid()) {
+ if (!p_only_functions) {
+ List<String> constants;
+ ClassDB::get_integer_constant_list(type, &constants);
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ r_result.insert(E->get());
+ }
- StringName type = nc->get_name();
+ if (!_static) {
+ List<PropertyInfo> pinfo;
+ ClassDB::get_property_list(type, &pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ continue;
+ }
+ if (E->get().name.find("/") != -1) {
+ continue;
+ }
+ r_result.insert(E->get().name);
+ }
+ }
+ }
- if (!p_only_functions) {
+ if (!_static) {
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(type, &methods, false, true);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("_")) {
+ continue;
+ }
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
+ }
+ }
- List<String> constants;
- ClassDB::get_integer_constant_list(type, &constants);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- result.insert(E->get());
+ return;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return;
}
- List<PropertyInfo> pinfo;
+ if (!p_only_functions) {
+ List<PropertyInfo> members;
+ tmp.get_property_list(&members);
- ClassDB::get_property_list(type, &pinfo);
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ if (String(E->get().name).find("/") == -1) {
+ r_result.insert(E->get().name);
+ }
+ }
+ }
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- result.insert(E->get().name);
+ List<MethodInfo> methods;
+ tmp.get_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().arguments.size()) {
+ r_result.insert(E->get().name + "(");
+ } else {
+ r_result.insert(E->get().name + "()");
+ }
}
- }
- List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods, false, true);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_"))
- continue;
- if (E->get().arguments.size())
- result.insert(E->get().name + "(");
- else
- result.insert(E->get().name + "()");
- }
- break;
- } else
- break;
+ return;
+ } break;
+ default: {
+ return;
+ } break;
+ }
}
}
-static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bool p_only_functions, Set<String> &result) {
+static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Set<String> &r_result) {
- const GDScriptParser::BlockNode *block = context.block;
+ const GDScriptParser::BlockNode *block = p_context.block;
- if (context.function) {
+ if (p_context.function) {
- const GDScriptParser::FunctionNode *f = context.function;
+ const GDScriptParser::FunctionNode *f = p_context.function;
for (int i = 0; i < f->arguments.size(); i++) {
- result.insert(f->arguments[i].operator String());
+ r_result.insert(f->arguments[i].operator String());
}
}
- while (block) {
-
- GDScriptCompletionContext c = context;
+ if (!p_only_functions && block) {
+ GDScriptCompletionContext c = p_context;
c.block = block;
-
- _find_identifiers_in_block(c, p_line, p_only_functions, result);
- block = block->parent_block;
+ _find_identifiers_in_block(c, r_result);
}
- const GDScriptParser::ClassNode *clss = context._class;
-
- bool _static = context.function && context.function->_static;
+ const GDScriptParser::ClassNode *clss = p_context._class;
+ bool _static = !p_context.function || p_context.function->_static;
while (clss) {
- GDScriptCompletionContext c = context;
+ GDScriptCompletionContext c = p_context;
c._class = clss;
c.block = NULL;
c.function = NULL;
- _find_identifiers_in_class(c, _static, p_only_functions, result);
+ _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
+ _static = true;
clss = clss->owner;
}
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
-
- result.insert(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+ MethodInfo mi = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
+ if (mi.arguments.size() || (mi.flags & METHOD_FLAG_VARARG)) {
+ r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "(");
+ } else {
+ r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "()");
+ }
}
static const char *_type_names[Variant::VARIANT_MAX] = {
@@ -1526,667 +2102,372 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo
};
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- result.insert(_type_names[i]);
+ r_result.insert(_type_names[i]);
}
- List<String> reserved_words;
- GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words);
+ static const char *_keywords[] = {
+ "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
+ "breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
+ "const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif",
+ "else", "for", "pass", "return", "match", "while", "remote", "sync", "master", "slave",
+ "remotesync", "mastersync", "slavesync",
+ 0
+ };
- for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) {
- result.insert(E->get());
+ const char **kw = _keywords;
+ while (*kw) {
+ r_result.insert(*kw);
+ kw++;
}
- //autoload singletons
+ // Autoload singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
-
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
String s = E->get().name;
- if (!s.begins_with("autoload/"))
+ if (!s.begins_with("autoload/")) {
continue;
- String name = s.get_slice("/", 1);
+ }
String path = ProjectSettings::get_singleton()->get(s);
if (path.begins_with("*")) {
- result.insert(name);
+ r_result.insert(s.get_slice("/", 1));
}
}
- for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
- result.insert(E->key().operator String());
+ // Named scripts
+ List<StringName> named_scripts;
+ ScriptServer::get_global_class_list(&named_scripts);
+ for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
+ r_result.insert(E->get().operator String());
}
-}
-static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = true) {
-
- String n = p_info.name;
- int idx = n.find(":");
- if (idx != -1) {
- return n.substr(idx + 1, n.length());
- }
-
- if (p_info.type == Variant::OBJECT && p_info.hint == PROPERTY_HINT_RESOURCE_TYPE)
- return p_info.hint_string;
- if (p_info.type == Variant::NIL) {
- if (p_isarg)
- return "var";
- else
- return "void";
+ // Native classes
+ for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
+ r_result.insert(E->key().operator String());
}
-
- return Variant::get_type_name(p_info.type);
}
-static void _make_function_hint(const GDScriptParser::FunctionNode *p_func, int p_argidx, String &arghint) {
-
- arghint = "func " + p_func->name + "(";
- for (int i = 0; i < p_func->arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += p_func->arguments[i].operator String();
- int deffrom = p_func->arguments.size() - p_func->default_values.size();
-
- if (i >= deffrom) {
- int defidx = deffrom - i;
-
- if (defidx >= 0 && defidx < p_func->default_values.size()) {
-
- if (p_func->default_values[defidx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
-
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[defidx]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]);
- arghint += "=" + cn->value.get_construct_string();
+static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Set<String> &r_result, String &r_arghint) {
+ Variant base = p_base.value;
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = false;
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_method) {
+ r_arghint = _make_arguments_hint(base_type.class_type->static_functions[i], p_argidx);
+ return;
+ }
+ }
+ if (!_static) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_method) {
+ r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx);
+ return;
+ }
}
- } else {
}
- }
- }
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (p_func->arguments.size() > 0)
- arghint += " ";
- arghint += ")";
-}
-
-void get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) {
-
- for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- get_directory_contents(p_dir->get_subdir(i), r_list);
- }
-
- for (int i = 0; i < p_dir->get_file_count(); i++) {
- r_list.insert("\"" + p_dir->get_file_path(i) + "\"");
- }
-}
-
-static void _find_type_arguments(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, const StringName &p_method, const GDScriptCompletionIdentifier &id, int p_argidx, Set<String> &result, bool &r_forced, String &arghint) {
-
- //print_line("find type arguments?");
- if (id.type == Variant::OBJECT && id.obj_type != StringName()) {
-
- MethodBind *m = ClassDB::get_method(id.obj_type, p_method);
- if (!m) {
- //not in static method, see script
-
- //print_line("not in static: "+String(p_method));
- Ref<GDScript> on_script;
-
- if (id.value.get_type()) {
- Object *obj = id.value;
- GDScript *scr = Object::cast_to<GDScript>(obj);
- if (scr) {
- while (scr) {
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
+ r_result.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\"");
+ }
+ }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->is_static() && p_method == E->get()->get_name()) {
- arghint = "static func " + String(p_method) + "(";
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- arghint += ")";
- return; //found
- }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = base_type.script_type;
+ if (gds.is_valid()) {
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ List<MethodInfo> signals;
+ gds->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().name + "\"");
}
-
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ }
+ Ref<GDScript> base_script = gds->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = gds->get_instance_base_type();
}
} else {
- if (obj) {
- on_script = obj->get_script();
+ return;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ base_type.has_type = false;
+ break;
}
}
- }
-
- //print_line("but it has a script?");
- if (!on_script.is_valid() && id.script.is_valid()) {
- //print_line("yes");
- on_script = id.script;
- }
-
- if (on_script.is_valid()) {
-
- GDScript *scr = on_script.ptr();
- if (scr) {
- while (scr) {
-
- String code = scr->get_source_code();
- //print_line("has source code!");
-
- if (code != "") {
- //if there is code, parse it. This way is slower but updates in real-time
- GDScriptParser p;
- //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
-
- Error err = p.parse(scr->get_source_code(), scr->get_path().get_base_dir(), true, "", false);
- if (err == OK) {
- //print_line("checking the functions...");
- //only if ok, otherwise use what is cached on the script
- //GDScriptParser::ClassNode *base = p.
- const GDScriptParser::Node *root = p.get_parse_tree();
- ERR_FAIL_COND(root->type != GDScriptParser::Node::TYPE_CLASS);
+ List<MethodInfo> methods;
+ ClassDB::get_method_list(class_name, &methods);
+ ClassDB::get_virtual_methods(class_name, &methods);
+ int method_args = 0;
- const GDScriptParser::ClassNode *cl = static_cast<const GDScriptParser::ClassNode *>(root);
-
- const GDScriptParser::FunctionNode *func = NULL;
- bool st = false;
-
- for (int i = 0; i < cl->functions.size(); i++) {
- //print_line(String(cl->functions[i]->name)+" vs "+String(p_method));
- if (cl->functions[i]->name == p_method) {
- func = cl->functions[i];
- }
- }
-
- for (int i = 0; i < cl->static_functions.size(); i++) {
-
- //print_line(String(cl->static_functions[i]->name)+" vs "+String(p_method));
- if (cl->static_functions[i]->name == p_method) {
- func = cl->static_functions[i];
- st = true;
- }
- }
-
- if (func) {
-
- arghint = "func " + String(p_method) + "(";
- if (st)
- arghint = "static " + arghint;
- for (int i = 0; i < func->arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + String(func->arguments[i]);
- int deffrom = func->arguments.size() - func->default_values.size();
- if (i >= deffrom) {
-
- int defidx = deffrom - i;
-
- if (defidx >= 0 && defidx < func->default_values.size() && func->default_values[defidx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(func->default_values[defidx]);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN) {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(op->arguments[1]);
- arghint += "=" + cn->value.get_construct_string();
- }
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
-
- arghint += " )";
- return;
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_method) {
+ method_args = E->get().arguments.size();
+ if (base.get_type() == Variant::OBJECT) {
+ Object *obj = base.operator Object *();
+ if (obj) {
+ List<String> options;
+ obj->get_argument_options(p_method, p_argidx, &options);
+ for (List<String>::Element *E = options.front(); E; E = E->next()) {
+ r_result.insert(E->get());
}
- } else {
- //print_line("failed parsing?");
- code = "";
}
}
- if (code == "") {
-
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (p_method == E->get()->get_name()) {
- arghint = "func " + String(p_method) + "(";
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "var " + E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- arghint += ")";
- return; //found
- }
+ if (p_argidx < method_args) {
+ PropertyInfo arg_info = E->get().arguments[p_argidx];
+ if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ _find_enumeration_candidates(arg_info.class_name, r_result);
}
}
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ break;
}
}
- }
-
- } else {
- //regular method
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
- if (p_argidx < m->get_argument_count()) {
- PropertyInfo pi = m->get_argument_info(p_argidx);
-
- if (pi.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
- String enumeration = pi.class_name;
- if (enumeration.find(".") != -1) {
- //class constant
- List<StringName> constants;
- String cls = enumeration.get_slice(".", 0);
- String enm = enumeration.get_slice(".", 1);
-
- ClassDB::get_enum_constants(cls, enm, &constants);
- //constants.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- String add = cls + "." + E->get();
- result.insert(add);
- r_forced = true;
- }
- } else {
-
- //global constant
- StringName current_enum = enumeration;
-
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- result.insert(GlobalConstants::get_global_constant_name(i));
- r_forced = true;
- }
- }
- //global
+ if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) {
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(class_name, &signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().name + "\"");
}
}
- }
-#endif
- if (p_method.operator String() == "connect" || (p_method.operator String() == "emit_signal" && p_argidx == 0)) {
-
- if (p_argidx == 0) {
- List<MethodInfo> sigs;
- ClassDB::get_signal_list(id.obj_type, &sigs);
-
- if (id.script.is_valid()) {
- id.script->get_script_signal_list(&sigs);
- } else if (id.value.get_type() == Variant::OBJECT) {
- Object *obj = id.value;
- if (obj && !obj->get_script().is_null()) {
- Ref<Script> scr = obj->get_script();
- if (scr.is_valid()) {
- scr->get_script_signal_list(&sigs);
- }
- }
- }
- for (List<MethodInfo>::Element *E = sigs.front(); E; E = E->next()) {
- result.insert("\"" + E->get().name + "\"");
- r_forced = true;
- }
-
- } else if (p_argidx == 2) {
+ if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
+ // Get autoloads
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
- if (context._class) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- result.insert("\"" + context._class->functions[i]->name + "\"");
- r_forced = true;
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
}
+ String name = s.get_slice("/", 1);
+ r_result.insert("\"/root/" + name + "\"");
}
}
- /*if (p_argidx==2) {
-
- ERR_FAIL_COND(p_node->type!=GDScriptParser::Node::TYPE_OPERATOR);
- const GDScriptParser::OperatorNode *op=static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->arguments.size()>)
-
- }*/
- } else {
-
- if (p_argidx == 0 && (String(p_method) == "get_node" || String(p_method) == "has_node") && ClassDB::is_parent_class(id.obj_type, "Node")) {
+ if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) {
+ // Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
-
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-
String s = E->get().name;
- if (!s.begins_with("autoload/"))
+ if (!s.begins_with("input/")) {
continue;
- //print_line("found "+s);
+ }
String name = s.get_slice("/", 1);
- result.insert("\"/root/" + name + "\"");
- r_forced = true;
+ r_result.insert("\"" + name + "\"");
}
}
- Object *obj = id.value;
- if (obj) {
- List<String> options;
- obj->get_argument_options(p_method, p_argidx, &options);
-
- for (List<String>::Element *E = options.front(); E; E = E->next()) {
-
- result.insert(E->get());
- r_forced = true;
+ base_type.has_type = false;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ if (base.get_type() == Variant::NIL) {
+ Variant::CallError err;
+ base = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ return;
}
}
- }
-
- arghint = _get_visual_datatype(m->get_return_info(), false) + " " + p_method.operator String() + String("(");
-
- for (int i = 0; i < m->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
-
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- String n = m->get_argument_info(i).name;
- int dp = n.find(":");
- if (dp != -1)
- n = n.substr(0, dp);
- arghint += _get_visual_datatype(m->get_argument_info(i)) + " " + n;
- int deffrom = m->get_argument_count() - m->get_default_argument_count();
-
- if (i >= deffrom) {
- int defidx = i - deffrom;
- if (defidx >= 0 && defidx < m->get_default_argument_count()) {
- Variant v = m->get_default_argument(i);
- arghint += "=" + v.get_construct_string();
+ List<MethodInfo> methods;
+ base.get_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name == p_method) {
+ r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ return;
}
}
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (m->get_argument_count() > 0)
- arghint += " ";
-
- arghint += ")";
+ base_type.has_type = false;
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
}
-static void _find_call_arguments(GDScriptCompletionContext &context, const GDScriptParser::Node *p_node, int p_line, int p_argidx, Set<String> &result, bool &r_forced, String &arghint) {
+static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) {
if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) {
-
return;
}
+ Variant base;
+ GDScriptParser::DataType base_type;
+ StringName function;
+ bool _static = false;
const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_node);
- if (op->op != GDScriptParser::OperatorNode::OP_CALL) {
+ GDScriptCompletionIdentifier connect_base;
+ if (op->op != GDScriptParser::OperatorNode::OP_CALL && op->op != GDScriptParser::OperatorNode::OP_PARENT_CALL) {
return;
}
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- //complete built-in function
- const GDScriptParser::BuiltInFunctionNode *fn = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- MethodInfo mi = GDScriptFunctions::get_info(fn->function);
+ if (!op->arguments.size()) {
+ return;
+ }
- if (mi.name == "load" && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), result);
- }
+ if (op->op == GDScriptParser::OperatorNode::OP_CALL) {
+ if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
+ // Complete built-in function
+ const GDScriptParser::BuiltInFunctionNode *fn = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(fn->function);
- arghint = _get_visual_datatype(mi.return_val, false) + " " + GDScriptFunctions::get_func_name(fn->function) + String("(");
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) {
- arghint += String::chr(0xFFFF);
- }
- arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name;
- if (i == p_argidx || ((mi.flags & METHOD_FLAG_VARARG) && i > p_argidx)) {
- arghint += String::chr(0xFFFF);
+ if ((mi.name == "load" || mi.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
}
- }
- if (mi.arguments.size() > 0)
- arghint += " ";
- arghint += ")";
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- //complete constructor
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
+ r_arghint = _make_arguments_hint(mi, p_argidx);
+ return;
- List<MethodInfo> mil;
- Variant::get_constructor_list(tn->vtype, &mil);
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
+ // Complete constructor
+ const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
- for (List<MethodInfo>::Element *E = mil.front(); E; E = E->next()) {
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(tn->vtype, &constructors);
- MethodInfo mi = E->get();
- if (mi.arguments.size() == 0)
- continue;
- if (E->prev())
- arghint += "\n";
- arghint += Variant::get_type_name(tn->vtype) + " " + Variant::get_type_name(tn->vtype) + String("(");
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += _get_visual_datatype(mi.arguments[i]) + " " + mi.arguments[i].name;
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
+ int i = 0;
+ for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
+ if (p_argidx >= E->get().arguments.size()) {
+ continue;
}
- }
- if (mi.arguments.size() > 0)
- arghint += " ";
- arghint += ")";
- }
-
- } else if (op->arguments.size() >= 2 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- //make sure identifier exists...
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_SELF) {
- //self, look up
-
- for (int i = 0; i < context._class->static_functions.size(); i++) {
- if (context._class->static_functions[i]->name == id->name) {
- _make_function_hint(context._class->static_functions[i], p_argidx, arghint);
- return;
+ if (i > 0) {
+ r_arghint += "\n";
}
+ r_arghint += _make_arguments_hint(E->get(), p_argidx);
+ i++;
}
+ return;
+ } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_SELF) {
- if (context.function && !context.function->_static) {
-
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == id->name) {
- _make_function_hint(context._class->functions[i], p_argidx, arghint);
- return;
- }
- }
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
}
- Ref<Reference> base = _get_parent_class(context);
+ base = p_context.base;
- while (true) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
+ function = id->name;
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ _static = p_context.function && p_context.function->_static;
- Ref<GDScript> script = base;
- Ref<GDScriptNativeClass> nc = base;
- if (script.is_valid()) {
-
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
-
- if (E->key() == id->name) {
-
- if (context.function && context.function->_static && !E->get()->is_static())
- continue;
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
- arghint = "func " + id->name.operator String() + String("(");
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
- if (i > 0)
- arghint += ", ";
- else
- arghint += " ";
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += E->get()->get_argument_name(i);
- int deffrom = E->get()->get_argument_count() - E->get()->get_default_argument_count();
- if (i >= deffrom) {
- int defidx = deffrom - i;
- if (defidx >= 0 && defidx < E->get()->get_default_argument_count()) {
- arghint += "=" + E->get()->get_default_argument(defidx).get_construct_string();
- }
- }
- if (i == p_argidx) {
- arghint += String::chr(0xFFFF);
- }
- }
- if (E->get()->get_argument_count() > 0)
- arghint += " ";
- arghint += ")";
- return;
- }
- }
+ } else {
+ if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
+ }
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
+ function = id->name;
- base = script->get_base();
- if (base.is_null())
- base = script->get_native();
- } else if (nc.is_valid()) {
+ GDScriptCompletionIdentifier ci;
+ if (_guess_expression_type(p_context, op->arguments[0], ci)) {
+ base_type = ci.type;
+ base = ci.value;
+ } else {
+ return;
+ }
+ _static = ci.type.is_meta_type;
- if (!(context.function && context.function->_static)) {
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
+ }
+ } else {
+ if (!p_context._class || op->arguments.size() < 1 || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return;
+ }
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
+ base_type.is_meta_type = p_context.function && p_context.function->_static;
+ base = p_context.base;
- GDScriptCompletionIdentifier ci;
- ci.type = Variant::OBJECT;
- ci.obj_type = nc->get_name();
- if (!context._class->owner)
- ci.value = context.base;
+ function = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
- _find_type_arguments(context, p_node, p_line, id->name, ci, p_argidx, result, r_forced, arghint);
- //guess type..
- /*
- List<MethodInfo> methods;
- ClassDB::get_method_list(type,&methods);
- for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
- if (E->get().arguments.size())
- result.insert(E->get().name+"(");
- else
- result.insert(E->get().name+"()");
- }*/
- }
- break;
- } else
- break;
- }
- } else {
- //indexed lookup
+ if (function == "connect" && op->arguments.size() >= 4) {
+ _guess_expression_type(p_context, op->arguments[3], connect_base);
+ }
+ }
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, op->arguments[0], p_line, ci)) {
+ GDScriptCompletionIdentifier ci;
+ ci.type = base_type;
+ ci.value = base;
+ _find_call_arguments(p_context, ci, function, p_argidx, _static, r_result, r_arghint);
- _find_type_arguments(context, p_node, p_line, id->name, ci, p_argidx, result, r_forced, arghint);
- return;
- }
+ if (function == "connect" && p_argidx == 2) {
+ Set<String> methods;
+ _find_identifiers_in_base(p_context, connect_base, true, methods);
+ for (Set<String>::Element *E = methods.front(); E; E = E->next()) {
+ r_result.insert("\"" + E->get().replace("(", "").replace(")", "") + "\"");
}
}
+
+ r_forced = r_result.size() > 0;
}
Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) {
- GDScriptParser p;
+ GDScriptParser parser;
- p.parse(p_code, p_base_path, false, "", true);
- bool isfunction = false;
- Set<String> options;
+ parser.parse(p_code, p_base_path, false, "", true);
r_forced = false;
+ Set<String> options;
GDScriptCompletionContext context;
- context._class = p.get_completion_class();
- context.block = p.get_completion_block();
- context.function = p.get_completion_function();
+ context._class = parser.get_completion_class();
+ context.block = parser.get_completion_block();
+ context.function = parser.get_completion_function();
context.base = p_owner;
context.base_path = p_base_path;
+ context.line = parser.get_completion_line();
+ bool is_function = false;
- switch (p.get_completion_type()) {
-
+ switch (parser.get_completion_type()) {
case GDScriptParser::COMPLETION_NONE: {
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
- Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(), &constants);
+ Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants);
for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
options.insert(E->get().operator String());
}
-
- } break;
- case GDScriptParser::COMPLETION_FUNCTION:
- isfunction = true;
- case GDScriptParser::COMPLETION_IDENTIFIER: {
-
- _find_identifiers(context, p.get_completion_line(), isfunction, options);
} break;
case GDScriptParser::COMPLETION_PARENT_FUNCTION: {
-
+ _find_identifiers_in_class(context, !context.function || context.function->_static, true, true, options);
+ } break;
+ case GDScriptParser::COMPLETION_FUNCTION: {
+ is_function = true;
+ } // fallthrough
+ case GDScriptParser::COMPLETION_IDENTIFIER: {
+ _find_identifiers(context, is_function, options);
} break;
case GDScriptParser::COMPLETION_GET_NODE: {
-
if (p_owner) {
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
@@ -2204,315 +2485,358 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
}
}
+
+ // Get autoloads
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ options.insert("\"/root/" + name + "\"");
+ }
}
} break;
- case GDScriptParser::COMPLETION_METHOD:
- isfunction = true;
+ case GDScriptParser::COMPLETION_METHOD: {
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_INDEX: {
-
- const GDScriptParser::Node *node = p.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR)
+ const GDScriptParser::Node *node = parser.get_completion_node();
+ if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
break;
+ }
+ const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(node);
+ if (op->arguments.size() < 1) {
+ break;
+ }
- GDScriptCompletionIdentifier t;
- if (_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], p.get_completion_line(), t, true)) {
-
- if (t.type == Variant::OBJECT && t.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = t.value;
- if (gdn.is_valid()) {
- StringName cn = gdn->get_name();
- List<String> cnames;
- ClassDB::get_integer_constant_list(cn, &cnames);
- for (List<String>::Element *E = cnames.front(); E; E = E->next()) {
- options.insert(E->get());
- }
-
- List<PropertyInfo> pinfo;
- ClassDB::get_property_list(cn, &pinfo);
-
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- options.insert(E->get().name);
- }
- }
- } else if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
-
- Ref<GDScript> on_script;
-
- if (t.value.get_type()) {
- Object *obj = t.value;
-
- GDScript *scr = Object::cast_to<GDScript>(obj);
- if (scr) {
- while (scr) {
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(context, op->arguments[0], base)) {
+ break;
+ }
- if (!isfunction) {
- for (const Map<StringName, Variant>::Element *E = scr->get_constants().front(); E; E = E->next()) {
- options.insert(E->key());
- }
- }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->is_static())
- options.insert(E->key());
- }
+ GDScriptCompletionContext c = context;
+ c.function = NULL;
+ c.block = NULL;
+ c.base = base.value.get_type() == Variant::OBJECT ? base.value.operator Object *() : NULL;
+ if (base.type.kind == GDScriptParser::DataType::CLASS) {
+ c._class = base.type.class_type;
+ } else {
+ c._class = NULL;
+ }
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ _find_identifiers_in_base(c, base, is_function, options);
+ } break;
+ case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
+ _find_call_arguments(context, parser.get_completion_node(), parser.get_completion_argument_index(), options, r_forced, r_call_hint);
+ } break;
+ case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
+ GDScriptParser::DataType native_type = context._class->base_type;
+ while (native_type.has_type && native_type.kind != GDScriptParser::DataType::NATIVE) {
+ switch (native_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ native_type = native_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<GDScript> gds = native_type.script_type;
+ if (gds.is_valid()) {
+ Ref<GDScript> base = gds->get_base_script();
+ if (base.is_valid()) {
+ native_type.script_type = base;
+ } else {
+ native_type.native_type = gds->get_instance_base_type();
+ native_type.kind = GDScriptParser::DataType::NATIVE;
}
} else {
- if (obj) {
- on_script = obj->get_script();
- }
+ native_type.has_type = false;
}
- }
-
- if (!on_script.is_valid() && t.script.is_valid()) {
- on_script = t.script;
- }
-
- if (on_script.is_valid()) {
-
- GDScript *scr = on_script.ptr();
- if (scr) {
- while (scr) {
-
- String code = scr->get_source_code();
-
- if (code != "") {
- //if there is code, parse it. This way is slower but updates in real-time
- GDScriptParser p;
-
- Error err = p.parse(scr->get_source_code(), scr->get_path().get_base_dir(), true, "", false);
-
- if (err == OK) {
- //only if ok, otherwise use what is cached on the script
- //GDScriptParser::ClassNode *base = p.
- const GDScriptParser::Node *root = p.get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_PARSE_ERROR);
-
- const GDScriptParser::ClassNode *cl = static_cast<const GDScriptParser::ClassNode *>(root);
-
- for (int i = 0; i < cl->functions.size(); i++) {
-
- if (cl->functions[i]->arguments.size())
- options.insert(String(cl->functions[i]->name) + "(");
- else
- options.insert(String(cl->functions[i]->name) + "()");
- }
-
- for (int i = 0; i < cl->static_functions.size(); i++) {
-
- if (cl->static_functions[i]->arguments.size())
- options.insert(String(cl->static_functions[i]->name) + "(");
- else
- options.insert(String(cl->static_functions[i]->name) + "()");
- }
+ } break;
+ default: {
+ native_type.has_type = false;
+ } break;
+ }
+ }
- if (!isfunction) {
- for (int i = 0; i < cl->variables.size(); i++) {
+ if (!native_type.has_type) {
+ break;
+ }
- options.insert(String(cl->variables[i].identifier));
- }
+ StringName class_name = native_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ break;
+ }
+ }
- for (int i = 0; i < cl->constant_expressions.size(); i++) {
+ bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool();
- options.insert(String(cl->constant_expressions[i].identifier));
- }
- }
+ List<MethodInfo> virtual_methods;
+ ClassDB::get_virtual_methods(class_name, &virtual_methods);
+ for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- } else {
- code = ""; //well, then no code
- }
- }
-
- if (code == "") {
- //use class directly, no code was found
- if (!isfunction) {
- for (const Map<StringName, Variant>::Element *E = scr->get_constants().front(); E; E = E->next()) {
- options.insert(E->key());
- }
- }
- for (const Map<StringName, GDScriptFunction *>::Element *E = scr->get_member_functions().front(); E; E = E->next()) {
- if (E->get()->get_argument_count())
- options.insert(String(E->key()) + "()");
- else
- options.insert(String(E->key()) + "(");
- }
-
- for (const Set<StringName>::Element *E = scr->get_members().front(); E; E = E->next()) {
- options.insert(E->get());
- }
- }
+ MethodInfo &mi = E->get();
+ String method_hint = mi.name;
+ if (method_hint.find(":") != -1) {
+ method_hint = method_hint.get_slice(":", 0);
+ }
+ method_hint += "(";
- if (scr->get_base().is_valid())
- scr = scr->get_base().ptr();
- else
- scr = NULL;
+ if (mi.arguments.size()) {
+ for (int i = 0; i < mi.arguments.size(); i++) {
+ if (i > 0) {
+ method_hint += ", ";
+ }
+ String arg = mi.arguments[i].name;
+ if (arg.find(":") != -1) {
+ arg = arg.substr(0, arg.find(":"));
+ }
+ method_hint += arg;
+ if (use_type_hint && mi.arguments[i].type != Variant::NIL) {
+ method_hint += ": ";
+ if (mi.arguments[i].type == Variant::OBJECT && mi.arguments[i].class_name != StringName()) {
+ method_hint += mi.arguments[i].class_name.operator String();
+ } else {
+ method_hint += Variant::get_type_name(mi.arguments[i].type);
}
}
}
-
- if (!isfunction) {
- ClassDB::get_integer_constant_list(t.obj_type, r_options);
-
- List<PropertyInfo> pinfo;
- ClassDB::get_property_list(t.obj_type, &pinfo);
-
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY))
- continue;
- if (String(E->get().name).find("/") != -1)
- continue;
- r_options->push_back(E->get().name);
- }
+ }
+ method_hint += ")";
+ if (use_type_hint && (mi.return_val.type != Variant::NIL || !(mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
+ method_hint += " -> ";
+ if (mi.return_val.type == Variant::NIL) {
+ method_hint += "void";
+ } else if (mi.return_val.type == Variant::OBJECT && mi.return_val.class_name != StringName()) {
+ method_hint += mi.return_val.class_name.operator String();
+ } else {
+ method_hint += Variant::get_type_name(mi.return_val.type);
}
+ }
+ method_hint += ":";
- List<MethodInfo> mi;
- ClassDB::get_method_list(t.obj_type, &mi, false, true);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
-
- if (E->get().name.begins_with("_"))
- continue;
+ options.insert(method_hint);
+ }
+ } break;
+ case GDScriptParser::COMPLETION_YIELD: {
+ const GDScriptParser::Node *node = parser.get_completion_node();
- if (E->get().arguments.size())
- options.insert(E->get().name + "(");
- else
- options.insert(E->get().name + "()");
- }
- } else {
+ GDScriptCompletionContext c = context;
+ c.line = node->line;
+ GDScriptCompletionIdentifier type;
+ if (!_guess_expression_type(c, node, type)) {
+ break;
+ }
- //check InputEvent hint
- {
- if (t.value.get_type() == Variant::NIL) {
- Variant::CallError ce;
- t.value = Variant::construct(t.type, NULL, 0, ce);
+ GDScriptParser::DataType base_type = type.type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
+ options.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\"");
}
-
- if (!isfunction) {
- List<PropertyInfo> pl;
- t.value.get_property_list(&pl);
- for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
-
- if (String(E->get().name).find("/") == -1)
- options.insert(E->get().name);
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ List<MethodInfo> signals;
+ scr->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ options.insert("\"" + E->get().name + "\"");
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ base_type.has_type = false;
+
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ break;
}
}
- List<MethodInfo> mi;
- t.value.get_method_list(&mi);
- for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
- if (E->get().arguments.size())
- options.insert(E->get().name + "(");
- else
- options.insert(E->get().name + "()");
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(class_name, &signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ options.insert("\"" + E->get().name + "\"");
}
+ } break;
+ default: {
+ base_type.has_type = false;
}
}
}
-
} break;
- case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
-
- _find_call_arguments(context, p.get_completion_node(), p.get_completion_line(), p.get_completion_argument_index(), options, r_forced, r_call_hint);
+ case GDScriptParser::COMPLETION_RESOURCE_PATH: {
+ if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
+ r_forced = true;
+ }
} break;
- case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
-
- GDScriptCompletionIdentifier cid = _get_native_class(context);
-
- if (cid.obj_type != StringName()) {
- List<MethodInfo> vm;
- ClassDB::get_virtual_methods(cid.obj_type, &vm);
- for (List<MethodInfo>::Element *E = vm.front(); E; E = E->next()) {
+ case GDScriptParser::COMPLETION_ASSIGN: {
+ GDScriptCompletionIdentifier type;
+ if (!_guess_expression_type(context, parser.get_completion_node(), type)) {
+ break;
+ }
- MethodInfo &mi = E->get();
- String m = mi.name;
- if (m.find(":") != -1)
- m = m.substr(0, m.find(":"));
- m += "(";
-
- if (mi.arguments.size()) {
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0)
- m += ", ";
- String n = mi.arguments[i].name;
- if (n.find(":") != -1)
- n = n.substr(0, n.find(":"));
- m += n;
+ if (!type.enumeration.empty()) {
+ _find_enumeration_candidates(type.enumeration, options);
+ r_forced = options.size() > 0;
+ }
+ } break;
+ case GDScriptParser::COMPLETION_TYPE_HINT: {
+ const GDScriptParser::ClassNode *clss = context._class;
+ while (clss) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = clss->constant_expressions.front(); E; E = E->next()) {
+ GDScriptCompletionIdentifier constant;
+ GDScriptCompletionContext c = context;
+ c.function = NULL;
+ c.block = NULL;
+ c.line = E->value().expression->line;
+ if (_guess_expression_type(c, E->value().expression, constant)) {
+ if (constant.type.has_type && constant.type.is_meta_type) {
+ options.insert(E->key().operator String());
}
}
- m += "):";
+ }
+ for (int i = 0; i < clss->subclasses.size(); i++) {
+ if (clss->subclasses[i]->name != StringName()) {
+ options.insert(clss->subclasses[i]->name.operator String());
+ }
+ }
+ clss = clss->owner;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ options.insert(Variant::get_type_name((Variant::Type)i));
+ }
+ }
- options.insert(m);
+ List<StringName> native_classes;
+ ClassDB::get_class_list(&native_classes);
+ for (List<StringName>::Element *E = native_classes.front(); E; E = E->next()) {
+ String class_name = E->get().operator String();
+ if (class_name.begins_with("_")) {
+ class_name = class_name.right(1);
}
+ if (Engine::get_singleton()->has_singleton(class_name)) {
+ continue;
+ }
+ options.insert(class_name);
}
- } break;
- case GDScriptParser::COMPLETION_YIELD: {
- const GDScriptParser::Node *node = p.get_completion_node();
+ // Named scripts
+ List<StringName> named_scripts;
+ ScriptServer::get_global_class_list(&named_scripts);
+ for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
+ options.insert(E->get().operator String());
+ }
- GDScriptCompletionIdentifier t;
- if (!_guess_expression_type(context, node, p.get_completion_line(), t))
+ if (parser.get_completion_identifier_is_function()) {
+ options.insert("void");
+ }
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_TYPE_HINT_INDEX: {
+ GDScriptCompletionIdentifier base;
+ String index = parser.get_completion_cursor().operator String();
+ if (!_guess_identifier_type(context, index.get_slice(".", 0), base)) {
break;
+ }
- if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
+ GDScriptCompletionContext c = context;
+ c._class = NULL;
+ c.function = NULL;
+ c.block = NULL;
+ bool finding = true;
+ index = index.right(index.find(".") + 1);
+ while (index.find(".") != -1) {
+ String id = index.get_slice(".", 0);
- List<MethodInfo> sigs;
- ClassDB::get_signal_list(t.obj_type, &sigs);
- for (List<MethodInfo>::Element *E = sigs.front(); E; E = E->next()) {
- options.insert("\"" + E->get().name + "\"");
- r_forced = true;
+ GDScriptCompletionIdentifier sub_base;
+ if (!_guess_identifier_type_from_base(c, base, id, sub_base)) {
+ finding = false;
+ break;
}
+ index = index.right(index.find(".") + 1);
+ base = sub_base;
}
- } break;
- case GDScriptParser::COMPLETION_RESOURCE_PATH: {
-
- if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
- get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
- r_forced = true;
+ if (!finding) {
+ break;
}
- } break;
- case GDScriptParser::COMPLETION_ASSIGN: {
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(context, p.get_completion_node(), p.get_completion_line(), ci)) {
-
- String enumeration = ci.enumeration;
- if (enumeration.find(".") != -1) {
- //class constant
- List<StringName> constants;
- String cls = enumeration.get_slice(".", 0);
- String enm = enumeration.get_slice(".", 1);
-
- ClassDB::get_enum_constants(cls, enm, &constants);
- //constants.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- String add = cls + "." + E->get();
- r_options->push_back(add);
- r_forced = true;
- }
- } else {
- //global constant
- StringName current_enum = enumeration;
+ GDScriptParser::DataType base_type = base.type;
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type) {
+ for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = base_type.class_type->constant_expressions.front(); E; E = E->next()) {
+ GDScriptCompletionIdentifier constant;
+ GDScriptCompletionContext c = context;
+ c._class = base_type.class_type;
+ c.function = NULL;
+ c.block = NULL;
+ c.line = E->value().expression->line;
+ if (_guess_expression_type(c, E->value().expression, constant)) {
+ if (constant.type.has_type && constant.type.is_meta_type) {
+ options.insert(E->key().operator String());
+ }
+ }
+ }
+ for (int i = 0; i < base_type.class_type->subclasses.size(); i++) {
+ if (base_type.class_type->subclasses[i]->name != StringName()) {
+ options.insert(base_type.class_type->subclasses[i]->name.operator String());
+ }
+ }
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- r_options->push_back(GlobalConstants::get_global_constant_name(i));
- r_forced = true;
+ base_type = base_type.class_type->base_type;
+ } else {
+ base_type.has_type = false;
}
- }
- //global
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ Ref<Script> const_scr = E->value();
+ if (const_scr.is_valid()) {
+ options.insert(E->key().operator String());
+ }
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.has_type = false;
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
}
}
-#endif
+ r_forced = options.size() > 0;
} break;
}
@@ -2531,6 +2855,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
#endif
+//////// END COMPLETION //////////
+
String GDScriptLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@@ -2602,8 +2928,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
break;
}
- //print_line(itos(indent_stack.size())+","+itos(tc)+": "+l);
- lines[i] = l;
+ lines.write[i] = l;
}
p_code = "";
@@ -2616,6 +2941,185 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
#ifdef TOOLS_ENABLED
+static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, bool p_is_function, GDScriptLanguage::LookupResult &r_result) {
+ GDScriptParser::DataType base_type = p_base;
+
+ while (base_type.has_type) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ if (base_type.class_type) {
+ if (p_is_function) {
+ for (int i = 0; i < base_type.class_type->functions.size(); i++) {
+ if (base_type.class_type->functions[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->functions[i]->line;
+ return OK;
+ }
+ }
+ for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
+ if (base_type.class_type->static_functions[i]->name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->static_functions[i]->line;
+ return OK;
+ }
+ }
+ } else {
+ if (base_type.class_type->constant_expressions.has(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->constant_expressions[p_symbol].expression->line;
+ return OK;
+ }
+
+ for (int i = 0; i < base_type.class_type->variables.size(); i++) {
+ if (base_type.class_type->variables[i].identifier == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->variables[i].line;
+ return OK;
+ }
+ }
+ }
+ }
+ base_type = base_type.class_type->base_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::GDSCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ int line = scr->get_member_line(p_symbol);
+ if (line >= 0) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = line;
+ r_result.script = scr;
+ return OK;
+ }
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName class_name = base_type.native_type;
+ if (!ClassDB::class_exists(class_name)) {
+ class_name = String("_") + class_name;
+ if (!ClassDB::class_exists(class_name)) {
+ base_type.has_type = false;
+ break;
+ }
+ }
+
+ if (ClassDB::has_method(class_name, p_symbol, true)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ List<MethodInfo> virtual_methods;
+ ClassDB::get_virtual_methods(class_name, &virtual_methods, true);
+ for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
+ if (E->get().name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true);
+ if (enum_name != StringName()) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = enum_name;
+ return OK;
+ }
+
+ List<String> constants;
+ ClassDB::get_integer_constant_list(class_name, &constants, true);
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ if (E->get() == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(class_name, &properties, true);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_symbol) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ StringName parent = ClassDB::get_parent_class(class_name);
+ if (parent != StringName()) {
+ if (String(parent).begins_with("_")) {
+ base_type.native_type = String(parent).right(1);
+ } else {
+ base_type.native_type = parent;
+ }
+ } else {
+ base_type.has_type = false;
+ }
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ base_type.has_type = false;
+
+ if (Variant::has_constant(base_type.builtin_type, p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ Variant v;
+ REF v_ref;
+ if (base_type.builtin_type == Variant::OBJECT) {
+ v_ref.instance();
+ v = v_ref;
+ } else {
+ Variant::CallError err;
+ v = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ break;
+ }
+ }
+
+ if (v.has_method(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
+ bool valid = false;
+ v.get(p_symbol, &valid);
+ if (valid) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name = Variant::get_type_name(base_type.builtin_type);
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ } break;
+ default: {
+ base_type.has_type = false;
+ } break;
+ }
+ }
+
+ return ERR_CANT_RESOLVE;
+}
+
Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) {
//before parsing, try the usual stuff
@@ -2623,6 +3127,13 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
r_result.class_name = p_symbol;
return OK;
+ } else {
+ String under_prefix = "_" + p_symbol;
+ if (ClassDB::class_exists(under_prefix)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name = p_symbol;
+ return OK;
+ }
}
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@@ -2650,17 +3161,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
- GDScriptParser p;
- p.parse(p_code, p_base_path, false, "", true);
+ GDScriptParser parser;
+ parser.parse(p_code, p_base_path, false, "", true);
- if (p.get_completion_type() == GDScriptParser::COMPLETION_NONE)
+ if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) {
return ERR_CANT_RESOLVE;
+ }
GDScriptCompletionContext context;
-
- context._class = p.get_completion_class();
- context.block = p.get_completion_block();
- context.function = p.get_completion_function();
+ context._class = parser.get_completion_class();
+ context.function = parser.get_completion_function();
+ context.block = parser.get_completion_block();
+ context.line = parser.get_completion_line();
context.base = p_owner;
context.base_path = p_base_path;
@@ -2675,171 +3187,68 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- bool isfunction = false;
+ bool is_function = false;
- switch (p.get_completion_type()) {
-
- case GDScriptParser::COMPLETION_GET_NODE:
- case GDScriptParser::COMPLETION_NONE: {
- } break;
+ switch (parser.get_completion_type()) {
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
-
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = Variant::get_type_name(p.get_completion_built_in_constant());
+ r_result.class_name = Variant::get_type_name(parser.get_completion_built_in_constant());
r_result.class_member = p_symbol;
return OK;
-
} break;
+ case GDScriptParser::COMPLETION_PARENT_FUNCTION:
case GDScriptParser::COMPLETION_FUNCTION: {
-
- if (context._class && context._class->functions.size()) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->functions[i]->line;
- return OK;
- }
- }
- }
-
- Ref<GDScript> parent = _get_parent_class(context);
- while (parent.is_valid()) {
- int line = parent->get_member_line(p_symbol);
- if (line >= 0) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = line;
- r_result.script = parent;
- return OK;
- }
-
- parent = parent->get_base();
- }
-
- GDScriptCompletionIdentifier identifier = _get_native_class(context);
- print_line("identifier: " + String(identifier.obj_type));
-
- if (ClassDB::has_method(identifier.obj_type, p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = identifier.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- } break;
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_IDENTIFIER: {
- //check if a function
- if (p.get_completion_identifier_is_function()) {
- if (context._class && context._class->functions.size()) {
- for (int i = 0; i < context._class->functions.size(); i++) {
- if (context._class->functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->functions[i]->line;
- return OK;
- }
- }
- }
-
- Ref<GDScript> parent = _get_parent_class(context);
- while (parent.is_valid()) {
- int line = parent->get_member_line(p_symbol);
- if (line >= 0) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = line;
- r_result.script = parent;
- return OK;
- }
-
- parent = parent->get_base();
- }
-
- GDScriptCompletionIdentifier identifier = _get_native_class(context);
-
- if (ClassDB::has_method(identifier.obj_type, p_symbol)) {
+ if (!is_function) {
+ is_function = parser.get_completion_identifier_is_function();
+ }
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = identifier.obj_type;
- r_result.class_member = p_symbol;
- return OK;
+ GDScriptParser::DataType base_type;
+ if (context._class) {
+ if (parser.get_completion_type() != GDScriptParser::COMPLETION_PARENT_FUNCTION) {
+ base_type.has_type = true;
+ base_type.kind = GDScriptParser::DataType::CLASS;
+ base_type.class_type = const_cast<GDScriptParser::ClassNode *>(context._class);
+ } else {
+ base_type = context._class->base_type;
}
} else {
+ break;
+ }
- GDScriptCompletionIdentifier gdi = _get_native_class(context);
- if (gdi.obj_type != StringName()) {
- bool valid;
- Variant::Type t = ClassDB::get_property_type(gdi.obj_type, p_symbol, &valid);
- if (t != Variant::NIL && valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = gdi.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
- }
-
+ if (!is_function && context.block) {
+ // Lookup local variables
const GDScriptParser::BlockNode *block = context.block;
- //search in blocks going up (local var?)
while (block) {
-
- for (int i = 0; i < block->statements.size(); i++) {
-
- if (block->statements[i]->line > p.get_completion_line())
- continue;
-
- if (block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
-
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(block->statements[i]);
-
- if (lv->assign && lv->name == p_symbol) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = block->statements[i]->line;
- return OK;
- }
- }
+ if (block->variables.has(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = block->variables[p_symbol]->line;
+ return OK;
}
block = block->parent_block;
}
+ }
- //guess from function arguments
- if (context.function && context.function->name != StringName()) {
-
- for (int i = 0; i < context.function->arguments.size(); i++) {
-
- if (context.function->arguments[i] == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context.function->line;
- return OK;
- }
- }
- }
-
- //guess in class constants
-
- for (int i = 0; i < context._class->constant_expressions.size(); i++) {
-
- if (context._class->constant_expressions[i].identifier == p_symbol) {
+ if (context.function && context.function->name != StringName()) {
+ // Lookup function arguments
+ for (int i = 0; i < context.function->arguments.size(); i++) {
+ if (context.function->arguments[i] == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->constant_expressions[i].expression->line;
+ r_result.location = context.function->line;
return OK;
}
}
+ }
- //guess in class variables
- if (!(context.function && context.function->_static)) {
-
- for (int i = 0; i < context._class->variables.size(); i++) {
-
- if (context._class->variables[i].identifier == p_symbol) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context._class->variables[i].line;
- return OK;
- }
- }
- }
+ if (_lookup_symbol_from_base(base_type, p_symbol, is_function, r_result) == OK) {
+ return OK;
+ }
- //guess in autoloads as singletons
+ if (!is_function) {
+ // Guess in autoloads as singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2856,8 +3265,8 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
String script = path.substr(1, path.length());
if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
+ // Not a script, try find the script anyway,
+ // may have some success
script = script.get_basename() + ".gd";
}
@@ -2872,7 +3281,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- //global
+ // Global
Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
if (classes.has(p_symbol)) {
Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]];
@@ -2918,152 +3327,31 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
}
-
- } break;
- case GDScriptParser::COMPLETION_PARENT_FUNCTION: {
-
} break;
- case GDScriptParser::COMPLETION_METHOD:
- isfunction = true;
+ case GDScriptParser::COMPLETION_METHOD: {
+ is_function = true;
+ } // fallthrough
case GDScriptParser::COMPLETION_INDEX: {
-
- const GDScriptParser::Node *node = p.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR)
+ const GDScriptParser::Node *node = parser.get_completion_node();
+ if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ break;
+ }
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], base)) {
break;
-
- GDScriptCompletionIdentifier t;
- if (_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], p.get_completion_line(), t)) {
-
- if (t.type == Variant::OBJECT && t.obj_type == "GDScriptNativeClass") {
- //native enum
- Ref<GDScriptNativeClass> gdn = t.value;
- if (gdn.is_valid()) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = gdn->get_name();
- r_result.class_member = p_symbol;
- return OK;
- }
- } else if (t.type == Variant::OBJECT && t.obj_type != StringName()) {
-
- Ref<GDScript> on_script;
-
- if (t.value.get_type()) {
- Object *obj = t.value;
-
- if (obj) {
-
- on_script = obj->get_script();
-
- if (on_script.is_valid()) {
- int loc = on_script->get_member_line(p_symbol);
- if (loc >= 0) {
- r_result.script = on_script;
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = loc;
- return OK;
- }
- }
- }
- }
-
- if (ClassDB::has_method(t.obj_type, p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true);
- if (enumName != StringName()) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
- r_result.class_name = t.obj_type;
- r_result.class_member = enumName;
- return OK;
- }
-
- bool success;
- ClassDB::get_integer_constant(t.obj_type, p_symbol, &success);
- if (success) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- ClassDB::get_property_type(t.obj_type, p_symbol, &success);
-
- if (success) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = t.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
-
- } else {
-
- Variant::CallError ce;
- Variant v = Variant::construct(t.type, NULL, 0, ce);
-
- bool valid;
- v.get_numeric_constant_value(t.type, p_symbol, &valid);
- if (valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
-
- //todo check all inputevent types for property
-
- v.get(p_symbol, &valid);
-
- if (valid) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
-
- if (v.has_method(p_symbol)) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = Variant::get_type_name(t.type);
- r_result.class_member = p_symbol;
- return OK;
- }
- }
}
- } break;
- case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
-
- return ERR_CANT_RESOLVE;
+ if (_lookup_symbol_from_base(base.type, p_symbol, is_function, r_result) == OK) {
+ return OK;
+ }
} break;
case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
+ GDScriptParser::DataType base_type = context._class->base_type;
- GDScriptCompletionIdentifier cid = _get_native_class(context);
-
- if (cid.obj_type != StringName()) {
- List<MethodInfo> vm;
- ClassDB::get_virtual_methods(cid.obj_type, &vm);
- for (List<MethodInfo>::Element *E = vm.front(); E; E = E->next()) {
-
- if (p_symbol == E->get().name) {
-
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = cid.obj_type;
- r_result.class_member = p_symbol;
- return OK;
- }
- }
+ if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) {
+ return OK;
}
} break;
- case GDScriptParser::COMPLETION_YIELD: {
-
- return ERR_CANT_RESOLVE;
-
- } break;
}
return ERR_CANT_RESOLVE;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 61130cb58f..d615b58b24 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -62,7 +62,7 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
}
#endif
//member indexing is O(1)
- return &p_instance->members[address];
+ return &p_instance->members.write[address];
} break;
case ADDR_TYPE_CLASS_CONSTANT: {
@@ -191,6 +191,7 @@ static String _get_var_type(const Variant *p_type) {
static const void *switch_table_ops[] = { \
&&OPCODE_OPERATOR, \
&&OPCODE_EXTENDS_TEST, \
+ &&OPCODE_IS_BUILTIN, \
&&OPCODE_SET, \
&&OPCODE_GET, \
&&OPCODE_SET_NAMED, \
@@ -200,6 +201,12 @@ static String _get_var_type(const Variant *p_type) {
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
@@ -318,10 +325,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (_stack_size) {
stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++)
- memnew_placement(&stack[i], Variant(*p_args[i]));
- for (int i = p_argcount; i < _stack_size; i++)
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i], Variant(arg));
+ } else {
+ memnew_placement(&stack[i], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
+ }
} else {
stack = NULL;
}
@@ -512,6 +537,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_IS_BUILTIN) {
+
+ CHECK_SPACE(4);
+
+ GET_VARIANT_PTR(value, 1);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
+ GET_VARIANT_PTR(dst, 3);
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ *dst = value->get_type() == var_type;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_SET) {
CHECK_SPACE(3);
@@ -709,6 +749,215 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != var_type) {
+ if (Variant::can_convert_strict(src->get_type(), var_type)) {
+ Variant::CallError ce;
+ *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce);
+ } else {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ OPCODE_BREAK;
+ }
+ } else {
+#endif // DEBUG_ENABLED
+ *dst = *src;
+#ifdef DEBUG_ENABLED
+ }
+#endif // DEBUG_ENABLED
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+#ifdef DEBUG_ENABLED
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+ if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
+ "' to a variable of type '" + nc->get_name() + "'.";
+ OPCODE_BREAK;
+ }
+#endif // DEBUG_ENABLED
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(type, 1);
+ GET_VARIANT_PTR(dst, 2);
+ GET_VARIANT_PTR(src, 3);
+
+#ifdef DEBUG_ENABLED
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+ if (!scr_inst) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+ err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+
+ CHECK_SPACE(4);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+
+ Variant::CallError err;
+ *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Variant::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Invalid cast: can't convert a non-object value to an object type.";
+ OPCODE_BREAK;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+
+ CHECK_SPACE(4);
+ GET_VARIANT_PTR(to_type, 1);
+ GET_VARIANT_PTR(src, 2);
+ GET_VARIANT_PTR(dst, 3);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+#ifdef DEBUG_ENABLED
+ if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
+ err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
+
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_CONSTRUCT) {
CHECK_SPACE(2);
@@ -1001,7 +1250,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.stack.resize(alloca_size);
//copy variant stack
for (int i = 0; i < _stack_size; i++) {
- memnew_placement(&gdfs->state.stack[sizeof(Variant) * i], Variant(stack[i]));
+ memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
}
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
@@ -1362,7 +1611,7 @@ StringName GDScriptFunction::get_global_name(int p_idx) const {
int GDScriptFunction::get_default_argument_count() const {
- return default_arguments.size();
+ return _default_arg_count;
}
int GDScriptFunction::get_default_argument_addr(int p_idx) const {
@@ -1370,6 +1619,15 @@ int GDScriptFunction::get_default_argument_addr(int p_idx) const {
return default_arguments[p_idx];
}
+GDScriptDataType GDScriptFunction::get_return_type() const {
+ return return_type;
+}
+
+GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, argument_types.size(), GDScriptDataType());
+ return argument_types[p_idx];
+}
+
StringName GDScriptFunction::get_name() const {
return name;
@@ -1552,7 +1810,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1560,10 +1818,10 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
@@ -1614,7 +1872,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
- gdfs->previous_state = Ref<GDScriptFunctionState>(this);
+ gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
}
}
@@ -1622,10 +1880,10 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
state.result = Variant();
if (completed) {
- GDScriptFunctionState *state = this;
- while (state != NULL) {
- state->emit_signal("completed", ret);
- state = *(state->previous_state);
+ if (first_state.is_valid()) {
+ first_state->emit_signal("completed", ret);
+ } else {
+ emit_signal("completed", ret);
}
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 836325f0fe..633cca35d3 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -42,11 +42,101 @@
class GDScriptInstance;
class GDScript;
+struct GDScriptDataType {
+ bool has_type;
+ enum {
+ BUILTIN,
+ NATIVE,
+ SCRIPT,
+ GDSCRIPT
+ } kind;
+ Variant::Type builtin_type;
+ StringName native_type;
+ Ref<Script> script_type;
+
+ bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
+ if (!has_type) return true; // Can't type check
+
+ switch (kind) {
+ case BUILTIN: {
+ Variant::Type var_type = p_variant.get_type();
+ bool valid = builtin_type == var_type;
+ if (!valid && p_allow_implicit_conversion) {
+ valid = Variant::can_convert_strict(var_type, builtin_type);
+ }
+ return valid;
+ } break;
+ case NATIVE: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
+ return false;
+ }
+ return true;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ if (p_variant.get_type() == Variant::NIL) {
+ return true;
+ }
+ if (p_variant.get_type() != Variant::OBJECT) {
+ return false;
+ }
+ Object *obj = p_variant.operator Object *();
+ Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : NULL;
+ bool valid = false;
+ while (base.is_valid()) {
+ if (base == script_type) {
+ valid = true;
+ break;
+ }
+ base = base->get_base_script();
+ }
+ return valid;
+ } break;
+ }
+ return false;
+ }
+
+ operator PropertyInfo() const {
+ PropertyInfo info;
+ if (has_type) {
+ switch (kind) {
+ case BUILTIN: {
+ info.type = builtin_type;
+ } break;
+ case NATIVE: {
+ info.type = Variant::OBJECT;
+ info.class_name = native_type;
+ } break;
+ case SCRIPT:
+ case GDSCRIPT: {
+ info.type = Variant::OBJECT;
+ info.class_name = script_type->get_instance_base_type();
+ } break;
+ }
+ } else {
+ info.type = Variant::NIL;
+ info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ return info;
+ }
+
+ GDScriptDataType() :
+ has_type(false) {}
+};
+
class GDScriptFunction {
public:
enum Opcode {
OPCODE_OPERATOR,
OPCODE_EXTENDS_TEST,
+ OPCODE_IS_BUILTIN,
OPCODE_SET,
OPCODE_GET,
OPCODE_SET_NAMED,
@@ -56,6 +146,12 @@ public:
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
+ OPCODE_ASSIGN_TYPED_BUILTIN,
+ OPCODE_ASSIGN_TYPED_NATIVE,
+ OPCODE_ASSIGN_TYPED_SCRIPT,
+ OPCODE_CAST_TO_BUILTIN,
+ OPCODE_CAST_TO_NATIVE,
+ OPCODE_CAST_TO_SCRIPT,
OPCODE_CONSTRUCT, //only for basic types!!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
@@ -139,6 +235,8 @@ private:
#endif
Vector<int> default_arguments;
Vector<int> code;
+ Vector<GDScriptDataType> argument_types;
+ GDScriptDataType return_type;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
@@ -199,6 +297,8 @@ public:
int get_max_stack_size() const;
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
+ GDScriptDataType get_return_type() const;
+ GDScriptDataType get_argument_type(int p_idx) const;
GDScript *get_script() const { return _script; }
StringName get_source() const { return source; }
@@ -234,7 +334,7 @@ class GDScriptFunctionState : public Reference {
GDScriptFunction *function;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
- Ref<GDScriptFunctionState> previous_state;
+ Ref<GDScriptFunctionState> first_state;
protected:
static void _bind_methods();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index a88ba477c6..03e7740887 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -105,6 +105,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"prints",
"printerr",
"printraw",
+ "print_debug",
"var2str",
"str2var",
"var2bytes",
@@ -120,6 +121,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"Color8",
"ColorN",
"print_stack",
+ "get_stack",
"instance_from_id",
"len",
"is_instance_valid",
@@ -640,7 +642,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -655,7 +656,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -670,7 +670,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_line(str);
r_ret = Variant();
@@ -684,7 +683,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
print_error(str);
r_ret = Variant();
@@ -696,11 +694,26 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
str += p_args[i]->operator String();
}
- //str+="\n";
OS::get_singleton()->print("%s", str.utf8().get_data());
r_ret = Variant();
} break;
+ case TEXT_PRINT_DEBUG: {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+
+ str += p_args[i]->operator String();
+ }
+
+ 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) + "'";
+ }
+
+ print_line(str);
+ r_ret = Variant();
+ } break;
case VAR_TO_STR: {
VALIDATE_ARG_COUNT(1);
String vars;
@@ -1079,7 +1092,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
if (d.has(E->key())) {
- ins->members[E->get().index] = d[E->key()];
+ ins->members.write[E->get().index] = d[E->key()];
}
}
@@ -1213,6 +1226,22 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
};
} break;
+ case GET_STACK: {
+ VALIDATE_ARG_COUNT(0);
+
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ Array ret;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ r_ret = ret;
+ } break;
+
case INSTANCE_FROM_ID: {
VALIDATE_ARG_COUNT(1);
@@ -1377,7 +1406,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) {
MethodInfo GDScriptFunctions::get_info(Function p_func) {
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
//using a switch, so the compiler generates a jumptable
switch (p_func) {
@@ -1628,7 +1657,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "WeakRef";
+ mi.return_val.class_name = "WeakRef";
return mi;
@@ -1637,19 +1666,20 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "FuncRef";
+ mi.return_val.class_name = "FuncRef";
return mi;
} break;
case TYPE_CONVERT: {
- MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::INT, "type"));
- mi.return_val.type = Variant::OBJECT;
+ MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
+ mi.return_val.type = Variant::NIL;
+ mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
return mi;
} break;
case TYPE_OF: {
- MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what"));
+ MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
@@ -1716,8 +1746,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TEXT_PRINT_DEBUG: {
+
+ MethodInfo mi("print_debug");
+ mi.return_val.type = Variant::NIL;
+ mi.flags |= METHOD_FLAG_VARARG;
+ return mi;
+
+ } break;
case VAR_TO_STR: {
- MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var"));
+ 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;
@@ -1730,7 +1768,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
- MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var"));
+ 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;
@@ -1753,7 +1791,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
mi.return_val.type = Variant::OBJECT;
- mi.return_val.name = "Resource";
+ mi.return_val.class_name = "Resource";
return mi;
} break;
case INST2DICT: {
@@ -1783,13 +1821,13 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break;
case TO_JSON: {
- MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("to_json", 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 HASH: {
- MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;
@@ -1813,6 +1851,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::NIL;
return mi;
} break;
+ case GET_STACK: {
+ MethodInfo mi("get_stack");
+ mi.return_val.type = Variant::ARRAY;
+ return mi;
+ } break;
case INSTANCE_FROM_ID: {
MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
@@ -1820,7 +1863,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case LEN: {
- MethodInfo mi("len", PropertyInfo(Variant::NIL, "var"));
+ MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index c4731d17a4..a29f06e839 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -96,6 +96,7 @@ public:
TEXT_PRINT_SPACED,
TEXT_PRINTERR,
TEXT_PRINTRAW,
+ TEXT_PRINT_DEBUG,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
@@ -111,6 +112,7 @@ public:
COLOR8,
COLORN,
PRINT_STACK,
+ GET_STACK,
INSTANCE_FROM_ID,
LEN,
IS_INSTANCE_VALID,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index fdb92a68a9..7502f09d8a 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,10 +30,15 @@
#include "gdscript_parser.h"
+#include "core/core_string_names.h"
+#include "core/engine.h"
+#include "core/project_settings.h"
+#include "core/reference.h"
#include "gdscript.h"
#include "io/resource_loader.h"
#include "os/file_access.h"
#include "print_string.h"
+#include "project_settings.h"
#include "script_language.h"
template <class T>
@@ -52,6 +57,8 @@ T *GDScriptParser::alloc_node() {
return t;
}
+static String _find_function_name(const GDScriptParser::OperatorNode *p_call);
+
bool GDScriptParser::_end_statement() {
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
@@ -138,8 +145,9 @@ bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bo
}
Node *arg = _parse_expression(p_parent, p_static);
- if (!arg)
+ if (!arg) {
return false;
+ }
p_args.push_back(arg);
@@ -263,6 +271,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
bool need_identifier = true;
bool done = false;
+ int line = tokenizer->get_token_line();
while (!done) {
@@ -330,16 +339,19 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_CALL;
-
+ op->line = line;
op->arguments.push_back(alloc_node<SelfNode>());
+ op->arguments[0]->line = line;
IdentifierNode *funcname = alloc_node<IdentifierNode>();
funcname->name = "get_node";
-
+ funcname->line = line;
op->arguments.push_back(funcname);
ConstantNode *nodepath = alloc_node<ConstantNode>();
nodepath->value = NodePath(StringName(path));
+ nodepath->datatype = _type_from_variant(nodepath->value);
+ nodepath->line = line;
op->arguments.push_back(nodepath);
expr = op;
@@ -353,6 +365,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = tokenizer->get_token_constant();
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_PI) {
@@ -360,6 +373,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_PI;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_TAU) {
@@ -367,6 +381,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_TAU;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_INF) {
@@ -374,6 +389,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_INF;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_NAN) {
@@ -381,6 +397,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//constant defined by tokenizer
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = Math_NAN;
+ constant->datatype = _type_from_variant(constant->value);
tokenizer->advance();
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_PRELOAD) {
@@ -419,15 +436,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (subexpr->type == Node::TYPE_IDENTIFIER) {
IdentifierNode *in = static_cast<IdentifierNode *>(subexpr);
- Vector<ClassNode::Constant> ce = current_class->constant_expressions;
// Try to find the constant expression by the identifier
- for (int i = 0; i < ce.size(); ++i) {
- if (ce[i].identifier == in->name) {
- if (ce[i].expression->type == Node::TYPE_CONSTANT) {
- cn = static_cast<ConstantNode *>(ce[i].expression);
- found_constant = true;
- }
+ if (current_class->constant_expressions.has(in->name)) {
+ Node *cn_exp = current_class->constant_expressions[in->name].expression;
+ if (cn_exp->type == Node::TYPE_CONSTANT) {
+ cn = static_cast<ConstantNode *>(cn_exp);
+ found_constant = true;
}
}
}
@@ -480,15 +495,28 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
_set_error("Expected ')' after 'preload' path");
return NULL;
}
+
+ Ref<GDScript> gds = res;
+ if (gds.is_valid() && !gds->is_valid()) {
+ _set_error("Could not fully preload the script, possible cyclic reference or compilation error.");
+ return NULL;
+ }
+
tokenizer->advance();
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = res;
+ constant->datatype = _type_from_variant(constant->value);
expr = constant;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_YIELD) {
- //constant defined by tokenizer
+ if (!current_function) {
+ _set_error("yield() can only be used inside function blocks.");
+ return NULL;
+ }
+
+ current_function->has_yield = true;
tokenizer->advance();
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
@@ -582,7 +610,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
_set_error("Built-in type constant or static function expected after '.'");
return NULL;
}
- if (!Variant::has_numeric_constant(bi_type, identifier)) {
+ if (!Variant::has_constant(bi_type, identifier)) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN &&
Variant::is_method_const(bi_type, identifier) &&
@@ -617,7 +645,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} else {
ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = Variant::get_numeric_constant_value(bi_type, identifier);
+ cn->value = Variant::get_constant_value(bi_type, identifier);
+ cn->datatype = _type_from_variant(cn->value);
expr = cn;
}
@@ -695,24 +724,65 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
const ClassNode *cln = current_class;
bool bfn = false;
StringName identifier;
+ int id_line = tokenizer->get_token_line();
if (_get_completable_identifier(COMPLETION_IDENTIFIER, identifier)) {
}
- if (p_parsing_constant) {
- for (int i = 0; i < cln->constant_expressions.size(); ++i) {
-
- if (cln->constant_expressions[i].identifier == identifier) {
+ BlockNode *b = current_block;
+ while (!bfn && b) {
+ if (b->variables.has(identifier)) {
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ LocalVarNode *lv = b->variables[identifier];
+ id->name = identifier;
+ id->declared_block = b;
+ id->line = id_line;
+ expr = id;
+ bfn = true;
- expr = cln->constant_expressions[i].expression;
- bfn = true;
- break;
+#ifdef DEBUG_ENABLED
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SUB: {
+ if (lv->assignments == 0) {
+ if (!lv->datatype.has_type) {
+ _set_error("Using assignment with operation on a variable that was never assigned.");
+ return NULL;
+ }
+ _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String());
+ }
+ } // fallthrough
+ case GDScriptTokenizer::TK_OP_ASSIGN: {
+ lv->assignments += 1;
+ lv->usages--; // Assignment is not really usage
+ } break;
+ default: {
+ lv->usages++;
+ }
}
+#endif // DEBUG_ENABLED
+ break;
}
+ b = b->parent_block;
+ }
- if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ if (!bfn && p_parsing_constant) {
+ if (cln->constant_expressions.has(identifier)) {
+ expr = cln->constant_expressions[identifier].expression;
+ bfn = true;
+ } else if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
//check from constants
ConstantNode *constant = alloc_node<ConstantNode>();
constant->value = GDScriptLanguage::get_singleton()->get_global_array()[GDScriptLanguage::get_singleton()->get_global_map()[identifier]];
+ constant->datatype = _type_from_variant(constant->value);
+ constant->line = id_line;
expr = constant;
bfn = true;
}
@@ -727,8 +797,35 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (!bfn) {
+#ifdef DEBUG_ENABLED
+ if (current_function) {
+ int arg_idx = current_function->arguments.find(identifier);
+ if (arg_idx != -1) {
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SUB:
+ case GDScriptTokenizer::TK_OP_ASSIGN: {
+ // Assignment is not really usage
+ current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1;
+ } break;
+ default: {
+ current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1;
+ }
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = identifier;
+ id->line = id_line;
expr = id;
}
@@ -764,6 +861,20 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(subexpr);
expr=op;*/
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_IS && tokenizer->get_token(1) == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
+ // 'is' operator with built-in type
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op = OperatorNode::OP_IS_BUILTIN;
+ op->arguments.push_back(expr);
+
+ tokenizer->advance();
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype = tokenizer->get_token_type();
+ op->arguments.push_back(tn);
+ tokenizer->advance();
+
+ expr = op;
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) {
// array
tokenizer->advance();
@@ -902,6 +1013,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//lua style identifier, easier to write
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = tokenizer->get_token_literal();
+ cn->datatype = _type_from_variant(cn->value);
key = cn;
tokenizer->advance(2);
expecting = DICT_EXPECT_VALUE;
@@ -941,7 +1053,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = dict;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR) && tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR)) {
// We check with is_token_literal, as this allows us to use match/sync/etc. as a name
// parent call
@@ -953,26 +1065,38 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(self);
forbidden for now */
StringName identifier;
- if (_get_completable_identifier(COMPLETION_PARENT_FUNCTION, identifier)) {
- //indexing stuff
- }
+ bool is_completion = _get_completable_identifier(COMPLETION_PARENT_FUNCTION, identifier) && for_completion;
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = identifier;
op->arguments.push_back(id);
- tokenizer->advance(1);
- if (!_parse_arguments(op, op->arguments, p_static))
- return NULL;
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
+ if (!is_completion) {
+ _set_error("Expected '(' for parent function call.");
+ return NULL;
+ }
+ } else {
+ tokenizer->advance();
+ if (!_parse_arguments(op, op->arguments, p_static)) {
+ return NULL;
+ }
+ }
expr = op;
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && expression.size() > 0 && expression[expression.size() - 1].is_op && expression[expression.size() - 1].op == OperatorNode::OP_IS) {
+ Expression e = expression[expression.size() - 1];
+ e.op = OperatorNode::OP_IS_BUILTIN;
+ expression.write[expression.size() - 1] = e;
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype = tokenizer->get_token_type();
+ expr = tn;
+ tokenizer->advance();
} else {
//find list [ or find dictionary {
-
- //print_line("found bug?");
-
_set_error("Error parsing expression, misplaced: " + String(tokenizer->get_token_name(tokenizer->get_token())));
return NULL; //nothing
}
@@ -1087,6 +1211,26 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
break;
}
+ /*****************/
+ /* Parse Casting */
+ /*****************/
+
+ bool has_casting = expr->type == Node::TYPE_CAST;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_AS) {
+ if (has_casting) {
+ _set_error("Unexpected 'as'.");
+ return NULL;
+ }
+ CastNode *cn = alloc_node<CastNode>();
+ if (!_parse_type(cn->cast_type)) {
+ _set_error("Expected type after 'as'.");
+ return NULL;
+ }
+ has_casting = true;
+ cn->source_node = expr;
+ expr = cn;
+ }
+
/******************/
/* Parse Operator */
/******************/
@@ -1110,7 +1254,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
//assign, if allowed is only allowed on the first operator
#define _VALIDATE_ASSIGN \
- if (!p_allow_assign) { \
+ if (!p_allow_assign || has_casting) { \
_set_error("Unexpected assign."); \
return NULL; \
} \
@@ -1208,6 +1352,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
switch (expression[i].op) {
case OperatorNode::OP_IS:
+ case OperatorNode::OP_IS_BUILTIN:
priority = -1;
break; //before anything
@@ -1325,8 +1470,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->op = expression[i].op;
op->arguments.push_back(expression[i + 1].node);
op->line = op_line; //line might have been changed from a \n
- expression[i].is_op = false;
- expression[i].node = op;
+ expression.write[i].is_op = false;
+ expression.write[i].node = op;
expression.remove(i + 1);
}
@@ -1338,11 +1483,11 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (next_op >= (expression.size() - 2) || expression[next_op + 2].op != OperatorNode::OP_TERNARY_ELSE) {
_set_error("Expected else after ternary if.");
- ERR_FAIL_V(NULL);
+ return NULL;
}
if (next_op >= (expression.size() - 3)) {
_set_error("Expected value after ternary else.");
- ERR_FAIL_V(NULL);
+ return NULL;
}
OperatorNode *op = alloc_node<OperatorNode>();
@@ -1380,7 +1525,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(expression[next_op + 3].node); //expression after next goes as when-false
//replace all 3 nodes by this operator and make it an expression
- expression[next_op - 1].node = op;
+ expression.write[next_op - 1].node = op;
expression.remove(next_op);
expression.remove(next_op);
expression.remove(next_op);
@@ -1416,7 +1561,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
op->arguments.push_back(expression[next_op + 1].node); //next expression goes as right
//replace all 3 nodes by this operator and make it an expression
- expression[next_op - 1].node = op;
+ expression.write[next_op - 1].node = op;
expression.remove(next_op);
expression.remove(next_op);
}
@@ -1440,7 +1585,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
for (int i = 0; i < an->elements.size(); i++) {
- an->elements[i] = _reduce_expression(an->elements[i], p_to_const);
+ an->elements.write[i] = _reduce_expression(an->elements[i], p_to_const);
if (an->elements[i]->type != Node::TYPE_CONSTANT)
all_constants = false;
}
@@ -1450,13 +1595,13 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
Array arr;
- //print_line("mk array "+itos(!p_to_const));
arr.resize(an->elements.size());
for (int i = 0; i < an->elements.size(); i++) {
ConstantNode *acn = static_cast<ConstantNode *>(an->elements[i]);
arr[i] = acn->value;
}
cn->value = arr;
+ cn->datatype = _type_from_variant(cn->value);
return cn;
}
@@ -1470,10 +1615,10 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
for (int i = 0; i < dn->elements.size(); i++) {
- dn->elements[i].key = _reduce_expression(dn->elements[i].key, p_to_const);
+ dn->elements.write[i].key = _reduce_expression(dn->elements[i].key, p_to_const);
if (dn->elements[i].key->type != Node::TYPE_CONSTANT)
all_constants = false;
- dn->elements[i].value = _reduce_expression(dn->elements[i].value, p_to_const);
+ dn->elements.write[i].value = _reduce_expression(dn->elements[i].value, p_to_const);
if (dn->elements[i].value->type != Node::TYPE_CONSTANT)
all_constants = false;
}
@@ -1490,6 +1635,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
dict[key_c->value] = value_c->value;
}
cn->value = dict;
+ cn->datatype = _type_from_variant(cn->value);
return cn;
}
@@ -1505,7 +1651,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
for (int i = 0; i < op->arguments.size(); i++) {
- op->arguments[i] = _reduce_expression(op->arguments[i], p_to_const);
+ op->arguments.write[i] = _reduce_expression(op->arguments[i], p_to_const);
if (op->arguments[i]->type != Node::TYPE_CONSTANT) {
all_constants = false;
last_not_constant = i;
@@ -1533,7 +1679,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
for (int i = 0; i < ptrs.size(); i++) {
ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i + 1]);
- ptrs[i] = &cn->value;
+ ptrs.write[i] = &cn->value;
}
vptr = (const Variant **)&ptrs[0];
@@ -1591,6 +1737,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
} else if (op->arguments[0]->type == Node::TYPE_BUILT_IN_FUNCTION && last_not_constant == 0) {
@@ -1620,24 +1767,9 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
-
- } /*else if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
-
- ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
- IdentifierNode *ib = static_cast<IdentifierNode*>(op->arguments[1]);
-
- bool valid;
- Variant v = ca->value.get_named(ib->name,&valid);
- if (!valid) {
- _set_error("invalid index '"+String(ib->name)+"' in constant expression");
- return op;
- }
-
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value=v;
- return cn;
- }*/
+ }
return op;
@@ -1658,13 +1790,14 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = v;
+ cn->datatype = _type_from_variant(v);
return cn;
}
return op;
}
- //validate assignment (don't assign to cosntant expression
+ //validate assignment (don't assign to constant expression
switch (op->op) {
case OperatorNode::OP_ASSIGN:
@@ -1711,6 +1844,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
} \
ConstantNode *cn = alloc_node<ConstantNode>(); \
cn->value = res; \
+ cn->datatype = _type_from_variant(res); \
return cn;
#define _REDUCE_BINARY(m_vop) \
@@ -1724,6 +1858,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
} \
ConstantNode *cn = alloc_node<ConstantNode>(); \
cn->value = res; \
+ cn->datatype = _type_from_variant(res); \
return cn;
switch (op->op) {
@@ -1799,6 +1934,13 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to
case OperatorNode::OP_BIT_XOR: {
_REDUCE_BINARY(Variant::OP_BIT_XOR);
} break;
+ case OperatorNode::OP_TERNARY_IF: {
+ if (static_cast<ConstantNode *>(op->arguments[0])->value.booleanize()) {
+ return op->arguments[1];
+ } else {
+ return op->arguments[2];
+ }
+ } break;
default: { ERR_FAIL_V(op); }
}
@@ -1903,8 +2045,25 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) {
// bind
case GDScriptTokenizer::TK_PR_VAR: {
tokenizer->advance();
+ if (!tokenizer->is_token_literal()) {
+ _set_error("Expected identifier for binding variable name.");
+ return NULL;
+ }
pattern->pt_type = GDScriptParser::PatternNode::PT_BIND;
pattern->bind = tokenizer->get_token_identifier();
+ // Check if variable name is already used
+ BlockNode *bl = current_block;
+ while (bl) {
+ if (bl->variables.has(pattern->bind)) {
+ _set_error("Binding name of '" + pattern->bind.operator String() + "' is already declared in this scope.");
+ return NULL;
+ }
+ bl = bl->parent_block;
+ }
+ // Create local variable for proper identifier detection later
+ LocalVarNode *lv = alloc_node<LocalVarNode>();
+ lv->name = pattern->bind;
+ current_block->variables.insert(lv->name, lv);
tokenizer->advance();
} break;
// dictionary
@@ -2038,18 +2197,34 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran
}
PatternBranchNode *branch = alloc_node<PatternBranchNode>();
+ branch->body = alloc_node<BlockNode>();
+ branch->body->parent_block = p_block;
+ p_block->sub_blocks.push_back(branch->body);
+ current_block = branch->body;
branch->patterns.push_back(_parse_pattern(p_static));
if (!branch->patterns[0]) {
return;
}
+ bool has_binding = branch->patterns[0]->pt_type == PatternNode::PT_BIND;
+ bool catch_all = has_binding || branch->patterns[0]->pt_type == PatternNode::PT_WILDCARD;
+
while (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
tokenizer->advance();
branch->patterns.push_back(_parse_pattern(p_static));
if (!branch->patterns[branch->patterns.size() - 1]) {
return;
}
+
+ PatternNode::PatternType pt = branch->patterns[branch->patterns.size() - 1]->pt_type;
+
+ if (pt == PatternNode::PT_BIND) {
+ _set_error("Cannot use bindings with multipattern.");
+ return;
+ }
+
+ catch_all = catch_all || pt == PatternNode::PT_WILDCARD;
}
if (!_enter_indent_block()) {
@@ -2057,54 +2232,76 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran
return;
}
- branch->body = alloc_node<BlockNode>();
- branch->body->parent_block = p_block;
- p_block->sub_blocks.push_back(branch->body);
- current_block = branch->body;
-
_parse_block(branch->body, p_static);
current_block = p_block;
+ if (catch_all && branch->body->has_return) {
+ p_block->has_return = true;
+ }
+
p_branches.push_back(branch);
}
}
void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings) {
+
+ const DataType &to_match_type = p_node_to_match->get_datatype();
+
switch (p_pattern->pt_type) {
case PatternNode::PT_CONSTANT: {
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
+ DataType pattern_type = _reduce_node_type(p_pattern->constant);
+ if (error_set) {
+ return;
+ }
+
+ OperatorNode *type_comp = NULL;
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
+ // static type check if possible
+ if (pattern_type.has_type && to_match_type.has_type) {
+ if (!_is_type_compatible(to_match_type, pattern_type) && !_is_type_compatible(pattern_type, to_match_type)) {
+ _set_error("Pattern type (" + pattern_type.to_string() + ") is not compatible with the type of the value to match (" + to_match_type.to_string() + ").",
+ p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
- OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
- typeof_pattern_value->op = OperatorNode::OP_CALL;
- typeof_pattern_value->arguments.push_back(typeof_node);
- typeof_pattern_value->arguments.push_back(p_pattern->constant);
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_pattern_value);
+ OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
+ typeof_pattern_value->op = OperatorNode::OP_CALL;
+ typeof_pattern_value->arguments.push_back(typeof_node);
+ typeof_pattern_value->arguments.push_back(p_pattern->constant);
- // comare the actual values
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_pattern_value);
+ }
+
+ // compare the actual values
OperatorNode *value_comp = alloc_node<OperatorNode>();
value_comp->op = OperatorNode::OP_EQUAL;
value_comp->arguments.push_back(p_pattern->constant);
value_comp->arguments.push_back(p_node_to_match);
- OperatorNode *comparison = alloc_node<OperatorNode>();
- comparison->op = OperatorNode::OP_AND;
- comparison->arguments.push_back(type_comp);
- comparison->arguments.push_back(value_comp);
+ if (type_comp) {
+ OperatorNode *full_comparison = alloc_node<OperatorNode>();
+ full_comparison->op = OperatorNode::OP_AND;
+ full_comparison->arguments.push_back(type_comp);
+ full_comparison->arguments.push_back(value_comp);
- p_resulting_node = comparison;
+ p_resulting_node = full_comparison;
+ } else {
+ p_resulting_node = value_comp;
+ }
} break;
case PatternNode::PT_BIND: {
@@ -2129,22 +2326,32 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
// typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
{
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
- typeof_array->name = "TYPE_ARRAY";
-
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_array);
+ OperatorNode *type_comp = NULL;
+ // static type check if possible
+ if (to_match_type.has_type) {
+ // must be an array
+ if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::ARRAY) {
+ _set_error("Cannot match an array pattern with a non-array expression.", p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
+ typeof_array->name = "TYPE_ARRAY";
+
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_array);
+ }
// size
ConstantNode *length = alloc_node<ConstantNode>();
@@ -2163,12 +2370,16 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
length_comparison->arguments.push_back(call);
length_comparison->arguments.push_back(length);
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
+ if (type_comp) {
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
- p_resulting_node = type_and_length_comparison;
+ p_resulting_node = type_and_length_comparison;
+ } else {
+ p_resulting_node = length_comparison;
+ }
}
for (int i = 0; i < p_pattern->array.size(); i++) {
@@ -2208,22 +2419,32 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
// typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
{
- // typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
- typeof_dictionary->name = "TYPE_DICTIONARY";
-
- OperatorNode *type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_dictionary);
+ OperatorNode *type_comp = NULL;
+ // static type check if possible
+ if (to_match_type.has_type) {
+ // must be an dictionary
+ if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::DICTIONARY) {
+ _set_error("Cannot match an dictionary pattern with a non-dictionary expression.", p_pattern->line);
+ return;
+ }
+ } else {
+ // runtime typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDScriptFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
+ typeof_dictionary->name = "TYPE_DICTIONARY";
+
+ type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_dictionary);
+ }
// size
ConstantNode *length = alloc_node<ConstantNode>();
@@ -2242,12 +2463,16 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
length_comparison->arguments.push_back(call);
length_comparison->arguments.push_back(length);
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
+ if (type_comp) {
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
- p_resulting_node = type_and_length_comparison;
+ p_resulting_node = type_and_length_comparison;
+ } else {
+ p_resulting_node = length_comparison;
+ }
}
for (Map<ConstantNode *, PatternNode *>::Element *e = p_pattern->dictionary.front(); e; e = e->next()) {
@@ -2308,9 +2533,20 @@ void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_m
}
}
-void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement) {
+void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) {
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = "#match_value";
+ id->line = p_match_statement->line;
+ id->datatype = _reduce_node_type(p_match_statement->val_to_match);
+ if (id->datatype.has_type) {
+ _mark_line_as_safe(id->line);
+ } else {
+ _mark_line_as_unsafe(id->line);
+ }
+
+ if (error_set) {
+ return;
+ }
for (int i = 0; i < p_match_statement->branches.size(); i++) {
@@ -2323,11 +2559,16 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
for (int j = 0; j < branch->patterns.size(); j++) {
PatternNode *pattern = branch->patterns[j];
+ _mark_line_as_safe(pattern->line);
Map<StringName, Node *> bindings;
- Node *resulting_node;
+ Node *resulting_node = NULL;
_generate_pattern(pattern, id, resulting_node, bindings);
+ if (!resulting_node) {
+ return;
+ }
+
if (!binding.empty() && !bindings.empty()) {
_set_error("Multipatterns can't contain bindings");
return;
@@ -2335,6 +2576,14 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
binding = bindings;
}
+ // Result is always a boolean
+ DataType resulting_node_type;
+ resulting_node_type.has_type = true;
+ resulting_node_type.is_constant = true;
+ resulting_node_type.kind = DataType::BUILTIN;
+ resulting_node_type.builtin_type = Variant::BOOL;
+ resulting_node->set_datatype(resulting_node_type);
+
if (compiled_branch.compiled_pattern) {
OperatorNode *or_node = alloc_node<OperatorNode>();
or_node->op = OperatorNode::OP_OR;
@@ -2350,12 +2599,19 @@ void GDScriptParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_
// prepare the body ...hehe
for (Map<StringName, Node *>::Element *e = binding.front(); e; e = e->next()) {
- LocalVarNode *local_var = alloc_node<LocalVarNode>();
- local_var->name = e->key();
+ if (!branch->body->variables.has(e->key())) {
+ _set_error("Parser bug: missing pattern bind variable.", branch->line);
+ ERR_FAIL();
+ }
+
+ LocalVarNode *local_var = branch->body->variables[e->key()];
local_var->assign = e->value();
+ local_var->set_datatype(local_var->assign->get_datatype());
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = local_var->name;
+ id->declared_block = branch->body;
+ id->set_datatype(local_var->assign->get_datatype());
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_ASSIGN;
@@ -2412,8 +2668,23 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
pending_newline = -1;
}
+#ifdef DEBUG_ENABLED
+ switch (token) {
+ case GDScriptTokenizer::TK_EOF:
+ case GDScriptTokenizer::TK_ERROR:
+ case GDScriptTokenizer::TK_NEWLINE:
+ case GDScriptTokenizer::TK_CF_PASS: {
+ // will check later
+ } break;
+ default: {
+ if (p_block->has_return && !current_function->has_unreachable_code) {
+ _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String());
+ current_function->has_unreachable_code = true;
+ }
+ } break;
+ }
+#endif // DEBUG_ENABLED
switch (token) {
-
case GDScriptTokenizer::TK_EOF:
p_block->end_line = tokenizer->get_token_line();
case GDScriptTokenizer::TK_ERROR: {
@@ -2443,6 +2714,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
_set_error("Expected ';' or <NewLine>.");
return;
}
+ _mark_line_as_safe(tokenizer->get_token_line());
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
// Ignore semicolon after 'pass'
@@ -2453,6 +2725,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
//variale declaration and (eventual) initialization
tokenizer->advance();
+ int var_line = tokenizer->get_token_line();
if (!tokenizer->is_token_literal(0, true)) {
_set_error("Expected identifier for local variable name.");
@@ -2470,24 +2743,34 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
}
BlockNode *check_block = p_block;
while (check_block) {
- for (int i = 0; i < check_block->variables.size(); i++) {
- if (n == check_block->variables[i]) {
- _set_error("Variable '" + String(n) + "' already defined in the scope (at line: " + itos(check_block->variable_lines[i]) + ").");
- return;
- }
+ if (check_block->variables.has(n)) {
+ _set_error("Variable '" + String(n) + "' already defined in the scope (at line: " + itos(check_block->variables[n]->line) + ").");
+ return;
}
check_block = check_block->parent_block;
}
- int var_line = tokenizer->get_token_line();
-
//must know when the local variable is declared
LocalVarNode *lv = alloc_node<LocalVarNode>();
lv->name = n;
+ lv->line = var_line;
p_block->statements.push_back(lv);
Node *assigned = NULL;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ lv->datatype = DataType();
+#ifdef DEBUG_ENABLED
+ lv->datatype.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(lv->datatype)) {
+ _set_error("Expected type for variable.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
tokenizer->advance();
@@ -2499,26 +2782,39 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
- lv->assign = subexpr;
+ lv->assignments++;
assigned = subexpr;
} else {
ConstantNode *c = alloc_node<ConstantNode>();
- c->value = Variant();
+ if (lv->datatype.has_type && lv->datatype.kind == DataType::BUILTIN) {
+ Variant::CallError err;
+ c->value = Variant::construct(lv->datatype.builtin_type, NULL, 0, err);
+ } else {
+ c->value = Variant();
+ }
+ c->line = var_line;
assigned = c;
}
+ lv->assign = assigned;
//must be added later, to avoid self-referencing.
- p_block->variables.push_back(n); //line?
- p_block->variable_lines.push_back(var_line);
+ p_block->variables.insert(n, lv);
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = n;
+ id->declared_block = p_block;
+ id->line = var_line;
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_ASSIGN;
op->arguments.push_back(id);
op->arguments.push_back(assigned);
+ op->line = var_line;
p_block->statements.push_back(op);
+ lv->assign_op = op;
+ lv->assign = assigned;
+
+ lv->assign_op = op;
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
@@ -2563,6 +2859,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
p_block->statements.push_back(cf_if);
+ bool all_have_return = cf_if->body->has_return;
+ bool have_else = false;
+
while (true) {
while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline())
@@ -2619,6 +2918,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
if (error_set)
return;
+ all_have_return = all_have_return && cf_else->body->has_return;
+
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELSE) {
if (tab_level.back()->get() > indent_level) {
@@ -2642,12 +2943,19 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
if (error_set)
return;
+ all_have_return = all_have_return && cf_if->body_else->has_return;
+ have_else = true;
+
break; //after else, exit
} else
break;
}
+ cf_if->body->has_return = all_have_return;
+ // If there's no else block, path out of the if might not have a return
+ p_block->has_return = all_have_return && have_else;
+
} break;
case GDScriptTokenizer::TK_CF_WHILE: {
@@ -2680,6 +2988,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
current_block = p_block;
if (error_set)
return;
+ p_block->has_return = cf_while->body->has_return;
p_block->statements.push_back(cf_while);
} break;
case GDScriptTokenizer::TK_CF_FOR: {
@@ -2711,6 +3020,9 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
+ DataType iter_type;
+ iter_type.is_constant = true;
+
if (container->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(container);
@@ -2745,6 +3057,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
case 2: cn->value = Vector2(constants[0], constants[1]); break;
case 3: cn->value = Vector3(constants[0], constants[1], constants[2]); break;
}
+ cn->datatype = _type_from_variant(cn->value);
container = cn;
} else {
OperatorNode *on = alloc_node<OperatorNode>();
@@ -2766,6 +3079,10 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
container = on;
}
}
+
+ iter_type.has_type = true;
+ iter_type.kind = DataType::BUILTIN;
+ iter_type.builtin_type = Variant::INT;
}
}
@@ -2789,15 +3106,20 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
// this is for checking variable for redefining
// inside this _parse_block
- cf_for->body->variables.push_back(id->name);
- cf_for->body->variable_lines.push_back(id->line);
+ LocalVarNode *lv = alloc_node<LocalVarNode>();
+ lv->name = id->name;
+ lv->line = id->line;
+ lv->assignments++;
+ id->declared_block = cf_for->body;
+ lv->set_datatype(iter_type);
+ id->set_datatype(iter_type);
+ cf_for->body->variables.insert(id->name, lv);
_parse_block(cf_for->body, p_static);
- cf_for->body->variables.remove(0);
- cf_for->body->variable_lines.remove(0);
current_block = p_block;
if (error_set)
return;
+ p_block->has_return = cf_for->body->has_return;
p_block->statements.push_back(cf_for);
} break;
case GDScriptTokenizer::TK_CF_CONTINUE: {
@@ -2827,6 +3149,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
tokenizer->advance();
ControlFlowNode *cf_return = alloc_node<ControlFlowNode>();
cf_return->cf_type = ControlFlowNode::CF_RETURN;
+ cf_return->line = tokenizer->get_token_line(-1);
if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON || tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE || tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
//expect end of statement
@@ -2850,6 +3173,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
return;
}
}
+ p_block->has_return = true;
} break;
case GDScriptTokenizer::TK_CF_MATCH: {
@@ -2882,12 +3206,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
_parse_pattern_block(compiled_branches, match_node->branches, p_static);
- _transform_match_statment(compiled_branches, match_node);
+ if (error_set) return;
ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
match_cf_node->match = match_node;
+ match_cf_node->body = compiled_branches;
+ p_block->has_return = p_block->has_return || compiled_branches->has_return;
p_block->statements.push_back(match_cf_node);
_end_statement();
@@ -2938,16 +3264,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
}
} break;
- /*
- case GDScriptTokenizer::TK_CF_LOCAL: {
-
- if (tokenizer->get_token(1)!=GDScriptTokenizer::TK_SEMICOLON && tokenizer->get_token(1)!=GDScriptTokenizer::TK_NEWLINE ) {
-
- _set_error("Expected ';' or <NewLine>.");
- }
- tokenizer->advance();
- } break;
- */
}
}
}
@@ -3087,6 +3403,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
switch (token) {
+ case GDScriptTokenizer::TK_CURSOR: {
+ tokenizer->advance();
+ } break;
case GDScriptTokenizer::TK_EOF:
p_class->end_line = tokenizer->get_token_line();
case GDScriptTokenizer::TK_ERROR: {
@@ -3103,6 +3422,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
case GDScriptTokenizer::TK_PR_EXTENDS: {
+ _mark_line_as_safe(tokenizer->get_token_line());
_parse_extends(p_class);
if (error_set)
return;
@@ -3112,6 +3432,59 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
} break;
+ case GDScriptTokenizer::TK_PR_CLASS_NAME: {
+
+ if (p_class->owner) {
+ _set_error("'class_name' is only valid for the main class namespace.");
+ return;
+ }
+ if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
+
+ _set_error("'class_name' syntax: 'class_name <UniqueName>'");
+ return;
+ }
+
+ p_class->name = tokenizer->get_token_identifier(1);
+
+ if (self_path != String() && ScriptServer::is_global_class(p_class->name) && ScriptServer::get_global_class_path(p_class->name) != self_path) {
+ _set_error("Unique global class '" + p_class->name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
+ return;
+ }
+
+ if (ClassDB::class_exists(p_class->name)) {
+ _set_error("Class '" + p_class->name + "' shadows a native class.");
+ return;
+ }
+
+ tokenizer->advance(2);
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
+ tokenizer->advance();
+
+ if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) {
+ Variant constant = tokenizer->get_token_constant();
+ String icon_path = constant.operator String();
+
+ String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path;
+ if (!FileAccess::exists(abs_icon_path)) {
+ _set_error("No class icon found at: " + abs_icon_path);
+ return;
+ }
+
+ p_class->icon_path = icon_path;
+
+ tokenizer->advance();
+ } else {
+ _set_error("Optional parameter after 'class_name' must be a string constant file path to an icon.");
+ return;
+ }
+
+ } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
+ _set_error("Class icon must be separated by a comma.");
+ return;
+ }
+
+ } break;
case GDScriptTokenizer::TK_PR_TOOL: {
if (p_class->tool) {
@@ -3128,7 +3501,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
//class inside class :D
StringName name;
- StringName extends;
if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
@@ -3138,6 +3510,31 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
name = tokenizer->get_token_identifier(1);
tokenizer->advance(2);
+ // Check if name is shadowing something else
+ if (ClassDB::class_exists(name) || ClassDB::class_exists("_" + name.operator String())) {
+ _set_error("Class '" + String(name) + "' shadows a native class.");
+ return;
+ }
+ if (ScriptServer::is_global_class(name)) {
+ _set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
+ return;
+ }
+ ClassNode *outer_class = p_class;
+ while (outer_class) {
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i]->name == name) {
+ _set_error("Another class named '" + String(name) + "' already exists in this scope (at line " + itos(outer_class->subclasses[i]->line) + ").");
+ return;
+ }
+ }
+ if (outer_class->constant_expressions.has(name)) {
+ _set_error("A constant named '" + String(name) + "' already exists in the outer class scope (at line" + itos(outer_class->constant_expressions[name].expression->line) + ").");
+ return;
+ }
+
+ outer_class = outer_class->owner;
+ }
+
ClassNode *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>();
newclass->initializer->parent_class = newclass;
@@ -3213,6 +3610,17 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+#ifdef DEBUG_ENABLED
+ if (p_class->constant_expressions.has(name)) {
+ _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name);
+ }
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ if (p_class->variables[i].identifier == name) {
+ _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name);
+ }
+ }
+#endif // DEBUG_ENABLED
+
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
_set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ).");
@@ -3222,7 +3630,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
Vector<StringName> arguments;
+ Vector<DataType> argument_types;
Vector<Node *> default_values;
+#ifdef DEBUG_ENABLED
+ Vector<int> arguments_usage;
+#endif // DEBUG_ENABLED
int fnline = tokenizer->get_token_line();
@@ -3249,9 +3661,24 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
StringName argname = tokenizer->get_token_identifier();
arguments.push_back(argname);
+#ifdef DEBUG_ENABLED
+ arguments_usage.push_back(0);
+#endif // DEBUG_ENABLED
tokenizer->advance();
+ DataType argtype;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ argtype.infer_type = true;
+ tokenizer->advance();
+ } else if (!_parse_type(argtype)) {
+ _set_error("Expected type for argument.");
+ return;
+ }
+ }
+ argument_types.push_back(argtype);
+
if (defaulting && tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
_set_error("Default parameter expected.");
@@ -3269,9 +3696,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
OperatorNode *on = alloc_node<OperatorNode>();
on->op = OperatorNode::OP_ASSIGN;
+ on->line = fnline;
IdentifierNode *in = alloc_node<IdentifierNode>();
in->name = argname;
+ in->line = fnline;
on->arguments.push_back(in);
on->arguments.push_back(defval);
@@ -3308,6 +3737,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (name == "_init") {
+ if (_static) {
+ _set_error("Constructor cannot be static.");
+ return;
+ }
+
if (p_class->extends_used) {
OperatorNode *cparent = alloc_node<OperatorNode>();
@@ -3322,6 +3756,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
tokenizer->advance();
if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
_set_error("expected '(' for parent constructor arguments.");
+ return;
}
tokenizer->advance();
@@ -3359,6 +3794,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+ DataType return_type;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_FORWARD_ARROW) {
+
+ if (!_parse_type(return_type, true)) {
+ _set_error("Expected return type for function.");
+ return;
+ }
+ }
+
if (!_enter_indent_block(block)) {
_set_error("Indented block expected.");
@@ -3367,11 +3811,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
FunctionNode *function = alloc_node<FunctionNode>();
function->name = name;
+ function->return_type = return_type;
function->arguments = arguments;
+ function->argument_types = argument_types;
function->default_values = default_values;
function->_static = _static;
function->line = fnline;
-
+#ifdef DEBUG_ENABLED
+ function->arguments_usage = arguments_usage;
+#endif // DEBUG_ENABLED
function->rpc_mode = rpc_mode;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
@@ -3398,6 +3846,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
ClassNode::Signal sig;
sig.name = tokenizer->get_token_identifier();
+ sig.emissions = 0;
+ sig.line = tokenizer->get_token_line();
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
@@ -3489,14 +3939,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") {
- //current_export.hint=PROPERTY_HINT_ALL_FLAGS;
tokenizer->advance();
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
+ ERR_EXPLAIN("Exporting bit flags hint requires string constants.");
+ WARN_DEPRECATED
break;
}
if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- _set_error("Expected ')' or ',' in bit flags hint.");
+ _set_error("Expected ',' in bit flags hint.");
return;
}
@@ -3861,6 +4312,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
current_export.hint_string = native_class->get_name();
+ current_export.class_name = native_class->get_name();
} else {
current_export = PropertyInfo();
@@ -4080,12 +4532,53 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member.expression = NULL;
member._export.name = member.identifier;
member.line = tokenizer->get_token_line();
+ member.usages = 0;
member.rpc_mode = rpc_mode;
+ if (current_class->constant_expressions.has(member.identifier)) {
+ _set_error("A constant named '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ itos(current_class->constant_expressions[member.identifier].expression->line) + ").");
+ return;
+ }
+
+ for (int i = 0; i < current_class->variables.size(); i++) {
+ if (current_class->variables[i].identifier == member.identifier) {
+ _set_error("Variable '" + String(member.identifier) + "' alread exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+#ifdef DEBUG_ENABLED
+ for (int i = 0; i < current_class->functions.size(); i++) {
+ if (current_class->functions[i]->name == member.identifier) {
+ _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
+ break;
+ }
+ }
+ for (int i = 0; i < current_class->static_functions.size(); i++) {
+ if (current_class->static_functions[i]->name == member.identifier) {
+ _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
+ break;
+ }
+ }
+#endif // DEBUG_ENABLED
tokenizer->advance();
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ member.data_type = DataType();
+#ifdef DEBUG_ENABLED
+ member.data_type.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(member.data_type)) {
+ _set_error("Expected type for class variable.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
@@ -4093,7 +4586,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
#endif
tokenizer->advance();
- Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport);
+ Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL);
if (!subexpr) {
if (_recover_from_completion()) {
break;
@@ -4117,42 +4610,31 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member.expression = subexpr;
- if (autoexport) {
- if (1) /*(subexpr->type==Node::TYPE_ARRAY) {
+ if (autoexport && !member.data_type.has_type) {
- member._export.type=Variant::ARRAY;
-
- } else if (subexpr->type==Node::TYPE_DICTIONARY) {
-
- member._export.type=Variant::DICTIONARY;
-
- } else*/
- {
-
- if (subexpr->type != Node::TYPE_CONSTANT) {
+ if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Type-less export needs a constant expression assigned to infer type.");
- return;
- }
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
+ }
- ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
- if (cn->value.get_type() == Variant::NIL) {
+ ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
+ if (cn->value.get_type() == Variant::NIL) {
- _set_error("Can't accept a null constant expression for inferring export type.");
+ _set_error("Can't accept a null constant expression for inferring export type.");
+ return;
+ }
+ member._export.type = cn->value.get_type();
+ member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type() == Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res == NULL) {
+ _set_error("Exported constant not a type or resource.");
return;
}
- member._export.type = cn->value.get_type();
- member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- if (cn->value.get_type() == Variant::OBJECT) {
- Object *obj = cn->value;
- Resource *res = Object::cast_to<Resource>(obj);
- if (res == NULL) {
- _set_error("Exported constant not a type or resource.");
- return;
- }
- member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member._export.hint_string = res->get_class();
- }
+ member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string = res->get_class();
}
}
#ifdef TOOLS_ENABLED
@@ -4186,15 +4668,37 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
else
p_class->initializer->statements.push_back(op);
- } else {
+ member.initial_assignment = op;
- if (autoexport) {
+ } else {
+ if (autoexport && !member.data_type.has_type) {
_set_error("Type-less export needs a constant expression assigned to infer type.");
return;
}
}
+ if (autoexport && member.data_type.has_type) {
+ if (member.data_type.kind == DataType::BUILTIN) {
+ member._export.type = member.data_type.builtin_type;
+ } else if (member.data_type.kind == DataType::NATIVE) {
+ if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) {
+ member._export.type = Variant::OBJECT;
+ member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+ member._export.hint_string = member.data_type.native_type;
+ member._export.class_name = member.data_type.native_type;
+ } else {
+ _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
+ return;
+ }
+
+ } else {
+ _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
+ return;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_SETGET) {
tokenizer->advance();
@@ -4231,7 +4735,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
} break;
case GDScriptTokenizer::TK_PR_CONST: {
- //variale declaration and (eventual) initialization
+ // constant declaration and initialization
ClassNode::Constant constant;
@@ -4242,9 +4746,38 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- constant.identifier = tokenizer->get_token_literal();
+ StringName const_id = tokenizer->get_token_literal();
+ int line = tokenizer->get_token_line();
+
+ if (current_class->constant_expressions.has(const_id)) {
+ _set_error("Constant '" + String(const_id) + "' alread 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) + "' alread exists in this class (at line: " +
+ itos(current_class->variables[i].line) + ").");
+ return;
+ }
+ }
+
tokenizer->advance();
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
+ if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
+ constant.type = DataType();
+#ifdef DEBUG_ENABLED
+ constant.type.infer_type = true;
+#endif
+ tokenizer->advance();
+ } else if (!_parse_type(constant.type)) {
+ _set_error("Expected type for class constant.");
+ return;
+ }
+ }
+
if (tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
_set_error("Constant expects assignment.");
return;
@@ -4261,14 +4794,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Expected constant expression");
+ _set_error("Expected constant expression", line);
+ return;
}
+ subexpr->line = line;
constant.expression = subexpr;
- p_class->constant_expressions.push_back(constant);
+ p_class->constant_expressions.insert(const_id, constant);
if (!_end_statement()) {
- _set_error("Expected end of statement (constant)");
+ _set_error("Expected end of statement (constant)", line);
return;
}
@@ -4311,7 +4846,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} else { // tokenizer->is_token_literal(0, true)
ClassNode::Constant constant;
- constant.identifier = tokenizer->get_token_literal();
+ StringName const_id = tokenizer->get_token_literal();
tokenizer->advance();
@@ -4328,22 +4863,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (subexpr->type != Node::TYPE_CONSTANT) {
_set_error("Expected constant expression");
+ return;
}
- const ConstantNode *subexpr_const = static_cast<const ConstantNode *>(subexpr);
+ ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
if (subexpr_const->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum");
+ return;
}
last_assign = subexpr_const->value;
- constant.expression = subexpr;
+ constant.expression = subexpr_const;
} 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;
}
@@ -4353,20 +4891,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (enum_name != "") {
const ConstantNode *cn = static_cast<const ConstantNode *>(constant.expression);
- enum_dict[constant.identifier] = cn->value;
+ enum_dict[const_id] = cn->value;
}
- p_class->constant_expressions.push_back(constant);
+ constant.type.has_type = true;
+ constant.type.kind = DataType::BUILTIN;
+ constant.type.builtin_type = Variant::INT;
+ p_class->constant_expressions.insert(const_id, constant);
}
}
if (enum_name != "") {
ClassNode::Constant enum_constant;
- enum_constant.identifier = enum_name;
ConstantNode *cn = alloc_node<ConstantNode>();
cn->value = enum_dict;
+ cn->datatype = _type_from_variant(cn->value);
+
enum_constant.expression = cn;
- p_class->constant_expressions.push_back(enum_constant);
+ enum_constant.type = cn->datatype;
+ p_class->constant_expressions.insert(enum_name, enum_constant);
}
if (!_end_statement()) {
@@ -4396,6 +4939,2966 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
}
}
+void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
+
+ if (p_class->extends_used) {
+ //do inheritance
+ String path = p_class->extends_file;
+
+ Ref<GDScript> script;
+ StringName native;
+ ClassNode *base_class = NULL;
+
+ if (path != "") {
+ //path (and optionally subclasses)
+
+ if (path.is_rel_path()) {
+
+ String base = base_path;
+
+ if (base == "" || base.is_rel_path()) {
+ _set_error("Could not resolve relative path for parent class: " + path, p_class->line);
+ return;
+ }
+ path = base.plus_file(path).simplify_path();
+ }
+ script = ResourceLoader::load(path);
+ if (script.is_null()) {
+ _set_error("Could not load base class: " + path, p_class->line);
+ return;
+ }
+ if (!script->is_valid()) {
+
+ _set_error("Script not fully loaded (cyclic preload?): " + path, p_class->line);
+ return;
+ }
+
+ if (p_class->extends_class.size()) {
+
+ for (int i = 0; i < p_class->extends_class.size(); i++) {
+
+ String sub = p_class->extends_class[i];
+ if (script->get_subclasses().has(sub)) {
+
+ Ref<Script> subclass = script->get_subclasses()[sub]; //avoid reference from disappearing
+ script = subclass;
+ } else {
+
+ _set_error("Could not find subclass: " + sub, p_class->line);
+ return;
+ }
+ }
+ }
+
+ } else {
+
+ if (p_class->extends_class.size() == 0) {
+ _set_error("Parser bug: undecidable inheritance.", p_class->line);
+ ERR_FAIL();
+ }
+ //look around for the subclasses
+
+ int extend_iter = 1;
+ String base = p_class->extends_class[0];
+ ClassNode *p = p_class->owner;
+ Ref<GDScript> base_script;
+
+ if (ScriptServer::is_global_class(base)) {
+ base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base));
+ if (!base_script.is_valid()) {
+ _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line);
+ return;
+ }
+ p = NULL;
+ }
+
+ while (p) {
+
+ bool found = false;
+
+ for (int i = 0; i < p->subclasses.size(); i++) {
+ if (p->subclasses[i]->name == base) {
+ ClassNode *test = p->subclasses[i];
+ while (test) {
+ if (test == p_class) {
+ _set_error("Cyclic inheritance.", test->line);
+ return;
+ }
+ if (test->base_type.kind == DataType::CLASS) {
+ test = test->base_type.class_type;
+ } else {
+ break;
+ }
+ }
+ found = true;
+ if (extend_iter < p_class->extends_class.size()) {
+ // Keep looking at current classes if possible
+ base = p_class->extends_class[extend_iter++];
+ p = p->subclasses[i];
+ } else {
+ base_class = p->subclasses[i];
+ }
+ break;
+ }
+ }
+
+ if (base_class) break;
+ if (found) continue;
+
+ if (p->constant_expressions.has(base)) {
+ if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) {
+ _set_error("Could not resolve constant '" + base + "'.", p_class->line);
+ return;
+ }
+ const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[base].expression);
+ base_script = cn->value;
+ if (base_script.is_null()) {
+ _set_error("Constant is not a class: " + base, p_class->line);
+ return;
+ }
+ break;
+ }
+
+ p = p->owner;
+ }
+
+ if (base_script.is_valid()) {
+
+ String ident = base;
+
+ for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
+
+ String subclass = p_class->extends_class[i];
+
+ ident += ("." + subclass);
+
+ if (base_script->get_subclasses().has(subclass)) {
+
+ base_script = base_script->get_subclasses()[subclass];
+ } else if (base_script->get_constants().has(subclass)) {
+
+ Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
+ if (new_base_class.is_null()) {
+ _set_error("Constant is not a class: " + ident, p_class->line);
+ return;
+ }
+ base_script = new_base_class;
+ } else {
+
+ _set_error("Could not find subclass: " + ident, p_class->line);
+ return;
+ }
+ }
+
+ script = base_script;
+
+ } else if (!base_class) {
+
+ if (p_class->extends_class.size() > 1) {
+
+ _set_error("Invalid inheritance (unknown class + subclasses)", p_class->line);
+ return;
+ }
+ //if not found, try engine classes
+ if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
+
+ _set_error("Unknown class: '" + base + "'", p_class->line);
+ return;
+ }
+
+ native = base;
+ }
+ }
+
+ if (base_class) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::CLASS;
+ p_class->base_type.class_type = base_class;
+ } else if (script.is_valid()) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::GDSCRIPT;
+ p_class->base_type.script_type = script;
+ p_class->base_type.native_type = script->get_instance_base_type();
+ } else if (native != StringName()) {
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::NATIVE;
+ p_class->base_type.native_type = native;
+ } else {
+ _set_error("Could not determine inheritance", p_class->line);
+ return;
+ }
+
+ } else {
+ // without extends, implicitly extend Reference
+ p_class->base_type.has_type = true;
+ p_class->base_type.kind = DataType::NATIVE;
+ p_class->base_type.native_type = "Reference";
+ }
+
+ // Recursively determine subclasses
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ _determine_inheritance(p_class->subclasses[i]);
+ }
+}
+
+String GDScriptParser::DataType::to_string() const {
+ if (!has_type) return "var";
+ switch (kind) {
+ case BUILTIN: {
+ if (builtin_type == Variant::NIL) return "null";
+ return Variant::get_type_name(builtin_type);
+ } break;
+ case NATIVE: {
+ if (is_meta_type) {
+ return "GDScriptNativeClass";
+ }
+ return native_type.operator String();
+ } break;
+
+ case GDSCRIPT: {
+ Ref<GDScript> gds = script_type;
+ const String &gds_class = gds->get_script_class_name();
+ if (!gds_class.empty()) {
+ return gds_class;
+ }
+ } // fallthrough
+ case SCRIPT: {
+ if (is_meta_type) {
+ return script_type->get_class_name().operator String();
+ }
+ String name = script_type->get_name();
+ if (name != String()) {
+ return name;
+ }
+ name = script_type->get_path().get_file();
+ if (name != String()) {
+ return name;
+ }
+ return native_type.operator String();
+ } break;
+ case CLASS: {
+ ERR_FAIL_COND_V(!class_type, String());
+ if (is_meta_type) {
+ return "GDScript";
+ }
+ if (class_type->name == StringName()) {
+ return "self";
+ }
+ return class_type->name.operator String();
+ } break;
+ }
+
+ return "Unresolved";
+}
+
+bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
+ tokenizer->advance();
+ r_type.has_type = true;
+
+ bool finished = false;
+ bool can_index = false;
+ String full_name;
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
+ completion_cursor = StringName();
+ completion_type = COMPLETION_TYPE_HINT;
+ completion_class = current_class;
+ completion_function = current_function;
+ completion_line = tokenizer->get_token_line();
+ completion_argument = 0;
+ completion_block = current_block;
+ completion_found = true;
+ completion_ident_is_call = p_can_be_void;
+ tokenizer->advance();
+ }
+
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_PR_VOID: {
+ if (!p_can_be_void) {
+ return false;
+ }
+ r_type.kind = DataType::BUILTIN;
+ r_type.builtin_type = Variant::NIL;
+ } break;
+ case GDScriptTokenizer::TK_BUILT_IN_TYPE: {
+ r_type.builtin_type = tokenizer->get_token_type();
+ if (tokenizer->get_token_type() == Variant::OBJECT) {
+ r_type.kind = DataType::NATIVE;
+ r_type.native_type = "Object";
+ } else {
+ r_type.kind = DataType::BUILTIN;
+ }
+ } break;
+ case GDScriptTokenizer::TK_IDENTIFIER: {
+ r_type.native_type = tokenizer->get_token_identifier();
+ if (ClassDB::class_exists(r_type.native_type) || ClassDB::class_exists("_" + r_type.native_type.operator String())) {
+ r_type.kind = DataType::NATIVE;
+ } else {
+ r_type.kind = DataType::UNRESOLVED;
+ can_index = true;
+ full_name = r_type.native_type;
+ }
+ } break;
+ default: {
+ return false;
+ }
+ }
+
+ tokenizer->advance();
+
+ if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
+ completion_cursor = r_type.native_type;
+ completion_type = COMPLETION_TYPE_HINT;
+ completion_class = current_class;
+ completion_function = current_function;
+ completion_line = tokenizer->get_token_line();
+ completion_argument = 0;
+ completion_block = current_block;
+ completion_found = true;
+ completion_ident_is_call = p_can_be_void;
+ tokenizer->advance();
+ }
+
+ if (can_index) {
+ while (!finished) {
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_PERIOD: {
+ if (!can_index) {
+ _set_error("Unexpected '.'.");
+ return false;
+ }
+ can_index = false;
+ tokenizer->advance();
+ } break;
+ case GDScriptTokenizer::TK_IDENTIFIER: {
+ if (can_index) {
+ _set_error("Unexpected identifier.");
+ return false;
+ }
+
+ StringName id;
+ bool has_completion = _get_completable_identifier(COMPLETION_TYPE_HINT_INDEX, id);
+ if (id == StringName()) {
+ id = "@temp";
+ }
+
+ full_name += "." + id.operator String();
+ can_index = true;
+ if (has_completion) {
+ completion_cursor = full_name;
+ }
+ } break;
+ default: {
+ finished = true;
+ } break;
+ }
+ }
+
+ if (tokenizer->get_token(-1) == GDScriptTokenizer::TK_PERIOD) {
+ _set_error("Expected subclass identifier.");
+ return false;
+ }
+
+ r_type.native_type = full_name;
+ }
+
+ return true;
+}
+
+GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, int p_line) {
+ if (!p_source.has_type) return p_source;
+ if (p_source.kind != DataType::UNRESOLVED) return p_source;
+
+ Vector<String> full_name = p_source.native_type.operator String().split(".", false);
+ int name_part = 0;
+
+ DataType result;
+ result.has_type = true;
+
+ while (name_part < full_name.size()) {
+
+ bool found = false;
+ StringName id = full_name[name_part];
+ DataType base_type = result;
+
+ ClassNode *p = NULL;
+ if (name_part == 0) {
+ if (ScriptServer::is_global_class(id)) {
+ String script_path = ScriptServer::get_global_class_path(id);
+ if (script_path == self_path) {
+ result.kind = DataType::CLASS;
+ result.class_type = current_class;
+ } else {
+ Ref<Script> script = ResourceLoader::load(script_path);
+ Ref<GDScript> gds = script;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic inheritance).", p_line);
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ } else if (script.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = script;
+ } else {
+ _set_error("Class '" + id + "' was found in global scope but its script could not be loaded.", p_line);
+ return DataType();
+ }
+ }
+ name_part++;
+ continue;
+ } else {
+ p = current_class;
+ }
+ } else if (base_type.kind == DataType::CLASS) {
+ p = base_type.class_type;
+ }
+ while (p) {
+ if (p->constant_expressions.has(id)) {
+ if (p->constant_expressions[id].expression->type != Node::TYPE_CONSTANT) {
+ _set_error("Parser bug: unresolved constant.", p_line);
+ ERR_FAIL_V(result);
+ }
+ const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[id].expression);
+ Ref<GDScript> gds = cn->value;
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ found = true;
+ } else {
+ Ref<Script> scr = cn->value;
+ if (scr.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = scr;
+ found = true;
+ }
+ }
+ break;
+ }
+
+ // Inner classes
+ ClassNode *outer_class = p;
+ while (outer_class) {
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i] == p) {
+ continue;
+ }
+ if (outer_class->subclasses[i]->name == id) {
+ found = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class->subclasses[i];
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ outer_class = outer_class->owner;
+ }
+
+ if (!found && p->base_type.kind == DataType::CLASS) {
+ p = p->base_type.class_type;
+ } else {
+ base_type = p->base_type;
+ break;
+ }
+ }
+
+ // Still look for class constants in parent script
+ if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) {
+ Ref<Script> scr = base_type.script_type;
+ ERR_FAIL_COND_V(scr.is_null(), result);
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+
+ if (constants.has(id)) {
+ Ref<GDScript> gds = constants[id];
+
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
+ found = true;
+ } else {
+ Ref<Script> scr = constants[id];
+ if (scr.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = scr;
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!found && !for_completion) {
+ String base;
+ if (name_part == 0) {
+ base = "self";
+ } else {
+ base = result.to_string();
+ }
+ _set_error("Identifier '" + String(id) + "' is not a valid type (not a script or class), or could not be found on base '" +
+ base + "'.",
+ p_line);
+ return DataType();
+ }
+
+ name_part++;
+ }
+
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_variant(const Variant &p_value) const {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.kind = DataType::BUILTIN;
+ result.builtin_type = p_value.get_type();
+
+ if (result.builtin_type == Variant::OBJECT) {
+ Object *obj = p_value.operator Object *();
+ if (!obj) {
+ return DataType();
+ }
+ result.native_type = obj->get_class_name();
+ Ref<Script> scr = p_value;
+ if (scr.is_valid()) {
+ result.is_meta_type = true;
+ } else {
+ result.is_meta_type = false;
+ scr = obj->get_script();
+ }
+ if (scr.is_valid()) {
+ result.script_type = scr;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ result.native_type = scr->get_instance_base_type();
+ } else {
+ result.kind = DataType::NATIVE;
+ }
+ }
+
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant) const {
+ DataType ret;
+ if (p_property.type == Variant::NIL && (p_nil_is_variant || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
+ // Variant
+ return ret;
+ }
+ ret.has_type = true;
+ ret.builtin_type = p_property.type;
+ if (p_property.type == Variant::OBJECT) {
+ ret.kind = DataType::NATIVE;
+ ret.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ } else {
+ ret.kind = DataType::BUILTIN;
+ }
+ return ret;
+}
+
+GDScriptParser::DataType GDScriptParser::_type_from_gdtype(const GDScriptDataType &p_gdtype) const {
+ DataType result;
+ if (!p_gdtype.has_type) {
+ return result;
+ }
+
+ result.has_type = true;
+ result.builtin_type = p_gdtype.builtin_type;
+ result.native_type = p_gdtype.native_type;
+ result.script_type = p_gdtype.script_type;
+
+ switch (p_gdtype.kind) {
+ case GDScriptDataType::BUILTIN: {
+ result.kind = DataType::BUILTIN;
+ } break;
+ case GDScriptDataType::NATIVE: {
+ result.kind = DataType::NATIVE;
+ } break;
+ case GDScriptDataType::GDSCRIPT: {
+ result.kind = DataType::GDSCRIPT;
+ } break;
+ case GDScriptDataType::SCRIPT: {
+ result.kind = DataType::SCRIPT;
+ } break;
+ }
+ return result;
+}
+
+GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const {
+ if (!p_a.has_type || !p_b.has_type) {
+ r_valid = true;
+ return DataType();
+ }
+
+ Variant::Type a_type = p_a.kind == DataType::BUILTIN ? p_a.builtin_type : Variant::OBJECT;
+ Variant::Type b_type = p_b.kind == DataType::BUILTIN ? p_b.builtin_type : Variant::OBJECT;
+
+ Variant a;
+ REF a_ref;
+ if (a_type == Variant::OBJECT) {
+ a_ref.instance();
+ a = a_ref;
+ } else {
+ Variant::CallError err;
+ a = Variant::construct(a_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ r_valid = false;
+ return DataType();
+ }
+ }
+ Variant b;
+ REF b_ref;
+ if (b_type == Variant::OBJECT) {
+ b_ref.instance();
+ b = b_ref;
+ } else {
+ Variant::CallError err;
+ b = Variant::construct(b_type, NULL, 0, err);
+ if (err.error != Variant::CallError::CALL_OK) {
+ r_valid = false;
+ return DataType();
+ }
+ }
+
+ // Avoid division by zero
+ if (a_type == Variant::INT || a_type == Variant::REAL) {
+ Variant::evaluate(Variant::OP_ADD, a, 1, a, r_valid);
+ }
+ if (b_type == Variant::INT || b_type == Variant::REAL) {
+ Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid);
+ }
+ if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
+ a = "%s"; // Work around for formatting operator (%)
+ }
+
+ Variant ret;
+ Variant::evaluate(p_op, a, b, ret, r_valid);
+
+ if (r_valid) {
+ return _type_from_variant(ret);
+ }
+
+ return DataType();
+}
+
+Variant::Operator GDScriptParser::_get_variant_operation(const OperatorNode::Operator &p_op) const {
+ switch (p_op) {
+ case OperatorNode::OP_NEG: {
+ return Variant::OP_NEGATE;
+ } break;
+ case OperatorNode::OP_POS: {
+ return Variant::OP_POSITIVE;
+ } break;
+ case OperatorNode::OP_NOT: {
+ return Variant::OP_NOT;
+ } break;
+ case OperatorNode::OP_BIT_INVERT: {
+ return Variant::OP_BIT_NEGATE;
+ } break;
+ case OperatorNode::OP_IN: {
+ return Variant::OP_IN;
+ } break;
+ case OperatorNode::OP_EQUAL: {
+ return Variant::OP_EQUAL;
+ } break;
+ case OperatorNode::OP_NOT_EQUAL: {
+ return Variant::OP_NOT_EQUAL;
+ } break;
+ case OperatorNode::OP_LESS: {
+ return Variant::OP_LESS;
+ } break;
+ case OperatorNode::OP_LESS_EQUAL: {
+ return Variant::OP_LESS_EQUAL;
+ } break;
+ case OperatorNode::OP_GREATER: {
+ return Variant::OP_GREATER;
+ } break;
+ case OperatorNode::OP_GREATER_EQUAL: {
+ return Variant::OP_GREATER_EQUAL;
+ } break;
+ case OperatorNode::OP_AND: {
+ return Variant::OP_AND;
+ } break;
+ case OperatorNode::OP_OR: {
+ return Variant::OP_OR;
+ } break;
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ADD: {
+ return Variant::OP_ADD;
+ } break;
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_SUB: {
+ return Variant::OP_SUBTRACT;
+ } break;
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_MUL: {
+ return Variant::OP_MULTIPLY;
+ } break;
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_DIV: {
+ return Variant::OP_DIVIDE;
+ } break;
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_MOD: {
+ return Variant::OP_MODULE;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_BIT_AND: {
+ return Variant::OP_BIT_AND;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_BIT_OR: {
+ return Variant::OP_BIT_OR;
+ } break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR:
+ case OperatorNode::OP_BIT_XOR: {
+ return Variant::OP_BIT_XOR;
+ } break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_SHIFT_LEFT: {
+ return Variant::OP_SHIFT_LEFT;
+ }
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_SHIFT_RIGHT: {
+ return Variant::OP_SHIFT_RIGHT;
+ }
+ default: {
+ return Variant::OP_MAX;
+ } break;
+ }
+}
+
+bool GDScriptParser::_is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion) const {
+ // Ignore for completion
+ if (!check_types || for_completion) {
+ return true;
+ }
+ // Can't test if not all have type
+ if (!p_container.has_type || !p_expression.has_type) {
+ return true;
+ }
+
+ // Should never get here unresolved
+ ERR_FAIL_COND_V(p_container.kind == DataType::UNRESOLVED, false);
+ ERR_FAIL_COND_V(p_expression.kind == DataType::UNRESOLVED, false);
+
+ if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
+ bool valid = p_container.builtin_type == p_expression.builtin_type;
+ if (p_allow_implicit_conversion) {
+ valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL);
+ valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT);
+ valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH);
+ valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING);
+ valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL);
+ valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT);
+ valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL);
+ valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL);
+ }
+ return valid;
+ }
+
+ if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) {
+ // Can't mix built-ins with objects
+ return false;
+ }
+
+ // From now on everything is objects, check polymorphism
+ // The container must be the same class or a superclass of the expression
+
+ if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) {
+ // Null can be assigned to object types
+ return true;
+ }
+
+ StringName expr_native;
+ Ref<Script> expr_script;
+ ClassNode *expr_class = NULL;
+
+ switch (p_expression.kind) {
+ case DataType::NATIVE: {
+ if (p_container.kind != DataType::NATIVE) {
+ // Non-native type can't be a superclass of a native type
+ return false;
+ }
+ if (p_expression.is_meta_type) {
+ expr_native = GDScriptNativeClass::get_class_static();
+ } else {
+ expr_native = p_expression.native_type;
+ }
+ } break;
+ case DataType::SCRIPT:
+ case DataType::GDSCRIPT: {
+ if (p_container.kind == DataType::CLASS) {
+ // This cannot be resolved without cyclic dependencies, so just bail out
+ return false;
+ }
+ if (p_expression.is_meta_type) {
+ expr_native = p_expression.script_type->get_class_name();
+ } else {
+ expr_script = p_expression.script_type;
+ expr_native = expr_script->get_instance_base_type();
+ }
+ } break;
+ case DataType::CLASS: {
+ if (p_expression.is_meta_type) {
+ expr_native = GDScript::get_class_static();
+ } else {
+ expr_class = p_expression.class_type;
+ ClassNode *base = expr_class;
+ while (base->base_type.kind == DataType::CLASS) {
+ base = base->base_type.class_type;
+ }
+ expr_native = base->base_type.native_type;
+ expr_script = base->base_type.script_type;
+ }
+ }
+ }
+
+ switch (p_container.kind) {
+ case DataType::NATIVE: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static());
+ } else {
+ return ClassDB::is_parent_class(expr_native, p_container.native_type);
+ }
+ } break;
+ case DataType::SCRIPT:
+ case DataType::GDSCRIPT: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
+ }
+ if (expr_class == head && p_container.script_type->get_path() == self_path) {
+ // Special case: container is self script and expression is self
+ return true;
+ }
+ while (expr_script.is_valid()) {
+ if (expr_script == p_container.script_type) {
+ return true;
+ }
+ expr_script = expr_script->get_base_script();
+ }
+ return false;
+ } break;
+ case DataType::CLASS: {
+ if (p_container.is_meta_type) {
+ return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
+ }
+ if (p_container.class_type == head && expr_script.is_valid() && expr_script->get_path() == self_path) {
+ // Special case: container is self and expression is self script
+ return true;
+ }
+ while (expr_class) {
+ if (expr_class == p_container.class_type) {
+ return true;
+ }
+ expr_class = expr_class->base_type.class_type;
+ }
+ return false;
+ }
+ }
+
+ return false;
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
+ if (p_node->get_datatype().has_type) {
+ return p_node->get_datatype();
+ }
+
+ DataType node_type;
+
+ switch (p_node->type) {
+ case Node::TYPE_CONSTANT: {
+ node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value);
+ } break;
+ case Node::TYPE_TYPE: {
+ TypeNode *tn = static_cast<TypeNode *>(p_node);
+ node_type.has_type = true;
+ node_type.is_meta_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = tn->vtype;
+ } break;
+ case Node::TYPE_ARRAY: {
+ node_type.has_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::ARRAY;
+#ifdef DEBUG_ENABLED
+ // Check stuff inside the array
+ ArrayNode *an = static_cast<ArrayNode *>(p_node);
+ for (int i = 0; i < an->elements.size(); i++) {
+ _reduce_node_type(an->elements[i]);
+ }
+#endif // DEBUG_ENABLED
+ } break;
+ case Node::TYPE_DICTIONARY: {
+ node_type.has_type = true;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::DICTIONARY;
+#ifdef DEBUG_ENABLED
+ // Check stuff inside the dictionarty
+ DictionaryNode *dn = static_cast<DictionaryNode *>(p_node);
+ for (int i = 0; i < dn->elements.size(); i++) {
+ _reduce_node_type(dn->elements[i].key);
+ _reduce_node_type(dn->elements[i].value);
+ }
+#endif // DEBUG_ENABLED
+ } break;
+ case Node::TYPE_SELF: {
+ node_type.has_type = true;
+ node_type.kind = DataType::CLASS;
+ node_type.class_type = current_class;
+ } break;
+ case Node::TYPE_IDENTIFIER: {
+ IdentifierNode *id = static_cast<IdentifierNode *>(p_node);
+ if (id->declared_block) {
+ node_type = id->declared_block->variables[id->name]->get_datatype();
+ id->declared_block->variables[id->name]->usages += 1;
+ } else if (id->name == "#match_value") {
+ // It's a special id just for the match statetement, ignore
+ break;
+ } else if (current_function && current_function->arguments.find(id->name) >= 0) {
+ int idx = current_function->arguments.find(id->name);
+ node_type = current_function->argument_types[idx];
+ } else {
+ node_type = _reduce_identifier_type(NULL, id->name, id->line);
+ }
+ } break;
+ case Node::TYPE_CAST: {
+ CastNode *cn = static_cast<CastNode *>(p_node);
+
+ DataType source_type = _reduce_node_type(cn->source_node);
+ cn->cast_type = _resolve_type(cn->cast_type, cn->line);
+ if (source_type.has_type) {
+
+ bool valid = false;
+ if (check_types) {
+ if (cn->cast_type.kind == DataType::BUILTIN && source_type.kind == DataType::BUILTIN) {
+ valid = Variant::can_convert(source_type.builtin_type, cn->cast_type.builtin_type);
+ }
+ if (cn->cast_type.kind != DataType::BUILTIN && source_type.kind != DataType::BUILTIN) {
+ valid = _is_type_compatible(cn->cast_type, source_type) || _is_type_compatible(source_type, cn->cast_type);
+ }
+
+ if (!valid) {
+ _set_error("Invalid cast. Cannot convert from '" + source_type.to_string() +
+ "' to '" + cn->cast_type.to_string() + "'.",
+ cn->line);
+ return DataType();
+ }
+ }
+ } else {
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::UNSAFE_CAST, cn->line, cn->cast_type.to_string());
+#endif // DEBUG_ENABLED
+ _mark_line_as_unsafe(cn->line);
+ }
+
+ node_type = cn->cast_type;
+
+ } break;
+ case Node::TYPE_OPERATOR: {
+ OperatorNode *op = static_cast<OperatorNode *>(p_node);
+
+ switch (op->op) {
+ case OperatorNode::OP_CALL:
+ case OperatorNode::OP_PARENT_CALL: {
+ node_type = _reduce_function_call_type(op);
+ } break;
+ case OperatorNode::OP_YIELD: {
+ if (op->arguments.size() == 2) {
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ DataType signal_type = _reduce_node_type(op->arguments[1]);
+ // TODO: Check if signal exists when it's a constant
+ if (base_type.has_type && base_type.kind == DataType::BUILTIN && base_type.builtin_type != Variant::NIL && base_type.builtin_type != Variant::OBJECT) {
+ _set_error("First argument of 'yield()' must be an object.", op->line);
+ return DataType();
+ }
+ if (signal_type.has_type && (signal_type.kind != DataType::BUILTIN || signal_type.builtin_type != Variant::STRING)) {
+ _set_error("Second argument of 'yield()' must be a string.", op->line);
+ return DataType();
+ }
+ }
+ // yield can return anything
+ node_type.has_type = false;
+ } break;
+ case OperatorNode::OP_IS:
+ case OperatorNode::OP_IS_BUILTIN: {
+
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: binary operation without 2 arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType value_type = _reduce_node_type(op->arguments[0]);
+ DataType type_type = _reduce_node_type(op->arguments[1]);
+
+ if (check_types && type_type.has_type) {
+ if (!type_type.is_meta_type && (type_type.kind != DataType::NATIVE || !ClassDB::is_parent_class(type_type.native_type, "Script"))) {
+ _set_error("Invalid 'is' test: right operand is not a type (not a native type nor a script).", op->line);
+ return DataType();
+ }
+ type_type.is_meta_type = false; // Test the actual type
+ if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) {
+ if (op->op == OperatorNode::OP_IS) {
+ _set_error("A value of type '" + value_type.to_string() + "' will never be an instance of '" + type_type.to_string() + "'.", op->line);
+ } else {
+ _set_error("A value of type '" + value_type.to_string() + "' will never be of type '" + type_type.to_string() + "'.", op->line);
+ }
+ return DataType();
+ }
+ }
+
+ node_type.has_type = true;
+ node_type.is_constant = true;
+ node_type.is_meta_type = false;
+ node_type.kind = DataType::BUILTIN;
+ node_type.builtin_type = Variant::BOOL;
+ } break;
+ // Unary operators
+ case OperatorNode::OP_NEG:
+ case OperatorNode::OP_POS:
+ case OperatorNode::OP_NOT:
+ case OperatorNode::OP_BIT_INVERT: {
+
+ DataType argument_type = _reduce_node_type(op->arguments[0]);
+ if (!argument_type.has_type) {
+ break;
+ }
+
+ Variant::Operator var_op = _get_variant_operation(op->op);
+ bool valid = false;
+ node_type = _get_operation_type(var_op, argument_type, argument_type, valid);
+
+ if (check_types && !valid) {
+ _set_error("Invalid operand type ('" + argument_type.to_string() +
+ "') to unary operator '" + Variant::get_operator_name(var_op) + "'.",
+ op->line, op->column);
+ return DataType();
+ }
+
+ } break;
+ // Binary operators
+ case OperatorNode::OP_IN:
+ case OperatorNode::OP_EQUAL:
+ case OperatorNode::OP_NOT_EQUAL:
+ case OperatorNode::OP_LESS:
+ case OperatorNode::OP_LESS_EQUAL:
+ case OperatorNode::OP_GREATER:
+ case OperatorNode::OP_GREATER_EQUAL:
+ case OperatorNode::OP_AND:
+ case OperatorNode::OP_OR:
+ case OperatorNode::OP_ADD:
+ case OperatorNode::OP_SUB:
+ case OperatorNode::OP_MUL:
+ case OperatorNode::OP_DIV:
+ case OperatorNode::OP_MOD:
+ case OperatorNode::OP_SHIFT_LEFT:
+ case OperatorNode::OP_SHIFT_RIGHT:
+ case OperatorNode::OP_BIT_AND:
+ case OperatorNode::OP_BIT_OR:
+ case OperatorNode::OP_BIT_XOR: {
+
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: binary operation without 2 arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType argument_a_type = _reduce_node_type(op->arguments[0]);
+ DataType argument_b_type = _reduce_node_type(op->arguments[1]);
+ if (!argument_a_type.has_type || !argument_b_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ Variant::Operator var_op = _get_variant_operation(op->op);
+ bool valid = false;
+ node_type = _get_operation_type(var_op, argument_a_type, argument_b_type, valid);
+
+ if (check_types && !valid) {
+ _set_error("Invalid operand types ('" + argument_a_type.to_string() + "' and '" +
+ argument_b_type.to_string() + "') to operator '" + Variant::get_operator_name(var_op) + "'.",
+ op->line, op->column);
+ return DataType();
+ }
+#ifdef DEBUG_ENABLED
+ if (var_op == Variant::OP_DIVIDE && argument_a_type.has_type && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT &&
+ argument_b_type.has_type && argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) {
+ _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line);
+ }
+#endif // DEBUG_ENABLED
+
+ } break;
+ // Ternary operators
+ case OperatorNode::OP_TERNARY_IF: {
+ if (op->arguments.size() != 3) {
+ _set_error("Parser bug: ternary operation without 3 arguments");
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType true_type = _reduce_node_type(op->arguments[1]);
+ DataType false_type = _reduce_node_type(op->arguments[2]);
+
+ // If types are equal, then the expression is of the same type
+ // If they are compatible, return the broader type
+ if (true_type == false_type || _is_type_compatible(true_type, false_type)) {
+ node_type = true_type;
+ } else if (_is_type_compatible(false_type, true_type)) {
+ node_type = false_type;
+ } else {
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::INCOMPATIBLE_TERNARY, op->line);
+#endif // DEBUG_ENABLED
+ }
+ } break;
+ // Assignment should never happen within an expression
+ case OperatorNode::OP_ASSIGN:
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_ASSIGN_BIT_XOR:
+ case OperatorNode::OP_INIT_ASSIGN: {
+
+ _set_error("Assignment inside expression is not allowed (parser bug?).", op->line);
+ return DataType();
+
+ } break;
+ case OperatorNode::OP_INDEX_NAMED: {
+ if (op->arguments.size() != 2) {
+ _set_error("Parser bug: named index with invalid arguments.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+ if (op->arguments[1]->type != Node::TYPE_IDENTIFIER) {
+ _set_error("Parser bug: named index without identifier argument.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ IdentifierNode *member_id = static_cast<IdentifierNode *>(op->arguments[1]);
+
+ if (base_type.has_type) {
+ if (check_types && base_type.kind == DataType::BUILTIN) {
+ // Variant type, just test if it's possible
+ DataType result;
+ switch (base_type.builtin_type) {
+ case Variant::NIL:
+ case Variant::DICTIONARY: {
+ result.has_type = false;
+ } break;
+ default: {
+ Variant::CallError err;
+ Variant temp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ bool valid = false;
+ Variant res = temp.get(member_id->name.operator String(), &valid);
+
+ if (valid) {
+ result = _type_from_variant(res);
+ } else if (check_types) {
+ _set_error("Can't get index '" + String(member_id->name.operator String()) + "' on base '" +
+ base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+ } break;
+ }
+ result.is_constant = false;
+ node_type = result;
+ } else {
+ node_type = _reduce_identifier_type(&base_type, member_id->name, op->line);
+#ifdef DEBUG_ENABLED
+ if (!node_type.has_type) {
+ _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string());
+ }
+#endif // DEBUG_ENABLED
+ }
+ } else {
+ _mark_line_as_unsafe(op->line);
+ }
+ if (error_set) {
+ return DataType();
+ }
+ } break;
+ case OperatorNode::OP_INDEX: {
+
+ if (op->arguments[1]->type == Node::TYPE_CONSTANT) {
+ ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
+ if (cn->value.get_type() == Variant::STRING) {
+ // Treat this as named indexing
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = cn->value.operator StringName();
+
+ op->op = OperatorNode::OP_INDEX_NAMED;
+ op->arguments.write[1] = id;
+
+ return _reduce_node_type(op);
+ }
+ }
+
+ DataType base_type = _reduce_node_type(op->arguments[0]);
+ DataType index_type = _reduce_node_type(op->arguments[1]);
+
+ if (!base_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ if (check_types && index_type.has_type) {
+ if (base_type.kind == DataType::BUILTIN) {
+ // Check if indexing is valid
+ bool error = index_type.kind != DataType::BUILTIN;
+ if (!error) {
+ switch (base_type.builtin_type) {
+ // Expect int or real as index
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_COLOR_ARRAY:
+ case Variant::POOL_INT_ARRAY:
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::POOL_VECTOR3_ARRAY:
+ case Variant::ARRAY:
+ case Variant::STRING: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::REAL;
+ } break;
+ // Expect String only
+ case Variant::RECT2:
+ case Variant::PLANE:
+ case Variant::QUAT:
+ case Variant::AABB:
+ case Variant::OBJECT: {
+ error = index_type.builtin_type != Variant::STRING;
+ } break;
+ // Expect String or number
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::TRANSFORM2D:
+ case Variant::BASIS:
+ case Variant::TRANSFORM: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::REAL &&
+ index_type.builtin_type != Variant::STRING;
+ } break;
+ // Expect String or int
+ case Variant::COLOR: {
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
+ } break;
+ }
+ }
+ if (error) {
+ _set_error("Invalid index type (" + index_type.to_string() + ") for base '" + base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+
+ if (op->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
+ ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
+ // Index is a constant, just try it if possible
+ switch (base_type.builtin_type) {
+ // Arrays/string have variable indexing, can't test directly
+ case Variant::STRING:
+ case Variant::ARRAY:
+ case Variant::DICTIONARY:
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_COLOR_ARRAY:
+ case Variant::POOL_INT_ARRAY:
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::POOL_VECTOR3_ARRAY: {
+ break;
+ }
+ default: {
+ Variant::CallError err;
+ Variant temp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ bool valid = false;
+ Variant res = temp.get(cn->value, &valid);
+
+ if (valid) {
+ node_type = _type_from_variant(res);
+ node_type.is_constant = false;
+ } else if (check_types) {
+ _set_error("Can't get index '" + String(cn->value) + "' on base '" +
+ base_type.to_string() + "'.",
+ op->line);
+ return DataType();
+ }
+ } break;
+ }
+ } else {
+ _mark_line_as_unsafe(op->line);
+ }
+ } else if (!for_completion && (index_type.kind != DataType::BUILTIN || index_type.builtin_type != Variant::STRING)) {
+ _set_error("Only strings can be used as index in the base type '" + base_type.to_string() + "'.", op->line);
+ return DataType();
+ }
+ }
+ if (check_types && !node_type.has_type) {
+ // Can infer indexing type for some variant types
+ DataType result;
+ result.has_type = true;
+ result.kind = DataType::BUILTIN;
+ switch (base_type.builtin_type) {
+ // Can't index at all
+ case Variant::NIL:
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::REAL:
+ case Variant::NODE_PATH:
+ case Variant::_RID: {
+ _set_error("Can't index on a value of type '" + base_type.to_string() + "'.", op->line);
+ return DataType();
+ } break;
+ // Return int
+ case Variant::POOL_BYTE_ARRAY:
+ case Variant::POOL_INT_ARRAY: {
+ result.builtin_type = Variant::INT;
+ } break;
+ // Return real
+ case Variant::POOL_REAL_ARRAY:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::QUAT: {
+ result.builtin_type = Variant::REAL;
+ } break;
+ // Return color
+ case Variant::POOL_COLOR_ARRAY: {
+ result.builtin_type = Variant::COLOR;
+ } break;
+ // Return string
+ case Variant::POOL_STRING_ARRAY:
+ case Variant::STRING: {
+ result.builtin_type = Variant::STRING;
+ } break;
+ // Return Vector2
+ case Variant::POOL_VECTOR2_ARRAY:
+ case Variant::TRANSFORM2D:
+ case Variant::RECT2: {
+ result.builtin_type = Variant::VECTOR2;
+ } break;
+ // Return Vector3
+ case Variant::POOL_VECTOR3_ARRAY:
+ case Variant::AABB:
+ case Variant::BASIS: {
+ result.builtin_type = Variant::VECTOR3;
+ } break;
+ // Depends on the index
+ case Variant::TRANSFORM:
+ case Variant::PLANE:
+ case Variant::COLOR:
+ default: {
+ result.has_type = false;
+ } break;
+ }
+ node_type = result;
+ }
+ } break;
+ default: {
+ _set_error("Parser bug: unhandled operation.", op->line);
+ ERR_FAIL_V(DataType());
+ }
+ }
+ } break;
+ }
+
+ p_node->set_datatype(_resolve_type(node_type, p_node->line));
+ return node_type;
+}
+
+bool GDScriptParser::_get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const {
+
+ r_static = false;
+ r_default_arg_count = 0;
+
+ DataType original_type = p_base_type;
+ ClassNode *base = NULL;
+ FunctionNode *callee = NULL;
+
+ if (p_base_type.kind == DataType::CLASS) {
+ base = p_base_type.class_type;
+ }
+
+ // Look up the current file (parse tree)
+ while (!callee && base) {
+ for (int i = 0; i < base->static_functions.size(); i++) {
+ FunctionNode *func = base->static_functions[i];
+ if (p_function == func->name) {
+ r_static = true;
+ callee = func;
+ break;
+ }
+ }
+ if (!callee && !p_base_type.is_meta_type) {
+ for (int i = 0; i < base->functions.size(); i++) {
+ FunctionNode *func = base->functions[i];
+ if (p_function == func->name) {
+ callee = func;
+ break;
+ }
+ }
+ }
+ p_base_type = base->base_type;
+ if (p_base_type.kind == DataType::CLASS) {
+ base = p_base_type.class_type;
+ } else {
+ break;
+ }
+ }
+
+ if (callee) {
+ r_return_type = callee->get_datatype();
+ for (int i = 0; i < callee->argument_types.size(); i++) {
+ r_arg_types.push_back(callee->argument_types[i]);
+ }
+ r_default_arg_count = callee->default_values.size();
+ return true;
+ }
+
+ // Nothing in current file, check parent script
+ Ref<GDScript> base_gdscript;
+ Ref<Script> base_script;
+ StringName native;
+ if (p_base_type.kind == DataType::GDSCRIPT) {
+ base_gdscript = p_base_type.script_type;
+ } else if (p_base_type.kind == DataType::SCRIPT) {
+ base_script = p_base_type.script_type;
+ } else if (p_base_type.kind == DataType::NATIVE) {
+ native = p_base_type.native_type;
+ }
+
+ while (base_gdscript.is_valid()) {
+ native = base_gdscript->get_instance_base_type();
+
+ Map<StringName, GDScriptFunction *> funcs = base_gdscript->get_member_functions();
+
+ if (funcs.has(p_function)) {
+ GDScriptFunction *f = funcs[p_function];
+ r_static = f->is_static();
+ r_default_arg_count = f->get_default_argument_count();
+ r_return_type = _type_from_gdtype(f->get_return_type());
+ for (int i = 0; i < f->get_argument_count(); i++) {
+ r_arg_types.push_back(_type_from_gdtype(f->get_argument_type(i)));
+ }
+ return true;
+ }
+
+ base_gdscript = base_gdscript->get_base_script();
+ }
+
+ while (base_script.is_valid()) {
+ native = base_script->get_instance_base_type();
+ MethodInfo mi = base_script->get_method_info(p_function);
+
+ if (!(mi == MethodInfo())) {
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_default_arg_count = mi.default_arguments.size();
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ r_arg_types.push_back(_type_from_property(E->get()));
+ }
+ return true;
+ }
+ base_script = base_script->get_base_script();
+ }
+
+#ifdef DEBUG_METHODS_ENABLED
+
+ // Only native remains
+ if (!ClassDB::class_exists(native)) {
+ native = "_" + native.operator String();
+ }
+ if (!ClassDB::class_exists(native)) {
+ if (!check_types) return false;
+ ERR_EXPLAIN("Parser bug: Class '" + String(native) + "' not found.");
+ ERR_FAIL_V(false);
+ }
+
+ MethodBind *method = ClassDB::get_method(native, p_function);
+
+ if (!method) {
+ // Try virtual methods
+ List<MethodInfo> virtuals;
+ ClassDB::get_virtual_methods(native, &virtuals);
+
+ for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
+ const MethodInfo &mi = E->get();
+ if (mi.name == p_function) {
+ r_default_arg_count = mi.default_arguments.size();
+ for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
+ r_arg_types.push_back(_type_from_property(pi->get()));
+ }
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_vararg = mi.flags & METHOD_FLAG_VARARG;
+ return true;
+ }
+ }
+
+ // If the base is a script, it might be trying to access members of the Script class itself
+ if (original_type.is_meta_type && !(p_function == "new") && (original_type.kind == DataType::SCRIPT || original_type.kind == DataType::GDSCRIPT)) {
+ method = ClassDB::get_method(original_type.script_type->get_class_name(), p_function);
+
+ if (method) {
+ r_static = true;
+ } else {
+ // Try virtual methods of the script type
+ virtuals.clear();
+ ClassDB::get_virtual_methods(original_type.script_type->get_class_name(), &virtuals);
+ for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
+ const MethodInfo &mi = E->get();
+ if (mi.name == p_function) {
+ r_default_arg_count = mi.default_arguments.size();
+ for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
+ r_arg_types.push_back(_type_from_property(pi->get()));
+ }
+ r_return_type = _type_from_property(mi.return_val, false);
+ r_static = true;
+ r_vararg = mi.flags & METHOD_FLAG_VARARG;
+ return true;
+ }
+ }
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ r_default_arg_count = method->get_default_argument_count();
+ r_return_type = _type_from_property(method->get_return_info(), false);
+ r_vararg = method->is_vararg();
+
+ for (int i = 0; i < method->get_argument_count(); i++) {
+ r_arg_types.push_back(_type_from_property(method->get_argument_info(i)));
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const OperatorNode *p_call) {
+ if (p_call->arguments.size() < 1) {
+ _set_error("Parser bug: function call without enough arguments.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ DataType return_type;
+ List<DataType> arg_types;
+ int default_args_count = 0;
+ int arg_count = p_call->arguments.size();
+ String callee_name;
+ bool is_vararg = false;
+
+ switch (p_call->arguments[0]->type) {
+ case GDScriptParser::Node::TYPE_TYPE: {
+ // Built-in constructor, special case
+ TypeNode *tn = static_cast<TypeNode *>(p_call->arguments[0]);
+
+ Vector<DataType> par_types;
+ par_types.resize(p_call->arguments.size() - 1);
+ for (int i = 1; i < p_call->arguments.size(); i++) {
+ par_types.write[i - 1] = _reduce_node_type(p_call->arguments[i]);
+ }
+
+ if (error_set) return DataType();
+
+ bool match = false;
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(tn->vtype, &constructors);
+ PropertyInfo return_type;
+
+ for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
+ MethodInfo &mi = E->get();
+
+ if (p_call->arguments.size() - 1 < mi.arguments.size() - mi.default_arguments.size()) {
+ continue;
+ }
+ if (p_call->arguments.size() - 1 > mi.arguments.size()) {
+ continue;
+ }
+
+ bool types_match = true;
+ for (int i = 0; i < par_types.size(); i++) {
+ DataType arg_type;
+ if (mi.arguments[i].type != Variant::NIL) {
+ arg_type.has_type = true;
+ arg_type.kind = mi.arguments[i].type == Variant::OBJECT ? DataType::NATIVE : DataType::BUILTIN;
+ arg_type.builtin_type = mi.arguments[i].type;
+ arg_type.native_type = mi.arguments[i].class_name;
+ }
+
+ if (!_is_type_compatible(arg_type, par_types[i], true)) {
+ types_match = false;
+ break;
+ } else {
+#ifdef DEBUG_ENABLED
+ if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_types[i].kind == DataType::BUILTIN && par_types[i].builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, Variant::get_type_name(tn->vtype));
+ }
+ if (par_types[i].may_yield && p_call->arguments[i + 1]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i + 1])));
+ }
+#endif // DEBUG_ENABLED
+ }
+ }
+
+ if (types_match) {
+ match = true;
+ return_type = mi.return_val;
+ break;
+ }
+ }
+
+ if (match) {
+ return _type_from_property(return_type, false);
+ } else if (check_types) {
+ String err = "No constructor of '";
+ err += Variant::get_type_name(tn->vtype);
+ err += "' matches the signature '";
+ err += Variant::get_type_name(tn->vtype) + "(";
+ for (int i = 0; i < par_types.size(); i++) {
+ if (i > 0) err += ", ";
+ err += par_types[i].to_string();
+ }
+ err += ")'.";
+ _set_error(err, p_call->line, p_call->column);
+ return DataType();
+ }
+ return DataType();
+ } break;
+ case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
+ BuiltInFunctionNode *func = static_cast<BuiltInFunctionNode *>(p_call->arguments[0]);
+ MethodInfo mi = GDScriptFunctions::get_info(func->function);
+
+ return_type = _type_from_property(mi.return_val, false);
+
+#ifdef DEBUG_ENABLED
+ // Check all arguments beforehand to solve warnings
+ for (int i = 1; i < p_call->arguments.size(); i++) {
+ _reduce_node_type(p_call->arguments[i]);
+ }
+#endif // DEBUG_ENABLED
+
+ // Check arguments
+
+ is_vararg = mi.flags & METHOD_FLAG_VARARG;
+
+ default_args_count = mi.default_arguments.size();
+ callee_name = mi.name;
+ arg_count -= 1;
+
+ // Check each argument type
+ for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
+ arg_types.push_back(_type_from_property(E->get()));
+ }
+ } break;
+ default: {
+ if (p_call->op == OperatorNode::OP_CALL && p_call->arguments.size() < 2) {
+ _set_error("Parser bug: self method call without enough arguments.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+ int arg_id = p_call->op == OperatorNode::OP_CALL ? 1 : 0;
+
+ if (p_call->arguments[arg_id]->type != Node::TYPE_IDENTIFIER) {
+ _set_error("Parser bug: invalid function call argument.", p_call->line);
+ ERR_FAIL_V(DataType());
+ }
+
+#ifdef DEBUG_ENABLED
+ // Check all arguments beforehand to solve warnings
+ for (int i = arg_id + 1; i < p_call->arguments.size(); i++) {
+ _reduce_node_type(p_call->arguments[i]);
+ }
+#endif // DEBUG_ENABLED
+
+ IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]);
+ callee_name = func_id->name;
+ arg_count -= 1 + arg_id;
+
+ DataType base_type;
+ if (p_call->op == OperatorNode::OP_PARENT_CALL) {
+ base_type = current_class->base_type;
+ } else {
+ base_type = _reduce_node_type(p_call->arguments[0]);
+ }
+
+ if (!base_type.has_type || (base_type.kind == DataType::BUILTIN && base_type.builtin_type == Variant::NIL)) {
+ _mark_line_as_unsafe(p_call->line);
+ return DataType();
+ }
+
+ if (base_type.kind == DataType::BUILTIN) {
+ Variant::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, NULL, 0, err);
+
+ if (check_types) {
+ if (!tmp.has_method(callee_name)) {
+ _set_error("Method '" + callee_name + "' is not declared on base '" + base_type.to_string() + "'.", p_call->line);
+ return DataType();
+ }
+
+ default_args_count = Variant::get_method_default_arguments(base_type.builtin_type, callee_name).size();
+ const Vector<Variant::Type> &var_arg_types = Variant::get_method_argument_types(base_type.builtin_type, callee_name);
+
+ for (int i = 0; i < var_arg_types.size(); i++) {
+ DataType argtype;
+ if (var_arg_types[i] != Variant::NIL) {
+ argtype.has_type = true;
+ argtype.kind = DataType::BUILTIN;
+ argtype.builtin_type = var_arg_types[i];
+ }
+ arg_types.push_back(argtype);
+ }
+ }
+
+ return_type.has_type = true;
+ return_type.kind = DataType::BUILTIN;
+ return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name);
+ break;
+ }
+
+ DataType original_type = base_type;
+ bool is_initializer = callee_name == "new";
+ bool is_static = false;
+ bool valid = false;
+
+ if (is_initializer && original_type.is_meta_type) {
+ // Try to check it as initializer
+ base_type = original_type;
+ callee_name = "_init";
+ base_type.is_meta_type = false;
+
+ valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
+ default_args_count, is_static, is_vararg);
+
+ if (valid) {
+ return_type = original_type;
+ return_type.is_meta_type = false;
+ }
+ }
+
+ if (!valid) {
+ base_type = original_type;
+ return_type = DataType();
+ valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
+ default_args_count, is_static, is_vararg);
+ }
+
+ if (!valid) {
+#ifdef DEBUG_ENABLED
+ if (p_call->arguments[0]->type == Node::TYPE_SELF) {
+ _set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line);
+ return DataType();
+ }
+ DataType tmp_type;
+ valid = _get_member_type(original_type, func_id->name, tmp_type);
+ if (valid) {
+ if (tmp_type.is_constant) {
+ _add_warning(GDScriptWarning::CONSTANT_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
+ } else {
+ _add_warning(GDScriptWarning::PROPERTY_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
+ }
+ }
+ _add_warning(GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->line, callee_name, original_type.to_string());
+ _mark_line_as_unsafe(p_call->line);
+#endif // DEBUG_ENABLED
+ return DataType();
+ }
+
+#ifdef DEBUG_ENABLED
+ if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) {
+ if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) {
+ _set_error("Can't call non-static function from a static function.", p_call->line);
+ return DataType();
+ }
+ }
+
+ if (check_types && !is_static && !is_initializer && base_type.is_meta_type) {
+ _set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
+ return DataType();
+ }
+
+ // Check signal emission for warnings
+ if (callee_name == "emit_signal" && p_call->op == OperatorNode::OP_CALL && p_call->arguments[0]->type == Node::TYPE_SELF && p_call->arguments.size() >= 3 && p_call->arguments[2]->type == Node::TYPE_CONSTANT) {
+ ConstantNode *sig = static_cast<ConstantNode *>(p_call->arguments[2]);
+ String emitted = sig->value.get_type() == Variant::STRING ? sig->value.operator String() : "";
+ for (int i = 0; i < current_class->_signals.size(); i++) {
+ if (current_class->_signals[i].name == emitted) {
+ current_class->_signals.write[i].emissions += 1;
+ break;
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
+ } break;
+ }
+
+ if (!check_types) {
+ return return_type;
+ }
+
+ if (arg_count < arg_types.size() - default_args_count) {
+ _set_error("Too few arguments for '" + callee_name + "()' call. Expected at least " + itos(arg_types.size() - default_args_count) + ".", p_call->line);
+ return return_type;
+ }
+ if (!is_vararg && arg_count > arg_types.size()) {
+ _set_error("Too many arguments for '" + callee_name + "()' call. Expected at most " + itos(arg_types.size()) + ".", p_call->line);
+ return return_type;
+ }
+
+ int arg_diff = p_call->arguments.size() - arg_count;
+ for (int i = arg_diff; i < p_call->arguments.size(); i++) {
+ DataType par_type = _reduce_node_type(p_call->arguments[i]);
+
+ if ((i - arg_diff) >= arg_types.size()) {
+ continue;
+ }
+
+ DataType arg_type = arg_types[i - arg_diff];
+
+ if (!par_type.has_type) {
+ _mark_line_as_unsafe(p_call->line);
+#ifdef DEBUG_ENABLED
+ if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i])));
+ }
+#endif // DEBUG_ENABLED
+ } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) {
+ // Supertypes are acceptable for dynamic compliance
+ if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
+ _set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" +
+ par_type.to_string() + ") doesn't match the function argument's type (" +
+ arg_types[i - arg_diff].to_string() + ").",
+ p_call->line);
+ return DataType();
+ } else {
+ _mark_line_as_unsafe(p_call->line);
+ }
+ } else {
+#ifdef DEBUG_ENABLED
+ if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name);
+ }
+#endif // DEBUG_ENABLED
+ }
+ }
+
+ return return_type;
+}
+
+bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const {
+ DataType base_type = p_base_type;
+
+ // Check classes in current file
+ ClassNode *base = NULL;
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ }
+
+ while (base) {
+ if (base->constant_expressions.has(p_member)) {
+ r_member_type = base->constant_expressions[p_member].expression->get_datatype();
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ for (int i = 0; i < base->variables.size(); i++) {
+ ClassNode::Member m = base->variables[i];
+ if (m.identifier == p_member) {
+ r_member_type = m.data_type;
+ return true;
+ }
+ }
+ } else {
+ for (int i = 0; i < base->subclasses.size(); i++) {
+ ClassNode *c = base->subclasses[i];
+ if (c->name == p_member) {
+ DataType class_type;
+ class_type.has_type = true;
+ class_type.is_constant = true;
+ class_type.is_meta_type = true;
+ class_type.kind = DataType::CLASS;
+ class_type.class_type = c;
+ r_member_type = class_type;
+ return true;
+ }
+ }
+ }
+
+ base_type = base->base_type;
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ } else {
+ break;
+ }
+ }
+
+ Ref<GDScript> gds;
+ if (base_type.kind == DataType::GDSCRIPT) {
+ gds = base_type.script_type;
+ }
+
+ Ref<Script> scr;
+ if (base_type.kind == DataType::SCRIPT) {
+ scr = base_type.script_type;
+ }
+
+ StringName native;
+ if (base_type.kind == DataType::NATIVE) {
+ native = base_type.native_type;
+ }
+
+ // Check GDScripts
+ while (gds.is_valid()) {
+ if (gds->get_constants().has(p_member)) {
+ Variant c = gds->get_constants()[p_member];
+ r_member_type = _type_from_variant(c);
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ if (gds->get_members().has(p_member)) {
+ r_member_type = _type_from_gdtype(gds->get_member_type(p_member));
+ return true;
+ }
+ }
+
+ native = gds->get_instance_base_type();
+ if (gds->get_base_script().is_valid()) {
+ gds = gds->get_base_script();
+ scr = gds->get_base_script();
+ bool is_meta = base_type.is_meta_type;
+ base_type = _type_from_variant(scr.operator Variant());
+ base_type.is_meta_type = is_meta;
+ } else {
+ break;
+ }
+ }
+
+ // Check other script types
+ while (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ if (constants.has(p_member)) {
+ r_member_type = _type_from_variant(constants[p_member]);
+ return true;
+ }
+
+ List<PropertyInfo> properties;
+ scr->get_script_property_list(&properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ r_member_type = _type_from_property(E->get());
+ return true;
+ }
+ }
+
+ base_type = _type_from_variant(scr.operator Variant());
+ native = scr->get_instance_base_type();
+ scr = scr->get_base_script();
+ }
+
+ // Check ClassDB
+ if (!ClassDB::class_exists(native)) {
+ native = "_" + native.operator String();
+ }
+ if (!ClassDB::class_exists(native)) {
+ if (!check_types) return false;
+ ERR_EXPLAIN("Parser bug: Class '" + String(native) + "' not found.");
+ ERR_FAIL_V(false);
+ }
+
+ bool valid = false;
+ ClassDB::get_integer_constant(native, p_member, &valid);
+ if (valid) {
+ DataType ct;
+ ct.has_type = true;
+ ct.is_constant = true;
+ ct.kind = DataType::BUILTIN;
+ ct.builtin_type = Variant::INT;
+ r_member_type = ct;
+ return true;
+ }
+
+ if (!base_type.is_meta_type) {
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(native, &properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ // Check if a getter exists
+ StringName getter_name = ClassDB::get_property_getter(native, p_member);
+ if (getter_name != StringName()) {
+ // Use the getter return type
+#ifdef DEBUG_METHODS_ENABLED
+ MethodBind *getter_method = ClassDB::get_method(native, getter_name);
+ if (getter_method) {
+ r_member_type = _type_from_property(getter_method->get_return_info());
+ } else {
+ r_member_type = DataType();
+ }
+#else
+ r_member_type = DataType();
+#endif
+ } else {
+ r_member_type = _type_from_property(E->get());
+ }
+ return true;
+ }
+ }
+ }
+
+ // If the base is a script, it might be trying to access members of the Script class itself
+ if (p_base_type.is_meta_type && (p_base_type.kind == DataType::SCRIPT || p_base_type.kind == DataType::GDSCRIPT)) {
+ native = p_base_type.script_type->get_class_name();
+ ClassDB::get_integer_constant(native, p_member, &valid);
+ if (valid) {
+ DataType ct;
+ ct.has_type = true;
+ ct.is_constant = true;
+ ct.kind = DataType::BUILTIN;
+ ct.builtin_type = Variant::INT;
+ r_member_type = ct;
+ return true;
+ }
+
+ List<PropertyInfo> properties;
+ ClassDB::get_property_list(native, &properties);
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (E->get().name == p_member) {
+ // Check if a getter exists
+ StringName getter_name = ClassDB::get_property_getter(native, p_member);
+ if (getter_name != StringName()) {
+ // Use the getter return type
+#ifdef DEBUG_METHODS_ENABLED
+ MethodBind *getter_method = ClassDB::get_method(native, getter_name);
+ if (getter_method) {
+ r_member_type = _type_from_property(getter_method->get_return_info());
+ } else {
+ r_member_type = DataType();
+ }
+#else
+ r_member_type = DataType();
+#endif
+ } else {
+ r_member_type = _type_from_property(E->get());
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line) {
+
+ if (p_base_type && !p_base_type->has_type) {
+ return DataType();
+ }
+
+ DataType base_type;
+
+ // Check classes in current file
+ ClassNode *base = NULL;
+ if (!p_base_type) {
+ base = current_class;
+ base_type.has_type = true;
+ base_type.is_constant = true;
+ base_type.kind = DataType::CLASS;
+ base_type.class_type = base;
+ } else {
+ base_type = DataType(*p_base_type);
+ if (base_type.kind == DataType::CLASS) {
+ base = base_type.class_type;
+ }
+ }
+
+ 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;
+ return member_type;
+ }
+ }
+
+ if (_get_member_type(base_type, p_identifier, member_type)) {
+ return member_type;
+ }
+
+ if (!p_base_type) {
+ // Possibly this is a global, check before failing
+
+ if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ if (Engine::get_singleton()->has_singleton(p_identifier) || Engine::get_singleton()->has_singleton("_" + p_identifier.operator String())) {
+ result.is_meta_type = false;
+ }
+ result.kind = DataType::NATIVE;
+ result.native_type = p_identifier;
+ return result;
+ }
+
+ ClassNode *outer_class = current_class;
+ while (outer_class) {
+ if (outer_class->name == p_identifier) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class;
+ return result;
+ }
+ if (outer_class->constant_expressions.has(p_identifier)) {
+ return outer_class->constant_expressions[p_identifier].type;
+ }
+ for (int i = 0; i < outer_class->subclasses.size(); i++) {
+ if (outer_class->subclasses[i] == current_class) {
+ continue;
+ }
+ if (outer_class->subclasses[i]->name == p_identifier) {
+ DataType result;
+ result.has_type = true;
+ result.is_constant = true;
+ result.is_meta_type = true;
+ result.kind = DataType::CLASS;
+ result.class_type = outer_class->subclasses[i];
+ return result;
+ }
+ }
+ outer_class = outer_class->owner;
+ }
+
+ if (ScriptServer::is_global_class(p_identifier)) {
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ if (scr.is_valid()) {
+ DataType result;
+ result.has_type = true;
+ result.script_type = scr;
+ result.is_meta_type = true;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Class '" + p_identifier + "' could not be fully loaded (script error or cyclic inheritance).");
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ return result;
+ }
+ _set_error("Class '" + p_identifier + "' was found in global scope but its script could not be loaded.");
+ return DataType();
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(p_identifier)) {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
+ Variant g = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return _type_from_variant(g);
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
+ Variant g = GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier];
+ return _type_from_variant(g);
+ }
+
+ // Non-tool singletons aren't loaded, check project settings
+ List<PropertyInfo> props;
+ ProjectSettings::get_singleton()->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String s = E->get().name;
+ if (!s.begins_with("autoload/")) {
+ continue;
+ }
+ String name = s.get_slice("/", 1);
+ if (name == p_identifier) {
+ String script = ProjectSettings::get_singleton()->get(s);
+ if (script.begins_with("*")) {
+ script = script.right(1);
+ }
+ if (!script.begins_with("res://")) {
+ script = "res://" + script;
+ }
+ Ref<Script> singleton = ResourceLoader::load(script);
+ if (singleton.is_valid()) {
+ DataType result;
+ result.has_type = true;
+ result.script_type = singleton;
+
+ Ref<GDScript> gds = singleton;
+ if (gds.is_valid()) {
+ if (!gds->is_valid()) {
+ _set_error("Couldn't fully load singleton script '" + p_identifier + "' (possible cyclic reference or parse error).", p_line);
+ return DataType();
+ }
+ result.kind = DataType::GDSCRIPT;
+ } else {
+ result.kind = DataType::SCRIPT;
+ }
+ }
+ }
+ }
+
+ // This means looking in the current class, which type is always known
+ _set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line);
+ }
+
+#ifdef DEBUG_ENABLED
+ {
+ DataType tmp_type;
+ List<DataType> arg_types;
+ int argcount;
+ bool _static;
+ bool vararg;
+ if (_get_function_signature(base_type, p_identifier, tmp_type, arg_types, argcount, _static, vararg)) {
+ _add_warning(GDScriptWarning::FUNCTION_USED_AS_PROPERTY, p_line, p_identifier.operator String(), base_type.to_string());
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ _mark_line_as_unsafe(p_line);
+ return DataType();
+}
+
+void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
+
+ _mark_line_as_safe(p_class->line);
+
+ // Constants
+ for (Map<StringName, ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
+ ClassNode::Constant &c = E->get();
+ _mark_line_as_safe(c.expression->line);
+ DataType cont = _resolve_type(c.type, c.expression->line);
+ DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line);
+
+ if (!_is_type_compatible(cont, expr)) {
+ _set_error("Constant value type (" + expr.to_string() + ") is not compatible with declared type (" + cont.to_string() + ").",
+ c.expression->line);
+ return;
+ }
+
+ expr.is_constant = true;
+ c.type = expr;
+ c.expression->set_datatype(expr);
+ }
+
+ // Function declarations
+ for (int i = 0; i < p_class->static_functions.size(); i++) {
+ _check_function_types(p_class->static_functions[i]);
+ if (error_set) return;
+ }
+
+ for (int i = 0; i < p_class->functions.size(); i++) {
+ _check_function_types(p_class->functions[i]);
+ if (error_set) return;
+ }
+
+ // Class variables
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ ClassNode::Member &v = p_class->variables.write[i];
+
+ DataType tmp;
+ if (_get_member_type(p_class->base_type, v.identifier, tmp)) {
+ _set_error("Member '" + String(v.identifier) + "' already exists in parent class.", v.line);
+ return;
+ }
+
+ _mark_line_as_safe(v.line);
+ v.data_type = _resolve_type(v.data_type, v.line);
+
+ if (v.expression) {
+ DataType expr_type = _reduce_node_type(v.expression);
+
+ if (!_is_type_compatible(v.data_type, expr_type)) {
+ // Try supertype test
+ if (_is_type_compatible(expr_type, v.data_type)) {
+ _mark_line_as_unsafe(v.line);
+ } else {
+ // Try with implicit conversion
+ if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) {
+ _set_error("Assigned expression type (" + expr_type.to_string() + ") doesn't match the variable's type (" +
+ v.data_type.to_string() + ").",
+ v.line);
+ return;
+ }
+
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = v.line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = v.line;
+ tgt_type->value = (int)v.data_type.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = v.line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(v.expression);
+ convert_call->arguments.push_back(tgt_type);
+
+ v.expression = convert_call;
+ v.initial_assignment->arguments.write[1] = convert_call;
+ }
+ }
+
+ if (v.data_type.infer_type) {
+ if (!expr_type.has_type) {
+ _set_error("Assigned value does not have a set type, variable type cannot be inferred.", v.line);
+ return;
+ }
+ v.data_type = expr_type;
+ v.data_type.is_constant = false;
+ }
+ } else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) {
+ // Create default value based on the type
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->line = v.line;
+ id->name = v.identifier;
+
+ ConstantNode *init = alloc_node<ConstantNode>();
+ init->line = v.line;
+ Variant::CallError err;
+ init->value = Variant::construct(v.data_type.builtin_type, NULL, 0, err);
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->line = v.line;
+ op->op = OperatorNode::OP_INIT_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(init);
+
+ p_class->initializer->statements.push_front(op);
+ v.initial_assignment = op;
+#ifdef DEBUG_ENABLED
+ NewLineNode *nl = alloc_node<NewLineNode>();
+ nl->line = v.line - 1;
+ p_class->initializer->statements.push_front(nl);
+#endif
+ }
+
+ // Check export hint
+ if (v.data_type.has_type && v._export.type != Variant::NIL) {
+ DataType export_type = _type_from_property(v._export);
+ if (!_is_type_compatible(v.data_type, export_type, true)) {
+ _set_error("Export hint type (" + export_type.to_string() + ") doesn't match the variable's type (" +
+ v.data_type.to_string() + ").",
+ v.line);
+ return;
+ }
+ }
+
+ // Setter and getter
+ if (v.setter == StringName() && v.getter == StringName()) continue;
+
+ bool found_getter = false;
+ bool found_setter = false;
+ for (int j = 0; j < p_class->functions.size(); j++) {
+ if (v.setter == p_class->functions[j]->name) {
+ found_setter = true;
+ FunctionNode *setter = p_class->functions[j];
+
+ if (setter->arguments.size() != 1) {
+ _set_error("Setter function needs to receive exactly 1 argument. See '" + setter->name +
+ "()' definition at line " + itos(setter->line) + ".",
+ v.line);
+ return;
+ }
+ if (!_is_type_compatible(v.data_type, setter->argument_types[0])) {
+ _set_error("Setter argument type (" + setter->argument_types[0].to_string() +
+ ") doesn't match the variable's type (" + v.data_type.to_string() + "). See '" +
+ setter->name + "()' definition at line " + itos(setter->line) + ".",
+ v.line);
+ return;
+ }
+ continue;
+ }
+ if (v.getter == p_class->functions[j]->name) {
+ found_getter = true;
+ FunctionNode *getter = p_class->functions[j];
+
+ if (getter->arguments.size() != 0) {
+ _set_error("Getter function can't receive arguments. See '" + getter->name +
+ "()' definition at line " + itos(getter->line) + ".",
+ v.line);
+ return;
+ }
+ if (!_is_type_compatible(v.data_type, getter->get_datatype())) {
+ _set_error("Getter return type (" + getter->get_datatype().to_string() +
+ ") doesn't match the variable's type (" + v.data_type.to_string() +
+ "). See '" + getter->name + "()' definition at line " + itos(getter->line) + ".",
+ v.line);
+ return;
+ }
+ }
+ if (found_getter && found_setter) break;
+ }
+
+ if ((found_getter || v.getter == StringName()) && (found_setter || v.setter == StringName())) continue;
+
+ // Check for static functions
+ for (int j = 0; j < p_class->static_functions.size(); j++) {
+ if (v.setter == p_class->static_functions[j]->name) {
+ FunctionNode *setter = p_class->static_functions[j];
+ _set_error("Setter can't be a static function. See '" + setter->name + "()' definition at line " + itos(setter->line) + ".", v.line);
+ return;
+ }
+ if (v.getter == p_class->static_functions[j]->name) {
+ FunctionNode *getter = p_class->static_functions[j];
+ _set_error("Getter can't be a static function. See '" + getter->name + "()' definition at line " + itos(getter->line) + ".", v.line);
+ return;
+ }
+ }
+
+ if (!found_setter && v.setter != StringName()) {
+ _set_error("Setter function is not defined.", v.line);
+ return;
+ }
+
+ if (!found_getter && v.getter != StringName()) {
+ _set_error("Getter function is not defined.", v.line);
+ return;
+ }
+ }
+
+ // Inner classes
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ current_class = p_class->subclasses[i];
+ _check_class_level_types(current_class);
+ if (error_set) return;
+ current_class = p_class;
+ }
+}
+
+void GDScriptParser::_check_function_types(FunctionNode *p_function) {
+
+ p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
+
+ // Arguments
+ int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
+ for (int i = 0; i < p_function->arguments.size(); i++) {
+ if (i < defaults_ofs) {
+ p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line);
+ } else {
+ if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) {
+ _set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column);
+ return;
+ }
+
+ OperatorNode *op = static_cast<OperatorNode *>(p_function->default_values[i - defaults_ofs]);
+
+ if (op->op != OperatorNode::OP_ASSIGN || op->arguments.size() != 2) {
+ _set_error("Parser bug: invalid argument default value operation.", p_function->line);
+ return;
+ }
+
+ DataType def_type = _reduce_node_type(op->arguments[1]);
+
+ if (p_function->argument_types[i].infer_type) {
+ def_type.is_constant = false;
+ p_function->argument_types.write[i] = def_type;
+ } else {
+ p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
+
+ if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
+ String arg_name = p_function->arguments[i];
+ _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
+ arg_name + "' (" + p_function->arguments[i] + ")",
+ p_function->line);
+ }
+ }
+ }
+#ifdef DEBUG_ENABLED
+ if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) {
+ _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String());
+ }
+#endif // DEBUG_ENABLED
+ }
+
+ if (!(p_function->name == "_init")) {
+ // Signature for the initializer may vary
+#ifdef DEBUG_ENABLED
+ DataType return_type;
+ List<DataType> arg_types;
+ int default_arg_count = 0;
+ bool _static = false;
+ bool vararg = false;
+
+ DataType base_type = current_class->base_type;
+ if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) {
+ bool valid = _static == p_function->_static;
+ valid = valid && return_type == p_function->return_type;
+ int argsize_diff = p_function->arguments.size() - arg_types.size();
+ valid = valid && argsize_diff >= 0;
+ valid = valid && p_function->default_values.size() >= default_arg_count + argsize_diff;
+ int i = 0;
+ for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) {
+ valid = valid && E->get() == p_function->argument_types[i++];
+ }
+
+ if (!valid) {
+ String parent_signature = return_type.has_type ? return_type.to_string() : "Variant";
+ if (parent_signature == "null") {
+ parent_signature = "void";
+ }
+ parent_signature += " " + p_function->name + "(";
+ if (arg_types.size()) {
+ int i = 0;
+ for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) {
+ if (E != arg_types.front()) {
+ parent_signature += ", ";
+ }
+ String arg = E->get().to_string();
+ if (arg == "null" || arg == "var") {
+ arg = "Variant";
+ }
+ parent_signature += arg;
+ if (i == arg_types.size() - default_arg_count) {
+ parent_signature += "=default";
+ }
+
+ i++;
+ }
+ }
+ parent_signature += ")";
+ _set_error("Function signature doesn't match the parent. Parent signature is: '" + parent_signature + "'.", p_function->line);
+ return;
+ }
+ }
+#endif // DEBUG_ENABLED
+ } else {
+ if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
+ _set_error("Constructor cannot return a value.", p_function->line);
+ return;
+ }
+ }
+
+ if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
+ if (!p_function->body->has_return) {
+ _set_error("Non-void function must return a value in all possible paths.", p_function->line);
+ return;
+ }
+ }
+
+ if (p_function->has_yield) {
+ // yield() will make the function return a GDScriptFunctionState, so the type is ambiguous
+ p_function->return_type.has_type = false;
+ p_function->return_type.may_yield = true;
+ }
+}
+
+void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
+
+ // Function blocks
+ for (int i = 0; i < p_class->static_functions.size(); i++) {
+ current_function = p_class->static_functions[i];
+ current_block = current_function->body;
+ _mark_line_as_safe(current_function->line);
+ _check_block_types(current_block);
+ current_block = NULL;
+ current_function = NULL;
+ if (error_set) return;
+ }
+
+ for (int i = 0; i < p_class->functions.size(); i++) {
+ current_function = p_class->functions[i];
+ current_block = current_function->body;
+ _mark_line_as_safe(current_function->line);
+ _check_block_types(current_block);
+ current_block = NULL;
+ current_function = NULL;
+ if (error_set) return;
+ }
+
+#ifdef DEBUG_ENABLED
+ // Warnings
+ for (int i = 0; i < p_class->variables.size(); i++) {
+ if (p_class->variables[i].usages == 0) {
+ _add_warning(GDScriptWarning::UNUSED_CLASS_VARIABLE, p_class->variables[i].line, p_class->variables[i].identifier);
+ }
+ }
+ for (int i = 0; i < p_class->_signals.size(); i++) {
+ if (p_class->_signals[i].emissions == 0) {
+ _add_warning(GDScriptWarning::UNUSED_SIGNAL, p_class->_signals[i].line, p_class->_signals[i].name);
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ // Inner classes
+ for (int i = 0; i < p_class->subclasses.size(); i++) {
+ current_class = p_class->subclasses[i];
+ _check_class_blocks_types(current_class);
+ if (error_set) return;
+ current_class = p_class;
+ }
+}
+
+#ifdef DEBUG_ENABLED
+static String _find_function_name(const GDScriptParser::OperatorNode *p_call) {
+ switch (p_call->arguments[0]->type) {
+ case GDScriptParser::Node::TYPE_TYPE: {
+ return Variant::get_type_name(static_cast<GDScriptParser::TypeNode *>(p_call->arguments[0])->vtype);
+ } break;
+ case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
+ return GDScriptFunctions::get_func_name(static_cast<GDScriptParser::BuiltInFunctionNode *>(p_call->arguments[0])->function);
+ } break;
+ default: {
+ int id_index = p_call->op == GDScriptParser::OperatorNode::OP_PARENT_CALL ? 0 : 1;
+ if (p_call->arguments.size() > id_index && p_call->arguments[id_index]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
+ return static_cast<GDScriptParser::IdentifierNode *>(p_call->arguments[id_index])->name;
+ }
+ } break;
+ }
+ return String();
+}
+#endif // DEBUG_ENABLED
+
+void GDScriptParser::_check_block_types(BlockNode *p_block) {
+
+ Node *last_var_assign = NULL;
+
+ // Check each statement
+ for (List<Node *>::Element *E = p_block->statements.front(); E; E = E->next()) {
+ Node *statement = E->get();
+ switch (statement->type) {
+ case Node::TYPE_NEWLINE:
+ case Node::TYPE_BREAKPOINT:
+ case Node::TYPE_ASSERT: {
+ // Nothing to do
+ } break;
+ case Node::TYPE_LOCAL_VAR: {
+ LocalVarNode *lv = static_cast<LocalVarNode *>(statement);
+ lv->datatype = _resolve_type(lv->datatype, lv->line);
+ _mark_line_as_safe(lv->line);
+
+ last_var_assign = lv->assign;
+ if (lv->assign) {
+ DataType assign_type = _reduce_node_type(lv->assign);
+#ifdef DEBUG_ENABLED
+ if (assign_type.has_type && assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) {
+ if (lv->assign->type == Node::TYPE_OPERATOR) {
+ OperatorNode *call = static_cast<OperatorNode *>(lv->assign);
+ if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
+ _add_warning(GDScriptWarning::VOID_ASSIGNMENT, lv->line, _find_function_name(call));
+ }
+ }
+ }
+ if (lv->datatype.has_type && assign_type.may_yield && lv->assign->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, lv->line, _find_function_name(static_cast<OperatorNode *>(lv->assign)));
+ }
+#endif // DEBUG_ENABLED
+
+ if (!_is_type_compatible(lv->datatype, assign_type)) {
+ // Try supertype test
+ if (_is_type_compatible(assign_type, lv->datatype)) {
+ _mark_line_as_unsafe(lv->line);
+ } else {
+ // Try implict conversion
+ if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) {
+ _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" +
+ lv->datatype.to_string() + ").",
+ lv->line);
+ return;
+ }
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = lv->line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = lv->line;
+ tgt_type->value = (int)lv->datatype.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = lv->line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(lv->assign);
+ convert_call->arguments.push_back(tgt_type);
+
+ lv->assign = convert_call;
+ lv->assign_op->arguments.write[1] = convert_call;
+#ifdef DEBUG_ENABLED
+ if (lv->datatype.builtin_type == Variant::INT && assign_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, lv->line);
+ }
+#endif // DEBUG_ENABLED
+ }
+ }
+ if (lv->datatype.infer_type) {
+ if (!assign_type.has_type) {
+ _set_error("Assigned value does not have a set type, variable type cannot be inferred.", lv->line);
+ return;
+ }
+ lv->datatype = assign_type;
+ lv->datatype.is_constant = false;
+ }
+ if (lv->datatype.has_type && !assign_type.has_type) {
+ _mark_line_as_unsafe(lv->line);
+ }
+ }
+ } break;
+ case Node::TYPE_OPERATOR: {
+ OperatorNode *op = static_cast<OperatorNode *>(statement);
+
+ switch (op->op) {
+ case OperatorNode::OP_ASSIGN:
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_ASSIGN_BIT_XOR: {
+ if (op->arguments.size() < 2) {
+ _set_error("Parser bug: operation without enough arguments.", op->line, op->column);
+ return;
+ }
+
+ if (op->arguments[1] == last_var_assign) {
+ // Assignment was already checked
+ break;
+ }
+
+ _mark_line_as_safe(op->line);
+
+ DataType lh_type = _reduce_node_type(op->arguments[0]);
+
+ if (error_set) {
+ return;
+ }
+
+ if (!lh_type.has_type) {
+ if (op->arguments[0]->type == Node::TYPE_OPERATOR) {
+ _mark_line_as_unsafe(op->line);
+ }
+ } else if (lh_type.is_constant) {
+ _set_error("Cannot assign a new value to a constant.", op->line);
+ return;
+ }
+
+ DataType rh_type;
+ if (op->op != OperatorNode::OP_ASSIGN) {
+ // Validate operation
+ DataType arg_type = _reduce_node_type(op->arguments[1]);
+ if (!arg_type.has_type) {
+ _mark_line_as_unsafe(op->line);
+ break;
+ }
+
+ Variant::Operator oper = _get_variant_operation(op->op);
+ bool valid = false;
+ rh_type = _get_operation_type(oper, lh_type, arg_type, valid);
+
+ if (!valid) {
+ _set_error("Invalid operand types ('" + lh_type.to_string() + "' and '" + arg_type.to_string() +
+ "') to assignment operator '" + Variant::get_operator_name(oper) + "'.",
+ op->line);
+ return;
+ }
+ } else {
+ rh_type = _reduce_node_type(op->arguments[1]);
+ }
+#ifdef DEBUG_ENABLED
+ if (rh_type.has_type && rh_type.kind == DataType::BUILTIN && rh_type.builtin_type == Variant::NIL) {
+ if (op->arguments[1]->type == Node::TYPE_OPERATOR) {
+ OperatorNode *call = static_cast<OperatorNode *>(op->arguments[1]);
+ if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
+ _add_warning(GDScriptWarning::VOID_ASSIGNMENT, op->line, _find_function_name(call));
+ }
+ }
+ }
+ if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) {
+ _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1])));
+ }
+#endif // DEBUG_ENABLED
+
+ if (!_is_type_compatible(lh_type, rh_type)) {
+ // Try supertype test
+ if (_is_type_compatible(rh_type, lh_type)) {
+ _mark_line_as_unsafe(op->line);
+ } else {
+ // Try implict conversion
+ if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) {
+ _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" +
+ lh_type.to_string() + ").",
+ op->line);
+ return;
+ }
+ // Replace assigment with implict conversion
+ BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
+ convert->line = op->line;
+ convert->function = GDScriptFunctions::TYPE_CONVERT;
+
+ ConstantNode *tgt_type = alloc_node<ConstantNode>();
+ tgt_type->line = op->line;
+ tgt_type->value = (int)lh_type.builtin_type;
+
+ OperatorNode *convert_call = alloc_node<OperatorNode>();
+ convert_call->line = op->line;
+ convert_call->op = OperatorNode::OP_CALL;
+ convert_call->arguments.push_back(convert);
+ convert_call->arguments.push_back(op->arguments[1]);
+ convert_call->arguments.push_back(tgt_type);
+
+ op->arguments.write[1] = convert_call;
+#ifdef DEBUG_ENABLED
+ if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) {
+ _add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line);
+ }
+#endif // DEBUG_ENABLED
+ }
+ }
+ if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) {
+ _mark_line_as_unsafe(op->line);
+ }
+ } break;
+ case OperatorNode::OP_CALL:
+ case OperatorNode::OP_PARENT_CALL: {
+ _mark_line_as_safe(op->line);
+ DataType func_type = _reduce_function_call_type(op);
+#ifdef DEBUG_ENABLED
+ if (func_type.has_type && (func_type.kind != DataType::BUILTIN || func_type.builtin_type != Variant::NIL)) {
+ // Figure out function name for warning
+ String func_name = _find_function_name(op);
+ if (func_name.empty()) {
+ func_name == "<undetected name>";
+ }
+ _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name);
+ }
+#endif // DEBUG_ENABLED
+ if (error_set) return;
+ } break;
+ case OperatorNode::OP_YIELD: {
+ _mark_line_as_safe(op->line);
+ _reduce_node_type(op);
+ } break;
+ default: {
+ _mark_line_as_safe(op->line);
+ _reduce_node_type(op); // Test for safety anyway
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
+ }
+ }
+ } break;
+ case Node::TYPE_CONTROL_FLOW: {
+ ControlFlowNode *cf = static_cast<ControlFlowNode *>(statement);
+ _mark_line_as_safe(cf->line);
+
+ switch (cf->cf_type) {
+ case ControlFlowNode::CF_RETURN: {
+ DataType function_type = current_function->get_datatype();
+
+ DataType ret_type;
+ if (cf->arguments.size() > 0) {
+ ret_type = _reduce_node_type(cf->arguments[0]);
+ if (error_set) {
+ return;
+ }
+ }
+
+ if (!function_type.has_type) break;
+
+ if (function_type.kind == DataType::BUILTIN && function_type.builtin_type == Variant::NIL) {
+ // Return void, should not have arguments
+ if (cf->arguments.size() > 0) {
+ _set_error("Void function cannot return a value.", cf->line, cf->column);
+ return;
+ }
+ } else {
+ // Return something, cannot be empty
+ if (cf->arguments.size() == 0) {
+ _set_error("Non-void function must return a value.", cf->line, cf->column);
+ return;
+ }
+
+ if (!_is_type_compatible(function_type, ret_type)) {
+ _set_error("Returned value type (" + ret_type.to_string() + ") doesn't match the function return type (" +
+ function_type.to_string() + ").",
+ cf->line, cf->column);
+ return;
+ }
+ }
+ } break;
+ case ControlFlowNode::CF_MATCH: {
+ MatchNode *match_node = cf->match;
+ _transform_match_statment(match_node);
+ } break;
+ default: {
+ if (cf->body_else) {
+ _mark_line_as_safe(cf->body_else->line);
+ }
+ for (int i = 0; i < cf->arguments.size(); i++) {
+ _reduce_node_type(cf->arguments[i]);
+ }
+ } break;
+ }
+ } break;
+ case Node::TYPE_CONSTANT: {
+ ConstantNode *cn = static_cast<ConstantNode *>(statement);
+ // Strings are fine since they can be multiline comments
+ if (cn->value.get_type() == Variant::STRING) {
+ break;
+ }
+ } // falthrough
+ default: {
+ _mark_line_as_safe(statement->line);
+ _reduce_node_type(statement); // Test for safety anyway
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
+ }
+ }
+ }
+
+ // Parse sub blocks
+ for (int i = 0; i < p_block->sub_blocks.size(); i++) {
+ current_block = p_block->sub_blocks[i];
+ _check_block_types(current_block);
+ current_block = p_block;
+ if (error_set) return;
+ }
+
+#ifdef DEBUG_ENABLED
+ // Warnings check
+ for (Map<StringName, LocalVarNode *>::Element *E = p_block->variables.front(); E; E = E->next()) {
+ LocalVarNode *lv = E->get();
+ if (!lv->name.operator String().begins_with("_")) {
+ if (lv->usages == 0) {
+ _add_warning(GDScriptWarning::UNUSED_VARIABLE, lv->line, lv->name);
+ } else if (lv->assignments == 0) {
+ _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE, lv->line, lv->name);
+ }
+ }
+ }
+#endif // DEBUG_ENABLED
+}
+
void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) {
if (error_set)
@@ -4407,6 +7910,56 @@ void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column)
error_set = true;
}
+#ifdef DEBUG_ENABLED
+void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ Vector<String> symbols;
+ if (!p_symbol1.empty()) {
+ symbols.push_back(p_symbol1);
+ }
+ if (!p_symbol2.empty()) {
+ symbols.push_back(p_symbol2);
+ }
+ if (!p_symbol3.empty()) {
+ symbols.push_back(p_symbol3);
+ }
+ if (!p_symbol4.empty()) {
+ symbols.push_back(p_symbol4);
+ }
+ _add_warning(p_code, p_line, symbols);
+}
+
+void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
+ if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
+ return;
+ }
+ String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
+ if (tokenizer->get_warning_global_skips().has(warn_name)) {
+ return;
+ }
+ if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
+ return;
+ }
+
+ GDScriptWarning warn;
+ warn.code = (GDScriptWarning::Code)p_code;
+ warn.symbols = p_symbols;
+ warn.line = p_line == -1 ? tokenizer->get_token_line() : p_line;
+
+ List<GDScriptWarning>::Element *before = NULL;
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
+ if (E->get().line > warn.line) {
+ break;
+ }
+ before = E;
+ }
+ if (before) {
+ warnings.insert_after(before, warn);
+ } else {
+ warnings.push_front(warn);
+ }
+}
+#endif // DEBUG_ENABLED
+
String GDScriptParser::get_error() const {
return error;
@@ -4440,10 +7993,70 @@ Error GDScriptParser::_parse(const String &p_base_path) {
_set_error("Parse Error: " + tokenizer->get_token_error());
}
+ if (error_set && !for_completion) {
+ return ERR_PARSE_ERROR;
+ }
+
+ _determine_inheritance(main_class);
+
+ if (error_set) {
+ return ERR_PARSE_ERROR;
+ }
+
+ current_class = main_class;
+ current_function = NULL;
+ current_block = NULL;
+#ifdef DEBUG_ENABLED
+ if (for_completion) check_types = false;
+#else
+ check_types = false;
+#endif
+
+ // Resolve all class-level stuff before getting into function blocks
+ _check_class_level_types(main_class);
+
if (error_set) {
+ return ERR_PARSE_ERROR;
+ }
+ // Resolve the function blocks
+ _check_class_blocks_types(main_class);
+
+ if (error_set) {
return ERR_PARSE_ERROR;
}
+
+#ifdef DEBUG_ENABLED
+ // Resolve warning ignores
+ Vector<Pair<int, String> > warning_skips = tokenizer->get_warning_skips();
+ bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize();
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E;) {
+ GDScriptWarning &w = E->get();
+ int skip_index = -1;
+ for (int i = 0; i < warning_skips.size(); i++) {
+ if (warning_skips[i].first >= w.line) {
+ break;
+ }
+ skip_index = i;
+ }
+ List<GDScriptWarning>::Element *next = E->next();
+ bool erase = false;
+ if (skip_index != -1) {
+ if (warning_skips[skip_index].second == GDScriptWarning::get_name_from_code(w.code).to_lower()) {
+ erase = true;
+ }
+ warning_skips.remove(skip_index);
+ }
+ if (erase) {
+ warnings.erase(E);
+ } else if (warning_is_error) {
+ _set_error(w.get_message() + " (warning treated as error)", w.line);
+ return ERR_PARSE_ERROR;
+ }
+ E = next;
+ }
+#endif // DEBUG_ENABLED
+
return OK;
}
@@ -4461,7 +8074,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St
return ret;
}
-Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion) {
+Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) {
clear();
@@ -4471,6 +8084,9 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo
validating = p_just_validate;
for_completion = p_for_completion;
+#ifdef DEBUG_ENABLED
+ safe_lines = r_safe_lines;
+#endif // DEBUG_ENABLED
tokenizer = tt;
Error ret = _parse(p_base_path);
memdelete(tt);
@@ -4523,7 +8139,11 @@ void GDScriptParser::clear() {
pending_newline = -1;
parenthesis = 0;
current_export.type = Variant::NIL;
+ check_types = true;
error = "";
+#ifdef DEBUG_ENABLED
+ safe_lines = NULL;
+#endif // DEBUG_ENABLED
}
GDScriptParser::CompletionType GDScriptParser::get_completion_type() {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index b88a59537c..3c51e3f372 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -37,8 +37,71 @@
#include "object.h"
#include "script_language.h"
+struct GDScriptDataType;
+struct GDScriptWarning;
+
class GDScriptParser {
public:
+ struct ClassNode;
+
+ struct DataType {
+ enum {
+ BUILTIN,
+ NATIVE,
+ SCRIPT,
+ GDSCRIPT,
+ CLASS,
+ UNRESOLVED
+ } kind;
+
+ bool has_type;
+ bool is_constant;
+ bool is_meta_type; // Whether the value can be used as a type
+ bool infer_type;
+ bool may_yield; // For function calls
+
+ Variant::Type builtin_type;
+ StringName native_type;
+ Ref<Script> script_type;
+ ClassNode *class_type;
+
+ String to_string() const;
+
+ bool operator==(const DataType &other) const {
+ if (!has_type || !other.has_type) {
+ return true; // Can be considered equal for parsing purpose
+ }
+ if (kind != other.kind) {
+ return false;
+ }
+ switch (kind) {
+ case BUILTIN: {
+ return builtin_type == other.builtin_type;
+ } break;
+ case NATIVE: {
+ return native_type == other.native_type;
+ } break;
+ case GDSCRIPT:
+ case SCRIPT: {
+ return script_type == other.script_type;
+ } break;
+ case CLASS: {
+ return class_type == other.class_type;
+ } break;
+ }
+ return false;
+ }
+
+ DataType() :
+ has_type(false),
+ is_constant(false),
+ is_meta_type(false),
+ infer_type(false),
+ may_yield(false),
+ builtin_type(Variant::NIL),
+ class_type(NULL) {}
+ };
+
struct Node {
enum Type {
@@ -55,6 +118,7 @@ public:
TYPE_OPERATOR,
TYPE_CONTROL_FLOW,
TYPE_LOCAL_VAR,
+ TYPE_CAST,
TYPE_ASSERT,
TYPE_BREAKPOINT,
TYPE_NEWLINE,
@@ -65,11 +129,17 @@ public:
int column;
Type type;
+ virtual DataType get_datatype() const { return DataType(); }
+ virtual void set_datatype(const DataType &p_datatype) {}
+
virtual ~Node() {}
};
struct FunctionNode;
struct BlockNode;
+ struct ConstantNode;
+ struct LocalVarNode;
+ struct OperatorNode;
struct ClassNode : public Node {
@@ -78,6 +148,8 @@ public:
bool extends_used;
StringName extends_file;
Vector<StringName> extends_class;
+ DataType base_type;
+ String icon_path;
struct Member {
PropertyInfo _export;
@@ -85,25 +157,30 @@ public:
Variant default_value;
#endif
StringName identifier;
+ DataType data_type;
StringName setter;
StringName getter;
int line;
Node *expression;
+ OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode;
+ int usages;
};
struct Constant {
- StringName identifier;
Node *expression;
+ DataType type;
};
struct Signal {
StringName name;
Vector<StringName> arguments;
+ int emissions;
+ int line;
};
Vector<ClassNode *> subclasses;
Vector<Member> variables;
- Vector<Constant> constant_expressions;
+ Map<StringName, Constant> constant_expressions;
Vector<FunctionNode *> functions;
Vector<FunctionNode *> static_functions;
Vector<Signal> _signals;
@@ -126,15 +203,27 @@ public:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
+ bool has_yield;
+ bool has_unreachable_code;
StringName name;
+ DataType return_type;
Vector<StringName> arguments;
+ Vector<DataType> argument_types;
Vector<Node *> default_values;
BlockNode *body;
+#ifdef DEBUG_ENABLED
+ Vector<int> arguments_usage;
+#endif // DEBUG_ENABLED
+
+ virtual DataType get_datatype() const { return return_type; }
+ virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
FunctionNode() {
type = TYPE_FUNCTION;
_static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ has_yield = false;
+ has_unreachable_code = false;
}
};
@@ -142,10 +231,9 @@ public:
ClassNode *parent_class;
BlockNode *parent_block;
- Map<StringName, int> locals;
List<Node *> statements;
- Vector<StringName> variables;
- Vector<int> variable_lines;
+ Map<StringName, LocalVarNode *> variables;
+ bool has_return;
Node *if_condition; //tiny hack to improve code completion on if () blocks
@@ -158,6 +246,7 @@ public:
end_line = -1;
parent_block = NULL;
parent_class = NULL;
+ has_return = false;
}
};
@@ -174,28 +263,55 @@ public:
struct IdentifierNode : public Node {
StringName name;
- IdentifierNode() { type = TYPE_IDENTIFIER; }
+ BlockNode *declared_block; // Simplify lookup by checking if it is declared locally
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ IdentifierNode() {
+ type = TYPE_IDENTIFIER;
+ declared_block = NULL;
+ }
};
struct LocalVarNode : public Node {
StringName name;
Node *assign;
+ OperatorNode *assign_op;
+ int assignments;
+ int usages;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
LocalVarNode() {
type = TYPE_LOCAL_VAR;
assign = NULL;
+ assign_op = NULL;
+ assignments = 0;
+ usages = 0;
}
};
struct ConstantNode : public Node {
Variant value;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
ConstantNode() { type = TYPE_CONSTANT; }
};
struct ArrayNode : public Node {
Vector<Node *> elements;
- ArrayNode() { type = TYPE_ARRAY; }
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ ArrayNode() {
+ type = TYPE_ARRAY;
+ datatype.has_type = true;
+ datatype.kind = DataType::BUILTIN;
+ datatype.builtin_type = Variant::ARRAY;
+ }
};
struct DictionaryNode : public Node {
@@ -207,7 +323,15 @@ public:
};
Vector<Pair> elements;
- DictionaryNode() { type = TYPE_DICTIONARY; }
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ DictionaryNode() {
+ type = TYPE_DICTIONARY;
+ datatype.has_type = true;
+ datatype.kind = DataType::BUILTIN;
+ datatype.builtin_type = Variant::DICTIONARY;
+ }
};
struct SelfNode : public Node {
@@ -221,6 +345,7 @@ public:
OP_PARENT_CALL,
OP_YIELD,
OP_IS,
+ OP_IS_BUILTIN,
//indexing operator
OP_INDEX,
OP_INDEX_NAMED,
@@ -229,10 +354,6 @@ public:
OP_POS,
OP_NOT,
OP_BIT_INVERT,
- OP_PREINC,
- OP_PREDEC,
- OP_INC,
- OP_DEC,
//binary operators (in precedence order)
OP_IN,
OP_EQUAL,
@@ -273,6 +394,9 @@ public:
Operator op;
Vector<Node *> arguments;
+ DataType datatype;
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
OperatorNode() { type = TYPE_OPERATOR; }
};
@@ -340,6 +464,15 @@ public:
}
};
+ struct CastNode : public Node {
+ Node *source_node;
+ DataType cast_type;
+ DataType return_type;
+ virtual DataType get_datatype() const { return return_type; }
+ virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
+ CastNode() { type = TYPE_CAST; }
+ };
+
struct AssertNode : public Node {
Node *condition;
AssertNode() { type = TYPE_ASSERT; }
@@ -362,76 +495,6 @@ public:
};
};
- /*
- struct OperatorNode : public Node {
-
- DataType return_cache;
- Operator op;
- Vector<Node*> arguments;
- virtual DataType get_datatype() const { return return_cache; }
-
- OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
- };
-
- struct VariableNode : public Node {
-
- DataType datatype_cache;
- StringName name;
- virtual DataType get_datatype() const { return datatype_cache; }
-
- VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; }
- };
-
- struct ConstantNode : public Node {
-
- DataType datatype;
- Variant value;
- virtual DataType get_datatype() const { return datatype; }
-
- ConstantNode() { type=TYPE_CONSTANT; }
- };
-
- struct BlockNode : public Node {
-
- Map<StringName,DataType> variables;
- List<Node*> statements;
- BlockNode() { type=TYPE_BLOCK; }
- };
-
- struct ControlFlowNode : public Node {
-
- FlowOperation flow_op;
- Vector<Node*> statements;
- ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
- };
-
- struct MemberNode : public Node {
-
- DataType datatype;
- StringName name;
- Node* owner;
- virtual DataType get_datatype() const { return datatype; }
- MemberNode() { type=TYPE_MEMBER; }
- };
-
-
- struct ProgramNode : public Node {
-
- struct Function {
- StringName name;
- FunctionNode*function;
- };
-
- Map<StringName,DataType> builtin_variables;
- Map<StringName,DataType> preexisting_variables;
-
- Vector<Function> functions;
- BlockNode *body;
-
- ProgramNode() { type=TYPE_PROGRAM; }
- };
-*/
-
enum CompletionType {
COMPLETION_NONE,
COMPLETION_BUILT_IN_TYPE_CONSTANT,
@@ -446,6 +509,8 @@ public:
COMPLETION_VIRTUAL_FUNC,
COMPLETION_YIELD,
COMPLETION_ASSIGN,
+ COMPLETION_TYPE_HINT,
+ COMPLETION_TYPE_HINT_INDEX,
};
private:
@@ -463,6 +528,14 @@ private:
String error;
int error_line;
int error_column;
+ bool check_types;
+#ifdef DEBUG_ENABLED
+ Set<int> *safe_lines;
+#endif // DEBUG_ENABLED
+
+#ifdef DEBUG_ENABLED
+ List<GDScriptWarning> warnings;
+#endif // DEBUG_ENABLED
int pending_newline;
@@ -496,6 +569,10 @@ private:
MultiplayerAPI::RPCMode rpc_mode;
void _set_error(const String &p_error, int p_line = -1, int p_column = -1);
+#ifdef DEBUG_ENABLED
+ void _add_warning(int p_code, int p_line = -1, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String());
+ void _add_warning(int p_code, int p_line, const Vector<String> &p_symbols);
+#endif // DEBUG_ENABLED
bool _recover_from_completion();
bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false);
@@ -507,7 +584,7 @@ private:
PatternNode *_parse_pattern(bool p_static);
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
- void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement);
+ void _transform_match_statment(MatchNode *p_match_statement);
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings);
void _parse_block(BlockNode *p_block, bool p_static);
@@ -515,13 +592,46 @@ private:
void _parse_class(ClassNode *p_class);
bool _end_statement();
+ void _determine_inheritance(ClassNode *p_class);
+ bool _parse_type(DataType &r_type, bool p_can_be_void = false);
+ DataType _resolve_type(const DataType &p_source, int p_line);
+ DataType _type_from_variant(const Variant &p_value) const;
+ DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const;
+ DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const;
+ DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const;
+ Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
+ bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
+ bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
+ bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
+
+ DataType _reduce_node_type(Node *p_node);
+ DataType _reduce_function_call_type(const OperatorNode *p_call);
+ DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line);
+ void _check_class_level_types(ClassNode *p_class);
+ void _check_class_blocks_types(ClassNode *p_class);
+ void _check_function_types(FunctionNode *p_function);
+ void _check_block_types(BlockNode *p_block);
+ _FORCE_INLINE_ void _mark_line_as_safe(int p_line) const {
+#ifdef DEBUG_ENABLED
+ if (safe_lines) safe_lines->insert(p_line);
+#endif // DEBUG_ENABLED
+ }
+ _FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const {
+#ifdef DEBUG_ENABLED
+ if (safe_lines) safe_lines->erase(p_line);
+#endif // DEBUG_ENABLED
+ }
+
Error _parse(const String &p_base_path);
public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
- Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false);
+#ifdef DEBUG_ENABLED
+ const List<GDScriptWarning> &get_warnings() const { return warnings; }
+#endif // DEBUG_ENABLED
+ Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
bool is_tool_script() const;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 3c8e1ddbe4..6e7842f190 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -91,6 +91,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"match",
"func",
"class",
+ "class_name",
"extends",
"is",
"onready",
@@ -100,6 +101,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"setget",
"const",
"var",
+ "as",
+ "void",
"enum",
"preload",
"assert",
@@ -124,6 +127,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"'.'",
"'?'",
"':'",
+ "'->'",
"'$'",
"'\\n'",
"PI",
@@ -187,6 +191,7 @@ static const _kws _keyword_list[] = {
//func
{ GDScriptTokenizer::TK_PR_FUNCTION, "func" },
{ GDScriptTokenizer::TK_PR_CLASS, "class" },
+ { GDScriptTokenizer::TK_PR_CLASS_NAME, "class_name" },
{ GDScriptTokenizer::TK_PR_EXTENDS, "extends" },
{ GDScriptTokenizer::TK_PR_IS, "is" },
{ GDScriptTokenizer::TK_PR_ONREADY, "onready" },
@@ -195,6 +200,8 @@ static const _kws _keyword_list[] = {
{ GDScriptTokenizer::TK_PR_EXPORT, "export" },
{ GDScriptTokenizer::TK_PR_SETGET, "setget" },
{ GDScriptTokenizer::TK_PR_VAR, "var" },
+ { GDScriptTokenizer::TK_PR_AS, "as" },
+ { GDScriptTokenizer::TK_PR_VOID, "void" },
{ GDScriptTokenizer::TK_PR_PRELOAD, "preload" },
{ GDScriptTokenizer::TK_PR_ASSERT, "assert" },
{ GDScriptTokenizer::TK_PR_YIELD, "yield" },
@@ -519,8 +526,13 @@ void GDScriptTokenizerText::_advance() {
return;
}
case '#': { // line comment skip
-
+#ifdef DEBUG_ENABLED
+ String comment;
+#endif // DEBUG_ENABLED
while (GETCHAR(0) != '\n') {
+#ifdef DEBUG_ENABLED
+ comment += GETCHAR(0);
+#endif // DEBUG_ENABLED
code_pos++;
if (GETCHAR(0) == 0) { //end of file
//_make_error("Unterminated Comment");
@@ -528,6 +540,17 @@ void GDScriptTokenizerText::_advance() {
return;
}
}
+#ifdef DEBUG_ENABLED
+ if (comment.begins_with("#warning-ignore:")) {
+ String code = comment.get_slice(":", 1);
+ warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower()));
+ } else if (comment.begins_with("#warning-ignore-all:")) {
+ String code = comment.get_slice(":", 1);
+ warning_global_skips.insert(code.strip_edges().to_lower());
+ } else if (comment.strip_edges() == "#warnings-disable") {
+ ignore_warnings = true;
+ }
+#endif // DEBUG_ENABLED
INCPOS(1);
column = 1;
line++;
@@ -705,11 +728,9 @@ void GDScriptTokenizerText::_advance() {
if (GETCHAR(1) == '=') {
_make_token(TK_OP_ASSIGN_SUB);
INCPOS(1);
- /*
- } else if (GETCHAR(1)=='-') {
- _make_token(TK_OP_MINUS_MINUS);
+ } else if (GETCHAR(1) == '>') {
+ _make_token(TK_FORWARD_ARROW);
INCPOS(1);
- */
} else {
_make_token(TK_OP_SUB);
}
@@ -916,7 +937,6 @@ void GDScriptTokenizerText::_advance() {
_make_constant(val);
} else if (period_found || exponent_found) {
double val = str.to_double();
- //print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
} else {
int64_t val = str.to_int64();
@@ -1040,6 +1060,9 @@ void GDScriptTokenizerText::set_code(const String &p_code) {
column = 1; //the same holds for columns
tk_rb_pos = 0;
error_flag = false;
+#ifdef DEBUG_ENABLED
+ ignore_warnings = false;
+#endif // DEBUG_ENABLED
last_error = "";
for (int i = 0; i < MAX_LOOKAHEAD + 1; i++)
_advance();
@@ -1135,9 +1158,9 @@ void GDScriptTokenizerText::advance(int p_amount) {
_advance();
}
- //////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 12
+#define BYTECODE_VERSION 13
Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) {
@@ -1167,15 +1190,15 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer)
Vector<uint8_t> cs;
cs.resize(len);
for (int j = 0; j < len; j++) {
- cs[j] = b[j] ^ 0xb6;
+ cs.write[j] = b[j] ^ 0xb6;
}
- cs[cs.size() - 1] = 0;
+ cs.write[cs.size() - 1] = 0;
String s;
s.parse_utf8((const char *)cs.ptr());
b += len;
total_len -= len + 4;
- identifiers[i] = s;
+ identifiers.write[i] = s;
}
constants.resize(constant_count);
@@ -1188,7 +1211,7 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer)
return err;
b += len;
total_len -= len;
- constants[i] = v;
+ constants.write[i] = v;
}
ERR_FAIL_COND_V(line_count * 8 > total_len, ERR_INVALID_DATA);
@@ -1213,10 +1236,10 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer)
if ((*b) & TOKEN_BYTE_MASK) { //little endian always
ERR_FAIL_COND_V(total_len < 4, ERR_INVALID_DATA);
- tokens[i] = decode_uint32(b) & ~TOKEN_BYTE_MASK;
+ tokens.write[i] = decode_uint32(b) & ~TOKEN_BYTE_MASK;
b += 4;
} else {
- tokens[i] = *b;
+ tokens.write[i] = *b;
b += 1;
total_len--;
}
@@ -1315,15 +1338,15 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code)
//save header
buf.resize(24);
- buf[0] = 'G';
- buf[1] = 'D';
- buf[2] = 'S';
- buf[3] = 'C';
- encode_uint32(BYTECODE_VERSION, &buf[4]);
- encode_uint32(identifier_map.size(), &buf[8]);
- encode_uint32(constant_map.size(), &buf[12]);
- encode_uint32(line_map.size(), &buf[16]);
- encode_uint32(token_array.size(), &buf[20]);
+ buf.write[0] = 'G';
+ buf.write[1] = 'D';
+ buf.write[2] = 'S';
+ buf.write[3] = 'C';
+ encode_uint32(BYTECODE_VERSION, &buf.write[4]);
+ encode_uint32(identifier_map.size(), &buf.write[8]);
+ encode_uint32(constant_map.size(), &buf.write[12]);
+ encode_uint32(line_map.size(), &buf.write[16]);
+ encode_uint32(token_array.size(), &buf.write[20]);
//save identifiers
@@ -1355,7 +1378,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code)
ERR_FAIL_COND_V(err != OK, Vector<uint8_t>());
int pos = buf.size();
buf.resize(pos + len);
- encode_variant(E->get(), &buf[pos], len);
+ encode_variant(E->get(), &buf.write[pos], len);
}
for (Map<int, uint32_t>::Element *E = rev_line_map.front(); E; E = E->next()) {
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index c4f1f9fd94..11a291cb2e 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,6 +31,7 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
+#include "core/pair.h"
#include "gdscript_functions.h"
#include "string_db.h"
#include "ustring.h"
@@ -96,6 +97,7 @@ public:
TK_CF_MATCH,
TK_PR_FUNCTION,
TK_PR_CLASS,
+ TK_PR_CLASS_NAME,
TK_PR_EXTENDS,
TK_PR_IS,
TK_PR_ONREADY,
@@ -105,6 +107,8 @@ public:
TK_PR_SETGET,
TK_PR_CONST,
TK_PR_VAR,
+ TK_PR_AS,
+ TK_PR_VOID,
TK_PR_ENUM,
TK_PR_PRELOAD,
TK_PR_ASSERT,
@@ -130,6 +134,7 @@ public:
TK_QUESTION_MARK,
TK_COLON,
TK_DOLLAR,
+ TK_FORWARD_ARROW,
TK_NEWLINE,
TK_CONST_PI,
TK_CONST_TAU,
@@ -167,6 +172,11 @@ public:
virtual int get_token_line_indent(int p_offset = 0) const = 0;
virtual String get_token_error(int p_offset = 0) const = 0;
virtual void advance(int p_amount = 1) = 0;
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const = 0;
+ virtual const Set<String> &get_warning_global_skips() const = 0;
+ virtual const bool is_ignoring_warnings() const = 0;
+#endif // DEBUG_ENABLED
virtual ~GDScriptTokenizer(){};
};
@@ -186,6 +196,7 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
union {
Variant::Type vtype; //for type types
GDScriptFunctions::Function func; //function for built in functions
+ int warning_code; //for warning skip
};
int line, col;
TokenData() {
@@ -213,6 +224,11 @@ class GDScriptTokenizerText : public GDScriptTokenizer {
int tk_rb_pos;
String last_error;
bool error_flag;
+#ifdef DEBUG_ENABLED
+ Vector<Pair<int, String> > warning_skips;
+ Set<String> warning_global_skips;
+ bool ignore_warnings;
+#endif // DEBUG_ENABLED
void _advance();
@@ -228,6 +244,11 @@ public:
virtual const Variant &get_token_constant(int p_offset = 0) const;
virtual String get_token_error(int p_offset = 0) const;
virtual void advance(int p_amount = 1);
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const { return warning_skips; }
+ virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; }
+ virtual const bool is_ignoring_warnings() const { return ignore_warnings; }
+#endif // DEBUG_ENABLED
};
class GDScriptTokenizerBuffer : public GDScriptTokenizer {
@@ -261,6 +282,17 @@ public:
virtual const Variant &get_token_constant(int p_offset = 0) const;
virtual String get_token_error(int p_offset = 0) const;
virtual void advance(int p_amount = 1);
+#ifdef DEBUG_ENABLED
+ virtual const Vector<Pair<int, String> > &get_warning_skips() const {
+ static Vector<Pair<int, String> > v;
+ return v;
+ }
+ virtual const Set<String> &get_warning_global_skips() const {
+ static Set<String> s;
+ return s;
+ }
+ virtual const bool is_ignoring_warnings() const { return true; }
+#endif // DEBUG_ENABLED
GDScriptTokenizerBuffer();
};
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index d5f9563600..09e8e39796 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -212,9 +212,12 @@
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask">
</member>
- <member name="theme" type="MeshLibrary" setter="set_theme" getter="get_theme">
+ <member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary].
</member>
+ <member name="theme" type="MeshLibrary" setter="set_theme" getter="get_theme">
+ Deprecated, use [member mesh_library] instead.
+ </member>
</members>
<constants>
<constant name="INVALID_CELL_ITEM" value="-1">
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 234a59e516..919607c3f6 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -208,21 +208,39 @@ bool GridMap::get_collision_layer_bit(int p_bit) const {
return get_collision_layer() & (1 << p_bit);
}
+#ifndef DISABLE_DEPRECATED
void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) {
- if (!theme.is_null())
- theme->unregister_owner(this);
- theme = p_theme;
- if (!theme.is_null())
- theme->register_owner(this);
+ ERR_EXPLAIN("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead.");
+ WARN_DEPRECATED
- _recreate_octant_data();
- _change_notify("theme");
+ set_mesh_library(p_theme);
}
Ref<MeshLibrary> GridMap::get_theme() const {
- return theme;
+ ERR_EXPLAIN("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead.");
+ WARN_DEPRECATED
+
+ return get_mesh_library();
+}
+#endif // DISABLE_DEPRECATED
+
+void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
+
+ if (!mesh_library.is_null())
+ mesh_library->unregister_owner(this);
+ mesh_library = p_mesh_library;
+ if (!mesh_library.is_null())
+ mesh_library->register_owner(this);
+
+ _recreate_octant_data();
+ _change_notify("mesh_library");
+}
+
+Ref<MeshLibrary> GridMap::get_mesh_library() const {
+
+ return mesh_library;
}
void GridMap::set_cell_size(const Vector3 &p_size) {
@@ -469,11 +487,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
ERR_CONTINUE(!cell_map.has(E->get()));
const Cell &c = cell_map[E->get()];
- if (!theme.is_valid() || !theme->has_item(c.item))
+ if (!mesh_library.is_valid() || !mesh_library->has_item(c.item))
continue;
- //print_line("OCTANT, CELLS: "+itos(ii.cells.size()));
-
Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z);
Vector3 ofs = _get_offset();
@@ -488,7 +504,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
if (baked_meshes.size() == 0) {
- if (theme->get_item_mesh(c.item).is_valid()) {
+ if (mesh_library->get_item_mesh(c.item).is_valid()) {
if (!multimesh_items.has(c.item)) {
multimesh_items[c.item] = List<Pair<Transform, IndexKey> >();
}
@@ -500,7 +516,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
}
- Vector<MeshLibrary::ShapeData> shapes = theme->get_item_shapes(c.item);
+ Vector<MeshLibrary::ShapeData> shapes = mesh_library->get_item_shapes(c.item);
// add the item's shape at given xform to octant's static_body
for (int i = 0; i < shapes.size(); i++) {
// add the item's shape
@@ -508,14 +524,12 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
continue;
PhysicsServer::get_singleton()->body_add_shape(g.static_body, shapes[i].shape->get_rid(), xform * shapes[i].local_transform);
if (g.collision_debug.is_valid()) {
- shapes[i].shape->add_vertices_to_array(col_debug, xform * shapes[i].local_transform);
+ shapes.write[i].shape->add_vertices_to_array(col_debug, xform * shapes[i].local_transform);
}
-
- //print_line("PHIS x: "+xform);
}
// add the item's navmesh at given xform to GridMap's Navigation ancestor
- Ref<NavigationMesh> navmesh = theme->get_item_navmesh(c.item);
+ Ref<NavigationMesh> navmesh = mesh_library->get_item_navmesh(c.item);
if (navmesh.is_valid()) {
Octant::NavMesh nm;
nm.xform = xform;
@@ -537,7 +551,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
RID mm = VS::get_singleton()->multimesh_create();
VS::get_singleton()->multimesh_allocate(mm, E->get().size(), VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_NONE);
- VS::get_singleton()->multimesh_set_mesh(mm, theme->get_item_mesh(E->key())->get_rid());
+ VS::get_singleton()->multimesh_set_mesh(mm, mesh_library->get_item_mesh(E->key())->get_rid());
int idx = 0;
for (List<Pair<Transform, IndexKey> >::Element *F = E->get().front(); F; F = F->next()) {
@@ -600,7 +614,6 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
Octant &g = *octant_map[p_key];
PhysicsServer::get_singleton()->body_set_state(g.static_body, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform());
PhysicsServer::get_singleton()->body_set_space(g.static_body, get_world()->get_space());
- //print_line("BODYPOS: "+get_global_transform());
if (g.collision_debug_instance.is_valid()) {
VS::get_singleton()->instance_set_scenario(g.collision_debug_instance, get_world()->get_scenario());
@@ -612,11 +625,11 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
VS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
}
- if (navigation && theme.is_valid()) {
+ if (navigation && mesh_library.is_valid()) {
for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
if (cell_map.has(F->key()) && F->get().id < 0) {
- Ref<NavigationMesh> nm = theme->get_item_navmesh(cell_map[F->key()].item);
+ Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F->key()].item);
if (nm.is_valid()) {
F->get().id = navigation->navmesh_add(nm, F->get().xform, this);
}
@@ -758,7 +771,7 @@ void GridMap::_update_visibility() {
for (Map<OctantKey, Octant *>::Element *e = octant_map.front(); e; e = e->next()) {
Octant *octant = e->value();
for (int i = 0; i < octant->multimesh_instances.size(); i++) {
- Octant::MultimeshInstance &mi = octant->multimesh_instances[i];
+ const Octant::MultimeshInstance &mi = octant->multimesh_instances[i];
VS::get_singleton()->instance_set_visible(mi.instance, is_visible());
}
}
@@ -846,8 +859,13 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &GridMap::set_collision_layer_bit);
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &GridMap::get_collision_layer_bit);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_theme", "theme"), &GridMap::set_theme);
ClassDB::bind_method(D_METHOD("get_theme"), &GridMap::get_theme);
+#endif // DISABLE_DEPRECATED
+
+ ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library);
+ ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library);
ClassDB::bind_method(D_METHOD("set_cell_size", "size"), &GridMap::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &GridMap::get_cell_size);
@@ -865,7 +883,6 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("world_to_map", "pos"), &GridMap::world_to_map);
ClassDB::bind_method(D_METHOD("map_to_world", "x", "y", "z"), &GridMap::map_to_world);
- //ClassDB::bind_method(D_METHOD("_recreate_octants"),&GridMap::_recreate_octants);
ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback);
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed);
@@ -889,7 +906,11 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_baked_meshes"), &GridMap::clear_baked_meshes);
ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1));
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_theme", "get_theme");
+#ifndef DISABLE_DEPRECATED
+ ADD_PROPERTYNO(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");
ADD_GROUP("Cell", "cell_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_octant_size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_octant_size", "get_octant_size");
@@ -952,7 +973,7 @@ Array GridMap::get_used_cells() const {
Array GridMap::get_meshes() {
- if (theme.is_null())
+ if (mesh_library.is_null())
return Array();
Vector3 ofs = _get_offset();
@@ -961,9 +982,9 @@ Array GridMap::get_meshes() {
for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) {
int id = E->get().item;
- if (!theme->has_item(id))
+ if (!mesh_library->has_item(id))
continue;
- Ref<Mesh> mesh = theme->get_item_mesh(id);
+ Ref<Mesh> mesh = mesh_library->get_item_mesh(id);
if (mesh.is_null())
continue;
@@ -1004,7 +1025,7 @@ void GridMap::clear_baked_meshes() {
void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texel_size) {
- if (!theme.is_valid())
+ if (!mesh_library.is_valid())
return;
//generate
@@ -1015,10 +1036,10 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
IndexKey key = E->key();
int item = E->get().item;
- if (!theme->has_item(item))
+ if (!mesh_library->has_item(item))
continue;
- Ref<Mesh> mesh = theme->get_item_mesh(item);
+ Ref<Mesh> mesh = mesh_library->get_item_mesh(item);
if (!mesh.is_valid())
continue;
@@ -1064,7 +1085,6 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
for (Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool> > >::Element *E = surface_map.front(); E; E = E->next()) {
- print_line("generating mesh " + itos(ofs++) + "/" + itos(surface_map.size()));
Ref<ArrayMesh> mesh;
mesh.instance();
for (Map<Ref<Material>, Ref<SurfaceTool> >::Element *F = E->get().front(); F; F = F->next()) {
@@ -1137,8 +1157,8 @@ GridMap::GridMap() {
GridMap::~GridMap() {
- if (!theme.is_null())
- theme->unregister_owner(this);
+ if (!mesh_library.is_null())
+ mesh_library->unregister_owner(this);
clear();
}
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index ed36751fc8..3d8be5c9c7 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -157,7 +157,7 @@ class GridMap : public Spatial {
Vector3::Axis clip_axis;
- Ref<MeshLibrary> theme;
+ Ref<MeshLibrary> mesh_library;
Map<OctantKey, Octant *> octant_map;
Map<IndexKey, Cell> cell_map;
@@ -227,8 +227,13 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
+#ifndef DISABLE_DEPRECATED
void set_theme(const Ref<MeshLibrary> &p_theme);
Ref<MeshLibrary> get_theme() const;
+#endif // DISABLE_DEPRECATED
+
+ void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
+ Ref<MeshLibrary> get_mesh_library() const;
void set_cell_size(const Vector3 &p_size);
Vector3 get_cell_size() const;
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 4b96824dca..90e28129cc 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -43,7 +43,7 @@ void GridMapEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = NULL;
hide();
- theme_pallete->hide();
+ mesh_library_palette->hide();
}
}
@@ -231,6 +231,13 @@ void GridMapEditor::_menu_option(int p_option) {
_delete_selection();
} break;
+ case MENU_OPTION_SELECTION_FILL: {
+ if (!selection.active)
+ return;
+
+ _fill_selection();
+
+ } break;
case MENU_OPTION_GRIDMAP_SETTINGS: {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50) * EDSCALE);
} break;
@@ -313,12 +320,12 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo
if (!spatial_editor)
return false;
- if (selected_pallete < 0 && input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE)
+ if (selected_palette < 0 && input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE)
return false;
- Ref<MeshLibrary> theme = node->get_theme();
- if (theme.is_null())
+ Ref<MeshLibrary> mesh_library = node->get_mesh_library();
+ if (mesh_library.is_null())
return false;
- if (input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE && !theme->has_item(selected_pallete))
+ if (input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE && !mesh_library->has_item(selected_palette))
return false;
Camera *camera = p_camera;
@@ -400,9 +407,9 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo
int item = node->get_cell_item(cell[0], cell[1], cell[2]);
if (item >= 0) {
- selected_pallete = item;
- theme_pallete->set_current(item);
- update_pallete();
+ selected_palette = item;
+ mesh_library_palette->set_current(item);
+ update_palette();
_update_cursor_instance();
}
return true;
@@ -410,12 +417,12 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo
if (input_action == INPUT_PAINT) {
SetItem si;
si.pos = Vector3(cell[0], cell[1], cell[2]);
- si.new_value = selected_pallete;
+ si.new_value = selected_palette;
si.new_orientation = cursor_rot;
si.old_value = node->get_cell_item(cell[0], cell[1], cell[2]);
si.old_orientation = node->get_cell_item_orientation(cell[0], cell[1], cell[2]);
set_items.push_back(si);
- node->set_cell_item(cell[0], cell[1], cell[2], selected_pallete, cursor_rot);
+ node->set_cell_item(cell[0], cell[1], cell[2], selected_palette, cursor_rot);
return true;
} else if (input_action == INPUT_ERASE) {
SetItem si;
@@ -455,6 +462,29 @@ void GridMapEditor::_delete_selection() {
_validate_selection();
}
+void GridMapEditor::_fill_selection() {
+
+ if (!selection.active)
+ return;
+
+ undo_redo->create_action(TTR("GridMap Fill Selection"));
+ for (int i = selection.begin.x; i <= selection.end.x; i++) {
+
+ for (int j = selection.begin.y; j <= selection.end.y; j++) {
+
+ for (int k = selection.begin.z; k <= selection.end.z; k++) {
+
+ undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_palette, cursor_rot);
+ undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ }
+ }
+ }
+ undo_redo->commit_action();
+
+ selection.active = false;
+ _validate_selection();
+}
+
void GridMapEditor::_update_duplicate_indicator() {
if (!selection.active || input_action != INPUT_DUPLICATE) {
@@ -682,42 +712,42 @@ void GridMapEditor::_set_display_mode(int p_mode) {
display_mode = p_mode;
- update_pallete();
+ update_palette();
}
-void GridMapEditor::update_pallete() {
- int selected = theme_pallete->get_current();
+void GridMapEditor::update_palette() {
+ int selected = mesh_library_palette->get_current();
- theme_pallete->clear();
+ mesh_library_palette->clear();
if (display_mode == DISPLAY_THUMBNAIL) {
- theme_pallete->set_max_columns(0);
- theme_pallete->set_icon_mode(ItemList::ICON_MODE_TOP);
+ mesh_library_palette->set_max_columns(0);
+ mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_TOP);
} else if (display_mode == DISPLAY_LIST) {
- theme_pallete->set_max_columns(1);
- theme_pallete->set_icon_mode(ItemList::ICON_MODE_LEFT);
+ mesh_library_palette->set_max_columns(1);
+ mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_LEFT);
}
float min_size = EDITOR_DEF("editors/grid_map/preview_size", 64);
- theme_pallete->set_fixed_icon_size(Size2(min_size, min_size));
- theme_pallete->set_fixed_column_width(min_size * 3 / 2);
- theme_pallete->set_max_text_lines(2);
+ mesh_library_palette->set_fixed_icon_size(Size2(min_size, min_size));
+ mesh_library_palette->set_fixed_column_width(min_size * 3 / 2);
+ mesh_library_palette->set_max_text_lines(2);
- Ref<MeshLibrary> theme = node->get_theme();
+ Ref<MeshLibrary> mesh_library = node->get_mesh_library();
- if (theme.is_null()) {
- last_theme = NULL;
+ if (mesh_library.is_null()) {
+ last_mesh_library = NULL;
return;
}
Vector<int> ids;
- ids = theme->get_item_list();
+ ids = mesh_library->get_item_list();
List<_CGMEItemSort> il;
for (int i = 0; i < ids.size(); i++) {
_CGMEItemSort is;
is.id = ids[i];
- is.name = theme->get_item_name(ids[i]);
+ is.name = mesh_library->get_item_name(ids[i]);
il.push_back(is);
}
il.sort();
@@ -727,28 +757,28 @@ void GridMapEditor::update_pallete() {
for (List<_CGMEItemSort>::Element *E = il.front(); E; E = E->next()) {
int id = E->get().id;
- theme_pallete->add_item("");
+ mesh_library_palette->add_item("");
- String name = theme->get_item_name(id);
- Ref<Texture> preview = theme->get_item_preview(id);
+ String name = mesh_library->get_item_name(id);
+ Ref<Texture> preview = mesh_library->get_item_preview(id);
if (!preview.is_null()) {
- theme_pallete->set_item_icon(item, preview);
- theme_pallete->set_item_tooltip(item, name);
+ mesh_library_palette->set_item_icon(item, preview);
+ mesh_library_palette->set_item_tooltip(item, name);
}
if (name != "") {
- theme_pallete->set_item_text(item, name);
+ mesh_library_palette->set_item_text(item, name);
}
- theme_pallete->set_item_metadata(item, id);
+ mesh_library_palette->set_item_metadata(item, id);
item++;
}
if (selected != -1) {
- theme_pallete->select(selected);
+ mesh_library_palette->select(selected);
}
- last_theme = theme.operator->();
+ last_mesh_library = mesh_library.operator->();
}
void GridMapEditor::edit(GridMap *p_gridmap) {
@@ -775,7 +805,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
return;
}
- update_pallete();
+ update_palette();
set_process(true);
@@ -884,7 +914,7 @@ void GridMapEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- theme_pallete->connect("item_selected", this, "_item_selected_cbk");
+ mesh_library_palette->connect("item_selected", this, "_item_selected_cbk");
for (int i = 0; i < 3; i++) {
grid[i] = VS::get_singleton()->mesh_create();
@@ -929,9 +959,9 @@ void GridMapEditor::_notification(int p_what) {
}
grid_xform = xf;
}
- Ref<MeshLibrary> cgmt = node->get_theme();
- if (cgmt.operator->() != last_theme)
- update_pallete();
+ Ref<MeshLibrary> cgmt = node->get_mesh_library();
+ if (cgmt.operator->() != last_mesh_library)
+ update_palette();
if (lock_view) {
@@ -964,10 +994,10 @@ void GridMapEditor::_update_cursor_instance() {
VisualServer::get_singleton()->free(cursor_instance);
cursor_instance = RID();
- if (selected_pallete >= 0) {
+ if (selected_palette >= 0) {
- if (node && !node->get_theme().is_null()) {
- Ref<Mesh> mesh = node->get_theme()->get_item_mesh(selected_pallete);
+ if (node && !node->get_mesh_library().is_null()) {
+ Ref<Mesh> mesh = node->get_mesh_library()->get_item_mesh(selected_palette);
if (!mesh.is_null() && mesh->get_rid().is_valid()) {
cursor_instance = VisualServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world()->get_scenario());
@@ -978,7 +1008,7 @@ void GridMapEditor::_update_cursor_instance() {
}
void GridMapEditor::_item_selected_cbk(int idx) {
- selected_pallete = theme_pallete->get_item_metadata(idx);
+ selected_palette = mesh_library_palette->get_item_metadata(idx);
_update_cursor_instance();
}
@@ -1072,6 +1102,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C);
options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X);
+ options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS);
@@ -1115,9 +1146,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
display_mode = DISPLAY_THUMBNAIL;
- theme_pallete = memnew(ItemList);
- add_child(theme_pallete);
- theme_pallete->set_v_size_flags(SIZE_EXPAND_FILL);
+ mesh_library_palette = memnew(ItemList);
+ add_child(mesh_library_palette);
+ mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
edit_axis = Vector3::AXIS_Y;
edit_floor[0] = -1;
@@ -1125,7 +1156,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
edit_floor[2] = -1;
cursor_visible = false;
- selected_pallete = -1;
+ selected_palette = -1;
lock_view = false;
cursor_rot = 0;
last_mouseover = Vector3(-1, -1, -1);
@@ -1284,9 +1315,24 @@ GridMapEditor::~GridMapEditor() {
VisualServer::get_singleton()->free(duplicate_instance);
}
+void GridMapEditorPlugin::_notification(int p_what) {
+
+ if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+
+ switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
+ case 0: { // Left.
+ SpatialEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 0);
+ } break;
+ case 1: { // Right.
+ SpatialEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 1);
+ } break;
+ }
+ }
+}
+
void GridMapEditorPlugin::edit(Object *p_object) {
- gridmap_editor->edit(Object::cast_to<GridMap>(p_object));
+ grid_map_editor->edit(Object::cast_to<GridMap>(p_object));
}
bool GridMapEditorPlugin::handles(Object *p_object) const {
@@ -1297,29 +1343,35 @@ bool GridMapEditorPlugin::handles(Object *p_object) const {
void GridMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- gridmap_editor->show();
- gridmap_editor->spatial_editor_hb->show();
- gridmap_editor->set_process(true);
+ grid_map_editor->show();
+ grid_map_editor->spatial_editor_hb->show();
+ grid_map_editor->set_process(true);
} else {
- gridmap_editor->spatial_editor_hb->hide();
- gridmap_editor->hide();
- gridmap_editor->edit(NULL);
- gridmap_editor->set_process(false);
+ grid_map_editor->spatial_editor_hb->hide();
+ grid_map_editor->hide();
+ grid_map_editor->edit(NULL);
+ grid_map_editor->set_process(false);
}
}
GridMapEditorPlugin::GridMapEditorPlugin(EditorNode *p_node) {
editor = p_node;
- gridmap_editor = memnew(GridMapEditor(editor));
- SpatialEditor::get_singleton()->get_palette_split()->add_child(gridmap_editor);
- // TODO: make this configurable, so the user can choose were to put this, it makes more sense
- // on the right, but some people might find it strange.
- SpatialEditor::get_singleton()->get_palette_split()->move_child(gridmap_editor, 1);
+ EDITOR_DEF("editors/grid_map/editor_side", 1);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/grid_map/editor_side", PROPERTY_HINT_ENUM, "Left,Right"));
- gridmap_editor->hide();
+ grid_map_editor = memnew(GridMapEditor(editor));
+ switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
+ case 0: { // Left.
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, grid_map_editor);
+ } break;
+ case 1: { // Right.
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT, grid_map_editor);
+ } break;
+ }
+ grid_map_editor->hide();
}
GridMapEditorPlugin::~GridMapEditorPlugin() {
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index f79d9aefa0..663274f46e 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -97,7 +97,7 @@ class GridMapEditor : public VBoxContainer {
List<SetItem> set_items;
GridMap *node;
- MeshLibrary *last_theme;
+ MeshLibrary *last_mesh_library;
ClipMode clip_mode;
bool lock_view;
@@ -141,7 +141,7 @@ class GridMapEditor : public VBoxContainer {
Vector3 last_mouseover;
int display_mode;
- int selected_pallete;
+ int selected_palette;
int cursor_rot;
enum Menu {
@@ -168,6 +168,7 @@ class GridMapEditor : public VBoxContainer {
MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR,
MENU_OPTION_SELECTION_DUPLICATE,
MENU_OPTION_SELECTION_CLEAR,
+ MENU_OPTION_SELECTION_FILL,
MENU_OPTION_REMOVE_AREA,
MENU_OPTION_GRIDMAP_SETTINGS
@@ -184,9 +185,9 @@ class GridMapEditor : public VBoxContainer {
void update_grid();
void _configure();
void _menu_option(int);
- void update_pallete();
+ void update_palette();
void _set_display_mode(int p_mode);
- ItemList *theme_pallete;
+ ItemList *mesh_library_palette;
void _item_selected_cbk(int idx);
void _update_cursor_transform();
void _update_cursor_instance();
@@ -200,12 +201,12 @@ class GridMapEditor : public VBoxContainer {
void _floor_changed(float p_value);
void _delete_selection();
+ void _fill_selection();
EditorNode *editor;
bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click);
friend class GridMapEditorPlugin;
- Panel *theme_panel;
protected:
void _notification(int p_what);
@@ -225,11 +226,14 @@ class GridMapEditorPlugin : public EditorPlugin {
GDCLASS(GridMapEditorPlugin, EditorPlugin);
- GridMapEditor *gridmap_editor;
+ GridMapEditor *grid_map_editor;
EditorNode *editor;
+protected:
+ void _notification(int p_what);
+
public:
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return gridmap_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
virtual String get_name() const { return "GridMap"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 0168be3a26..437c0d57fa 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -121,9 +121,7 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
img.instance();
Error err = jpeg_load_image_from_buffer(img.ptr(), p_png, p_size);
- if (err)
- ERR_PRINT("Couldn't initialize ImageLoaderJPG with the given resource.");
-
+ ERR_FAIL_COND_V(err, Ref<Image>());
return img;
}
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 38198c9105..d11d7a7ec7 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -11,6 +11,7 @@ if env['builtin_mbedtls']:
"aes.c",
"aesni.c",
"arc4.c",
+ "aria.c",
"asn1parse.c",
"asn1write.c",
"base64.c",
@@ -19,6 +20,8 @@ if env['builtin_mbedtls']:
"camellia.c",
"ccm.c",
"certs.c",
+ "chacha20.c",
+ "chachapoly.c",
"cipher.c",
"cipher_wrap.c",
"cmac.c",
@@ -36,6 +39,7 @@ if env['builtin_mbedtls']:
"error.c",
"gcm.c",
"havege.c",
+ "hkdf.c",
"hmac_drbg.c",
"md2.c",
"md4.c",
@@ -44,6 +48,7 @@ if env['builtin_mbedtls']:
"md_wrap.c",
"memory_buffer_alloc.c",
"net_sockets.c",
+ "nist_kw.c",
"oid.c",
"padlock.c",
"pem.c",
@@ -55,6 +60,8 @@ if env['builtin_mbedtls']:
"pk_wrap.c",
"pkwrite.c",
"platform.c",
+ "platform_util.c",
+ "poly1305.c",
"ripemd160.c",
"rsa.c",
"rsa_internal.c",
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
index a63e53ec1f..884c26ddfe 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.cpp
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "stream_peer_mbed_tls.h"
+#include "mbedtls/platform_util.h"
+#include "os/file_access.h"
static void my_debug(void *ctx, int level,
const char *file, int line,
@@ -81,6 +83,36 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
return got;
}
+void StreamPeerMbedTLS::_cleanup() {
+
+ mbedtls_ssl_free(&ssl);
+ mbedtls_ssl_config_free(&conf);
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ base = Ref<StreamPeer>();
+ status = STATUS_DISCONNECTED;
+}
+
+Error StreamPeerMbedTLS::_do_handshake() {
+ int ret = 0;
+ while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ ERR_PRINTS("TLS handshake error: " + itos(ret));
+ _print_error(ret);
+ disconnect_from_stream();
+ status = STATUS_ERROR;
+ return FAILED;
+ } else if (!blocking_handshake) {
+ // Will retry via poll later
+ return OK;
+ }
+ }
+
+ status = STATUS_CONNECTED;
+ return OK;
+}
+
Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs, const String &p_for_hostname) {
base = p_base;
@@ -95,6 +127,7 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
if (ret != 0) {
ERR_PRINTS(" failed\n ! mbedtls_ctr_drbg_seed returned an error" + itos(ret));
+ _cleanup();
return FAILED;
}
@@ -112,29 +145,24 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
mbedtls_ssl_set_bio(&ssl, this, bio_send, bio_recv, NULL);
- while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
- if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
- ERR_PRINTS("TLS handshake error: " + itos(ret));
- _print_error(ret);
- status = STATUS_ERROR_HOSTNAME_MISMATCH;
- return FAILED;
- }
- }
+ status = STATUS_HANDSHAKING;
- connected = true;
- status = STATUS_CONNECTED;
+ if ((ret = _do_handshake()) != OK) {
+ status = STATUS_ERROR_HOSTNAME_MISMATCH;
+ return FAILED;
+ }
return OK;
}
Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base) {
- return ERR_UNAVAILABLE;
+ return OK;
}
Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
Error err;
int sent = 0;
@@ -155,7 +183,7 @@ Error StreamPeerMbedTLS::put_data(const uint8_t *p_data, int p_bytes) {
Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
r_sent = 0;
@@ -177,7 +205,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in
Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
Error err;
@@ -199,7 +227,7 @@ Error StreamPeerMbedTLS::get_data(uint8_t *p_buffer, int p_bytes) {
Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
- ERR_FAIL_COND_V(!connected, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_UNCONFIGURED);
r_received = 0;
@@ -218,27 +246,30 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r
void StreamPeerMbedTLS::poll() {
- ERR_FAIL_COND(!connected);
+ ERR_FAIL_COND(status != STATUS_CONNECTED && status != STATUS_HANDSHAKING);
ERR_FAIL_COND(!base.is_valid());
+ if (status == STATUS_HANDSHAKING) {
+ _do_handshake();
+ return;
+ }
+
int ret = mbedtls_ssl_read(&ssl, NULL, 0);
if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
_print_error(ret);
disconnect_from_stream();
- return;
}
}
int StreamPeerMbedTLS::get_available_bytes() const {
- ERR_FAIL_COND_V(!connected, 0);
+ ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
return mbedtls_ssl_get_bytes_avail(&ssl);
}
StreamPeerMbedTLS::StreamPeerMbedTLS() {
- connected = false;
status = STATUS_DISCONNECTED;
}
@@ -248,17 +279,10 @@ StreamPeerMbedTLS::~StreamPeerMbedTLS() {
void StreamPeerMbedTLS::disconnect_from_stream() {
- if (!connected)
+ if (status != STATUS_CONNECTED && status != STATUS_HANDSHAKING)
return;
- mbedtls_ssl_free(&ssl);
- mbedtls_ssl_config_free(&conf);
- mbedtls_ctr_drbg_free(&ctr_drbg);
- mbedtls_entropy_free(&entropy);
-
- base = Ref<StreamPeer>();
- connected = false;
- status = STATUS_DISCONNECTED;
+ _cleanup();
}
StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h
index 2b96a194a1..7f4e5a4513 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -48,8 +48,6 @@ private:
Status status;
String hostname;
- bool connected;
-
Ref<StreamPeer> base;
static StreamPeerSSL *_create_func();
@@ -57,9 +55,11 @@ private:
static int bio_recv(void *ctx, unsigned char *buf, size_t len);
static int bio_send(void *ctx, const unsigned char *buf, size_t len);
+ void _cleanup();
protected:
static mbedtls_x509_crt cacert;
+
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
@@ -67,6 +67,8 @@ protected:
static void _bind_methods();
+ Error _do_handshake();
+
public:
virtual void poll();
virtual Error accept_stream(Ref<StreamPeer> p_base);
diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py
index 4912457e2b..e85fa631dd 100644
--- a/modules/mobile_vr/config.py
+++ b/modules/mobile_vr/config.py
@@ -1,6 +1,5 @@
def can_build(env, platform):
- # should probably change this to only be true on iOS and Android
- return False
+ return True
def configure(env):
pass
diff --git a/modules/mobile_vr/mobile_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 6b1c7eb279..e2c630565f 100644
--- a/modules/mobile_vr/mobile_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "mobile_interface.h"
+#include "mobile_vr_interface.h"
#include "core/os/input.h"
#include "core/os/os.h"
#include "servers/visual/visual_server_global.h"
@@ -279,7 +279,7 @@ bool MobileVRInterface::is_stereo() {
return true;
};
-bool MobileVRInterface::is_initialized() {
+bool MobileVRInterface::is_initialized() const {
return (initialized);
};
@@ -297,6 +297,47 @@ bool MobileVRInterface::initialize() {
mag_current_min = Vector3(0, 0, 0);
mag_current_max = Vector3(0, 0, 0);
+ // build our shader
+ if (lens_shader == NULL) {
+ ///@TODO need to switch between GLES2 and GLES3 version, Reduz suggested moving this into our drivers and making this a core shader
+ // create a shader
+ lens_shader = new LensDistortedShaderGLES3();
+
+ // create our shader stuff
+ lens_shader->init();
+
+ glGenBuffers(1, &half_screen_quad);
+ glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad);
+ {
+ /* clang-format off */
+ const float qv[16] = {
+ 0, -1,
+ -1, -1,
+ 0, 1,
+ -1, 1,
+ 1, 1,
+ 1, 1,
+ 1, -1,
+ 1, -1,
+ };
+ /* clang-format on */
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &half_screen_array);
+ glBindVertexArray(half_screen_array);
+ glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad);
+ glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8);
+ glEnableVertexAttribArray(4);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+
// reset our orientation
orientation = Basis();
@@ -304,7 +345,7 @@ bool MobileVRInterface::initialize() {
arvr_server->set_primary_interface(this);
last_ticks = OS::get_singleton()->get_ticks_usec();
- ;
+
initialized = true;
};
@@ -319,6 +360,15 @@ void MobileVRInterface::uninitialize() {
arvr_server->clear_primary_interface_if(this);
}
+ // cleanup our shader and buffers
+ if (lens_shader != NULL) {
+ glDeleteVertexArrays(1, &half_screen_array);
+ glDeleteBuffers(1, &half_screen_quad);
+
+ delete lens_shader;
+ lens_shader = NULL;
+ }
+
initialized = false;
};
};
@@ -394,6 +444,9 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t
// We must have a valid render target
ERR_FAIL_COND(!p_render_target.is_valid());
+ // We must have an initialised shader
+ ERR_FAIL_COND(lens_shader != NULL);
+
// Because we are rendering to our device we must use our main viewport!
ERR_FAIL_COND(p_screen_rect == Rect2());
@@ -420,13 +473,13 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texid);
- lens_shader.bind();
- lens_shader.set_uniform(LensDistortedShaderGLES3::OFFSET_X, offset_x);
- lens_shader.set_uniform(LensDistortedShaderGLES3::K1, k1);
- lens_shader.set_uniform(LensDistortedShaderGLES3::K2, k2);
- lens_shader.set_uniform(LensDistortedShaderGLES3::EYE_CENTER, eye_center);
- lens_shader.set_uniform(LensDistortedShaderGLES3::UPSCALE, oversample);
- lens_shader.set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio);
+ lens_shader->bind();
+ lens_shader->set_uniform(LensDistortedShaderGLES3::OFFSET_X, offset_x);
+ lens_shader->set_uniform(LensDistortedShaderGLES3::K1, k1);
+ lens_shader->set_uniform(LensDistortedShaderGLES3::K2, k2);
+ lens_shader->set_uniform(LensDistortedShaderGLES3::EYE_CENTER, eye_center);
+ lens_shader->set_uniform(LensDistortedShaderGLES3::UPSCALE, oversample);
+ lens_shader->set_uniform(LensDistortedShaderGLES3::ASPECT_RATIO, aspect_ratio);
glBindVertexArray(half_screen_array);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -454,41 +507,7 @@ MobileVRInterface::MobileVRInterface() {
k2 = 0.215;
last_ticks = 0;
- // create our shader stuff
- lens_shader.init();
-
- {
- glGenBuffers(1, &half_screen_quad);
- glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad);
- {
- /* clang-format off */
- const float qv[16] = {
- 0, -1,
- -1, -1,
- 0, 1,
- -1, 1,
- 1, 1,
- 1, 1,
- 1, -1,
- 1, -1,
- };
- /* clang-format on */
-
- glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
- }
-
- glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
-
- glGenVertexArrays(1, &half_screen_array);
- glBindVertexArray(half_screen_array);
- glBindBuffer(GL_ARRAY_BUFFER, half_screen_quad);
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8);
- glEnableVertexAttribArray(4);
- glBindVertexArray(0);
- glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
- }
+ lens_shader = NULL;
};
MobileVRInterface::~MobileVRInterface() {
diff --git a/modules/mobile_vr/mobile_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index bb84281b46..cee0cca90e 100644
--- a/modules/mobile_vr/mobile_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -58,7 +58,7 @@ private:
float eye_height;
uint64_t last_ticks;
- LensDistortedShaderGLES3 lens_shader;
+ LensDistortedShaderGLES3 *lens_shader;
GLuint half_screen_quad;
GLuint half_screen_array;
@@ -134,7 +134,7 @@ public:
virtual StringName get_name() const;
virtual int get_capabilities() const;
- virtual bool is_initialized();
+ virtual bool is_initialized() const;
virtual bool initialize();
virtual void uninitialize();
@@ -150,4 +150,4 @@ public:
~MobileVRInterface();
};
-#endif // MOBILE_VR_INTERFACE_H
+#endif // !MOBILE_VR_INTERFACE_H
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 0655727a4a..edddaa87e1 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "mobile_interface.h"
+#include "mobile_vr_interface.h"
void register_mobile_vr_types() {
ClassDB::register_class<MobileVRInterface>();
diff --git a/modules/mobile_vr/shaders/lens_distorted.glsl b/modules/mobile_vr/shaders/lens_distorted.glsl
index 5a2975d737..92604c891c 100644
--- a/modules/mobile_vr/shaders/lens_distorted.glsl
+++ b/modules/mobile_vr/shaders/lens_distorted.glsl
@@ -1,7 +1,9 @@
+/* clang-format off */
[vertex]
-layout(location=0) in highp vec4 vertex_attrib;
-layout(location=4) in vec2 uv_in;
+layout(location = 0) in highp vec4 vertex_attrib;
+/* clang-format on */
+layout(location = 4) in vec2 uv_in;
uniform float offset_x;
@@ -9,13 +11,15 @@ out vec2 uv_interp;
void main() {
- uv_interp = uv_in;
- gl_Position = vec4(vertex_attrib.x + offset_x, vertex_attrib.y, 0.0, 1.0);
+ uv_interp = uv_in;
+ gl_Position = vec4(vertex_attrib.x + offset_x, vertex_attrib.y, 0.0, 1.0);
}
+/* clang-format off */
[fragment]
uniform sampler2D source; //texunit:0
+/* clang-format on */
uniform vec2 eye_center;
uniform float k1;
@@ -28,32 +32,31 @@ in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
- vec2 coords = uv_interp;
- vec2 offset = coords - eye_center;
-
- // take aspect ratio into account
- offset.y /= aspect_ratio;
-
- // distort
- vec2 offset_sq = offset * offset;
- float radius_sq = offset_sq.x + offset_sq.y;
- float radius_s4 = radius_sq * radius_sq;
- float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
- offset *= distortion_scale;
-
- // reapply aspect ratio
- offset.y *= aspect_ratio;
-
- // add our eye center back in
- coords = offset + eye_center;
- coords /= upscale;
-
- // and check our color
- if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
- frag_color = vec4(0.0, 0.0, 0.0, 1.0);
- } else {
- coords = (coords + vec2(1.0)) / vec2(2.0);
- frag_color = textureLod(source, coords, 0.0);
- }
+ vec2 coords = uv_interp;
+ vec2 offset = coords - eye_center;
+
+ // take aspect ratio into account
+ offset.y /= aspect_ratio;
+
+ // distort
+ vec2 offset_sq = offset * offset;
+ float radius_sq = offset_sq.x + offset_sq.y;
+ float radius_s4 = radius_sq * radius_sq;
+ float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
+ offset *= distortion_scale;
+
+ // reapply aspect ratio
+ offset.y *= aspect_ratio;
+
+ // add our eye center back in
+ coords = offset + eye_center;
+ coords /= upscale;
+
+ // and check our color
+ if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
+ frag_color = vec4(0.0, 0.0, 0.0, 1.0);
+ } else {
+ coords = (coords + vec2(1.0)) / vec2(2.0);
+ frag_color = textureLod(source, coords, 0.0);
+ }
}
-
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 03e187e5b0..f3cf4c9c5d 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -5,9 +5,11 @@ Import('env_modules')
env_mono = env_modules.Clone()
-from compat import byte_to_str
+# TODO move functions to their own modules
def make_cs_files_header(src, dst):
+ from compat import byte_to_str
+
with open(dst, 'w') as header:
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
header.write('#ifndef _CS_FILES_DATA_H\n')
@@ -16,14 +18,20 @@ def make_cs_files_header(src, dst):
header.write('#include "ustring.h"\n')
inserted_files = ''
import os
- for file in os.listdir(src):
- if file.endswith('.cs'):
- with open(os.path.join(src, file), 'rb') as f:
+ latest_mtime = 0
+ for root, _, files in os.walk(src):
+ files = [f for f in files if f.endswith('.cs')]
+ for file in files:
+ filepath = os.path.join(root, file)
+ filepath_src_rel = os.path.relpath(filepath, src)
+ mtime = os.path.getmtime(filepath)
+ latest_mtime = mtime if mtime > latest_mtime else latest_mtime
+ with open(filepath, 'rb') as f:
buf = f.read()
decomp_size = len(buf)
import zlib
buf = zlib.compress(buf)
- name = os.path.splitext(file)[0]
+ name = os.path.splitext(os.path.normpath(filepath_src_rel))[0].strip(os.sep).replace(os.sep, '_').replace('.', '_dotto_')
header.write('\nstatic const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
@@ -31,18 +39,13 @@ def make_cs_files_header(src, dst):
if i > 0:
header.write(', ')
header.write(byte_to_str(buf[buf_idx]))
- inserted_files += '\tr_files.insert("' + file + '", ' \
+ inserted_files += '\tr_files.insert("' + filepath_src_rel + '", ' \
'CompressedFile(_cs_' + name + '_compressed_size, ' \
'_cs_' + name + '_uncompressed_size, ' \
'_cs_' + name + '_compressed));\n'
header.write(' };\n')
- version_file = os.path.join(src, 'VERSION.txt')
- with open(version_file, 'r') as content_file:
- try:
- glue_version = int(content_file.read()) # make sure the format is valid
- header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
- except ValueError:
- raise ValueError('Invalid C# glue version in: ' + version_file)
+ glue_version = int(latest_mtime) # The latest modified time will do for now
+ header.write('\n#define CS_GLUE_VERSION UINT32_C(' + str(glue_version) + ')\n')
header.write('\nstruct CompressedFile\n' '{\n'
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
@@ -59,6 +62,7 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp')
if env['tools']:
env_mono.add_source_files(env.modules_sources, 'editor/*.cpp')
+ # NOTE: It is safe to generate this file here, since this is still execute serially
make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h')
vars = Variables()
@@ -75,6 +79,13 @@ else:
if ARGUMENTS.get('yolo_copy', False):
env_mono.Append(CPPDEFINES=['YOLO_COPY'])
+# Configure TLS checks
+
+import tls_configure
+conf = Configure(env_mono)
+tls_configure.configure(conf)
+env_mono = conf.Finish()
+
# Build GodotSharpTools solution
@@ -88,7 +99,7 @@ def find_msbuild_unix(filename):
hint_dirs = ['/opt/novell/mono/bin']
if sys.platform == 'darwin':
- hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin'] + hint_dirs
+ hint_dirs = ['/Library/Frameworks/Mono.framework/Versions/Current/bin', '/usr/local/var/homebrew/linked/mono/bin'] + hint_dirs
for hint_dir in hint_dirs:
hint_path = os.path.join(hint_dir, filename)
@@ -128,12 +139,13 @@ def find_msbuild_windows():
raise RuntimeError('Cannot find mono root directory')
framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
-
mono_bin_dir = os.path.join(mono_root, 'bin')
-
msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
if os.path.isfile(msbuild_mono):
+ # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
+ # building with Mono's MSBuild. They must point to the batch files
+ # in Mono's bin directory to make sure they are executed with Mono.
mono_msbuild_env = {
'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),
diff --git a/modules/mono/config.py b/modules/mono/config.py
index ebf8512fb6..70fd1a35f1 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -4,23 +4,15 @@ import os
import sys
import subprocess
+from distutils.version import LooseVersion
from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
-def find_file_in_dir(directory, files, prefix='', extension=''):
- if not extension.startswith('.'):
- extension = '.' + extension
- for curfile in files:
- if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
- return curfile
- return ''
-
-
def can_build(env, platform):
- if platform in ["javascript"]:
+ if platform in ['javascript']:
return False # Not yet supported
return True
@@ -30,6 +22,27 @@ def is_enabled():
return False
+def get_doc_classes():
+ return [
+ '@C#',
+ 'CSharpScript',
+ 'GodotSharp',
+ ]
+
+
+def get_doc_path():
+ return 'doc_classes'
+
+
+def find_file_in_dir(directory, files, prefix='', extension=''):
+ if not extension.startswith('.'):
+ extension = '.' + extension
+ for curfile in files:
+ if os.path.isfile(os.path.join(directory, prefix + curfile + extension)):
+ return curfile
+ return ''
+
+
def copy_file(src_dir, dst_dir, name):
from shutil import copyfile
@@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env):
def configure(env):
env.use_ptrcall = True
- env.add_module_version_string("mono")
+ env.add_module_version_string('mono')
envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
@@ -70,6 +83,8 @@ def configure(env):
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
if env['platform'] == 'windows':
+ mono_root = ''
+
if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
@@ -84,6 +99,9 @@ def configure(env):
if not mono_root:
raise RuntimeError('Mono installation directory not found')
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -149,20 +167,14 @@ def configure(env):
# We can't use pkg-config to link mono statically,
# but we can still use it to find the mono root directory
if not mono_root and mono_static:
- def pkgconfig_try_find_mono_root():
- tmpenv = Environment()
- tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
- tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
- for hint_dir in tmpenv['LIBPATH']:
- name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
- if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
- return os.path.join(hint_dir, '..')
- return ''
- mono_root = pkgconfig_try_find_mono_root()
+ mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
if not mono_root:
raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
if mono_root:
+ mono_version = mono_root_try_find_mono_version(mono_root)
+ configure_for_mono_version(env, mono_version)
+
mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path)
@@ -178,18 +190,18 @@ def configure(env):
if mono_static:
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
else:
raise RuntimeError('mono-static: Not supported on this platform')
else:
env.Append(LIBS=[mono_lib])
- if sys.platform == "darwin":
+ if sys.platform == 'darwin':
env.Append(LIBS=['iconv', 'pthread'])
- elif sys.platform == "linux" or sys.platform == "linux2":
+ elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
if not mono_static:
@@ -204,11 +216,14 @@ def configure(env):
else:
assert not mono_static
+ mono_version = pkgconfig_try_find_mono_version()
+ configure_for_mono_version(env, mono_version)
+
env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
mono_lib_path = ''
mono_so_name = ''
- mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
+ mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -230,13 +245,46 @@ def configure(env):
env.Append(LINKFLAGS='-rdynamic')
-def get_doc_classes():
- return [
- "@C#",
- "CSharpScript",
- "GodotSharp",
- ]
+def configure_for_mono_version(env, mono_version):
+ if mono_version is None:
+ raise RuntimeError('Mono JIT compiler version not found')
+ print('Mono JIT compiler version: ' + str(mono_version))
+ if mono_version >= LooseVersion("5.12.0"):
+ env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
-def get_doc_path():
- return "doc_classes"
+def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
+ tmpenv = Environment()
+ tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
+ tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
+ for hint_dir in tmpenv['LIBPATH']:
+ name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
+ if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
+ return os.path.join(hint_dir, '..')
+ return ''
+
+
+def pkgconfig_try_find_mono_version():
+ from compat import decode_utf8
+
+ lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
+ greater_version = None
+ for line in lines:
+ try:
+ version = LooseVersion(decode_utf8(line))
+ if greater_version is None or version > greater_version:
+ greater_version = version
+ except ValueError:
+ pass
+ return greater_version
+
+
+def mono_root_try_find_mono_version(mono_root):
+ from compat import decode_utf8
+
+ output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version'])
+ first_line = decode_utf8(output.splitlines()[0])
+ try:
+ return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
+ except (ValueError, IndexError):
+ return None
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 46c40b2690..d2a861dbe8 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -49,6 +49,8 @@
#include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h"
+#include "utils/macros.h"
+#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -119,7 +121,7 @@ void CSharpLanguage::init() {
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&gdsharp_editor_init_callback);
- GLOBAL_DEF("mono/export/include_scripts_content", true);
+ GLOBAL_DEF("mono/export/include_scripts_content", false);
#endif
}
@@ -296,30 +298,28 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
String script_template = "using " BINDINGS_NAMESPACE ";\n"
"using System;\n"
"\n"
- "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n"
+ "public class %CLASS% : %BASE%\n"
"{\n"
- " // Member variables here, example:\n"
+ " // Declare member variables here. Examples:\n"
" // private int a = 2;\n"
- " // private string b = \"textvar\";\n"
+ " // private string b = \"text\";\n"
"\n"
+ " // Called when the node enters the scene tree for the first time.\n"
" public override void _Ready()\n"
" {\n"
- " // Called every time the node is added to the scene.\n"
- " // Initialization here.\n"
" \n"
" }\n"
"\n"
- "// public override void _Process(float delta)\n"
- "// {\n"
- "// // Called every frame. Delta is time since last frame.\n"
- "// // Update game logic here.\n"
- "// \n"
- "// }\n"
+ "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n"
+ "// public override void _Process(float delta)\n"
+ "// {\n"
+ "// \n"
+ "// }\n"
"}\n";
String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
- script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name)
- .replace("%CLASS_NAME%", p_class_name);
+ script_template = script_template.replace("%BASE%", base_class_name)
+ .replace("%CLASS%", p_class_name);
Ref<CSharpScript> script;
script.instance();
@@ -476,7 +476,7 @@ String CSharpLanguage::_get_indentation() const {
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED
- // Printing an error here will result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>();
@@ -500,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- // Printing an error here could result in endless recursion, so we must be careful
+ _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
- MonoArray *frames = st_get_frames(p_stack_trace, &exc);
+ MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -523,16 +523,16 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
si.resize(frame_count);
for (int i = 0; i < frame_count; i++) {
- StackInfo &sif = si[i];
+ StackInfo &sif = si.write[i];
MonoObject *frame = mono_array_get(frames, MonoObject *, i);
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
- get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc);
+ get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc);
if (exc) {
- GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>();
}
@@ -561,12 +561,12 @@ void CSharpLanguage::frame() {
ERR_FAIL_NULL(thunk);
- MonoObject *ex;
- thunk(task_scheduler, &ex);
+ MonoException *exc = NULL;
+ thunk(task_scheduler, (MonoObject **)&exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL();
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
+ _UNREACHABLE_();
}
}
}
@@ -638,33 +638,41 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#ifdef TOOLS_ENABLED
void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
- if (gdmono->is_runtime_initialized()) {
+ if (!gdmono->is_runtime_initialized())
+ return;
- GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
+ GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
- String name = ProjectSettings::get_singleton()->get("application/config/name");
- if (name.empty()) {
- name = "UnnamedProject";
- }
+ String name = ProjectSettings::get_singleton()->get("application/config/name");
+ if (name.empty()) {
+ name = "UnnamedProject";
+ }
- if (proj_assembly) {
- String proj_asm_path = proj_assembly->get_path();
+ name += ".dll";
- if (!FileAccess::exists(proj_assembly->get_path())) {
- // Maybe it wasn't loaded from the default path, so check this as well
- proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name);
- if (!FileAccess::exists(proj_asm_path))
- return; // No assembly to load
- }
+ if (proj_assembly) {
+ String proj_asm_path = proj_assembly->get_path();
- if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
- return; // Already up to date
- } else {
- if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name)))
+ if (!FileAccess::exists(proj_assembly->get_path())) {
+ // Maybe it wasn't loaded from the default path, so check this as well
+ proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name);
+ if (!FileAccess::exists(proj_asm_path))
return; // No assembly to load
}
+
+ if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
+ return; // Already up to date
+ } else {
+ if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name)))
+ return; // No assembly to load
}
+ if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
+ return; // The core API assembly to load is invalidated
+
+ if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
+ return; // The editor API assembly to load is invalidated
+
#ifndef NO_THREADS
lock->lock();
#endif
@@ -728,6 +736,9 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
obj->get_script_instance()->get_property_state(state);
map[obj->get_instance_id()] = state;
obj->set_script(RefPtr());
+ } else {
+ // no instance found. Let's remove it so we don't loop forever
+ E->get()->placeholders.erase(E->get()->placeholders.front()->get());
}
}
@@ -739,16 +750,31 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
}
}
- if (gdmono->reload_scripts_domain() != OK)
+ if (gdmono->reload_scripts_domain() != OK) {
+ // Failed to reload the scripts domain
+ // Make sure to add the scripts back to their owners before returning
+ for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) {
+ Ref<CSharpScript> scr = E->key();
+ for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) {
+ Object *obj = ObjectDB::get_instance(F->key());
+ if (!obj)
+ continue;
+ obj->set_script(scr.get_ref_ptr());
+ // Save reload state for next time if not saved
+ if (!scr->pending_reload_state.has(obj->get_instance_id())) {
+ scr->pending_reload_state[obj->get_instance_id()] = F->get();
+ }
+ }
+ }
return;
+ }
for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> scr = E->key();
- scr->signals_invalidated = true;
scr->exports_invalidated = true;
+ scr->signals_invalidated = true;
scr->reload(p_soft_reload);
- scr->update_signals();
scr->update_exports();
//restore state if saved
@@ -771,6 +797,14 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
continue;
}
+ if (scr->valid && scr->is_tool() && obj->get_script_instance()->is_placeholder()) {
+ // Script instance was a placeholder, but now the script was built successfully and is a tool script.
+ // We have to replace the placeholder with an actual C# script instance.
+ scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(obj->get_script_instance()));
+ ScriptInstance *script_instance = scr->instance_create(obj);
+ obj->set_script_instance(script_instance); // Not necessary as it's already done in instance_create, but just in case...
+ }
+
for (List<Pair<StringName, Variant> >::Element *G = F->get().front(); G; G = G->next()) {
obj->get_script_instance()->set(G->get().first, G->get().second);
}
@@ -979,6 +1013,69 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
#endif
}
+void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
+
+ Reference *ref_owner = Object::cast_to<Reference>(p_object);
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!ref_owner);
+#endif
+
+ void *data = p_object->get_script_instance_binding(get_language_index());
+ if (!data)
+ return;
+ Ref<MonoGCHandle> &gchandle = ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->get();
+
+ if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ // The reference count was increased after the managed side was the only one referencing our owner.
+ // This means the owner is being referenced again by the unmanaged side,
+ // so the owner must hold the managed side alive again to avoid it from being GCed.
+
+ MonoObject *target = gchandle->get_target();
+ if (!target)
+ return; // Called after the managed side was collected, so nothing to do here
+
+ // Release the current weak handle and replace it with a strong handle.
+ uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(target);
+ gchandle->release();
+ gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
+ }
+}
+
+bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
+
+ Reference *ref_owner = Object::cast_to<Reference>(p_object);
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!ref_owner);
+#endif
+
+ int refcount = ref_owner->reference_get_count();
+
+ void *data = p_object->get_script_instance_binding(get_language_index());
+ if (!data)
+ return refcount == 0;
+ Ref<MonoGCHandle> &gchandle = ((Map<Object *, Ref<MonoGCHandle> >::Element *)data)->get();
+
+ if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ // If owner owner is no longer referenced by the unmanaged side,
+ // the managed instance takes responsibility of deleting the owner when GCed.
+
+ MonoObject *target = gchandle->get_target();
+ if (!target)
+ return refcount == 0; // Called after the managed side was collected, so nothing to do here
+
+ // Release the current strong handle and replace it with a weak handle.
+ uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(target);
+ gchandle->release();
+ gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
+
+ return false;
+ }
+
+ return refcount == 0;
+}
+
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) {
CSharpInstance *instance = memnew(CSharpInstance);
@@ -1078,11 +1175,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *value = property->get_value(mono_object, &exc);
if (exc) {
r_ret = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
} else {
r_ret = GDMonoMarshal::mono_object_to_variant(value);
}
@@ -1269,11 +1366,13 @@ void CSharpInstance::mono_object_disposed() {
void CSharpInstance::refcount_incremented() {
+#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
+#endif
Reference *ref_owner = Object::cast_to<Reference>(owner);
- if (ref_owner->reference_get_count() > 1) { // The managed side also holds a reference, hence 1 instead of 0
+ if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
@@ -1281,26 +1380,28 @@ void CSharpInstance::refcount_incremented() {
// Release the current weak handle and replace it with a strong handle.
uint32_t strong_gchandle = MonoGCHandle::make_strong_handle(gchandle->get_target());
gchandle->release();
- gchandle->set_handle(strong_gchandle);
+ gchandle->set_handle(strong_gchandle, MonoGCHandle::STRONG_HANDLE);
}
}
bool CSharpInstance::refcount_decremented() {
+#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
+#endif
Reference *ref_owner = Object::cast_to<Reference>(owner);
int refcount = ref_owner->reference_get_count();
- if (refcount == 1) { // The managed side also holds a reference, hence 1 instead of 0
+ if (refcount == 1 && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
// Release the current strong handle and replace it with a weak handle.
uint32_t weak_gchandle = MonoGCHandle::make_weak_handle(gchandle->get_target());
gchandle->release();
- gchandle->set_handle(weak_gchandle);
+ gchandle->set_handle(weak_gchandle, MonoGCHandle::WEAK_HANDLE);
return false;
}
@@ -1467,8 +1568,12 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List
bool CSharpScript::_update_exports() {
#ifdef TOOLS_ENABLED
- if (!valid)
+ if (!valid) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(true);
+ }
return false;
+ }
bool changed = false;
@@ -1490,12 +1595,12 @@ bool CSharpScript::_update_exports() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- MonoObject *ex = NULL;
- ctor->invoke(tmp_object, NULL, &ex);
+ MonoException *exc = NULL;
+ ctor->invoke(tmp_object, NULL, &exc);
- if (ex) {
+ if (exc) {
ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
- mono_print_unhandled_exception(ex);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
tmp_object = NULL;
ERR_FAIL_V(false);
}
@@ -1544,11 +1649,11 @@ bool CSharpScript::_update_exports() {
exported_members_cache.push_front(prop_info);
if (tmp_object) {
- MonoObject *exc = NULL;
+ MonoException *exc = NULL;
MonoObject *ret = property->get_value(tmp_object, &exc);
if (exc) {
exported_members_defval_cache[name] = Variant();
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::debug_print_unhandled_exception(exc);
} else {
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
}
@@ -1570,6 +1675,7 @@ bool CSharpScript::_update_exports() {
_update_exports_values(values, propnames);
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->set_build_failed(false);
E->get()->update(propnames, values);
}
}
@@ -1579,42 +1685,38 @@ bool CSharpScript::_update_exports() {
return false;
}
-bool CSharpScript::_update_signals() {
- if (!valid)
- return false;
-
- bool changed = false;
-
- if (signals_invalidated) {
- signals_invalidated = false;
+void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- GDMonoClass *top = script_class;
+ // no need to load the script's signals more than once
+ if (!signals_invalidated) {
+ return;
+ }
- _signals.clear();
- changed = true; // TODO Do a real check for change
+ // make sure this classes signals are empty when loading for the first time
+ _signals.clear();
- while (top && top != native) {
- const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
- for (int i = delegates.size() - 1; i >= 0; --i) {
- Vector<Argument> parameters;
+ GDMonoClass *top = p_class;
+ while (top && top != p_native_class) {
+ const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
+ for (int i = delegates.size() - 1; i >= 0; --i) {
+ Vector<Argument> parameters;
- GDMonoClass *delegate = delegates[i];
+ GDMonoClass *delegate = delegates[i];
- if (_get_signal(top, delegate, parameters)) {
- _signals[delegate->get_name()] = parameters;
- }
+ if (_get_signal(top, delegate, parameters)) {
+ _signals[delegate->get_name()] = parameters;
}
-
- top = top->get_parent_class();
}
+
+ top = top->get_parent_class();
}
- return changed;
+ signals_invalidated = false;
}
bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params) {
if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate);
+ MonoType *raw_type = p_delegate->get_mono_type();
if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) {
// Arguments are accessibles as arguments of .Invoke method
@@ -1684,7 +1786,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p
MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
- PropertyHint hint;
+ PropertyHint hint = PROPERTY_HINT_NONE;
String hint_string;
if (variant_type == Variant::NIL) {
@@ -1848,6 +1950,8 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class) {
top = top->get_parent_class();
}
+ script->load_script_signals(script->script_class, script->native);
+
return script;
}
@@ -1868,7 +1972,11 @@ bool CSharpScript::can_instance() const {
}
#endif
- return valid || (!tool && !ScriptServer::is_scripting_enabled());
+#ifdef TOOLS_ENABLED
+ return valid && (tool || ScriptServer::is_scripting_enabled());
+#else
+ return valid;
+#endif
}
StringName CSharpScript::get_instance_base_type() const {
@@ -1918,7 +2026,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Construct
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- ctor->invoke(mono_object, p_args, NULL);
+ ctor->invoke(mono_object, p_args);
// Tie managed to unmanaged
instance->gchandle = MonoGCHandle::create_strong(mono_object);
@@ -1966,33 +2074,23 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call
ScriptInstance *CSharpScript::instance_create(Object *p_this) {
- ERR_FAIL_COND_V(!valid, NULL);
-
- if (!tool && !ScriptServer::is_scripting_enabled()) {
-#ifdef TOOLS_ENABLED
- PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
- placeholders.insert(si);
- _update_exports();
- _update_signals();
- return si;
-#else
- return NULL;
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!valid);
#endif
- }
if (!script_class) {
if (GDMono::get_singleton()->get_project_assembly() == NULL) {
// The project assembly is not loaded
ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path());
ERR_FAIL_V(NULL);
+ } else {
+ // The project assembly is loaded, but the class could not found
+ ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
+ ERR_FAIL_V(NULL);
}
-
- // The project assembly is loaded, but the class could not found
- ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path());
- ERR_FAIL_V(NULL);
}
- update_signals();
+ ERR_FAIL_COND_V(!valid, NULL);
if (native) {
String native_name = native->get_name();
@@ -2009,6 +2107,18 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
return _create_instance(NULL, 0, p_this, Object::cast_to<Reference>(p_this), unchecked_error);
}
+PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_this) {
+
+#ifdef TOOLS_ENABLED
+ PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
+ placeholders.insert(si);
+ _update_exports();
+ return si;
+#else
+ return NULL;
+#endif
+}
+
bool CSharpScript::instance_has(const Object *p_this) const {
#ifndef NO_THREADS
@@ -2046,6 +2156,9 @@ void CSharpScript::set_source_code(const String &p_code) {
bool CSharpScript::has_method(const StringName &p_method) const {
+ if (!script_class)
+ return false;
+
return script_class->has_fetched_method_unknown_params(p_method);
}
@@ -2072,9 +2185,7 @@ Error CSharpScript::reload(bool p_keep_state) {
if (script_class) {
#ifdef DEBUG_ENABLED
- OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." +
- script_class->get_name() + " for script " + get_path() + "\n")
- .utf8());
+ print_verbose("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path());
#endif
tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
@@ -2114,6 +2225,8 @@ Error CSharpScript::reload(bool p_keep_state) {
top->fetch_methods_with_godot_api_checks(native);
top = top->get_parent_class();
}
+
+ load_script_signals(script_class, native);
}
return OK;
@@ -2173,10 +2286,6 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
}
}
-void CSharpScript::update_signals() {
- _update_signals();
-}
-
Ref<Script> CSharpScript::get_base_script() const {
// TODO search in metadata file once we have it, not important any way?
@@ -2241,9 +2350,10 @@ CSharpScript::CSharpScript() :
#ifdef TOOLS_ENABLED
source_changed_cache = false;
exports_invalidated = true;
- signals_invalidated = true;
#endif
+ signals_invalidated = true;
+
_resource_path_changed();
#ifdef DEBUG_ENABLED
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index cae2bbf40a..1a5d0c8a69 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -111,7 +111,7 @@ class CSharpScript : public Script {
void _clear();
- bool _update_signals();
+ void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
bool _update_exports();
@@ -139,6 +139,7 @@ public:
virtual bool can_instance() const;
virtual StringName get_instance_base_type() const;
virtual ScriptInstance *instance_create(Object *p_this);
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
@@ -149,7 +150,6 @@ public:
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
- virtual void update_signals();
/* TODO */ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
@@ -293,7 +293,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; }
+ /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; }
virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
@@ -346,6 +346,8 @@ public:
// Don't use these. I'm watching you
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
+ virtual void refcount_incremented_instance_binding(Object *p_object);
+ virtual bool refcount_decremented_instance_binding(Object *p_object);
#ifdef DEBUG_ENABLED
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 4c598d4f37..cc954348f4 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -100,8 +100,6 @@
#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary"
-#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object"
#define BINDINGS_GENERATOR_VERSION UINT32_C(2)
@@ -514,6 +512,15 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
data.resize(file_data.uncompressed_size);
Compression::decompress(data.ptrw(), file_data.uncompressed_size, file_data.data, file_data.compressed_size, Compression::MODE_DEFLATE);
+ String output_dir = output_file.get_base_dir();
+
+ if (!DirAccess::exists(output_dir)) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
+ Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(output_dir));
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ }
+
FileAccessRef file = FileAccess::open(output_file, FileAccess::WRITE);
ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE);
file->store_buffer(data.ptr(), data.size());
@@ -526,7 +533,6 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
cs_icalls_content.push_back("using System;\n"
"using System.Runtime.CompilerServices;\n"
- "using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK);
@@ -631,7 +637,6 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
cs_icalls_content.push_back("using System;\n"
"using System.Runtime.CompilerServices;\n"
- "using System.Collections.Generic;\n"
"\n");
cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
@@ -704,10 +709,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
List<String> output;
output.push_back("using System;\n"); // IntPtr
-
- if (itype.requires_collections)
- output.push_back("using System.Collections.Generic;\n"); // Dictionary
-
+ output.push_back("\n#pragma warning disable CS1591 // Disable warning: "
+ "'Missing XML comment for publicly visible type or member'\n");
output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
const DocData::ClassDoc *class_doc = itype.class_doc;
@@ -730,8 +733,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(INDENT1 "public ");
- bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
- output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class "));
+ if (itype.is_object_type) {
+ if (itype.is_singleton) {
+ output.push_back("static partial class ");
+ } else {
+ output.push_back(itype.is_instantiable ? "partial class " : "abstract partial class ");
+ }
+ } else {
+ output.push_back("partial class ");
+ }
output.push_back(itype.proxy_name);
if (itype.is_singleton) {
@@ -1027,7 +1037,10 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
custom_icalls.push_back(ctor_icall);
}
- output.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ output.push_back(INDENT1 CLOSE_BLOCK /* class */
+ CLOSE_BLOCK /* namespace */);
+
+ output.push_back("\n#pragma warning restore CS1591\n");
return _save_file(p_output_file, output);
}
@@ -1338,7 +1351,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.empty()) {
p_output.push_back("return " + im_call + ";\n");
} else {
- p_output.push_back(INDENT3);
p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
p_output.push_back("\n");
}
@@ -1653,7 +1665,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
"\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
"\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
p_output.push_back(real_argc_str);
- p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK);
+ p_output.push_back(" + i, &varargs.write[i]);\n\t" CLOSE_BLOCK);
} else {
p_output.push_back(c_in_statements);
p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
@@ -1768,6 +1780,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
continue;
}
+ if (!ClassDB::is_class_enabled(type_cname)) {
+ if (verbose_output)
+ WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled");
+ class_list.pop_front();
+ continue;
+ }
+
ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(type_cname);
TypeInterface itype = TypeInterface::create_object_type(type_cname, api_type);
@@ -1909,9 +1928,6 @@ void BindingsGenerator::_populate_object_type_interfaces() {
imethod.return_type.cname = Variant::get_type_name(return_info.type);
}
- if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
- itype.requires_collections = true;
-
for (int i = 0; i < argc; i++) {
PropertyInfo arginfo = method_info.arguments[i];
@@ -1933,9 +1949,6 @@ void BindingsGenerator::_populate_object_type_interfaces() {
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
- if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
- itype.requires_collections = true;
-
if (m && m->has_default_argument(i)) {
_default_argument_from_variant(m->get_default_argument(i), iarg);
}
@@ -2337,7 +2350,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
- INSERT_ARRAY(Array, object);
INSERT_ARRAY(PoolIntArray, int);
INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte);
@@ -2355,20 +2367,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#undef INSERT_ARRAY
+ // Array
+ itype = TypeInterface();
+ itype.name = "Array";
+ itype.cname = itype.name;
+ itype.proxy_name = itype.name;
+ itype.c_out = "\treturn memnew(Array(%1));\n";
+ itype.c_type = itype.name;
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
+ itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new " + itype.cs_type + "(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
+ builtin_types.insert(itype.cname, itype);
+
// Dictionary
itype = TypeInterface();
itype.name = "Dictionary";
itype.cname = itype.name;
- itype.proxy_name = "Dictionary<object, object>";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n";
- itype.c_arg_in = "&%s_in";
+ itype.proxy_name = itype.name;
+ itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
+ itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new " + itype.cs_type + "(%0);";
+ itype.im_type_in = "IntPtr";
+ itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
@@ -2413,9 +2441,6 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
else
iarg.type.cname = Variant::get_type_name(pi.type);
- if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
- r_itype.requires_collections = true;
-
if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0)
_default_argument_from_variant(Variant::construct(pi.type, NULL, 0, cerror), iarg);
@@ -2429,9 +2454,6 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
imethod.return_type.cname = Variant::get_type_name(mi.return_val.type);
}
- if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
- r_itype.requires_collections = true;
-
if (r_itype.class_doc) {
for (int i = 0; i < r_itype.class_doc->methods.size(); i++) {
if (r_itype.class_doc->methods[i].name == imethod.name) {
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 5b33a0e53f..735e7ce7cc 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -192,7 +192,7 @@ class BindingsGenerator {
/**
* Used only by Object-derived types.
- * Determines if this type is not virtual (incomplete).
+ * Determines if this type is not abstract (incomplete).
* e.g.: CanvasItem cannot be instantiated.
*/
bool is_instantiable;
@@ -204,12 +204,6 @@ class BindingsGenerator {
*/
bool memory_own;
- /**
- * Determines if the file must have a using directive for System.Collections.Generic
- * e.g.: When the generated class makes use of Dictionary
- */
- bool requires_collections;
-
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
// !! When renaming those fields, make sure to rename their references in the comments
@@ -295,7 +289,7 @@ class BindingsGenerator {
/**
* Type used for method signatures, both for parameters and the return type.
- * Same as [proxy_name] except for variable arguments (VarArg).
+ * Same as [proxy_name] except for variable arguments (VarArg) and collections (which include the namespace).
*/
String cs_type;
@@ -414,7 +408,6 @@ class BindingsGenerator {
is_instantiable = false;
memory_own = false;
- requires_collections = false;
c_arg_in = "%s";
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index e4269b0aec..bc95607743 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
Variant dir = p_dir;
Variant compile_items = p_files;
const Variant *args[2] = { &dir, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
Variant core_dll_path = p_core_dll_path;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
Variant name = p_name;
Variant compile_items = p_files;
const Variant *args[3] = { &dir, &name, &compile_items };
- MonoObject *ex = NULL;
- MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String());
}
@@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
Variant item_type = p_item_type;
Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include };
- MonoObject *ex = NULL;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex);
+ MonoException *exc = NULL;
+ klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index 43d58caad2..2faab1718d 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -64,6 +64,7 @@ String _find_build_engine_on_unix(const String &p_name) {
const char *locations[] = {
#ifdef OSX_ENABLED
"/Library/Frameworks/Mono.framework/Versions/Current/bin/",
+ "/usr/local/var/homebrew/linked/mono/bin/",
#endif
"/opt/novell/mono/bin/"
};
@@ -96,8 +97,7 @@ MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
}
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...\n");
+ print_verbose("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Trying with '" PROP_NAME_MSBUILD_MONO "'...");
} // FALL THROUGH
case GodotSharpBuilds::MSBUILD_MONO: {
String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
@@ -209,6 +209,8 @@ bool GodotSharpBuilds::build_api_sln(const String &p_name, const String &p_api_s
if (!FileAccess::exists(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)) {
@@ -512,14 +514,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *ctor_args[2] = { &solution, &config };
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
GDMonoMethod *ctor = klass->get_method(".ctor", 2);
- ctor->invoke(mono_object, ctor_args, &ex);
+ ctor->invoke(mono_object, ctor_args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
@@ -534,14 +536,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
- ex = NULL;
+ exc = NULL;
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
- build_method->invoke(mono_object, args, &ex);
+ build_method->invoke(mono_object, args, &exc);
- if (ex) {
+ if (exc) {
exited = true;
- GDMonoUtils::print_unhandled_exception(ex);
- String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex);
+ GDMonoUtils::debug_unhandled_exception(exc);
+ String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message);
ERR_FAIL();
@@ -553,8 +555,9 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
exited = true;
exit_code = klass->get_field("exitCode")->get_int_value(mono_object);
- if (exit_code != 0 && OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print(String("MSBuild finished with exit code " + itos(exit_code) + "\n").utf8());
+ if (exit_code != 0) {
+ print_verbose("MSBuild finished with exit code " + itos(exit_code));
+ }
build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
} else {
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index 998da8bda3..faeb58e5a7 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -278,13 +278,11 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
about_label->set_autowrap(true);
String about_text =
String("C# support in Godot Engine is a brand new feature and a work in progress.\n") +
- "It is at the alpha stage and thus not suitable for use in production.\n\n" +
- "As of Godot 3.0, C# support is not feature-complete and may crash in some situations. " +
- "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases, " +
+ "It is currently in an alpha stage and is not suitable for use in production.\n\n" +
+ "As of Godot 3.1, C# support is not feature-complete and may crash in some situations. " +
+ "Bugs and usability issues will be addressed gradually over future 3.x releases, " +
"including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
- "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " +
- "but not to share them as standalone binaries yet. This feature is of course high on the priority list and should be available as soon as possible.\n\n" +
- "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" +
+ "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc:\n\n" +
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
about_label->set_text(about_text);
diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp
index 48a285561d..9f05711fd6 100644
--- a/modules/mono/editor/monodevelop_instance.cpp
+++ b/modules/mono/editor/monodevelop_instance.cpp
@@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
ERR_FAIL_NULL(execute_method);
ERR_FAIL_COND(gc_handle.is_null());
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant files = p_files;
const Variant *args[1] = { &files };
- execute_method->invoke(gc_handle->get_target(), args, &ex);
+ execute_method->invoke(gc_handle->get_target(), args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
}
@@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
GDMonoMethod *ctor = klass->get_method(".ctor", 1);
- MonoObject *ex = NULL;
+ MonoException *exc = NULL;
Variant solution = p_solution;
const Variant *args[1] = { &solution };
- ctor->invoke(obj, args, &ex);
+ ctor->invoke(obj, args, &exc);
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL();
}
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
new file mode 100644
index 0000000000..bb218b49b7
--- /dev/null
+++ b/modules/mono/glue/collections_glue.cpp
@@ -0,0 +1,240 @@
+/*************************************************************************/
+/* collections_glue.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "collections_glue.h"
+
+#include <mono/metadata/exception.h>
+
+#include "../mono_gd/gd_mono_class.h"
+
+Array *godot_icall_Array_Ctor() {
+ return memnew(Array);
+}
+
+void godot_icall_Array_Dtor(Array *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
+}
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+int godot_icall_Array_Count(Array *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+ ptr->append(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Clear(Array *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
+}
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
+ int count = ptr->size();
+
+ if (mono_array_length(array) < (array_index + count)) {
+ MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ GDMonoUtils::set_pending_exception(exc);
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
+ mono_array_setref(array, array_index, boxed);
+ array_index++;
+ }
+}
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
+ return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+}
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
+}
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
+ int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
+ if (idx >= 0) {
+ ptr->remove(idx);
+ return true;
+ }
+ return false;
+}
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index) {
+ if (index < 0 || index > ptr->size()) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
+ return;
+ }
+ ptr->remove(index);
+}
+
+Dictionary *godot_icall_Dictionary_Ctor() {
+ return memnew(Dictionary);
+}
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
+ memdelete(ptr);
+}
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!exc);
+#endif
+ GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::set_pending_exception((MonoException *)exc);
+ return NULL;
+ }
+ return GDMonoMarshal::variant_to_mono_object(ret);
+}
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
+ return memnew(Array(ptr->keys()));
+}
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
+ return memnew(Array(ptr->values()));
+}
+
+int godot_icall_Dictionary_Count(Dictionary *ptr) {
+ return ptr->size();
+}
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL) {
+ GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
+ return;
+ }
+ ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
+}
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr) {
+ ptr->clear();
+}
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ // no dupes
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value);
+}
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
+ return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
+}
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
+ Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
+
+ // no dupes
+ Variant *ret = ptr->getptr(varKey);
+ if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
+ ptr->erase(varKey);
+ return true;
+ }
+
+ return false;
+}
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
+ Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
+ if (ret == NULL) {
+ *value = NULL;
+ return false;
+ }
+ *value = GDMonoMarshal::variant_to_mono_object(ret);
+ return true;
+}
+
+void godot_register_collections_icalls() {
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
+
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
+ mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
+}
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
new file mode 100644
index 0000000000..eb5ecfb725
--- /dev/null
+++ b/modules/mono/glue/collections_glue.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* collections_glue.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 COLLECTIONS_GLUE_H
+#define COLLECTIONS_GLUE_H
+
+#include "core/array.h"
+
+#include "../mono_gd/gd_mono_marshal.h"
+
+// Array
+
+Array *godot_icall_Array_Ctor();
+
+void godot_icall_Array_Dtor(Array *ptr);
+
+MonoObject *godot_icall_Array_At(Array *ptr, int index);
+
+void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
+
+int godot_icall_Array_Count(Array *ptr);
+
+void godot_icall_Array_Add(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Clear(Array *ptr);
+
+bool godot_icall_Array_Contains(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
+
+int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
+
+bool godot_icall_Array_Remove(Array *ptr, MonoObject *item);
+
+void godot_icall_Array_RemoveAt(Array *ptr, int index);
+
+// Dictionary
+
+Dictionary *godot_icall_Dictionary_Ctor();
+
+void godot_icall_Dictionary_Dtor(Dictionary *ptr);
+
+MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key);
+
+void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+Array *godot_icall_Dictionary_Keys(Dictionary *ptr);
+
+Array *godot_icall_Dictionary_Values(Dictionary *ptr);
+
+int godot_icall_Dictionary_Count(Dictionary *ptr);
+
+void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+void godot_icall_Dictionary_Clear(Dictionary *ptr);
+
+bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
+
+bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
+
+bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
+
+// Register internal calls
+
+void godot_register_collections_icalls();
+
+#endif // COLLECTIONS_GLUE_H
diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs
index 39f2d2ed51..0df2e615f1 100644
--- a/modules/mono/glue/cs_files/AABB.cs
+++ b/modules/mono/glue/cs_files/AABB.cs
@@ -15,39 +15,33 @@ namespace Godot
{
public struct AABB : IEquatable<AABB>
{
- private Vector3 position;
- private Vector3 size;
+ private Vector3 _position;
+ private Vector3 _size;
public Vector3 Position
{
- get
- {
- return position;
- }
+ get { return _position; }
+ set { _position = value; }
}
public Vector3 Size
{
- get
- {
- return size;
- }
+ get { return _size; }
+ set { _size = value; }
}
public Vector3 End
{
- get
- {
- return position + size;
- }
+ get { return _position + _size; }
+ set { _size = value - _position; }
}
public bool Encloses(AABB with)
{
- Vector3 src_min = position;
- Vector3 src_max = position + size;
- Vector3 dst_min = with.position;
- Vector3 dst_max = with.position + with.size;
+ Vector3 src_min = _position;
+ Vector3 src_max = _position + _size;
+ Vector3 dst_min = with._position;
+ Vector3 dst_max = with._position + with._size;
return src_min.x <= dst_min.x &&
src_max.x > dst_max.x &&
@@ -59,8 +53,8 @@ namespace Godot
public AABB Expand(Vector3 to_point)
{
- Vector3 begin = position;
- Vector3 end = position + size;
+ Vector3 begin = _position;
+ Vector3 end = _position + _size;
if (to_point.x < begin.x)
begin.x = to_point.x;
@@ -81,7 +75,7 @@ namespace Godot
public real_t GetArea()
{
- return size.x * size.y * size.z;
+ return _size.x * _size.y * _size.z;
}
public Vector3 GetEndpoint(int idx)
@@ -89,21 +83,21 @@ namespace Godot
switch (idx)
{
case 0:
- return new Vector3(position.x, position.y, position.z);
+ return new Vector3(_position.x, _position.y, _position.z);
case 1:
- return new Vector3(position.x, position.y, position.z + size.z);
+ return new Vector3(_position.x, _position.y, _position.z + _size.z);
case 2:
- return new Vector3(position.x, position.y + size.y, position.z);
+ return new Vector3(_position.x, _position.y + _size.y, _position.z);
case 3:
- return new Vector3(position.x, position.y + size.y, position.z + size.z);
+ return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z);
case 4:
- return new Vector3(position.x + size.x, position.y, position.z);
+ return new Vector3(_position.x + _size.x, _position.y, _position.z);
case 5:
- return new Vector3(position.x + size.x, position.y, position.z + size.z);
+ return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z);
case 6:
- return new Vector3(position.x + size.x, position.y + size.y, position.z);
+ return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z);
case 7:
- return new Vector3(position.x + size.x, position.y + size.y, position.z + size.z);
+ return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z);
default:
throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
}
@@ -112,15 +106,15 @@ namespace Godot
public Vector3 GetLongestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y > max_size)
+ if (_size.y > max_size)
{
axis = new Vector3(0f, 1f, 0f);
- max_size = size.y;
+ max_size = _size.y;
}
- if (size.z > max_size)
+ if (_size.z > max_size)
{
axis = new Vector3(0f, 0f, 1f);
}
@@ -131,15 +125,15 @@ namespace Godot
public Vector3.Axis GetLongestAxisIndex()
{
var axis = Vector3.Axis.X;
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y > max_size)
+ if (_size.y > max_size)
{
axis = Vector3.Axis.Y;
- max_size = size.y;
+ max_size = _size.y;
}
- if (size.z > max_size)
+ if (_size.z > max_size)
{
axis = Vector3.Axis.Z;
}
@@ -149,13 +143,13 @@ namespace Godot
public real_t GetLongestAxisSize()
{
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y > max_size)
- max_size = size.y;
+ if (_size.y > max_size)
+ max_size = _size.y;
- if (size.z > max_size)
- max_size = size.z;
+ if (_size.z > max_size)
+ max_size = _size.z;
return max_size;
}
@@ -163,15 +157,15 @@ namespace Godot
public Vector3 GetShortestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y < max_size)
+ if (_size.y < max_size)
{
axis = new Vector3(0f, 1f, 0f);
- max_size = size.y;
+ max_size = _size.y;
}
- if (size.z < max_size)
+ if (_size.z < max_size)
{
axis = new Vector3(0f, 0f, 1f);
}
@@ -182,15 +176,15 @@ namespace Godot
public Vector3.Axis GetShortestAxisIndex()
{
var axis = Vector3.Axis.X;
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y < max_size)
+ if (_size.y < max_size)
{
axis = Vector3.Axis.Y;
- max_size = size.y;
+ max_size = _size.y;
}
- if (size.z < max_size)
+ if (_size.z < max_size)
{
axis = Vector3.Axis.Z;
}
@@ -200,21 +194,21 @@ namespace Godot
public real_t GetShortestAxisSize()
{
- real_t max_size = size.x;
+ real_t max_size = _size.x;
- if (size.y < max_size)
- max_size = size.y;
+ if (_size.y < max_size)
+ max_size = _size.y;
- if (size.z < max_size)
- max_size = size.z;
+ if (_size.z < max_size)
+ max_size = _size.z;
return max_size;
}
public Vector3 GetSupport(Vector3 dir)
{
- Vector3 half_extents = size * 0.5f;
- Vector3 ofs = position + half_extents;
+ Vector3 half_extents = _size * 0.5f;
+ Vector3 ofs = _position + half_extents;
return ofs + new Vector3(
dir.x > 0f ? -half_extents.x : half_extents.x,
@@ -226,39 +220,39 @@ namespace Godot
{
var res = this;
- res.position.x -= by;
- res.position.y -= by;
- res.position.z -= by;
- res.size.x += 2.0f * by;
- res.size.y += 2.0f * by;
- res.size.z += 2.0f * by;
+ res._position.x -= by;
+ res._position.y -= by;
+ res._position.z -= by;
+ res._size.x += 2.0f * by;
+ res._size.y += 2.0f * by;
+ res._size.z += 2.0f * by;
return res;
}
public bool HasNoArea()
{
- return size.x <= 0f || size.y <= 0f || size.z <= 0f;
+ return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f;
}
public bool HasNoSurface()
{
- return size.x <= 0f && size.y <= 0f && size.z <= 0f;
+ return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f;
}
public bool HasPoint(Vector3 point)
{
- if (point.x < position.x)
+ if (point.x < _position.x)
return false;
- if (point.y < position.y)
+ if (point.y < _position.y)
return false;
- if (point.z < position.z)
+ if (point.z < _position.z)
return false;
- if (point.x > position.x + size.x)
+ if (point.x > _position.x + _size.x)
return false;
- if (point.y > position.y + size.y)
+ if (point.y > _position.y + _size.y)
return false;
- if (point.z > position.z + size.z)
+ if (point.z > _position.z + _size.z)
return false;
return true;
@@ -266,10 +260,10 @@ namespace Godot
public AABB Intersection(AABB with)
{
- Vector3 src_min = position;
- Vector3 src_max = position + size;
- Vector3 dst_min = with.position;
- Vector3 dst_max = with.position + with.size;
+ Vector3 src_min = _position;
+ Vector3 src_max = _position + _size;
+ Vector3 dst_min = with._position;
+ Vector3 dst_max = with._position + with._size;
Vector3 min, max;
@@ -302,17 +296,17 @@ namespace Godot
public bool Intersects(AABB with)
{
- if (position.x >= with.position.x + with.size.x)
+ if (_position.x >= with._position.x + with._size.x)
return false;
- if (position.x + size.x <= with.position.x)
+ if (_position.x + _size.x <= with._position.x)
return false;
- if (position.y >= with.position.y + with.size.y)
+ if (_position.y >= with._position.y + with._size.y)
return false;
- if (position.y + size.y <= with.position.y)
+ if (_position.y + _size.y <= with._position.y)
return false;
- if (position.z >= with.position.z + with.size.z)
+ if (_position.z >= with._position.z + with._size.z)
return false;
- if (position.z + size.z <= with.position.z)
+ if (_position.z + _size.z <= with._position.z)
return false;
return true;
@@ -322,14 +316,14 @@ namespace Godot
{
Vector3[] points =
{
- new Vector3(position.x, position.y, position.z),
- new Vector3(position.x, position.y, position.z + size.z),
- new Vector3(position.x, position.y + size.y, position.z),
- new Vector3(position.x, position.y + size.y, position.z + size.z),
- new Vector3(position.x + size.x, position.y, position.z),
- new Vector3(position.x + size.x, position.y, position.z + size.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z + size.z)
+ new Vector3(_position.x, _position.y, _position.z),
+ new Vector3(_position.x, _position.y, _position.z + _size.z),
+ new Vector3(_position.x, _position.y + _size.y, _position.z),
+ new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z),
+ new Vector3(_position.x + _size.x, _position.y, _position.z),
+ new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z),
+ new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z),
+ new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z)
};
bool over = false;
@@ -355,8 +349,8 @@ namespace Godot
{
real_t seg_from = from[i];
real_t seg_to = to[i];
- real_t box_begin = position[i];
- real_t box_end = box_begin + size[i];
+ real_t box_begin = _position[i];
+ real_t box_end = box_begin + _size[i];
real_t cmin, cmax;
if (seg_from < seg_to)
@@ -394,10 +388,10 @@ namespace Godot
public AABB Merge(AABB with)
{
- Vector3 beg_1 = position;
- Vector3 beg_2 = with.position;
- var end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
- var end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
+ Vector3 beg_1 = _position;
+ Vector3 beg_2 = with._position;
+ var end_1 = new Vector3(_size.x, _size.y, _size.z) + beg_1;
+ var end_2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg_2;
var min = new Vector3(
beg_1.x < beg_2.x ? beg_1.x : beg_2.x,
@@ -417,8 +411,8 @@ namespace Godot
// Constructors
public AABB(Vector3 position, Vector3 size)
{
- this.position = position;
- this.size = size;
+ _position = position;
+ _size = size;
}
public static bool operator ==(AABB left, AABB right)
@@ -443,20 +437,20 @@ namespace Godot
public bool Equals(AABB other)
{
- return position == other.position && size == other.size;
+ return _position == other._position && _size == other._size;
}
public override int GetHashCode()
{
- return position.GetHashCode() ^ size.GetHashCode();
+ return _position.GetHashCode() ^ _size.GetHashCode();
}
public override string ToString()
{
return String.Format("{0} - {1}", new object[]
{
- position.ToString(),
- size.ToString()
+ _position.ToString(),
+ _size.ToString()
});
}
@@ -464,8 +458,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- position.ToString(format),
- size.ToString(format)
+ _position.ToString(format),
+ _size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs
new file mode 100644
index 0000000000..2f0185b1e3
--- /dev/null
+++ b/modules/mono/glue/cs_files/Array.cs
@@ -0,0 +1,340 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot.Collections
+{
+ class ArraySafeHandle : SafeHandle
+ {
+ public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Array.godot_icall_Array_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Add(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
+
+ ArraySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Array()
+ {
+ safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ safeHandle = new ArraySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ return godot_icall_Array_At(GetPtr(), index);
+ }
+ set
+ {
+ godot_icall_Array_SetAt(GetPtr(), index, value);
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Array_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object item)
+ {
+ godot_icall_Array_Add(GetPtr(), item);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Array_Clear(GetPtr());
+ }
+
+ public bool Contains(object item)
+ {
+ return godot_icall_Array_Contains(GetPtr(), item);
+ }
+
+ public void CopyTo(object[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // Internal call may throw ArgumentException
+ godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex);
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return godot_icall_Array_At(GetPtr(), i);
+ }
+ }
+
+ public int IndexOf(object item)
+ {
+ return godot_icall_Array_IndexOf(GetPtr(), item);
+ }
+
+ public void Insert(int index, object item)
+ {
+ godot_icall_Array_Insert(GetPtr(), index, item);
+ }
+
+ public bool Remove(object item)
+ {
+ return godot_icall_Array_Remove(GetPtr(), item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ godot_icall_Array_RemoveAt(GetPtr(), index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ {
+ Array objectArray;
+
+ public Array()
+ {
+ objectArray = new Array();
+ }
+
+ public Array(Array array)
+ {
+ objectArray = array;
+ }
+
+ internal Array(IntPtr handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ internal Array(ArraySafeHandle handle)
+ {
+ objectArray = new Array(handle);
+ }
+
+ public static explicit operator Array(Array<T> from)
+ {
+ return from.objectArray;
+ }
+
+ public T this[int index]
+ {
+ get
+ {
+ return (T)objectArray[index];
+ }
+ set
+ {
+ objectArray[index] = value;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectArray.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectArray.IsReadOnly;
+ }
+ }
+
+ public void Add(T item)
+ {
+ objectArray.Add(item);
+ }
+
+ public void Clear()
+ {
+ objectArray.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return objectArray.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+
+ // TODO This may be quite slow because every element access is an internal call.
+ // It could be moved entirely to an internal call if we find out how to do the cast there.
+
+ int count = objectArray.Count;
+
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = (T)objectArray[i];
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ int count = objectArray.Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ yield return (T)objectArray[i];
+ }
+ }
+
+ public int IndexOf(T item)
+ {
+ return objectArray.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ objectArray.Insert(index, item);
+ }
+
+ public bool Remove(T item)
+ {
+ return objectArray.Remove(item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ objectArray.RemoveAt(index);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return objectArray.GetPtr();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs b/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs
new file mode 100644
index 0000000000..6adf044886
--- /dev/null
+++ b/modules/mono/glue/cs_files/Attributes/ExportAttribute.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public class ExportAttribute : Attribute
+ {
+ private PropertyHint hint;
+ private string hintString;
+
+ public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
+ {
+ this.hint = hint;
+ this.hintString = hintString;
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs
index 55848769d5..55848769d5 100644
--- a/modules/mono/glue/cs_files/GodotMethodAttribute.cs
+++ b/modules/mono/glue/cs_files/Attributes/GodotMethodAttribute.cs
diff --git a/modules/mono/glue/cs_files/RPCAttributes.cs b/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs
index 6bf9560bfa..6bf9560bfa 100644
--- a/modules/mono/glue/cs_files/RPCAttributes.cs
+++ b/modules/mono/glue/cs_files/Attributes/RPCAttributes.cs
diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs
index 3957387be9..3957387be9 100644
--- a/modules/mono/glue/cs_files/SignalAttribute.cs
+++ b/modules/mono/glue/cs_files/Attributes/SignalAttribute.cs
diff --git a/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs b/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs
new file mode 100644
index 0000000000..d0437409af
--- /dev/null
+++ b/modules/mono/glue/cs_files/Attributes/ToolAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public class ToolAttribute : Attribute {}
+}
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index aa49a5e04f..10286f3832 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -343,17 +343,17 @@ namespace Godot
{
var tr = this;
- real_t temp = this[0, 1];
- this[0, 1] = this[1, 0];
- this[1, 0] = temp;
+ real_t temp = tr[0, 1];
+ tr[0, 1] = tr[1, 0];
+ tr[1, 0] = temp;
- temp = this[0, 2];
- this[0, 2] = this[2, 0];
- this[2, 0] = temp;
+ temp = tr[0, 2];
+ tr[0, 2] = tr[2, 0];
+ tr[2, 0] = temp;
- temp = this[1, 2];
- this[1, 2] = this[2, 1];
- this[2, 1] = temp;
+ temp = tr[1, 2];
+ tr[1, 2] = tr[2, 1];
+ tr[2, 1] = temp;
return tr;
}
@@ -426,7 +426,7 @@ namespace Godot
public Basis(Quat quat)
{
- real_t s = 2.0f / quat.LengthSquared();
+ real_t s = 2.0f / quat.LengthSquared;
real_t xs = quat.x * s;
real_t ys = quat.y * s;
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index e0d6d27840..49e04b333a 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -258,11 +258,6 @@ namespace Godot
return res;
}
- public float Gray()
- {
- return (r + g + b) / 3.0f;
- }
-
public Color Inverted()
{
return new Color(
@@ -293,28 +288,80 @@ namespace Godot
return res;
}
- public int ToRgba32()
+ public int ToAbgr32()
{
- int c = (byte)(r * 255);
+ int c = (byte)Math.Round(a * 255);
c <<= 8;
- c |= (byte)(g * 255);
+ c |= (byte)Math.Round(b * 255);
c <<= 8;
- c |= (byte)(b * 255);
+ c |= (byte)Math.Round(g * 255);
c <<= 8;
- c |= (byte)(a * 255);
+ c |= (byte)Math.Round(r * 255);
+
+ return c;
+ }
+
+ public long ToAbgr64()
+ {
+ long c = (ushort)Math.Round(a * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(b * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(g * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(r * 65535);
return c;
}
public int ToArgb32()
{
- int c = (byte)(a * 255);
+ int c = (byte)Math.Round(a * 255);
c <<= 8;
- c |= (byte)(r * 255);
+ c |= (byte)Math.Round(r * 255);
c <<= 8;
- c |= (byte)(g * 255);
+ c |= (byte)Math.Round(g * 255);
c <<= 8;
- c |= (byte)(b * 255);
+ c |= (byte)Math.Round(b * 255);
+
+ return c;
+ }
+
+ public long ToArgb64()
+ {
+ long c = (ushort)Math.Round(a * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(r * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(g * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(b * 65535);
+
+ return c;
+ }
+
+ public int ToRgba32()
+ {
+ int c = (byte)Math.Round(r * 255);
+ c <<= 8;
+ c |= (byte)Math.Round(g * 255);
+ c <<= 8;
+ c |= (byte)Math.Round(b * 255);
+ c <<= 8;
+ c |= (byte)Math.Round(a * 255);
+
+ return c;
+ }
+
+ public long ToRgba64()
+ {
+ long c = (ushort)Math.Round(r * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(g * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(b * 65535);
+ c <<= 16;
+ c |= (ushort)Math.Round(a * 65535);
return c;
}
@@ -353,6 +400,17 @@ namespace Godot
r = (rgba & 0xFF) / 255.0f;
}
+ public Color(long rgba)
+ {
+ a = (rgba & 0xFFFF) / 65535.0f;
+ rgba >>= 16;
+ b = (rgba & 0xFFFF) / 65535.0f;
+ rgba >>= 16;
+ g = (rgba & 0xFFFF) / 65535.0f;
+ rgba >>= 16;
+ r = (rgba & 0xFFFF) / 65535.0f;
+ }
+
private static int _parse_col(string str, int ofs)
{
int ig = 0;
@@ -392,7 +450,7 @@ namespace Godot
private String _to_hex(float val)
{
- var v = (int) Mathf.Clamp(val * 255.0f, 0, 255);
+ int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
var ret = string.Empty;
diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs
new file mode 100644
index 0000000000..64cb9f935d
--- /dev/null
+++ b/modules/mono/glue/cs_files/Dictionary.cs
@@ -0,0 +1,406 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot.Collections
+{
+ class DictionarySafeHandle : SafeHandle
+ {
+ public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+ {
+ this.handle = handle;
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Dictionary.godot_icall_Dictionary_Dtor(handle);
+ return true;
+ }
+ }
+
+ public class Dictionary :
+ IDictionary<object, object>,
+ ICollection<KeyValuePair<object, object>>,
+ IEnumerable<KeyValuePair<object, object>>,
+ IDisposable
+ {
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
+
+ DictionarySafeHandle safeHandle;
+ bool disposed = false;
+
+ public Dictionary()
+ {
+ safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ safeHandle = handle;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ safeHandle = new DictionarySafeHandle(handle);
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return safeHandle.DangerousGetHandle();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (safeHandle != null)
+ {
+ safeHandle.Dispose();
+ safeHandle = null;
+ }
+
+ disposed = true;
+ }
+
+ public object this[object key]
+ {
+ get
+ {
+ return godot_icall_Dictionary_GetValue(GetPtr(), key);
+ }
+ set
+ {
+ godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ }
+ }
+
+ public ICollection<object> Keys
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<object> Values
+ {
+ get
+ {
+ IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
+ return new Array(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return godot_icall_Dictionary_Count(GetPtr());
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public void Add(object key, object value)
+ {
+ godot_icall_Dictionary_Add(GetPtr(), key, value);
+ }
+
+ public void Add(KeyValuePair<object, object> item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ godot_icall_Dictionary_Clear(GetPtr());
+ }
+
+ public bool Contains(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool ContainsKey(object key)
+ {
+ return godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ }
+
+ public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array keys = (Array)Keys;
+ Array values = (Array)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<object, object>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(object key)
+ {
+ return godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ }
+
+ public bool Remove(KeyValuePair<object, object> item)
+ {
+ return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
+ }
+
+ public bool TryGetValue(object key, out object value)
+ {
+ object retValue;
+ bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue);
+ value = found ? retValue : default(object);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+
+ public class Dictionary<TKey, TValue> :
+ IDictionary<TKey, TValue>,
+ ICollection<KeyValuePair<TKey, TValue>>,
+ IEnumerable<KeyValuePair<TKey, TValue>>
+ {
+ Dictionary objectDict;
+
+ public Dictionary()
+ {
+ objectDict = new Dictionary();
+ }
+
+ public Dictionary(Dictionary dictionary)
+ {
+ objectDict = dictionary;
+ }
+
+ internal Dictionary(IntPtr handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ internal Dictionary(DictionarySafeHandle handle)
+ {
+ objectDict = new Dictionary(handle);
+ }
+
+ public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
+ {
+ return from.objectDict;
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return (TValue)objectDict[key];
+ }
+ set
+ {
+ objectDict[key] = value;
+ }
+ }
+
+ public ICollection<TKey> Keys
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
+ return new Array<TKey>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public ICollection<TValue> Values
+ {
+ get
+ {
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
+ return new Array<TValue>(new ArraySafeHandle(handle));
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return objectDict.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return objectDict.IsReadOnly;
+ }
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ objectDict.Add(key, value);
+ }
+
+ public void Add(KeyValuePair<TKey, TValue> item)
+ {
+ objectDict.Add(item.Key, item.Value);
+ }
+
+ public void Clear()
+ {
+ objectDict.Clear();
+ }
+
+ public bool Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return objectDict.ContainsKey(key);
+ }
+
+ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ arrayIndex++;
+ }
+ }
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ // TODO 3 internal calls, can reduce to 1
+ Array<TKey> keys = (Array<TKey>)Keys;
+ Array<TValue> values = (Array<TValue>)Values;
+ int count = Count;
+
+ for (int i = 0; i < count; i++)
+ {
+ // TODO 2 internal calls, can reduce to 1
+ yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ }
+ }
+
+ public bool Remove(TKey key)
+ {
+ return objectDict.Remove(key);
+ }
+
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value));
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ object retValue;
+ bool found = objectDict.TryGetValue(key, out retValue);
+ value = found ? (TValue)retValue : default(TValue);
+ return found;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ internal IntPtr GetPtr()
+ {
+ return objectDict.GetPtr();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/ExportAttribute.cs b/modules/mono/glue/cs_files/ExportAttribute.cs
deleted file mode 100644
index e6f569e1bb..0000000000
--- a/modules/mono/glue/cs_files/ExportAttribute.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Godot
-{
- [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public class ExportAttribute : Attribute
- {
- private PropertyHint hint;
- private string hintString;
-
- public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
- {
- this.hint = hint;
- this.hintString = hintString;
- }
- }
-}
diff --git a/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs b/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs
new file mode 100644
index 0000000000..71534d7782
--- /dev/null
+++ b/modules/mono/glue/cs_files/Extensions/NodeExtensions.cs
@@ -0,0 +1,45 @@
+namespace Godot
+{
+ public partial class Node
+ {
+ public T GetNode<T>(NodePath path) where T : Godot.Node
+ {
+ return (T)GetNode(path);
+ }
+
+ public T GetNodeOrNull<T>(NodePath path) where T : Godot.Node
+ {
+ return GetNode(path) as T;
+ }
+
+ public T GetChild<T>(int idx) where T : Godot.Node
+ {
+ return (T)GetChild(idx);
+ }
+
+ public T GetChildOrNull<T>(int idx) where T : Godot.Node
+ {
+ return GetChild(idx) as T;
+ }
+
+ public T GetOwner<T>() where T : Godot.Node
+ {
+ return (T)GetOwner();
+ }
+
+ public T GetOwnerOrNull<T>() where T : Godot.Node
+ {
+ return GetOwner() as T;
+ }
+
+ public T GetParent<T>() where T : Godot.Node
+ {
+ return (T)GetParent();
+ }
+
+ public T GetParentOrNull<T>() where T : Godot.Node
+ {
+ return GetParent() as T;
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs b/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs
new file mode 100644
index 0000000000..5c9e6609f4
--- /dev/null
+++ b/modules/mono/glue/cs_files/Extensions/ObjectExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Godot
+{
+ public partial class Object
+ {
+ public static bool IsInstanceValid(Object instance)
+ {
+ return instance != null && instance.NativeInstance != IntPtr.Zero;
+ }
+
+ public static WeakRef WeakRef(Object obj)
+ {
+ return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj));
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs
new file mode 100644
index 0000000000..ceecc589e6
--- /dev/null
+++ b/modules/mono/glue/cs_files/Extensions/ResourceLoaderExtensions.cs
@@ -0,0 +1,10 @@
+namespace Godot
+{
+ public static partial class ResourceLoader
+ {
+ public static T Load<T>(string path) where T : Godot.Resource
+ {
+ return (T) Load(path);
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs
index ec1534cb9a..0a5d703f27 100644
--- a/modules/mono/glue/cs_files/GD.cs
+++ b/modules/mono/glue/cs_files/GD.cs
@@ -1,4 +1,9 @@
using System;
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
// TODO: Add comments describing what this class does. It is not obvious.
@@ -16,22 +21,22 @@ namespace Godot
return NativeCalls.godot_icall_Godot_convert(what, type);
}
- public static float Db2Linear(float db)
+ public static real_t Db2Linear(real_t db)
{
- return (float)Math.Exp(db * 0.11512925464970228420089957273422);
+ return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
- public static float Dectime(float value, float amount, float step)
+ public static real_t DecTime(real_t value, real_t amount, real_t step)
{
- float sgn = value < 0 ? -1.0f : 1.0f;
- float val = Mathf.Abs(value);
+ real_t sgn = Mathf.Sign(value);
+ real_t val = Mathf.Abs(value);
val -= amount * step;
- if (val < 0.0f)
- val = 0.0f;
+ if (val < 0)
+ val = 0;
return val * sgn;
}
- public static FuncRef Funcref(Object instance, string funcname)
+ public static FuncRef FuncRef(Object instance, string funcname)
{
var ret = new FuncRef();
ret.SetInstance(instance);
@@ -49,9 +54,9 @@ namespace Godot
return NativeCalls.godot_icall_Godot_instance_from_id(instanceId);
}
- public static double Linear2Db(double linear)
+ public static real_t Linear2Db(real_t linear)
{
- return Math.Log(linear) * 8.6858896380650365530225783783321;
+ return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
}
public static Resource Load(string path)
@@ -59,6 +64,11 @@ namespace Godot
return ResourceLoader.Load(path);
}
+ public static T Load<T>(string path) where T : Godot.Resource
+ {
+ return (T) ResourceLoader.Load(path);
+ }
+
public static void Print(params object[] what)
{
NativeCalls.godot_icall_Godot_print(what);
@@ -69,22 +79,22 @@ namespace Godot
Print(System.Environment.StackTrace);
}
- public static void Printerr(params object[] what)
+ public static void PrintErr(params object[] what)
{
NativeCalls.godot_icall_Godot_printerr(what);
}
- public static void Printraw(params object[] what)
+ public static void PrintRaw(params object[] what)
{
NativeCalls.godot_icall_Godot_printraw(what);
}
- public static void Prints(params object[] what)
+ public static void PrintS(params object[] what)
{
NativeCalls.godot_icall_Godot_prints(what);
}
- public static void Printt(params object[] what)
+ public static void PrintT(params object[] what)
{
NativeCalls.godot_icall_Godot_printt(what);
}
@@ -182,10 +192,5 @@ namespace Godot
{
return NativeCalls.godot_icall_Godot_var2str(var);
}
-
- public static WeakRef Weakref(Object obj)
- {
- return NativeCalls.godot_icall_Godot_weakref(Object.GetPtr(obj));
- }
}
}
diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs
deleted file mode 100644
index b5aa1a5389..0000000000
--- a/modules/mono/glue/cs_files/IAwaiter.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace Godot
-{
- public interface IAwaiter : INotifyCompletion
- {
- bool IsCompleted { get; }
-
- void GetResult();
- }
-
- public interface IAwaiter<out TResult> : INotifyCompletion
- {
- bool IsCompleted { get; }
-
- TResult GetResult();
- }
-}
diff --git a/modules/mono/glue/cs_files/IAwaitable.cs b/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs
index 0397957d00..0397957d00 100644
--- a/modules/mono/glue/cs_files/IAwaitable.cs
+++ b/modules/mono/glue/cs_files/Interfaces/IAwaitable.cs
diff --git a/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs b/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs
new file mode 100644
index 0000000000..d3be9d781c
--- /dev/null
+++ b/modules/mono/glue/cs_files/Interfaces/IAwaiter.cs
@@ -0,0 +1,18 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot
+{
+ public interface IAwaiter : INotifyCompletion
+ {
+ bool IsCompleted { get; }
+
+ void GetResult();
+ }
+
+ public interface IAwaiter<out TResult> : INotifyCompletion
+ {
+ bool IsCompleted { get; }
+
+ TResult GetResult();
+ }
+}
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index ff4477cc6c..f7699a15bf 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -1,36 +1,18 @@
using System;
-using System.Collections.Generic;
+using Godot.Collections;
namespace Godot
{
- internal static class MarshalUtils
+ static class MarshalUtils
{
- private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
+ static bool IsArrayGenericType(Type type)
{
- var ret = new Dictionary<object, object>();
-
- for (int i = 0; i < keys.Length; i++)
- {
- ret.Add(keys[i], values[i]);
- }
-
- return ret;
- }
-
- private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
- {
- var keys = from.Keys;
- keysTo = new object[keys.Count];
- keys.CopyTo(keysTo, 0);
-
- var values = from.Values;
- valuesTo = new object[values.Count];
- values.CopyTo(valuesTo, 0);
+ return type.GetGenericTypeDefinition() == typeof(Array<>);
}
- private static Type GetDictionaryType()
+ static bool IsDictionaryGenericType(Type type)
{
- return typeof(Dictionary<object, object>);
+ return type.GetGenericTypeDefinition() == typeof(Dictionary<, >);
}
}
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index 0d20a12563..a89dfe5f27 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -138,19 +138,9 @@ namespace Godot
return (real_t)Math.Floor(s);
}
- public static real_t Fposmod(real_t x, real_t y)
- {
- if (x >= 0f)
- {
- return x % y;
- }
-
- return y - -x % y;
- }
-
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
- return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ return (weight - from) / (to - from);
}
public static bool IsInf(real_t s)
@@ -165,7 +155,7 @@ namespace Godot
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
- return from + (to - from) * Clamp(weight, 0f, 1f);
+ return from + (to - from) * weight;
}
public static real_t Log(real_t s)
@@ -210,6 +200,32 @@ namespace Godot
return new Vector2(r * Cos(th), r * Sin(th));
}
+ /// <summary>
+ /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// </summary>
+ public static real_t PosMod(real_t a, real_t b)
+ {
+ real_t c = a % b;
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ {
+ c += b;
+ }
+ return c;
+ }
+
+ /// <summary>
+ /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// </summary>
+ public static int PosMod(int a, int b)
+ {
+ int c = a % b;
+ if ((c < 0 && b > 0) || (c > 0 && b < 0))
+ {
+ c += b;
+ }
+ return c;
+ }
+
public static real_t Pow(real_t x, real_t y)
{
return (real_t)Math.Pow(x, y);
diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index 1020f06bf5..9611dce11e 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -145,6 +145,15 @@ namespace Godot
return point - _normal * DistanceTo(point);
}
+ // Constants
+ private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0);
+ private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
+ private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
+
+ public static Plane PlaneYZ { get { return _planeYZ; } }
+ public static Plane PlaneXZ { get { return _planeXZ; } }
+ public static Plane PlaneXY { get { return _planeXY; } }
+
// Constructors
public Plane(real_t a, real_t b, real_t c, real_t d)
{
diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs
index c69c55d997..eaa027eb69 100644
--- a/modules/mono/glue/cs_files/Quat.cs
+++ b/modules/mono/glue/cs_files/Quat.cs
@@ -11,18 +11,11 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Quat : IEquatable<Quat>
{
- private static readonly Quat identity = new Quat(0f, 0f, 0f, 1f);
-
public real_t x;
public real_t y;
public real_t z;
public real_t w;
- public static Quat Identity
- {
- get { return identity; }
- }
-
public real_t this[int index]
{
get
@@ -63,6 +56,16 @@ namespace Godot
}
}
+ public real_t Length
+ {
+ get { return Mathf.Sqrt(LengthSquared); }
+ }
+
+ public real_t LengthSquared
+ {
+ get { return Dot(this); }
+ }
+
public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
{
real_t t2 = (1.0f - t) * t * 2f;
@@ -76,24 +79,20 @@ namespace Godot
return x * b.x + y * b.y + z * b.z + w * b.w;
}
- public Quat Inverse()
- {
- return new Quat(-x, -y, -z, w);
- }
-
- public real_t Length()
+ public Vector3 GetEuler()
{
- return Mathf.Sqrt(LengthSquared());
+ var basis = new Basis(this);
+ return basis.GetEuler();
}
- public real_t LengthSquared()
+ public Quat Inverse()
{
- return Dot(this);
+ return new Quat(-x, -y, -z, w);
}
public Quat Normalized()
{
- return this / Length();
+ return this / Length;
}
public void Set(real_t x, real_t y, real_t z, real_t w)
@@ -103,12 +102,20 @@ namespace Godot
this.z = z;
this.w = w;
}
+
public void Set(Quat q)
{
- x = q.x;
- y = q.y;
- z = q.z;
- w = q.w;
+ this = q;
+ }
+
+ public void SetAxisAngle(Vector3 axis, real_t angle)
+ {
+ this = new Quat(axis, angle);
+ }
+
+ public void SetEuler(Vector3 eulerYXZ)
+ {
+ this = new Quat(eulerYXZ);
}
public Quat Slerp(Quat b, real_t t)
@@ -192,6 +199,9 @@ namespace Godot
return new Vector3(q.x, q.y, q.z);
}
+ // Static Readonly Properties
+ public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f);
+
// Constructors
public Quat(real_t x, real_t y, real_t z, real_t w)
{
@@ -199,15 +209,46 @@ namespace Godot
this.y = y;
this.z = z;
this.w = w;
- }
+ }
+
+ public bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ }
+
public Quat(Quat q)
- {
- x = q.x;
- y = q.y;
- z = q.z;
- w = q.w;
+ {
+ this = q;
+ }
+
+ public Quat(Basis basis)
+ {
+ this = basis.Quat();
}
-
+
+ public Quat(Vector3 eulerYXZ)
+ {
+ real_t half_a1 = eulerYXZ.y * (real_t)0.5;
+ real_t half_a2 = eulerYXZ.x * (real_t)0.5;
+ real_t half_a3 = eulerYXZ.z * (real_t)0.5;
+
+ // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
+ // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
+ // a3 is the angle of the first rotation, following the notation in this reference.
+
+ real_t cos_a1 = Mathf.Cos(half_a1);
+ real_t sin_a1 = Mathf.Sin(half_a1);
+ real_t cos_a2 = Mathf.Cos(half_a2);
+ real_t sin_a2 = Mathf.Sin(half_a2);
+ real_t cos_a3 = Mathf.Cos(half_a3);
+ real_t sin_a3 = Mathf.Sin(half_a3);
+
+ x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
+ y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
+ z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3;
+ w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
+ }
+
public Quat(Vector3 axis, real_t angle)
{
real_t d = axis.Length();
diff --git a/modules/mono/glue/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs
index 6f16656573..cb25c267bc 100644
--- a/modules/mono/glue/cs_files/Rect2.cs
+++ b/modules/mono/glue/cs_files/Rect2.cs
@@ -11,24 +11,25 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Rect2 : IEquatable<Rect2>
{
- private Vector2 position;
- private Vector2 size;
+ private Vector2 _position;
+ private Vector2 _size;
public Vector2 Position
{
- get { return position; }
- set { position = value; }
+ get { return _position; }
+ set { _position = value; }
}
public Vector2 Size
{
- get { return size; }
- set { size = value; }
+ get { return _size; }
+ set { _size = value; }
}
public Vector2 End
{
- get { return position + size; }
+ get { return _position + _size; }
+ set { _size = value - _position; }
}
public real_t Area
@@ -36,6 +37,13 @@ namespace Godot
get { return GetArea(); }
}
+ public Rect2 Abs()
+ {
+ Vector2 end = End;
+ Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y));
+ return new Rect2(topLeft, _size.Abs());
+ }
+
public Rect2 Clip(Rect2 b)
{
var newRect = b;
@@ -43,31 +51,31 @@ namespace Godot
if (!Intersects(newRect))
return new Rect2();
- newRect.position.x = Mathf.Max(b.position.x, position.x);
- newRect.position.y = Mathf.Max(b.position.y, position.y);
+ newRect._position.x = Mathf.Max(b._position.x, _position.x);
+ newRect._position.y = Mathf.Max(b._position.y, _position.y);
- Vector2 bEnd = b.position + b.size;
- Vector2 end = position + size;
+ Vector2 bEnd = b._position + b._size;
+ Vector2 end = _position + _size;
- newRect.size.x = Mathf.Min(bEnd.x, end.x) - newRect.position.x;
- newRect.size.y = Mathf.Min(bEnd.y, end.y) - newRect.position.y;
+ newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x;
+ newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y;
return newRect;
}
public bool Encloses(Rect2 b)
{
- return b.position.x >= position.x && b.position.y >= position.y &&
- b.position.x + b.size.x < position.x + size.x &&
- b.position.y + b.size.y < position.y + size.y;
+ return b._position.x >= _position.x && b._position.y >= _position.y &&
+ b._position.x + b._size.x < _position.x + _size.x &&
+ b._position.y + b._size.y < _position.y + _size.y;
}
public Rect2 Expand(Vector2 to)
{
var expanded = this;
- Vector2 begin = expanded.position;
- Vector2 end = expanded.position + expanded.size;
+ Vector2 begin = expanded._position;
+ Vector2 end = expanded._position + expanded._size;
if (to.x < begin.x)
begin.x = to.x;
@@ -79,25 +87,25 @@ namespace Godot
if (to.y > end.y)
end.y = to.y;
- expanded.position = begin;
- expanded.size = end - begin;
+ expanded._position = begin;
+ expanded._size = end - begin;
return expanded;
}
public real_t GetArea()
{
- return size.x * size.y;
+ return _size.x * _size.y;
}
public Rect2 Grow(real_t by)
{
var g = this;
- g.position.x -= by;
- g.position.y -= by;
- g.size.x += by * 2;
- g.size.y += by * 2;
+ g._position.x -= by;
+ g._position.y -= by;
+ g._size.x += by * 2;
+ g._size.y += by * 2;
return g;
}
@@ -106,10 +114,10 @@ namespace Godot
{
var g = this;
- g.position.x -= left;
- g.position.y -= top;
- g.size.x += left + right;
- g.size.y += top + bottom;
+ g._position.x -= left;
+ g._position.y -= top;
+ g._size.x += left + right;
+ g._size.y += top + bottom;
return g;
}
@@ -128,19 +136,19 @@ namespace Godot
public bool HasNoArea()
{
- return size.x <= 0 || size.y <= 0;
+ return _size.x <= 0 || _size.y <= 0;
}
public bool HasPoint(Vector2 point)
{
- if (point.x < position.x)
+ if (point.x < _position.x)
return false;
- if (point.y < position.y)
+ if (point.y < _position.y)
return false;
- if (point.x >= position.x + size.x)
+ if (point.x >= _position.x + _size.x)
return false;
- if (point.y >= position.y + size.y)
+ if (point.y >= _position.y + _size.y)
return false;
return true;
@@ -148,13 +156,13 @@ namespace Godot
public bool Intersects(Rect2 b)
{
- if (position.x > b.position.x + b.size.x)
+ if (_position.x > b._position.x + b._size.x)
return false;
- if (position.x + size.x < b.position.x)
+ if (_position.x + _size.x < b._position.x)
return false;
- if (position.y > b.position.y + b.size.y)
+ if (_position.y > b._position.y + b._size.y)
return false;
- if (position.y + size.y < b.position.y)
+ if (_position.y + _size.y < b._position.y)
return false;
return true;
@@ -164,13 +172,13 @@ namespace Godot
{
Rect2 newRect;
- newRect.position.x = Mathf.Min(b.position.x, position.x);
- newRect.position.y = Mathf.Min(b.position.y, position.y);
+ newRect._position.x = Mathf.Min(b._position.x, _position.x);
+ newRect._position.y = Mathf.Min(b._position.y, _position.y);
- newRect.size.x = Mathf.Max(b.position.x + b.size.x, position.x + size.x);
- newRect.size.y = Mathf.Max(b.position.y + b.size.y, position.y + size.y);
+ newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
+ newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
- newRect.size = newRect.size - newRect.position; // Make relative again
+ newRect._size = newRect._size - newRect._position; // Make relative again
return newRect;
}
@@ -178,23 +186,23 @@ namespace Godot
// Constructors
public Rect2(Vector2 position, Vector2 size)
{
- this.position = position;
- this.size = size;
+ _position = position;
+ _size = size;
}
public Rect2(Vector2 position, real_t width, real_t height)
{
- this.position = position;
- size = new Vector2(width, height);
+ _position = position;
+ _size = new Vector2(width, height);
}
public Rect2(real_t x, real_t y, Vector2 size)
{
- position = new Vector2(x, y);
- this.size = size;
+ _position = new Vector2(x, y);
+ _size = size;
}
public Rect2(real_t x, real_t y, real_t width, real_t height)
{
- position = new Vector2(x, y);
- size = new Vector2(width, height);
+ _position = new Vector2(x, y);
+ _size = new Vector2(width, height);
}
public static bool operator ==(Rect2 left, Rect2 right)
@@ -219,20 +227,20 @@ namespace Godot
public bool Equals(Rect2 other)
{
- return position.Equals(other.position) && size.Equals(other.size);
+ return _position.Equals(other._position) && _size.Equals(other._size);
}
public override int GetHashCode()
{
- return position.GetHashCode() ^ size.GetHashCode();
+ return _position.GetHashCode() ^ _size.GetHashCode();
}
public override string ToString()
{
return String.Format("({0}, {1})", new object[]
{
- position.ToString(),
- size.ToString()
+ _position.ToString(),
+ _size.ToString()
});
}
@@ -240,8 +248,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- position.ToString(format),
- size.ToString(format)
+ _position.ToString(format),
+ _size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs
index eaeed7b37b..b58f8bc6a8 100644
--- a/modules/mono/glue/cs_files/StringExtensions.cs
+++ b/modules/mono/glue/cs_files/StringExtensions.cs
@@ -1,5 +1,3 @@
-//using System;
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -583,7 +581,7 @@ namespace Godot
// </summary>
public static byte[] Md5Buffer(this string instance)
{
- return NativeCalls.godot_icall_String_md5_buffer(instance);
+ return NativeCalls.godot_icall_String_md5_buffer(instance);
}
// <summary>
@@ -591,7 +589,7 @@ namespace Godot
// </summary>
public static string Md5Text(this string instance)
{
- return NativeCalls.godot_icall_String_md5_text(instance);
+ return NativeCalls.godot_icall_String_md5_text(instance);
}
// <summary>
@@ -750,7 +748,7 @@ namespace Godot
// </summary>
public static int Rfind(this string instance, string what, int from = -1)
{
- return NativeCalls.godot_icall_String_rfind(instance, what, from);
+ return NativeCalls.godot_icall_String_rfind(instance, what, from);
}
// <summary>
@@ -758,7 +756,7 @@ namespace Godot
// </summary>
public static int Rfindn(this string instance, string what, int from = -1)
{
- return NativeCalls.godot_icall_String_rfindn(instance, what, from);
+ return NativeCalls.godot_icall_String_rfindn(instance, what, from);
}
// <summary>
@@ -777,7 +775,7 @@ namespace Godot
public static byte[] Sha256Buffer(this string instance)
{
- return NativeCalls.godot_icall_String_sha256_buffer(instance);
+ return NativeCalls.godot_icall_String_sha256_buffer(instance);
}
// <summary>
@@ -785,7 +783,7 @@ namespace Godot
// </summary>
public static string Sha256Text(this string instance)
{
- return NativeCalls.godot_icall_String_sha256_text(instance);
+ return NativeCalls.godot_icall_String_sha256_text(instance);
}
// <summary>
diff --git a/modules/mono/glue/cs_files/ToolAttribute.cs b/modules/mono/glue/cs_files/ToolAttribute.cs
deleted file mode 100644
index d8601b5b32..0000000000
--- a/modules/mono/glue/cs_files/ToolAttribute.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using System;
-
-namespace Godot
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class ToolAttribute : Attribute {}
-}
diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs
index d1b247a552..e432d5b52c 100644
--- a/modules/mono/glue/cs_files/Transform.cs
+++ b/modules/mono/glue/cs_files/Transform.cs
@@ -102,7 +102,18 @@ namespace Godot
basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z
);
}
-
+
+ // Constants
+ private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero);
+ private static readonly Transform _flipX = new Transform(new Basis(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)), Vector3.Zero);
+ private static readonly Transform _flipY = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)), Vector3.Zero);
+ private static readonly Transform _flipZ = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)), Vector3.Zero);
+
+ public static Transform Identity { get { return _identity; } }
+ public static Transform FlipX { get { return _flipX; } }
+ public static Transform FlipY { get { return _flipY; } }
+ public static Transform FlipZ { get { return _flipZ; } }
+
// Constructors
public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
{
diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs
index ff5259178b..8d30833066 100644
--- a/modules/mono/glue/cs_files/Transform2D.cs
+++ b/modules/mono/glue/cs_files/Transform2D.cs
@@ -11,22 +11,10 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Transform2D : IEquatable<Transform2D>
{
- private static readonly Transform2D identity = new Transform2D
- (
- new Vector2(1f, 0f),
- new Vector2(0f, 1f),
- new Vector2(0f, 0f)
- );
-
public Vector2 x;
public Vector2 y;
public Vector2 o;
- public static Transform2D Identity
- {
- get { return identity; }
- }
-
public Vector2 Origin
{
get { return o; }
@@ -264,6 +252,15 @@ namespace Godot
Vector2 vInv = v - o;
return new Vector2(x.Dot(vInv), y.Dot(vInv));
}
+
+ // Constants
+ private static readonly Transform2D _identity = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, 1f), Vector2.Zero);
+ private static readonly Transform2D _flipX = new Transform2D(new Vector2(-1f, 0f), new Vector2(0f, 1f), Vector2.Zero);
+ private static readonly Transform2D _flipY = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, -1f), Vector2.Zero);
+
+ public static Transform2D Identity { get { return _identity; } }
+ public static Transform2D FlipX { get { return _flipX; } }
+ public static Transform2D FlipY { get { return _flipY; } }
// Constructors
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
deleted file mode 100755
index 00750edc07..0000000000
--- a/modules/mono/glue/cs_files/VERSION.txt
+++ /dev/null
@@ -1 +0,0 @@
-3
diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs
index c274364895..080b8802ba 100644
--- a/modules/mono/glue/cs_files/Vector2.cs
+++ b/modules/mono/glue/cs_files/Vector2.cs
@@ -184,6 +184,11 @@ namespace Godot
return result;
}
+ public Vector2 Project(Vector2 onNormal)
+ {
+ return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
+ }
+
public Vector2 Reflect(Vector2 n)
{
return 2.0f * n * Dot(n) - this;
@@ -231,24 +236,27 @@ namespace Godot
{
return new Vector2(y, -x);
}
-
- private static readonly Vector2 zero = new Vector2 (0, 0);
- private static readonly Vector2 one = new Vector2 (1, 1);
- private static readonly Vector2 negOne = new Vector2 (-1, -1);
-
- private static readonly Vector2 up = new Vector2 (0, 1);
- private static readonly Vector2 down = new Vector2 (0, -1);
- private static readonly Vector2 right = new Vector2 (1, 0);
- private static readonly Vector2 left = new Vector2 (-1, 0);
-
- public static Vector2 Zero { get { return zero; } }
- public static Vector2 One { get { return one; } }
- public static Vector2 NegOne { get { return negOne; } }
-
- public static Vector2 Up { get { return up; } }
- public static Vector2 Down { get { return down; } }
- public static Vector2 Right { get { return right; } }
- public static Vector2 Left { get { return left; } }
+
+ // Constants
+ private static readonly Vector2 _zero = new Vector2(0, 0);
+ private static readonly Vector2 _one = new Vector2(1, 1);
+ private static readonly Vector2 _negOne = new Vector2(-1, -1);
+ private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf);
+
+ private static readonly Vector2 _up = new Vector2(0, -1);
+ private static readonly Vector2 _down = new Vector2(0, 1);
+ private static readonly Vector2 _right = new Vector2(1, 0);
+ private static readonly Vector2 _left = new Vector2(-1, 0);
+
+ public static Vector2 Zero { get { return _zero; } }
+ public static Vector2 NegOne { get { return _negOne; } }
+ public static Vector2 One { get { return _one; } }
+ public static Vector2 Inf { get { return _inf; } }
+
+ public static Vector2 Up { get { return _up; } }
+ public static Vector2 Down { get { return _down; } }
+ public static Vector2 Right { get { return _right; } }
+ public static Vector2 Left { get { return _left; } }
// Constructors
public Vector2(real_t x, real_t y)
diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs
index 085a4f0043..6fffe5e4d6 100644
--- a/modules/mono/glue/cs_files/Vector3.cs
+++ b/modules/mono/glue/cs_files/Vector3.cs
@@ -210,6 +210,11 @@ namespace Godot
);
}
+ public Vector3 Project(Vector3 onNormal)
+ {
+ return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
+ }
+
public Vector3 Reflect(Vector3 n)
{
#if DEBUG
@@ -272,27 +277,30 @@ namespace Godot
);
}
- private static readonly Vector3 zero = new Vector3 (0, 0, 0);
- private static readonly Vector3 one = new Vector3 (1, 1, 1);
- private static readonly Vector3 negOne = new Vector3 (-1, -1, -1);
+ // Constants
+ private static readonly Vector3 _zero = new Vector3(0, 0, 0);
+ private static readonly Vector3 _one = new Vector3(1, 1, 1);
+ private static readonly Vector3 _negOne = new Vector3(-1, -1, -1);
+ private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf);
- private static readonly Vector3 up = new Vector3 (0, 1, 0);
- private static readonly Vector3 down = new Vector3 (0, -1, 0);
- private static readonly Vector3 right = new Vector3 (1, 0, 0);
- private static readonly Vector3 left = new Vector3 (-1, 0, 0);
- private static readonly Vector3 forward = new Vector3 (0, 0, -1);
- private static readonly Vector3 back = new Vector3 (0, 0, 1);
-
- public static Vector3 Zero { get { return zero; } }
- public static Vector3 One { get { return one; } }
- public static Vector3 NegOne { get { return negOne; } }
+ private static readonly Vector3 _up = new Vector3(0, 1, 0);
+ private static readonly Vector3 _down = new Vector3(0, -1, 0);
+ private static readonly Vector3 _right = new Vector3(1, 0, 0);
+ private static readonly Vector3 _left = new Vector3(-1, 0, 0);
+ private static readonly Vector3 _forward = new Vector3(0, 0, -1);
+ private static readonly Vector3 _back = new Vector3(0, 0, 1);
+
+ public static Vector3 Zero { get { return _zero; } }
+ public static Vector3 One { get { return _one; } }
+ public static Vector3 NegOne { get { return _negOne; } }
+ public static Vector3 Inf { get { return _inf; } }
- public static Vector3 Up { get { return up; } }
- public static Vector3 Down { get { return down; } }
- public static Vector3 Right { get { return right; } }
- public static Vector3 Left { get { return left; } }
- public static Vector3 Forward { get { return forward; } }
- public static Vector3 Back { get { return back; } }
+ public static Vector3 Up { get { return _up; } }
+ public static Vector3 Down { get { return _down; } }
+ public static Vector3 Right { get { return _right; } }
+ public static Vector3 Left { get { return _left; } }
+ public static Vector3 Forward { get { return _forward; } }
+ public static Vector3 Back { get { return _back; } }
// Constructors
public Vector3(real_t x, real_t y, real_t z)
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index cedc8e9992..6a6f3062b4 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "builtin_types_glue.h"
+#include "collections_glue.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_class.h"
@@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) {
void godot_register_header_icalls() {
godot_register_builtin_type_icalls();
+ godot_register_collections_icalls();
}
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index f604464e8f..39d608de9f 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -32,6 +32,7 @@
#define GODOTSHARP_DEFS_H
#define BINDINGS_NAMESPACE "Godot"
+#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections"
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_PTR_FIELD "ptr"
#define BINDINGS_NATIVE_NAME_FIELD "nativeName"
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index 4e82bcd03e..9f0e933a8c 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -34,25 +34,22 @@
uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
- return mono_gchandle_new(
- p_object,
- false /* do not pin the object */
- );
+ return mono_gchandle_new(p_object, /* pinned: */ false);
}
uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
- return mono_gchandle_new_weakref(p_object, false);
+ return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
}
Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {
- return memnew(MonoGCHandle(make_strong_handle(p_object)));
+ return memnew(MonoGCHandle(make_strong_handle(p_object), STRONG_HANDLE));
}
Ref<MonoGCHandle> MonoGCHandle::create_weak(MonoObject *p_object) {
- return memnew(MonoGCHandle(make_weak_handle(p_object)));
+ return memnew(MonoGCHandle(make_weak_handle(p_object), WEAK_HANDLE));
}
void MonoGCHandle::release() {
@@ -67,9 +64,10 @@ void MonoGCHandle::release() {
}
}
-MonoGCHandle::MonoGCHandle(uint32_t p_handle) {
+MonoGCHandle::MonoGCHandle(uint32_t p_handle, HandleType p_handle_type) {
released = false;
+ weak = p_handle_type == WEAK_HANDLE;
handle = p_handle;
}
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index 9cb3ef0fbb..7eeaba30e0 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -40,24 +40,33 @@ class MonoGCHandle : public Reference {
GDCLASS(MonoGCHandle, Reference)
bool released;
+ bool weak;
uint32_t handle;
public:
+ enum HandleType {
+ STRONG_HANDLE,
+ WEAK_HANDLE
+ };
+
static uint32_t make_strong_handle(MonoObject *p_object);
static uint32_t make_weak_handle(MonoObject *p_object);
static Ref<MonoGCHandle> create_strong(MonoObject *p_object);
static Ref<MonoGCHandle> create_weak(MonoObject *p_object);
+ _FORCE_INLINE_ bool is_weak() { return weak; }
+
_FORCE_INLINE_ MonoObject *get_target() const { return released ? NULL : mono_gchandle_get_target(handle); }
- _FORCE_INLINE_ void set_handle(uint32_t p_handle) {
- handle = p_handle;
+ _FORCE_INLINE_ void set_handle(uint32_t p_handle, HandleType p_handle_type) {
released = false;
+ weak = p_handle_type == WEAK_HANDLE;
+ handle = p_handle;
}
void release();
- MonoGCHandle(uint32_t p_handle);
+ MonoGCHandle(uint32_t p_handle, HandleType p_handle_type);
~MonoGCHandle();
};
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0646580eaa..fadac941e9 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -53,14 +53,6 @@
#include "main/main.h"
#endif
-void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
-
- (void)user_data; // UNUSED
-
- GDMonoUtils::print_unhandled_exception(exc);
- abort();
-}
-
#ifdef MONO_PRINT_HANDLER_ENABLED
void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
@@ -85,12 +77,12 @@ void setup_runtime_main_args() {
Vector<char *> main_args;
main_args.resize(cmdline_args.size() + 1);
- main_args[0] = execpath.ptrw();
+ main_args.write[0] = execpath.ptrw();
int i = 1;
for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get();
- main_args[i] = stored.ptrw();
+ main_args.write[i] = stored.ptrw();
i++;
}
@@ -156,7 +148,7 @@ void GDMono::initialize() {
ERR_FAIL_NULL(Engine::get_singleton());
- OS::get_singleton()->print("Mono: Initializing module...\n");
+ print_verbose("Mono: Initializing module...");
#ifdef DEBUG_METHODS_ENABLED
_initialize_and_check_api_hashes();
@@ -197,6 +189,8 @@ void GDMono::initialize() {
mono_config_parse(NULL);
+ mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
+
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime");
@@ -208,7 +202,7 @@ void GDMono::initialize() {
runtime_initialized = true;
- OS::get_singleton()->print("Mono: Runtime initialized\n");
+ print_verbose("Mono: Runtime initialized");
// mscorlib assembly MUST be present at initialization
ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
@@ -232,7 +226,7 @@ void GDMono::initialize() {
#ifdef DEBUG_ENABLED
bool debugger_attached = _wait_for_debugger_msecs(500);
if (!debugger_attached && OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Debugger wait timeout\n");
+ print_error("Mono: Debugger wait timeout");
#endif
_register_internal_calls();
@@ -262,7 +256,7 @@ void GDMono::initialize() {
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
}
- OS::get_singleton()->print("Mono: Proceeding to unload scripts domain because of invalid API assemblies\n");
+ print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies.");
Error err = _unload_scripts_domain();
if (err != OK) {
@@ -275,13 +269,10 @@ void GDMono::initialize() {
}
}
#else
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+ print_verbose("Mono: Glue disabled, ignoring script assemblies.");
#endif
- mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
-
- OS::get_singleton()->print("Mono: INITIALIZED\n");
+ print_verbose("Mono: INITIALIZED");
}
#ifndef MONO_GLUE_DISABLED
@@ -360,8 +351,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
CRASH_COND(!r_assembly);
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print((String() + "Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...\n").utf8());
+ print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
MonoImageOpenStatus status = MONO_IMAGE_OK;
MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly);
@@ -380,8 +370,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
*r_assembly = *stored_assembly;
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print(String("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path() + "\n").utf8());
+ print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
return true;
}
@@ -508,7 +497,7 @@ bool GDMono::_load_project_assembly() {
mono_assembly_set_main(project_assembly->get_assembly());
} else {
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load project assembly\n");
+ print_error("Mono: Failed to load project assembly");
}
return success;
@@ -518,13 +507,13 @@ bool GDMono::_load_api_assemblies() {
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load Core API assembly\n");
+ print_error("Mono: Failed to load Core API assembly");
return false;
} else {
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->printerr("Mono: Failed to load Editor API assembly\n");
+ print_error("Mono: Failed to load Editor API assembly");
return false;
}
#endif
@@ -601,9 +590,7 @@ Error GDMono::_load_scripts_domain() {
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Mono: Loading scripts domain...\n");
- }
+ print_verbose("Mono: Loading scripts domain...");
scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain");
@@ -619,9 +606,7 @@ Error GDMono::_unload_scripts_domain() {
ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Mono: Unloading scripts domain...\n");
- }
+ print_verbose("Mono: Unloading scripts domain...");
_GodotSharp::get_singleton()->_dispose_callback();
@@ -652,12 +637,12 @@ Error GDMono::_unload_scripts_domain() {
_GodotSharp::get_singleton()->_dispose_callback();
- MonoObject *ex = NULL;
- mono_domain_try_unload(domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINT("Exception thrown when unloading scripts domain:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINT("Exception thrown when unloading scripts domain");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -669,9 +654,7 @@ Error GDMono::_load_tools_domain() {
ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Mono: Loading tools domain...\n");
- }
+ print_verbose("Mono: Loading tools domain...");
tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
@@ -736,8 +719,7 @@ Error GDMono::reload_scripts_domain() {
if (!_load_project_assembly())
return ERR_CANT_OPEN;
#else
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
+ print_verbose("Mono: Glue disabled, ignoring script assemblies.");
#endif
return OK;
@@ -750,9 +732,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
String domain_name = mono_domain_get_friendly_name(p_domain);
- if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print(String("Mono: Unloading domain `" + domain_name + "`...\n").utf8());
- }
+ print_verbose("Mono: Unloading domain `" + domain_name + "`...");
if (mono_domain_get() != root_domain)
mono_domain_set(root_domain, true);
@@ -763,12 +743,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
- MonoObject *ex = NULL;
- mono_domain_try_unload(p_domain, &ex);
+ MonoException *exc = NULL;
+ mono_domain_try_unload(p_domain, (MonoObject **)&exc);
- if (ex) {
- ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:");
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
+ GDMonoUtils::debug_unhandled_exception(exc);
return FAILED;
}
@@ -811,6 +791,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
assemblies.erase(p_domain_id);
}
+void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
+
+ // This method will be called by the runtime when a thrown exception is not handled.
+ // It won't be called when we manually treat a thrown exception as unhandled.
+ // We assume the exception was already printed before calling this hook.
+
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ abort();
+ _UNREACHABLE_();
+}
+
GDMono::GDMono() {
singleton = this;
@@ -870,7 +865,7 @@ GDMono::~GDMono() {
GDMonoUtils::clear_cache();
- OS::get_singleton()->print("Mono: Runtime cleanup...\n");
+ print_verbose("Mono: Runtime cleanup...");
runtime_initialized = false;
mono_jit_cleanup(root_domain);
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 5e01152870..e0ec6ced5e 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -164,6 +164,8 @@ public:
static GDMono *get_singleton() { return singleton; }
+ static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+
// Do not use these, unless you know what you're doing
void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
GDMonoAssembly **get_loaded_assembly(const String &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 9d3bee2176..27ce39b6d7 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -42,8 +42,25 @@
#include "gd_mono_class.h"
bool GDMonoAssembly::no_search = false;
+bool GDMonoAssembly::in_preload = false;
+
Vector<String> GDMonoAssembly::search_dirs;
+void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, void *user_data) {
+
+ if (no_search)
+ return;
+
+ // If our search and preload hooks fail to load the assembly themselves, the mono runtime still might.
+ // Just do Assembly.LoadFrom("/Full/Path/On/Disk.dll");
+ // In this case, we wouldn't have the assembly known in GDMono, which causes crashes
+ // if any class inside the assembly is looked up by Godot.
+ // And causing a lookup like that is as easy as throwing an exception defined in it...
+ // No, we can't make the assembly load hooks smart enough because they get passed a MonoAssemblyName* only,
+ // not the disk path passed to say Assembly.LoadFrom().
+ _wrap_mono_assembly(assembly);
+}
+
MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
return GDMonoAssembly::_search_hook(aname, user_data, false);
}
@@ -111,6 +128,8 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, void *user_d
return res ? res->get_assembly() : NULL;
}
+static _THREAD_LOCAL_(MonoImage *) image_corlib_loading = NULL;
+
MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly) {
(void)user_data; // UNUSED
@@ -138,16 +157,38 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
}
}
+ {
+ // If we find the assembly here, we load it with `mono_assembly_load_from_full`,
+ // which in turn invokes load hooks before returning the MonoAssembly to us.
+ // One of the load hooks is `load_aot_module`. This hook can end up calling preload hooks
+ // again for the same assembly in certain in certain circumstances (the `do_load_image` part).
+ // If this is the case and we return NULL due to the no_search condition below,
+ // it will result in an internal crash later on. Therefore we need to return the assembly we didn't
+ // get yet from `mono_assembly_load_from_full`. Luckily we have the image, which already got it.
+ // This must be done here. If done in search hooks, it would cause `mono_assembly_load_from_full`
+ // to think another MonoAssembly for this assembly was already loaded, making it delete its own,
+ // when in fact both pointers were the same... This hooks thing is confusing.
+ if (image_corlib_loading) {
+ return mono_image_get_assembly(image_corlib_loading);
+ }
+ }
+
+ if (no_search)
+ return NULL;
+
+ no_search = true;
+ in_preload = true;
+
String name = mono_assembly_name_get_name(aname);
bool has_extension = name.ends_with(".dll");
+ GDMonoAssembly *res = NULL;
if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") {
GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
if (stored_assembly)
return (*stored_assembly)->get_assembly();
String path;
- GDMonoAssembly *res = NULL;
for (int i = 0; i < search_dirs.size(); i++) {
const String &search_dir = search_dirs[i];
@@ -168,11 +209,12 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
}
}
}
-
- return res ? res->get_assembly() : NULL;
}
- return NULL;
+ no_search = false;
+ in_preload = false;
+
+ return res ? res->get_assembly() : NULL;
}
GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly) {
@@ -192,12 +234,30 @@ GDMonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const
return assembly;
}
+void GDMonoAssembly::_wrap_mono_assembly(MonoAssembly *assembly) {
+ String name = mono_assembly_name_get_name(mono_assembly_get_name(assembly));
+
+ MonoImage *image = mono_assembly_get_image(assembly);
+
+ GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, mono_image_get_filename(image)));
+ Error err = gdassembly->wrapper_for_image(image);
+
+ if (err != OK) {
+ memdelete(gdassembly);
+ ERR_FAIL();
+ }
+
+ MonoDomain *domain = mono_domain_get();
+ GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
+}
+
void GDMonoAssembly::initialize() {
mono_install_assembly_search_hook(&assembly_search_hook, NULL);
mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, NULL);
mono_install_assembly_preload_hook(&assembly_preload_hook, NULL);
mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, NULL);
+ mono_install_assembly_load_hook(&assembly_load_hook, NULL);
}
Error GDMonoAssembly::load(bool p_refonly) {
@@ -241,8 +301,16 @@ no_pdb:
#endif
+ bool is_corlib_preload = in_preload && name == "mscorlib";
+
+ if (is_corlib_preload)
+ image_corlib_loading = image;
+
assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, refonly);
+ if (is_corlib_preload)
+ image_corlib_loading = NULL;
+
ERR_FAIL_COND_V(status != MONO_IMAGE_OK || assembly == NULL, ERR_FILE_CANT_OPEN);
loaded = true;
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 5cf744a5a2..2c6d367fc6 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -89,8 +89,10 @@ class GDMonoAssembly {
#endif
static bool no_search;
+ static bool in_preload;
static Vector<String> search_dirs;
+ static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
@@ -100,6 +102,7 @@ class GDMonoAssembly {
static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
static GDMonoAssembly *_load_assembly_from(const String &p_name, const String &p_path, bool p_refonly);
+ static void _wrap_mono_assembly(MonoAssembly *assembly);
friend class GDMono;
static void initialize();
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 66339d7ae6..4e515cde28 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -33,23 +33,35 @@
#include <mono/metadata/attrdefs.h>
#include "gd_mono_assembly.h"
+#include "gd_mono_marshal.h"
-MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) {
+String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
+ // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
+ MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
- return mono_class_get_type(p_class->get_mono_ptr());
-}
+ MonoException *exc = NULL;
+ MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+ return GDMonoMarshal::mono_string_to_godot(str);
+}
- return mono_class_is_assignable_from(mono_class, p_from->mono_class);
+MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
+ return mono_class_get_type(p_mono_class);
}
String GDMonoClass::get_full_name() const {
+ return get_full_name(mono_class);
+}
- String res = namespace_name;
- if (res.length())
- res += ".";
- return res + class_name;
+MonoType *GDMonoClass::get_mono_type() {
+ // Care, you cannot compare MonoType pointers
+ return get_mono_type(mono_class);
+}
+
+bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
+
+ return mono_class_is_assignable_from(mono_class, p_from->mono_class);
}
GDMonoClass *GDMonoClass::get_parent_class() {
@@ -308,6 +320,12 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
return get_method(method);
}
+void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) {
+
+ GDMonoMethod *method = get_method(p_name, p_params_count);
+ return method ? method->get_thunk() : NULL;
+}
+
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
@@ -483,7 +501,7 @@ GDMonoClass::~GDMonoClass() {
}
}
- deleted_methods[offset] = method;
+ deleted_methods.write[offset] = method;
++offset;
memdelete(method);
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 417c138594..f4e386549a 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -98,7 +98,11 @@ class GDMonoClass {
GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
public:
- static MonoType *get_raw_type(GDMonoClass *p_class);
+ static String get_full_name(MonoClass *p_mono_class);
+ static MonoType *get_mono_type(MonoClass *p_mono_class);
+
+ String get_full_name() const;
+ MonoType *get_mono_type();
bool is_assignable_from(GDMonoClass *p_from) const;
@@ -108,8 +112,6 @@ public:
_FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
- String get_full_name() const;
-
GDMonoClass *get_parent_class();
#ifdef TOOLS_ENABLED
@@ -131,6 +133,8 @@ public:
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
+ void *get_method_thunk(const StringName &p_name, int p_params_count = 0);
+
GDMonoField *get_field(const StringName &p_name);
const Vector<GDMonoField *> &get_all_fields();
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 3b91777ed4..d3a673dc1b 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
SET_FROM_ARRAY_AND_BREAK(Array);
@@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
ERR_FAIL();
} break;
@@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
case Variant::DICTIONARY: {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
+ mono_field_set_value(p_object, mono_field, managed);
+ } break;
+ case Variant::ARRAY: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array);
case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray);
case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray);
case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray);
@@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 2b5110f0b9..72a5439044 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -45,7 +45,8 @@ struct ManagedType {
GDMonoClass *type_class;
ManagedType() {
- type_class = 0;
+ type_encoding = 0;
+ type_class = NULL;
}
};
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index a1a79f957f..505c030ca1 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -32,8 +32,12 @@
#include "../csharp_script.h"
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
@@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
return;
}
+
+void unhandled_exception(MonoException *p_exc) {
+ mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+ abort();
+ _UNREACHABLE_();
+}
+
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index abec65e7d4..50cadcedf6 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -33,11 +33,20 @@
#include <mono/jit/jit.h>
+#include "../utils/macros.h"
+
#include "core/object.h"
namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
-}
+
+/**
+ * Do not call this function directly.
+ * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
+ */
+_NO_RETURN_ void unhandled_exception(MonoException *p_exc);
+
+} // namespace GDMonoInternals
#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index eabea8dc3c..5224d309de 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -152,8 +152,7 @@ void GDMonoLog::initialize() {
log_level_id = log_level_get_id(log_level);
if (log_file) {
- if (OS::get_singleton()->is_stdout_verbose())
- OS::get_singleton()->print(String("Mono: Logfile is " + log_file_path + "\n").utf8());
+ print_verbose("Mono: Logfile is " + log_file_path);
mono_trace_set_log_handler(gdmono_MonoLogCallback, this);
} else {
OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c04fcca962..de91e71bab 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return Variant::ARRAY;
@@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
if (CACHED_CLASS(RID) == type_class) {
return Variant::_RID;
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return Variant::DICTIONARY;
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return Variant::ARRAY;
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
return Variant::DICTIONARY;
}
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return Variant::ARRAY;
+ }
} break;
default: {
@@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
type.type_encoding = MONO_TYPE_OBJECT;
+ // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
return variant_to_mono_object(p_var, type);
}
@@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
@@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
if (CACHED_CLASS(RID) == type_class) {
return GDMonoUtils::create_managed_from(p_var->operator RID());
}
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ if (CACHED_CLASS(Array) == type_class) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+ }
} break;
case MONO_TYPE_OBJECT: {
// Variant
@@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
}
case Variant::DICTIONARY:
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
case Variant::ARRAY:
- return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
case Variant::POOL_BYTE_ARRAY:
return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
case Variant::POOL_INT_ARRAY:
@@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) {
- return Dictionary_to_mono_object(p_var->operator Dictionary());
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
+ }
+
+ exc = NULL;
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
} break;
} break;
@@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj));
ERR_FAIL_COND_V(!tclass, Variant());
- MonoType *raw_type = tclass->get_raw_type(tclass);
+ MonoType *raw_type = tclass->get_mono_type();
ManagedType type;
@@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class));
+ MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
return mono_array_to_Array((MonoArray *)p_obj);
@@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
return ptr ? Variant(*ptr) : Variant();
}
+
+ if (CACHED_CLASS(Array) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr);
+ Array *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
+
+ if (CACHED_CLASS(Dictionary) == type_class) {
+ MonoException *exc = NULL;
+ GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr);
+ Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return ptr ? Variant(*ptr) : Variant();
+ }
} break;
case MONO_TYPE_GENERICINST: {
- if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) {
- return mono_object_to_Dictionary(p_obj);
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+
+ MonoException *exc = NULL;
+
+ GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType);
+ MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_dict) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Dictionary *>(ret);
+ }
+
+ exc = NULL;
+
+ GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType);
+ MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ if (is_array) {
+ MonoException *exc = NULL;
+ MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return *unbox<Array *>(ret);
}
} break;
}
@@ -822,62 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
}
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
- MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
- MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size());
-
- int i = 0;
- const Variant *dkey = NULL;
- while ((dkey = p_dict.next(dkey))) {
- mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey));
- mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey]));
- i++;
- }
-
- GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
-
- MonoObject *ex = NULL;
- MonoObject *ret = arrays_to_dict(keys, values, &ex);
-
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL_V(NULL);
- }
-
- return ret;
-}
-
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
- Dictionary ret;
-
- if (!p_dict)
- return ret;
-
- GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays);
-
- MonoArray *keys = NULL;
- MonoArray *values = NULL;
- MonoObject *ex = NULL;
- dict_to_arrays(p_dict, &keys, &values, &ex);
-
- if (ex) {
- mono_print_unhandled_exception(ex);
- ERR_FAIL_V(Dictionary());
- }
-
- int length = mono_array_length(keys);
-
- for (int i = 0; i < length; i++) {
- MonoObject *key_obj = mono_array_get(keys, MonoObject *, i);
- MonoObject *value_obj = mono_array_get(values, MonoObject *, i);
-
- Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant();
- Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant();
-
- ret[key] = value;
- }
-
- return ret;
-}
-}
+} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 6572408ab5..464f584a0a 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array);
MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array);
PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array);
-// Dictionary
-
-MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict);
-Dictionary mono_object_to_Dictionary(MonoObject *p_dict);
-
#ifdef YOLO_COPY
#define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in;
#define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in);
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index ad52904945..630bda8b4e 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method);
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
@@ -104,28 +104,28 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
mono_array_set(params, MonoObject *, i, boxed_param);
}
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc);
+ MonoException *exc = NULL;
+ MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
return ret;
} else {
- MonoObject *exc = NULL;
- mono_runtime_invoke(mono_method, p_object, NULL, &exc);
+ MonoException *exc = NULL;
+ GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
@@ -133,21 +133,21 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
}
}
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) {
+MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
return invoke_raw(p_object, NULL, r_exc);
}
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc);
+MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index a173af83f4..444ec2a67d 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -71,9 +71,9 @@ public:
void *get_thunk();
- MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL);
- MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL);
- MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
+ MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
+ MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 0fe527b199..ce66e0c8db 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -138,47 +138,44 @@ bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != NULL;
}
-void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) {
+void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property);
-
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
mono_array_set(params, MonoObject *, 0, p_value);
-
- MonoObject *exc = NULL;
- mono_runtime_invoke_array(prop_method, p_object, params, &exc);
-
+ MonoException *exc = NULL;
+ GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
}
-void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- mono_property_set_value(mono_property, p_object, p_params, &exc);
+void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
}
-MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) {
- MonoObject *exc = NULL;
- MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc);
+MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
+ MonoException *exc = NULL;
+ MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc);
if (exc) {
ret = NULL;
if (r_exc) {
*r_exc = exc;
} else {
- GDMonoUtils::print_unhandled_exception(exc);
+ GDMonoUtils::set_pending_exception(exc);
}
}
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index 2a0065e850..b3f1e2114a 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -62,9 +62,9 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; }
- void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL);
- void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL);
- MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL);
+ void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL);
+ void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
+ MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL);
bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index fbdbeaa3f7..c1f56bc3d2 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -30,12 +30,15 @@
#include "gd_mono_utils.h"
+#include <mono/metadata/exception.h>
+
#include "os/dir_access.h"
#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
#include "../csharp_script.h"
+#include "../utils/macros.h"
#include "gd_mono.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
@@ -84,6 +87,8 @@ void MonoCache::clear_members() {
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
+ class_KeyNotFoundException = NULL;
+
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
@@ -104,6 +109,8 @@ void MonoCache::clear_members() {
class_Control = NULL;
class_Spatial = NULL;
class_WeakRef = NULL;
+ class_Array = NULL;
+ class_Dictionary = NULL;
class_MarshalUtils = NULL;
#ifdef DEBUG_ENABLED
@@ -131,8 +138,10 @@ void MonoCache::clear_members() {
field_Image_ptr = NULL;
field_RID_ptr = NULL;
- methodthunk_MarshalUtils_DictionaryToArrays = NULL;
- methodthunk_MarshalUtils_ArraysToDictionary = NULL;
+ methodthunk_Array_GetPtr = NULL;
+ methodthunk_Dictionary_GetPtr = NULL;
+ methodthunk_MarshalUtils_IsArrayGenericType = NULL;
+ methodthunk_MarshalUtils_IsDictionaryGenericType = NULL;
methodthunk_SignalAwaiter_SignalCallback = NULL;
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
@@ -147,6 +156,7 @@ void MonoCache::cleanup() {
}
#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
+#define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
void update_corlib_cache() {
@@ -167,11 +177,13 @@ void update_corlib_cache() {
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
- CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames"));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
#endif
+ CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
+
mono_cache.corlib_cache_updated = true;
}
@@ -195,6 +207,8 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
+ CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
+ CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED
@@ -221,34 +235,21 @@ void update_godot_api_cache() {
CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
- CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method_thunk("GetPtr", 0));
+ CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method_thunk("GetPtr", 0));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
+ CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
#ifdef DEBUG_ENABLED
- CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
+ CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
#endif
- {
- /*
- * TODO Right now we only support Dictionary<object, object>.
- * It would be great if we could support other key/value types
- * without forcing the user to copy the entries.
- */
- GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0);
- ERR_FAIL_NULL(method_get_dict_type);
- MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL);
- ERR_FAIL_NULL(dict_refl_type);
- MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type);
- ERR_FAIL_NULL(dict_type);
-
- CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type));
- }
-
+ // TODO Move to CSharpLanguage::init()
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- mono_runtime_object_init(task_scheduler);
+ GDMonoUtils::runtime_object_init(task_scheduler);
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
@@ -301,6 +302,12 @@ MonoThread *get_current_thread() {
return mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_runtime_object_init(p_this_obj);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
GDMonoClass *get_object_class(MonoObject *p_object) {
return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
}
@@ -355,7 +362,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
return mono_object;
}
@@ -365,7 +372,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
@@ -377,13 +384,73 @@ MonoObject *create_managed_from(const RID &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- mono_runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object);
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
return mono_object;
}
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Array *new_array = memnew(Array(p_from));
+ void *args[1] = { &new_array };
+
+ MonoException *exc = NULL;
+ GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
+ MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+ ERR_FAIL_NULL_V(mono_object, NULL);
+
+ // Search constructor that takes a pointer as parameter
+ MonoMethod *m;
+ void *iter = NULL;
+ while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
+ if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
+ MonoMethodSignature *sig = mono_method_signature(m);
+ void *front = NULL;
+ if (mono_signature_get_param_count(sig) == 1 &&
+ mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
+ break;
+ }
+ }
+ }
+
+ CRASH_COND(m == NULL);
+
+ Dictionary *new_dict = memnew(Dictionary(p_from));
+ void *args[1] = { &new_dict };
+
+ MonoException *exc = NULL;
+ GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+
+ return mono_object;
+}
+
MonoDomain *create_domain(const String &p_friendly_name) {
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
@@ -397,10 +464,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
-String get_exception_name_and_message(MonoObject *p_ex) {
+String get_exception_name_and_message(MonoException *p_exc) {
String res;
- MonoClass *klass = mono_object_get_class(p_ex);
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type);
@@ -410,29 +477,39 @@ String get_exception_name_and_message(MonoObject *p_ex) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL);
+ MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL);
res += GDMonoMarshal::mono_string_to_godot(msg);
return res;
}
-void print_unhandled_exception(MonoObject *p_exc) {
- print_unhandled_exception(p_exc, false);
+void set_exception_message(MonoException *p_exc, String message) {
+ MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
+ MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
+ MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
+ void *params[1] = { msg };
+ property_set_value(prop, (MonoObject *)p_exc, params, NULL);
+}
+
+void debug_print_unhandled_exception(MonoException *p_exc) {
+ print_unhandled_exception(p_exc);
+ debug_send_unhandled_exception_error(p_exc);
}
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
- mono_print_unhandled_exception(p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
if (!ScriptDebugger::get_singleton())
return;
+ _TLS_RECURSION_GUARD_;
+
ScriptLanguage::StackInfo separator;
- separator.file = "";
+ separator.file = String();
separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
separator.line = 0;
Vector<ScriptLanguage::StackInfo> si;
- String exc_msg = "";
+ String exc_msg;
while (p_exc != NULL) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
@@ -441,24 +518,16 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info };
- MonoObject *unexpected_exc = NULL;
+ MonoException *unexpected_exc = NULL;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
- if (unexpected_exc != NULL) {
- mono_print_unhandled_exception(unexpected_exc);
-
- if (p_recursion_caution) {
- // Called from CSharpLanguage::get_current_stack_info,
- // so printing an error here could result in endless recursion
- OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
- return;
- } else {
- ERR_FAIL();
- }
+ if (unexpected_exc) {
+ GDMonoInternals::unhandled_exception(unexpected_exc);
+ _UNREACHABLE_();
}
Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != NULL && !p_recursion_caution) {
+ if (stack_trace != NULL) {
_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
for (int i = _si.size() - 1; i >= 0; i--)
si.insert(0, _si[i]);
@@ -466,10 +535,15 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
- GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException");
- p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL;
- if (p_exc != NULL)
+ GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
+ GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
+ CRASH_COND(inner_exc_prop == NULL);
+
+ MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
+ if (inner_exc != NULL)
si.insert(0, separator);
+
+ p_exc = (MonoException *)inner_exc;
}
String file = si.size() ? si[0].file : __FILE__;
@@ -481,4 +555,72 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
#endif
}
+void debug_unhandled_exception(MonoException *p_exc) {
+#ifdef DEBUG_ENABLED
+ GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
+ if (ScriptDebugger::get_singleton())
+ ScriptDebugger::get_singleton()->idle_poll();
+#endif
+ GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
+ _UNREACHABLE_();
+}
+
+void print_unhandled_exception(MonoException *p_exc) {
+ mono_print_unhandled_exception((MonoObject *)p_exc);
+}
+
+void set_pending_exception(MonoException *p_exc) {
+#ifdef HAS_PENDING_EXCEPTIONS
+ if (get_runtime_invoke_count() == 0) {
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+ }
+
+ if (!mono_runtime_set_pending_exception(p_exc, false)) {
+ ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
+ GDMonoUtils::debug_print_unhandled_exception(p_exc);
+ }
+#else
+ debug_unhandled_exception(p_exc);
+ _UNREACHABLE_();
+#endif
+}
+
+_THREAD_LOCAL_(int)
+current_invoke_count = 0;
+
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ return ret;
+}
+
+MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)p_exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ return ret;
+}
+
+MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ return ret;
+}
+
+void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+}
+
+MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) {
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc);
+ GD_MONO_END_RUNTIME_INVOKE;
+ return ret;
+}
+
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index fc13a00e85..bf8860c85a 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -34,19 +34,31 @@
#include <mono/metadata/threads.h>
#include "../mono_gc_handle.h"
+#include "../utils/macros.h"
+#include "../utils/thread_local.h"
#include "gd_mono_header.h"
#include "object.h"
#include "reference.h"
+#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \
+ if (unlikely(m_exc != NULL)) { \
+ GDMonoUtils::debug_unhandled_exception(m_exc); \
+ _UNREACHABLE_(); \
+ }
+
namespace GDMonoUtils {
-typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **);
-typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **);
+typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **);
+typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **);
+typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache {
@@ -77,6 +89,8 @@ struct MonoCache {
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
+ GDMonoClass *class_KeyNotFoundException;
+
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@@ -98,6 +112,8 @@ struct MonoCache {
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
GDMonoClass *class_WeakRef;
+ GDMonoClass *class_Array;
+ GDMonoClass *class_Dictionary;
GDMonoClass *class_MarshalUtils;
#ifdef DEBUG_ENABLED
@@ -125,8 +141,10 @@ struct MonoCache {
GDMonoField *field_Image_ptr;
GDMonoField *field_RID_ptr;
- MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays;
- MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary;
+ Array_GetPtr methodthunk_Array_GetPtr;
+ Dictionary_GetPtr methodthunk_Dictionary_GetPtr;
+ IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType;
+ IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType;
SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback;
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
@@ -173,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() {
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
}
+void runtime_object_init(MonoObject *p_this_obj);
+
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
GDMonoClass *get_class_native_base(GDMonoClass *p_class);
@@ -181,13 +201,42 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
MonoObject *create_managed_from(const NodePath &p_from);
MonoObject *create_managed_from(const RID &p_from);
+MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
+MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
MonoDomain *create_domain(const String &p_friendly_name);
-String get_exception_name_and_message(MonoObject *p_ex);
+String get_exception_name_and_message(MonoException *p_exc);
+void set_exception_message(MonoException *p_exc, String message);
+
+void debug_print_unhandled_exception(MonoException *p_exc);
+void debug_send_unhandled_exception_error(MonoException *p_exc);
+_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc);
+void print_unhandled_exception(MonoException *p_exc);
+
+/**
+ * Sets the exception as pending. The exception will be thrown when returning to managed code.
+ * If no managed method is being invoked by the runtime, the exception will be treated as
+ * an unhandled exception and the method will not return.
+ */
+void set_pending_exception(MonoException *p_exc);
+
+extern _THREAD_LOCAL_(int) current_invoke_count;
+
+_FORCE_INLINE_ int get_runtime_invoke_count() {
+ return current_invoke_count;
+}
+_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);
+
+MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc);
-void print_unhandled_exception(MonoObject *p_exc);
-void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
+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);
} // namespace GDMonoUtils
@@ -206,4 +255,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif
+#define GD_MONO_BEGIN_RUNTIME_INVOKE \
+ int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
+ _runtime_invoke_count_ref += 1;
+
+#define GD_MONO_END_RUNTIME_INVOKE \
+ _runtime_invoke_count_ref -= 1;
+
#endif // GD_MONOUTILS_H
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py
index 9c188d07a7..c8ebb54ded 100644
--- a/modules/mono/mono_reg_utils.py
+++ b/modules/mono/mono_reg_utils.py
@@ -60,10 +60,10 @@ def _find_mono_in_reg_old(subkey, bits):
def find_mono_root_dir(bits):
root_dir = _find_mono_in_reg(r'SOFTWARE\Mono', bits)
if root_dir is not None:
- return root_dir
+ return str(root_dir)
root_dir = _find_mono_in_reg_old(r'SOFTWARE\Novell\Mono', bits)
if root_dir is not None:
- return root_dir
+ return str(root_dir)
return ''
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index b9d8520ac9..add1e506ea 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -42,8 +42,7 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
- uint32_t awaiter_handle = MonoGCHandle::make_strong_handle(p_awaiter);
- Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(awaiter_handle));
+ Ref<SignalAwaiterHandle> sa_con = memnew(SignalAwaiterHandle(p_awaiter));
#ifdef DEBUG_ENABLED
sa_con->set_connection_target(p_target);
#endif
@@ -101,11 +100,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback);
- MonoObject *ex = NULL;
- thunk(get_target(), signal_args, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(get_target(), signal_args, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Variant());
}
@@ -117,8 +118,8 @@ void SignalAwaiterHandle::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &SignalAwaiterHandle::_signal_callback, MethodInfo("_signal_callback"));
}
-SignalAwaiterHandle::SignalAwaiterHandle(uint32_t p_managed_handle) :
- MonoGCHandle(p_managed_handle) {
+SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) :
+ MonoGCHandle(MonoGCHandle::make_strong_handle(p_managed), STRONG_HANDLE) {
#ifdef DEBUG_ENABLED
conn_target_id = 0;
@@ -133,11 +134,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
MonoObject *awaiter = get_target();
if (awaiter) {
- MonoObject *ex = NULL;
- thunk(awaiter, &ex);
+ MonoException *exc = NULL;
+ GD_MONO_BEGIN_RUNTIME_INVOKE;
+ thunk(awaiter, (MonoObject **)&exc);
+ GD_MONO_END_RUNTIME_INVOKE;
- if (ex) {
- mono_print_unhandled_exception(ex);
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V();
}
}
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index a6a205ff8d..1920432709 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -64,7 +64,7 @@ public:
}
#endif
- SignalAwaiterHandle(uint32_t p_managed_handle);
+ SignalAwaiterHandle(MonoObject *p_managed);
~SignalAwaiterHandle();
};
diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py
new file mode 100644
index 0000000000..622280b00b
--- /dev/null
+++ b/modules/mono/tls_configure.py
@@ -0,0 +1,36 @@
+from __future__ import print_function
+
+def supported(result):
+ return 'supported' if result else 'not supported'
+
+
+def check_cxx11_thread_local(conf):
+ print('Checking for `thread_local` support...', end=" ")
+ result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_declspec_thread(conf):
+ print('Checking for `__declspec(thread)` support...', end=" ")
+ result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def check_gcc___thread(conf):
+ print('Checking for `__thread` support...', end=" ")
+ result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
+ print(supported(result))
+ return bool(result)
+
+
+def configure(conf):
+ if check_cxx11_thread_local(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
+ else:
+ if conf.env.msvc:
+ if check_declspec_thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
+ elif check_gcc___thread(conf):
+ conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
new file mode 100644
index 0000000000..337a86870e
--- /dev/null
+++ b/modules/mono/utils/macros.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* util_macros.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 UTIL_MACROS_H
+#define UTIL_MACROS_H
+
+// noreturn
+
+#undef _NO_RETURN_
+
+#ifdef __GNUC__
+#define _NO_RETURN_ __attribute__((noreturn))
+#elif _MSC_VER
+#define _NO_RETURN_ __declspec(noreturn)
+#else
+#error Platform or compiler not supported
+#endif
+
+// unreachable
+
+#if defined(_MSC_VER)
+#define _UNREACHABLE_() __assume(0)
+#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+#define _UNREACHABLE_() __builtin_unreachable()
+#else
+#define _UNREACHABLE_() \
+ CRASH_NOW(); \
+ do { \
+ } while (true);
+#endif
+
+#endif // UTIL_MACROS_H
diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp
new file mode 100644
index 0000000000..a0e28fca5f
--- /dev/null
+++ b/modules/mono/utils/thread_local.cpp
@@ -0,0 +1,107 @@
+/*************************************************************************/
+/* thread_local.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "thread_local.h"
+
+#ifdef WINDOWS_ENABLED
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include "core/os/memory.h"
+#include "core/print_string.h"
+
+struct ThreadLocalStorage::Impl {
+
+#ifdef WINDOWS_ENABLED
+ DWORD dwFlsIndex;
+#else
+ pthread_key_t key;
+#endif
+
+ void *get_value() const {
+#ifdef WINDOWS_ENABLED
+ return FlsGetValue(dwFlsIndex);
+#else
+ return pthread_getspecific(key);
+#endif
+ }
+
+ void set_value(void *p_value) const {
+#ifdef WINDOWS_ENABLED
+ FlsSetValue(dwFlsIndex, p_value);
+#else
+ pthread_setspecific(key, p_value);
+#endif
+ }
+
+#ifdef WINDOWS_ENABLED
+#define _CALLBACK_FUNC_ __stdcall
+#else
+#define _CALLBACK_FUNC_
+#endif
+
+ Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
+#ifdef WINDOWS_ENABLED
+ dwFlsIndex = FlsAlloc(p_destr_callback_func);
+ ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
+#else
+ pthread_key_create(&key, p_destr_callback_func);
+#endif
+ }
+
+ ~Impl() {
+#ifdef WINDOWS_ENABLED
+ FlsFree(dwFlsIndex);
+#else
+ pthread_key_delete(key);
+#endif
+ }
+};
+
+void *ThreadLocalStorage::get_value() const {
+ return pimpl->get_value();
+}
+
+void ThreadLocalStorage::set_value(void *p_value) const {
+ pimpl->set_value(p_value);
+}
+
+void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
+ pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
+}
+
+#undef _CALLBACK_FUNC_
+
+void ThreadLocalStorage::free() {
+ memdelete(pimpl);
+ pimpl = NULL;
+}
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
new file mode 100644
index 0000000000..84dae1d86b
--- /dev/null
+++ b/modules/mono/utils/thread_local.h
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* thread_local.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 THREAD_LOCAL_H
+#define THREAD_LOCAL_H
+
+#ifdef HAVE_CXX11_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) thread_local m_t
+#else
+
+#if !defined(__GNUC__) && !defined(_MSC_VER)
+#error Platform or compiler not supported
+#endif
+
+#ifdef __GNUC__
+
+#ifdef HAVE_GCC___THREAD
+#define _THREAD_LOCAL_(m_t) __thread m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#elif _MSC_VER
+
+#ifdef HAVE_DECLSPEC_THREAD
+#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
+#else
+#define USE_CUSTOM_THREAD_LOCAL
+#endif
+
+#endif // __GNUC__ _MSC_VER
+
+#endif // HAVE_CXX11_THREAD_LOCAL
+
+#ifdef USE_CUSTOM_THREAD_LOCAL
+#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
+#endif
+
+#include "core/typedefs.h"
+
+#ifdef WINDOWS_ENABLED
+#define _CALLBACK_FUNC_ __stdcall
+#else
+#define _CALLBACK_FUNC_
+#endif
+
+struct ThreadLocalStorage {
+
+ void *get_value() const;
+ void set_value(void *p_value) const;
+
+ void alloc(void(_CALLBACK_FUNC_ *p_dest_callback)(void *));
+ void free();
+
+private:
+ struct Impl;
+ Impl *pimpl;
+};
+
+template <typename T>
+class ThreadLocal {
+
+ ThreadLocalStorage storage;
+
+ T init_val;
+
+ static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
+ memdelete(static_cast<T *>(tls_data));
+ }
+
+ T *_tls_get_value() const {
+ void *tls_data = storage.get_value();
+
+ if (tls_data)
+ return static_cast<T *>(tls_data);
+
+ T *data = memnew(T(init_val));
+
+ storage.set_value(data);
+
+ return data;
+ }
+
+public:
+ ThreadLocal() :
+ ThreadLocal(T()) {}
+
+ ThreadLocal(const T &p_init_val) :
+ init_val(p_init_val) {
+ storage.alloc(&destr_callback);
+ }
+
+ ThreadLocal(const ThreadLocal &other) :
+ ThreadLocal(*other._tls_get_value()) {}
+
+ ~ThreadLocal() {
+ storage.free();
+ }
+
+ _FORCE_INLINE_ T *operator&() const {
+ return _tls_get_value();
+ }
+
+ _FORCE_INLINE_ operator T &() const {
+ return *_tls_get_value();
+ }
+
+ _FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
+ T *ptr = _tls_get_value();
+ *ptr = val;
+ return *this;
+ }
+};
+
+struct FlagScopeGuard {
+
+ FlagScopeGuard(bool &p_flag) :
+ flag(p_flag) {
+ flag = !flag;
+ }
+
+ ~FlagScopeGuard() {
+ flag = !flag;
+ }
+
+private:
+ bool &flag;
+};
+
+#undef _CALLBACK_FUNC_
+
+#define _TLS_RECURSION_GUARD_V_(m_ret) \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return m_ret; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#define _TLS_RECURSION_GUARD_ \
+ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
+ if (_recursion_flag_) \
+ return; \
+ FlagScopeGuard _recursion_guard_(_recursion_flag_);
+
+#endif // THREAD_LOCAL_H
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 8174bccdb7..6ec44023c1 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -93,7 +93,7 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path,
print_line("bmask: "+itos(bmask));
print_line("amask: "+itos(amask));
print_line("surfcount: "+itos(surfcount));
-*/
+ */
PoolVector<uint8_t> data;
data.resize(surfsize);
@@ -159,8 +159,6 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path,
if (mipmaps)
tex_flags |= Texture::FLAG_MIPMAPS;
- print_line("flip: " + itos(flags & PVR_VFLIP));
-
Ref<Image> image = memnew(Image(width, height, mipmaps, format, data));
ERR_FAIL_COND_V(image->empty(), RES());
@@ -240,11 +238,11 @@ ResourceFormatPVR::ResourceFormatPVR() {
Image::_image_compress_pvrtc2_func = _compress_pvrtc4;
}
- /////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////
- //PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
+//PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
- /////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////
#define PT_INDEX 2
#define BLK_Y_SIZE 4
@@ -646,12 +644,6 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int
static void _pvrtc_decompress(Image *p_img) {
- /*
- static void decompress_pvrtc(const void *p_comp_img, const int p_2bit, const int p_width, const int p_height, unsigned char* p_dst) {
- decompress_pvrtc((PVRTCBlock*)p_comp_img,p_2bit,p_width,p_height,1,p_dst);
- }
- */
-
ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC2 && p_img->get_format() != Image::FORMAT_PVRTC2A && p_img->get_format() != Image::FORMAT_PVRTC4 && p_img->get_format() != Image::FORMAT_PVRTC4A);
bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC2 || p_img->get_format() == Image::FORMAT_PVRTC2A);
@@ -665,12 +657,6 @@ static void _pvrtc_decompress(Image *p_img) {
decompress_pvrtc((PVRTCBlock *)r.ptr(), _2bit, p_img->get_width(), p_img->get_height(), 0, (unsigned char *)w.ptr());
- /*
- for(int i=0;i<newdata.size();i++) {
- print_line(itos(w[i]));
- }
- */
-
w = PoolVector<uint8_t>::Write();
r = PoolVector<uint8_t>::Read();
diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp
index 64c4b85269..c1ce2592a4 100644
--- a/modules/recast/navigation_mesh_generator.cpp
+++ b/modules/recast/navigation_mesh_generator.cpp
@@ -126,9 +126,9 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con
for (unsigned int j = 0; j < ntris; j++) {
Vector<int> nav_indices;
nav_indices.resize(3);
- nav_indices[0] = ((int)(bverts + tris[j * 4 + 0]));
- nav_indices[1] = ((int)(bverts + tris[j * 4 + 1]));
- nav_indices[2] = ((int)(bverts + tris[j * 4 + 2]));
+ nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0]));
+ nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 1]));
+ nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 2]));
p_nav_mesh->add_polygon(nav_indices);
}
}
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index 18b4051afe..4b8d5e9283 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -19,10 +19,13 @@ if env['builtin_pcre2']:
"pcre2_compile.c",
"pcre2_config.c",
"pcre2_context.c",
+ "pcre2_convert.c",
"pcre2_dfa_match.c",
"pcre2_error.c",
+ "pcre2_extuni.c",
"pcre2_find_bracket.c",
"pcre2_jit_compile.c",
+ #"pcre2_jit_match.c", "pcre2_jit_misc.c", # these files are included in pcre2_jit_compile.c.
"pcre2_maketables.c",
"pcre2_match.c",
"pcre2_match_data.c",
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index 6f2bb46fc8..bdd3e31eb8 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -205,6 +205,8 @@ Error RegEx::compile(const String &p_pattern) {
code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx);
+ pcre2_compile_context_free_16(cctx);
+
if (!code) {
PCRE2_UCHAR16 buf[256];
pcre2_get_error_message_16(err, buf, 256);
@@ -221,6 +223,8 @@ Error RegEx::compile(const String &p_pattern) {
code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx);
+ pcre2_compile_context_free_32(cctx);
+
if (!code) {
PCRE2_UCHAR32 buf[256];
pcre2_get_error_message_32(err, buf, 256);
@@ -265,8 +269,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
for (uint32_t i = 0; i < size; i++) {
- result->data[i].start = ovector[i * 2];
- result->data[i].end = ovector[i * 2 + 1];
+ result->data.write[i].start = ovector[i * 2];
+ result->data.write[i].end = ovector[i * 2 + 1];
}
pcre2_match_data_free_16(match);
@@ -285,6 +289,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
if (res < 0) {
pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
+
return NULL;
}
@@ -295,8 +301,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
for (uint32_t i = 0; i < size; i++) {
- result->data[i].start = ovector[i * 2];
- result->data[i].end = ovector[i * 2 + 1];
+ result->data.write[i].start = ovector[i * 2];
+ result->data.write[i].end = ovector[i * 2 + 1];
}
pcre2_match_data_free_32(match);
diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp
index 0cf24dd8d8..bb8a4bdbea 100644
--- a/modules/squish/image_compress_squish.cpp
+++ b/modules/squish/image_compress_squish.cpp
@@ -32,12 +32,6 @@
#include "print_string.h"
-#if defined(__SSE2__)
-#define SQUISH_USE_SSE 2
-#elif defined(__SSE__)
-#define SQUISH_USE_SSE 1
-#endif
-
#include <squish.h>
void image_decompress_squish(Image *p_image) {
@@ -46,7 +40,7 @@ void image_decompress_squish(Image *p_image) {
Image::Format target_format = Image::FORMAT_RGBA8;
PoolVector<uint8_t> data;
- int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
+ int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
int mm_count = p_image->get_mipmap_count();
data.resize(target_size);
@@ -65,7 +59,7 @@ void image_decompress_squish(Image *p_image) {
} else if (p_image->get_format() == Image::FORMAT_RGTC_RG) {
squish_flags = squish::kBc5;
} else {
- print_line("Can't decompress unknown format: " + itos(p_image->get_format()));
+ ERR_EXPLAIN("Squish: Can't decompress unknown format: " + itos(p_image->get_format()));
ERR_FAIL_COND(true);
return;
}
@@ -81,7 +75,7 @@ void image_decompress_squish(Image *p_image) {
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
-void image_compress_squish(Image *p_image, Image::CompressSource p_source) {
+void image_compress_squish(Image *p_image, float p_lossy_quality, Image::CompressSource p_source) {
if (p_image->get_format() >= Image::FORMAT_DXT1)
return; //do not compress, already compressed
@@ -92,10 +86,43 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) {
if (p_image->get_format() <= Image::FORMAT_RGBA8) {
int squish_comp = squish::kColourRangeFit;
+
+ if (p_lossy_quality > 0.85)
+ squish_comp = squish::kColourIterativeClusterFit;
+ else if (p_lossy_quality > 0.75)
+ squish_comp = squish::kColourClusterFit;
+
Image::Format target_format = Image::FORMAT_RGBA8;
Image::DetectChannels dc = p_image->get_detected_channels();
+ if (p_source == Image::COMPRESS_SOURCE_LAYERED) {
+ //keep what comes in
+ switch (p_image->get_format()) {
+ case Image::FORMAT_L8: {
+ dc = Image::DETECTED_L;
+ } break;
+ case Image::FORMAT_LA8: {
+ dc = Image::DETECTED_LA;
+ } break;
+ case Image::FORMAT_R8: {
+ dc = Image::DETECTED_R;
+ } break;
+ case Image::FORMAT_RG8: {
+ dc = Image::DETECTED_RG;
+ } break;
+ case Image::FORMAT_RGB8: {
+ dc = Image::DETECTED_RGB;
+ } break;
+ case Image::FORMAT_RGBA8:
+ case Image::FORMAT_RGBA4444:
+ case Image::FORMAT_RGBA5551: {
+ dc = Image::DETECTED_RGBA;
+ } break;
+ default: {}
+ }
+ }
+
p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
if (p_source == Image::COMPRESS_SOURCE_SRGB && (dc == Image::DETECTED_R || dc == Image::DETECTED_RG)) {
@@ -148,7 +175,7 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) {
}
PoolVector<uint8_t> data;
- int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
+ int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
data.resize(target_size);
int shift = Image::get_format_pixel_rshift(target_format);
@@ -166,8 +193,8 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) {
int src_ofs = p_image->get_mipmap_offset(i);
squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp);
dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift;
- w >>= 1;
- h >>= 1;
+ w = MAX(w / 2, 1);
+ h = MAX(h / 2, 1);
}
rb = PoolVector<uint8_t>::Read();
diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h
index c022063fe5..6da947beea 100644
--- a/modules/squish/image_compress_squish.h
+++ b/modules/squish/image_compress_squish.h
@@ -33,7 +33,7 @@
#include "image.h"
-void image_compress_squish(Image *p_image, Image::CompressSource p_source);
+void image_compress_squish(Image *p_image, float p_lossy_quality, Image::CompressSource p_source);
void image_decompress_squish(Image *p_image);
#endif // IMAGE_COMPRESS_SQUISH_H
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index c95a8ac2dd..0e533d3978 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -208,8 +208,6 @@ void AudioStreamOGGVorbis::set_data(const PoolVector<uint8_t> &p_data) {
//does this work? (it's less mem..)
//decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size;
- //print_line("succeeded "+itos(ogg_alloc.alloc_buffer_length_in_bytes)+" setup "+itos(info.setup_memory_required)+" setup temp "+itos(info.setup_temp_memory_required)+" temp "+itos(info.temp_memory_required)+" maxframe"+itos(info.max_frame_size));
-
length = stb_vorbis_stream_length_in_seconds(ogg_stream);
stb_vorbis_close(ogg_stream);
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 7391bed699..d4fa88afa7 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -250,8 +250,9 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
if (tga_header.image_width <= 0 || tga_header.image_height <= 0)
err = FAILED;
- if (tga_header.pixel_depth != 8 && tga_header.pixel_depth != 24 && tga_header.pixel_depth != 32)
+ if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
err = FAILED;
+ }
if (err == OK) {
f->seek(f->get_position() + tga_header.id_length);
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index 9bc5ed903a..971fe39c44 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_theora.h"
+
#include "video_stream_theora.h"
+static ResourceFormatLoaderTheora *resource_loader_theora = NULL;
+
void register_theora_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterTheora> theora_import;
- theora_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(theora_import);
-#endif
+ resource_loader_theora = memnew(ResourceFormatLoaderTheora);
+ ResourceLoader::add_resource_format_loader(resource_loader_theora, true);
+
ClassDB::register_class<VideoStreamTheora>();
}
void unregister_theora_types() {
+
+ if (resource_loader_theora) {
+ memdelete(resource_loader_theora);
+ }
}
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 9e6307c0bf..68087ac01f 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -387,7 +387,6 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
thread_sem->post();
#endif
- //print_line("play "+rtos(p_delta));
time += p_delta;
if (videobuf_time > get_time()) {
@@ -442,16 +441,8 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
int tr = vorbis_synthesis_read(&vd, ret - to_read);
- if (vd.granulepos >= 0) {
- //print_line("wrote: "+itos(audio_frames_wrote)+" gpos: "+itos(vd.granulepos));
- }
-
- //print_line("mix audio!");
-
audio_frames_wrote += ret - to_read;
- //print_line("AGP: "+itos(vd.granulepos)+" added "+itos(ret-to_read));
-
} else {
/* no pending audio; is there a pending packet to decode? */
@@ -460,7 +451,6 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
vorbis_synthesis_blockin(&vd, &vb);
}
} else { /* we need more data; break out to suck in another page */
- //printf("need moar data\n");
break;
};
}
@@ -730,3 +720,46 @@ void VideoStreamTheora::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
}
+
+////////////
+
+RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ if (r_error) {
+ *r_error = ERR_CANT_OPEN;
+ }
+ memdelete(f);
+ return RES();
+ }
+
+ VideoStreamTheora *stream = memnew(VideoStreamTheora);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return ogv_stream;
+}
+
+void ResourceFormatLoaderTheora::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ogv");
+}
+
+bool ResourceFormatLoaderTheora::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderTheora::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "ogv")
+ return "VideoStreamTheora";
+ return "";
+}
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 4bdbbdae20..1aba3f56da 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -163,7 +163,6 @@ public:
class VideoStreamTheora : public VideoStream {
GDCLASS(VideoStreamTheora, VideoStream);
- RES_BASE_EXTENSION("ogvstr");
String file;
int audio_track;
@@ -186,4 +185,12 @@ public:
VideoStreamTheora() { audio_track = 0; }
};
+class ResourceFormatLoaderTheora : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
#endif
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 0abefe11ee..b19bcfefcb 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -129,15 +129,45 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
PoolVector<uint8_t> imgdata;
Image::Format format;
+ int output_channels = 0;
if (idxA > 0) {
imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16
format = Image::FORMAT_RGBAH;
+ output_channels = 4;
} else {
imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16
format = Image::FORMAT_RGBH;
+ output_channels = 3;
+ }
+
+ EXRTile single_image_tile;
+ int num_tiles;
+ int tile_width = 0;
+ int tile_height = 0;
+
+ const EXRTile *exr_tiles;
+
+ if (!exr_header.tiled) {
+ single_image_tile.images = exr_image.images;
+ single_image_tile.width = exr_image.width;
+ single_image_tile.height = exr_image.height;
+ single_image_tile.level_x = exr_image.width;
+ single_image_tile.level_y = exr_image.height;
+ single_image_tile.offset_x = 0;
+ single_image_tile.offset_y = 0;
+
+ exr_tiles = &single_image_tile;
+ num_tiles = 1;
+ tile_width = exr_image.width;
+ tile_height = exr_image.height;
+ } else {
+ tile_width = exr_header.tile_size_x;
+ tile_height = exr_header.tile_size_y;
+ num_tiles = exr_image.num_tiles;
+ exr_tiles = exr_image.tiles;
}
{
@@ -145,22 +175,51 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
uint16_t *iw = (uint16_t *)wd.ptr();
// Assume `out_rgba` have enough memory allocated.
- for (int i = 0; i < exr_image.width * exr_image.height; i++) {
+ for (int tile_index = 0; tile_index < num_tiles; tile_index++) {
- Color color(
- reinterpret_cast<float **>(exr_image.images)[idxR][i],
- reinterpret_cast<float **>(exr_image.images)[idxG][i],
- reinterpret_cast<float **>(exr_image.images)[idxB][i]);
+ const EXRTile &tile = exr_tiles[tile_index];
- if (p_force_linear)
- color = color.to_linear();
+ int tw = tile.width;
+ int th = tile.height;
- *iw++ = Math::make_half_float(color.r);
- *iw++ = Math::make_half_float(color.g);
- *iw++ = Math::make_half_float(color.b);
+ const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]);
+ const float *g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);
+ const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);
+ const float *a_channel_start = NULL;
if (idxA > 0) {
- *iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxA][i]);
+ a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]);
+ }
+
+ uint16_t *first_row_w = iw + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
+
+ for (int y = 0; y < th; y++) {
+ const float *r_channel = r_channel_start + y * tile_width;
+ const float *g_channel = g_channel_start + y * tile_width;
+ const float *b_channel = b_channel_start + y * tile_width;
+ const float *a_channel = NULL;
+
+ if (a_channel_start) {
+ a_channel = a_channel_start + y * tile_width;
+ }
+
+ uint16_t *row_w = first_row_w + (y * exr_image.width * output_channels);
+
+ for (int x = 0; x < tw; x++) {
+
+ Color color(*r_channel++, *g_channel++, *b_channel++);
+
+ if (p_force_linear)
+ color = color.to_linear();
+
+ *row_w++ = Math::make_half_float(color.r);
+ *row_w++ = Math::make_half_float(color.g);
+ *row_w++ = Math::make_half_float(color.b);
+
+ if (idxA > 0) {
+ *row_w++ = Math::make_half_float(*a_channel++);
+ }
+ }
}
}
}
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 9331092171..bbdec7195f 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -771,7 +771,7 @@ void VisualScript::custom_signal_set_argument_type(const StringName &p_func, int
ERR_FAIL_COND(instances.size());
ERR_FAIL_COND(!custom_signals.has(p_func));
ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func][p_argidx].type = p_type;
+ custom_signals[p_func].write[p_argidx].type = p_type;
}
Variant::Type VisualScript::custom_signal_get_argument_type(const StringName &p_func, int p_argidx) const {
@@ -783,7 +783,7 @@ void VisualScript::custom_signal_set_argument_name(const StringName &p_func, int
ERR_FAIL_COND(instances.size());
ERR_FAIL_COND(!custom_signals.has(p_func));
ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func][p_argidx].name = p_name;
+ custom_signals[p_func].write[p_argidx].name = p_name;
}
String VisualScript::custom_signal_get_argument_name(const StringName &p_func, int p_argidx) const {
@@ -811,7 +811,7 @@ void VisualScript::custom_signal_swap_argument(const StringName &p_func, int p_a
ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
ERR_FAIL_INDEX(p_with_argidx, custom_signals[p_func].size());
- SWAP(custom_signals[p_func][p_argidx], custom_signals[p_func][p_with_argidx]);
+ SWAP(custom_signals[p_func].write[p_argidx], custom_signals[p_func].write[p_with_argidx]);
}
void VisualScript::remove_custom_signal(const StringName &p_name) {
@@ -1333,6 +1333,19 @@ VisualScript::VisualScript() {
base_type = "Object";
}
+Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) {
+ List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>);
+ get_sequence_connection_list(edited_func, sc);
+ Set<int> connected;
+ for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) {
+ if (E->get().from_node == from_node) {
+ connected.insert(E->get().from_output);
+ }
+ }
+ memdelete(sc);
+ return connected;
+}
+
VisualScript::~VisualScript() {
while (!functions.empty()) {
@@ -2402,7 +2415,7 @@ void VisualScriptLanguage::make_template(const String &p_class_name, const Strin
script->set_instance_base_type(p_base_class_name);
}
-bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const {
+bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
return false;
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index aaa6dfea11..13a8b909b0 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -319,6 +319,7 @@ public:
void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx);
void remove_custom_signal(const StringName &p_name);
void rename_custom_signal(const StringName &p_name, const StringName &p_new_name);
+ Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node);
void get_custom_signal_list(List<StringName> *r_custom_signals) const;
@@ -563,7 +564,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 73b6d702c1..8e98b08b22 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -1144,15 +1144,12 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
case VisualScriptBuiltinFunc::TEXT_PRINTERR: {
String str = *p_inputs[0];
-
- //str+="\n";
print_error(str);
} break;
case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
- String str = *p_inputs[0];
- //str+="\n";
+ String str = *p_inputs[0];
OS::get_singleton()->print("%s", str.utf8().get_data());
} break;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 0618064ea6..4471fbd0c4 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -33,8 +33,10 @@
#include "core/script_language.h"
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
+#include "object.h"
#include "os/input.h"
#include "os/keyboard.h"
+#include "variant.h"
#include "visual_script_expression.h"
#include "visual_script_flow_control.h"
#include "visual_script_func_nodes.h"
@@ -880,7 +882,6 @@ void VisualScriptEditor::_member_selected() {
ERR_FAIL_COND(!ti);
selected = ti->get_metadata(0);
- //print_line("selected: "+String(selected));
if (ti->get_parent() == members->get_root()->get_children()) {
@@ -1293,7 +1294,7 @@ void VisualScriptEditor::_on_nodes_duplicate() {
Ref<VisualScriptNode> node = script->get_node(edited_func, F->get());
- Ref<VisualScriptNode> dupe = node->duplicate();
+ Ref<VisualScriptNode> dupe = node->duplicate(true);
int new_id = idc++;
to_select.insert(new_id);
@@ -1327,6 +1328,10 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
}
}
+void VisualScriptEditor::_generic_search() {
+ new_connect_node_select->select_from_visual_script(String(""), false);
+}
+
void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
@@ -1780,7 +1785,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
call->set_base_type(node->get_class());
n = call;
- method_select->select_method_from_instance(node);
+ method_select->select_from_instance(node);
selecting_method_id = base_id;
}
@@ -1917,7 +1922,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
}
-void VisualScriptEditor::_selected_method(const String &p_method) {
+void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) {
Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id);
if (!vsfc.is_valid())
@@ -1962,22 +1967,16 @@ void VisualScriptEditor::_button_resource_previewed(const String &p_path, const
void VisualScriptEditor::apply_code() {
}
-Ref<Script> VisualScriptEditor::get_edited_script() const {
-
+RES VisualScriptEditor::get_edited_resource() const {
return script;
}
-Vector<String> VisualScriptEditor::get_functions() {
-
- return Vector<String>();
-}
+void VisualScriptEditor::set_edited_resource(const RES &p_res) {
-void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) {
-
- script = p_script;
- signal_editor->script = p_script;
+ script = p_res;
+ signal_editor->script = script;
signal_editor->undo_redo = undo_redo;
- variable_editor->script = p_script;
+ variable_editor->script = script;
variable_editor->undo_redo = undo_redo;
script->connect("node_ports_changed", this, "_node_ports_changed");
@@ -1986,6 +1985,11 @@ void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) {
_update_available_nodes();
}
+Vector<String> VisualScriptEditor::get_functions() {
+
+ return Vector<String>();
+}
+
void VisualScriptEditor::reload_text() {
}
@@ -2436,33 +2440,19 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro
if (!vsn.is_valid())
return;
- if (p_from_slot < vsn->get_output_sequence_port_count()) {
+ port_action_pos = p_release_pos;
- port_action_popup->clear();
- port_action_popup->add_item(TTR("Condition"), CREATE_COND);
- port_action_popup->add_item(TTR("Sequence"), CREATE_SEQUENCE);
- port_action_popup->add_item(TTR("Switch"), CREATE_SWITCH);
- port_action_popup->add_item(TTR("Iterator"), CREATE_ITERATOR);
- port_action_popup->add_item(TTR("While"), CREATE_WHILE);
- port_action_popup->add_item(TTR("Return"), CREATE_RETURN);
+ if (p_from_slot < vsn->get_output_sequence_port_count()) {
port_action_node = p_from.to_int();
port_action_output = p_from_slot;
-
+ _port_action_menu(CREATE_ACTION);
} else {
- port_action_popup->clear();
- port_action_popup->add_item(TTR("Call"), CREATE_CALL);
- port_action_popup->add_item(TTR("Get"), CREATE_GET);
- port_action_popup->add_item(TTR("Set"), CREATE_SET);
port_action_output = p_from_slot - vsn->get_output_sequence_port_count();
port_action_node = p_from.to_int();
+ _port_action_menu(CREATE_CALL_SET_GET);
}
-
- port_action_pos = p_release_pos;
- port_action_popup->set_size(Size2(1, 1));
- port_action_popup->set_position(graph->get_global_position() + p_release_pos);
- port_action_popup->popup();
}
VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) {
@@ -2530,168 +2520,205 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
bool seq_connect = false;
- Ref<VisualScriptNode> vnode;
Set<int> vn;
switch (p_option) {
- case CREATE_CALL: {
-
+ case CREATE_CALL_SET_GET: {
Ref<VisualScriptFunctionCall> n;
n.instance();
- vnode = n;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
-
- if (tg.gdclass != StringName()) {
- n->set_base_type(tg.gdclass);
- } else {
- n->set_base_type("Object");
- }
+ if (tg.gdclass != StringName()) {
+ n->set_base_type(tg.gdclass);
+ } else {
+ n->set_base_type("Object");
+ }
+ String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ if (tg.type == Variant::OBJECT) {
if (tg.script.is_valid()) {
- n->set_base_script(tg.script->get_path());
- new_connect_node_select->select_method_from_script(tg.script);
+ new_connect_node_select->select_from_script(tg.script, "");
+ } else if (type_string != String()) {
+ new_connect_node_select->select_from_base_type(type_string);
} else {
- new_connect_node_select->select_method_from_base_type(n->get_base_type());
+ new_connect_node_select->select_from_base_type(n->get_base_type());
}
-
+ } else if (tg.type == Variant::NIL) {
+ new_connect_node_select->select_from_base_type("");
} else {
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
- n->set_basic_type(tg.type);
- new_connect_node_select->select_method_from_basic_type(tg.type);
+ new_connect_node_select->select_from_basic_type(tg.type);
}
-
} break;
- case CREATE_SET: {
-
- Ref<VisualScriptPropertySet> n;
- n.instance();
- vnode = n;
-
+ case CREATE_ACTION: {
+ seq_connect = true;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
-
+ PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output);
if (tg.type == Variant::OBJECT) {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
-
- if (tg.gdclass != StringName()) {
- n->set_base_type(tg.gdclass);
- } else {
- n->set_base_type("Object");
- }
-
- if (tg.script.is_valid()) {
- n->set_base_script(tg.script->get_path());
- new_connect_node_select->select_property_from_script(tg.script);
+ if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) {
+ new_connect_node_select->select_from_action(property_info.hint_string);
} else {
- new_connect_node_select->select_property_from_base_type(n->get_base_type());
+ new_connect_node_select->select_from_action("");
}
-
+ } else if (tg.type == Variant::NIL) {
+ new_connect_node_select->select_from_action("");
} else {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
- n->set_basic_type(tg.type);
- new_connect_node_select->select_property_from_basic_type(tg.type);
+ new_connect_node_select->select_from_action(Variant::get_type_name(tg.type));
}
} break;
- case CREATE_GET: {
+ }
+}
- Ref<VisualScriptPropertyGet> n;
- n.instance();
- vnode = n;
+void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) {
+ Set<int> vn;
+ Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node);
+ int new_id = script->get_available_id();
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id);
+ undo_redo->add_do_method(this, "_update_graph", new_id);
+ undo_redo->add_undo_method(this, "_update_graph", new_id);
+ undo_redo->commit_action();
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ port_action_new_node = new_id;
+}
- if (tg.type == Variant::OBJECT) {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) {
+ undo_redo->create_action(TTR("Connect Node Data"));
+ VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr());
+ if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) {
+ vnode_return->set_enable_return_value(true);
+ }
+ if (vnode_old->get_output_value_port_count() <= 0) {
+ undo_redo->commit_action();
+ return;
+ }
+ if (vnode->get_input_value_port_count() <= 0) {
+ undo_redo->commit_action();
+ return;
+ }
+ int port = port_action_output;
+ int value_count = vnode_old->get_output_value_port_count();
+ if (port >= value_count) {
+ port = 0;
+ }
+ int count = vnode_old->get_output_value_port_count() + vnode_old->get_output_sequence_port_count();
+ undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0);
+ undo_redo->commit_action();
+}
- if (tg.gdclass != StringName()) {
- n->set_base_type(tg.gdclass);
- } else {
- n->set_base_type("Object");
- }
+void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
+ Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap, snap));
+ }
+ ofs /= EDSCALE;
- if (tg.script.is_valid()) {
- n->set_base_script(tg.script->get_path());
- new_connect_node_select->select_property_from_script(tg.script);
- } else {
- new_connect_node_select->select_property_from_base_type(n->get_base_type());
- }
+ Set<int> vn;
+
+ if (p_category == "visualscript") {
+ Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text);
+ Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node);
+ int new_id = script->get_available_id();
+
+ if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) {
+ Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type;
+ Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type);
+ }
+
+ if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) {
+ Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type;
+ String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ if (type == Variant::OBJECT) {
+ Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name);
+ } else if (type == Variant::NIL) {
+ Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type("");
} else {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
- n->set_basic_type(tg.type);
- new_connect_node_select->select_property_from_basic_type(tg.type);
+ Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type));
}
+ }
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs);
+ if (vnode_old.is_valid() && p_connecting == true) {
+ connect_seq(vnode_old, vnode_new, new_id);
+ connect_data(vnode_old, vnode_new, new_id);
+ }
- } break;
- case CREATE_COND: {
+ undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+ return;
+ }
+
+ Ref<VisualScriptNode> vnode;
+
+ seq_connect = false;
+ if (p_category == String("method")) {
+
+ Ref<VisualScriptFunctionCall> n;
+ n.instance();
+ vnode = n;
+ } else if (p_category == String("set")) {
+
+ Ref<VisualScriptPropertySet> n;
+ n.instance();
+ n->set_property(p_text);
+ vnode = n;
+ } else if (p_category == String("get")) {
+
+ Ref<VisualScriptPropertyGet> n;
+ n.instance();
+ n->set_property(p_text);
+ vnode = n;
+ }
+
+ if (p_category == String("action")) {
+ if (p_text == "VisualScriptCondition") {
Ref<VisualScriptCondition> n;
n.instance();
vnode = n;
seq_connect = true;
+ }
+ if (p_text == "VisualScriptSwitch") {
- } break;
- case CREATE_SEQUENCE: {
-
- Ref<VisualScriptSequence> n;
+ Ref<VisualScriptSwitch> n;
n.instance();
vnode = n;
seq_connect = true;
+ } else if (p_text == "VisualScriptSequence") {
- } break;
- case CREATE_SWITCH: {
-
- Ref<VisualScriptSwitch> n;
+ Ref<VisualScriptSequence> n;
n.instance();
vnode = n;
seq_connect = true;
-
- } break;
- case CREATE_ITERATOR: {
+ } else if (p_text == "VisualScriptIterator") {
Ref<VisualScriptIterator> n;
n.instance();
vnode = n;
seq_connect = true;
-
- } break;
- case CREATE_WHILE: {
+ } else if (p_text == "VisualScriptWhile") {
Ref<VisualScriptWhile> n;
n.instance();
vnode = n;
seq_connect = true;
-
- } break;
- case CREATE_RETURN: {
+ } else if (p_text == "VisualScriptReturn") {
Ref<VisualScriptReturn> n;
n.instance();
vnode = n;
seq_connect = true;
-
- } break;
- }
-
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs);
- if (seq_connect) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id);
+ }
}
- undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id);
- undo_redo->add_do_method(this, "_update_graph", new_id);
- undo_redo->add_undo_method(this, "_update_graph", new_id);
- undo_redo->commit_action();
-
- port_action_new_node = new_id;
-}
-void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p_text) {
+ new_node(vnode, ofs);
Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node);
@@ -2699,28 +2726,152 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p
Ref<VisualScriptFunctionCall> vsfc = vsn;
vsfc->set_function(p_text);
- script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0);
+
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ if (tg.type == Variant::OBJECT) {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
+ vsfc->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsfc->set_base_type(tg.gdclass);
+
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsfc->set_base_type(base_type);
+ }
+ if (p_text == "call" || p_text == "call_deferred") {
+ vsfc->set_function(String(""));
+ }
+ }
+ if (tg.script.is_valid()) {
+ vsfc->set_base_script(tg.script->get_path());
+ }
+ } else if (tg.type == Variant::NIL) {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
+ vsfc->set_base_type(String(""));
+ } else {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
+ vsfc->set_basic_type(tg.type);
+ }
}
if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
Ref<VisualScriptPropertySet> vsp = vsn;
- vsp->set_property(p_text);
- script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0);
+
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ if (tg.type == Variant::OBJECT) {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsp->set_base_type(tg.gdclass);
+
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsp->set_base_type(base_type);
+ }
+ }
+ if (tg.script.is_valid()) {
+ vsp->set_base_script(tg.script->get_path());
+ }
+ } else if (tg.type == Variant::NIL) {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ } else {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
+ vsp->set_basic_type(tg.type);
+ }
}
if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
-
Ref<VisualScriptPropertyGet> vsp = vsn;
- vsp->set_property(p_text);
- script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0);
- }
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ if (tg.type == Variant::OBJECT) {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsp->set_base_type(tg.gdclass);
+
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsp->set_base_type(base_type);
+ }
+ }
+ if (tg.script.is_valid()) {
+ vsp->set_base_script(tg.script->get_path());
+ }
+ } else if (tg.type == Variant::NIL) {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ } else {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
+ vsp->set_basic_type(tg.type);
+ }
+ }
+ Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node);
+ if (vnode_old.is_valid() && p_connecting == true) {
+ connect_seq(vnode_old, vnode, port_action_new_node);
+ connect_data(vnode_old, vnode, port_action_new_node);
+ }
_update_graph(port_action_new_node);
_update_graph_connections();
}
-void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) {
+void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) {
+ int seq_count = vnode_old->get_output_sequence_port_count();
+ VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr());
+ if (vnode_operator != NULL && vnode_operator->has_input_sequence_port() == false) {
+ return;
+ }
+ VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr());
+ if (vnode_constructor != NULL) {
+ return;
+ }
+ if (vnode_old->get_output_sequence_port_count() <= 0) {
+ return;
+ }
+ if (vnode_new->has_input_sequence_port() == false) {
+ return;
+ }
+ VisualScriptFunction *vnode_function = Object::cast_to<VisualScriptFunction>(vnode_old.ptr());
+ undo_redo->create_action(TTR("Connect Node Sequence"));
+ int pass_port = -vnode_old->get_output_sequence_port_count() + 1;
+ int return_port = port_action_output - 1;
+ if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") &&
+ !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id);
+ } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") &&
+ !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id);
+ } else {
+ for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) {
+ int count = vnode_old->get_output_sequence_port_count();
+ if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id);
+ break;
+ } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id);
+ break;
+ }
+ }
+ }
+
+ undo_redo->commit_action();
+}
+
+void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) {
String name = p_text;
if (script->has_function(name)) {
@@ -2753,7 +2904,7 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) {
undo_redo->add_do_method(script.ptr(), "add_function", name);
for (int i = 0; i < minfo.arguments.size(); i++) {
- func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name);
+ func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string);
}
undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node);
@@ -2777,12 +2928,29 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) {
_update_graph();
}
-void VisualScriptEditor::_cancel_connect_node_method_or_setget() {
-
- script->remove_node(edited_func, port_action_new_node);
+void VisualScriptEditor::_cancel_connect_node() {
+ // Causes crashes
+ //script->remove_node(edited_func, port_action_new_node);
_update_graph();
}
+void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) {
+ Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ if (graph->is_using_snap()) {
+ int snap = graph->get_snap();
+ ofs = ofs.snapped(Vector2(snap, snap));
+ }
+ ofs /= EDSCALE;
+ Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text);
+ int new_id = script->get_available_id();
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id);
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+ undo_redo->commit_action();
+}
+
void VisualScriptEditor::_default_value_changed() {
Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id);
@@ -2863,7 +3031,8 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) {
void VisualScriptEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_READY) {
- node_filter->add_icon_override("right_icon", Control::get_icon("Search", "EditorIcons"));
+ 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");
@@ -2980,9 +3149,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_FIND_NODE_TYPE: {
- //popup disappearing grabs focus to owner, so use call deferred
- node_filter->call_deferred("grab_focus");
- node_filter->call_deferred("select_all");
+ _generic_search();
} break;
case EDIT_COPY_NODES:
case EDIT_CUT_NODES: {
@@ -3006,7 +3173,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
return;
}
if (node.is_valid()) {
- clipboard->nodes[id] = node->duplicate();
+ clipboard->nodes[id] = node->duplicate(true);
clipboard->nodes_positions[id] = script->get_node_position(edited_func, id);
}
}
@@ -3286,10 +3453,11 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized);
ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed);
ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu);
- ClassDB::bind_method("_selected_connect_node_method_or_setget", &VisualScriptEditor::_selected_connect_node_method_or_setget);
+ ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node);
ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method);
- ClassDB::bind_method("_cancel_connect_node_method_or_setget", &VisualScriptEditor::_cancel_connect_node_method_or_setget);
+ ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node);
+ ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node);
ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed);
ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
@@ -3318,6 +3486,8 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option);
ClassDB::bind_method("_update_available_nodes", &VisualScriptEditor::_update_available_nodes);
+
+ ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
}
VisualScriptEditor::VisualScriptEditor() {
@@ -3389,6 +3559,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
+ graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
graph->connect("node_selected", this, "_node_selected");
graph->connect("_begin_node_move", this, "_begin_node_move");
@@ -3479,25 +3650,21 @@ VisualScriptEditor::VisualScriptEditor() {
add_child(default_value_edit);
default_value_edit->connect("variant_changed", this, "_default_value_changed");
- method_select = memnew(PropertySelector);
+ method_select = memnew(VisualScriptPropertySelector);
add_child(method_select);
method_select->connect("selected", this, "_selected_method");
error_line = -1;
- new_connect_node_select = memnew(PropertySelector);
+ new_connect_node_select = memnew(VisualScriptPropertySelector);
add_child(new_connect_node_select);
- new_connect_node_select->connect("selected", this, "_selected_connect_node_method_or_setget");
- new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node_method_or_setget");
+ new_connect_node_select->connect("selected", this, "_selected_connect_node");
+ new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node");
- new_virtual_method_select = memnew(PropertySelector);
+ 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");
- port_action_popup = memnew(PopupMenu);
- add_child(port_action_popup);
- port_action_popup->connect("id_pressed", this, "_port_action_menu");
-
member_popup = memnew(PopupMenu);
add_child(member_popup);
members->connect("item_rmb_selected", this, "_member_rmb_selected");
@@ -3514,9 +3681,9 @@ VisualScriptEditor::~VisualScriptEditor() {
memdelete(variable_editor);
}
-static ScriptEditorBase *create_editor(const Ref<Script> &p_script) {
+static ScriptEditorBase *create_editor(const RES &p_resource) {
- if (Object::cast_to<VisualScript>(*p_script)) {
+ if (Object::cast_to<VisualScript>(*p_resource)) {
return memnew(VisualScriptEditor);
}
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 72b5e09222..8bfd147519 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -34,9 +34,9 @@
#include "editor/create_dialog.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/property_editor.h"
-#include "editor/property_selector.h"
#include "scene/gui/graph_edit.h"
#include "visual_script.h"
+#include "visual_script_property_selector.h"
class VisualScriptEditorSignalEdit;
class VisualScriptEditorVariableEdit;
@@ -62,15 +62,8 @@ class VisualScriptEditor : public ScriptEditorBase {
enum PortAction {
- CREATE_CALL,
- CREATE_SET,
- CREATE_GET,
- CREATE_COND,
- CREATE_SEQUENCE,
- CREATE_SWITCH,
- CREATE_ITERATOR,
- CREATE_WHILE,
- CREATE_RETURN,
+ CREATE_CALL_SET_GET,
+ CREATE_ACTION,
};
enum MemberAction {
@@ -102,9 +95,9 @@ class VisualScriptEditor : public ScriptEditorBase {
AcceptDialog *edit_signal_dialog;
PropertyEditor *edit_signal_edit;
- PropertySelector *method_select;
- PropertySelector *new_connect_node_select;
- PropertySelector *new_virtual_method_select;
+ VisualScriptPropertySelector *method_select;
+ VisualScriptPropertySelector *new_connect_node_select;
+ VisualScriptPropertySelector *new_virtual_method_select;
VisualScriptEditorVariableEdit *variable_editor;
@@ -137,7 +130,7 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String> > args;
};
- HashMap<StringName, Ref<StyleBox>, StringNameHasher> node_styles;
+ HashMap<StringName, Ref<StyleBox> > node_styles;
StringName edited_func;
void _update_graph_connections();
@@ -162,21 +155,29 @@ class VisualScriptEditor : public ScriptEditorBase {
static Clipboard *clipboard;
- PopupMenu *port_action_popup;
PopupMenu *member_popup;
-
MemberType member_type;
String member_name;
+ bool seq_connect = false;
+
PortAction port_action;
int port_action_node;
int port_action_output;
Vector2 port_action_pos;
int port_action_new_node;
void _port_action_menu(int p_option);
- void _selected_connect_node_method_or_setget(const String &p_text);
- void _cancel_connect_node_method_or_setget();
- void _selected_new_virtual_method(const String &p_text);
+
+ void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs);
+
+ void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id);
+
+ void _selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting = true);
+ void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id);
+
+ 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);
int error_line;
@@ -211,6 +212,9 @@ class VisualScriptEditor : public ScriptEditorBase {
String revert_on_drag;
void _input(const Ref<InputEvent> &p_event);
+
+ void _generic_search();
+
void _members_gui_input(const Ref<InputEvent> &p_event);
void _on_nodes_delete();
void _on_nodes_duplicate();
@@ -231,7 +235,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _comment_node_resized(const Vector2 &p_new_size, int p_node);
int selecting_method_id;
- void _selected_method(const String &p_method);
+ void _selected_method(const String &p_method, const String &p_type);
void _draw_color_over_button(Object *obj, Color p_color);
void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud);
@@ -250,9 +254,9 @@ public:
virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
virtual void apply_code();
- virtual Ref<Script> get_edited_script() const;
+ virtual RES get_edited_resource() const;
+ virtual void set_edited_resource(const RES &p_res);
virtual Vector<String> get_functions();
- virtual void set_edited_script(const Ref<Script> &p_script);
virtual void reload_text();
virtual String get_name();
virtual Ref<Texture> get_icon();
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index d5f9d21348..868d22b541 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -56,11 +56,11 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val
int from = inputs.size();
inputs.resize(int(p_value));
for (int i = from; i < inputs.size(); i++) {
- inputs[i].name = String::chr('a' + i);
+ inputs.write[i].name = String::chr('a' + i);
if (from == 0) {
- inputs[i].type = output_type;
+ inputs.write[i].type = output_type;
} else {
- inputs[i].type = inputs[from - 1].type;
+ inputs.write[i].type = inputs[from - 1].type;
}
}
expression_dirty = true;
@@ -78,10 +78,10 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val
if (what == "type") {
- inputs[idx].type = Variant::Type(int(p_value));
+ inputs.write[idx].type = Variant::Type(int(p_value));
} else if (what == "name") {
- inputs[idx].name = p_value;
+ inputs.write[idx].name = p_value;
} else {
return false;
}
@@ -1153,8 +1153,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
op->op = expression[i].op;
op->nodes[0] = expression[i + 1].node;
op->nodes[1] = NULL;
- expression[i].is_op = false;
- expression[i].node = op;
+ expression.write[i].is_op = false;
+ expression.write[i].node = op;
expression.remove(i + 1);
}
@@ -1188,7 +1188,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
op->nodes[1] = expression[next_op + 1].node; //next expression goes as right
//replace all 3 nodes by this operator and make it an expression
- expression[next_op - 1].node = op;
+ expression.write[next_op - 1].node = op;
expression.remove(next_op);
expression.remove(next_op);
}
@@ -1370,8 +1370,8 @@ public:
bool ret = _execute(p_inputs, constructor->arguments[i], value, r_error_str, ce);
if (ret)
return true;
- arr[i] = value;
- argp[i] = &arr[i];
+ arr.write[i] = value;
+ argp.write[i] = &arr[i];
}
r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce);
@@ -1397,8 +1397,8 @@ public:
bool ret = _execute(p_inputs, bifunc->arguments[i], value, r_error_str, ce);
if (ret)
return true;
- arr[i] = value;
- argp[i] = &arr[i];
+ arr.write[i] = value;
+ argp.write[i] = &arr[i];
}
VisualScriptBuiltinFunc::exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str);
@@ -1429,8 +1429,8 @@ public:
bool ret = _execute(p_inputs, call->arguments[i], value, r_error_str, ce);
if (ret)
return true;
- arr[i] = value;
- argp[i] = &arr[i];
+ arr.write[i] = value;
+ argp.write[i] = &arr[i];
}
r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce);
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index ea23ab1b2a..7535f37ffc 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -684,7 +684,7 @@ bool VisualScriptSwitch::_set(const StringName &p_name, const Variant &p_value)
int idx = String(p_name).get_slice("/", 1).to_int();
ERR_FAIL_INDEX_V(idx, case_values.size(), false);
- case_values[idx].type = Variant::Type(int(p_value));
+ case_values.write[idx].type = Variant::Type(int(p_value));
_change_notify();
ports_changed_notify();
@@ -767,7 +767,7 @@ PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "");
+ return PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_TYPE_STRING, get_base_type());
}
String VisualScriptTypeCast::get_caption() const {
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index bdf5705ecd..f926d4e2eb 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -43,7 +43,7 @@
int VisualScriptFunctionCall::get_output_sequence_port_count() const {
- if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)))
+ if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)))
return 0;
else
return 1;
@@ -51,7 +51,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const {
bool VisualScriptFunctionCall::has_input_sequence_port() const {
- if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)))
+ if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)))
return false;
else
return true;
@@ -231,7 +231,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
if (call_mode == CALL_MODE_INSTANCE) {
if (p_idx == 0) {
- return PropertyInfo(Variant::OBJECT, "pass");
+ return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
} else {
p_idx--;
}
@@ -394,7 +394,6 @@ void VisualScriptFunctionCall::_update_method_cache() {
}
}
- //print_line("BASE: "+String(type)+" FUNC: "+String(function));
MethodBind *mb = ClassDB::get_method(type, function);
if (mb) {
use_default_args = mb->get_default_argument_count();
@@ -1055,7 +1054,7 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons
if (call_mode == CALL_MODE_BASIC_TYPE) {
return PropertyInfo(basic_type, "out");
} else if (call_mode == CALL_MODE_INSTANCE) {
- return PropertyInfo(Variant::OBJECT, "pass");
+ return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
} else {
return PropertyInfo();
}
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 8b7b809ec0..d499512d93 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -54,8 +54,8 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
arguments.resize(new_argc);
for (int i = argc; i < new_argc; i++) {
- arguments[i].name = "arg" + itos(i + 1);
- arguments[i].type = Variant::NIL;
+ arguments.write[i].name = "arg" + itos(i + 1);
+ arguments.write[i].type = Variant::NIL;
}
ports_changed_notify();
_change_notify();
@@ -68,7 +68,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
if (what == "type") {
Variant::Type new_type = Variant::Type(int(p_value));
- arguments[idx].type = new_type;
+ arguments.write[idx].type = new_type;
ports_changed_notify();
return true;
@@ -76,7 +76,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
if (what == "name") {
- arguments[idx].name = p_value;
+ arguments.write[idx].name = p_value;
ports_changed_notify();
return true;
}
@@ -205,6 +205,8 @@ PropertyInfo VisualScriptFunction::get_output_value_port_info(int p_idx) const {
PropertyInfo out;
out.type = arguments[p_idx].type;
out.name = arguments[p_idx].name;
+ out.hint = arguments[p_idx].hint;
+ out.hint_string = arguments[p_idx].hint_string;
return out;
}
@@ -218,11 +220,13 @@ String VisualScriptFunction::get_text() const {
return get_name(); //use name as function name I guess
}
-void VisualScriptFunction::add_argument(Variant::Type p_type, const String &p_name, int p_index) {
+void VisualScriptFunction::add_argument(Variant::Type p_type, const String &p_name, int p_index, const PropertyHint p_hint, const String &p_hint_string) {
Argument arg;
arg.name = p_name;
arg.type = p_type;
+ arg.hint = p_hint;
+ arg.hint_string = p_hint_string;
if (p_index >= 0)
arguments.insert(p_index, arg);
else
@@ -234,7 +238,7 @@ void VisualScriptFunction::set_argument_type(int p_argidx, Variant::Type p_type)
ERR_FAIL_INDEX(p_argidx, arguments.size());
- arguments[p_argidx].type = p_type;
+ arguments.write[p_argidx].type = p_type;
ports_changed_notify();
}
Variant::Type VisualScriptFunction::get_argument_type(int p_argidx) const {
@@ -246,7 +250,7 @@ void VisualScriptFunction::set_argument_name(int p_argidx, const String &p_name)
ERR_FAIL_INDEX(p_argidx, arguments.size());
- arguments[p_argidx].name = p_name;
+ arguments.write[p_argidx].name = p_name;
ports_changed_notify();
}
String VisualScriptFunction::get_argument_name(int p_argidx) const {
@@ -1659,7 +1663,7 @@ Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const {
class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance {
public:
- int value;
+ Variant value;
bool valid;
//virtual int get_working_memory_size() const { return 0; }
@@ -1678,7 +1682,7 @@ public:
VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instance(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceBasicTypeConstant *instance = memnew(VisualScriptNodeInstanceBasicTypeConstant);
- instance->value = Variant::get_numeric_constant_value(type, name, &instance->valid);
+ instance->value = Variant::get_constant_value(type, name, &instance->valid);
return instance;
}
@@ -1687,7 +1691,7 @@ void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) c
if (property.name == "constant") {
List<StringName> constants;
- Variant::get_numeric_constants_for_type(type, &constants);
+ Variant::get_constants_for_type(type, &constants);
if (constants.size() == 0) {
property.usage = 0;
@@ -2198,7 +2202,7 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const {
PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "Scene Tree");
+ return PropertyInfo(Variant::OBJECT, "Scene Tree", PROPERTY_HINT_TYPE_STRING, "SceneTree");
}
String VisualScriptSceneTree::get_caption() const {
@@ -3560,8 +3564,8 @@ void VisualScriptDeconstruct::_set_elem_cache(const Array &p_elements) {
ERR_FAIL_COND(p_elements.size() % 2 == 1);
elements.resize(p_elements.size() / 2);
for (int i = 0; i < elements.size(); i++) {
- elements[i].name = p_elements[i * 2 + 0];
- elements[i].type = Variant::Type(int(p_elements[i * 2 + 1]));
+ elements.write[i].name = p_elements[i * 2 + 0];
+ elements.write[i].type = Variant::Type(int(p_elements[i * 2 + 1]));
}
}
@@ -3606,7 +3610,7 @@ VisualScriptNodeInstance *VisualScriptDeconstruct::instance(VisualScriptInstance
instance->instance = p_instance;
instance->outputs.resize(elements.size());
for (int i = 0; i < elements.size(); i++) {
- instance->outputs[i] = elements[i].name;
+ instance->outputs.write[i] = elements[i].name;
}
return instance;
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index 9bfbd46e47..f7ac995816 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -40,6 +40,8 @@ class VisualScriptFunction : public VisualScriptNode {
struct Argument {
String name;
Variant::Type type;
+ PropertyHint hint;
+ String hint_string;
};
Vector<Argument> arguments;
@@ -70,7 +72,7 @@ public:
virtual String get_text() const;
virtual String get_category() const { return "flow_control"; }
- void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1);
+ void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(""));
void set_argument_type(int p_argidx, Variant::Type p_type);
Variant::Type get_argument_type(int p_argidx) const;
void set_argument_name(int p_argidx, const String &p_name);
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
new file mode 100644
index 0000000000..8d465c8872
--- /dev/null
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -0,0 +1,733 @@
+/*************************************************************************/
+/* visual_script_property_selector.cpp */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+#include "visual_script_property_selector.h"
+
+#include "editor_scale.h"
+#include "modules/visual_script/visual_script.h"
+#include "modules/visual_script/visual_script_builtin_funcs.h"
+#include "modules/visual_script/visual_script_flow_control.h"
+#include "modules/visual_script/visual_script_func_nodes.h"
+#include "modules/visual_script/visual_script_nodes.h"
+#include "os/keyboard.h"
+#include "scene/main/node.h"
+#include "scene/main/viewport.h"
+
+void VisualScriptPropertySelector::_text_changed(const String &p_newtext) {
+ _update_search();
+}
+
+void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
+
+ Ref<InputEventKey> k = p_ie;
+
+ if (k.is_valid()) {
+
+ switch (k->get_scancode()) {
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGEUP:
+ case KEY_PAGEDOWN: {
+
+ search_options->call("_gui_input", k);
+ search_box->accept_event();
+
+ TreeItem *root = search_options->get_root();
+ if (!root->get_children())
+ break;
+
+ TreeItem *current = search_options->get_selected();
+
+ TreeItem *item = search_options->get_next_selected(root);
+ while (item) {
+ item->deselect(0);
+ item = search_options->get_next_selected(item);
+ }
+
+ current->select(0);
+
+ } break;
+ }
+ }
+}
+
+void VisualScriptPropertySelector::_update_search() {
+ set_title(TTR("Search VisualScript"));
+
+ search_options->clear();
+ help_bit->set_text("");
+
+ TreeItem *root = search_options->create_item();
+ bool found = false;
+
+ if (properties) {
+
+ List<PropertyInfo> props;
+
+ if (instance) {
+ instance->get_property_list(&props, true);
+ } else if (type != Variant::NIL) {
+ Variant v;
+ Variant::CallError ce;
+ v = Variant::construct(type, NULL, 0, ce);
+
+ v.get_property_list(&props);
+ } else {
+
+ Object *obj = ObjectDB::get_instance(script);
+ if (Object::cast_to<Script>(obj)) {
+
+ props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));
+ Object::cast_to<Script>(obj)->get_script_property_list(&props);
+ }
+
+ StringName base = base_type;
+ while (base) {
+ props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));
+ ClassDB::get_property_list(base, &props, true);
+ base = ClassDB::get_parent_class_nocheck(base);
+ }
+ }
+
+ TreeItem *category = NULL;
+
+ Ref<Texture> type_icons[Variant::VARIANT_MAX] = {
+ Control::get_icon("Variant", "EditorIcons"),
+ Control::get_icon("bool", "EditorIcons"),
+ Control::get_icon("int", "EditorIcons"),
+ Control::get_icon("float", "EditorIcons"),
+ Control::get_icon("String", "EditorIcons"),
+ Control::get_icon("Vector2", "EditorIcons"),
+ Control::get_icon("Rect2", "EditorIcons"),
+ Control::get_icon("Vector3", "EditorIcons"),
+ Control::get_icon("Transform2D", "EditorIcons"),
+ Control::get_icon("Plane", "EditorIcons"),
+ Control::get_icon("Quat", "EditorIcons"),
+ Control::get_icon("AABB", "EditorIcons"),
+ Control::get_icon("Basis", "EditorIcons"),
+ Control::get_icon("Transform", "EditorIcons"),
+ Control::get_icon("Color", "EditorIcons"),
+ Control::get_icon("Path", "EditorIcons"),
+ Control::get_icon("RID", "EditorIcons"),
+ Control::get_icon("Object", "EditorIcons"),
+ Control::get_icon("Dictionary", "EditorIcons"),
+ Control::get_icon("Array", "EditorIcons"),
+ Control::get_icon("PoolByteArray", "EditorIcons"),
+ Control::get_icon("PoolIntArray", "EditorIcons"),
+ Control::get_icon("PoolRealArray", "EditorIcons"),
+ Control::get_icon("PoolStringArray", "EditorIcons"),
+ Control::get_icon("PoolVector2Array", "EditorIcons"),
+ Control::get_icon("PoolVector3Array", "EditorIcons"),
+ Control::get_icon("PoolColorArray", "EditorIcons")
+ };
+
+ if (!seq_connect && visual_script_generic == false) {
+ get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
+ get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
+ if (type == Variant::INT) {
+ get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
+ }
+ if (type == Variant::BOOL) {
+ get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box);
+ }
+ if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) {
+ get_visual_node_names("operators/math/", Set<String>(), found, root, search_box);
+ }
+ }
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ if (E->get().usage == PROPERTY_USAGE_CATEGORY) {
+ if (category && category->get_children() == NULL) {
+ memdelete(category); //old category was unused
+ }
+ category = search_options->create_item(root);
+ category->set_text(0, E->get().name);
+ category->set_selectable(0, false);
+
+ Ref<Texture> icon;
+ if (E->get().name == "Script Variables") {
+ icon = get_icon("Script", "EditorIcons");
+ } else if (has_icon(E->get().name, "EditorIcons")) {
+ icon = get_icon(E->get().name, "EditorIcons");
+ } else {
+ icon = get_icon("Object", "EditorIcons");
+ }
+ category->set_icon(0, icon);
+ continue;
+ }
+
+ if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE))
+ continue;
+
+ if (type_filter.size() && type_filter.find(E->get().type) == -1)
+ continue;
+
+ String get_text_raw = String(vformat(TTR("Get %s"), E->get().name));
+ String get_text = get_text_raw.capitalize();
+
+ String set_text_raw = String(vformat(TTR("Set %s"), E->get().name));
+ String set_text = set_text_raw.capitalize();
+ String input = search_box->get_text().capitalize();
+ if (input == String() ||
+ get_text_raw.findn(input) != -1 ||
+ get_text.findn(input) != -1) {
+ TreeItem *item = search_options->create_item(category ? category : root);
+ item->set_text(0, get_text);
+ item->set_metadata(0, E->get().name);
+ item->set_icon(0, type_icons[E->get().type]);
+ item->set_metadata(1, "get");
+ item->set_collapsed(1);
+ item->set_selectable(0, true);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
+
+ if (input == String() ||
+ set_text_raw.findn(input) != -1 &&
+ set_text.findn(input) != -1) {
+ TreeItem *item = search_options->create_item(category ? category : root);
+ item->set_text(0, set_text);
+ item->set_metadata(0, E->get().name);
+ item->set_icon(0, type_icons[E->get().type]);
+ item->set_metadata(1, "set");
+ item->set_selectable(0, true);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
+ }
+
+ if (category && category->get_children() == NULL) {
+ memdelete(category); //old category was unused
+ }
+ }
+
+ if (seq_connect == true && visual_script_generic == false) {
+ String text = search_box->get_text();
+ create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition"));
+ create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch"));
+ create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence"));
+ create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator"));
+ create_visualscript_item(String("VisualScriptWhile"), root, text, String("While"));
+ create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return"));
+ get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
+ }
+
+ if (visual_script_generic) {
+ get_visual_node_names("", Set<String>(), found, root, search_box);
+ }
+
+ List<MethodInfo> methods;
+
+ if (type != Variant::NIL) {
+ Variant v;
+ Variant::CallError ce;
+ v = Variant::construct(type, NULL, 0, ce);
+ v.get_method_list(&methods);
+ } else {
+
+ Object *obj = ObjectDB::get_instance(script);
+ if (Object::cast_to<Script>(obj)) {
+
+ methods.push_back(MethodInfo("*Script Methods"));
+ Object::cast_to<Script>(obj)->get_script_method_list(&methods);
+ }
+
+ StringName base = base_type;
+ while (base) {
+ methods.push_back(MethodInfo("*" + String(base)));
+ ClassDB::get_method_list(base, &methods, true, true);
+ base = ClassDB::get_parent_class_nocheck(base);
+ }
+ }
+ TreeItem *category = NULL;
+ bool script_methods = false;
+
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("*")) {
+ if (category && category->get_children() == NULL) {
+ memdelete(category); //old category was unused
+ }
+ category = search_options->create_item(root);
+ category->set_text(0, E->get().name.replace_first("*", ""));
+ category->set_selectable(0, false);
+
+ Ref<Texture> icon;
+ script_methods = false;
+ String rep = E->get().name.replace("*", "");
+ if (E->get().name == "*Script Methods") {
+ icon = get_icon("Script", "EditorIcons");
+ script_methods = true;
+ } else if (has_icon(rep, "EditorIcons")) {
+ icon = get_icon(rep, "EditorIcons");
+ } else {
+ icon = get_icon("Object", "EditorIcons");
+ }
+ category->set_icon(0, icon);
+
+ continue;
+ }
+
+ String name = E->get().name.get_slice(":", 0);
+ if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL))
+ continue;
+
+ if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL))
+ continue;
+
+ if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL))
+ continue;
+
+ MethodInfo mi = E->get();
+ String desc = mi.name.capitalize() + " (";
+
+ if (search_box->get_text() != String() &&
+ name.findn(search_box->get_text()) == -1 &&
+ desc.findn(search_box->get_text()) == -1)
+ continue;
+
+ TreeItem *item = search_options->create_item(category ? category : root);
+
+ for (int i = 0; i < mi.arguments.size(); i++) {
+
+ if (i > 0)
+ desc += ", ";
+
+ if (mi.arguments[i].type == Variant::NIL)
+ desc += "var";
+ else if (mi.arguments[i].name.find(":") != -1) {
+ desc += mi.arguments[i].name.get_slice(":", 1);
+ mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
+ } else
+ desc += Variant::get_type_name(mi.arguments[i].type);
+ }
+
+ desc += ")";
+
+ item->set_text(0, desc);
+ item->set_icon(0, get_icon("MemberMethod", "EditorIcons"));
+ item->set_metadata(0, name);
+ item->set_selectable(0, true);
+
+ item->set_metadata(1, "method");
+ item->set_collapsed(1);
+ item->set_selectable(1, false);
+
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+
+ if (category && category->get_children() == NULL) {
+ memdelete(category); //old category was unused
+ }
+ }
+
+ TreeItem *selected_item = search_options->search_item_text(search_box->get_text());
+ if (!found && selected_item != NULL) {
+ selected_item->select(0);
+ found = true;
+ }
+
+ if (category && category->get_children() == NULL) {
+ memdelete(category); //old category was unused
+ }
+
+ get_ok()->set_disabled(root->get_children() == NULL);
+}
+
+void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) {
+ if (search_input == String() || text.findn(search_input) != -1) {
+ TreeItem *item = search_options->create_item(root);
+ item->set_text(0, text);
+ item->set_icon(0, get_icon("VisualScript", "EditorIcons"));
+ item->set_metadata(0, name);
+ item->set_metadata(1, "action");
+ item->set_selectable(0, true);
+ item->set_collapsed(1);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
+}
+
+void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) {
+ Map<String, TreeItem *> path_cache;
+
+ List<String> fnodes;
+ VisualScriptLanguage::singleton->get_registered_node_names(&fnodes);
+
+ for (List<String>::Element *E = fnodes.front(); E; E = E->next()) {
+ if (!E->get().begins_with(root_filter)) {
+ continue;
+ }
+ Vector<String> path = E->get().split("/");
+ bool is_filter = false;
+ for (Set<String>::Element *E = filter.front(); E; E = E->next()) {
+ if (path.size() >= 2 && path[1].findn(E->get()) != -1) {
+ is_filter = true;
+ break;
+ }
+ }
+ if (is_filter == true) {
+ continue;
+ }
+
+ if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) {
+ continue;
+ }
+ TreeItem *item = search_options->create_item(root);
+ VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get()));
+ String type_name;
+ if (vnode_operator != NULL) {
+ String type;
+ if (path.size() >= 2) {
+ type = path[1];
+ }
+ type_name = type.capitalize() + " ";
+ }
+ VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get()));
+ if (vnode_function_call != NULL) {
+ String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type());
+ type_name = basic_type.capitalize() + " ";
+ }
+ VisualScriptBuiltinFunc *vnode_builtin_function_call = Object::cast_to<VisualScriptBuiltinFunc>(*VisualScriptLanguage::singleton->create_node_from_name(E->get()));
+ if (vnode_builtin_function_call != NULL) {
+ type_name = "Builtin ";
+ }
+ item->set_text(0, type_name + path[path.size() - 1].capitalize());
+ item->set_icon(0, get_icon("VisualScript", "EditorIcons"));
+ item->set_selectable(0, true);
+ item->set_metadata(0, E->get());
+ item->set_selectable(0, true);
+ item->set_metadata(1, "visualscript");
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
+}
+
+void VisualScriptPropertySelector::_confirmed() {
+
+ TreeItem *ti = search_options->get_selected();
+ if (!ti)
+ return;
+ emit_signal("selected", ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2));
+ hide();
+}
+
+void VisualScriptPropertySelector::_item_selected() {
+
+ help_bit->set_text("");
+
+ TreeItem *item = search_options->get_selected();
+ if (!item)
+ return;
+ String name = item->get_metadata(0);
+
+ String class_type;
+ if (type) {
+ class_type = Variant::get_type_name(type);
+
+ } else {
+ class_type = base_type;
+ }
+
+ DocData *dd = EditorHelp::get_doc_data();
+ String text;
+
+ String at_class = class_type;
+
+ while (at_class != String()) {
+
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
+ if (E) {
+ for (int i = 0; i < E->get().properties.size(); i++) {
+ if (E->get().properties[i].name == name) {
+ text = E->get().properties[i].description;
+ }
+ }
+ }
+
+ at_class = ClassDB::get_parent_class_nocheck(at_class);
+ }
+ at_class = class_type;
+
+ while (at_class != String()) {
+
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
+ if (E) {
+ for (int i = 0; i < E->get().methods.size(); i++) {
+ if (E->get().methods[i].name == name) {
+ text = E->get().methods[i].description;
+ }
+ }
+ }
+
+ at_class = ClassDB::get_parent_class_nocheck(at_class);
+ }
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type);
+ if (E) {
+ for (int i = 0; i < E->get().methods.size(); i++) {
+ Vector<String> functions = name.rsplit("/", false, 1);
+ if (E->get().methods[i].name == functions[functions.size() - 1]) {
+ text = E->get().methods[i].description;
+ }
+ }
+ }
+
+ List<String> *names = memnew(List<String>);
+ VisualScriptLanguage::singleton->get_registered_node_names(names);
+ if (names->find(name) != NULL) {
+ Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name);
+ if (operator_node.is_valid()) {
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(operator_node->get_class_name());
+ if (E) {
+ text = Variant::get_operator_name(operator_node->get_operator());
+ }
+ }
+ Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name);
+ if (typecast_node.is_valid()) {
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(typecast_node->get_class_name());
+ if (E) {
+ text = E->get().description;
+ }
+ }
+
+ Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name);
+ if (builtin_node.is_valid()) {
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(builtin_node->get_class_name());
+ if (E) {
+ for (int i = 0; i < E->get().constants.size(); i++) {
+ if (E->get().constants[i].value.to_int() == int(builtin_node->get_func())) {
+ text = E->get().constants[i].description;
+ }
+ }
+ }
+ }
+ }
+
+ memdelete(names);
+
+ if (text == String())
+ return;
+
+ help_bit->set_text(text);
+}
+
+void VisualScriptPropertySelector::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ connect("confirmed", this, "_confirmed");
+ }
+}
+
+void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting) {
+
+ base_type = p_base;
+ selected = p_current;
+ type = Variant::NIL;
+ script = 0;
+ properties = false;
+ instance = NULL;
+ virtuals_only = p_virtuals_only;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {
+ type_filter = p_type_filter;
+}
+
+void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting) {
+
+ base_type = p_base;
+ selected = p_current;
+ type = Variant::NIL;
+ script = 0;
+ properties = true;
+ instance = NULL;
+ virtuals_only = p_virtuals_only;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ seq_connect = p_seq_connect;
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting) {
+ ERR_FAIL_COND(p_script.is_null());
+
+ base_type = p_script->get_instance_base_type();
+ selected = p_current;
+ type = Variant::NIL;
+ script = p_script->get_instance_id();
+ properties = true;
+ instance = NULL;
+ virtuals_only = false;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ seq_connect = false;
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting) {
+ ERR_FAIL_COND(p_type == Variant::NIL);
+ base_type = "";
+ selected = p_current;
+ type = p_type;
+ script = 0;
+ properties = true;
+ instance = NULL;
+ virtuals_only = false;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ seq_connect = false;
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting) {
+ base_type = p_type;
+ selected = p_current;
+ type = Variant::NIL;
+ script = 0;
+ properties = false;
+ instance = NULL;
+ virtuals_only = false;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ seq_connect = true;
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting) {
+ base_type = "";
+ selected = p_current;
+ type = Variant::NIL;
+ script = 0;
+ properties = true;
+ instance = p_instance;
+ virtuals_only = false;
+
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ seq_connect = false;
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting) {
+ base_type = p_base;
+ selected = "";
+ type = Variant::NIL;
+ script = 0;
+ properties = true;
+ visual_script_generic = true;
+ instance = NULL;
+ virtuals_only = false;
+ show_window(.5f);
+ search_box->set_text("");
+ search_box->grab_focus();
+ connecting = p_connecting;
+
+ _update_search();
+}
+
+void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
+ Rect2 rect;
+ Point2 window_size = get_viewport_rect().size;
+ rect.size = (window_size * p_screen_ratio).floor();
+ rect.size.x = rect.size.x / 1.25f;
+ rect.position = ((window_size - rect.size) / 2.0f).floor();
+ popup(rect);
+}
+
+void VisualScriptPropertySelector::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_text_changed"), &VisualScriptPropertySelector::_text_changed);
+ ClassDB::bind_method(D_METHOD("_confirmed"), &VisualScriptPropertySelector::_confirmed);
+ ClassDB::bind_method(D_METHOD("_sbox_input"), &VisualScriptPropertySelector::_sbox_input);
+ ClassDB::bind_method(D_METHOD("_item_selected"), &VisualScriptPropertySelector::_item_selected);
+
+ ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
+}
+
+VisualScriptPropertySelector::VisualScriptPropertySelector() {
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ add_child(vbc);
+ //set_child_rect(vbc);
+ search_box = memnew(LineEdit);
+ vbc->add_margin_child(TTR("Search:"), search_box);
+ search_box->connect("text_changed", this, "_text_changed");
+ search_box->connect("gui_input", this, "_sbox_input");
+ search_options = memnew(Tree);
+ vbc->add_margin_child(TTR("Matches:"), search_options, true);
+ get_ok()->set_text(TTR("Open"));
+ get_ok()->set_disabled(true);
+ register_text_enter(search_box);
+ set_hide_on_ok(false);
+ search_options->connect("item_activated", this, "_confirmed");
+ search_options->connect("cell_selected", this, "_item_selected");
+ search_options->set_hide_root(true);
+ search_options->set_hide_folding(true);
+ virtuals_only = false;
+ help_bit = memnew(EditorHelpBit);
+ vbc->add_margin_child(TTR("Description:"), help_bit);
+ help_bit->connect("request_hide", this, "_closed");
+ search_options->set_columns(3);
+ search_options->set_column_expand(1, false);
+ search_options->set_column_expand(2, false);
+}
diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h
new file mode 100644
index 0000000000..917ef9ae6d
--- /dev/null
+++ b/modules/visual_script/visual_script_property_selector.h
@@ -0,0 +1,93 @@
+/*************************************************************************/
+/* visual_script_property_selector.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 VISUALSCRIPT_PROPERTYSELECTOR_H
+#define VISUALSCRIPT_PROPERTYSELECTOR_H
+
+#include "editor/property_editor.h"
+#include "editor_help.h"
+#include "scene/gui/rich_text_label.h"
+
+class VisualScriptPropertySelector : public ConfirmationDialog {
+ GDCLASS(VisualScriptPropertySelector, ConfirmationDialog)
+
+ LineEdit *search_box;
+ Tree *search_options;
+
+ void _update_search();
+
+ void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text);
+
+ void get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box);
+
+ void _sbox_input(const Ref<InputEvent> &p_ie);
+
+ void _confirmed();
+ void _text_changed(const String &p_newtext);
+
+ EditorHelpBit *help_bit;
+
+ bool properties;
+ bool visual_script_generic;
+ bool connecting;
+ String selected;
+ Variant::Type type;
+ String base_type;
+ ObjectID script;
+ Object *instance;
+ bool virtuals_only;
+
+ bool seq_connect = false;
+
+ void _item_selected();
+
+ Vector<Variant::Type> type_filter;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true);
+ void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true);
+ void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true);
+ void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true);
+ void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true);
+ void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true);
+ void select_from_visual_script(const String &p_base, const bool p_connecting = true);
+
+ void show_window(float p_screen_ratio);
+
+ void set_type_filter(const Vector<Variant::Type> &p_type_filter);
+
+ VisualScriptPropertySelector();
+};
+
+#endif // VISUALSCRIPT_PROPERTYSELECTOR_H
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index bae8f7be5f..5bc82f267f 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -48,7 +48,7 @@ size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst, size_t p_data, s
int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f, ogg_int64_t offs, int whence) {
-//printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
+ //printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
#ifdef SEEK_SET
//printf("seek set defined\n");
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index c681e2b34f..2daf8c282f 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -38,7 +38,6 @@ libvpx_sources = [
"vp8/decoder/decodemv.c",
"vp8/decoder/detokenize.c",
"vp8/decoder/onyxd_if.c",
- "vp8/decoder/threading.c",
"vp9/vp9_dx_iface.c",
@@ -102,6 +101,10 @@ libvpx_sources = [
"vpx_util/vpx_thread.c"
]
+libvpx_sources_mt = [
+ "vp8/decoder/threading.c",
+]
+
libvpx_sources_intrin_x86 = [
"vp8/common/x86/filter_x86.c",
"vp8/common/x86/loopfilter_x86.c",
@@ -231,6 +234,7 @@ libvpx_sources_arm_neon_gas_apple = [
]
libvpx_sources = [libvpx_dir + file for file in libvpx_sources]
+libvpx_sources_mt = [libvpx_dir + file for file in libvpx_sources_mt]
libvpx_sources_intrin_x86 = [libvpx_dir + file for file in libvpx_sources_intrin_x86]
libvpx_sources_intrin_x86_mmx = [libvpx_dir + file for file in libvpx_sources_intrin_x86_mmx]
libvpx_sources_intrin_x86_sse2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_sse2]
@@ -253,6 +257,8 @@ env_webm.Append(CPPPATH=[libvpx_dir])
env_libvpx = env.Clone()
env_libvpx.Append(CPPPATH=[libvpx_dir])
+webm_multithread = env["platform"] != 'javascript'
+
cpu_bits = env["bits"]
webm_cpu_x86 = False
webm_cpu_arm = False
@@ -338,6 +344,10 @@ if webm_simd_optimizations == False:
print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!")
env_libvpx.add_source_files(env.modules_sources, libvpx_sources)
+
+if webm_multithread:
+ env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt)
+
if webm_cpu_x86:
is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ)
diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp
index 1183dd41f7..121b528d5b 100644
--- a/modules/webm/register_types.cpp
+++ b/modules/webm/register_types.cpp
@@ -29,18 +29,22 @@
/*************************************************************************/
#include "register_types.h"
-#include "resource_importer_webm.h"
+
#include "video_stream_webm.h"
+static ResourceFormatLoaderWebm *resource_loader_webm = NULL;
+
void register_webm_types() {
-#ifdef TOOLS_ENABLED
- Ref<ResourceImporterWebm> webm_import;
- webm_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(webm_import);
-#endif
+ resource_loader_webm = memnew(ResourceFormatLoaderWebm);
+ ResourceLoader::add_resource_format_loader(resource_loader_webm, true);
+
ClassDB::register_class<VideoStreamWebm>();
}
void unregister_webm_types() {
+
+ if (resource_loader_webm) {
+ memdelete(resource_loader_webm);
+ }
}
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index fac47225bc..1bb9a43886 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -443,3 +443,46 @@ void VideoStreamWebm::set_audio_track(int p_track) {
audio_track = p_track;
}
+
+////////////
+
+RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ if (r_error) {
+ *r_error = ERR_CANT_OPEN;
+ }
+ memdelete(f);
+ return RES();
+ }
+
+ VideoStreamWebm *stream = memnew(VideoStreamWebm);
+ stream->set_file(p_path);
+
+ Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return webm_stream;
+}
+
+void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("webm");
+}
+
+bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const {
+
+ return ClassDB::is_parent_class(p_type, "VideoStream");
+}
+
+String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const {
+
+ String el = p_path.get_extension().to_lower();
+ if (el == "webm")
+ return "VideoStreamWebm";
+ return "";
+}
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
index dde993d154..dcf88092c5 100644
--- a/modules/webm/video_stream_webm.h
+++ b/modules/webm/video_stream_webm.h
@@ -109,7 +109,6 @@ private:
class VideoStreamWebm : public VideoStream {
GDCLASS(VideoStreamWebm, VideoStream);
- RES_BASE_EXTENSION("webmstr");
String file;
int audio_track;
@@ -127,4 +126,12 @@ public:
virtual void set_audio_track(int p_track);
};
+class ResourceFormatLoaderWebm : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
#endif // VIDEO_STREAM_WEBM_H
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index cdf2d75e96..42b2c77777 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -116,64 +116,73 @@ static Ref<Image> _webp_lossy_unpack(const PoolVector<uint8_t> &p_buffer) {
return img;
}
-Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
- uint32_t size = f->get_len();
- PoolVector<uint8_t> src_image;
- src_image.resize(size);
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
WebPBitstreamFeatures features;
-
- PoolVector<uint8_t>::Write src_w = src_image.write();
- f->get_buffer(src_w.ptr(), size);
- ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_EOF);
-
- if (WebPGetFeatures(src_w.ptr(), size, &features) != VP8_STATUS_OK) {
- f->close();
- //ERR_EXPLAIN("Error decoding WEBP image: "+p_file);
+ if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
+ // ERR_EXPLAIN("Error decoding WEBP image");
ERR_FAIL_V(ERR_FILE_CORRUPT);
}
- /*
- print_line("width: " + itos(features.width));
- print_line("height: " + itos(features.height));
- print_line("alpha: " + itos(features.has_alpha));
- */
-
- src_w = PoolVector<uint8_t>::Write();
-
PoolVector<uint8_t> dst_image;
int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
dst_image.resize(datasize);
-
- PoolVector<uint8_t>::Read src_r = src_image.read();
PoolVector<uint8_t>::Write dst_w = dst_image.write();
bool errdec = false;
if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(src_r.ptr(), size, dst_w.ptr(), datasize, 4 * features.width) == NULL;
+ errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w.ptr(), datasize, 4 * features.width) == NULL;
} else {
- errdec = WebPDecodeRGBInto(src_r.ptr(), size, dst_w.ptr(), datasize, 3 * features.width) == NULL;
+ errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w.ptr(), datasize, 3 * features.width) == NULL;
}
+ dst_w = PoolVector<uint8_t>::Write();
- //ERR_EXPLAIN("Error decoding webp! - "+p_file);
+ //ERR_EXPLAIN("Error decoding webp!");
ERR_FAIL_COND_V(errdec, ERR_FILE_CORRUPT);
- src_r = PoolVector<uint8_t>::Read();
- dst_w = PoolVector<uint8_t>::Write();
-
p_image->create(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
return OK;
}
+static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
+
+ Ref<Image> img;
+ img.instance();
+ Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
+ ERR_FAIL_COND_V(err, Ref<Image>());
+ return img;
+}
+
+Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
+
+ PoolVector<uint8_t> src_image;
+ int src_image_len = f->get_len();
+ ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
+ src_image.resize(src_image_len);
+
+ PoolVector<uint8_t>::Write w = src_image.write();
+
+ f->get_buffer(&w[0], src_image_len);
+
+ f->close();
+
+ Error err = webp_load_image_from_buffer(p_image.ptr(), w.ptr(), src_image_len);
+
+ w = PoolVector<uint8_t>::Write();
+
+ return err;
+}
+
void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("webp");
}
ImageLoaderWEBP::ImageLoaderWEBP() {
-
+ Image::_webp_mem_loader_func = _webp_mem_loader_func;
Image::lossy_packer = _webp_lossy_pack;
Image::lossy_unpacker = _webp_lossy_unpack;
}
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index 15a88773e7..c0985b3245 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -7,87 +7,88 @@ Import('env_modules')
env_lws = env_modules.Clone()
-thirdparty_dir = "#thirdparty/libwebsockets/"
-helper_dir = "win32helpers/"
-thirdparty_sources = [
-
- "core/alloc.c",
- "core/context.c",
- "core/libwebsockets.c",
- "core/output.c",
- "core/pollfd.c",
- "core/service.c",
-
- "event-libs/poll/poll.c",
-
- "misc/base64-decode.c",
- "misc/lejp.c",
- "misc/sha-1.c",
-
- "roles/h1/ops-h1.c",
- "roles/http/header.c",
- "roles/http/client/client.c",
- "roles/http/client/client-handshake.c",
- "roles/http/server/fops-zip.c",
- "roles/http/server/lejp-conf.c",
- "roles/http/server/parsers.c",
- "roles/http/server/server.c",
- "roles/listen/ops-listen.c",
- "roles/pipe/ops-pipe.c",
- "roles/raw/ops-raw.c",
-
- "roles/ws/client-ws.c",
- "roles/ws/client-parser-ws.c",
- "roles/ws/ops-ws.c",
- "roles/ws/server-ws.c",
-
- "tls/tls.c",
- "tls/tls-client.c",
- "tls/tls-server.c",
-
- "tls/mbedtls/wrapper/library/ssl_cert.c",
- "tls/mbedtls/wrapper/library/ssl_pkey.c",
- "tls/mbedtls/wrapper/library/ssl_stack.c",
- "tls/mbedtls/wrapper/library/ssl_methods.c",
- "tls/mbedtls/wrapper/library/ssl_lib.c",
- "tls/mbedtls/wrapper/library/ssl_x509.c",
- "tls/mbedtls/wrapper/platform/ssl_port.c",
- "tls/mbedtls/wrapper/platform/ssl_pm.c",
- "tls/mbedtls/lws-genhash.c",
- "tls/mbedtls/mbedtls-client.c",
- "tls/mbedtls/lws-genrsa.c",
- "tls/mbedtls/ssl.c",
- "tls/mbedtls/mbedtls-server.c"
-]
-
-if env_lws["platform"] == "android": # Builtin getifaddrs
- thirdparty_sources += ["misc/getifaddrs.c"]
-
-if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock
- thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
-else: # Unix socket
- thirdparty_sources += ["plat/lws-plat-unix.c"]
-
-
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-if env_lws["platform"] == "javascript": # No need to add third party libraries at all
- pass
-else:
- env_lws.add_source_files(env.modules_sources, thirdparty_sources)
- env_lws.Append(CPPPATH=[thirdparty_dir])
-
- wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
- env_lws.Prepend(CPPPATH=wrapper_includes)
-
- if env['builtin_mbedtls']:
- mbedtls_includes = "#thirdparty/mbedtls/include"
- env_lws.Prepend(CPPPATH=[mbedtls_includes])
-
- if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
- env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
-
- if env_lws["platform"] == "uwp":
- env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
+if env['builtin_libwebsockets']:
+ thirdparty_dir = "#thirdparty/libwebsockets/"
+ helper_dir = "win32helpers/"
+ thirdparty_sources = [
+
+ "core/alloc.c",
+ "core/context.c",
+ "core/libwebsockets.c",
+ "core/output.c",
+ "core/pollfd.c",
+ "core/service.c",
+
+ "event-libs/poll/poll.c",
+
+ "misc/base64-decode.c",
+ "misc/lejp.c",
+ "misc/sha-1.c",
+
+ "roles/h1/ops-h1.c",
+ "roles/http/header.c",
+ "roles/http/client/client.c",
+ "roles/http/client/client-handshake.c",
+ "roles/http/server/fops-zip.c",
+ "roles/http/server/lejp-conf.c",
+ "roles/http/server/parsers.c",
+ "roles/http/server/server.c",
+ "roles/listen/ops-listen.c",
+ "roles/pipe/ops-pipe.c",
+ "roles/raw/ops-raw.c",
+
+ "roles/ws/client-ws.c",
+ "roles/ws/client-parser-ws.c",
+ "roles/ws/ops-ws.c",
+ "roles/ws/server-ws.c",
+
+ "tls/tls.c",
+ "tls/tls-client.c",
+ "tls/tls-server.c",
+
+ "tls/mbedtls/wrapper/library/ssl_cert.c",
+ "tls/mbedtls/wrapper/library/ssl_pkey.c",
+ "tls/mbedtls/wrapper/library/ssl_stack.c",
+ "tls/mbedtls/wrapper/library/ssl_methods.c",
+ "tls/mbedtls/wrapper/library/ssl_lib.c",
+ "tls/mbedtls/wrapper/library/ssl_x509.c",
+ "tls/mbedtls/wrapper/platform/ssl_port.c",
+ "tls/mbedtls/wrapper/platform/ssl_pm.c",
+ "tls/mbedtls/lws-genhash.c",
+ "tls/mbedtls/mbedtls-client.c",
+ "tls/mbedtls/lws-genrsa.c",
+ "tls/mbedtls/ssl.c",
+ "tls/mbedtls/mbedtls-server.c"
+ ]
+
+ if env_lws["platform"] == "android": # Builtin getifaddrs
+ thirdparty_sources += ["misc/getifaddrs.c"]
+
+ if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock
+ thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
+ else: # Unix socket
+ thirdparty_sources += ["plat/lws-plat-unix.c"]
+
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ if env_lws["platform"] == "javascript": # No need to add third party libraries at all
+ pass
+ else:
+ env_lws.add_source_files(env.modules_sources, thirdparty_sources)
+ env_lws.Append(CPPPATH=[thirdparty_dir])
+
+ wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
+ env_lws.Prepend(CPPPATH=wrapper_includes)
+
+ if env['builtin_mbedtls']:
+ mbedtls_includes = "#thirdparty/mbedtls/include"
+ env_lws.Prepend(CPPPATH=[mbedtls_includes])
+
+ if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
+ env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
+
+ if env_lws["platform"] == "uwp":
+ env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
env_lws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 1405fa98b0..836b564de8 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -62,26 +62,23 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int
Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
+ String proto_string = p_protocols.join(",");
String str = "ws://";
- String proto_string = "";
- int i = 0;
if (p_ssl)
str = "wss://";
str += p_host + ":" + itos(p_port) + p_path;
- for (int i = 0; i < p_protocols.size(); i++) {
- proto_string += p_protocols[i];
- proto_string += ",";
- }
- if (proto_string == "")
- proto_string = "binary,";
-
- proto_string = proto_string.substr(0, proto_string.length() - 1);
_is_connecting = true;
/* clang-format off */
int peer_sock = EM_ASM_INT({
- var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(","));
+ var proto_str = UTF8ToString($2);
+ var socket = null;
+ if (proto_str) {
+ socket = new WebSocket(UTF8ToString($1), proto_str.split(","));
+ } else {
+ socket = new WebSocket(UTF8ToString($1));
+ }
var c_ptr = Module.IDHandler.get($0);
socket.binaryType = "arraybuffer";
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index 06f97aaf05..09f6422058 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -48,12 +48,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
- // prepare protocols
- if (p_protocols.size() == 0) // default to binary protocol
- p_protocols.append("binary");
+ // Prepare protocols
_lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref);
- // init lws client
+ // Init lws client
struct lws_context_creation_info info;
struct lws_client_connect_info i;
@@ -87,7 +85,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
strncpy(pbuf, p_path.utf8().get_data(), 2048);
i.context = context;
- i.protocol = _lws_ref->lws_names;
+ if (p_protocols.size() > 0)
+ i.protocol = _lws_ref->lws_names;
+ else
+ i.protocol = NULL;
i.address = abuf;
i.host = hbuf;
i.path = pbuf;
@@ -127,11 +128,6 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
case LWS_CALLBACK_CLIENT_ESTABLISHED:
peer->set_wsi(wsi);
peer_data->peer_id = 0;
- peer_data->in_size = 0;
- peer_data->in_count = 0;
- peer_data->out_count = 0;
- peer_data->rbw.resize(16);
- peer_data->rbr.resize(16);
peer_data->force_close = false;
_on_connect(lws_get_protocol(wsi)->name);
break;
@@ -139,17 +135,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
_on_error();
destroy_context();
- return -1; // we should close the connection (would probably happen anyway)
+ return -1; // We should close the connection (would probably happen anyway)
case LWS_CALLBACK_CLIENT_CLOSED:
- peer_data->in_count = 0;
- peer_data->out_count = 0;
- peer_data->rbw.resize(0);
- peer_data->rbr.resize(0);
peer->close();
destroy_context();
_on_disconnect();
- return 0; // we can end here
+ return 0; // We can end here
case LWS_CALLBACK_CLIENT_RECEIVE:
peer->read_wsi(in, len);
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
index a850a545d3..70256ccf16 100644
--- a/modules/websocket/lws_helper.h
+++ b/modules/websocket/lws_helper.h
@@ -30,6 +30,9 @@
#ifndef LWS_HELPER_H
#define LWS_HELPER_H
+#define LWS_BUF_SIZE 65536
+#define LWS_PACKET_SIZE LWS_BUF_SIZE
+
#include "core/io/stream_peer.h"
#include "core/os/os.h"
#include "core/reference.h"
@@ -102,60 +105,64 @@ static bool _lws_poll(struct lws_context *context, _LWSRef *ref) {
}
/*
- * prepare the protocol_structs to be fed to context
- * also prepare the protocol string used by the client
+ * Prepare the protocol_structs to be fed to context.
+ * Also prepare the protocol string used by the client.
*/
static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) {
- /* the input strings might go away after this call,
- * we need to copy them. Will clear them when
- * destroying the context */
+ // The input strings might go away after this call, we need to copy them.
+ // We will clear them when destroying the context.
int i;
int len = p_names.size();
size_t data_size = sizeof(struct LWSPeer::PeerData);
PoolVector<String>::Read pnr = p_names.read();
- /*
- * This is a reference connecting the object with lws
- * keep track of status, mallocs, etc.
- * Must survive as long the context
- * Must be freed manually when context creation fails.
- */
+ // This is a reference connecting the object with lws keep track of status, mallocs, etc.
+ // Must survive as long the context.
+ // Must be freed manually when context creation fails.
_LWSRef *ref = _lws_create_ref(p_obj);
- /* LWS protocol structs */
+ // LWS protocol structs.
ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2));
+ memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2));
CharString strings = p_names.join(",").ascii();
int str_len = strings.length();
- /* Joined string of protocols, double the size: comma separated first, NULL separated last */
- ref->lws_names = (char *)memalloc((str_len + 1) * 2); /* plus the terminator */
+ // Joined string of protocols, double the size: comma separated first, NULL separated last
+ ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator
char *names_ptr = ref->lws_names;
struct lws_protocols *structs_ptr = ref->lws_structs;
- copymem(names_ptr, strings.get_data(), str_len);
- names_ptr[str_len] = '\0'; /* NULL terminator */
- /* NULL terminated strings to be used in protocol structs */
- copymem(&names_ptr[str_len + 1], strings.get_data(), str_len);
- names_ptr[(str_len * 2) + 1] = '\0'; /* NULL terminator */
+ // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header
+ if (str_len > 0)
+ copymem(names_ptr, strings.get_data(), str_len);
+ names_ptr[str_len] = '\0'; // NULL terminator
+
+ // NULL terminated protocol strings to be used in protocol structs
+ if (str_len > 0)
+ copymem(&names_ptr[str_len + 1], strings.get_data(), str_len);
+ names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator
int pos = str_len + 1;
- /* the first protocol is always http-only */
- structs_ptr[0].name = "http-only";
+ // The first protocol is the default for any http request (before upgrade).
+ // It is also used as the websocket protocol when no subprotocol is specified.
+ structs_ptr[0].name = "default";
structs_ptr[0].callback = p_callback;
structs_ptr[0].per_session_data_size = data_size;
- structs_ptr[0].rx_buffer_size = 0;
- /* add user defined protocols */
+ structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE;
+ // Add user defined protocols
for (i = 0; i < len; i++) {
structs_ptr[i + 1].name = (const char *)&names_ptr[pos];
structs_ptr[i + 1].callback = p_callback;
structs_ptr[i + 1].per_session_data_size = data_size;
- structs_ptr[i + 1].rx_buffer_size = 0;
+ structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE;
+ structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE;
pos += pnr[i].ascii().length() + 1;
names_ptr[pos - 1] = '\0';
}
- /* add protocols terminator */
+ // Add protocols terminator
structs_ptr[len + 1].name = NULL;
structs_ptr[len + 1].callback = NULL;
structs_ptr[len + 1].per_session_data_size = 0;
@@ -209,6 +216,6 @@ public: \
\
protected:
- /* clang-format on */
+/* clang-format on */
#endif // LWS_HELPER_H
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index 96acb99cc4..0989357258 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -41,6 +41,10 @@
#include "drivers/unix/socket_helpers.h"
void LWSPeer::set_wsi(struct lws *p_wsi) {
+ ERR_FAIL_COND(wsi != NULL);
+
+ rbw.resize(16);
+ rbr.resize(16);
wsi = p_wsi;
};
@@ -57,24 +61,24 @@ Error LWSPeer::read_wsi(void *in, size_t len) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
- uint32_t size = peer_data->in_size;
+ uint32_t size = in_size;
uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
- if (peer_data->rbr.space_left() < len + 5) {
+ if (rbr.space_left() < len + 5) {
ERR_EXPLAIN("Buffer full! Dropping data");
ERR_FAIL_V(FAILED);
}
- copymem(&(peer_data->input_buffer[size]), in, len);
+ copymem(&(input_buffer[size]), in, len);
size += len;
- peer_data->in_size = size;
+ in_size = size;
if (lws_is_final_fragment(wsi)) {
- peer_data->rbr.write((uint8_t *)&size, 4);
- peer_data->rbr.write((uint8_t *)&is_string, 1);
- peer_data->rbr.write(peer_data->input_buffer, size);
- peer_data->in_count++;
- peer_data->in_size = 0;
+ rbr.write((uint8_t *)&size, 4);
+ rbr.write((uint8_t *)&is_string, 1);
+ rbr.write(input_buffer, size);
+ in_count++;
+ in_size = 0;
}
return OK;
@@ -86,26 +90,26 @@ Error LWSPeer::write_wsi() {
PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
PoolVector<uint8_t> tmp;
- int left = peer_data->rbw.data_left();
+ int left = rbw.data_left();
uint32_t to_write = 0;
- if (left == 0 || peer_data->out_count == 0)
+ if (left == 0 || out_count == 0)
return OK;
- peer_data->rbw.read((uint8_t *)&to_write, 4);
- peer_data->out_count--;
+ rbw.read((uint8_t *)&to_write, 4);
+ out_count--;
if (left < to_write) {
- peer_data->rbw.advance_read(left);
+ rbw.advance_read(left);
return FAILED;
}
tmp.resize(LWS_PRE + to_write);
- peer_data->rbw.read(&(tmp.write()[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);
- if (peer_data->out_count > 0)
+ if (out_count > 0)
lws_callback_on_writable(wsi); // we want to write more!
return OK;
@@ -116,9 +120,9 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
- peer_data->rbw.write((uint8_t *)&p_buffer_size, 4);
- peer_data->rbw.write(p_buffer, MIN(p_buffer_size, peer_data->rbw.space_left()));
- peer_data->out_count++;
+ rbw.write((uint8_t *)&p_buffer_size, 4);
+ rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left()));
+ out_count++;
lws_callback_on_writable(wsi); // notify that we want to write
return OK;
@@ -130,7 +134,7 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
- if (peer_data->in_count == 0)
+ if (in_count == 0)
return ERR_UNAVAILABLE;
uint32_t to_read = 0;
@@ -138,17 +142,17 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
uint8_t is_string = 0;
r_buffer_size = 0;
- peer_data->rbr.read((uint8_t *)&to_read, 4);
- peer_data->in_count--;
- left = peer_data->rbr.data_left();
+ rbr.read((uint8_t *)&to_read, 4);
+ in_count--;
+ left = rbr.data_left();
if (left < to_read + 1) {
- peer_data->rbr.advance_read(left);
+ rbr.advance_read(left);
return FAILED;
}
- peer_data->rbr.read(&is_string, 1);
- peer_data->rbr.read(packet_buffer, to_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;
@@ -161,7 +165,7 @@ int LWSPeer::get_available_packet_count() const {
if (!is_connected_to_host())
return 0;
- return ((PeerData *)lws_wsi_user(wsi))->in_count;
+ return in_count;
};
bool LWSPeer::was_string_packet() const {
@@ -176,12 +180,17 @@ bool LWSPeer::is_connected_to_host() const {
void LWSPeer::close() {
if (wsi != NULL) {
- struct lws *tmp = wsi;
PeerData *data = ((PeerData *)lws_wsi_user(wsi));
data->force_close = true;
- wsi = NULL;
- lws_callback_on_writable(tmp); // notify that we want to disconnect
+ lws_callback_on_writable(wsi); // notify that we want to disconnect
}
+ wsi = NULL;
+ rbw.resize(0);
+ rbr.resize(0);
+ in_count = 0;
+ in_size = 0;
+ out_count = 0;
+ _was_string = false;
};
IP_Address LWSPeer::get_connected_host() const {
@@ -228,8 +237,8 @@ uint16_t LWSPeer::get_connected_port() const {
LWSPeer::LWSPeer() {
wsi = NULL;
- _was_string = false;
write_mode = WRITE_MODE_BINARY;
+ close();
};
LWSPeer::~LWSPeer() {
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h
index e96b38b168..d7d46e3076 100644
--- a/modules/websocket/lws_peer.h
+++ b/modules/websocket/lws_peer.h
@@ -57,14 +57,15 @@ public:
struct PeerData {
uint32_t peer_id;
bool force_close;
- RingBuffer<uint8_t> rbw;
- RingBuffer<uint8_t> rbr;
- mutable uint8_t input_buffer[PACKET_BUFFER_SIZE];
- uint32_t in_size;
- int in_count;
- int out_count;
};
+ 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);
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 8d13dc7a98..4a614f6933 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -41,9 +41,6 @@ Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_a
struct lws_context_creation_info info;
memset(&info, 0, sizeof info);
- if (p_protocols.size() == 0) // default to binary protocol
- p_protocols.append(String("binary"));
-
// Prepare lws protocol structs
_lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref);
@@ -92,11 +89,6 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
_peer_map[id] = peer;
peer_data->peer_id = id;
- peer_data->in_size = 0;
- peer_data->in_count = 0;
- peer_data->out_count = 0;
- peer_data->rbw.resize(16);
- peer_data->rbr.resize(16);
peer_data->force_close = false;
_on_connect(id, lws_get_protocol(wsi)->name);
@@ -111,10 +103,6 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
_peer_map[id]->close();
_peer_map.erase(id);
}
- peer_data->in_count = 0;
- peer_data->out_count = 0;
- peer_data->rbr.resize(0);
- peer_data->rbw.resize(0);
_on_disconnect(id);
return 0; // we can end here
}