diff options
Diffstat (limited to 'modules')
54 files changed, 1011 insertions, 491 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 c8c782267e..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); diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index a94b88d566..812dcd2d56 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -265,3 +265,11 @@ bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF 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 85659e1523..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."); 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/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 8bb621a863..2027d8e1eb 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -461,7 +461,47 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) { int l_width = d["width"]; int l_depth = d["depth"]; - PoolVector<real_t> l_heights = d["heights"]; + + // TODO This code will need adjustments if real_t is set to `double`, + // because that precision is unnecessary for a heightmap and Bullet doesn't support it... + + PoolVector<real_t> l_heights; + Variant l_heights_v = d["heights"]; + + if (l_heights_v.get_type() == Variant::POOL_REAL_ARRAY) { + // Ready-to-use heights can be passed + + l_heights = l_heights_v; + + } else if (l_heights_v.get_type() == Variant::OBJECT) { + // If an image is passed, we have to convert it to a format Bullet supports. + // this would be expensive to do with a script, so it's nice to have it here. + + Ref<Image> l_image = l_heights_v; + ERR_FAIL_COND(l_image.is_null()); + + // Float is the only common format between Godot and Bullet that can be used for decent collision. + // (Int16 would be nice too but we still don't have it) + // We could convert here automatically but it's better to not be intrusive and let the caller do it if necessary. + ERR_FAIL_COND(l_image->get_format() != Image::FORMAT_RF); + + PoolByteArray im_data = l_image->get_data(); + + l_heights.resize(l_image->get_width() * l_image->get_width()); + + PoolRealArray::Write w = l_heights.write(); + PoolByteArray::Read r = im_data.read(); + float *rp = (float *)r.ptr(); + // At this point, `rp` could be used directly for Bullet, but I don't know how safe it would be. + + for (int i = 0; i < l_heights.size(); ++i) { + w[i] = rp[i]; + } + + } else { + ERR_EXPLAIN("Expected PoolRealArray or float Image."); + ERR_FAIL(); + } ERR_FAIL_COND(l_width <= 0); ERR_FAIL_COND(l_depth <= 0); @@ -497,19 +537,8 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const { void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) { // TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes - { // Copy - - // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields - // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data). - - const int heights_size = p_heights.size(); - heights.resize(heights_size); - PoolVector<real_t>::Read p_heights_r = p_heights.read(); - PoolVector<real_t>::Write heights_w = heights.write(); - for (int i = heights_size - 1; 0 <= i; --i) { - heights_w[i] = p_heights_r[i]; - } - } + // If this array is resized outside of here, it should be preserved due to CoW + heights = p_heights; width = p_width; depth = p_depth; 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 a4c34e7583..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); @@ -468,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(); 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 c67cf124c0..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; } } @@ -1182,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()) { @@ -1253,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; @@ -1760,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; @@ -1842,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; @@ -1856,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_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 2f31d59c46..9ff33594ce 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -758,22 +758,14 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ r_ret = Variant(); return; } + r_ret = *p_args[0]; VariantParser::StreamString ss; ss.s = *p_args[0]; String errs; int line; - Error err = VariantParser::parse(&ss, r_ret, errs, line); - - if (err != OK) { - r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::STRING; - r_ret = "Parse error at line " + itos(line) + ": " + errs; - return; - } - + (void)VariantParser::parse(&ss, r_ret, errs, line); } break; case VAR_TO_BYTES: { VALIDATE_ARG_COUNT(1); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a4bda42b16..6ea0dbcb19 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); @@ -5743,18 +5746,23 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) { bool valid = p_container.builtin_type == p_expression.builtin_type; if (p_allow_implicit_conversion) { - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::STRING && p_expression.builtin_type == Variant::NODE_PATH); - valid = valid || (p_container.builtin_type == Variant::NODE_PATH && p_expression.builtin_type == Variant::STRING); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::REAL); - valid = valid || (p_container.builtin_type == Variant::BOOL && p_expression.builtin_type == Variant::INT); - valid = valid || (p_container.builtin_type == Variant::INT && p_expression.builtin_type == Variant::BOOL); - valid = valid || (p_container.builtin_type == Variant::REAL && p_expression.builtin_type == Variant::BOOL); + valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type); } return valid; } + if (p_container.kind == DataType::BUILTIN && p_container.builtin_type == Variant::OBJECT) { + // Object built-in is a special case, it's compatible with any object and with null + if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) { + return true; + } + if (p_expression.kind == DataType::BUILTIN) { + return false; + } + // If it's not a built-in, must be an object + return true; + } + if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) { // Can't mix built-ins with objects return false; 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..943d95bfc9 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 == "String") - return "string"; // I prefer this one >:[ + if (p_var_type_name == Variant::get_type_name(Variant::OBJECT)) + return "Godot.Object"; - // TODO these will be rewritten later into custom containers + if (p_var_type_name == Variant::get_type_name(Variant::REAL)) { +#ifdef REAL_T_IS_DOUBLE + return "double"; +#else + return "float"; +#endif + } - if (p_var_type_name == "Array") - return "object[]"; + if (p_var_type_name == Variant::get_type_name(Variant::STRING)) + return "string"; // I prefer this one >:[ - if (p_var_type_name == "Dictionary") - return "Dictionary<object, object>"; + if (p_var_type_name == Variant::get_type_name(Variant::DICTIONARY)) + return "Collections.Dictionary"; - 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::ARRAY)) + return "Collections.Array"; - return "object"; + 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 + }; + + 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); - Object *obj = ObjectDB::get_instance(F->key()); - if (!obj) - continue; + if (!obj) { + scr->pending_reload_state.erase(obj_id); + continue; + } - 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(); + ScriptInstance *si = obj->get_script_instance(); + +#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 = F->get().front(); G; G = G->next()) { - obj->get_script_instance()->set(G->get().first, G->get().second); + 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); + } + + 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 + { + SCOPED_MUTEX_LOCK(language_bind_mutex); - Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data; + Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_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); - } - - 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 { @@ -2444,6 +2474,18 @@ void CSharpScript::set_source_code(const String &p_code) { #endif } +void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const { + + if (!script_class) + return; + + // TODO: Filter out things unsuitable for explicit calls, like constructors. + const Vector<GDMonoMethod *> &methods = script_class->get_all_methods(); + for (int i = 0; i < methods.size(); ++i) { + p_list->push_back(methods[i]->get_method_info()); + } +} + bool CSharpScript::has_method(const StringName &p_method) const { if (!script_class) @@ -2452,17 +2494,32 @@ bool CSharpScript::has_method(const StringName &p_method) const { return script_class->has_fetched_method_unknown_params(p_method); } -Error CSharpScript::reload(bool p_keep_state) { +MethodInfo CSharpScript::get_method_info(const StringName &p_method) const { -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->lock(); -#endif + if (!script_class) + return MethodInfo(); - bool has_instances = instances.size(); + GDMonoClass *top = script_class; -#ifndef NO_THREADS - CSharpLanguage::singleton->lock->unlock(); -#endif + while (top && top != native) { + GDMonoMethod *params = top->get_fetched_method_unknown_params(p_method); + if (params) { + return params->get_method_info(); + } + + top = top->get_parent_class(); + } + + return MethodInfo(); +} + +Error CSharpScript::reload(bool p_keep_state) { + + 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 +2705,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..08466bae58 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,12 +169,14 @@ 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; - /* TODO */ virtual void get_script_method_list(List<MethodInfo> *p_list) const {} + virtual void get_script_method_list(List<MethodInfo> *p_list) const; bool has_method(const StringName &p_method) const; - /* TODO */ MethodInfo get_method_info(const StringName &p_method) const { return MethodInfo(); } + MethodInfo get_method_info(const StringName &p_method) const; virtual int get_member_line(const StringName &p_member) 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/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index 54d2813abf..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 ); } @@ -535,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/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index 687ed8f011..fa85855edd 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -124,9 +124,9 @@ 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; } } diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 8e62ec778d..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 ); } 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_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 4e515cde28..c55f9160bd 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -151,6 +151,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { StringName name = mono_method_get_name(raw_method); + // get_method implicitly fetches methods and adds them to this->methods GDMonoMethod *method = get_method(raw_method, name); ERR_CONTINUE(!method); @@ -449,6 +450,21 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() { return delegates_list; } +const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() { + + if (!method_list_fetched) { + void *iter = NULL; + MonoMethod *raw_method = NULL; + while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { + method_list.push_back(memnew(GDMonoMethod(mono_method_get_name(raw_method), raw_method))); + } + + method_list_fetched = true; + } + + return method_list; +} + GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) { namespace_name = p_namespace; @@ -460,6 +476,7 @@ GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name attributes = NULL; methods_fetched = false; + method_list_fetched = false; fields_fetched = false; properties_fetched = false; delegates_fetched = false; @@ -512,4 +529,8 @@ GDMonoClass::~GDMonoClass() { methods.clear(); } + + for (int i = 0; i < method_list.size(); ++i) { + memdelete(method_list[i]); + } } diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 477305d503..689001f494 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -79,9 +79,14 @@ class GDMonoClass { bool attrs_fetched; MonoCustomAttrInfo *attributes; + // This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions. + // Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming. bool methods_fetched; HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods; + bool method_list_fetched; + Vector<GDMonoMethod *> method_list; + bool fields_fetched; Map<StringName, GDMonoField *> fields; Vector<GDMonoField *> fields_list; @@ -143,6 +148,8 @@ public: const Vector<GDMonoClass *> &get_all_delegates(); + const Vector<GDMonoMethod *> &get_all_methods(); + ~GDMonoClass(); }; 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_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 630bda8b4e..6ef6e97f5a 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -68,6 +68,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { param_types.push_back(param_type); } + + // clear the cache + method_info_fetched = false; + method_info = MethodInfo(); } bool GDMonoMethod::is_static() { @@ -246,11 +250,34 @@ void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const { } } +const MethodInfo &GDMonoMethod::get_method_info() { + + if (!method_info_fetched) { + method_info.name = name; + method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type), ""); + + Vector<StringName> names; + get_parameter_names(names); + + for (int i = 0; i < params_count; ++i) { + method_info.arguments.push_back(PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i]), names[i])); + } + + // TODO: default arguments + + method_info_fetched = true; + } + + return method_info; +} + GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) { name = p_name; mono_method = p_method; + method_info_fetched = false; + attrs_fetched = false; attributes = NULL; diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index 444ec2a67d..6c3ae5fce0 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -43,6 +43,9 @@ class GDMonoMethod : public GDMonoClassMember { ManagedType return_type; Vector<ManagedType> param_types; + bool method_info_fetched; + MethodInfo method_info; + bool attrs_fetched; MonoCustomAttrInfo *attributes; @@ -83,6 +86,8 @@ public: void get_parameter_names(Vector<StringName> &names) const; void get_parameter_types(Vector<ManagedType> &types) const; + const MethodInfo &get_method_info(); + GDMonoMethod(StringName p_name, MonoMethod *p_method); ~GDMonoMethod(); }; 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/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 a08ac7bd28..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) { @@ -76,6 +74,7 @@ void image_decompress_squish(Image *p_image) { p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } +#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) @@ -204,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/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 ded32ced23..080d0643a2 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/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: |