diff options
Diffstat (limited to 'modules')
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> ¶ms) { 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 } |