diff options
Diffstat (limited to 'modules')
84 files changed, 1489 insertions, 700 deletions
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 315afe3d72..7bc731e75e 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -1471,6 +1471,22 @@ bool BulletPhysicsServer::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis return generic_6dof_joint->get_flag(p_axis, p_flag); } +void BulletPhysicsServer::generic_6dof_joint_set_precision(RID p_joint, int p_precision) { + JointBullet *joint = joint_owner.get(p_joint); + ERR_FAIL_COND(!joint); + ERR_FAIL_COND(joint->get_type() != JOINT_6DOF); + Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint); + generic_6dof_joint->set_precision(p_precision); +} + +int BulletPhysicsServer::generic_6dof_joint_get_precision(RID p_joint) { + JointBullet *joint = joint_owner.get(p_joint); + ERR_FAIL_COND_V(!joint, 0); + ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, 0); + Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint); + return generic_6dof_joint->get_precision(); +} + void BulletPhysicsServer::free(RID p_rid) { if (shape_owner.owns(p_rid)) { diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 6b7dcd86e6..0cea3f5ba6 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -375,6 +375,9 @@ public: virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable); virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag); + virtual void generic_6dof_joint_set_precision(RID p_joint, int precision); + virtual int generic_6dof_joint_get_precision(RID p_joint); + /* MISC */ virtual void free(RID p_rid); @@ -397,6 +400,8 @@ public: virtual void flush_queries(); virtual void finish(); + virtual bool is_flushing_queries() const { return false; } + virtual int get_process_info(ProcessInfo p_info); CollisionObjectBullet *get_collisin_object(RID p_object) const; diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 402a276f95..441fa7c8af 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -304,7 +304,11 @@ bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) { } void RigidCollisionObjectBullet::shape_changed(int p_shape_index) { - bulletdelete(shapes.write[p_shape_index].bt_shape); + ShapeWrapper &shp = shapes.write[p_shape_index]; + if (shp.bt_shape == mainShape) { + mainShape = NULL; + } + bulletdelete(shp.bt_shape); reload_shapes(); } @@ -366,5 +370,8 @@ void RigidCollisionObjectBullet::body_scale_changed() { void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) { ShapeWrapper &shp = shapes.write[p_index]; shp.shape->remove_owner(this, p_permanentlyFromThisBody); + if (shp.bt_shape == mainShape) { + mainShape = NULL; + } bulletdelete(shp.bt_shape); } diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index a36f1123bc..812dcd2d56 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -135,6 +135,15 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis] = p_value; break; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING: + sixDOFConstraint->getTranslationalLimitMotor()->m_springDamping.m_floats[p_axis] = p_value; + break; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: + sixDOFConstraint->getTranslationalLimitMotor()->m_springStiffness.m_floats[p_axis] = p_value; + break; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: + sixDOFConstraint->getTranslationalLimitMotor()->m_equilibriumPoint.m_floats[p_axis] = p_value; + break; case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: limits_lower[1][p_axis] = p_value; set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter @@ -143,6 +152,9 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO limits_upper[1][p_axis] = p_value; set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT]); // Reload bullet parameter break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION: + sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_bounce = p_value; + break; case PhysicsServer::G6DOF_JOINT_ANGULAR_ERP: sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_stopERP = p_value; break; @@ -152,6 +164,15 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce = p_value; break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: + sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springStiffness = p_value; + break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: + sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springDamping = p_value; + break; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: + sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_equilibriumPoint = p_value; + break; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); WARN_DEPRECATED @@ -170,6 +191,12 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6 return sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis]; case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: return sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis]; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_DAMPING: + return sixDOFConstraint->getTranslationalLimitMotor()->m_springDamping.m_floats[p_axis]; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: + return sixDOFConstraint->getTranslationalLimitMotor()->m_springStiffness.m_floats[p_axis]; + case PhysicsServer::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: + return sixDOFConstraint->getTranslationalLimitMotor()->m_equilibriumPoint.m_floats[p_axis]; case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: return limits_lower[1][p_axis]; case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: @@ -182,6 +209,12 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6 return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_targetVelocity; case PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_maxMotorForce; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: + return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springStiffness; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: + return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_springDamping; + case PhysicsServer::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: + return sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_equilibriumPoint; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); WARN_DEPRECATED; @@ -215,6 +248,12 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag]; break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING: + sixDOFConstraint->getTranslationalLimitMotor()->m_enableSpring[p_axis] = p_value; + break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING: + sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableSpring = p_value; + break; default: ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated"); WARN_DEPRECATED @@ -224,6 +263,13 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const { ERR_FAIL_INDEX_V(p_axis, 3, false); - return flags[p_axis][p_flag]; } + +void Generic6DOFJointBullet::set_precision(int p_precision) { + sixDOFConstraint->setOverrideNumSolverIterations(MAX(1, p_precision)); +} + +int Generic6DOFJointBullet::get_precision() const { + return sixDOFConstraint->getOverrideNumSolverIterations(); +} diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h index 176127ed6c..848c3a10cd 100644 --- a/modules/bullet/generic_6dof_joint_bullet.h +++ b/modules/bullet/generic_6dof_joint_bullet.h @@ -68,6 +68,9 @@ public: void set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag, bool p_value); bool get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const; + + void set_precision(int p_precision); + int get_precision() const; }; #endif diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 3b44ab838e..0117bb375f 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -102,6 +102,9 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con } btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) { + if (count >= m_resultMax) + return 1; // not used by bullet + CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(convexResult.m_hitCollisionObject->getUserPointer()); PhysicsDirectSpaceState::ShapeResult &result = m_results[count]; @@ -172,6 +175,9 @@ btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::Loc } bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { + if (m_count >= m_resultMax) + return false; + const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask); if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); @@ -249,6 +255,8 @@ bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *pr } btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint &cp, const btCollisionObjectWrapper *colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper *colObj1Wrap, int partId1, int index1) { + if (m_count >= m_resultMax) + return 1; // not used by bullet if (m_self_object == colObj0Wrap->getCollisionObject()) { B_TO_G(cp.m_localPointA, m_results[m_count * 2 + 0]); // Local contact diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 37e7718969..9dd04100ed 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -268,6 +268,7 @@ RigidBodyBullet::RigidBodyBullet() : can_integrate_forces(false), maxCollisionsDetection(0), collisionsCount(0), + prev_collision_count(0), maxAreasWhereIam(10), areaWhereIamCount(0), countGravityPointSpaces(0), @@ -293,6 +294,9 @@ RigidBodyBullet::RigidBodyBullet() : areasWhereIam.write[i] = NULL; } btBody->setSleepingThresholds(0.2, 0.2); + + prev_collision_traces = &collision_traces_1; + curr_collision_traces = &collision_traces_2; } RigidBodyBullet::~RigidBodyBullet() { @@ -410,7 +414,14 @@ void RigidBodyBullet::on_collision_filters_change() { } void RigidBodyBullet::on_collision_checker_start() { + + prev_collision_count = collisionsCount; collisionsCount = 0; + + // Swap array + Vector<RigidBodyBullet *> *s = prev_collision_traces; + prev_collision_traces = curr_collision_traces; + curr_collision_traces = s; } void RigidBodyBullet::on_collision_checker_end() { @@ -433,10 +444,20 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const cd.other_object_shape = p_other_shape_index; cd.local_shape = p_local_shape_index; + curr_collision_traces->write[collisionsCount] = p_otherObject; + ++collisionsCount; return true; } +bool RigidBodyBullet::was_colliding(RigidBodyBullet *p_other_object) { + for (int i = prev_collision_count - 1; 0 <= i; --i) { + if ((*prev_collision_traces)[i] == p_other_object) + return true; + } + return false; +} + void RigidBodyBullet::assert_no_constraints() { if (btBody->getNumConstraintRefs()) { WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body."); @@ -797,7 +818,10 @@ void RigidBodyBullet::reload_shapes() { const btScalar mass = invMass == 0 ? 0 : 1 / invMass; if (mainShape) { - btVector3 inertia; + // inertia initialised zero here because some of bullet's collision + // shapes incorrectly do not set the vector in calculateLocalIntertia. + // Arbitrary zero is preferable to undefined behaviour. + btVector3 inertia(0, 0, 0); mainShape->calculateLocalInertia(mass, inertia); btBody->setMassProps(mass, inertia); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 26e5018c87..0696073d21 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -205,9 +205,15 @@ private: bool can_integrate_forces; Vector<CollisionData> collisions; + Vector<RigidBodyBullet *> collision_traces_1; + Vector<RigidBodyBullet *> collision_traces_2; + Vector<RigidBodyBullet *> *prev_collision_traces; + Vector<RigidBodyBullet *> *curr_collision_traces; + // these parameters are used to avoid vector resize int maxCollisionsDetection; int collisionsCount; + int prev_collision_count; Vector<AreaBullet *> areasWhereIam; // these parameters are used to avoid vector resize @@ -244,9 +250,17 @@ public: virtual void on_collision_checker_end(); void set_max_collisions_detection(int p_maxCollisionsDetection) { + + ERR_FAIL_COND(0 > p_maxCollisionsDetection); + maxCollisionsDetection = p_maxCollisionsDetection; + collisions.resize(p_maxCollisionsDetection); + collision_traces_1.resize(p_maxCollisionsDetection); + collision_traces_2.resize(p_maxCollisionsDetection); + collisionsCount = 0; + prev_collision_count = MIN(prev_collision_count, p_maxCollisionsDetection); } int get_max_collisions_detection() { return maxCollisionsDetection; @@ -254,6 +268,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, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index); + bool was_colliding(RigidBodyBullet *p_other_object); void assert_no_constraints(); diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index ab2d1781ad..fed12cd5ed 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -786,16 +786,22 @@ void SpaceBullet::check_body_collision() { } const int numContacts = contactManifold->getNumContacts(); + + /// Since I don't need report all contacts for these objects, + /// So report only the first #define REPORT_ALL_CONTACTS 0 #if REPORT_ALL_CONTACTS for (int j = 0; j < numContacts; j++) { btManifoldPoint &pt = contactManifold->getContactPoint(j); #else - // Since I don't need report all contacts for these objects, I'll report only the first if (numContacts) { btManifoldPoint &pt = contactManifold->getContactPoint(0); #endif - if (pt.getDistance() <= 0.0) { + if ( + pt.getDistance() <= 0.0 || + bodyA->was_colliding(bodyB) || + bodyB->was_colliding(bodyA)) { + Vector3 collisionWorldPosition; Vector3 collisionLocalPosition; Vector3 normalOnB; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 4eb798de11..4e35014459 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -47,6 +47,7 @@ void CSGShape::set_use_collision(bool p_enable) { PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); _make_dirty(); //force update } else { PhysicsServer::get_singleton()->free(root_collision_instance); @@ -159,6 +160,72 @@ CSGBrush *CSGShape::_get_brush() { return brush; } +int CSGShape::mikktGetNumFaces(const SMikkTSpaceContext *pContext) { + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + return surface.vertices.size() / 3; +} + +int CSGShape::mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) { + // always 3 + return 3; +} + +void CSGShape::mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert) { + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + Vector3 v = surface.verticesw[iFace * 3 + iVert]; + fvPosOut[0] = v.x; + fvPosOut[1] = v.y; + fvPosOut[2] = v.z; +} + +void CSGShape::mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert) { + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + Vector3 n = surface.normalsw[iFace * 3 + iVert]; + fvNormOut[0] = n.x; + fvNormOut[1] = n.y; + fvNormOut[2] = n.z; +} + +void CSGShape::mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert) { + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + Vector2 t = surface.uvsw[iFace * 3 + iVert]; + fvTexcOut[0] = t.x; + fvTexcOut[1] = t.y; +} + +void CSGShape::mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert) { + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + int i = (iFace * 3 + iVert) * 4; + + surface.tansw[i++] = fvTangent[0]; + surface.tansw[i++] = fvTangent[1]; + surface.tansw[i++] = fvTangent[2]; + surface.tansw[i++] = fSign; +} + +void CSGShape::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert) { + + ShapeUpdateSurface &surface = *((ShapeUpdateSurface *)pContext->m_pUserData); + + int i = iFace * 3 + iVert; + Vector3 normal = surface.normalsw[i]; + Vector3 tangent = Vector3(fvTangent[0], fvTangent[1], fvTangent[2]); + Vector3 bitangent = Vector3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]); + float d = bitangent.dot(normal.cross(tangent)); + + i *= 4; + surface.tansw[i++] = tangent.x; + surface.tansw[i++] = tangent.y; + surface.tansw[i++] = tangent.z; + surface.tansw[i++] = d < 0 ? -1 : 1; +} + void CSGShape::_update_shape() { if (parent) @@ -211,6 +278,9 @@ void CSGShape::_update_shape() { 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); + if (calculate_tangents) { + surfaces.write[i].tans.resize(face_count[i] * 3 * 4); + } surfaces.write[i].last_added = 0; if (i != surfaces.size() - 1) { @@ -220,6 +290,9 @@ void CSGShape::_update_shape() { 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(); + if (calculate_tangents) { + surfaces.write[i].tansw = surfaces.write[i].tans.write(); + } } //fill arrays @@ -274,9 +347,19 @@ void CSGShape::_update_shape() { normal = -normal; } - surfaces[idx].verticesw[last + order[j]] = v; - surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j]; - surfaces[idx].normalsw[last + order[j]] = normal; + int k = last + order[j]; + surfaces[idx].verticesw[k] = v; + surfaces[idx].uvsw[k] = n->faces[i].uvs[j]; + surfaces[idx].normalsw[k] = normal; + + if (calculate_tangents) { + // zero out our tangents for now + k *= 4; + surfaces[idx].tansw[k++] = 0.0; + surfaces[idx].tansw[k++] = 0.0; + surfaces[idx].tansw[k++] = 0.0; + surfaces[idx].tansw[k++] = 0.0; + } } surfaces.write[idx].last_added += 3; @@ -287,20 +370,43 @@ void CSGShape::_update_shape() { //create surfaces for (int i = 0; i < surfaces.size(); i++) { + // calculate tangents for this surface + bool have_tangents = calculate_tangents; + if (have_tangents) { + SMikkTSpaceInterface mkif; + mkif.m_getNormal = mikktGetNormal; + mkif.m_getNumFaces = mikktGetNumFaces; + mkif.m_getNumVerticesOfFace = mikktGetNumVerticesOfFace; + mkif.m_getPosition = mikktGetPosition; + mkif.m_getTexCoord = mikktGetTexCoord; + mkif.m_setTSpace = mikktSetTSpaceDefault; + mkif.m_setTSpaceBasic = NULL; + + SMikkTSpaceContext msc; + msc.m_pInterface = &mkif; + msc.m_pUserData = &surfaces.write[i]; + have_tangents = genTangSpaceDefault(&msc); + } + // unset write access surfaces.write[i].verticesw = PoolVector<Vector3>::Write(); surfaces.write[i].normalsw = PoolVector<Vector3>::Write(); surfaces.write[i].uvsw = PoolVector<Vector2>::Write(); + surfaces.write[i].tansw = PoolVector<float>::Write(); if (surfaces[i].last_added == 0) continue; + // and convert to surface array Array array; array.resize(Mesh::ARRAY_MAX); array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices; array[Mesh::ARRAY_NORMAL] = surfaces[i].normals; array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs; + if (have_tangents) { + array[Mesh::ARRAY_TANGENT] = surfaces[i].tans; + } int idx = root_mesh->get_surface_count(); root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); @@ -363,6 +469,7 @@ void CSGShape::_notification(int p_what) { PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id()); } _make_dirty(); @@ -400,6 +507,15 @@ CSGShape::Operation CSGShape::get_operation() const { return operation; } +void CSGShape::set_calculate_tangents(bool p_calculate_tangents) { + calculate_tangents = p_calculate_tangents; + _make_dirty(); +} + +bool CSGShape::is_calculating_tangents() const { + return calculate_tangents; +} + void CSGShape::_validate_property(PropertyInfo &property) const { if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) { //hide collision if not root @@ -421,9 +537,13 @@ void CSGShape::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); + ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents); + ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents); + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents"); BIND_ENUM_CONSTANT(OPERATION_UNION); BIND_ENUM_CONSTANT(OPERATION_INTERSECTION); @@ -438,6 +558,7 @@ CSGShape::CSGShape() { use_collision = false; operation = OPERATION_UNION; snap = 0.001; + calculate_tangents = true; } CSGShape::~CSGShape() { @@ -1545,6 +1666,24 @@ CSGBrush *CSGPolygon::_build_brush() { Path *path = NULL; Ref<Curve3D> curve; + // get bounds for our polygon + Vector2 final_polygon_min; + Vector2 final_polygon_max; + for (int i = 0; i < final_polygon.size(); i++) { + Vector2 p = final_polygon[i]; + if (i == 0) { + final_polygon_min = p; + final_polygon_max = final_polygon_min; + } else { + if (p.x < final_polygon_min.x) final_polygon_min.x = p.x; + if (p.y < final_polygon_min.y) final_polygon_min.y = p.y; + + if (p.x > final_polygon_max.x) final_polygon_max.x = p.x; + if (p.y > final_polygon_max.y) final_polygon_max.y = p.y; + } + } + Vector2 final_polygon_size = final_polygon_max - final_polygon_min; + if (mode == MODE_PATH) { if (!has_node(path_node)) return NULL; @@ -1636,6 +1775,10 @@ CSGBrush *CSGPolygon::_build_brush() { v.z -= depth; } facesw[face * 3 + k] = v; + uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; + if (i == 0) { + uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ + } } smoothw[face] = false; @@ -1767,6 +1910,7 @@ CSGBrush *CSGPolygon::_build_brush() { Vector2 p = final_polygon[triangles[j + src[k]]]; Vector3 v = Vector3(p.x, p.y, 0); facesw[face * 3 + k] = v; + uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; } smoothw[face] = false; @@ -1784,6 +1928,8 @@ CSGBrush *CSGPolygon::_build_brush() { Vector2 p = final_polygon[triangles[j + src[k]]]; Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x); facesw[face * 3 + k] = v; + uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; + uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ } smoothw[face] = false; @@ -1869,10 +2015,10 @@ CSGBrush *CSGPolygon::_build_brush() { }; Vector2 u[4] = { - Vector2(u1, 0), Vector2(u1, 1), - Vector2(u2, 1), - Vector2(u2, 0) + Vector2(u1, 0), + Vector2(u2, 0), + Vector2(u2, 1) }; // face 1 @@ -1915,6 +2061,7 @@ CSGBrush *CSGPolygon::_build_brush() { Vector2 p = final_polygon[triangles[j + src[k]]]; Vector3 v = Vector3(p.x, p.y, 0); facesw[face * 3 + k] = xf.xform(v); + uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; } smoothw[face] = false; @@ -1932,6 +2079,8 @@ CSGBrush *CSGPolygon::_build_brush() { Vector2 p = final_polygon[triangles[j + src[k]]]; Vector3 v = Vector3(p.x, p.y, 0); facesw[face * 3 + k] = xf.xform(v); + uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size; + uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */ } smoothw[face] = false; @@ -1956,6 +2105,9 @@ CSGBrush *CSGPolygon::_build_brush() { } else { aabb.expand_to(facesw[i]); } + + // invert UVs on the Y-axis OpenGL = upside down + uvsw[i].y = 1.0 - uvsw[i].y; } } diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 6898cdaf64..0a4bb5f665 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -36,6 +36,7 @@ #include "csg.h" #include "scene/3d/visual_instance.h" #include "scene/resources/concave_polygon_shape.h" +#include "thirdparty/misc/mikktspace.h" class CSGShape : public VisualInstance { GDCLASS(CSGShape, VisualInstance); @@ -63,6 +64,8 @@ private: Ref<ConcavePolygonShape> root_collision_shape; RID root_collision_instance; + bool calculate_tangents; + Ref<ArrayMesh> root_mesh; struct Vector3Hasher { @@ -78,14 +81,26 @@ private: PoolVector<Vector3> vertices; PoolVector<Vector3> normals; PoolVector<Vector2> uvs; + PoolVector<float> tans; Ref<Material> material; int last_added; PoolVector<Vector3>::Write verticesw; PoolVector<Vector3>::Write normalsw; PoolVector<Vector2>::Write uvsw; + PoolVector<float>::Write tansw; }; + //mikktspace callbacks + static int mikktGetNumFaces(const SMikkTSpaceContext *pContext); + static int mikktGetNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace); + static void mikktGetPosition(const SMikkTSpaceContext *pContext, float fvPosOut[], const int iFace, const int iVert); + static void mikktGetNormal(const SMikkTSpaceContext *pContext, float fvNormOut[], const int iFace, const int iVert); + static void mikktGetTexCoord(const SMikkTSpaceContext *pContext, float fvTexcOut[], const int iFace, const int iVert); + static void mikktSetTSpaceBasic(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); + static void mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert); + void _update_shape(); protected: @@ -115,6 +130,9 @@ public: void set_snap(float p_snap); float get_snap() const; + void set_calculate_tangents(bool p_calculate_tangents); + bool is_calculating_tangents() const; + bool is_root_shape() const; CSGShape(); ~CSGShape(); diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml index 90621b94f4..ac3c2342fc 100644 --- a/modules/csg/doc_classes/CSGShape.xml +++ b/modules/csg/doc_classes/CSGShape.xml @@ -20,6 +20,9 @@ </method> </methods> <members> + <member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents"> + Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child. + </member> <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation"> The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. </member> diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index f9d1ed9dc5..ff18a3ae69 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -87,20 +87,20 @@ def _build_gdnative_api_struct_header(api): ret_val = [] if core['next']: ret_val += generate_core_extension_struct(core['next']) - + ret_val += [ 'typedef struct godot_gdnative_core_' + ('{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + '_api_struct {', '\tunsigned int type;', '\tgodot_gdnative_api_version version;', '\tconst godot_gdnative_api_struct *next;', ] - + for funcdef in core['api']: args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - + ret_val += ['} godot_gdnative_core_' + '{0}_{1}'.format(core['version']['major'], core['version']['minor']) + '_api_struct;', ''] - + return ret_val @@ -171,26 +171,26 @@ def _build_gdnative_api_struct_source(api): ret_val += ['};\n'] return ret_val - - + + def get_core_struct_definition(core): ret_val = [] - + if core['next']: ret_val += get_core_struct_definition(core['next']) - + ret_val += [ 'extern const godot_gdnative_core_' + ('{0}_{1}_api_struct api_{0}_{1}'.format(core['version']['major'], core['version']['minor'])) + ' = {', '\tGDNATIVE_' + core['type'] + ',', '\t{' + str(core['version']['major']) + ', ' + str(core['version']['minor']) + '},', '\t' + ('NULL' if not core['next'] else ('(const godot_gdnative_api_struct *)& api_{0}_{1}'.format(core['version']['major'], core['version']['minor']))) + ',' ] - + for funcdef in core['api']: ret_val.append('\t%s,' % funcdef['name']) - + ret_val += ['};\n'] - + return ret_val for ext in api['extensions']: @@ -204,7 +204,7 @@ def _build_gdnative_api_struct_source(api): out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,'] out += ['};\n'] - + if api['core']['next']: out += get_core_struct_definition(api['core']['next']) diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index eb133502c4..bcaf3f346e 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -294,6 +294,10 @@ MethodInfo NativeScript::get_method_info(const StringName &p_method) const { return MethodInfo(); } +bool NativeScript::is_valid() const { + return true; +} + bool NativeScript::is_tool() const { NativeScriptDesc *script_data = get_script_desc(); diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 51370f5fbf..e6f3c06ee5 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -160,6 +160,7 @@ public: virtual MethodInfo get_method_info(const StringName &p_method) const; virtual bool is_tool() const; + virtual bool is_valid() const; virtual ScriptLanguage *get_language() const; diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 31c6c4d67f..3ade8ac004 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -102,6 +102,7 @@ public: PropertyInfo get_property_info(const StringName &p_property) const; bool is_tool() const { return _tool; } + bool is_valid() const { return true; } virtual ScriptLanguage *get_language() const; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 159085df34..538249c8e2 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -421,31 +421,40 @@ bool GDScript::_update_exports() { base_cache = Ref<GDScript>(); } - if (c->extends_used && String(c->extends_file) != "" && String(c->extends_file) != get_path()) { - - String path = c->extends_file; - if (path.is_rel_path()) { - - String base = get_path(); - if (base == "" || base.is_rel_path()) { - - ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); - } else { - path = base.get_base_dir().plus_file(path); + if (c->extends_used) { + String path = ""; + if (String(c->extends_file) != "" && String(c->extends_file) != get_path()) { + path = c->extends_file; + if (path.is_rel_path()) { + + String base = get_path(); + if (base == "" || base.is_rel_path()) { + + ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); + } else { + path = base.get_base_dir().plus_file(path); + } } + } else if (c->extends_class.size() != 0) { + String base = c->extends_class[0]; + + if (ScriptServer::is_global_class(base)) + path = ScriptServer::get_global_class_path(base); } - if (path != get_path()) { + if (path != "") { + if (path != get_path()) { - Ref<GDScript> bf = ResourceLoader::load(path); + Ref<GDScript> bf = ResourceLoader::load(path); - if (bf.is_valid()) { + if (bf.is_valid()) { - base_cache = bf; - bf->inheriters_cache.insert(get_instance_id()); + base_cache = bf; + bf->inheriters_cache.insert(get_instance_id()); + } + } else { + ERR_PRINT(("Path extending itself in " + path).utf8().get_data()); } - } else { - ERR_PRINT(("Path extending itself in " + path).utf8().get_data()); } } @@ -858,7 +867,6 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { else if (base_cache.is_valid()) { return base_cache->has_script_signal(p_signal); } - #endif return false; } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index f344beba9f..752d660ffb 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -141,7 +141,7 @@ protected: static void _bind_methods(); public: - bool is_valid() const { return valid; } + virtual bool is_valid() const { return valid; } const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; } const Map<StringName, Variant> &get_constants() const { return constants; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 45319c59e7..caa7fbfeca 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -132,7 +132,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D result.kind = GDScriptDataType::SCRIPT; result.script_type = p_datatype.script_type; result.native_type = result.script_type->get_instance_base_type(); - } + } break; case GDScriptParser::DataType::GDSCRIPT: { result.kind = GDScriptDataType::GDSCRIPT; result.script_type = p_datatype.script_type; @@ -1603,13 +1603,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo return OK; } -Error GDScriptCompiler::_parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) { +Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) { Vector<int> bytecode; CodeGen codegen; codegen.class_node = p_class; - codegen.script = p_script.ptr(); + codegen.script = p_script; codegen.function_node = p_func; codegen.stack_max = 0; codegen.current_line = 0; @@ -1853,7 +1853,7 @@ Error GDScriptCompiler::_parse_function(Ref<GDScript> p_script, const GDScriptPa return OK; } -Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { if (p_class->owner && p_class->owner->owner) { // Owner is not root @@ -1887,7 +1887,7 @@ Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_script->initializer = NULL; p_script->subclasses.clear(); - p_script->_owner = p_owner.ptr(); + p_script->_owner = p_owner; p_script->tool = p_class->tool; p_script->name = p_class->name; @@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> StringName name = p_class->_signals[i].name; - GDScript *c = p_script.ptr(); + GDScript *c = p_script; while (c) { @@ -2054,7 +2054,7 @@ Error GDScriptCompiler::_parse_class_level(Ref<GDScript> p_script, Ref<GDScript> return OK; } -Error GDScriptCompiler::_parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { //parse methods bool has_initializer = false; @@ -2159,7 +2159,7 @@ Error GDScriptCompiler::_parse_class_blocks(Ref<GDScript> p_script, const GDScri return OK; } -void GDScriptCompiler::_make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { Map<StringName, Ref<GDScript> > old_subclasses; @@ -2178,20 +2178,20 @@ void GDScriptCompiler::_make_scripts(const Ref<GDScript> p_script, const GDScrip subclass.instance(); } - subclass->_owner = const_cast<GDScript *>(p_script.ptr()); + subclass->_owner = const_cast<GDScript *>(p_script); class_map.insert(name, subclass); _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state); } } -Error GDScriptCompiler::compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state) { +Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { err_line = -1; err_column = -1; error = ""; parser = p_parser; - main_script = p_script.ptr(); + main_script = p_script; const GDScriptParser::Node *root = parser->get_parse_tree(); ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 23c6450aa7..55f5e2b48e 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -148,17 +148,17 @@ class GDScriptCompiler { int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level); int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false); Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1); - Error _parse_function(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); - Error _parse_class_level(Ref<GDScript> p_script, Ref<GDScript> p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - Error _parse_class_blocks(Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); - void _make_scripts(const Ref<GDScript> p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); + Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); int err_line; int err_column; StringName source; String error; public: - Error compile(const GDScriptParser *p_parser, Ref<GDScript> p_script, bool p_keep_state = false); + Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false); String get_error() const; int get_error_line() const; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 31115a4bd9..cd6c21a629 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -370,7 +370,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } script = p_instance->script.ptr(); } else { - script = this->_script.ptr(); + script = _script; } } @@ -477,56 +477,53 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, 3); #ifdef DEBUG_ENABLED - if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) { - - err_text = "Left operand of 'is' is not an instance of anything."; - OPCODE_BREAK; - } if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) { err_text = "Right operand of 'is' is not a class."; OPCODE_BREAK; } #endif - Object *obj_A = *a; - Object *obj_B = *b; - - GDScript *scr_B = Object::cast_to<GDScript>(obj_B); bool extends_ok = false; + if (a->get_type() == Variant::OBJECT && a->operator Object *() != NULL) { + Object *obj_A = *a; + Object *obj_B = *b; - if (scr_B) { - //if B is a script, the only valid condition is that A has an instance which inherits from the script - //in other situation, this shoul return false. + GDScript *scr_B = Object::cast_to<GDScript>(obj_B); - if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) { + if (scr_B) { + //if B is a script, the only valid condition is that A has an instance which inherits from the script + //in other situation, this shoul return false. - GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr()); - //bool found=false; - while (cmp) { + if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) { - if (cmp == scr_B) { - //inherits from script, all ok - extends_ok = true; - break; - } + GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr()); + //bool found=false; + while (cmp) { + + if (cmp == scr_B) { + //inherits from script, all ok + extends_ok = true; + break; + } - cmp = cmp->_base; + cmp = cmp->_base; + } } - } - } else { + } else { - GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); + GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B); #ifdef DEBUG_ENABLED - if (!nc) { + if (!nc) { - err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; - OPCODE_BREAK; - } + err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; + OPCODE_BREAK; + } #endif - extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); + extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name()); + } } *dst = extends_ok; @@ -1185,7 +1182,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, argc + 3); - const GDScript *gds = _script.ptr(); + const GDScript *gds = _script; const Map<StringName, GDScriptFunction *>::Element *E = NULL; while (gds->base.ptr()) { @@ -1256,7 +1253,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.stack_size = _stack_size; gdfs->state.self = self; gdfs->state.alloca_size = alloca_size; - gdfs->state.script = _script; + gdfs->state.script = Ref<GDScript>(_script); gdfs->state.ip = ip + ipofs; gdfs->state.line = line; gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0; @@ -1763,22 +1760,14 @@ GDScriptFunction::~GDScriptFunction() { Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error) { -#ifdef DEBUG_ENABLED - // Checking this first since it's faster - if (!state.script.is_valid()) { - ERR_EXPLAIN("Resumed after yield, but script is gone"); - ERR_FAIL_V(Variant()); - } - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { +#ifdef DEBUG_ENABLED ERR_EXPLAIN("Resumed after yield, but class instance is gone"); ERR_FAIL_V(Variant()); - } #else - if (!is_valid()) { return Variant(); - } #endif + } Variant arg; r_error.error = Variant::CallError::CALL_OK; @@ -1845,9 +1834,6 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { return false; if (p_extended_check) { - //script gone? (checking this first since it's faster) - if (!state.script.is_valid()) - return false; //class instance gone? if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) return false; @@ -1859,18 +1845,14 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const { Variant GDScriptFunctionState::resume(const Variant &p_arg) { ERR_FAIL_COND_V(!function, Variant()); -#ifdef DEBUG_ENABLED - // Checking this first since it's faster - if (!state.script.is_valid()) { - ERR_EXPLAIN("Resumed after yield, but script is gone"); - ERR_FAIL_V(Variant()); - } - if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) { +#ifdef DEBUG_ENABLED ERR_EXPLAIN("Resumed after yield, but class instance is gone"); ERR_FAIL_V(Variant()); - } +#else + return Variant(); #endif + } state.result = p_arg; Variant::CallError err; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index a47070de4f..5509edf24a 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -225,7 +225,7 @@ private: bool _static; MultiplayerAPI::RPCMode rpc_mode; - Ref<GDScript> _script; + GDScript *_script; StringName name; Vector<Variant> constants; @@ -297,7 +297,7 @@ public: int get_default_argument_addr(int p_idx) const; GDScriptDataType get_return_type() const; GDScriptDataType get_argument_type(int p_idx) const; - GDScript *get_script() const { return const_cast<GDScript *>(_script.ptr()); } + GDScript *get_script() const { return _script; } StringName get_source() const { return source; } void debug_get_stack_member_state(int p_line, List<Pair<StringName, int> > *r_stackvars) const; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 09b3a5631f..0926c1a1ab 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3752,6 +3752,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { BlockNode *block = alloc_node<BlockNode>(); block->parent_class = p_class; + FunctionNode *function = alloc_node<FunctionNode>(); + function->name = name; + function->arguments = arguments; + function->argument_types = argument_types; + function->default_values = default_values; + function->_static = _static; + function->line = fnline; +#ifdef DEBUG_ENABLED + function->arguments_usage = arguments_usage; +#endif // DEBUG_ENABLED + function->rpc_mode = rpc_mode; + rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; + if (name == "_init") { if (_static) { @@ -3782,7 +3795,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { parenthesis++; while (true) { + current_function = function; Node *arg = _parse_and_reduce_expression(p_class, _static); + current_function = NULL; cparent->arguments.push_back(arg); if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { @@ -3826,19 +3841,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - FunctionNode *function = alloc_node<FunctionNode>(); - function->name = name; function->return_type = return_type; - function->arguments = arguments; - function->argument_types = argument_types; - function->default_values = default_values; - function->_static = _static; - function->line = fnline; -#ifdef DEBUG_ENABLED - function->arguments_usage = arguments_usage; -#endif // DEBUG_ENABLED - function->rpc_mode = rpc_mode; - rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; if (_static) p_class->static_functions.push_back(function); @@ -6432,6 +6435,10 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String StringName native; if (p_base_type.kind == DataType::GDSCRIPT) { base_gdscript = p_base_type.script_type; + if (base_gdscript.is_null() || !base_gdscript->is_valid()) { + // GDScript wasn't properly compÃled, don't bother trying + return false; + } } else if (p_base_type.kind == DataType::SCRIPT) { base_script = p_base_type.script_type; } else if (p_base_type.kind == DataType::NATIVE) { @@ -6472,6 +6479,12 @@ bool GDScriptParser::_get_function_signature(DataType &p_base_type, const String base_script = base_script->get_base_script(); } + if (native == StringName()) { + // Empty native class, might happen in some Script implementations + // Just ignore it + return false; + } + #ifdef DEBUG_METHODS_ENABLED // Only native remains @@ -6914,6 +6927,10 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN Ref<GDScript> gds; if (base_type.kind == DataType::GDSCRIPT) { gds = base_type.script_type; + if (gds.is_null() || !gds->is_valid()) { + // GDScript wasn't properly compÃled, don't bother trying + return false; + } } Ref<Script> scr; @@ -6976,6 +6993,12 @@ bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringN scr = scr->get_base_script(); } + if (native == StringName()) { + // Empty native class, might happen in some Script implementations + // Just ignore it + return false; + } + // Check ClassDB if (!ClassDB::class_exists(native)) { native = "_" + native.operator String(); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index fae88042af..126b49832a 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -802,7 +802,9 @@ void GridMapEditor::edit(GridMap *p_gridmap) { VisualServer::get_singleton()->instance_set_visible(grid_instance[i], false); } - VisualServer::get_singleton()->instance_set_visible(cursor_instance, false); + if (cursor_instance.is_valid()) { + VisualServer::get_singleton()->instance_set_visible(cursor_instance, false); + } return; } diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 0e5dd9b4cf..e1f5e2ef28 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -103,6 +103,16 @@ import os def find_nuget_unix(): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + import os.path import sys @@ -129,6 +139,16 @@ def find_nuget_unix(): def find_nuget_windows(): + import os + + if 'NUGET_PATH' in os.environ: + hint_path = os.environ['NUGET_PATH'] + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + hint_path = os.path.join(hint_path, 'nuget.exe') + if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): + return hint_path + import mono_reg_utils as monoreg mono_root = '' @@ -160,14 +180,6 @@ def find_nuget_windows(): if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): return hint_path - if 'NUGET_PATH' in os.environ: - hint_path = os.environ['NUGET_PATH'] - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - hint_path = os.path.join(hint_path, 'nuget.exe') - if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): - return hint_path - return None diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 700e518cfc..3c818898e6 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -50,6 +50,7 @@ #include "mono_gd/gd_mono_marshal.h" #include "signal_awaiter_utils.h" #include "utils/macros.h" +#include "utils/mutex_utils.h" #include "utils/string_utils.h" #include "utils/thread_local.h" @@ -378,60 +379,72 @@ static String variant_type_to_managed_name(const String &p_var_type_name) { return "object"; if (!ClassDB::class_exists(p_var_type_name)) { - Variant::Type var_types[] = { - Variant::BOOL, - Variant::INT, - Variant::REAL, - Variant::STRING, - Variant::VECTOR2, - Variant::RECT2, - Variant::VECTOR3, - Variant::TRANSFORM2D, - Variant::PLANE, - Variant::QUAT, - Variant::AABB, - Variant::BASIS, - Variant::TRANSFORM, - Variant::COLOR, - Variant::NODE_PATH, - Variant::_RID - }; - - for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { - if (p_var_type_name == Variant::get_type_name(var_types[i])) - return p_var_type_name; - } + return p_var_type_name; + } + + if (p_var_type_name == Variant::get_type_name(Variant::OBJECT)) + return "Godot.Object"; - if (p_var_type_name == "String") - return "string"; // I prefer this one >:[ + if (p_var_type_name == Variant::get_type_name(Variant::REAL)) { +#ifdef REAL_T_IS_DOUBLE + return "double"; +#else + return "float"; +#endif + } - // TODO these will be rewritten later into custom containers + if (p_var_type_name == Variant::get_type_name(Variant::STRING)) + return "string"; // I prefer this one >:[ - if (p_var_type_name == "Array") - return "object[]"; + if (p_var_type_name == Variant::get_type_name(Variant::DICTIONARY)) + return "Collections.Dictionary"; - if (p_var_type_name == "Dictionary") - return "Dictionary<object, object>"; + if (p_var_type_name == Variant::get_type_name(Variant::ARRAY)) + return "Collections.Array"; - if (p_var_type_name == "PoolByteArray") - return "byte[]"; - if (p_var_type_name == "PoolIntArray") - return "int[]"; - if (p_var_type_name == "PoolRealArray") - return "float[]"; - if (p_var_type_name == "PoolStringArray") - return "string[]"; - if (p_var_type_name == "PoolVector2Array") - return "Vector2[]"; - if (p_var_type_name == "PoolVector3Array") - return "Vector3[]"; - if (p_var_type_name == "PoolColorArray") - return "Color[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_BYTE_ARRAY)) + return "byte[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_INT_ARRAY)) + return "int[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_REAL_ARRAY)) { +#ifdef REAL_T_IS_DOUBLE + return "double[]"; +#else + return "float[]"; +#endif + } + if (p_var_type_name == Variant::get_type_name(Variant::POOL_STRING_ARRAY)) + return "string[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_VECTOR2_ARRAY)) + return "Vector2[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_VECTOR3_ARRAY)) + return "Vector3[]"; + if (p_var_type_name == Variant::get_type_name(Variant::POOL_COLOR_ARRAY)) + return "Color[]"; + + Variant::Type var_types[] = { + Variant::BOOL, + Variant::INT, + Variant::VECTOR2, + Variant::RECT2, + Variant::VECTOR3, + Variant::TRANSFORM2D, + Variant::PLANE, + Variant::QUAT, + Variant::AABB, + Variant::BASIS, + Variant::TRANSFORM, + Variant::COLOR, + Variant::NODE_PATH, + Variant::_RID + }; - return "object"; + for (int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) { + if (p_var_type_name == Variant::get_type_name(var_types[i])) + return p_var_type_name; } - return p_var_type_name; + return "object"; } String CSharpLanguage::make_function(const String &, const String &p_name, const PoolStringArray &p_args) const { @@ -507,8 +520,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoException *exc = NULL; - GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames); - MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc); + MonoArray *frames = invoke_method_thunk(CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames), p_stack_trace, (MonoObject **)&exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -532,7 +544,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; MonoString *method_decl; - get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); + invoke_method_thunk(get_sf_info, frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); @@ -561,10 +573,8 @@ void CSharpLanguage::frame() { MonoObject *task_scheduler = task_scheduler_handle->get_target(); if (task_scheduler) { - GDMonoUtils::GodotTaskScheduler_Activate thunk = CACHED_METHOD_THUNK(GodotTaskScheduler, Activate); - MonoException *exc = NULL; - thunk(task_scheduler, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(GodotTaskScheduler, Activate), task_scheduler, (MonoObject **)&exc); if (exc) { GDMonoUtils::debug_unhandled_exception(exc); @@ -599,24 +609,20 @@ void CSharpLanguage::reload_all_scripts() { #ifdef DEBUG_ENABLED -#ifndef NO_THREADS - lock->lock(); -#endif - List<Ref<CSharpScript> > scripts; - SelfList<CSharpScript> *elem = script_list.first(); - while (elem) { - if (elem->self()->get_path().is_resource_file()) { - scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to gdscript to avoid being erased by accident + { + SCOPED_MUTEX_LOCK(script_instances_mutex); + + SelfList<CSharpScript> *elem = script_list.first(); + while (elem) { + if (elem->self()->get_path().is_resource_file()) { + scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to gdscript to avoid being erased by accident + } + elem = elem->next(); } - elem = elem->next(); } -#ifndef NO_THREADS - lock->unlock(); -#endif - //as scripts are going to be reloaded, must proceed without locking here scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order @@ -625,6 +631,7 @@ void CSharpLanguage::reload_all_scripts() { E->get()->load_source_code(E->get()->get_path()); E->get()->reload(true); } + #endif } @@ -634,15 +641,17 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft #ifdef TOOLS_ENABLED MonoReloadNode::get_singleton()->restart_reload_timer(); - reload_assemblies_if_needed(p_soft_reload); + if (is_assembly_reloading_needed()) { + reload_assemblies(p_soft_reload); + } #endif } #ifdef TOOLS_ENABLED -void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { +bool CSharpLanguage::is_assembly_reloading_needed() { if (!gdmono->is_runtime_initialized()) - return; + return false; GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); @@ -660,164 +669,208 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { // 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 + return false; // No assembly to load } if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) - return; // Already up to date + return false; // Already up to date } else { if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) - return; // No assembly to load + return false; // 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 + return false; // 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 + return false; // The editor API assembly to load is invalidated -#ifndef NO_THREADS - lock->lock(); -#endif + return true; +} + +void CSharpLanguage::reload_assemblies(bool p_soft_reload) { + + if (!gdmono->is_runtime_initialized()) + return; + + // There is no soft reloading with Mono. It's always hard reloading. List<Ref<CSharpScript> > scripts; - SelfList<CSharpScript> *elem = script_list.first(); - while (elem) { - if (elem->self()->get_path().is_resource_file()) { + { + SCOPED_MUTEX_LOCK(script_instances_mutex); - scripts.push_back(Ref<CSharpScript>(elem->self())); //cast to CSharpScript to avoid being erased by accident + for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) { + if (elem->self()->get_path().is_resource_file()) { + // Cast to CSharpScript to avoid being erased by accident + scripts.push_back(Ref<CSharpScript>(elem->self())); + } } - elem = elem->next(); } -#ifndef NO_THREADS - lock->unlock(); -#endif + List<Ref<CSharpScript> > to_reload; - //when someone asks you why dynamically typed languages are easier to write.... + // As scripts are going to be reloaded, must proceed without locking here - Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload; + scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order - //as scripts are going to be reloaded, must proceed without locking here + for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { - scripts.sort_custom<CSharpScriptDepSort>(); //update in inheritance dependency order + Ref<CSharpScript> &script = E->get(); - for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) { + to_reload.push_back(script); - to_reload.insert(E->get(), Map<ObjectID, List<Pair<StringName, Variant> > >()); + // Script::instances are deleted during managed object disposal, which happens on domain finalize. + // Only placeholders are kept. Therefore we need to keep a copy before that happens. - if (!p_soft_reload) { + for (Set<Object *>::Element *E = script->instances.front(); E; E = E->next()) { + script->pending_reload_instances.insert(E->get()->get_instance_id()); + } - //save state and remove script from instances - Map<ObjectID, List<Pair<StringName, Variant> > > &map = to_reload[E->get()]; +#ifdef TOOLS_ENABLED + for (Set<PlaceHolderScriptInstance *>::Element *E = script->placeholders.front(); E; E = E->next()) { + script->pending_reload_instances.insert(E->get()->get_owner()->get_instance_id()); + } +#endif - while (E->get()->instances.front()) { - Object *obj = E->get()->instances.front()->get(); - //save instance info - List<Pair<StringName, Variant> > state; - if (obj->get_script_instance()) { + // FIXME: What about references? Need to keep them alive if only managed code references them. - obj->get_script_instance()->get_property_state(state); + // Save state and remove script from instances + Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state; - Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle; - if (gchandle.is_valid()) - gchandle->release(); + while (script->instances.front()) { + Object *obj = script->instances.front()->get(); + // Save instance info + CSharpScript::StateBackup state; - map[obj->get_instance_id()] = state; - obj->set_script(RefPtr()); - } - } + ERR_CONTINUE(!obj->get_script_instance()); - //same thing for placeholders - while (E->get()->placeholders.size()) { - - Object *obj = E->get()->placeholders.front()->get()->get_owner(); - //save instance info - List<Pair<StringName, Variant> > state; - if (obj->get_script_instance()) { - obj->get_script_instance()->get_property_state(state); - map[obj->get_instance_id()] = state; - obj->set_script(RefPtr()); - } else { - // no instance found. Let's remove it so we don't loop forever - E->get()->placeholders.erase(E->get()->placeholders.front()->get()); - } - } + // TODO: Proper state backup (Not only variants, serialize managed state of scripts) + obj->get_script_instance()->get_property_state(state.properties); - for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get()->pending_reload_state.front(); F; F = F->next()) { - map[F->key()] = F->get(); //pending to reload, use this one instead - } + Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle; + if (gchandle.is_valid()) + gchandle->release(); - E->get()->_clear(); + owners_map[obj->get_instance_id()] = state; + obj->set_script(RefPtr()); // Remove script and existing script instances (placeholder are not removed before domain reload) } + + script->_clear(); } + // Do domain reload if (gdmono->reload_scripts_domain() != OK) { // Failed to reload the scripts domain // Make sure to add the scripts back to their owners before returning - for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { - Ref<CSharpScript> scr = E->key(); - for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) { + Ref<CSharpScript> scr = E->get(); + + for (const Map<ObjectID, CSharpScript::StateBackup>::Element *F = scr->pending_reload_state.front(); F; F = F->next()) { Object *obj = ObjectDB::get_instance(F->key()); + if (!obj) continue; + + ObjectID obj_id = obj->get_instance_id(); + + // Use a placeholder for now to avoid losing the state when saving a scene + obj->set_script(scr.get_ref_ptr()); - // Save reload state for next time if not saved - if (!scr->pending_reload_state.has(obj->get_instance_id())) { - scr->pending_reload_state[obj->get_instance_id()] = F->get(); + + PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj); + obj->set_script_instance(placeholder); + + // Even though build didn't fail, this tells the placeholder to keep properties and + // it allows using property_set_fallback for restoring the state without a valid script. + placeholder->set_build_failed(true); + + // Restore Variant properties state, it will be kept by the placeholder until the next script reloading + for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) { + placeholder->property_set_fallback(G->get().first, G->get().second, NULL); } + + scr->pending_reload_state.erase(obj_id); } } return; } - for (Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > >::Element *E = to_reload.front(); E; E = E->next()) { + for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) { - Ref<CSharpScript> scr = E->key(); + Ref<CSharpScript> scr = E->get(); scr->exports_invalidated = true; scr->signals_invalidated = true; scr->reload(p_soft_reload); scr->update_exports(); - //restore state if saved - for (Map<ObjectID, List<Pair<StringName, Variant> > >::Element *F = E->get().front(); F; F = F->next()) { + { +#ifdef DEBUG_ENABLED + for (Set<ObjectID>::Element *F = scr->pending_reload_instances.front(); F; F = F->next()) { + ObjectID obj_id = F->get(); + Object *obj = ObjectDB::get_instance(obj_id); + + if (!obj) { + scr->pending_reload_state.erase(obj_id); + continue; + } - Object *obj = ObjectDB::get_instance(F->key()); - if (!obj) - continue; + ScriptInstance *si = obj->get_script_instance(); - if (!p_soft_reload) { - //clear it just in case (may be a pending reload state) - obj->set_script(RefPtr()); - } - obj->set_script(scr.get_ref_ptr()); - if (!obj->get_script_instance()) { - //failed, save reload state for next time if not saved - if (!scr->pending_reload_state.has(obj->get_instance_id())) { - scr->pending_reload_state[obj->get_instance_id()] = F->get(); +#ifdef TOOLS_ENABLED + if (si) { + // If the script instance is not null, then it must be a placeholder. + // Non-placeholder script instances are removed in godot_icall_Object_Disposed. + CRASH_COND(!si->is_placeholder()); + + if (scr->is_tool() || ScriptServer::is_scripting_enabled()) { + // Replace placeholder with a script instance + + CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id]; + + // Backup placeholder script instance state before replacing it with a script instance + obj->get_script_instance()->get_property_state(state_backup.properties); + + ScriptInstance *script_instance = scr->instance_create(obj); + + if (script_instance) { + scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si)); + obj->set_script_instance(script_instance); + } + + // TODO: Restore serialized state + + for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) { + script_instance->set(G->get().first, G->get().second); + } + + scr->pending_reload_state.erase(obj_id); + } + + continue; } - continue; - } +#else + CRASH_COND(si != NULL); +#endif + // Re-create script instance - if (scr->valid && scr->is_tool() && obj->get_script_instance()->is_placeholder()) { - // Script instance was a placeholder, but now the script was built successfully and is a tool script. - // We have to replace the placeholder with an actual C# script instance. - scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(obj->get_script_instance())); - ScriptInstance *script_instance = scr->instance_create(obj); - obj->set_script_instance(script_instance); // Not necessary as it's already done in instance_create, but just in case... - } + obj->set_script(scr.get_ref_ptr()); // will create the script instance as well + + // TODO: Restore serialized state + + for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) { + obj->get_script_instance()->set(G->get().first, G->get().second); + } - for (List<Pair<StringName, Variant> >::Element *G = F->get().front(); G; G = G->next()) { - obj->get_script_instance()->set(G->get().first, G->get().second); + scr->pending_reload_state.erase(obj_id); } +#endif - scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state + scr->pending_reload_instances.clear(); } - - //if instance states were saved, set them! } + // FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative. if (Engine::get_singleton()->is_editor_hint()) { EditorNode::get_singleton()->get_inspector()->update_tree(); NodeDock::singleton->update_lists(); @@ -940,27 +993,18 @@ void CSharpLanguage::set_language_index(int p_idx) { void CSharpLanguage::release_script_gchandle(Ref<MonoGCHandle> &p_gchandle) { - if (!p_gchandle->is_released()) { // Do not locking unnecessarily -#ifndef NO_THREADS - get_singleton()->script_gchandle_release_lock->lock(); -#endif - + if (!p_gchandle->is_released()) { // Do not lock unnecessarily + SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex); p_gchandle->release(); - -#ifndef NO_THREADS - get_singleton()->script_gchandle_release_lock->unlock(); -#endif } } -void CSharpLanguage::release_script_gchandle(MonoObject *p_pinned_expected_obj, Ref<MonoGCHandle> &p_gchandle) { +void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle) { - uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_pinned_expected_obj); // we might lock after this, so pin it + uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(p_expected_obj); // We might lock after this, so pin it - if (!p_gchandle->is_released()) { // Do not locking unnecessarily -#ifndef NO_THREADS - get_singleton()->script_gchandle_release_lock->lock(); -#endif + if (!p_gchandle->is_released()) { // Do not lock unnecessarily + SCOPED_MUTEX_LOCK(get_singleton()->script_gchandle_release_mutex); MonoObject *target = p_gchandle->get_target(); @@ -968,13 +1012,9 @@ void CSharpLanguage::release_script_gchandle(MonoObject *p_pinned_expected_obj, // already released and could have been replaced) or if we can't get its target MonoObject* // (which doesn't necessarily mean it was released, and we want it released in order to // avoid locking other threads unnecessarily). - if (target == p_pinned_expected_obj || target == NULL) { + if (target == p_expected_obj || target == NULL) { p_gchandle->release(); } - -#ifndef NO_THREADS - get_singleton()->script_gchandle_release_lock->unlock(); -#endif } MonoGCHandle::free_handle(pinned_gchandle); @@ -990,13 +1030,13 @@ CSharpLanguage::CSharpLanguage() { gdmono = NULL; #ifdef NO_THREADS - lock = NULL; - gchandle_bind_lock = NULL; - script_gchandle_release_lock = NULL; + script_instances_mutex = NULL; + script_gchandle_release_mutex = NULL; + language_bind_mutex = NULL; #else - lock = Mutex::create(); - script_bind_lock = Mutex::create(); - script_gchandle_release_lock = Mutex::create(); + script_instances_mutex = Mutex::create(); + script_gchandle_release_mutex = Mutex::create(); + language_bind_mutex = Mutex::create(); #endif lang_idx = -1; @@ -1006,19 +1046,19 @@ CSharpLanguage::~CSharpLanguage() { finish(); - if (lock) { - memdelete(lock); - lock = NULL; + if (script_instances_mutex) { + memdelete(script_instances_mutex); + script_instances_mutex = NULL; } - if (script_bind_lock) { - memdelete(script_bind_lock); - script_bind_lock = NULL; + if (language_bind_mutex) { + memdelete(language_bind_mutex); + language_bind_mutex = NULL; } - if (script_gchandle_release_lock) { - memdelete(script_gchandle_release_lock); - script_gchandle_release_lock = NULL; + if (script_gchandle_release_mutex) { + memdelete(script_gchandle_release_mutex); + script_gchandle_release_mutex = NULL; } singleton = NULL; @@ -1055,15 +1095,12 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) { script_binding.wrapper_class = type_class; // cache script_binding.gchandle = MonoGCHandle::create_strong(mono_object); -#ifndef NO_THREADS - script_bind_lock->lock(); -#endif - - void *data = (void *)script_bindings.insert(p_object, script_binding); + void *data; -#ifndef NO_THREADS - script_bind_lock->unlock(); -#endif + { + SCOPED_MUTEX_LOCK(language_bind_mutex); + data = (void *)script_bindings.insert(p_object, script_binding); + } // Tie managed to unmanaged Reference *ref = Object::cast_to<Reference>(p_object); @@ -1093,23 +1130,19 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { if (finalizing) return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there -#ifndef NO_THREADS - script_bind_lock->lock(); -#endif - - Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; + { + SCOPED_MUTEX_LOCK(language_bind_mutex); - // Set the native instance field to IntPtr.Zero, if not yet garbage collected - MonoObject *mono_object = data->value().gchandle->get_target(); - if (mono_object) { - CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); - } + Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; - script_bindings.erase(data); + // Set the native instance field to IntPtr.Zero, if not yet garbage collected + MonoObject *mono_object = data->value().gchandle->get_target(); + if (mono_object) { + CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, NULL); + } -#ifndef NO_THREADS - script_bind_lock->unlock(); -#endif + script_bindings.erase(data); + } } void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) { @@ -1524,7 +1557,7 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f } else { r_owner_deleted = false; CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle); - if (p_is_finalizer) { + if (p_is_finalizer && !GDMono::get_singleton()->is_finalizing_scripts_domain()) { // If the native instance is still alive, then it was // referenced from another thread before the finalizer could // unreference it and delete it, so we want to keep it. @@ -1651,6 +1684,8 @@ void CSharpInstance::notification(int p_notification) { // It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed // to be sent at least once, which happens right before the call to the destructor. + predelete_notified = true; + if (base_ref) { // It's not safe to proceed if the owner derives Reference and the refcount reached 0. // At this point, Dispose() was already called (manually or from the finalizer) so @@ -1666,10 +1701,8 @@ void CSharpInstance::notification(int p_notification) { MonoObject *mono_object = get_mono_object(); ERR_FAIL_NULL(mono_object); - GDMonoUtils::GodotObject_Dispose thunk = CACHED_METHOD_THUNK(GodotObject, Dispose); - MonoException *exc = NULL; - thunk(mono_object, (MonoObject **)&exc); + GDMonoUtils::dispose(mono_object, &exc); if (exc) { GDMonoUtils::set_pending_exception(exc); @@ -1720,12 +1753,35 @@ CSharpInstance::CSharpInstance() : owner(NULL), base_ref(false), ref_dying(false), - unsafe_referenced(false) { + unsafe_referenced(false), + predelete_notified(false), + destructing_script_instance(false) { } CSharpInstance::~CSharpInstance() { if (gchandle.is_valid()) { + if (!predelete_notified && !ref_dying) { + // This destructor is not called from the owners destructor. + // This could be being called from the owner's set_script_instance method, + // meaning this script is being replaced with another one. If this is the case, + // we must call Dispose here, because Dispose calls owner->set_script_instance(NULL) + // and that would mess up with the new script instance if called later. + + MonoObject *mono_object = gchandle->get_target(); + + if (mono_object) { + MonoException *exc = NULL; + destructing_script_instance = true; + GDMonoUtils::dispose(mono_object, &exc); + destructing_script_instance = false; + + if (exc) { + GDMonoUtils::set_pending_exception(exc); + } + } + } + gchandle->release(); // Make sure it's released } @@ -1734,9 +1790,7 @@ CSharpInstance::~CSharpInstance() { } if (script.is_valid() && owner) { -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->lock(); -#endif + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); #ifdef DEBUG_ENABLED // CSharpInstance must not be created unless it's going to be added to the list for sure @@ -1746,10 +1800,6 @@ CSharpInstance::~CSharpInstance() { #else script->instances.erase(owner); #endif - -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->unlock(); -#endif } } @@ -1882,10 +1932,8 @@ bool CSharpScript::_update_exports() { // Dispose the temporary managed instance - GDMonoUtils::GodotObject_Dispose thunk = CACHED_METHOD_THUNK(GodotObject, Dispose); - MonoException *exc = NULL; - thunk(tmp_object, (MonoObject **)&exc); + GDMonoUtils::dispose(tmp_object, &exc); if (exc) { ERR_PRINT("Exception thrown from method Dispose() of temporary MonoObject:"); @@ -2312,17 +2360,13 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg ERR_FAIL_V(NULL); } - uint32_t pinned_gchandle = MonoGCHandle::new_strong_handle_pinned(mono_object); // we might lock after this, so pin it - -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->lock(); -#endif - - instances.insert(instance->owner); + // Tie managed to unmanaged + instance->gchandle = MonoGCHandle::create_strong(mono_object); -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->unlock(); -#endif + { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); + instances.insert(instance->owner); + } CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner); @@ -2330,13 +2374,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); ctor->invoke(mono_object, p_args); - // Tie managed to unmanaged - instance->gchandle = MonoGCHandle::create_strong(mono_object); - /* STEP 3, PARTY */ - MonoGCHandle::free_handle(pinned_gchandle); - //@TODO make thread safe return instance; } @@ -2411,17 +2450,8 @@ PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_t bool CSharpScript::instance_has(const Object *p_this) const { -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->lock(); -#endif - - bool ret = instances.has((Object *)p_this); - -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->unlock(); -#endif - - return ret; + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); + return instances.has((Object *)p_this); } bool CSharpScript::has_source_code() const { @@ -2454,15 +2484,11 @@ bool CSharpScript::has_method(const StringName &p_method) const { Error CSharpScript::reload(bool p_keep_state) { -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->lock(); -#endif - - bool has_instances = instances.size(); - -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->unlock(); -#endif + bool has_instances; + { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); + has_instances = instances.size(); + } ERR_FAIL_COND_V(!p_keep_state && has_instances, ERR_ALREADY_IN_USE); @@ -2648,35 +2674,19 @@ CSharpScript::CSharpScript() : _resource_path_changed(); #ifdef DEBUG_ENABLED - -#ifndef NO_THREADS - CSharpLanguage::get_singleton()->lock->lock(); -#endif - - CSharpLanguage::get_singleton()->script_list.add(&script_list); - -#ifndef NO_THREADS - CSharpLanguage::get_singleton()->lock->unlock(); + { + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); + CSharpLanguage::get_singleton()->script_list.add(&this->script_list); + } #endif - -#endif // DEBUG_ENABLED } CSharpScript::~CSharpScript() { #ifdef DEBUG_ENABLED - -#ifndef NO_THREADS - CSharpLanguage::get_singleton()->lock->lock(); + SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->script_instances_mutex); + CSharpLanguage::get_singleton()->script_list.remove(&this->script_list); #endif - - CSharpLanguage::get_singleton()->script_list.remove(&script_list); - -#ifndef NO_THREADS - CSharpLanguage::get_singleton()->lock->unlock(); -#endif - -#endif // DEBUG_ENABLED } /*************** RESOURCE ***************/ diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index fc604df2a0..501e0d9d6d 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -82,6 +82,21 @@ class CSharpScript : public Script { Set<Object *> instances; +#ifdef DEBUG_ENABLED + Set<ObjectID> pending_reload_instances; +#endif + + struct StateBackup { + // TODO + // Replace with buffer containing the serialized state of managed scripts. + // Keep variant state backup to use only with script instance placeholders. + List<Pair<StringName, Variant> > properties; + }; + +#ifdef TOOLS_ENABLED + Map<ObjectID, CSharpScript::StateBackup> pending_reload_state; +#endif + String source; StringName name; @@ -105,10 +120,6 @@ class CSharpScript : public Script { virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder); #endif -#ifdef DEBUG_ENABLED - Map<ObjectID, List<Pair<StringName, Variant> > > pending_reload_state; -#endif - Map<StringName, PropertyInfo> member_info; void _clear(); @@ -158,6 +169,8 @@ public: virtual void update_exports(); virtual bool is_tool() const { return tool; } + virtual bool is_valid() const { return valid; } + virtual Ref<Script> get_base_script() const; virtual ScriptLanguage *get_language() const; @@ -184,6 +197,8 @@ class CSharpInstance : public ScriptInstance { bool base_ref; bool ref_dying; bool unsafe_referenced; + bool predelete_notified; + bool destructing_script_instance; Ref<CSharpScript> script; Ref<MonoGCHandle> gchandle; @@ -204,6 +219,8 @@ class CSharpInstance : public ScriptInstance { public: MonoObject *get_mono_object() const; + _FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; } + virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; virtual void get_property_list(List<PropertyInfo> *p_properties) const; @@ -253,11 +270,9 @@ class CSharpLanguage : public ScriptLanguage { GDMono *gdmono; SelfList<CSharpScript>::List script_list; - Mutex *lock; - Mutex *script_bind_lock; - Mutex *script_gchandle_release_lock; - - Map<Ref<CSharpScript>, Map<ObjectID, List<Pair<StringName, Variant> > > > to_reload; + Mutex *script_instances_mutex; + Mutex *script_gchandle_release_mutex; + Mutex *language_bind_mutex; Map<Object *, CSharpScriptBinding> script_bindings; @@ -294,7 +309,8 @@ public: bool debug_break_parse(const String &p_file, int p_line, const String &p_error); #ifdef TOOLS_ENABLED - void reload_assemblies_if_needed(bool p_soft_reload); + bool is_assembly_reloading_needed(); + void reload_assemblies(bool p_soft_reload); #endif void project_assembly_loaded(); diff --git a/modules/mono/editor/GodotSharpTools/.gitignore b/modules/mono/editor/GodotSharpTools/.gitignore new file mode 100644 index 0000000000..296ad48834 --- /dev/null +++ b/modules/mono/editor/GodotSharpTools/.gitignore @@ -0,0 +1,2 @@ +# nuget packages +packages
\ No newline at end of file diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index f27511ad5e..cce86efbf5 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -475,7 +475,9 @@ MonoReloadNode *MonoReloadNode::singleton = NULL; void MonoReloadNode::_reload_timer_timeout() { - CSharpLanguage::get_singleton()->reload_assemblies_if_needed(false); + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { + CSharpLanguage::get_singleton()->reload_assemblies(false); + } } void MonoReloadNode::restart_reload_timer() { @@ -493,7 +495,9 @@ void MonoReloadNode::_notification(int p_what) { switch (p_what) { case MainLoop::NOTIFICATION_WM_FOCUS_IN: { restart_reload_timer(); - CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { + CSharpLanguage::get_singleton()->reload_assemblies(false); + } } break; default: { } break; diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp index d7bfa54aba..e89d21d92d 100644 --- a/modules/mono/editor/mono_bottom_panel.cpp +++ b/modules/mono/editor/mono_bottom_panel.cpp @@ -154,10 +154,14 @@ void MonoBottomPanel::_build_project_pressed() { Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path); ERR_FAIL_COND(metadata_err != OK); - GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); + bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools"); - MonoReloadNode::get_singleton()->restart_reload_timer(); - CSharpLanguage::get_singleton()->reload_assemblies_if_needed(true); + if (build_success) { + MonoReloadNode::get_singleton()->restart_reload_timer(); + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) { + CSharpLanguage::get_singleton()->reload_assemblies(false); + } + } } void MonoBottomPanel::_view_log_pressed() { diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index bc8eed81cf..9042bff74a 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -259,6 +259,8 @@ Error ScriptClassParser::_skip_generic_type_params() { if (err) return err; continue; + } else if (tk == TK_OP_GREATER) { + return OK; } else if (tk != TK_COMMA) { error_str = "Unexpected token: " + get_token_name(tk); error = true; @@ -312,27 +314,108 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) { Token tk = get_token(); + bool generic = false; if (tk == TK_OP_LESS) { - // We don't add it to the base list if it's generic Error err = _skip_generic_type_params(); if (err) return err; - } else if (tk == TK_COMMA) { + // We don't add it to the base list if it's generic + generic = true; + tk = get_token(); + } + + if (tk == TK_COMMA) { Error err = _parse_class_base(r_base); if (err) return err; - r_base.push_back(name); + } else if (tk == TK_IDENTIFIER && String(value) == "where") { + Error err = _parse_type_constraints(); + if (err) { + return err; + } + + // An open curly bracket was parsed by _parse_type_constraints, so we can exit } else if (tk == TK_CURLY_BRACKET_OPEN) { - r_base.push_back(name); + // we are finished when we hit the open curly bracket } else { error_str = "Unexpected token: " + get_token_name(tk); error = true; return ERR_PARSE_ERROR; } + if (!generic) { + r_base.push_back(name); + } + return OK; } +Error ScriptClassParser::_parse_type_constraints() { + Token tk = get_token(); + if (tk != TK_IDENTIFIER) { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + if (tk != TK_COLON) { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + while (true) { + tk = get_token(); + if (tk == TK_IDENTIFIER) { + if (String(value) == "where") { + return _parse_type_constraints(); + } + + tk = get_token(); + if (tk == TK_PERIOD) { + while (true) { + tk = get_token(); + + if (tk != TK_IDENTIFIER) { + error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + + tk = get_token(); + + if (tk != TK_PERIOD) + break; + } + } + } + + if (tk == TK_COMMA) { + continue; + } else if (tk == TK_IDENTIFIER && String(value) == "where") { + return _parse_type_constraints(); + } else if (tk == TK_SYMBOL && String(value) == "(") { + tk = get_token(); + if (tk != TK_SYMBOL || String(value) != ")") { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } else if (tk == TK_OP_LESS) { + Error err = _skip_generic_type_params(); + if (err) + return err; + } else if (tk == TK_CURLY_BRACKET_OPEN) { + return OK; + } else { + error_str = "Unexpected token: " + get_token_name(tk); + error = true; + return ERR_PARSE_ERROR; + } + } +} + Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) { Token tk = get_token(); @@ -425,6 +508,16 @@ Error ScriptClassParser::parse(const String &p_code) { Error err = _skip_generic_type_params(); if (err) return err; + } else if (tk == TK_IDENTIFIER && String(value) == "where") { + Error err = _parse_type_constraints(); + if (err) { + return err; + } + + // An open curly bracket was parsed by _parse_type_constraints, so we can exit + curly_stack++; + type_curly_stack++; + break; } else { error_str = "Unexpected token: " + get_token_name(tk); error = true; diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h index 1e174c28a9..184adebaf2 100644 --- a/modules/mono/editor/script_class_parser.h +++ b/modules/mono/editor/script_class_parser.h @@ -65,6 +65,7 @@ private: Error _parse_type_full_name(String &r_full_name); Error _parse_class_base(Vector<String> &r_base); + Error _parse_type_constraints(); Error _parse_namespace_name(String &r_name, int &r_curly_stack); public: diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs index 66490b5e25..33b2b46712 100644 --- a/modules/mono/glue/Managed/Files/AABB.cs +++ b/modules/mono/glue/Managed/Files/AABB.cs @@ -407,8 +407,8 @@ namespace Godot return new AABB(min, max - min); } - - // Constructors + + // Constructors public AABB(Vector3 position, Vector3 size) { _position = position; diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index a5618cb28d..b318d96bb9 100644 --- a/modules/mono/glue/Managed/Files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -13,9 +13,9 @@ namespace Godot { private static readonly Basis identity = new Basis ( - new Vector3(1f, 0f, 0f), - new Vector3(0f, 1f, 0f), - new Vector3(0f, 0f, 1f) + 1f, 0f, 0f, + 0f, 1f, 0f, + 0f, 0f, 1f ); private static readonly Basis[] orthoBases = { @@ -159,9 +159,9 @@ namespace Godot { return new Basis ( - new Vector3(xAxis.x, yAxis.x, zAxis.x), - new Vector3(xAxis.y, yAxis.y, zAxis.y), - new Vector3(xAxis.z, yAxis.z, zAxis.z) + xAxis.x, yAxis.x, zAxis.x, + xAxis.y, yAxis.y, zAxis.y, + xAxis.z, yAxis.z, zAxis.z ); } @@ -410,10 +410,12 @@ namespace Godot ); } - public Quat Quat() { + public Quat Quat() + { real_t trace = _x[0] + _y[1] + _z[2]; - if (trace > 0.0f) { + if (trace > 0.0f) + { real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( @@ -424,7 +426,8 @@ namespace Godot ); } - if (_x[0] > _y[1] && _x[0] > _z[2]) { + if (_x[0] > _y[1] && _x[0] > _z[2]) + { real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( @@ -435,7 +438,8 @@ namespace Godot ); } - if (_y[1] > _z[2]) { + if (_y[1] > _z[2]) + { real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( @@ -444,7 +448,9 @@ namespace Godot (_y[2] + _z[1]) * inv_s, (_x[2] - _z[0]) * inv_s ); - } else { + } + else + { real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( @@ -502,8 +508,8 @@ namespace Godot { var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - real_t cosine = Mathf.Cos( phi); - real_t sine = Mathf.Sin( phi); + real_t cosine = Mathf.Cos(phi); + real_t sine = Mathf.Sin(phi); _x = new Vector3 ( @@ -529,12 +535,17 @@ namespace Godot public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) { - _x = xAxis; - _y = yAxis; - _z = zAxis; + _x = new Vector3(xAxis.x, yAxis.x, zAxis.x); + _y = new Vector3(xAxis.y, yAxis.y, zAxis.y); + _z = new Vector3(xAxis.z, yAxis.z, zAxis.z); + // Same as: + // SetAxis(0, xAxis); + // SetAxis(1, yAxis); + // SetAxis(2, zAxis); + // We need to assign the struct fields so we can't do that... } - public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) + internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { _x = new Vector3(xx, xy, xz); _y = new Vector3(yx, yy, yz); diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs index 88cb8524b8..fc5bb010a9 100644 --- a/modules/mono/glue/Managed/Files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -379,8 +379,8 @@ namespace Godot return txt; } - - // Constructors + + // Constructors public Color(float r, float g, float b, float a = 1.0f) { this.r = r; diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index a89dfe5f27..dcab3c1ffc 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -206,7 +206,7 @@ namespace Godot public static real_t PosMod(real_t a, real_t b) { real_t c = a % b; - if ((c < 0 && b > 0) || (c > 0 && b < 0)) + if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; } @@ -219,7 +219,7 @@ namespace Godot public static int PosMod(int a, int b) { int c = a % b; - if ((c < 0 && b > 0) || (c > 0 && b < 0)) + if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; } diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 739b7fb568..2ef02cc288 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -29,7 +29,7 @@ namespace Godot public static int FloorToInt(real_t s) { return (int)Math.Floor(s); - } + } public static int RoundToInt(real_t s) { diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index 9611dce11e..f11cd490a9 100644 --- a/modules/mono/glue/Managed/Files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -144,7 +144,7 @@ namespace Godot { return point - _normal * DistanceTo(point); } - + // Constants private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0); private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); @@ -153,8 +153,8 @@ namespace Godot public static Plane PlaneYZ { get { return _planeYZ; } } public static Plane PlaneXZ { get { return _planeXZ; } } public static Plane PlaneXY { get { return _planeXY; } } - - // Constructors + + // Constructors public Plane(real_t a, real_t b, real_t c, real_t d) { _normal = new Vector3(a, b, c); diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index eaa027eb69..fd1ac01083 100644 --- a/modules/mono/glue/Managed/Files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -202,7 +202,7 @@ namespace Godot // Static Readonly Properties public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f); - // Constructors + // Constructors public Quat(real_t x, real_t y, real_t z, real_t w) { this.x = x; diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs index cb25c267bc..888f300347 100644 --- a/modules/mono/glue/Managed/Files/Rect2.cs +++ b/modules/mono/glue/Managed/Files/Rect2.cs @@ -182,8 +182,8 @@ namespace Godot return newRect; } - - // Constructors + + // Constructors public Rect2(Vector2 position, Vector2 size) { _position = position; diff --git a/modules/mono/glue/Managed/Files/StringExtensions.cs b/modules/mono/glue/Managed/Files/StringExtensions.cs index 21c9be98c1..c194facd0b 100644 --- a/modules/mono/glue/Managed/Files/StringExtensions.cs +++ b/modules/mono/glue/Managed/Files/StringExtensions.cs @@ -185,7 +185,7 @@ namespace Godot int instanceIndex = 0; int toIndex = 0; - + if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication. { while (true) @@ -480,9 +480,9 @@ namespace Godot return false; // Don't start with number plz } - bool validChar = instance[i] >= '0' && - instance[i] <= '9' || instance[i] >= 'a' && - instance[i] <= 'z' || instance[i] >= 'A' && + bool validChar = instance[i] >= '0' && + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && instance[i] <= 'Z' || instance[i] == '_'; if (!validChar) diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index 068007d7f0..fa85855edd 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -124,16 +124,16 @@ namespace Godot // Constants private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero); - private static readonly Transform _flipX = new Transform(new Basis(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)), Vector3.Zero); - private static readonly Transform _flipY = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)), Vector3.Zero); - private static readonly Transform _flipZ = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)), Vector3.Zero); + private static readonly Transform _flipX = new Transform(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero); + private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero); + private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero); public static Transform Identity { get { return _identity; } } public static Transform FlipX { get { return _flipX; } } public static Transform FlipY { get { return _flipY; } } public static Transform FlipZ { get { return _flipZ; } } - // Constructors + // Constructors public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) { basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs index 8d30833066..c9e5b560b2 100644 --- a/modules/mono/glue/Managed/Files/Transform2D.cs +++ b/modules/mono/glue/Managed/Files/Transform2D.cs @@ -261,15 +261,15 @@ namespace Godot public static Transform2D Identity { get { return _identity; } } public static Transform2D FlipX { get { return _flipX; } } public static Transform2D FlipY { get { return _flipY; } } - - // Constructors + + // Constructors public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) { x = xAxis; y = yAxis; o = origin; } - + public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { x = new Vector2(xx, xy); diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 080b8802ba..ce41886bfc 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -215,7 +215,7 @@ namespace Godot x = v.x; y = v.y; } - + public Vector2 Slerp(Vector2 b, real_t t) { real_t theta = AngleTo(b); @@ -242,7 +242,7 @@ namespace Godot private static readonly Vector2 _one = new Vector2(1, 1); private static readonly Vector2 _negOne = new Vector2(-1, -1); private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf); - + private static readonly Vector2 _up = new Vector2(0, -1); private static readonly Vector2 _down = new Vector2(0, 1); private static readonly Vector2 _right = new Vector2(1, 0); diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 6fffe5e4d6..f6ff27989d 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -204,9 +204,9 @@ namespace Godot public Basis Outer(Vector3 b) { return new Basis( - new Vector3(x * b.x, x * b.y, x * b.z), - new Vector3(y * b.x, y * b.y, y * b.z), - new Vector3(z * b.x, z * b.y, z * b.z) + x * b.x, x * b.y, x * b.z, + y * b.x, y * b.y, y * b.z, + z * b.x, z * b.y, z * b.z ); } @@ -276,13 +276,13 @@ namespace Godot 0f, 0f, z ); } - + // Constants private static readonly Vector3 _zero = new Vector3(0, 0, 0); private static readonly Vector3 _one = new Vector3(1, 1, 1); private static readonly Vector3 _negOne = new Vector3(-1, -1, -1); private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf); - + private static readonly Vector3 _up = new Vector3(0, 1, 0); private static readonly Vector3 _down = new Vector3(0, -1, 0); private static readonly Vector3 _right = new Vector3(1, 0, 0); @@ -294,7 +294,7 @@ namespace Godot public static Vector3 One { get { return _one; } } public static Vector3 NegOne { get { return _negOne; } } public static Vector3 Inf { get { return _inf; } } - + public static Vector3 Up { get { return _up; } } public static Vector3 Down { get { return _down; } } public static Vector3 Right { get { return _right; } } diff --git a/modules/mono/glue/Managed/Properties/AssemblyInfo.cs b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs index 7ed68acad7..77b3774e81 100644 --- a/modules/mono/glue/Managed/Properties/AssemblyInfo.cs +++ b/modules/mono/glue/Managed/Properties/AssemblyInfo.cs @@ -1,7 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; -// Information about this assembly is defined by the following attributes. +// Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle("Managed")] @@ -19,7 +19,7 @@ using System.Runtime.CompilerServices; [assembly: AssemblyVersion("1.0.*")] -// The following attributes are used to specify the signing key for the assembly, +// The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index d718c3cc61..58916c5283 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -54,8 +54,10 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) { if (p_ptr->get_script_instance()) { CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance()); if (cs_instance) { - cs_instance->mono_object_disposed(p_obj); - p_ptr->set_script_instance(NULL); + if (!cs_instance->is_destructing_script_instance()) { + cs_instance->mono_object_disposed(p_obj); + p_ptr->set_script_instance(NULL); + } return; } } @@ -82,12 +84,14 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, bool p_is_ if (ref->get_script_instance()) { CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance()); if (cs_instance) { - bool r_owner_deleted; - cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); - if (!r_owner_deleted && !p_is_finalizer) { - // If the native instance is still alive and Dispose() was called - // (instead of the finalizer), then we remove the script instance. - ref->set_script_instance(NULL); + if (!cs_instance->is_destructing_script_instance()) { + bool r_owner_deleted; + cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, r_owner_deleted); + if (!r_owner_deleted && !p_is_finalizer) { + // If the native instance is still alive and Dispose() was called + // (instead of the finalizer), then we remove the script instance. + ref->set_script_instance(NULL); + } } return; } diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 5d9b9213e7..f09e93e662 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -423,7 +423,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ MonoException *exc = NULL; GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -435,7 +435,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ exc = NULL; GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 2543f5dc47..3f0a5d6e50 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -163,7 +163,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { MonoException *exc = NULL; GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -172,7 +172,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { exc = NULL; GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { @@ -192,8 +192,11 @@ String mono_to_utf8_string(MonoString *p_mono_string) { MonoError error; char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); - ERR_EXPLAIN("Conversion of MonoString to UTF8 failed."); - ERR_FAIL_COND_V(!mono_error_ok(&error), String()); + if (!mono_error_ok(&error)) { + ERR_PRINTS(String("Failed to convert MonoString* to UTF-8: ") + mono_error_get_message(&error)); + mono_error_cleanup(&error); + return String(); + } String ret = String::utf8(utf8); @@ -546,7 +549,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty MonoException *exc = NULL; GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -555,7 +558,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty exc = NULL; GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { @@ -710,16 +713,14 @@ Variant mono_object_to_variant(MonoObject *p_obj) { 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); + Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), 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); + Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } @@ -731,7 +732,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { MonoException *exc = NULL; GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); - MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_dict = invoke_method_thunk(type_is_dict, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_dict) { @@ -744,7 +745,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { exc = NULL; GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); - MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + MonoBoolean is_array = invoke_method_thunk(type_is_array, (MonoObject *)reftype, (MonoObject **)&exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); if (is_array) { diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index fe2c09799c..211987d242 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -694,4 +694,8 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool & } } +void dispose(MonoObject *p_mono_object, MonoException **r_exc) { + invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, (MonoObject **)r_exc); +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index f00680ff03..170df32991 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -243,6 +243,8 @@ MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_param uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error); +void dispose(MonoObject *p_mono_object, MonoException **r_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))) @@ -267,4 +269,93 @@ uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool & #define GD_MONO_END_RUNTIME_INVOKE \ _runtime_invoke_count_ref -= 1; +inline void invoke_method_thunk(void (*p_method_thunk)()) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R> +R invoke_method_thunk(R (*p_method_thunk)()) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + +template <class P1> +void invoke_method_thunk(void (*p_method_thunk)(P1), P1 p_arg1) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(p_arg1); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R, class P1> +R invoke_method_thunk(R (*p_method_thunk)(P1), P1 p_arg1) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(p_arg1); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + +template <class P1, class P2> +void invoke_method_thunk(void (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(p_arg1, p_arg2); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R, class P1, class P2> +R invoke_method_thunk(R (*p_method_thunk)(P1, P2), P1 p_arg1, P2 p_arg2) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(p_arg1, p_arg2); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + +template <class P1, class P2, class P3> +void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(p_arg1, p_arg2, p_arg3); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R, class P1, class P2, class P3> +R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3), P1 p_arg1, P2 p_arg2, P3 p_arg3) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(p_arg1, p_arg2, p_arg3); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + +template <class P1, class P2, class P3, class P4> +void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R, class P1, class P2, class P3, class P4> +R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + +template <class P1, class P2, class P3, class P4, class P5> +void invoke_method_thunk(void (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); + GD_MONO_END_RUNTIME_INVOKE; +} + +template <class R, class P1, class P2, class P3, class P4, class P5> +R invoke_method_thunk(R (*p_method_thunk)(P1, P2, P3, P4, P5), P1 p_arg1, P2 p_arg2, P3 p_arg3, P4 p_arg4, P5 p_arg5) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + R r = p_method_thunk(p_arg1, p_arg2, p_arg3, p_arg4, p_arg5); + GD_MONO_END_RUNTIME_INVOKE; + return r; +} + #endif // GD_MONOUTILS_H diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index fa1fbebb16..c6748309f3 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -98,11 +98,9 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc mono_array_set(signal_args, MonoObject *, i, boxed); } - GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); - MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - thunk(get_target(), signal_args, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback), get_target(), signal_args, (MonoObject **)&exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { @@ -129,14 +127,12 @@ SignalAwaiterHandle::SignalAwaiterHandle(MonoObject *p_managed) : SignalAwaiterHandle::~SignalAwaiterHandle() { if (!completed) { - GDMonoUtils::SignalAwaiter_FailureCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback); - MonoObject *awaiter = get_target(); if (awaiter) { MonoException *exc = NULL; GD_MONO_BEGIN_RUNTIME_INVOKE; - thunk(awaiter, (MonoObject **)&exc); + invoke_method_thunk(CACHED_METHOD_THUNK(SignalAwaiter, FailureCallback), awaiter, (MonoObject **)&exc); GD_MONO_END_RUNTIME_INVOKE; if (exc) { diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h index 40b47e8648..c801fb2f33 100644 --- a/modules/mono/utils/macros.h +++ b/modules/mono/utils/macros.h @@ -31,15 +31,17 @@ #ifndef UTIL_MACROS_H #define UTIL_MACROS_H +#define _GD_VARNAME_CONCAT_B(m_ignore, m_name) m_name +#define _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B(hello there, m_a##m_b##m_c) +#define _GD_VARNAME_CONCAT(m_a, m_b, m_c) _GD_VARNAME_CONCAT_A(m_a, m_b, m_c) +#define GD_UNIQUE_NAME(m_name) _GD_VARNAME_CONCAT(m_name, _, __COUNTER__) + // noreturn #if __cpp_static_assert #define GD_STATIC_ASSERT(m_cond) static_assert((m_cond), "Condition '" #m_cond "' failed") #else -#define _GD_STATIC_ASSERT_VARNAME_CONCAT_B(m_ignore, m_name) m_name -#define _GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_B(hello there, m_a##m_b) -#define _GD_STATIC_ASSERT_VARNAME_CONCAT(m_a, m_b) GD_STATIC_ASSERT_VARNAME_CONCAT_A(m_a, m_b) -#define GD_STATIC_ASSERT(m_cond) typedef int GD_STATIC_ASSERT_VARNAME_CONCAT(godot_static_assert_, __COUNTER__)[((m_cond) ? 1 : -1)] +#define GD_STATIC_ASSERT(m_cond) typedef int GD_UNIQUE_NAME(godot_static_assert)[((m_cond) ? 1 : -1)] #endif #undef _NO_RETURN_ diff --git a/modules/mono/utils/mutex_utils.h b/modules/mono/utils/mutex_utils.h new file mode 100644 index 0000000000..07d659b6eb --- /dev/null +++ b/modules/mono/utils/mutex_utils.h @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* mutex_utils.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 MUTEX_UTILS_H +#define MUTEX_UTILS_H + +#include "core/error_macros.h" +#include "core/os/mutex.h" + +#include "macros.h" + +class ScopedMutexLock { + Mutex *mutex; + +public: + ScopedMutexLock(Mutex *mutex) { + this->mutex = mutex; +#ifndef NO_THREADS +#ifdef DEBUG_ENABLED + CRASH_COND(!mutex); +#endif + this->mutex->lock(); +#endif + } + + ~ScopedMutexLock() { +#ifndef NO_THREADS +#ifdef DEBUG_ENABLED + CRASH_COND(!mutex); +#endif + mutex->unlock(); +#endif + } +}; + +#define SCOPED_MUTEX_LOCK(m_mutex) ScopedMutexLock GD_UNIQUE_NAME(__scoped_mutex_lock__)(m_mutex); + +// TODO: Add version that receives a lambda instead, once C++11 is allowed + +#endif // MUTEX_UTILS_H diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index bdd3e31eb8..03ef024587 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -178,13 +178,17 @@ void RegEx::clear() { if (sizeof(CharType) == 2) { - if (code) + if (code) { pcre2_code_free_16((pcre2_code_16 *)code); + code = NULL; + } } else { - if (code) + if (code) { pcre2_code_free_32((pcre2_code_32 *)code); + code = NULL; + } } } diff --git a/modules/squish/config.py b/modules/squish/config.py index 098f1eafa9..1c8cd12a2d 100644 --- a/modules/squish/config.py +++ b/modules/squish/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return env['tools'] + return True def configure(env): pass diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 26cb76011c..4161a0f6ae 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -30,8 +30,6 @@ #include "image_compress_squish.h" -#include "core/print_string.h" - #include <squish.h> void image_decompress_squish(Image *p_image) { @@ -64,17 +62,19 @@ void image_decompress_squish(Image *p_image) { return; } - int dst_ofs = 0; - for (int i = 0; i <= mm_count; i++) { int src_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0; p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h); - squish::DecompressImage(&wb[dst_ofs], mipmap_w, mipmap_h, &rb[src_ofs], squish_flags); + int dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i); + squish::DecompressImage(&wb[dst_ofs], w, h, &rb[src_ofs], squish_flags); + w >>= 1; + h >>= 1; } p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } +#ifdef TOOLS_ENABLED void image_compress_squish(Image *p_image, float p_lossy_quality, Image::CompressSource p_source) { if (p_image->get_format() >= Image::FORMAT_DXT1) @@ -203,3 +203,4 @@ void image_compress_squish(Image *p_image, float p_lossy_quality, Image::Compres p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } } +#endif diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h index dfebdc955f..dd53f2787a 100644 --- a/modules/squish/image_compress_squish.h +++ b/modules/squish/image_compress_squish.h @@ -33,7 +33,9 @@ #include "core/image.h" +#ifdef TOOLS_ENABLED void image_compress_squish(Image *p_image, float p_lossy_quality, Image::CompressSource p_source); +#endif void image_decompress_squish(Image *p_image); #endif // IMAGE_COMPRESS_SQUISH_H diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp index d4ed676cce..9a5bb47f19 100644 --- a/modules/squish/register_types.cpp +++ b/modules/squish/register_types.cpp @@ -29,17 +29,14 @@ /*************************************************************************/ #include "register_types.h" - -#ifdef TOOLS_ENABLED - #include "image_compress_squish.h" void register_squish_types() { +#ifdef TOOLS_ENABLED Image::set_compress_bc_func(image_compress_squish); +#endif Image::_image_decompress_bc = image_decompress_squish; } void unregister_squish_types() {} - -#endif diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h index 00f5c345c4..9dbd69c46b 100644 --- a/modules/squish/register_types.h +++ b/modules/squish/register_types.h @@ -28,7 +28,5 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef TOOLS_ENABLED void register_squish_types(); void unregister_squish_types(); -#endif diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index b8e77a1b0f..ff3ed66e81 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -125,12 +125,10 @@ </argument> <description> Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error. - The [code]inputs[/code] array contains the values of the input ports. [code]outputs[/code] is an array whose indices should be set to the respective outputs. The [code]start_mode[/code] is usually [code]START_MODE_BEGIN_SEQUENCE[/code], unless you have used the STEP_* constants. [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. - When returning, you can mask the returned value with one of the STEP_* constants. </description> </method> diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 186e9e63b1..5b3b3a6769 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -981,6 +981,10 @@ bool VisualScript::is_tool() const { return false; } +bool VisualScript::is_valid() const { + return true; //always valid +} + ScriptLanguage *VisualScript::get_language() const { return VisualScriptLanguage::singleton; diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index bd666447a3..cdc9159a73 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -341,6 +341,7 @@ public: virtual Error reload(bool p_keep_state = false); virtual bool is_tool() const; + virtual bool is_valid() const; virtual ScriptLanguage *get_language() const; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index c5f2070963..afaa6a9b95 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1923,7 +1923,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } -void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) { +void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) { Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); if (!vsfc.is_valid()) diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index ce3245bc28..5f707c9e4c 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -234,7 +234,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, const String &p_type); + void _selected_method(const String &p_method, const String &p_type, const bool p_connecting); void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud); diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 8255ed7116..82a577790e 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -31,6 +31,7 @@ #include "emws_client.h" #include "core/io/ip.h" +#include "core/project_settings.h" #include "emscripten.h" extern "C" { @@ -43,8 +44,9 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) { EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) { EMWSClient *client = static_cast<EMWSClient *>(obj); - static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); - client->_on_peer_packet(); + Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); + if (err == OK) + client->_on_peer_packet(); } EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { @@ -159,7 +161,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data()); /* clang-format on */ - static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock); + static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size); return OK; }; @@ -198,7 +200,13 @@ uint16_t EMWSClient::get_connected_port() const { return 1025; }; +int EMWSClient::get_max_packet_size() const { + return (1 << _in_buf_size) - PROTO_SIZE; +} + EMWSClient::EMWSClient() { + _in_buf_size = GLOBAL_GET(WSC_IN_BUF); + _in_pkt_size = GLOBAL_GET(WSC_IN_PKT); _is_connecting = false; _peer = Ref<EMWSPeer>(memnew(EMWSPeer)); /* clang-format off */ diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index b20633baff..a21090a1a3 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -41,6 +41,8 @@ class EMWSClient : public WebSocketClient { GDCIIMPL(EMWSClient, WebSocketClient); private: + int _in_buf_size; + int _in_pkt_size; int _js_id; public: @@ -52,6 +54,7 @@ public: IP_Address get_connected_host() const; uint16_t get_connected_port() const; virtual ConnectionStatus get_connection_status() const; + int get_max_packet_size() const; virtual void poll(); EMWSClient(); ~EMWSClient(); diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 68f41165eb..bb97934824 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -32,11 +32,11 @@ #include "emws_peer.h" #include "core/io/ip.h" -void EMWSPeer::set_sock(int p_sock) { +void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) { peer_sock = p_sock; - in_buffer.clear(); - queue_count = 0; + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _packet_buffer.resize((1 << p_in_buf_size)); } void EMWSPeer::set_write_mode(WriteMode p_mode) { @@ -47,18 +47,10 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const { return write_mode; } -void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { - - if (in_buffer.space_left() < p_size + 5) { - ERR_EXPLAIN("Buffer full! Dropping data"); - ERR_FAIL(); - } +Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { uint8_t is_string = p_is_string ? 1 : 0; - in_buffer.write((uint8_t *)&p_size, 4); - in_buffer.write((uint8_t *)&is_string, 1); - in_buffer.write(p_data, p_size); - queue_count++; + return _in_buffer.write_packet(p_data, p_size, &is_string); } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { @@ -89,40 +81,28 @@ Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - if (queue_count == 0) + if (_in_buffer.packets_left() == 0) return ERR_UNAVAILABLE; - uint32_t to_read = 0; - uint32_t left = 0; - uint8_t is_string = 0; - r_buffer_size = 0; - - in_buffer.read((uint8_t *)&to_read, 4); - --queue_count; - left = in_buffer.data_left(); + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + int read = 0; + Error err = _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); + ERR_FAIL_COND_V(err != OK, err); - if (left < to_read + 1) { - in_buffer.advance_read(left); - return FAILED; - } - - in_buffer.read(&is_string, 1); - _was_string = is_string == 1; - in_buffer.read(packet_buffer, to_read); - *r_buffer = packet_buffer; - r_buffer_size = to_read; + *r_buffer = rw.ptr(); + r_buffer_size = read; return OK; }; int EMWSPeer::get_available_packet_count() const { - return queue_count; + return _in_buffer.packets_left(); }; bool EMWSPeer::was_string_packet() const { - return _was_string; + return _is_string; }; bool EMWSPeer::is_connected_to_host() const { @@ -143,9 +123,9 @@ void EMWSPeer::close(int p_code, String p_reason) { }, peer_sock, p_code, p_reason.utf8().get_data()); /* clang-format on */ } + _is_string = 0; + _in_buffer.clear(); peer_sock = -1; - queue_count = 0; - in_buffer.clear(); }; IP_Address EMWSPeer::get_connected_host() const { @@ -162,15 +142,12 @@ uint16_t EMWSPeer::get_connected_port() const { EMWSPeer::EMWSPeer() { peer_sock = -1; - queue_count = 0; - _was_string = false; - in_buffer.resize(16); write_mode = WRITE_MODE_BINARY; + close(); }; EMWSPeer::~EMWSPeer() { - in_buffer.resize(0); close(); }; diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index a4b2c8f50b..4beb86d45b 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -36,6 +36,7 @@ #include "core/io/packet_peer.h" #include "core/ring_buffer.h" #include "emscripten.h" +#include "packet_buffer.h" #include "websocket_peer.h" class EMWSPeer : public WebSocketPeer { @@ -43,25 +44,20 @@ class EMWSPeer : public WebSocketPeer { GDCIIMPL(EMWSPeer, WebSocketPeer); private: - enum { - PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type - }; - int peer_sock; WriteMode write_mode; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; - RingBuffer<uint8_t> in_buffer; - int queue_count; - bool _was_string; + PoolVector<uint8_t> _packet_buffer; + PacketBuffer<uint8_t> _in_buffer; + uint8_t _is_string; public: - void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); - void set_sock(int sock); + Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); + void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size); virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + virtual int get_max_packet_size() const { return _packet_buffer.size(); }; virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; @@ -72,10 +68,6 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi); - Error read_wsi(void *in, size_t len); - Error write_wsi(); - EMWSPeer(); ~EMWSPeer(); }; diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index db02162699..09f9c1ceec 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -74,6 +74,10 @@ void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { void EMWSServer::poll() { } +int EMWSServer::get_max_packet_size() const { + return 0; +} + EMWSServer::EMWSServer() { } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 74b689a29b..2dc455c389 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -49,6 +49,7 @@ public: IP_Address get_peer_address(int p_peer_id) const; int get_peer_port(int p_peer_id) const; void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); + int get_max_packet_size() const; virtual void poll(); virtual PoolVector<String> get_protocols() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index 07583bb85b..fa0bb4cfb2 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -32,6 +32,7 @@ #include "lws_client.h" #include "core/io/ip.h" #include "core/io/stream_peer_ssl.h" +#include "core/project_settings.h" #include "tls/mbedtls/wrapper/include/openssl/ssl.h" Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { @@ -104,6 +105,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, return OK; }; +int LWSClient::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + void LWSClient::poll() { _lws_poll(); @@ -124,7 +129,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi } break; case LWS_CALLBACK_CLIENT_ESTABLISHED: - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); peer_data->peer_id = 0; peer_data->force_close = false; peer_data->clean_close = false; @@ -207,6 +212,11 @@ uint16_t LWSClient::get_connected_port() const { }; LWSClient::LWSClient() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1); + context = NULL; _lws_ref = NULL; _peer = Ref<LWSPeer>(memnew(LWSPeer)); diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index 1bbc19f352..fdecb99925 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_client.h @@ -43,8 +43,15 @@ class LWSClient : public WebSocketClient { LWS_HELPER(LWSClient); +private: + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; + public: Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index fd8f85371b..def4f5cfd0 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.h @@ -60,6 +60,7 @@ void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVec protected: \ struct _LWSRef *_lws_ref; \ struct lws_context *context; \ + bool _keep_servicing; \ \ static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \ \ @@ -71,6 +72,7 @@ protected: \ if (!ref->is_valid) \ return 0; \ CNAME *helper = (CNAME *)ref->obj; \ + helper->_keep_servicing = true; \ return helper->_handle_cb(wsi, reason, user, in, len); \ } \ \ @@ -91,11 +93,14 @@ public: \ \ void _lws_poll() { \ ERR_FAIL_COND(context == NULL); \ - \ - if (::_lws_poll(context, _lws_ref)) { \ - context = NULL; \ - _lws_ref = NULL; \ - } \ + do { \ + _keep_servicing = false; \ + if (::_lws_poll(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + break; \ + } \ + } while (_keep_servicing); \ } \ \ protected: diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index b5c130b308..04e6e7c951 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -41,11 +41,12 @@ #include "drivers/unix/net_socket_posix.h" -void LWSPeer::set_wsi(struct lws *p_wsi) { +void LWSPeer::set_wsi(struct lws *p_wsi, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) { ERR_FAIL_COND(wsi != NULL); - rbw.resize(16); - rbr.resize(16); + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _out_buffer.resize(p_out_pkt_size, p_out_buf_size); + _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE); wsi = p_wsi; }; @@ -61,24 +62,29 @@ Error LWSPeer::read_wsi(void *in, size_t len) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - uint32_t size = in_size; - uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + if (lws_is_first_fragment(wsi)) + _in_size = 0; + else if (_in_size == -1) // Trash this frame + return ERR_FILE_CORRUPT; - if (rbr.space_left() < len + 5) { - ERR_EXPLAIN("Buffer full! Dropping data"); - ERR_FAIL_V(FAILED); + Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL); + + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); } - copymem(&(input_buffer[size]), in, len); - size += len; + _in_size += len; - in_size = size; if (lws_is_final_fragment(wsi)) { - rbr.write((uint8_t *)&size, 4); - rbr.write((uint8_t *)&is_string, 1); - rbr.write(input_buffer, size); - in_count++; - in_size = 0; + uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + err = _in_buffer.write_packet(NULL, _in_size, &is_string); + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); + } } return OK; @@ -89,26 +95,20 @@ Error LWSPeer::write_wsi() { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); PoolVector<uint8_t> tmp; - int left = rbw.data_left(); - uint32_t to_write = 0; + int count = _out_buffer.packets_left(); - if (left == 0 || out_count == 0) + if (count == 0) return OK; - rbw.read((uint8_t *)&to_write, 4); - out_count--; - - if (left < to_write) { - rbw.advance_read(left); - return FAILED; - } + int read = 0; + uint8_t is_string; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read); - tmp.resize(LWS_PRE + to_write); - rbw.read(&(tmp.write()[LWS_PRE]), to_write); - lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode); - tmp.resize(0); + enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY; + lws_write(wsi, &(rw[LWS_PRE]), read, mode); - if (out_count > 0) + if (count > 1) lws_callback_on_writable(wsi); // we want to write more! return OK; @@ -118,40 +118,27 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - rbw.write((uint8_t *)&p_buffer_size, 4); - rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left())); - out_count++; - + uint8_t is_string = write_mode == WRITE_MODE_TEXT; + _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string); lws_callback_on_writable(wsi); // notify that we want to write return OK; }; Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + r_buffer_size = 0; + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - if (in_count == 0) + if (_in_buffer.packets_left() == 0) return ERR_UNAVAILABLE; - uint32_t to_read = 0; - uint32_t left = 0; - uint8_t is_string = 0; - r_buffer_size = 0; - - rbr.read((uint8_t *)&to_read, 4); - in_count--; - left = rbr.data_left(); - - if (left < to_read + 1) { - rbr.advance_read(left); - return FAILED; - } + int read = 0; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); - rbr.read(&is_string, 1); - rbr.read(packet_buffer, to_read); - *r_buffer = packet_buffer; - r_buffer_size = to_read; - _was_string = is_string; + *r_buffer = rw.ptr(); + r_buffer_size = read; return OK; }; @@ -161,12 +148,12 @@ int LWSPeer::get_available_packet_count() const { if (!is_connected_to_host()) return 0; - return in_count; + return _in_buffer.packets_left(); }; bool LWSPeer::was_string_packet() const { - return _was_string; + return _is_string; }; bool LWSPeer::is_connected_to_host() const { @@ -219,12 +206,11 @@ void LWSPeer::close(int p_code, String p_reason) { close_reason = ""; } wsi = NULL; - rbw.resize(0); - rbr.resize(0); - in_count = 0; - in_size = 0; - out_count = 0; - _was_string = false; + _in_buffer.clear(); + _out_buffer.clear(); + _in_size = 0; + _is_string = 0; + _packet_buffer.resize(0); }; IP_Address LWSPeer::get_connected_host() const { diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index 571445db01..3ded3810d1 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_peer.h @@ -37,6 +37,7 @@ #include "core/ring_buffer.h" #include "libwebsockets.h" #include "lws_config.h" +#include "packet_buffer.h" #include "websocket_peer.h" class LWSPeer : public WebSocketPeer { @@ -44,14 +45,16 @@ class LWSPeer : public WebSocketPeer { GDCIIMPL(LWSPeer, WebSocketPeer); private: - enum { - PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type - }; + int _in_size; + uint8_t _is_string; + // Our packet info is just a boolean (is_string), using uint8_t for it. + PacketBuffer<uint8_t> _in_buffer; + PacketBuffer<uint8_t> _out_buffer; + + PoolVector<uint8_t> _packet_buffer; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; struct lws *wsi; WriteMode write_mode; - bool _was_string; int close_code; String close_reason; @@ -63,17 +66,10 @@ public: bool clean_close; }; - RingBuffer<uint8_t> rbw; - RingBuffer<uint8_t> rbr; - uint8_t input_buffer[PACKET_BUFFER_SIZE]; - uint32_t in_size; - int in_count; - int out_count; - virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + virtual int get_max_packet_size() const { return _packet_buffer.size(); }; virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; @@ -84,7 +80,7 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi); + void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size); Error read_wsi(void *in, size_t len); Error write_wsi(); void send_close_status(struct lws *wsi); diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index 58fa043346..0e551eb318 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -31,6 +31,7 @@ #include "lws_server.h" #include "core/os/os.h" +#include "core/project_settings.h" Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { @@ -67,6 +68,10 @@ bool LWSServer::is_listening() const { return context != NULL; } +int LWSServer::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; @@ -85,7 +90,7 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi int32_t id = _gen_unique_id(); Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _peer_map[id] = peer; peer_data->peer_id = id; @@ -192,6 +197,10 @@ void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { } LWSServer::LWSServer() { + _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10; + _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1); + _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10; + _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1); context = NULL; _lws_ref = NULL; } diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 346773ebc4..c43044f194 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_server.h @@ -45,11 +45,16 @@ class LWSServer : public WebSocketServer { private: Map<int, Ref<LWSPeer> > peer_map; + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; public: Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; + int get_max_packet_size() const; bool has_peer(int p_id) const; Ref<WebSocketPeer> get_peer(int p_id) const; IP_Address get_peer_address(int p_peer_id) const; diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h new file mode 100644 index 0000000000..a3af7f728a --- /dev/null +++ b/modules/websocket/packet_buffer.h @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* packet_buffer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PACKET_BUFFER_H +#define PACKET_BUFFER_H + +#include "core/os/copymem.h" +#include "core/ring_buffer.h" + +template <class T> +class PacketBuffer { + +private: + typedef struct { + uint32_t size; + T info; + } _Packet; + + RingBuffer<_Packet> _packets; + RingBuffer<uint8_t> _payload; + +public: + Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) { +#ifdef TOOLS_ENABLED + // Verbose buffer warnings + if (p_payload && _payload.space_left() < p_size) { + ERR_PRINT("Buffer payload full! Dropping data."); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + } + if (p_info && _packets.space_left() < 1) { + ERR_PRINT("Too many packets in queue! Dropping data."); + ERR_FAIL_V(ERR_OUT_OF_MEMORY); + } +#else + ERR_FAIL_COND_V(p_payload && _payload.space_left() < p_size, ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY); +#endif + + // If p_info is NULL, only the payload is written + if (p_info) { + _Packet p; + p.size = p_size; + copymem(&p.info, p_info, sizeof(T)); + _packets.write(p); + } + + // If p_payload is NULL, only the packet information is written. + if (p_payload) { + _payload.write((const uint8_t *)p_payload, p_size); + } + + return OK; + } + + Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) { + ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE); + _Packet p; + _packets.read(&p, 1); + ERR_FAIL_COND_V(_payload.data_left() < p.size, ERR_BUG); + ERR_FAIL_COND_V(p_bytes < p.size, ERR_OUT_OF_MEMORY); + + r_read = p.size; + copymem(r_info, &p.info, sizeof(T)); + _payload.read(r_payload, p.size); + return OK; + } + + void discard_payload(int p_size) { + _packets.decrease_write(p_size); + } + + void resize(int p_pkt_shift, int p_buf_shift) { + _packets.resize(p_pkt_shift); + _payload.resize(p_buf_shift); + } + + int packets_left() const { + return _packets.data_left(); + } + + void clear() { + _payload.resize(0); + _packets.resize(0); + } + + PacketBuffer() { + clear(); + } + + ~PacketBuffer() { + clear(); + } +}; + +#endif // PACKET_BUFFER_H diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 538cd40454..8946faffa9 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "register_types.h" #include "core/error_macros.h" +#include "core/project_settings.h" #ifdef JAVASCRIPT_ENABLED #include "emscripten.h" #include "emws_client.h" @@ -41,6 +42,22 @@ #endif void register_websocket_types() { +#define _SET_HINT(NAME, _VAL_, _MAX_) \ + GLOBAL_DEF(NAME, _VAL_); \ + ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater")); + + // Client buffers project settings + _SET_HINT(WSC_IN_BUF, 64, 4096); + _SET_HINT(WSC_IN_PKT, 1024, 16384); + _SET_HINT(WSC_OUT_BUF, 64, 4096); + _SET_HINT(WSC_OUT_PKT, 1024, 16384); + + // Server buffers project settings + _SET_HINT(WSS_IN_BUF, 64, 4096); + _SET_HINT(WSS_IN_PKT, 1024, 16384); + _SET_HINT(WSS_OUT_BUF, 64, 4096); + _SET_HINT(WSS_OUT_PKT, 1024, 16384); + #ifdef JAVASCRIPT_ENABLED EM_ASM({ var IDHandler = {}; diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index d27fb4d778..45dd30d0ce 100644 --- a/modules/websocket/websocket_macros.h +++ b/modules/websocket/websocket_macros.h @@ -30,6 +30,16 @@ #ifndef WEBSOCKETMACTOS_H #define WEBSOCKETMACTOS_H +#define WSC_IN_BUF "network/limits/websocket_client/max_in_buffer_kb" +#define WSC_IN_PKT "network/limits/websocket_client/max_in_packets" +#define WSC_OUT_BUF "network/limits/websocket_client/max_out_buffer_kb" +#define WSC_OUT_PKT "network/limits/websocket_client/max_out_packets" + +#define WSS_IN_BUF "network/limits/websocket_server/max_in_buffer_kb" +#define WSS_IN_PKT "network/limits/websocket_server/max_in_packets" +#define WSS_OUT_BUF "network/limits/websocket_server/max_out_buffer_kb" +#define WSS_OUT_PKT "network/limits/websocket_server/max_out_packets" + /* clang-format off */ #define GDCICLASS(CNAME) \ public:\ diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp index 873658559a..9a95c17e47 100644 --- a/modules/websocket/websocket_multiplayer.cpp +++ b/modules/websocket/websocket_multiplayer.cpp @@ -100,13 +100,6 @@ int WebSocketMultiplayerPeer::get_available_packet_count() const { return _incoming_packets.size(); } -int WebSocketMultiplayerPeer::get_max_packet_size() const { - - ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); - - return MAX_PACKET_SIZE; -} - Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { r_buffer_size = 0; diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h index 8edfc5296e..3cba0011fc 100644 --- a/modules/websocket/websocket_multiplayer.h +++ b/modules/websocket/websocket_multiplayer.h @@ -51,9 +51,7 @@ protected: SYS_DEL = 2, SYS_ID = 3, - PROTO_SIZE = 9, - SYS_PACKET_SIZE = 13, - MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer + PROTO_SIZE = 9 }; struct Packet { @@ -93,7 +91,7 @@ public: /* PacketPeer */ virtual int get_available_packet_count() const; - virtual int get_max_packet_size() const; + virtual int get_max_packet_size() const = 0; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 5918fda3c2..4966cdfc72 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -32,7 +32,6 @@ #include "core/error_list.h" #include "core/io/packet_peer.h" -#include "core/ring_buffer.h" #include "websocket_macros.h" class WebSocketPeer : public PacketPeer { |