summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/bullet/area_bullet.cpp6
-rw-r--r--modules/bullet/bullet_physics_server.cpp202
-rw-r--r--modules/bullet/bullet_physics_server.h48
-rw-r--r--modules/bullet/collision_object_bullet.cpp18
-rw-r--r--modules/bullet/register_types.cpp4
-rw-r--r--modules/bullet/rigid_body_bullet.cpp37
-rw-r--r--modules/bullet/rigid_body_bullet.h5
-rw-r--r--modules/bullet/shape_bullet.cpp2
-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.cpp24
-rw-r--r--modules/bullet/space_bullet.h8
-rw-r--r--modules/csg/csg.cpp74
-rw-r--r--modules/csg/csg_gizmos.cpp81
-rw-r--r--modules/csg/csg_gizmos.h24
-rw-r--r--modules/csg/csg_shape.cpp28
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp2
-rw-r--r--modules/etc/image_etc.cpp29
-rw-r--r--modules/gdnative/SCsub257
-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.py2
-rw-r--r--modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml15
-rw-r--r--modules/gdnative/doc_classes/PacketPeerGDNative.xml15
-rw-r--r--modules/gdnative/doc_classes/StreamPeerGDNative.xml15
-rw-r--r--modules/gdnative/gdnative.cpp10
-rw-r--r--modules/gdnative/gdnative.h6
-rw-r--r--modules/gdnative/gdnative/string.cpp14
-rw-r--r--modules/gdnative/gdnative_builders.py261
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp2
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp33
-rw-r--r--modules/gdnative/nativescript/nativescript.h2
-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.cpp8
-rw-r--r--modules/gdscript/gdscript.cpp180
-rw-r--r--modules/gdscript/gdscript.h51
-rw-r--r--modules/gdscript/gdscript_compiler.cpp44
-rw-r--r--modules/gdscript/gdscript_editor.cpp21
-rw-r--r--modules/gdscript/gdscript_function.cpp38
-rw-r--r--modules/gdscript/gdscript_functions.cpp4
-rw-r--r--modules/gdscript/gdscript_parser.cpp578
-rw-r--r--modules/gdscript/gdscript_parser.h24
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp53
-rw-r--r--modules/gdscript/gdscript_tokenizer.h22
-rw-r--r--modules/gridmap/grid_map.cpp4
-rwxr-xr-xmodules/mbedtls/SCsub5
-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)4
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h (renamed from modules/mobile_vr/mobile_interface.h)4
-rw-r--r--modules/mobile_vr/register_types.cpp2
-rw-r--r--modules/mono/SCsub1
-rw-r--r--modules/mono/config.py1
-rw-r--r--modules/mono/csharp_script.cpp65
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/bindings_generator.cpp40
-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/Array.cs335
-rw-r--r--modules/mono/glue/cs_files/Color.cs83
-rw-r--r--modules/mono/glue/cs_files/Dictionary.cs401
-rw-r--r--modules/mono/glue/cs_files/GD.cs5
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs29
-rw-r--r--modules/mono/glue/cs_files/NodeExtensions.cs45
-rw-r--r--modules/mono/glue/cs_files/ResourceLoaderExtensions.cs10
-rwxr-xr-xmodules/mono/glue/cs_files/VERSION.txt2
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp4
-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_marshal.cpp173
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp12
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp15
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp169
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h39
-rw-r--r--modules/mono/utils/thread_local.cpp12
-rw-r--r--modules/mono/utils/thread_local.h17
-rw-r--r--modules/recast/navigation_mesh_generator.cpp6
-rw-r--r--modules/regex/regex.cpp8
-rw-r--r--modules/squish/image_compress_squish.cpp31
-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_editor.cpp491
-rw-r--r--modules/visual_script/visual_script_editor.h46
-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.cpp8
-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.cpp734
-rw-r--r--modules/visual_script/visual_script_property_selector.h93
-rw-r--r--modules/websocket/emws_client.cpp1
-rw-r--r--modules/websocket/lws_client.cpp9
-rw-r--r--modules/websocket/lws_peer.cpp71
-rw-r--r--modules/websocket/lws_peer.h13
-rw-r--r--modules/websocket/lws_server.cpp9
99 files changed, 5047 insertions, 1322 deletions
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index bfb452d109..b004641838 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;
}
}
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 54431f93f1..0857635492 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -169,7 +169,7 @@ real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
}
RID BulletPhysicsServer::space_create() {
- SpaceBullet *space = bulletnew(SpaceBullet(false));
+ SpaceBullet *space = bulletnew(SpaceBullet);
CreateThenReturnRID(space_owner, space);
}
@@ -567,9 +567,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);
@@ -710,6 +707,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);
@@ -853,6 +878,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);
@@ -879,11 +911,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) {
@@ -961,14 +993,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) {
@@ -983,6 +1017,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);
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index e931915bba..0e858ff311 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -225,6 +225,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);
@@ -259,10 +264,12 @@ public:
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 +284,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/register_types.cpp b/modules/bullet/register_types.cpp
index b75f7464ab..a76b0438b4 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -32,6 +32,7 @@
#include "bullet_physics_server.h"
#include "class_db.h"
+#include "project_settings.h"
/**
@author AndreaCatania
@@ -47,6 +48,9 @@ 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
}
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2fc96a77b5..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,8 +228,8 @@ 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:
@@ -229,11 +237,11 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
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;
}
}
}
@@ -241,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);
@@ -281,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);
}
@@ -401,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;
@@ -795,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;
}
}
@@ -827,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;
@@ -840,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 92e7c2df98..e4c1a5f9b5 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -304,7 +304,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]);
}
}
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..97228a972f 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"
@@ -325,7 +326,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 +339,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 +356,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 +485,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 +497,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 +553,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 +595,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) {
ghostPairCallback = bulletnew(btGhostPairCallback);
godotFilterCallback = bulletnew(GodotFilterCallback);
gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution;
+ gCalculateCombinedFrictionCallback = &calculateGodotCombinedFriction;
dynamicsWorld->setWorldUserInfo(this);
@@ -645,7 +656,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) {
@@ -754,19 +765,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
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index a6c2786878..6b86fc2f03 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -84,7 +84,7 @@ public:
};
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; }
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_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index 2150320c4a..f9744c72af 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);
@@ -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);
+}
- clear();
+String CSGShapeSpatialGizmoPlugin::get_name() const {
+ return "CSGShapes";
+}
+
+bool CSGShapeSpatialGizmoPlugin::is_selectable_when_hidden() const {
+ return true;
+}
- Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
- Ref<Material> material = create_material("shape_material", gizmo_color);
+void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
+
+ CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node());
+
+ p_gizmo->clear();
+
+ 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,7 +322,7 @@ 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)) {
@@ -303,7 +332,7 @@ void CSGShapeSpatialGizmo::redraw() {
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);
+ 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);
- }
-}
-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;
+ p_gizmo->add_handles(handles, handles_material);
}
-
- 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 67cc7e1ba2..9f2171a82a 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -177,7 +177,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 +200,7 @@ void CSGShape::_update_shape() {
}
}
- face_count[idx]++;
+ face_count.write[idx]++;
}
Vector<ShapeUpdateSurface> surfaces;
@@ -210,18 +210,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 +281,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 +290,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;
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..ddfa7af771 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;
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 116a86b27b..46b2a832f1 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -17,268 +17,19 @@ 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 c5b37d35b4..701a13d32f 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -9,9 +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/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml
new file mode 100644
index 0000000000..0ae54bc9c7
--- /dev/null
+++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PacketPeerGDNative" inherits="PacketPeer" 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/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml
new file mode 100644
index 0000000000..d86cd2c25a
--- /dev/null
+++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="StreamPeerGDNative" inherits="StreamPeer" 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/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/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_builders.py b/modules/gdnative/gdnative_builders.py
new file mode 100644
index 0000000000..8a1cd049af
--- /dev/null
+++ b/modules/gdnative/gdnative_builders.py
@@ -0,0 +1,261 @@
+"""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
+
+ 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):
+
+ 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/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/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 5e093109d5..0f3b497c94 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -149,7 +149,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 +556,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 +601,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;
@@ -1053,7 +1060,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, Set<int> *r_safe_lines) 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;
}
@@ -1154,8 +1161,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;
}
@@ -1170,7 +1177,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);
@@ -1196,7 +1203,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;
}
}
@@ -1205,7 +1212,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];
@@ -1218,7 +1225,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);
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 1b39b63ad9..688ed295db 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -295,7 +295,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, Set<int> *r_safe_lines = 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, 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/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index 816b0f0cab..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, Set<int> *r_safe_lines) 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 2443e31361..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, Set<int> *r_safe_lines = 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 d18297f2f8..ae1a218392 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -341,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;
}
@@ -374,7 +374,7 @@ void unregister_gdnative_types() {
continue;
}
- singleton_gdnatives[i]->terminate();
+ singleton_gdnatives.write[i]->terminate();
}
singleton_gdnatives.clear();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 8bd29ffc55..ef6a42f145 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -596,6 +596,13 @@ 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()) {
+ String msg = "Script warning: " + E->get().get_name() + " (" + path + ") line " + itos(E->get().line) + ": ";
+ msg += E->get().get_message();
+ WARN_PRINTS(msg);
+ }
+#endif
valid = true;
@@ -730,7 +737,7 @@ Error GDScript::load_byte_code(const String &p_path) {
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);
@@ -941,7 +948,7 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (!E->get().data_type.is_type(p_value)) {
return false; // Type mismatch
}
- members[E->get().index] = p_value;
+ members.write[E->get().index] = p_value;
}
return true;
}
@@ -1270,7 +1277,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 +1327,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();
@@ -1867,6 +1874,162 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
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;
@@ -1903,6 +2066,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() {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index d5fe7a000b..edad12f1f3 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -261,6 +261,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;
@@ -355,10 +398,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;
}
@@ -397,7 +440,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, Set<int> *r_safe_lines = 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/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index a428ccd306..fe393957db 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -140,7 +140,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::CLASS: {
result.kind = GDScriptDataType::GDSCRIPT;
- if (p_datatype.class_type->name == StringName()) {
+ if (!p_datatype.class_type->owner) {
result.script_type = Ref<GDScript>(main_script);
} else {
result.script_type = class_map[p_datatype.class_type->name];
@@ -482,7 +482,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
Variant script;
int idx = -1;
- if (cn->cast_type.class_type->name == StringName()) {
+ if (!cn->cast_type.class_type->owner) {
script = codegen.script;
} else {
StringName name = cn->cast_type.class_type->name;
@@ -785,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;
@@ -818,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;
@@ -850,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;
@@ -859,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;
@@ -1181,7 +1181,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
Variant script;
int idx = -1;
- if (assign_type.class_type->name == StringName()) {
+ if (!assign_type.class_type->owner) {
script = codegen.script;
} else {
StringName name = assign_type.class_type->name;
@@ -1361,10 +1361,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo
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();
} break;
@@ -1393,16 +1393,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;
@@ -1453,7 +1453,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();
@@ -1479,7 +1479,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: {
@@ -1689,7 +1689,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
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[i] = _gdtype_from_datatype(p_func->argument_types[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 {
@@ -1708,11 +1708,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 {
@@ -1726,7 +1726,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();
@@ -1741,7 +1741,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();
}
@@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->_signals[name] = p_class->_signals[i].arguments;
}
- if (p_class->name != StringName()) {
+ if (!p_class->owner) {
parsed_classes.insert(p_class->name);
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2e4a4c40dd..abd56d2757 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -116,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, Set<int> *r_safe_lines) 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, 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();
@@ -2442,7 +2455,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
- Variant::get_numeric_constants_for_type(parser.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());
}
@@ -2918,7 +2931,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
}
//print_line(itos(indent_stack.size())+","+itos(tc)+": "+l);
- lines[i] = l;
+ lines.write[i] = l;
}
p_code = "";
@@ -3065,7 +3078,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
case GDScriptParser::DataType::BUILTIN: {
base_type.has_type = false;
- if (Variant::has_numeric_constant(base_type.builtin_type, p_symbol)) {
+ 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;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 6a08d86904..bae3f48923 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: {
@@ -742,13 +742,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+#ifdef DEBUG_ENABLED
if (src->get_type() != var_type) {
- 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;
+ 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
}
-
- *dst = *src;
+#endif // DEBUG_ENABLED
ip += 4;
}
@@ -761,17 +770,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
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 *();
- GD_ERR_BREAK(!src_obj);
- if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ 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;
@@ -785,6 +799,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
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);
@@ -820,6 +835,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK;
}
}
+#endif // DEBUG_ENABLED
*dst = *src;
@@ -1218,7 +1234,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;
@@ -1579,7 +1595,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 {
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index 7e98b6ced9..f2e52d48dd 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -1098,7 +1098,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()];
}
}
@@ -1412,7 +1412,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) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ac53f33e9e..e0ed2b332b 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -38,6 +38,7 @@
#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>
@@ -56,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) {
@@ -607,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) &&
@@ -642,7 +645,7 @@ 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;
}
@@ -726,7 +729,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
BlockNode *b = current_block;
- while (b) {
+ while (!bfn && b) {
if (b->variables.has(identifier)) {
IdentifierNode *id = alloc_node<IdentifierNode>();
LocalVarNode *lv = b->variables[identifier];
@@ -736,6 +739,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr = id;
bfn = true;
+#ifdef DEBUG_ENABLED
switch (tokenizer->get_token()) {
case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
@@ -747,15 +751,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
case GDScriptTokenizer::TK_OP_ASSIGN_SUB: {
- if (lv->assignments == 0 && !lv->datatype.has_type) {
- _set_error("Using assignment with operation on a variable that was never assigned.");
- return NULL;
+ 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;
@@ -785,6 +797,32 @@ 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;
@@ -1411,8 +1449,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);
}
@@ -1466,7 +1504,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);
@@ -1502,7 +1540,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);
}
@@ -1526,7 +1564,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;
}
@@ -1556,10 +1594,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;
}
@@ -1592,7 +1630,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;
@@ -1620,7 +1658,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];
@@ -2601,6 +2639,7 @@ 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:
@@ -2609,13 +2648,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
// will check later
} break;
default: {
- // TODO: Make this a warning
- /*if (p_block->has_return) {
- _set_error("Unreacheable code.");
- return;
- }*/
+ 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();
@@ -2728,6 +2767,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
c->line = var_line;
assigned = c;
}
+ lv->assign = assigned;
//must be added later, to avoid self-referencing.
p_block->variables.insert(n, lv);
@@ -2745,6 +2785,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
lv->assign_op = op;
lv->assign = assigned;
+ lv->assign_op = op;
+
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
return;
@@ -3332,6 +3374,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: {
@@ -3510,6 +3555,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]):' ).");
@@ -3521,6 +3577,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
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();
@@ -3547,12 +3606,18 @@ 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 (!_parse_type(argtype)) {
+ 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;
}
@@ -3697,7 +3762,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
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;
@@ -3724,6 +3791,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) {
@@ -4187,6 +4256,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();
@@ -4406,6 +4476,7 @@ 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)) {
@@ -4421,7 +4492,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
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;
@@ -4546,6 +4630,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
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);
@@ -5433,6 +5518,9 @@ GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Oper
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);
@@ -5678,11 +5766,26 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
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;
@@ -5693,6 +5796,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
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;
@@ -5727,6 +5831,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
}
} 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);
}
@@ -5853,6 +5960,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
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
@@ -5871,10 +5984,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
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
}
-
- // TODO: Warn if types aren't compatible
-
} break;
// Assignment should never happen within an expression
case OperatorNode::OP_ASSIGN:
@@ -5937,6 +6051,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
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);
@@ -5956,7 +6075,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
id->name = cn->value.operator StringName();
op->op = OperatorNode::OP_INDEX_NAMED;
- op->arguments[1] = id;
+ op->arguments.write[1] = id;
return _reduce_node_type(op);
}
@@ -6323,7 +6442,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
Vector<DataType> par_types;
par_types.resize(p_call->arguments.size() - 1);
for (int i = 1; i < p_call->arguments.size(); i++) {
- par_types[i - 1] = _reduce_node_type(p_call->arguments[i]);
+ par_types.write[i - 1] = _reduce_node_type(p_call->arguments[i]);
}
if (error_set) return DataType();
@@ -6356,6 +6475,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
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
}
}
@@ -6389,6 +6517,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
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;
@@ -6415,6 +6550,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
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;
@@ -6494,8 +6636,18 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
_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
+#endif // DEBUG_ENABLED
return DataType();
}
@@ -6511,7 +6663,19 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
_set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line);
return DataType();
}
-#endif
+
+ // 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;
}
@@ -6536,8 +6700,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
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])) {
@@ -6549,6 +6720,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
} 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
}
}
@@ -6770,7 +6947,35 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
// Check classes in current file
ClassNode *base = NULL;
if (!p_base_type) {
- // Possibly this is a global, check first
+ 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;
@@ -6796,6 +7001,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
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;
@@ -6885,31 +7093,23 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType
}
}
- // Nothing found, keep looking in local scope
-
- 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 = *p_base_type;
- if (base_type.kind == DataType::CLASS) {
- base = base_type.class_type;
- }
- }
-
- DataType member_type;
-
- if (_get_member_type(base_type, p_identifier, member_type)) {
- return member_type;
- }
-
- if (!p_base_type) {
// 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();
}
@@ -6949,7 +7149,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
// Class variables
for (int i = 0; i < p_class->variables.size(); i++) {
- ClassNode::Member &v = p_class->variables[i];
+ ClassNode::Member &v = p_class->variables.write[i];
DataType tmp;
if (_get_member_type(p_class->base_type, v.identifier, tmp)) {
@@ -6993,7 +7193,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
convert_call->arguments.push_back(tgt_type);
v.expression = convert_call;
- v.initial_assignment->arguments[1] = convert_call;
+ v.initial_assignment->arguments.write[1] = convert_call;
}
}
@@ -7131,11 +7331,9 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
// Arguments
int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
for (int i = 0; i < p_function->arguments.size(); i++) {
-
- // Resolve types
- p_function->argument_types[i] = _resolve_type(p_function->argument_types[i], p_function->line);
-
- if (i >= defaults_ofs) {
+ 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;
@@ -7150,17 +7348,30 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
DataType def_type = _reduce_node_type(op->arguments[1]);
- 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);
+ 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) {
+ _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;
@@ -7171,18 +7382,44 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
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;
- valid = valid && p_function->default_values.size() >= default_arg_count;
- valid = valid && arg_types.size() == p_function->arguments.size();
+ 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) {
- _set_error("Function signature doesn't match the parent.", p_function->line);
+ 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);
@@ -7200,6 +7437,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
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;
}
}
@@ -7226,6 +7464,20 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
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];
@@ -7235,6 +7487,26 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *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;
@@ -7253,8 +7525,23 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
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)) {
@@ -7284,7 +7571,12 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
convert_call->arguments.push_back(tgt_type);
lv->assign = convert_call;
- lv->assign_op->arguments[1] = 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) {
@@ -7299,15 +7591,6 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
_mark_line_as_unsafe(lv->line);
}
}
- last_var_assign = lv->assign;
-
- // TODO: Make a warning
- /*
- if (lv->assignments == 0) {
- _set_error("Variable '" + String(lv->name) + "' is never assigned.", lv->line);
- return;
- }
- */
} break;
case Node::TYPE_OPERATOR: {
OperatorNode *op = static_cast<OperatorNode *>(statement);
@@ -7373,6 +7656,19 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
} 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
@@ -7402,7 +7698,12 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
convert_call->arguments.push_back(op->arguments[1]);
convert_call->arguments.push_back(tgt_type);
- op->arguments[1] = convert_call;
+ 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)) {
@@ -7412,15 +7713,29 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
case OperatorNode::OP_CALL:
case OperatorNode::OP_PARENT_CALL: {
_mark_line_as_safe(op->line);
- _reduce_function_call_type(op);
+ 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
- // TODO: Make this a warning
- /*_set_error("Standalone expression, nothing is done in this line.", statement->line);
- return; */
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
}
}
} break;
@@ -7487,9 +7802,9 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
default: {
_mark_line_as_safe(statement->line);
_reduce_node_type(statement); // Test for safety anyway
- // TODO: Make this a warning
- /* _set_error("Standalone expression, nothing is done in this line.", statement->line);
- return; */
+#ifdef DEBUG_ENABLED
+ _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
+#endif // DEBUG_ENABLED
}
}
}
@@ -7501,6 +7816,18 @@ void GDScriptParser::_check_block_types(BlockNode *p_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->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) {
@@ -7514,6 +7841,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;
@@ -7580,6 +7957,37 @@ Error GDScriptParser::_parse(const String &p_base_path) {
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;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 48f256b4c6..d8ee4e8159 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -38,6 +38,7 @@
#include "script_language.h"
struct GDScriptDataType;
+struct GDScriptWarning;
class GDScriptParser {
public:
@@ -57,6 +58,7 @@ public:
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;
@@ -95,6 +97,7 @@ public:
is_constant(false),
is_meta_type(false),
infer_type(false),
+ may_yield(false),
builtin_type(Variant::NIL),
class_type(NULL) {}
};
@@ -160,6 +163,7 @@ public:
Node *expression;
OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode;
+ int usages;
};
struct Constant {
Node *expression;
@@ -169,6 +173,8 @@ public:
struct Signal {
StringName name;
Vector<StringName> arguments;
+ int emissions;
+ int line;
};
Vector<ClassNode *> subclasses;
@@ -197,12 +203,16 @@ 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; }
@@ -212,6 +222,7 @@ public:
_static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
has_yield = false;
+ has_unreachable_code = false;
}
};
@@ -267,6 +278,7 @@ public:
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; }
@@ -275,6 +287,7 @@ public:
assign = NULL;
assign_op = NULL;
assignments = 0;
+ usages = 0;
}
};
@@ -518,6 +531,10 @@ private:
Set<int> *safe_lines;
#endif // DEBUG_ENABLED
+#ifdef DEBUG_ENABLED
+ List<GDScriptWarning> warnings;
+#endif // DEBUG_ENABLED
+
int pending_newline;
List<int> tab_level;
@@ -550,6 +567,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);
@@ -605,6 +626,9 @@ public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
+#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 = "");
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 940bdcbc8d..537a0c5eaf 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -526,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");
@@ -535,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++;
@@ -1045,6 +1061,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();
@@ -1172,15 +1191,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);
@@ -1193,7 +1212,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);
@@ -1218,10 +1237,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--;
}
@@ -1320,15 +1339,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
@@ -1360,7 +1379,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 5bd303224c..28a08bfaf8 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"
@@ -171,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(){};
};
@@ -190,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() {
@@ -217,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();
@@ -232,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 {
@@ -265,6 +282,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 Vector<Pair<int, String> >(); }
+ virtual const Set<String> &get_warning_global_skips() const { return Set<String>(); }
+ virtual const bool is_ignoring_warnings() const { return true; }
+#endif // DEBUG_ENABLED
GDScriptTokenizerBuffer();
};
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 234a59e516..0c5df57d49 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -508,7 +508,7 @@ 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);
@@ -758,7 +758,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());
}
}
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 40540a857f..d11d7a7ec7 100755
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -20,6 +20,8 @@ if env['builtin_mbedtls']:
"camellia.c",
"ccm.c",
"certs.c",
+ "chacha20.c",
+ "chachapoly.c",
"cipher.c",
"cipher_wrap.c",
"cmac.c",
@@ -37,6 +39,7 @@ if env['builtin_mbedtls']:
"error.c",
"gcm.c",
"havege.c",
+ "hkdf.c",
"hmac_drbg.c",
"md2.c",
"md4.c",
@@ -45,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",
@@ -57,6 +61,7 @@ if env['builtin_mbedtls']:
"pkwrite.c",
"platform.c",
"platform_util.c",
+ "poly1305.c",
"ripemd160.c",
"rsa.c",
"rsa_internal.c",
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..2ec00aa72d 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);
};
diff --git a/modules/mobile_vr/mobile_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index bb84281b46..7b2344f1fe 100644
--- a/modules/mobile_vr/mobile_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -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/mono/SCsub b/modules/mono/SCsub
index c69a3c9ba6..a2df83925c 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -61,6 +61,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()
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 9a000a2a72..7f226443a1 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -83,6 +83,7 @@ def configure(env):
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
if env['platform'] == 'windows':
+ mono_root = None
if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 62a6b96bb5..7d7028a7a6 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -121,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
}
@@ -523,7 +523,7 @@ 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;
@@ -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
@@ -1609,7 +1617,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
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
@@ -1963,8 +1971,6 @@ 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));
@@ -1981,13 +1987,15 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
// 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);
}
+ ERR_FAIL_COND_V(!valid, NULL);
+
if (native) {
String native_name = native->get_name();
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -2040,6 +2048,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);
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 7f9732c297..363ae59d22 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -292,7 +292,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, Set<int> *r_safe_lines = NULL) 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;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 6fa317ee70..76907451e7 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)
@@ -731,7 +729,7 @@ 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 "));
+ output.push_back(itype.is_singleton ? "static partial class " : (is_abstract ? "abstract partial class " : "partial class "));
output.push_back(itype.proxy_name);
if (itype.is_singleton) {
@@ -1338,7 +1336,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 +1650,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 "[");
@@ -2344,7 +2341,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);
@@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#undef INSERT_ARRAY
+ // Array
+ itype = TypeInterface();
+ itype.name = "Array";
+ itype.cname = itype.name;
+ itype.proxy_name = "Array";
+ 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 = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Array(%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 = "Dictionary";
+ itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
+ itype.c_type_in = itype.c_type + "*";
+ itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
+ itype.cs_out = "return new Dictionary(%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)
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
new file mode 100644
index 0000000000..0551c1991a
--- /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_checked(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_checked(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.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
+ mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
+
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
+ mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
+ mono_add_internal_call("Godot.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/Array.cs b/modules/mono/glue/cs_files/Array.cs
new file mode 100644
index 0000000000..51f57daef4
--- /dev/null
+++ b/modules/mono/glue/cs_files/Array.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ 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();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index e0d6d27840..1195071bd3 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -293,28 +293,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)Math.Round(r * 255);
+ c <<= 8;
+ c |= (byte)Math.Round(g * 255);
+ c <<= 8;
+ 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)(r * 255);
+ c |= (byte)Math.Round(g * 255);
c <<= 8;
- c |= (byte)(g * 255);
+ c |= (byte)Math.Round(b * 255);
c <<= 8;
- c |= (byte)(b * 255);
+ 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 +405,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 +455,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..57a1960ad9
--- /dev/null
+++ b/modules/mono/glue/cs_files/Dictionary.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ 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();
+ }
+ }
+}
diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs
index e2457ff98b..43de9156f2 100644
--- a/modules/mono/glue/cs_files/GD.cs
+++ b/modules/mono/glue/cs_files/GD.cs
@@ -64,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);
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index ff4477cc6c..6ad4b3dcb2 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -1,36 +1,17 @@
using System;
-using System.Collections.Generic;
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/NodeExtensions.cs b/modules/mono/glue/cs_files/NodeExtensions.cs
new file mode 100644
index 0000000000..71534d7782
--- /dev/null
+++ b/modules/mono/glue/cs_files/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/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs
new file mode 100644
index 0000000000..ceecc589e6
--- /dev/null
+++ b/modules/mono/glue/cs_files/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/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt
index 7ed6ff82de..7f8f011eb7 100755
--- a/modules/mono/glue/cs_files/VERSION.txt
+++ b/modules/mono/glue/cs_files/VERSION.txt
@@ -1 +1 @@
-5
+7
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/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 0f4e211be5..f564b93f8f 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -77,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++;
}
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_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 5cd77d63e2..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,66 +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);
-
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- 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;
- MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- 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 c8df1038ce..630bda8b4e 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -105,9 +105,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
}
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
if (exc) {
ret = NULL;
@@ -121,9 +119,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
return ret;
} else {
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc);
if (exc) {
if (r_exc) {
@@ -144,9 +140,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
if (exc) {
ret = NULL;
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 1f837a2d78..ce66e0c8db 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -140,15 +140,10 @@ bool GDMonoProperty::has_setter() {
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);
-
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
-
+ GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
@@ -160,9 +155,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoEx
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc);
if (exc) {
if (r_exc) {
@@ -175,9 +168,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoExcept
MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
MonoException *exc = NULL;
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc);
- GD_MONO_END_RUNTIME_INVOKE;
+ MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc);
if (exc) {
ret = NULL;
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index a229552b76..911d629956 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -87,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;
@@ -107,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
@@ -134,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;
@@ -170,11 +176,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;
}
@@ -198,6 +206,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_CLASS(Array));
+ CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED
@@ -224,34 +234,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_CLASS(Array)->get_method_thunk("GetPtr", 0));
+ CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(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;
@@ -304,6 +301,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));
}
@@ -358,7 +361,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;
}
@@ -368,7 +371,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)));
@@ -380,13 +383,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);
@@ -400,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain;
}
-String get_exception_name_and_message(MonoException *p_ex) {
+String get_exception_name_and_message(MonoException *p_exc) {
String res;
- MonoClass *klass = mono_object_get_class((MonoObject *)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);
@@ -413,12 +476,20 @@ String get_exception_name_and_message(MonoException *p_ex) {
res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)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 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);
@@ -517,4 +588,38 @@ void set_pending_exception(MonoException *p_exc) {
_THREAD_LOCAL_(int)
current_invoke_count = 0;
+MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) {
+ 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 4f8e5932cd..bf8860c85a 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -41,14 +41,24 @@
#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 {
@@ -79,6 +89,8 @@ struct MonoCache {
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
+ GDMonoClass *class_KeyNotFoundException;
+
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@@ -100,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
@@ -127,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;
@@ -175,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);
@@ -183,10 +201,13 @@ 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(MonoException *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);
@@ -209,6 +230,14 @@ _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 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
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL)))
diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp
index 6f8b0f90bc..ae9f130518 100644
--- a/modules/mono/utils/thread_local.cpp
+++ b/modules/mono/utils/thread_local.cpp
@@ -63,7 +63,13 @@ struct ThreadLocalStorage::Impl {
#endif
}
- Impl(void (*p_destr_callback_func)(void *)) {
+#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);
@@ -89,10 +95,12 @@ void ThreadLocalStorage::set_value(void *p_value) const {
pimpl->set_value(p_value);
}
-void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) {
+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
index 7ff10b4efc..783e40dc01 100644
--- a/modules/mono/utils/thread_local.h
+++ b/modules/mono/utils/thread_local.h
@@ -65,12 +65,18 @@
#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 (*p_dest_callback)(void *));
+ void alloc(void (_CALLBACK_FUNC_ *p_dest_callback)(void *));
void free();
private:
@@ -85,17 +91,10 @@ class ThreadLocal {
T init_val;
-#ifdef WINDOWS_ENABLED
-#define _CALLBACK_FUNC_ __stdcall
-#else
-#define _CALLBACK_FUNC_
-#endif
-
static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
memdelete(static_cast<T *>(tls_data));
}
-#undef _CALLBACK_FUNC_
T *_tls_get_value() const {
void *tls_data = storage.get_value();
@@ -156,6 +155,8 @@ private:
bool &flag;
};
+#undef _CALLBACK_FUNC_
+
#define _TLS_RECURSION_GUARD_V_(m_ret) \
static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
if (_recursion_flag_) \
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/regex.cpp b/modules/regex/regex.cpp
index 6f2bb46fc8..733f32277b 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -265,8 +265,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);
@@ -295,8 +295,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..f6be537413 100644
--- a/modules/squish/image_compress_squish.cpp
+++ b/modules/squish/image_compress_squish.cpp
@@ -46,7 +46,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);
@@ -96,6 +96,33 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) {
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);
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index af9fea6681..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, Set<int> *r_safe_lines) 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 b163203a3a..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, Set<int> *r_safe_lines = 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_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 873cc293c9..45a27d1e79 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"
@@ -1293,7 +1295,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 +1329,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 +1786,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 +1923,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 +1968,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 +1986,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 +2441,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 +2521,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 +2727,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 +2905,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 +2929,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 +3032,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 +3150,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 +3174,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 +3454,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 +3487,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() {
@@ -3480,25 +3651,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");
@@ -3515,9 +3682,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 0bd64d6a1d..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;
@@ -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..ad886bc758 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--;
}
@@ -1055,7 +1055,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..e4dfc5fe45
--- /dev/null
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -0,0 +1,734 @@
+/*************************************************************************/
+/* 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(TTR("Get")) + String(" ") + E->get().name;
+ String get_text = get_text_raw.capitalize();
+
+ String set_text_raw = String(TTR("Set ")) + String(" ") + 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;
+ print_line("name: " + E->get().name);
+ 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/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 1405fa98b0..00c36ebb47 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -64,7 +64,6 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
String str = "ws://";
String proto_string = "";
- int i = 0;
if (p_ssl)
str = "wss://";
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index 06f97aaf05..ac31daa108 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -127,11 +127,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;
@@ -142,10 +137,6 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
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();
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..bb724bce9c 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -92,11 +92,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 +106,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
}