diff options
Diffstat (limited to 'modules')
157 files changed, 5319 insertions, 2496 deletions
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h index 5a2c50e213..29e09411ff 100644 --- a/modules/arkit/arkit_interface.h +++ b/modules/arkit/arkit_interface.h @@ -44,6 +44,15 @@ // forward declaration for some needed objects class ARKitShader; +#ifdef __OBJC__ + +typedef ARAnchor GodotARAnchor; + +#else + +typedef void GodotARAnchor; +#endif + class ARKitInterface : public XRInterface { GDCLASS(ARKitInterface, XRInterface); @@ -115,8 +124,8 @@ public: virtual void process() override; // called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm) - void _add_or_update_anchor(void *p_anchor); - void _remove_anchor(void *p_anchor); + void _add_or_update_anchor(GodotARAnchor *p_anchor); + void _remove_anchor(GodotARAnchor *p_anchor); ARKitInterface(); ~ARKitInterface(); diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm index 3fb2cc933d..e8fa023ac7 100644 --- a/modules/arkit/arkit_interface.mm +++ b/modules/arkit/arkit_interface.mm @@ -306,12 +306,10 @@ void ARKitInterface::uninitialize() { remove_all_anchors(); if (@available(iOS 11.0, *)) { - [ar_session release]; - ar_session = NULL; + ar_session = nil; } - [ar_delegate release]; - ar_delegate = NULL; + ar_delegate = nil; initialized = false; session_was_started = false; } @@ -687,7 +685,7 @@ void ARKitInterface::process() { } } -void ARKitInterface::_add_or_update_anchor(void *p_anchor) { +void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) { // _THREAD_SAFE_METHOD_ if (@available(iOS 11.0, *)) { @@ -749,7 +747,7 @@ void ARKitInterface::_add_or_update_anchor(void *p_anchor) { } } -void ARKitInterface::_remove_anchor(void *p_anchor) { +void ARKitInterface::_remove_anchor(GodotARAnchor *p_anchor) { // _THREAD_SAFE_METHOD_ if (@available(iOS 11.0, *)) { @@ -768,7 +766,7 @@ ARKitInterface::ARKitInterface() { plane_detection_is_enabled = false; light_estimation_is_enabled = false; if (@available(iOS 11.0, *)) { - ar_session = NULL; + ar_session = nil; } z_near = 0.01; z_far = 1000.0; diff --git a/modules/arkit/register_types.h b/modules/arkit/register_types.h index 5c697baf68..f8939a1e3f 100644 --- a/modules/arkit/register_types.h +++ b/modules/arkit/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef ARKIT_REGISTER_TYPES_H +#define ARKIT_REGISTER_TYPES_H + void register_arkit_types(); void unregister_arkit_types(); + +#endif // ARKIT_REGISTER_TYPES_H diff --git a/modules/basis_universal/texture_basisu.h b/modules/basis_universal/texture_basisu.h index 8de151ede0..20ecf15a59 100644 --- a/modules/basis_universal/texture_basisu.h +++ b/modules/basis_universal/texture_basisu.h @@ -28,6 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef BASIS_UNIVERSAL_TEXTURE_BASISU_H +#define BASIS_UNIVERSAL_TEXTURE_BASISU_H + #include "scene/resources/texture.h" #ifdef TOOLS_ENABLED @@ -75,3 +78,5 @@ public: }; #endif + +#endif // BASIS_UNIVERSAL_TEXTURE_BASISU_H diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index b35019bea3..f8f7d79a11 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -65,11 +65,14 @@ AreaBullet::~AreaBullet() { } void AreaBullet::dispatch_callbacks() { - RigidCollisionObjectBullet::dispatch_callbacks(); + if (!isScratched) { + return; + } + isScratched = false; // Reverse order because I've to remove EXIT objects for (int i = overlappingObjects.size() - 1; 0 <= i; --i) { - OverlappingObjectData &otherObj = overlappingObjects[i]; + OverlappingObjectData &otherObj = overlappingObjects.write[i]; switch (otherObj.state) { case OVERLAP_STATE_ENTER: @@ -109,9 +112,10 @@ void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3 } void AreaBullet::scratch() { - if (space != nullptr) { - space->add_to_pre_flush_queue(this); + if (isScratched) { + return; } + isScratched = true; } void AreaBullet::clear_overlaps(bool p_notify) { @@ -160,7 +164,7 @@ void AreaBullet::main_shape_changed() { btGhost->setCollisionShape(get_main_shape()); } -void AreaBullet::do_reload_body() { +void AreaBullet::reload_body() { if (space) { space->remove_area(this); space->add_area(this); @@ -169,25 +173,22 @@ void AreaBullet::do_reload_body() { void AreaBullet::set_space(SpaceBullet *p_space) { // Clear the old space if there is one - if (space) { clear_overlaps(false); + isScratched = false; // Remove this object form the physics world - space->unregister_collision_object(this); space->remove_area(this); } space = p_space; if (space) { - space->register_collision_object(this); - reload_body(); - scratch(); + space->add_area(this); } } -void AreaBullet::do_reload_collision_filters() { +void AreaBullet::on_collision_filters_change() { if (space) { space->reload_collision_filters(this); } @@ -201,13 +202,13 @@ void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) { void AreaBullet::put_overlap_as_exit(int p_index) { scratch(); - overlappingObjects[p_index].state = OVERLAP_STATE_EXIT; + overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT; } void AreaBullet::put_overlap_as_inside(int p_index) { // This check is required to be sure this body was inside if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) { - overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE; + overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE; } } diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h index 51fbc1f71d..c0bcc858fe 100644 --- a/modules/bullet/area_bullet.h +++ b/modules/bullet/area_bullet.h @@ -32,7 +32,7 @@ #define AREABULLET_H #include "collision_object_bullet.h" -#include "core/local_vector.h" +#include "core/vector.h" #include "servers/physics_server_3d.h" #include "space_bullet.h" @@ -83,7 +83,7 @@ private: Variant *call_event_res_ptr[5]; btGhostObject *btGhost; - LocalVector<OverlappingObjectData> overlappingObjects; + Vector<OverlappingObjectData> overlappingObjects; bool monitorable = true; PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED; @@ -96,6 +96,8 @@ private: real_t spOv_angularDump = 0.1; int spOv_priority = 0; + bool isScratched = false; + InOutEventCallback eventsCallbacks[2]; public: @@ -137,11 +139,11 @@ public: _FORCE_INLINE_ void set_spOv_priority(int p_priority) { spOv_priority = p_priority; } _FORCE_INLINE_ int get_spOv_priority() { return spOv_priority; } - virtual void main_shape_changed() override; - virtual void do_reload_body() override; - virtual void set_space(SpaceBullet *p_space) override; + virtual void main_shape_changed(); + virtual void reload_body(); + virtual void set_space(SpaceBullet *p_space); - virtual void dispatch_callbacks() override; + virtual void dispatch_callbacks(); void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status); void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant()); void scratch(); @@ -150,9 +152,9 @@ public: // Dispatch the callbacks and removes from overlapping list void remove_overlap(CollisionObjectBullet *p_object, bool p_notify); - virtual void do_reload_collision_filters() override; - virtual void on_collision_checker_start() override {} - virtual void on_collision_checker_end() override { isTransformChanged = false; } + virtual void on_collision_filters_change(); + virtual void on_collision_checker_start() {} + virtual void on_collision_checker_end() { isTransformChanged = false; } void add_overlap(CollisionObjectBullet *p_otherObject); void put_overlap_as_exit(int p_index); @@ -164,8 +166,8 @@ public: void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method); bool has_event_callback(Type p_callbackObjectType); - virtual void on_enter_area(AreaBullet *p_area) override; - virtual void on_exit_area(AreaBullet *p_area) override; + virtual void on_enter_area(AreaBullet *p_area); + virtual void on_exit_area(AreaBullet *p_area); }; #endif diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index eb95120f74..6078babaf8 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -52,7 +52,7 @@ class BulletPhysicsServer3D : public PhysicsServer3D { bool active = true; char active_spaces_count = 0; - LocalVector<SpaceBullet *> active_spaces; + Vector<SpaceBullet *> active_spaces; mutable RID_PtrOwner<SpaceBullet> space_owner; mutable RID_PtrOwner<ShapeBullet> shape_owner; diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp index c9493d8892..09b90fe09e 100644 --- a/modules/bullet/bullet_types_converter.cpp +++ b/modules/bullet/bullet_types_converter.cpp @@ -95,12 +95,61 @@ void G_TO_B(Transform const &inVal, btTransform &outVal) { } void UNSCALE_BT_BASIS(btTransform &scaledBasis) { - btMatrix3x3 &m(scaledBasis.getBasis()); - btVector3 column0(m[0][0], m[1][0], m[2][0]); - btVector3 column1(m[0][1], m[1][1], m[2][1]); - btVector3 column2(m[0][2], m[1][2], m[2][2]); + btMatrix3x3 &basis(scaledBasis.getBasis()); + btVector3 column0 = basis.getColumn(0); + btVector3 column1 = basis.getColumn(1); + btVector3 column2 = basis.getColumn(2); + + // Check for zero scaling. + if (column0.fuzzyZero()) { + if (column1.fuzzyZero()) { + if (column2.fuzzyZero()) { + // All dimensions are fuzzy zero. Create a default basis. + column0 = btVector3(1, 0, 0); + column1 = btVector3(0, 1, 0); + column2 = btVector3(0, 0, 1); + } else { // Column 2 scale not fuzzy zero. + // Create two vectors orthogonal to row 2. + // Ensure that a default basis is created if row 2 = <0, 0, 1> + column1 = btVector3(0, column2[2], -column2[1]); + column0 = column1.cross(column2); + } + } else { // Column 1 scale not fuzzy zero. + if (column2.fuzzyZero()) { + // Create two vectors othogonal to column 1. + // Ensure that a default basis is created if column 1 = <0, 1, 0> + column0 = btVector3(column1[1], -column1[0], 0); + column2 = column0.cross(column1); + } else { // Column 1 and column 2 scales not fuzzy zero. + // Create column 0 orthogonal to column 1 and column 2. + column0 = column1.cross(column2); + } + } + } else { // Column 0 scale not fuzzy zero. + if (column1.fuzzyZero()) { + if (column2.fuzzyZero()) { + // Create two vectors orthogonal to column 0. + // Ensure that a default basis is created if column 0 = <1, 0, 0> + column2 = btVector3(-column0[2], 0, column0[0]); + column1 = column2.cross(column0); + } else { // Column 0 and column 2 scales not fuzzy zero. + // Create column 1 orthogonal to column 0 and column 2. + column1 = column2.cross(column0); + } + } else { // Column 0 and column 1 scales not fuzzy zero. + if (column2.fuzzyZero()) { + // Create column 2 orthogonal to column 0 and column 1. + column2 = column0.cross(column1); + } + } + } + + // Normalize column0.normalize(); column1.normalize(); column2.normalize(); - m.setValue(column0[0], column1[0], column2[0], column0[1], column1[1], column2[1], column0[2], column1[2], column2[2]); + + basis.setValue(column0[0], column1[0], column2[0], + column0[1], column1[1], column2[1], + column0[2], column1[2], column2[2]); } diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 660e9afc5e..a3158a15e5 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -79,7 +79,7 @@ btTransform CollisionObjectBullet::ShapeWrapper::get_adjusted_transform() const } void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) { - if (bt_shape == nullptr) { + if (!bt_shape) { if (active) { bt_shape = shape->create_bt_shape(scale * body_scale); } else { @@ -88,13 +88,6 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s } } -void CollisionObjectBullet::ShapeWrapper::release_bt_shape() { - if (bt_shape != nullptr) { - shape->destroy_bt_shape(bt_shape); - bt_shape = nullptr; - } -} - CollisionObjectBullet::CollisionObjectBullet(Type p_type) : RIDBullet(), type(p_type) {} @@ -165,22 +158,6 @@ bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet return !bt_collision_object->checkCollideWith(p_otherCollisionObject->bt_collision_object); } -void CollisionObjectBullet::reload_body() { - needs_body_reload = true; -} - -void CollisionObjectBullet::dispatch_callbacks() {} - -void CollisionObjectBullet::pre_process() { - if (needs_body_reload) { - do_reload_body(); - } else if (needs_collision_filters_reload) { - do_reload_collision_filters(); - } - needs_body_reload = false; - needs_collision_filters_reload = false; -} - void CollisionObjectBullet::set_collision_enabled(bool p_enabled) { collisionsEnabled = p_enabled; if (collisionsEnabled) { @@ -254,7 +231,7 @@ void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform } void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) { - ShapeWrapper &shp = shapes[p_index]; + ShapeWrapper &shp = shapes.write[p_index]; shp.shape->remove_owner(this); p_shape->add_owner(this); shp.shape = p_shape; @@ -316,7 +293,7 @@ void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBod void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) { ERR_FAIL_INDEX(p_index, get_shape_count()); - shapes[p_index].set_transform(p_transform); + shapes.write[p_index].set_transform(p_transform); shape_changed(p_index); } @@ -334,7 +311,7 @@ void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled if (shapes[p_index].active != p_disabled) { return; } - shapes[p_index].active = !p_disabled; + shapes.write[p_index].active = !p_disabled; shape_changed(p_index); } @@ -342,28 +319,16 @@ bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) { return !shapes[p_index].active; } -void RigidCollisionObjectBullet::pre_process() { - if (need_shape_reload) { - do_reload_shapes(); - need_shape_reload = false; - } - CollisionObjectBullet::pre_process(); -} - void RigidCollisionObjectBullet::shape_changed(int p_shape_index) { - ShapeWrapper &shp = shapes[p_shape_index]; + ShapeWrapper &shp = shapes.write[p_shape_index]; if (shp.bt_shape == mainShape) { mainShape = nullptr; } - shp.release_bt_shape(); + bulletdelete(shp.bt_shape); reload_shapes(); } void RigidCollisionObjectBullet::reload_shapes() { - need_shape_reload = true; -} - -void RigidCollisionObjectBullet::do_reload_shapes() { if (mainShape && mainShape->isCompound()) { // Destroy compound bulletdelete(mainShape); @@ -371,38 +336,41 @@ void RigidCollisionObjectBullet::do_reload_shapes() { mainShape = nullptr; + ShapeWrapper *shpWrapper; const int shape_count = shapes.size(); - // Reset all shapes if required + // Reset shape if required if (force_shape_reset) { for (int i(0); i < shape_count; ++i) { - shapes[i].release_bt_shape(); + shpWrapper = &shapes.write[i]; + bulletdelete(shpWrapper->bt_shape); } force_shape_reset = false; } const btVector3 body_scale(get_bt_body_scale()); + // Try to optimize by not using compound if (1 == shape_count) { - // Is it possible to optimize by not using compound? - btTransform transform = shapes[0].get_adjusted_transform(); + shpWrapper = &shapes.write[0]; + btTransform transform = shpWrapper->get_adjusted_transform(); if (transform.getOrigin().isZero() && transform.getBasis() == transform.getBasis().getIdentity()) { - shapes[0].claim_bt_shape(body_scale); - mainShape = shapes[0].bt_shape; + shpWrapper->claim_bt_shape(body_scale); + mainShape = shpWrapper->bt_shape; main_shape_changed(); - // Nothing more to do return; } } - // Optimization not possible use a compound shape. + // Optimization not possible use a compound shape btCompoundShape *compoundShape = bulletnew(btCompoundShape(enableDynamicAabbTree, shape_count)); for (int i(0); i < shape_count; ++i) { - shapes[i].claim_bt_shape(body_scale); - btTransform scaled_shape_transform(shapes[i].get_adjusted_transform()); + shpWrapper = &shapes.write[i]; + shpWrapper->claim_bt_shape(body_scale); + btTransform scaled_shape_transform(shpWrapper->get_adjusted_transform()); scaled_shape_transform.getOrigin() *= body_scale; - compoundShape->addChildShape(scaled_shape_transform, shapes[i].bt_shape); + compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape); } compoundShape->recalculateLocalAabb(); @@ -416,10 +384,10 @@ void RigidCollisionObjectBullet::body_scale_changed() { } void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) { - ShapeWrapper &shp = shapes[p_index]; + ShapeWrapper &shp = shapes.write[p_index]; shp.shape->remove_owner(this, p_permanentlyFromThisBody); if (shp.bt_shape == mainShape) { mainShape = nullptr; } - shp.release_bt_shape(); + bulletdelete(shp.bt_shape); } diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h index 920d80af23..36b3a9550c 100644 --- a/modules/bullet/collision_object_bullet.h +++ b/modules/bullet/collision_object_bullet.h @@ -31,10 +31,9 @@ #ifndef COLLISION_OBJECT_BULLET_H #define COLLISION_OBJECT_BULLET_H -#include "core/local_vector.h" +#include "core/class_db.h" #include "core/math/transform.h" #include "core/math/vector3.h" -#include "core/object.h" #include "core/vset.h" #include "shape_owner_bullet.h" @@ -71,12 +70,11 @@ public: struct ShapeWrapper { ShapeBullet *shape = nullptr; + btCollisionShape *bt_shape = nullptr; btTransform transform; btVector3 scale; bool active = true; - btCollisionShape *bt_shape = nullptr; - public: ShapeWrapper() {} ShapeWrapper(ShapeBullet *p_shape, const btTransform &p_transform, bool p_active) : @@ -109,7 +107,6 @@ public: btTransform get_adjusted_transform() const; void claim_bt_shape(const btVector3 &body_scale); - void release_bt_shape(); }; protected: @@ -127,20 +124,13 @@ protected: VSet<RID> exceptions; - bool needs_body_reload = true; - bool needs_collision_filters_reload = true; - /// This array is used to know all areas where this Object is overlapped in /// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea) /// This array is used mainly to know which area hold the pointer of this object - LocalVector<AreaBullet *> areasOverlapped; + Vector<AreaBullet *> areasOverlapped; bool isTransformChanged = false; public: - bool is_in_world = false; - bool is_in_flush_queue = false; - -public: CollisionObjectBullet(Type p_type); virtual ~CollisionObjectBullet(); @@ -174,7 +164,7 @@ public: _FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) { if (collisionLayer != p_layer) { collisionLayer = p_layer; - needs_collision_filters_reload = true; + on_collision_filters_change(); } } _FORCE_INLINE_ uint32_t get_collision_layer() const { return collisionLayer; } @@ -182,32 +172,25 @@ public: _FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) { if (collisionMask != p_mask) { collisionMask = p_mask; - needs_collision_filters_reload = true; + on_collision_filters_change(); } } _FORCE_INLINE_ uint32_t get_collision_mask() const { return collisionMask; } - virtual void do_reload_collision_filters() = 0; + virtual void on_collision_filters_change() = 0; _FORCE_INLINE_ bool test_collision_mask(CollisionObjectBullet *p_other) const { return collisionLayer & p_other->collisionMask || p_other->collisionLayer & collisionMask; } - bool need_reload_body() const { - return needs_body_reload; - } - - void reload_body(); - - virtual void do_reload_body() = 0; + virtual void reload_body() = 0; virtual void set_space(SpaceBullet *p_space) = 0; _FORCE_INLINE_ SpaceBullet *get_space() const { return space; } virtual void on_collision_checker_start() = 0; virtual void on_collision_checker_end() = 0; - virtual void dispatch_callbacks(); - virtual void pre_process(); + virtual void dispatch_callbacks() = 0; void set_collision_enabled(bool p_enabled); bool is_collisions_response_enabled(); @@ -231,15 +214,14 @@ public: class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet { protected: btCollisionShape *mainShape = nullptr; - LocalVector<ShapeWrapper> shapes; - bool need_shape_reload = true; + Vector<ShapeWrapper> shapes; public: RigidCollisionObjectBullet(Type p_type) : CollisionObjectBullet(p_type) {} ~RigidCollisionObjectBullet(); - _FORCE_INLINE_ const LocalVector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; } + _FORCE_INLINE_ const Vector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; } _FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; } @@ -250,9 +232,9 @@ public: ShapeBullet *get_shape(int p_index) const; btCollisionShape *get_bt_shape(int p_index) const; - virtual int find_shape(ShapeBullet *p_shape) const override; + int find_shape(ShapeBullet *p_shape) const; - virtual void remove_shape_full(ShapeBullet *p_shape) override; + virtual void remove_shape_full(ShapeBullet *p_shape); void remove_shape_full(int p_index); void remove_all_shapes(bool p_permanentlyFromThisBody = false, bool p_force_not_reload = false); @@ -264,15 +246,11 @@ public: void set_shape_disabled(int p_index, bool p_disabled); bool is_shape_disabled(int p_index); - virtual void pre_process() override; - - virtual void shape_changed(int p_shape_index) override; - virtual void reload_shapes() override; - bool need_reload_shapes() const { return need_shape_reload; } - virtual void do_reload_shapes(); + virtual void shape_changed(int p_shape_index); + virtual void reload_shapes(); virtual void main_shape_changed() = 0; - virtual void body_scale_changed() override; + virtual void body_scale_changed(); private: void internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody = false); diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 32c3240a35..eb599df74c 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -51,7 +51,9 @@ BulletPhysicsDirectBodyState3D *BulletPhysicsDirectBodyState3D::singleton = nullptr; Vector3 BulletPhysicsDirectBodyState3D::get_total_gravity() const { - return body->total_gravity; + Vector3 gVec; + B_TO_G(body->btBody->getGravity(), gVec); + return gVec; } float BulletPhysicsDirectBodyState3D::get_total_angular_damp() const { @@ -181,7 +183,7 @@ int BulletPhysicsDirectBodyState3D::get_contact_collider_shape(int p_contact_idx } Vector3 BulletPhysicsDirectBodyState3D::get_contact_collider_velocity_at_position(int p_contact_idx) const { - RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx]; + RigidBodyBullet::CollisionData &colDat = body->collisions.write[p_contact_idx]; btVector3 hitLocation; G_TO_B(colDat.hitLocalLocation, hitLocation); @@ -211,7 +213,7 @@ void RigidBodyBullet::KinematicUtilities::setSafeMargin(btScalar p_margin) { } void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { - const LocalVector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers()); + const Vector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers()); const int shapes_count = shapes_wrappers.size(); just_delete_shapes(shapes_count); @@ -226,8 +228,8 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { continue; } - shapes[i].transform = shape_wrapper->transform; - shapes[i].transform.getOrigin() *= owner_scale; + shapes.write[i].transform = shape_wrapper->transform; + shapes.write[i].transform.getOrigin() *= owner_scale; switch (shape_wrapper->shape->get_type()) { case PhysicsServer3D::SHAPE_SPHERE: case PhysicsServer3D::SHAPE_BOX: @@ -235,11 +237,11 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { case PhysicsServer3D::SHAPE_CYLINDER: case PhysicsServer3D::SHAPE_CONVEX_POLYGON: case PhysicsServer3D::SHAPE_RAY: { - shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->internal_create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin)); + shapes.write[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin)); } break; default: WARN_PRINT("This shape is not supported for kinematic collision."); - shapes[i].shape = nullptr; + shapes.write[i].shape = nullptr; } } } @@ -247,7 +249,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) { for (int i = shapes.size() - 1; 0 <= i; --i) { if (shapes[i].shape) { - bulletdelete(shapes[i].shape); + bulletdelete(shapes.write[i].shape); } } shapes.resize(new_size); @@ -262,6 +264,7 @@ RigidBodyBullet::RigidBodyBullet() : btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, nullptr, localInertia); btBody = bulletnew(btRigidBody(cInfo)); + btBody->setFriction(1.0); reload_shapes(); setupBulletCollisionObject(btBody); @@ -269,8 +272,8 @@ RigidBodyBullet::RigidBodyBullet() : reload_axis_lock(); areasWhereIam.resize(maxAreasWhereIam); - for (uint32_t i = 0; i < areasWhereIam.size(); i += 1) { - areasWhereIam[i] = nullptr; + for (int i = areasWhereIam.size() - 1; 0 <= i; --i) { + areasWhereIam.write[i] = nullptr; } btBody->setSleepingThresholds(0.2, 0.2); @@ -305,7 +308,7 @@ void RigidBodyBullet::main_shape_changed() { set_continuous_collision_detection(is_continuous_collision_detection_enabled()); // Reset } -void RigidBodyBullet::do_reload_body() { +void RigidBodyBullet::reload_body() { if (space) { space->remove_rigid_body(this); if (get_main_shape()) { @@ -324,24 +327,23 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) { assert_no_constraints(); // Remove this object form the physics world - space->unregister_collision_object(this); space->remove_rigid_body(this); } space = p_space; if (space) { - space->register_collision_object(this); - reload_body(); - space->add_to_flush_queue(this); + space->add_rigid_body(this); } } void RigidBodyBullet::dispatch_callbacks() { - RigidCollisionObjectBullet::dispatch_callbacks(); - /// The check isFirstTransformChanged is necessary in order to call integrated forces only when the first transform is sent if ((btBody->isKinematicObject() || btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && can_integrate_forces) { + if (omit_forces_integration) { + btBody->clearForces(); + } + BulletPhysicsDirectBodyState3D *bodyDirect = BulletPhysicsDirectBodyState3D::get_singleton(this); Variant variantBodyDirect = bodyDirect; @@ -359,22 +361,16 @@ void RigidBodyBullet::dispatch_callbacks() { } } - previousActiveState = btBody->isActive(); -} - -void RigidBodyBullet::pre_process() { - RigidCollisionObjectBullet::pre_process(); - if (isScratchedSpaceOverrideModificator || 0 < countGravityPointSpaces) { isScratchedSpaceOverrideModificator = false; reload_space_override_modificator(); } - if (is_active()) { - /// Lock axis - btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor()); - btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor()); - } + /// Lock axis + btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor()); + btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor()); + + previousActiveState = btBody->isActive(); } void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata) { @@ -395,7 +391,7 @@ void RigidBodyBullet::scratch_space_override_modificator() { isScratchedSpaceOverrideModificator = true; } -void RigidBodyBullet::do_reload_collision_filters() { +void RigidBodyBullet::on_collision_filters_change() { if (space) { space->reload_collision_filters(this); } @@ -408,15 +404,14 @@ void RigidBodyBullet::on_collision_checker_start() { collisionsCount = 0; // Swap array - SWAP(prev_collision_traces, curr_collision_traces); + Vector<RigidBodyBullet *> *s = prev_collision_traces; + prev_collision_traces = curr_collision_traces; + curr_collision_traces = s; } void RigidBodyBullet::on_collision_checker_end() { // Always true if active and not a static or kinematic body isTransformChanged = btBody->isActive() && !btBody->isStaticOrKinematicObject(); - if (isTransformChanged && space != nullptr) { - space->add_to_flush_queue(this); - } } bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) { @@ -424,7 +419,7 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const return false; } - CollisionData &cd = collisions[collisionsCount]; + CollisionData &cd = collisions.write[collisionsCount]; cd.hitLocalLocation = p_hitLocalLocation; cd.otherObject = p_otherObject; cd.hitWorldLocation = p_hitWorldLocation; @@ -433,7 +428,7 @@ 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)[collisionsCount] = p_otherObject; + curr_collision_traces->write[collisionsCount] = p_otherObject; ++collisionsCount; return true; @@ -468,7 +463,6 @@ bool RigidBodyBullet::is_active() const { void RigidBodyBullet::set_omit_forces_integration(bool p_omit) { omit_forces_integration = p_omit; - scratch_space_override_modificator(); } void RigidBodyBullet::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) { @@ -811,8 +805,8 @@ const btTransform &RigidBodyBullet::get_transform__bullet() const { } } -void RigidBodyBullet::do_reload_shapes() { - RigidCollisionObjectBullet::do_reload_shapes(); +void RigidBodyBullet::reload_shapes() { + RigidCollisionObjectBullet::reload_shapes(); const btScalar invMass = btBody->getInvMass(); const btScalar mass = invMass == 0 ? 0 : 1 / invMass; @@ -844,15 +838,15 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) { for (int i = 0; i < areaWhereIamCount; ++i) { if (nullptr == areasWhereIam[i]) { // This area has the highest priority - areasWhereIam[i] = p_area; + areasWhereIam.write[i] = p_area; break; } else { if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) { // The position was found, just shift all elements - for (int j = i; j < areaWhereIamCount; ++j) { - areasWhereIam[j + 1] = areasWhereIam[j]; + for (int j = areaWhereIamCount; j > i; j--) { + areasWhereIam.write[j] = areasWhereIam[j - 1]; } - areasWhereIam[i] = p_area; + areasWhereIam.write[i] = p_area; break; } } @@ -876,7 +870,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { if (p_area == areasWhereIam[i]) { // The area was found, just shift down all elements for (int j = i; j < areaWhereIamCount; ++j) { - areasWhereIam[j] = areasWhereIam[j + 1]; + areasWhereIam.write[j] = areasWhereIam[j + 1]; } wasTheAreaFound = true; break; @@ -889,7 +883,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { } --areaWhereIamCount; - areasWhereIam[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe + areasWhereIam.write[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe if (PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) { scratch_space_override_modificator(); } @@ -901,31 +895,36 @@ void RigidBodyBullet::reload_space_override_modificator() { return; } - Vector3 newGravity; + Vector3 newGravity(0.0, 0.0, 0.0); real_t newLinearDamp = MAX(0.0, linearDamp); real_t newAngularDamp = MAX(0.0, angularDamp); + AreaBullet *currentArea; + // Variable used to calculate new gravity for gravity point areas, it is pointed by currentGravity pointer + Vector3 support_gravity(0, 0, 0); + bool stopped = false; - for (int i = 0; i < areaWhereIamCount && !stopped; i += 1) { - AreaBullet *currentArea = areasWhereIam[i]; + for (int i = areaWhereIamCount - 1; (0 <= i) && !stopped; --i) { + currentArea = areasWhereIam[i]; if (!currentArea || PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED == currentArea->get_spOv_mode()) { continue; } - Vector3 support_gravity; - /// Here is calculated the gravity if (currentArea->is_spOv_gravityPoint()) { /// It calculates the direction of new gravity support_gravity = currentArea->get_transform().xform(currentArea->get_spOv_gravityVec()) - get_transform().get_origin(); - - const real_t distanceMag = support_gravity.length(); + real_t distanceMag = support_gravity.length(); // Normalized in this way to avoid the double call of function "length()" if (distanceMag == 0) { - support_gravity = Vector3(); + support_gravity.x = 0; + support_gravity.y = 0; + support_gravity.z = 0; } else { - support_gravity /= distanceMag; + support_gravity.x /= distanceMag; + support_gravity.y /= distanceMag; + support_gravity.z /= distanceMag; } /// Here is calculated the final gravity @@ -987,17 +986,10 @@ void RigidBodyBullet::reload_space_override_modificator() { newAngularDamp += space->get_angular_damp(); } - total_gravity = newGravity; - - if (omit_forces_integration) { - // Custom behaviour. - btBody->setGravity(btVector3(0, 0, 0)); - } else { - btVector3 newBtGravity; - G_TO_B(newGravity * gravity_scale, newBtGravity); - btBody->setGravity(newBtGravity); - } + btVector3 newBtGravity; + G_TO_B(newGravity * gravity_scale, newBtGravity); + btBody->setGravity(newBtGravity); btBody->setDamping(newLinearDamp, newAngularDamp); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 047645677b..c643611397 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -171,7 +171,7 @@ public: struct KinematicUtilities { RigidBodyBullet *owner; btScalar safe_margin; - LocalVector<KinematicShape> shapes; + Vector<KinematicShape> shapes; KinematicUtilities(RigidBodyBullet *p_owner); ~KinematicUtilities(); @@ -193,7 +193,6 @@ private: PhysicsServer3D::BodyMode mode; GodotMotionState *godotMotionState; btRigidBody *btBody; - Vector3 total_gravity; uint16_t locked_axis = 0; real_t mass = 1; real_t gravity_scale = 1; @@ -203,18 +202,18 @@ private: bool omit_forces_integration = false; bool can_integrate_forces = false; - LocalVector<CollisionData> collisions; - LocalVector<RigidBodyBullet *> collision_traces_1; - LocalVector<RigidBodyBullet *> collision_traces_2; - LocalVector<RigidBodyBullet *> *prev_collision_traces; - LocalVector<RigidBodyBullet *> *curr_collision_traces; + 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 - uint32_t maxCollisionsDetection = 0; - uint32_t collisionsCount = 0; - uint32_t prev_collision_count = 0; + int maxCollisionsDetection = 0; + int collisionsCount = 0; + int prev_collision_count = 0; - LocalVector<AreaBullet *> areasWhereIam; + Vector<AreaBullet *> areasWhereIam; // these parameters are used to avoid vector resize int maxAreasWhereIam = 10; int areaWhereIamCount = 0; @@ -236,20 +235,21 @@ public: _FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; } - virtual void main_shape_changed() override; - virtual void do_reload_body() override; - virtual void set_space(SpaceBullet *p_space) override; + virtual void main_shape_changed(); + virtual void reload_body(); + virtual void set_space(SpaceBullet *p_space); - virtual void dispatch_callbacks() override; - virtual void pre_process() override; + virtual void dispatch_callbacks(); void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant()); void scratch_space_override_modificator(); - virtual void do_reload_collision_filters() override; - virtual void on_collision_checker_start() override; - virtual void on_collision_checker_end() override; + virtual void on_collision_filters_change(); + virtual void on_collision_checker_start(); + virtual void on_collision_checker_end(); + + void set_max_collisions_detection(int p_maxCollisionsDetection) { + ERR_FAIL_COND(0 > p_maxCollisionsDetection); - void set_max_collisions_detection(uint32_t p_maxCollisionsDetection) { maxCollisionsDetection = p_maxCollisionsDetection; collisions.resize(p_maxCollisionsDetection); @@ -312,19 +312,19 @@ public: void set_angular_velocity(const Vector3 &p_velocity); Vector3 get_angular_velocity() const; - virtual void set_transform__bullet(const btTransform &p_global_transform) override; - virtual const btTransform &get_transform__bullet() const override; + virtual void set_transform__bullet(const btTransform &p_global_transform); + virtual const btTransform &get_transform__bullet() const; - virtual void do_reload_shapes() override; + virtual void reload_shapes(); - virtual void on_enter_area(AreaBullet *p_area) override; - virtual void on_exit_area(AreaBullet *p_area) override; + virtual void on_enter_area(AreaBullet *p_area); + virtual void on_exit_area(AreaBullet *p_area); void reload_space_override_modificator(); /// Kinematic void reload_kinematic_shapes(); - virtual void notify_transform_changed() override; + virtual void notify_transform_changed(); private: void _internal_set_mass(real_t p_mass); diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 274493ed17..fdc2944dad 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -46,15 +46,9 @@ @author AndreaCatania */ -ShapeBullet::ShapeBullet() { -} +ShapeBullet::ShapeBullet() {} -ShapeBullet::~ShapeBullet() { - if (default_shape != nullptr) { - bulletdelete(default_shape); - default_shape = nullptr; - } -} +ShapeBullet::~ShapeBullet() {} btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge) { btVector3 s; @@ -62,22 +56,6 @@ btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, return create_bt_shape(s, p_extra_edge); } -btCollisionShape *ShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { - if (p_extra_edge == 0.0 && (p_implicit_scale - btVector3(1, 1, 1)).length2() <= CMP_EPSILON) { - return default_shape; - } - - return internal_create_bt_shape(p_implicit_scale, p_extra_edge); -} - -void ShapeBullet::destroy_bt_shape(btCollisionShape *p_shape) const { - if (p_shape != default_shape && p_shape != old_default_shape) { - if (likely(p_shape != nullptr)) { - bulletdelete(p_shape); - } - } -} - btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const { p_btShape->setUserPointer(const_cast<ShapeBullet *>(this)); p_btShape->setMargin(margin); @@ -85,21 +63,10 @@ btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const { } void ShapeBullet::notifyShapeChanged() { - // Store the old shape ptr so to not lose the reference pointer. - old_default_shape = default_shape; - // Create the new default shape with the new data. - default_shape = internal_create_bt_shape(btVector3(1, 1, 1)); - for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) { ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E->key()); owner->shape_changed(owner->find_shape(this)); } - - if (old_default_shape) { - // At this point now one has the old default shape; just delete it. - bulletdelete(old_default_shape); - old_default_shape = nullptr; - } } void ShapeBullet::add_owner(ShapeOwnerBullet *p_owner) { @@ -219,7 +186,7 @@ void PlaneShapeBullet::setup(const Plane &p_plane) { notifyShapeChanged(); } -btCollisionShape *PlaneShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { btVector3 btPlaneNormal; G_TO_B(plane.normal, btPlaneNormal); return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d)); @@ -247,7 +214,7 @@ void SphereShapeBullet::setup(real_t p_radius) { notifyShapeChanged(); } -btCollisionShape *SphereShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_extra_edge)); } @@ -274,7 +241,7 @@ void BoxShapeBullet::setup(const Vector3 &p_half_extents) { notifyShapeChanged(); } -btCollisionShape *BoxShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_extra_edge, p_extra_edge, p_extra_edge))); } @@ -307,8 +274,8 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) { notifyShapeChanged(); } -btCollisionShape *CapsuleShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { - return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge)); +btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { + return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1])); } /* Cylinder */ @@ -340,7 +307,7 @@ void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) { notifyShapeChanged(); } -btCollisionShape *CylinderShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) { +btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) { return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin)); } @@ -382,7 +349,7 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) { notifyShapeChanged(); } -btCollisionShape *ConvexPolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { if (!vertices.size()) { // This is necessary since 0 vertices return prepare(ShapeBullet::create_shape_empty()); @@ -464,7 +431,7 @@ void ConcavePolygonShapeBullet::setup(Vector<Vector3> p_faces) { notifyShapeChanged(); } -btCollisionShape *ConcavePolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape); if (!cs) { // This is necessary since if 0 faces the creation of concave return null @@ -591,7 +558,7 @@ void HeightMapShapeBullet::setup(Vector<real_t> &p_heights, int p_width, int p_d notifyShapeChanged(); } -btCollisionShape *HeightMapShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height)); cs->setLocalScaling(p_implicit_scale); prepare(cs); @@ -624,6 +591,6 @@ void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) { notifyShapeChanged(); } -btCollisionShape *RayShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { +btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) { return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_extra_edge, slips_on_slope)); } diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 6ca4d36a23..a35a1d8a18 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -53,10 +53,6 @@ class ShapeBullet : public RIDBullet { Map<ShapeOwnerBullet *, int> owners; real_t margin = 0.04; - // Contains the default shape. - btCollisionShape *default_shape = nullptr; - btCollisionShape *old_default_shape = nullptr; - protected: /// return self btCollisionShape *prepare(btCollisionShape *p_btShape) const; @@ -67,11 +63,7 @@ public: virtual ~ShapeBullet(); btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge = 0); - btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); - - void destroy_bt_shape(btCollisionShape *p_shape) const; - - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0; + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0; void add_owner(ShapeOwnerBullet *p_owner); void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false); @@ -110,7 +102,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(const Plane &p_plane); @@ -126,7 +118,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(real_t p_radius); @@ -142,7 +134,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(const Vector3 &p_half_extents); @@ -160,7 +152,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(real_t p_height, real_t p_radius); @@ -178,7 +170,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(real_t p_height, real_t p_radius); @@ -194,7 +186,7 @@ public: void get_vertices(Vector<Vector3> &out_vertices); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(const Vector<Vector3> &p_vertices); @@ -212,7 +204,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(Vector<Vector3> p_faces); @@ -231,7 +223,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height); @@ -247,7 +239,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer3D::ShapeType get_type() const; - virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0); private: void setup(real_t p_length, bool p_slips_on_slope); diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index ee48b3c5f0..6794d6c313 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -41,7 +41,7 @@ SoftBodyBullet::SoftBodyBullet() : SoftBodyBullet::~SoftBodyBullet() { } -void SoftBodyBullet::do_reload_body() { +void SoftBodyBullet::reload_body() { if (space) { space->remove_soft_body(this); space->add_soft_body(this); @@ -51,15 +51,13 @@ void SoftBodyBullet::do_reload_body() { void SoftBodyBullet::set_space(SpaceBullet *p_space) { if (space) { isScratched = false; - space->unregister_collision_object(this); space->remove_soft_body(this); } space = p_space; if (space) { - space->register_collision_object(this); - reload_body(); + space->add_soft_body(this); } } @@ -346,14 +344,14 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector indices_table.push_back(Vector<int>()); } - indices_table[vertex_id].push_back(vs_vertex_index); + indices_table.write[vertex_id].push_back(vs_vertex_index); vs_indices_to_physics_table.push_back(vertex_id); } } const int indices_map_size(indices_table.size()); - LocalVector<btScalar> bt_vertices; + Vector<btScalar> bt_vertices; { // Parse vertices to bullet @@ -361,13 +359,13 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector const Vector3 *p_vertices_read = p_vertices.ptr(); for (int i = 0; i < indices_map_size; ++i) { - bt_vertices[3 * i + 0] = p_vertices_read[indices_table[i][0]].x; - bt_vertices[3 * i + 1] = p_vertices_read[indices_table[i][0]].y; - bt_vertices[3 * i + 2] = p_vertices_read[indices_table[i][0]].z; + bt_vertices.write[3 * i + 0] = p_vertices_read[indices_table[i][0]].x; + bt_vertices.write[3 * i + 1] = p_vertices_read[indices_table[i][0]].y; + bt_vertices.write[3 * i + 2] = p_vertices_read[indices_table[i][0]].z; } } - LocalVector<int> bt_triangles; + Vector<int> bt_triangles; const int triangles_size(p_indices.size() / 3); { // Parse indices @@ -377,9 +375,9 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector const int *p_indices_read = p_indices.ptr(); for (int i = 0; i < triangles_size; ++i) { - bt_triangles[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]]; - bt_triangles[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]]; - bt_triangles[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]]; + bt_triangles.write[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]]; + bt_triangles.write[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]]; + bt_triangles.write[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]]; } } diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index 229204b539..da8a2412ed 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -32,6 +32,7 @@ #define SOFT_BODY_BULLET_H #include "collision_object_bullet.h" +#include "scene/resources/material.h" // TODO remove this please #ifdef None /// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet @@ -57,7 +58,7 @@ class SoftBodyBullet : public CollisionObjectBullet { private: btSoftBody *bt_soft_body = nullptr; - LocalVector<Vector<int>> indices_table; + Vector<Vector<int>> indices_table; btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody bool isScratched = false; @@ -72,7 +73,7 @@ private: real_t pose_matching_coefficient = 0.; // [0,1] real_t damping_coefficient = 0.01; // [0,1] real_t drag_coefficient = 0.; // [0,1] - LocalVector<int> pinned_nodes; + Vector<int> pinned_nodes; // Other property to add //btScalar kVC; // Volume conversation coefficient [0,+inf] @@ -86,14 +87,15 @@ public: SoftBodyBullet(); ~SoftBodyBullet(); - virtual void do_reload_body() override; - virtual void set_space(SpaceBullet *p_space) override; + virtual void reload_body(); + virtual void set_space(SpaceBullet *p_space); - virtual void do_reload_collision_filters() override {} - virtual void on_collision_checker_start() override {} - virtual void on_collision_checker_end() override {} - virtual void on_enter_area(AreaBullet *p_area) override; - virtual void on_exit_area(AreaBullet *p_area) override; + virtual void dispatch_callbacks() {} + virtual void on_collision_filters_change() {} + virtual void on_collision_checker_start() {} + virtual void on_collision_checker_end() {} + virtual void on_enter_area(AreaBullet *p_area); + virtual void on_exit_area(AreaBullet *p_area); _FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; } diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index d0515e7c97..c581d1804e 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -127,7 +127,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { - shape->destroy_bt_shape(btShape); + bulletdelete(btShape); ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return 0; } @@ -147,7 +147,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra btQuery.m_closestDistanceThreshold = 0; space->dynamicsWorld->contactTest(&collision_object, btQuery); - shape->destroy_bt_shape(btShape); + bulletdelete(btConvex); return btQuery.m_count; } @@ -163,7 +163,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin); if (!btShape->isConvex()) { - shape->destroy_bt_shape(btShape); + bulletdelete(btShape); ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return false; } @@ -177,7 +177,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf bt_xform_to.getOrigin() += bt_motion; if ((bt_xform_to.getOrigin() - bt_xform_from.getOrigin()).fuzzyZero()) { - shape->destroy_bt_shape(btShape); + bulletdelete(btShape); return false; } @@ -207,7 +207,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf r_closest_unsafe = 1.0f; } - shape->destroy_bt_shape(btShape); + bulletdelete(bt_convex_shape); return true; // Mean success } @@ -222,7 +222,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform & btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { - shape->destroy_bt_shape(btShape); + bulletdelete(btShape); ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return false; } @@ -243,7 +243,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform & space->dynamicsWorld->contactTest(&collision_object, btQuery); r_result_count = btQuery.m_count; - shape->destroy_bt_shape(btShape); + bulletdelete(btConvex); return btQuery.m_count; } @@ -254,7 +254,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin); if (!btShape->isConvex()) { - shape->destroy_bt_shape(btShape); + bulletdelete(btShape); ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type())); return false; } @@ -274,7 +274,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh btQuery.m_closestDistanceThreshold = 0; space->dynamicsWorld->contactTest(&collision_object, btQuery); - shape->destroy_bt_shape(btShape); + bulletdelete(btConvex); if (btQuery.m_collided) { if (btCollisionObject::CO_RIGID_BODY == btQuery.m_rest_info_collision_object->getInternalType()) { @@ -349,46 +349,14 @@ SpaceBullet::~SpaceBullet() { destroy_world(); } -void SpaceBullet::add_to_pre_flush_queue(CollisionObjectBullet *p_co) { - if (p_co->is_in_flush_queue == false) { - p_co->is_in_flush_queue = true; - queue_pre_flush.push_back(p_co); - } -} - -void SpaceBullet::add_to_flush_queue(CollisionObjectBullet *p_co) { - if (p_co->is_in_flush_queue == false) { - p_co->is_in_flush_queue = true; - queue_flush.push_back(p_co); - } -} - -void SpaceBullet::remove_from_any_queue(CollisionObjectBullet *p_co) { - if (p_co->is_in_flush_queue) { - p_co->is_in_flush_queue = false; - queue_pre_flush.erase(p_co); - queue_flush.erase(p_co); - } -} - void SpaceBullet::flush_queries() { - for (uint32_t i = 0; i < queue_pre_flush.size(); i += 1) { - queue_pre_flush[i]->dispatch_callbacks(); - queue_pre_flush[i]->is_in_flush_queue = false; - } - for (uint32_t i = 0; i < queue_flush.size(); i += 1) { - queue_flush[i]->dispatch_callbacks(); - queue_flush[i]->is_in_flush_queue = false; + const btCollisionObjectArray &colObjArray = dynamicsWorld->getCollisionObjectArray(); + for (int i = colObjArray.size() - 1; 0 <= i; --i) { + static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->dispatch_callbacks(); } - queue_pre_flush.clear(); - queue_flush.clear(); } void SpaceBullet::step(real_t p_delta_time) { - for (uint32_t i = 0; i < collision_objects.size(); i += 1) { - collision_objects[i]->pre_process(); - } - delta_time = p_delta_time; dynamicsWorld->stepSimulation(p_delta_time, 0, 0); } @@ -481,30 +449,16 @@ real_t SpaceBullet::get_param(PhysicsServer3D::SpaceParameter p_param) { } void SpaceBullet::add_area(AreaBullet *p_area) { -#ifdef TOOLS_ENABLED - // This never happen, and there is no way for the user to trigger it. - // If in future a bug is introduced into this bullet integration and this - // function is called twice, the crash will notify the developer that will - // fix it even before do the eventual PR. - CRASH_COND(p_area->is_in_world); -#endif areas.push_back(p_area); dynamicsWorld->addCollisionObject(p_area->get_bt_ghost(), p_area->get_collision_layer(), p_area->get_collision_mask()); - p_area->is_in_world = true; } void SpaceBullet::remove_area(AreaBullet *p_area) { - if (p_area->is_in_world) { - areas.erase(p_area); - dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost()); - p_area->is_in_world = false; - } + areas.erase(p_area); + dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost()); } void SpaceBullet::reload_collision_filters(AreaBullet *p_area) { - if (p_area->is_in_world == false) { - return; - } btGhostObject *ghost_object = p_area->get_bt_ghost(); btBroadphaseProxy *ghost_proxy = ghost_object->getBroadphaseHandle(); @@ -514,47 +468,24 @@ void SpaceBullet::reload_collision_filters(AreaBullet *p_area) { dynamicsWorld->refreshBroadphaseProxy(ghost_object); } -void SpaceBullet::register_collision_object(CollisionObjectBullet *p_object) { - collision_objects.push_back(p_object); -} - -void SpaceBullet::unregister_collision_object(CollisionObjectBullet *p_object) { - remove_from_any_queue(p_object); - collision_objects.erase(p_object); -} - void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) { -#ifdef TOOLS_ENABLED - // This never happen, and there is no way for the user to trigger it. - // If in future a bug is introduced into this bullet integration and this - // function is called twice, the crash will notify the developer that will - // fix it even before do the eventual PR. - CRASH_COND(p_body->is_in_world); -#endif if (p_body->is_static()) { dynamicsWorld->addCollisionObject(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask()); } else { dynamicsWorld->addRigidBody(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask()); p_body->scratch_space_override_modificator(); } - p_body->is_in_world = true; } void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) { - if (p_body->is_in_world) { - if (p_body->is_static()) { - dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body()); - } else { - dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body()); - } - p_body->is_in_world = false; + if (p_body->is_static()) { + dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body()); + } else { + dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body()); } } void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) { - if (p_body->is_in_world == false) { - return; - } btRigidBody *rigid_body = p_body->get_bt_rigid_body(); btBroadphaseProxy *body_proxy = rigid_body->getBroadphaseProxy(); @@ -734,7 +665,7 @@ void SpaceBullet::check_ghost_overlaps() { /// 1. Reset all states for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) { - AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i]; + AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i]; // This check prevent the overwrite of ENTER state // if this function is called more times before dispatchCallbacks if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) { @@ -959,8 +890,8 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f SceneTree::get_singleton()->get_current_scene()->add_child(motionVec); SceneTree::get_singleton()->get_current_scene()->add_child(normalLine); - motionVec->set_as_toplevel(true); - normalLine->set_as_toplevel(true); + motionVec->set_as_top_level(true); + normalLine->set_as_top_level(true); red_mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); red_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 897f902fe1..de281064af 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -31,8 +31,8 @@ #ifndef SPACE_BULLET_H #define SPACE_BULLET_H -#include "core/local_vector.h" #include "core/variant.h" +#include "core/vector.h" #include "godot_result_callbacks.h" #include "rid_bullet.h" #include "servers/physics_server_3d.h" @@ -110,23 +110,16 @@ class SpaceBullet : public RIDBullet { real_t linear_damp = 0.0; real_t angular_damp = 0.0; - LocalVector<CollisionObjectBullet *> queue_pre_flush; - LocalVector<CollisionObjectBullet *> queue_flush; - LocalVector<CollisionObjectBullet *> collision_objects; - LocalVector<AreaBullet *> areas; + Vector<AreaBullet *> areas; - LocalVector<Vector3> contactDebug; - uint32_t contactDebugCount = 0; + Vector<Vector3> contactDebug; + int contactDebugCount = 0; real_t delta_time = 0.; public: SpaceBullet(); virtual ~SpaceBullet(); - void add_to_flush_queue(CollisionObjectBullet *p_co); - void add_to_pre_flush_queue(CollisionObjectBullet *p_co); - void remove_from_any_queue(CollisionObjectBullet *p_co); - void flush_queries(); real_t get_delta_time() { return delta_time; } void step(real_t p_delta_time); @@ -157,9 +150,6 @@ public: void remove_area(AreaBullet *p_area); void reload_collision_filters(AreaBullet *p_area); - void register_collision_object(CollisionObjectBullet *p_object); - void unregister_collision_object(CollisionObjectBullet *p_object); - void add_rigid_body(RigidBodyBullet *p_body); void remove_rigid_body(RigidBodyBullet *p_body); void reload_collision_filters(RigidBodyBullet *p_body); @@ -183,7 +173,7 @@ public: } _FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) { if (contactDebugCount < contactDebug.size()) { - contactDebug[contactDebugCount++] = p_contact; + contactDebug.write[contactDebugCount++] = p_contact; } } _FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; } diff --git a/modules/camera/camera_ios.mm b/modules/camera/camera_ios.mm index c10b13b2af..e4cb928805 100644 --- a/modules/camera/camera_ios.mm +++ b/modules/camera/camera_ios.mm @@ -124,18 +124,12 @@ if (output) { [self removeOutput:output]; [output setSampleBufferDelegate:nil queue:NULL]; - [output release]; output = nil; } [self commitConfiguration]; } -- (void)dealloc { - // bye bye - [super dealloc]; -} - - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { // This gets called every time our camera has a new image for us to process. // May need to investigate in a way to throttle this if we get more images then we're rendering frames.. @@ -272,7 +266,6 @@ CameraFeedIOS::CameraFeedIOS() { void CameraFeedIOS::set_device(AVCaptureDevice *p_device) { device = p_device; - [device retain]; // get some info NSString *device_name = p_device.localizedName; @@ -286,14 +279,12 @@ void CameraFeedIOS::set_device(AVCaptureDevice *p_device) { }; CameraFeedIOS::~CameraFeedIOS() { - if (capture_session != NULL) { - [capture_session release]; - capture_session = NULL; + if (capture_session) { + capture_session = nil; }; - if (device != NULL) { - [device release]; - device = NULL; + if (device) { + device = nil; }; }; @@ -312,8 +303,7 @@ void CameraFeedIOS::deactivate_feed() { // end camera capture if we have one if (capture_session) { [capture_session cleanup]; - [capture_session release]; - capture_session = NULL; + capture_session = nil; }; }; @@ -347,8 +337,6 @@ void CameraFeedIOS::deactivate_feed() { // remove notifications [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil]; - - [super dealloc]; } @end @@ -453,5 +441,5 @@ CameraIOS::CameraIOS() { }; CameraIOS::~CameraIOS() { - [device_notifications release]; + device_notifications = nil; }; diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h index f2753cb6d7..e34f84bf2c 100644 --- a/modules/camera/register_types.h +++ b/modules/camera/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef CAMERA_REGISTER_TYPES_H +#define CAMERA_REGISTER_TYPES_H + void register_camera_types(); void unregister_camera_types(); + +#endif // CAMERA_REGISTER_TYPES_H diff --git a/modules/csg/icons/CSGBox3D.svg b/modules/csg/icons/CSGBox3D.svg index 67e34df444..ceef9196a7 100644 --- a/modules/csg/icons/CSGBox3D.svg +++ b/modules/csg/icons/CSGBox3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> -<path transform="translate(0 1036.4)" d="m8 0.94531-7 3.5v7.2227l7 3.5 0.29492-0.14844c-0.18282-0.30101-0.29492-0.64737-0.29492-1.0195v-2c0-0.72651 0.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c0.36419 0 0.70336 0.10754 1 0.2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/><path d="m8 .94531-7 3.5v7.2227l7 3.5.29492-.14844c-.18282-.30101-.29492-.64737-.29492-1.0195v-2c0-.72651.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c.36419 0 .70336.10754 1 .2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/></svg> diff --git a/modules/csg/icons/CSGCapsule3D.svg b/modules/csg/icons/CSGCapsule3D.svg index 92a7b5a870..14e582ee84 100644 --- a/modules/csg/icons/CSGCapsule3D.svg +++ b/modules/csg/icons/CSGCapsule3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g> -<path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922 0.092943 0 0.18367-0.008623 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72887 0.41095-1.3691 1.0059-1.7188v-0.28125c0.34771-0.034464 0.68259-0.10691 1.0156-0.19922 0.10394-0.99856 0.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-0.66556-0.1047-1.2974-0.37231-1.9941-0.66211v-1.3223c0-1.3474 0.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957 0.4088 1.9941 1.5256 1.9941 2.873v1.3457c-0.68406 0.3054-1.3142 0.57292-1.9941 0.66602v-4.8848zm-4.0059 6.334c0.67836 0.2231 1.3126 0.44599 1.9941 0.52539v2.8848c-1.1957-0.4092-1.9941-1.5237-1.9941-2.8711v-0.53906z" fill="#fc9c9c"/> -<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922.092943 0 .18367-.008623.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72887.41095-1.3691 1.0059-1.7188v-.28125c.34771-.034464.68259-.10691 1.0156-.19922.10394-.99856.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-.66556-.1047-1.2974-.37231-1.9941-.66211v-1.3223c0-1.3474.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957.4088 1.9941 1.5256 1.9941 2.873v1.3457c-.68406.3054-1.3142.57292-1.9941.66602v-4.8848zm-4.0059 6.334c.67836.2231 1.3126.44599 1.9941.52539v2.8848c-1.1957-.4092-1.9941-1.5237-1.9941-2.8711v-.53906z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg> diff --git a/modules/csg/icons/CSGCombiner3D.svg b/modules/csg/icons/CSGCombiner3D.svg index cce2902e24..50ce4179d9 100644 --- a/modules/csg/icons/CSGCombiner3D.svg +++ b/modules/csg/icons/CSGCombiner3D.svg @@ -1,8 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> -<g fill="#fc9c9c"> -<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-0.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046 0.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/> -</g> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/><path d="m3 1c-1.1046 0-2 .89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/></svg> diff --git a/modules/csg/icons/CSGCylinder3D.svg b/modules/csg/icons/CSGCylinder3D.svg index 645a74c79b..c84594928a 100644 --- a/modules/csg/icons/CSGCylinder3D.svg +++ b/modules/csg/icons/CSGCylinder3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg"> -<g> -<path transform="scale(.9375)" d="m8 1c-1.7469 0-3.328 0.22648-4.5586 0.63672-0.61528 0.20512-1.1471 0.45187-1.5898 0.80078-0.44272 0.34891-0.85156 0.88101-0.85156 1.5625v8c0 0.68149 0.40884 1.2155 0.85156 1.5645 0.44272 0.34891 0.97457 0.59577 1.5898 0.80078 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477 0.095648 0 0.18467-0.008426 0.2793-0.009766-0.1722-0.29446-0.2793-0.62995-0.2793-0.99023v-1c-1.5668 0-2.9867-0.2195-3.9277-0.5332-0.46329-0.15435-0.90474-0.33752-1.0723-0.4668v-5.8125c0.1468 0.058667 0.2835 0.12515 0.44141 0.17773 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477s3.328-0.22453 4.5586-0.63477c0.15791-0.052267 0.29461-0.11864 0.44141-0.17773v1.8125h1c0.36396 0 0.70348 0.10774 1 0.2832v-4.2832c0-0.68149-0.40884-1.2136-0.85156-1.5625-0.44272-0.34891-0.97457-0.59566-1.5898-0.80078-1.2306-0.41024-2.8117-0.63672-4.5586-0.63672zm0 2c1.5668 0 2.9867 0.22145 3.9277 0.53516 0.46368 0.15456 0.80138 0.33741 0.96875 0.4668-0.16752 0.12928-0.50546 0.3105-0.96875 0.46484-0.94102 0.31371-2.361 0.5332-3.9277 0.5332s-2.9867-0.2195-3.9277-0.5332c-0.46329-0.15435-0.80123-0.33556-0.96875-0.46484 0.16737-0.12939 0.50507-0.31224 0.96875-0.4668 0.94102-0.31371 2.361-0.53516 3.9277-0.53516z" fill="#fc9c9c" stroke-width="1.0667"/> -<path d="m11.25 8.4375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v0.9375h1.875v1.875h0.9375c0.51938 0 0.9375-0.41812 0.9375-0.9375v-1.875c0-0.51938-0.41812-0.9375-0.9375-0.9375zm0.9375 3.75h-1.875v-1.875h-0.9375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v1.875c0 0.51938 0.41812 0.9375 0.9375 0.9375h1.875c0.51938 0 0.9375-0.41812 0.9375-0.9375z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-1.7469 0-3.328.22648-4.5586.63672-.61528.20512-1.1471.45187-1.5898.80078-.44272.34891-.85156.88101-.85156 1.5625v8c0 .68149.40884 1.2155.85156 1.5645.44272.34891.97457.59577 1.5898.80078 1.2306.41024 2.8117.63477 4.5586.63477.095648 0 .18467-.008426.2793-.009766-.1722-.29446-.2793-.62995-.2793-.99023v-1c-1.5668 0-2.9867-.2195-3.9277-.5332-.46329-.15435-.90474-.33752-1.0723-.4668v-5.8125c.1468.058667.2835.12515.44141.17773 1.2306.41024 2.8117.63477 4.5586.63477s3.328-.22453 4.5586-.63477c.15791-.052267.29461-.11864.44141-.17773v1.8125h1c.36396 0 .70348.10774 1 .2832v-4.2832c0-.68149-.40884-1.2136-.85156-1.5625-.44272-.34891-.97457-.59566-1.5898-.80078-1.2306-.41024-2.8117-.63672-4.5586-.63672zm0 2c1.5668 0 2.9867.22145 3.9277.53516.46368.15456.80138.33741.96875.4668-.16752.12928-.50546.3105-.96875.46484-.94102.31371-2.361.5332-3.9277.5332s-2.9867-.2195-3.9277-.5332c-.46329-.15435-.80123-.33556-.96875-.46484.16737-.12939.50507-.31224.96875-.4668.94102-.31371 2.361-.53516 3.9277-.53516z" fill="#fc9c9c" stroke-width="1.0667" transform="scale(.9375)"/><path d="m11.25 8.4375c-.51938 0-.9375.41812-.9375.9375v.9375h1.875v1.875h.9375c.51938 0 .9375-.41812.9375-.9375v-1.875c0-.51938-.41812-.9375-.9375-.9375zm.9375 3.75h-1.875v-1.875h-.9375c-.51938 0-.9375.41812-.9375.9375v1.875c0 .51938.41812.9375.9375.9375h1.875c.51938 0 .9375-.41812.9375-.9375z" fill="#84c2ff"/></svg> diff --git a/modules/csg/icons/CSGMesh3D.svg b/modules/csg/icons/CSGMesh3D.svg index 6e940a4aa5..962e71f6ae 100644 --- a/modules/csg/icons/CSGMesh3D.svg +++ b/modules/csg/icons/CSGMesh3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g> -<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.649e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35663-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.65e-4 1.3735-0.38169 1.7305-1h3.2695v-2h-3.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l4.916 4.916c0.31428-0.20669 0.68609-0.33008 1.084-0.33008 0-0.3979 0.12338-0.76971 0.33008-1.084l-4.916-4.916h5.8574c0.17478 0.30301 0.42598 0.55488 0.72852 0.73047v3.2695h2v-3.2715c0.61771-0.35663 0.99874-1.0152 1-1.7285 0-1.1046-0.89543-2-2-2-0.71397 5.648e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35663-0.61771-1.0152-0.99874-1.7285-1z" fill="#fc9c9c"/> -<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2 .0005649.71397.38169 1.3735 1 1.7305v6.541c-.61771.35663-.99874 1.0152-1 1.7285 0 1.1046.89543 2 2 2 .71397-.000565 1.3735-.38169 1.7305-1h3.2695v-2h-3.2715c-.17478-.30301-.42598-.55488-.72852-.73047v-5.8555l4.916 4.916c.31428-.20669.68609-.33008 1.084-.33008 0-.3979.12338-.76971.33008-1.084l-4.916-4.916h5.8574c.17478.30301.42598.55488.72852.73047v3.2695h2v-3.2715c.61771-.35663.99874-1.0152 1-1.7285 0-1.1046-.89543-2-2-2-.71397.0005648-1.3735.38169-1.7305 1h-6.541c-.35663-.61771-1.0152-.99874-1.7285-1z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg> diff --git a/modules/csg/icons/CSGPolygon3D.svg b/modules/csg/icons/CSGPolygon3D.svg index 71b03cb8e6..1d496e5fd9 100644 --- a/modules/csg/icons/CSGPolygon3D.svg +++ b/modules/csg/icons/CSGPolygon3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m7.9629 1.002c-0.14254 0.00487-0.28238 0.04016-0.41016 0.10352l-6 3c-0.33878 0.16944-0.55276 0.51574-0.55273 0.89453v5.832c-0.105 0.61631 0.37487 1.1768 1 1.168h5v2c2.16e-5 0.67546 0.64487 1.1297 1.2617 0.95898-0.16118-0.28721-0.26172-0.61135-0.26172-0.95898v-2c0-0.72673 0.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c0.36397 0 0.70348 0.10774 1 0.2832v-3.2773c6e-6 -0.00195 6e-6 -0.0039094 0-0.0058594 2.6e-5 -0.37879-0.21395-0.72509-0.55273-0.89453l-6-3c-0.15022-0.074574-0.31679-0.11017-0.48438-0.10352zm0.037109 2.1172l3.7637 1.8809-2.7637 1.3809v-1.3809c-5.52e-5 -0.55226-0.44774-0.99994-1-1h-1.7617l1.7617-0.88086zm-5 2.8809h4v4h-4v-4z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> -<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9629 1.002c-.14254.00487-.28238.04016-.41016.10352l-6 3c-.33878.16944-.55276.51574-.55273.89453v5.832c-.105.61631.37487 1.1768 1 1.168h5v2c.0000216.67546.64487 1.1297 1.2617.95898-.16118-.28721-.26172-.61135-.26172-.95898v-2c0-.72673.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c.36397 0 .70348.10774 1 .2832v-3.2773c.000006-.00195.000006-.0039094 0-.0058594.000026-.37879-.21395-.72509-.55273-.89453l-6-3c-.15022-.074574-.31679-.11017-.48438-.10352zm.037109 2.1172 3.7637 1.8809-2.7637 1.3809v-1.3809c-.0000552-.55226-.44774-.99994-1-1h-1.7617l1.7617-.88086zm-5 2.8809h4v4h-4z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg> diff --git a/modules/csg/icons/CSGSphere3D.svg b/modules/csg/icons/CSGSphere3D.svg index f81b566993..639e38f49f 100644 --- a/modules/csg/icons/CSGSphere3D.svg +++ b/modules/csg/icons/CSGSphere3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g> -<path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 0.093042 0 0.18321-0.01004 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72673 0.40794-1.3664 1-1.7168v-0.33398c0.34074-0.019259 0.67728-0.069097 1.0156-0.10547 0.083091-1.0187 0.94713-1.8438 1.9844-1.8438h2c0.35841 0 0.69292 0.10484 0.98633 0.27539 0.003633-0.092184 0.013672-0.18235 0.013672-0.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-0.071342-2.6061-0.29819-3.9434-0.69141 0.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549 0.41253 3.637 2.0767 3.9414 4.1699-1.3046 0.36677-2.6158 0.60259-3.9414 0.6875v-4.8574zm-5.7793 6.2988c1.2733 0.31892 2.5337 0.50215 3.7793 0.5625v2.9414c-1.8291-0.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/> -<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 .093042 0 .18321-.01004.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72673.40794-1.3664 1-1.7168v-.33398c.34074-.019259.67728-.069097 1.0156-.10547.083091-1.0187.94713-1.8438 1.9844-1.8438h2c.35841 0 .69292.10484.98633.27539.003633-.092184.013672-.18235.013672-.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-.071342-2.6061-.29819-3.9434-.69141.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549.41253 3.637 2.0767 3.9414 4.1699-1.3046.36677-2.6158.60259-3.9414.6875zm-5.7793 6.2988c1.2733.31892 2.5337.50215 3.7793.5625v2.9414c-1.8291-.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg> diff --git a/modules/csg/icons/CSGTorus3D.svg b/modules/csg/icons/CSGTorus3D.svg index 3d30aa47b2..eb8c0f37cb 100644 --- a/modules/csg/icons/CSGTorus3D.svg +++ b/modules/csg/icons/CSGTorus3D.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m8 3c-1.8145 0-3.4691 0.41721-4.7461 1.1621-1.277 0.745-2.2539 1.9082-2.2539 3.3379 0 1.4298 0.9769 2.5949 2.2539 3.3398 1.277 0.7449 2.9316 1.1602 4.7461 1.1602 0-1.0907 0.90931-2 2-2 0-0.080836 0.013744-0.15778 0.023438-0.23633-0.61769 0.14673-1.3008 0.23633-2.0234 0.23633-1.4992 0-2.8437-0.36687-3.7383-0.88867-0.89456-0.5219-1.2617-1.108-1.2617-1.6113 0-0.5032 0.36716-1.0876 1.2617-1.6094 0.89456-0.5219 2.2391-0.89062 3.7383-0.89062s2.8437 0.36872 3.7383 0.89062c0.89456 0.5218 1.2617 1.1062 1.2617 1.6094 0 0.15978-0.053679 0.32822-0.13281 0.5h1.1328c0.32481 0 0.62893 0.088408 0.90234 0.23047 0.057552-0.23582 0.097656-0.47718 0.097656-0.73047 0-1.4297-0.9769-2.5929-2.2539-3.3379-1.277-0.7449-2.9316-1.1621-4.7461-1.1621z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> -<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 3c-1.8145 0-3.4691.41721-4.7461 1.1621-1.277.745-2.2539 1.9082-2.2539 3.3379 0 1.4298.9769 2.5949 2.2539 3.3398s2.9316 1.1602 4.7461 1.1602c0-1.0907.90931-2 2-2 0-.080836.013744-.15778.023438-.23633-.61769.14673-1.3008.23633-2.0234.23633-1.4992 0-2.8437-.36687-3.7383-.88867-.89456-.5219-1.2617-1.108-1.2617-1.6113 0-.5032.36716-1.0876 1.2617-1.6094.89456-.5219 2.2391-.89062 3.7383-.89062s2.8437.36872 3.7383.89062c.89456.5218 1.2617 1.1062 1.2617 1.6094 0 .15978-.053679.32822-.13281.5h1.1328c.32481 0 .62893.088408.90234.23047.057552-.23582.097656-.47718.097656-.73047 0-1.4297-.9769-2.5929-2.2539-3.3379-1.277-.7449-2.9316-1.1621-4.7461-1.1621z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg> diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h index 8472980c6a..36b5e332d6 100644 --- a/modules/cvtt/register_types.h +++ b/modules/cvtt/register_types.h @@ -28,14 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef TOOLS_ENABLED - #ifndef CVTT_REGISTER_TYPES_H #define CVTT_REGISTER_TYPES_H +#ifdef TOOLS_ENABLED + void register_cvtt_types(); void unregister_cvtt_types(); -#endif // CVTT_REGISTER_TYPES_H - #endif // TOOLS_ENABLED + +#endif // CVTT_REGISTER_TYPES_H diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h index d01bbd10a5..74a9d8af86 100644 --- a/modules/denoise/lightmap_denoiser.h +++ b/modules/denoise/lightmap_denoiser.h @@ -31,7 +31,7 @@ #ifndef LIGHTMAP_DENOISER_H #define LIGHTMAP_DENOISER_H -#include "core/object.h" +#include "core/class_db.h" #include "scene/3d/lightmapper.h" struct OIDNDeviceImpl; diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h index 2ffc36ee2c..f0f1f44bfe 100644 --- a/modules/denoise/register_types.h +++ b/modules/denoise/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef DENOISE_REGISTER_TYPES_H +#define DENOISE_REGISTER_TYPES_H + void register_denoise_types(); void unregister_denoise_types(); + +#endif // DENOISE_REGISTER_TYPES_H diff --git a/modules/denoise/resource_to_cpp.py b/modules/denoise/resource_to_cpp.py index 4c0b67f701..6c83277355 100644 --- a/modules/denoise/resource_to_cpp.py +++ b/modules/denoise/resource_to_cpp.py @@ -17,8 +17,6 @@ ## ======================================================================== ## import os -import sys -import argparse from array import array # Generates a C++ file from the specified binary resource file diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 26c40b625c..1fa19f4ff5 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -40,9 +40,10 @@ extern "C" { #endif +static_assert(sizeof(godot_char16_string) == sizeof(Char16String), "Char16String size mismatch"); static_assert(sizeof(godot_char_string) == sizeof(CharString), "CharString size mismatch"); static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch"); -static_assert(sizeof(godot_char_type) == sizeof(CharType), "CharType size mismatch"); +static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch"); godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs) { const CharString *cs = (const CharString *)p_cs; @@ -62,6 +63,24 @@ void GDAPI godot_char_string_destroy(godot_char_string *p_cs) { cs->~CharString(); } +godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs) { + const Char16String *cs = (const Char16String *)p_cs; + + return cs->length(); +} + +const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs) { + const Char16String *cs = (const Char16String *)p_cs; + + return cs->get_data(); +} + +void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs) { + Char16String *cs = (Char16String *)p_cs; + + cs->~Char16String(); +} + void GDAPI godot_string_new(godot_string *r_dest) { String *dest = (String *)r_dest; memnew_placement(dest, String); @@ -70,27 +89,97 @@ void GDAPI godot_string_new(godot_string *r_dest) { void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src) { String *dest = (String *)r_dest; const String *src = (const String *)p_src; - memnew_placement(dest, String(*src)); + memnew_placement(dest, String); + *dest = String(*src); +} + +void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents); +} + +void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents); +} + +void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents); +} + +void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); +} + +void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents) { + String *dest = (String *)r_dest; + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents); + } +} + +void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf8(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + dest->parse_utf16(p_contents, p_size); +} + +void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size) { + String *dest = (String *)r_dest; + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); } -void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size) { +void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size) { String *dest = (String *)r_dest; - memnew_placement(dest, String(p_contents, p_size)); + if (sizeof(wchar_t) == 2) { + // wchar_t is 16 bit, parse. + memnew_placement(dest, String); + dest->parse_utf16((const char16_t *)p_contents, p_size); + } else { + // wchar_t is 32 bit, copy. + memnew_placement(dest, String); + *dest = String((const char32_t *)p_contents, p_size); + } } -const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { +const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) { String *self = (String *)p_self; return &(self->operator[](p_idx)); } -wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) { +godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) { const String *self = (const String *)p_self; return self->operator[](p_idx); } -const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self) { +const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self) { const String *self = (const String *)p_self; - return self->c_str(); + return self->get_data(); } godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b) { @@ -162,22 +251,14 @@ godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, return self->begins_with(p_char_array); } -godot_array GDAPI godot_string_bigrams(const godot_string *p_self) { +godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector<String> return_value = self->bigrams(); - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->bigrams())); + return ret; }; -godot_string GDAPI godot_string_chr(wchar_t p_character) { +godot_string GDAPI godot_string_chr(godot_char_type p_character) { godot_string result; memnew_placement(&result, String(String::chr(p_character))); @@ -191,88 +272,73 @@ godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_ return self->ends_with(*string); } -godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) { +godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + + return self->ends_with(p_char_array); +} + +godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) { + const String *self = (const String *)p_self; + const String *what = (const String *)p_what; return self->count(*what, p_from, p_to); } -godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) { +godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->countn(*what, p_from, p_to); } -godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->find(*what); } -godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->find(*what, p_from); } -godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys) { +godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys) { const String *self = (const String *)p_self; - - Vector<String> keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - - return self->findmk(keys); + const Vector<String> *keys = (const Vector<String> *)p_keys; + return self->findmk(*keys); } -godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from) { +godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from) { const String *self = (const String *)p_self; - - Vector<String> keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - - return self->findmk(keys, p_from); + const Vector<String> *keys = (const Vector<String> *)p_keys; + return self->findmk(*keys, p_from); } -godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key) { +godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key) { const String *self = (const String *)p_self; - - Vector<String> keys; - Array *keys_proxy = (Array *)p_keys; - keys.resize(keys_proxy->size()); - for (int i = 0; i < keys_proxy->size(); i++) { - keys.write[i] = (*keys_proxy)[i]; - } - + const Vector<String> *keys = (const Vector<String> *)p_keys; int key; - int ret = self->findmk(keys, p_from, &key); + int ret = self->findmk(*keys, p_from, &key); if (r_key) { *r_key = key; } return ret; } -godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->findn(*what); } -godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->findn(*what, p_from); } @@ -303,21 +369,9 @@ godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot return result; } -godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) { - const String *self = (const String *)p_self; - - return self->hex_to_int(); -} - -godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self) { - const String *self = (const String *)p_self; - - return self->hex_to_int(true); -} - -godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string) { +godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string) { const String *self = (const String *)p_self; - String *content = (String *)&p_string; + const String *content = (const String *)p_string; godot_string result; memnew_placement(&result, String(self->insert(p_at_pos, *content))); @@ -440,58 +494,58 @@ godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int return result; } -godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replace(*key, *with))); return result; } -godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replacen(*key, *with))); return result; } -godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfind(*what); } -godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what) { +godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfindn(*what); } -godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfind(*what, p_from); } -godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) { +godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) { const String *self = (const String *)p_self; - String *what = (String *)&p_what; + const String *what = (const String *)p_what; return self->rfindn(*what, p_from); } -godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with) { +godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) { const String *self = (const String *)p_self; - String *key = (String *)&p_key; - String *with = (String *)&p_with; + const String *key = (const String *)p_key; + const String *with = (const String *)p_with; godot_string result; memnew_placement(&result, String(self->replace_first(*key, *with))); @@ -541,16 +595,16 @@ godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_f return result; } -double GDAPI godot_string_to_float(const godot_string *p_self) { +godot_int GDAPI godot_string_to_int(const godot_string *p_self) { const String *self = (const String *)p_self; - return self->to_float(); + return self->to_int(); } -godot_int GDAPI godot_string_to_int(const godot_string *p_self) { +double GDAPI godot_string_to_float(const godot_string *p_self) { const String *self = (const String *)p_self; - return self->to_int(); + return self->to_float(); } godot_string GDAPI godot_string_capitalize(const godot_string *p_self) { @@ -581,11 +635,15 @@ double GDAPI godot_string_char_to_float(const char *p_what) { return String::to_float(p_what); } +double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end) { + return String::to_float(p_str, r_end); +} + godot_int GDAPI godot_string_char_to_int(const char *p_what) { return String::to_int(p_what); } -int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str) { +godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str) { return String::to_int(p_str); } @@ -593,42 +651,32 @@ godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int return String::to_int(p_what, p_len); } -int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len) { +godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len) { return String::to_int(p_str, p_len); } -int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self) { +godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) { const String *self = (const String *)p_self; return self->hex_to_int(false); } -int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self) { +godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self) { const String *self = (const String *)p_self; return self->hex_to_int(); } -int64_t GDAPI godot_string_to_int64(const godot_string *p_self) { +godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice) { const String *self = (const String *)p_self; - - return self->to_int(); -} - -double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end) { - return String::to_float(p_str, r_end); -} - -godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice) { - const String *self = (const String *)p_self; - String *splitter = (String *)&p_splitter; + const String *splitter = (const String *)p_splitter; godot_string result; memnew_placement(&result, String(self->get_slice(*splitter, p_slice))); return result; } -godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice) { +godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice) { const String *self = (const String *)p_self; godot_string result; memnew_placement(&result, String(self->get_slicec(p_splitter, p_slice))); @@ -636,221 +684,149 @@ godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p return result; } -godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<String> return_value = self->split(*splitter, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->split(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<String> return_value = self->split(*splitter); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->split(*splitter, true))); + return ret; +} - return result; +godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) { + const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->split(*splitter, p_allow_empty, p_maxsplit))); + return ret; } -godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<float> return_value = self->split_floats(*splitter, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<float> return_value = self->split_floats(*splitter); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, true))); + return ret; } -godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) { const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - Vector<String> splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<float> return_value = self->split_floats_mk(splitters, false); + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, p_allow_empty, p_maxsplit))); + return ret; +} - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } +godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) { + const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - Vector<String> splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<float> return_value = self->split_floats_mk(splitters); + godot_packed_float32_array ret; + memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, true))); + return ret; +} - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } +godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) { + const String *self = (const String *)p_self; + const Vector<String> *splitters = (const Vector<String> *)p_splitters; - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, false))); + return ret; } -godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) { const String *self = (const String *)p_self; - const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<int> return_value = self->split_ints(*splitter, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } + const Vector<String> *splitters = (const Vector<String> *)p_splitters; - return result; + godot_packed_float32_array ret; + memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, true))); + return ret; } -godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter) { +godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; const String *splitter = (const String *)p_splitter; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<int> return_value = self->split_ints(*splitter); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - return result; + godot_packed_int32_array ret; + memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, false))); + return ret; } -godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; + const String *splitter = (const String *)p_splitter; - Vector<String> splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<int> return_value = self->split_ints_mk(splitters, false); - - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_int32_array ret; + memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, true))); + return ret; } -godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) { +godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) { const String *self = (const String *)p_self; + const Vector<String> *splitters = (const Vector<String> *)p_splitters; - Vector<String> splitters; - Array *splitter_proxy = (Array *)p_splitters; - splitters.resize(splitter_proxy->size()); - for (int i = 0; i < splitter_proxy->size(); i++) { - splitters.write[i] = (*splitter_proxy)[i]; - } - - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<int> return_value = self->split_ints_mk(splitters); + godot_packed_int32_array ret; + memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, false))); + return ret; +} - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } +godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) { + const String *self = (const String *)p_self; + const Vector<String> *splitters = (const Vector<String> *)p_splitters; - return result; + godot_packed_int32_array ret; + memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, true))); + return ret; } -godot_array GDAPI godot_string_split_spaces(const godot_string *p_self) { +godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self) { const String *self = (const String *)p_self; - godot_array result; - memnew_placement(&result, Array); - Array *proxy = (Array *)&result; - Vector<String> return_value = self->split_spaces(); - proxy->resize(return_value.size()); - for (int i = 0; i < return_value.size(); i++) { - (*proxy)[i] = return_value[i]; - } - - return result; + godot_packed_string_array ret; + memnew_placement(&ret, Vector<String>(self->split_spaces())); + return ret; } -godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter) { +godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter) { const String *self = (const String *)p_self; - String *splitter = (String *)&p_splitter; + const String *splitter = (const String *)p_splitter; return self->get_slice_count(*splitter); } -wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char) { +godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char) { return String::char_lowercase(p_char); } -wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char) { +godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char) { return String::char_uppercase(p_char); } @@ -894,7 +870,7 @@ godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos return result; } -wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) { +godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) { const String *self = (const String *)p_self; return self->ord_at(p_idx); @@ -917,6 +893,14 @@ godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_po return result; } +godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count) { + const String *self = (const String *)p_self; + godot_string result; + memnew_placement(&result, String(self->repeat(p_count))); + + return result; +} + godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right) { const String *self = (const String *)p_self; godot_string result; @@ -948,7 +932,7 @@ godot_char_string GDAPI godot_string_ascii(const godot_string *p_self) { return result; } -godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self) { +godot_char_string GDAPI godot_string_latin1(const godot_string *p_self) { const String *self = (const String *)p_self; godot_char_string result; @@ -994,6 +978,42 @@ godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot return result; } +godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self) { + const String *self = (const String *)p_self; + + godot_char16_string result; + + memnew_placement(&result, Char16String(self->utf16())); + + return result; +} + +godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16) { + String *self = (String *)p_self; + + return self->parse_utf16(p_utf16); +} + +godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len) { + String *self = (String *)p_self; + + return self->parse_utf16(p_utf16, p_len); +} + +godot_string GDAPI godot_string_chars_to_utf16(const char16_t *p_utf16) { + godot_string result; + memnew_placement(&result, String(String::utf16(p_utf16))); + + return result; +} + +godot_string GDAPI godot_string_chars_to_utf16_with_len(const char16_t *p_utf16, godot_int p_len) { + godot_string result; + memnew_placement(&result, String(String::utf16(p_utf16, p_len))); + + return result; +} + uint32_t GDAPI godot_string_hash(const godot_string *p_self) { const String *self = (const String *)p_self; @@ -1014,28 +1034,18 @@ uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_ return String::hash(p_cstr, p_len); } -uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str) { +uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str) { return String::hash(p_str); } -uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len) { +uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len) { return String::hash(p_str, p_len); } godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector<uint8_t> tmp_result = self->md5_buffer(); - godot_packed_byte_array result; - memnew_placement(&result, PackedByteArray); - PackedByteArray *proxy = (PackedByteArray *)&result; - uint8_t *proxy_writer = proxy->ptrw(); - proxy->resize(tmp_result.size()); - - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } - + memnew_placement(&result, PackedByteArray(self->md5_buffer())); return result; } @@ -1047,23 +1057,28 @@ godot_string GDAPI godot_string_md5_text(const godot_string *p_self) { return result; } -godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) { +godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self) { const String *self = (const String *)p_self; - Vector<uint8_t> tmp_result = self->sha256_buffer(); - godot_packed_byte_array result; - memnew_placement(&result, PackedByteArray); - PackedByteArray *proxy = (PackedByteArray *)&result; - uint8_t *proxy_writer = proxy->ptrw(); - proxy->resize(tmp_result.size()); + memnew_placement(&result, PackedByteArray(self->sha1_buffer())); + return result; +} - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } +godot_string GDAPI godot_string_sha1_text(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_string result; + memnew_placement(&result, String(self->sha1_text())); return result; } +godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) { + const String *self = (const String *)p_self; + godot_packed_byte_array result; + memnew_placement(&result, PackedByteArray(self->sha256_buffer())); + return result; +} + godot_string GDAPI godot_string_sha256_text(const godot_string *p_self) { const String *self = (const String *)p_self; godot_string result; @@ -1206,15 +1221,6 @@ godot_string GDAPI godot_string_json_escape(const godot_string *p_self) { return result; } -godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line) { - const String *self = (const String *)p_self; - godot_string result; - String return_value = self->word_wrap(p_chars_per_line); - memnew_placement(&result, String(return_value)); - - return result; -} - godot_string GDAPI godot_string_xml_escape(const godot_string *p_self) { const String *self = (const String *)p_self; godot_string result; @@ -1260,6 +1266,22 @@ godot_string GDAPI godot_string_percent_encode(const godot_string *p_self) { return result; } +godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts) { + const String *self = (const String *)p_self; + const Vector<String> *parts = (const Vector<String> *)p_parts; + godot_string result; + String return_value = self->join(*parts); + memnew_placement(&result, String(return_value)); + + return result; +} + +godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self) { + const String *self = (const String *)p_self; + + return self->is_valid_filename(); +} + godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self) { const String *self = (const String *)p_self; @@ -1325,31 +1347,22 @@ godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const go return result; } -godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) { +godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars) { const String *self = (const String *)p_self; String *chars = (String *)p_chars; godot_string result; - String return_value = self->rstrip(*chars); + String return_value = self->lstrip(*chars); memnew_placement(&result, String(return_value)); return result; } -godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, - const godot_bool p_allow_empty, const godot_int p_maxsplit) { +godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) { const String *self = (const String *)p_self; - String *divisor = (String *)p_divisor; - - godot_packed_string_array result; - memnew_placement(&result, PackedStringArray); - PackedStringArray *proxy = (PackedStringArray *)&result; - String *proxy_writer = proxy->ptrw(); - Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit); - proxy->resize(tmp_result.size()); - - for (int i = 0; i < tmp_result.size(); i++) { - proxy_writer[i] = tmp_result[i]; - } + String *chars = (String *)p_chars; + godot_string result; + String return_value = self->rstrip(*chars); + memnew_placement(&result, String(return_value)); return result; } diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 8ccf44ff1a..82bfbd23de 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -3732,6 +3732,27 @@ ] }, { + "name": "godot_char16_string_length", + "return_type": "godot_int", + "arguments": [ + ["const godot_char16_string *", "p_cs"] + ] + }, + { + "name": "godot_char16_string_get_data", + "return_type": "const char16_t *", + "arguments": [ + ["const godot_char16_string *", "p_cs"] + ] + }, + { + "name": "godot_char16_string_destroy", + "return_type": "void", + "arguments": [ + ["godot_char16_string *", "p_cs"] + ] + }, + { "name": "godot_string_new", "return_type": "void", "arguments": [ @@ -3747,7 +3768,83 @@ ] }, { - "name": "godot_string_new_with_wide_string", + "name": "godot_string_new_with_latin1_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf8_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf16_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char16_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_utf32_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char32_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_wide_chars", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const wchar_t *", "p_contents"] + ] + }, + { + "name": "godot_string_new_with_latin1_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf8_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf16_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char16_t *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_utf32_chars_and_len", + "return_type": "void", + "arguments": [ + ["godot_string *", "r_dest"], + ["const char32_t *", "p_contents"], + ["const int", "p_size"] + ] + }, + { + "name": "godot_string_new_with_wide_chars_and_len", "return_type": "void", "arguments": [ ["godot_string *", "r_dest"], @@ -3757,7 +3854,7 @@ }, { "name": "godot_string_operator_index", - "return_type": "const wchar_t *", + "return_type": "const godot_char_type *", "arguments": [ ["godot_string *", "p_self"], ["const godot_int", "p_idx"] @@ -3765,15 +3862,15 @@ }, { "name": "godot_string_operator_index_const", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ ["const godot_string *", "p_self"], ["const godot_int", "p_idx"] ] }, { - "name": "godot_string_wide_str", - "return_type": "const wchar_t *", + "name": "godot_string_get_data", + "return_type": "const godot_char_type *", "arguments": [ ["const godot_string *", "p_self"] ] @@ -3807,7 +3904,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"], ["godot_int", "p_to"] ] @@ -3817,7 +3914,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"], ["godot_int", "p_to"] ] @@ -3878,7 +3975,7 @@ }, { "name": "godot_string_bigrams", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"] ] @@ -3887,7 +3984,7 @@ "name": "godot_string_chr", "return_type": "godot_string", "arguments": [ - ["wchar_t", "p_character"] + ["godot_char_type", "p_character"] ] }, { @@ -3899,11 +3996,19 @@ ] }, { + "name": "godot_string_ends_with_char_array", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"], + ["const char *", "p_char_array"] + ] + }, + { "name": "godot_string_find", "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -3911,7 +4016,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -3920,7 +4025,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"] + ["const godot_packed_string_array *", "p_keys"] ] }, { @@ -3928,7 +4033,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"], + ["const godot_packed_string_array *", "p_keys"], ["godot_int", "p_from"] ] }, @@ -3937,7 +4042,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_keys"], + ["const godot_packed_string_array *", "p_keys"], ["godot_int", "p_from"], ["godot_int *", "r_key"] ] @@ -3947,7 +4052,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -3955,7 +4060,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -3985,26 +4090,12 @@ ] }, { - "name": "godot_string_hex_to_int", - "return_type": "godot_int", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { - "name": "godot_string_hex_to_int_without_prefix", - "return_type": "godot_int", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { "name": "godot_string_insert", "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], ["godot_int", "p_at_pos"], - ["godot_string", "p_string"] + ["const godot_string *", "p_string"] ] }, { @@ -4137,8 +4228,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4146,8 +4237,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4155,8 +4246,8 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_key"], - ["godot_string", "p_with"] + ["const godot_string *", "p_key"], + ["const godot_string *", "p_with"] ] }, { @@ -4164,7 +4255,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -4172,7 +4263,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"] + ["const godot_string *", "p_what"] ] }, { @@ -4180,7 +4271,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -4189,7 +4280,7 @@ "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_what"], + ["const godot_string *", "p_what"], ["godot_int", "p_from"] ] }, @@ -4237,15 +4328,15 @@ ] }, { - "name": "godot_string_to_float", - "return_type": "double", + "name": "godot_string_to_int", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_to_int", - "return_type": "godot_int", + "name": "godot_string_to_float", + "return_type": "double", "arguments": [ ["const godot_string *", "p_self"] ] @@ -4279,6 +4370,14 @@ ] }, { + "name": "godot_string_wchar_to_float", + "return_type": "double", + "arguments": [ + ["const wchar_t *", "p_str"], + ["const wchar_t **", "r_end"] + ] + }, + { "name": "godot_string_char_to_int", "return_type": "godot_int", "arguments": [ @@ -4287,7 +4386,7 @@ }, { "name": "godot_string_wchar_to_int", - "return_type": "int64_t", + "return_type": "godot_int", "arguments": [ ["const wchar_t *", "p_str"] ] @@ -4301,48 +4400,33 @@ ] }, { - "name": "godot_string_char_to_int64_with_len", - "return_type": "int64_t", + "name": "godot_string_wchar_to_int_with_len", + "return_type": "godot_int", "arguments": [ ["const wchar_t *", "p_str"], ["int", "p_len"] ] }, { - "name": "godot_string_hex_to_int64", - "return_type": "int64_t", - "arguments": [ - ["const godot_string *", "p_self"] - ] - }, - { - "name": "godot_string_hex_to_int64_with_prefix", - "return_type": "int64_t", + "name": "godot_string_hex_to_int", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_to_int64", - "return_type": "int64_t", + "name": "godot_string_hex_to_int_with_prefix", + "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_unicode_char_to_float", - "return_type": "double", - "arguments": [ - ["const wchar_t *", "p_str"], - ["const wchar_t **", "r_end"] - ] - }, - { "name": "godot_string_get_slice_count", "return_type": "godot_int", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_splitter"] + ["const godot_string *", "p_splitter"] ] }, { @@ -4350,7 +4434,7 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["godot_string", "p_splitter"], + ["const godot_string *", "p_splitter"], ["godot_int", "p_slice"] ] }, @@ -4359,13 +4443,13 @@ "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["wchar_t", "p_splitter"], + ["godot_char_type", "p_splitter"], ["godot_int", "p_slice"] ] }, { "name": "godot_string_split", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4373,23 +4457,59 @@ }, { "name": "godot_string_split_allow_empty", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, { + "name": "godot_string_split_with_maxsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, + { + "name": "godot_string_rsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_rsplit_allow_empty", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"] + ] + }, + { + "name": "godot_string_rsplit_with_maxsplit", + "return_type": "godot_packed_string_array", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_string *", "p_splitter"], + ["const godot_bool", "p_allow_empty"], + ["const godot_int", "p_maxsplit"] + ] + }, + { "name": "godot_string_split_floats", - "return_type": "godot_array", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, { - "name": "godot_string_split_floats_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_floats_allow_empty", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4397,31 +4517,31 @@ }, { "name": "godot_string_split_floats_mk", - "return_type": "godot_array", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { - "name": "godot_string_split_floats_mk_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_floats_mk_allow_empty", + "return_type": "godot_packed_float32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { "name": "godot_string_split_ints", - "return_type": "godot_array", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] ] }, { - "name": "godot_string_split_ints_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_ints_allow_empty", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], ["const godot_string *", "p_splitter"] @@ -4429,29 +4549,29 @@ }, { "name": "godot_string_split_ints_mk", - "return_type": "godot_array", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { - "name": "godot_string_split_ints_mk_allows_empty", - "return_type": "godot_array", + "name": "godot_string_split_ints_mk_allow_empty", + "return_type": "godot_packed_int32_array", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_array *", "p_splitters"] + ["const godot_packed_string_array *", "p_splitters"] ] }, { "name": "godot_string_split_spaces", - "return_type": "godot_array", + "return_type": "godot_packed_string_array", "arguments": [ ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_rstrip", + "name": "godot_string_lstrip", "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], @@ -4459,13 +4579,11 @@ ] }, { - "name": "godot_string_rsplit", - "return_type": "godot_packed_string_array", + "name": "godot_string_rstrip", + "return_type": "godot_string", "arguments": [ ["const godot_string *", "p_self"], - ["const godot_string *", "p_divisor"], - ["const godot_bool", "p_allow_empty"], - ["const godot_int", "p_maxsplit"] + ["const godot_string *", "p_chars"] ] }, { @@ -4486,16 +4604,16 @@ }, { "name": "godot_string_char_lowercase", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ - ["wchar_t", "p_char"] + ["godot_char_type", "p_char"] ] }, { "name": "godot_string_char_uppercase", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ - ["wchar_t", "p_char"] + ["godot_char_type", "p_char"] ] }, { @@ -4536,7 +4654,7 @@ }, { "name": "godot_string_ord_at", - "return_type": "wchar_t", + "return_type": "godot_char_type", "arguments": [ ["const godot_string *", "p_self"], ["godot_int", "p_idx"] @@ -4559,6 +4677,14 @@ ] }, { + "name": "godot_string_repeat", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["godot_int", "p_count"] + ] + }, + { "name": "godot_string_strip_edges", "return_type": "godot_string", "arguments": [ @@ -4591,7 +4717,7 @@ ] }, { - "name": "godot_string_ascii_extended", + "name": "godot_string_latin1", "return_type": "godot_char_string", "arguments": [ ["const godot_string *", "p_self"] @@ -4622,17 +4748,26 @@ ] }, { - "name": "godot_string_chars_to_utf8", - "return_type": "godot_string", + "name": "godot_string_utf16", + "return_type": "godot_char16_string", "arguments": [ - ["const char *", "p_utf8"] + ["const godot_string *", "p_self"] ] }, { - "name": "godot_string_chars_to_utf8_with_len", - "return_type": "godot_string", + "name": "godot_string_parse_utf16", + "return_type": "godot_bool", "arguments": [ - ["const char *", "p_utf8"], + ["godot_string *", "p_self"], + ["const char16_t *", "p_utf16"] + ] + }, + { + "name": "godot_string_parse_utf16_with_len", + "return_type": "godot_bool", + "arguments": [ + ["godot_string *", "p_self"], + ["const char16_t *", "p_utf16"], ["godot_int", "p_len"] ] }, @@ -4666,14 +4801,14 @@ ] }, { - "name": "godot_string_hash_utf8_chars", + "name": "godot_string_hash_wide_chars", "return_type": "uint32_t", "arguments": [ ["const wchar_t *", "p_str"] ] }, { - "name": "godot_string_hash_utf8_chars_with_len", + "name": "godot_string_hash_wide_chars_with_len", "return_type": "uint32_t", "arguments": [ ["const wchar_t *", "p_str"], @@ -4695,6 +4830,20 @@ ] }, { + "name": "godot_string_sha1_buffer", + "return_type": "godot_packed_byte_array", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { + "name": "godot_string_sha1_text", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { "name": "godot_string_sha256_buffer", "return_type": "godot_packed_byte_array", "arguments": [ @@ -4823,14 +4972,6 @@ ] }, { - "name": "godot_string_word_wrap", - "return_type": "godot_string", - "arguments": [ - ["const godot_string *", "p_self"], - ["godot_int", "p_chars_per_line"] - ] - }, - { "name": "godot_string_xml_escape", "return_type": "godot_string", "arguments": [ @@ -4866,6 +5007,21 @@ ] }, { + "name": "godot_string_join", + "return_type": "godot_string", + "arguments": [ + ["const godot_string *", "p_self"], + ["const godot_packed_string_array *", "p_parts"] + ] + }, + { + "name": "godot_string_is_valid_filename", + "return_type": "godot_bool", + "arguments": [ + ["const godot_string *", "p_self"] + ] + }, + { "name": "godot_string_is_valid_float", "return_type": "godot_bool", "arguments": [ diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp index fdd755845f..f0f095ddf5 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.cpp +++ b/modules/gdnative/gdnative_library_editor_plugin.cpp @@ -318,6 +318,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { platform_ios.name = "iOS"; platform_ios.entries.push_back("armv7"); platform_ios.entries.push_back("arm64"); + platform_ios.entries.push_back("x86_64"); // iOS can use both Static and Dynamic libraries. // Frameworks is actually a folder with files. platform_ios.library_extension = "*.framework; Framework, *.xcframework; Binary Framework, *.a; Static Library, *.dylib; Dynamic Library"; diff --git a/modules/gdnative/icons/GDNativeLibrary.svg b/modules/gdnative/icons/GDNativeLibrary.svg index b494c7af6e..0ddfd4e6f2 100644 --- a/modules/gdnative/icons/GDNativeLibrary.svg +++ b/modules/gdnative/icons/GDNativeLibrary.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539v-5.2695a2 2 0 0 1 -1 -1.7305 2 2 0 0 1 1 -1.7285v-0.27148h1 4.5762a5 5 0 0 0 -0.11328 -0.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm2 7v1 5 1h5c0.55228 0 1-0.4477 1-1v-5c0-0.5523-0.44772-1-1-1v4l-1-1-1 1v-4h-3z" fill="#e0e0e0"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539v-5.2695a2 2 0 0 1 -1-1.7305 2 2 0 0 1 1-1.7285v-.27148h1 4.5762a5 5 0 0 0 -.11328-.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm2 7v1 5 1h5c.55228 0 1-.4477 1-1v-5c0-.5523-.44772-1-1-1v4l-1-1-1 1v-4z" fill="#e0e0e0"/></svg> diff --git a/modules/gdnative/icons/NativeScript.svg b/modules/gdnative/icons/NativeScript.svg index fb9e135627..2224b36b29 100644 --- a/modules/gdnative/icons/NativeScript.svg +++ b/modules/gdnative/icons/NativeScript.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625h3v1 1h2v-0.95117a2 2 0 0 1 0 -0.048828 2 2 0 0 1 2 -2 2 2 0 0 1 2 2v1h5v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3 -3 3 3 0 0 0 -3 -3v-2h-2zm6 0v2h2v-2h-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3 -3h-2zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v-2zm4 0v4h2v-4h-2z" fill="#e0e0e0"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625h3v1 1h2v-.95117a2 2 0 0 1 0-.048828 2 2 0 0 1 2-2 2 2 0 0 1 2 2v1h5v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3v-2zm6 0v2h2v-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3-3zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1zm4 0v4h2v-4z" fill="#e0e0e0"/></svg> diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h index d89383dc1b..0582d95f63 100644 --- a/modules/gdnative/include/gdnative/string.h +++ b/modules/gdnative/include/gdnative/string.h @@ -38,10 +38,11 @@ extern "C" { #include <stdint.h> #include <wchar.h> -typedef wchar_t godot_char_type; +typedef char32_t godot_char_type; #define GODOT_STRING_SIZE sizeof(void *) #define GODOT_CHAR_STRING_SIZE sizeof(void *) +#define GODOT_CHAR16_STRING_SIZE sizeof(void *) #ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED #define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED @@ -58,6 +59,13 @@ typedef struct { } godot_char_string; #endif +#ifndef GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED +#define GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED +typedef struct { + uint8_t _dont_touch_that[GODOT_CHAR16_STRING_SIZE]; +} godot_char16_string; +#endif + // reduce extern "C" nesting for VS2013 #ifdef __cplusplus } @@ -75,13 +83,28 @@ godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs); const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs); void GDAPI godot_char_string_destroy(godot_char_string *p_cs); +godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs); +const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs); +void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs); + void GDAPI godot_string_new(godot_string *r_dest); void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src); -void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size); -const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); -wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); -const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self); +void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents); +void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents); +void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents); +void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents); +void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents); + +void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size); +void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size); +void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size); + +const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx); +godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx); +const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self); godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b); godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b); @@ -89,7 +112,7 @@ godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const /* Standard size stuff */ -godot_int GDAPI godot_string_length(const godot_string *p_self); +/*+++*/ godot_int GDAPI godot_string_length(const godot_string *p_self); /* Helpers */ @@ -99,24 +122,25 @@ signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, c godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string); godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array); -godot_array GDAPI godot_string_bigrams(const godot_string *p_self); -godot_string GDAPI godot_string_chr(wchar_t p_character); +godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self); +godot_string GDAPI godot_string_chr(godot_char_type p_character); godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string); -godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to); -godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to); -godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys); -godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from); -godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key); -godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); +godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array); +godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to); +godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to); +godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); +godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys); +godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from); +godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key); +godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values); godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder); godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len); godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self); -godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self); -godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string); +godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self); +godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string); godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self); godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string); godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string); @@ -133,13 +157,13 @@ godot_string GDAPI godot_string_num_scientific(double p_num); godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals); godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits); godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits); -godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with); -godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what); -godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from); -godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from); +godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with); +godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what); +godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); +godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from); godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length); godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character); godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string); @@ -151,64 +175,79 @@ godot_int GDAPI godot_string_to_int(const godot_string *p_self); godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self); godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self); godot_string GDAPI godot_string_capitalize(const godot_string *p_self); + double GDAPI godot_string_char_to_float(const char *p_what); +double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end); + godot_int GDAPI godot_string_char_to_int(const char *p_what); -int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str); +godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str); + godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len); -int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len); -int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self); -int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self); -int64_t GDAPI godot_string_to_int64(const godot_string *p_self); -double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end); - -godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter); -godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice); -godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice); - -godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter); -godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters); -godot_array GDAPI godot_string_split_spaces(const godot_string *p_self); - -wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char); -wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char); +godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len); + +godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter); +godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice); +godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice); + +godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit); + +godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit); + +godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter); +godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters); +godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters); + +godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self); + +godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char); +godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char); godot_string GDAPI godot_string_to_lower(const godot_string *p_self); godot_string GDAPI godot_string_to_upper(const godot_string *p_self); godot_string GDAPI godot_string_get_basename(const godot_string *p_self); godot_string GDAPI godot_string_get_extension(const godot_string *p_self); godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos); -wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx); +godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx); godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file); godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos); +godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count); godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right); godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self); void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars); godot_char_string GDAPI godot_string_ascii(const godot_string *p_self); -godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self); +godot_char_string GDAPI godot_string_latin1(const godot_string *p_self); + godot_char_string GDAPI godot_string_utf8(const godot_string *p_self); godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8); godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len); -godot_string GDAPI godot_string_chars_to_utf8(const char *p_utf8); -godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot_int p_len); + +godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self); +godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16); +godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len); uint32_t GDAPI godot_string_hash(const godot_string *p_self); uint64_t GDAPI godot_string_hash64(const godot_string *p_self); + uint32_t GDAPI godot_string_hash_chars(const char *p_cstr); uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len); -uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str); -uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len); +uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str); +uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len); + godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self); godot_string GDAPI godot_string_md5_text(const godot_string *p_self); +godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self); +godot_string GDAPI godot_string_sha1_text(const godot_string *p_self); godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self); godot_string GDAPI godot_string_sha256_text(const godot_string *p_self); @@ -231,14 +270,15 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self); godot_string GDAPI godot_string_http_escape(const godot_string *p_self); godot_string GDAPI godot_string_http_unescape(const godot_string *p_self); godot_string GDAPI godot_string_json_escape(const godot_string *p_self); -godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line); godot_string GDAPI godot_string_xml_escape(const godot_string *p_self); godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self); godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self); godot_string GDAPI godot_string_percent_decode(const godot_string *p_self); godot_string GDAPI godot_string_percent_encode(const godot_string *p_self); +godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts); +godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self); godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix); godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self); @@ -249,8 +289,8 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self); godot_string GDAPI godot_string_dedent(const godot_string *p_self); godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix); godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix); +godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars); godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars); -godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit); void GDAPI godot_string_destroy(godot_string *p_self); diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 019fa0d1f8..8dbaec4e75 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -176,10 +176,10 @@ List<ClassAPI> generate_c_api_classes() { // Register global constants as a fake GlobalConstants singleton class { ClassAPI global_constants_api; - global_constants_api.class_name = L"GlobalConstants"; + global_constants_api.class_name = "GlobalConstants"; global_constants_api.api_type = ClassDB::API_CORE; global_constants_api.is_singleton = true; - global_constants_api.singleton_name = L"GlobalConstants"; + global_constants_api.singleton_name = "GlobalConstants"; global_constants_api.is_instanciable = false; const int constants_count = GlobalConstants::get_global_constant_count(); for (int i = 0; i < constants_count; ++i) { diff --git a/modules/gdnative/tests/test_string.h b/modules/gdnative/tests/test_string.h new file mode 100644 index 0000000000..aeb855a1c4 --- /dev/null +++ b/modules/gdnative/tests/test_string.h @@ -0,0 +1,1980 @@ +/*************************************************************************/ +/* test_string.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 TEST_GDNATIVE_STRING_H +#define TEST_GDNATIVE_STRING_H + +namespace TestGDNativeString { + +#include "gdnative/string.h" + +#include "tests/test_macros.h" + +int u32scmp(const char32_t *l, const char32_t *r) { + for (; *l == *r && *l && *r; l++, r++) + ; + return *l - *r; +} + +TEST_CASE("[GDNative String] Construct from Latin-1 char string") { + godot_string s; + + godot_string_new_with_latin1_chars(&s, "Hello"); + CHECK(u32scmp(godot_string_get_data(&s), U"Hello") == 0); + godot_string_destroy(&s); + + godot_string_new_with_latin1_chars_and_len(&s, "Hello", 3); + CHECK(u32scmp(godot_string_get_data(&s), U"Hel") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Construct from wchar_t string") { + godot_string s; + + godot_string_new_with_wide_chars(&s, L"Give me"); + CHECK(u32scmp(godot_string_get_data(&s), U"Give me") == 0); + godot_string_destroy(&s); + + godot_string_new_with_wide_chars_and_len(&s, L"Give me", 3); + CHECK(u32scmp(godot_string_get_data(&s), U"Giv") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Construct from UTF-8 char string") { + static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 }; + static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + + godot_string s; + + godot_string_new_with_utf8_chars(&s, (const char *)u8str); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 5); + CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf32_chars(&s, u32str); + godot_char_string cs = godot_string_utf8(&s); + godot_string_parse_utf8(&s, godot_char_string_get_data(&cs)); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + godot_char_string_destroy(&cs); + + godot_string_new_with_utf32_chars(&s, u32str); + cs = godot_string_utf8(&s); + godot_string_parse_utf8_with_len(&s, godot_char_string_get_data(&cs), godot_char_string_length(&cs)); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + godot_char_string_destroy(&cs); +} + +TEST_CASE("[GDNative String] Construct from UTF-8 string with BOM") { + static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 }; + static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + + godot_string s; + + godot_string_new_with_utf8_chars(&s, (const char *)u8str); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 8); + CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Construct from UTF-16 string") { + static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 }; + static const char16_t u16str[] = { 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; + + godot_string s; + + godot_string_new_with_utf16_chars(&s, u16str); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf16_chars_and_len(&s, u16str, 4); + CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf32_chars(&s, u32str); + godot_char16_string cs = godot_string_utf16(&s); + godot_string_parse_utf16(&s, godot_char16_string_get_data(&cs)); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + godot_char16_string_destroy(&cs); + + godot_string_new_with_utf32_chars(&s, u32str); + cs = godot_string_utf16(&s); + godot_string_parse_utf16_with_len(&s, godot_char16_string_get_data(&cs), godot_char16_string_length(&cs)); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + godot_char16_string_destroy(&cs); +} + +TEST_CASE("[GDNative String] Construct from UTF-16 string with BOM ") { + static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; + static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 }; + static const char16_t u16str[] = { 0xFEFF, 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; + static const char16_t u16str_swap[] = { 0xFFFE, 0x4500, 0x2000, 0x3CD8, 0xA4DF, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 }; + + godot_string s; + + godot_string_new_with_utf16_chars(&s, u16str); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf16_chars(&s, u16str_swap); + CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf16_chars_and_len(&s, u16str, 5); + CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0); + godot_string_destroy(&s); + + godot_string_new_with_utf16_chars_and_len(&s, u16str_swap, 5); + CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Construct string copy") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "Hello"); + godot_string_new_copy(&t, &s); + CHECK(u32scmp(godot_string_get_data(&t), U"Hello") == 0); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Construct empty string") { + godot_string s; + + godot_string_new(&s); + CHECK(u32scmp(godot_string_get_data(&s), U"") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] ASCII/Latin-1") { + godot_string s; + godot_string_new_with_utf32_chars(&s, U"Primero Leche"); + + godot_char_string cs = godot_string_ascii(&s); + CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0); + godot_char_string_destroy(&cs); + + cs = godot_string_latin1(&s); + CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0); + godot_char_string_destroy(&cs); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Comparisons (equal)") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "Test Compare"); + godot_string_new_with_latin1_chars(&t, "Test Compare"); + CHECK(godot_string_operator_equal(&s, &t)); + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] Comparisons (operator <)") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "Bees"); + + godot_string_new_with_latin1_chars(&t, "Elephant"); + CHECK(godot_string_operator_less(&s, &t)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Amber"); + CHECK(!godot_string_operator_less(&s, &t)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Beatrix"); + CHECK(!godot_string_operator_less(&s, &t)); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Concatenation (operator +)") { + godot_string s, t, x; + + godot_string_new_with_latin1_chars(&s, "Hel"); + godot_string_new_with_latin1_chars(&t, "lo"); + x = godot_string_operator_plus(&s, &t); + CHECK(u32scmp(godot_string_get_data(&x), U"Hello") == 0); + godot_string_destroy(&x); + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] Testing size and length of string") { + godot_string s; + + godot_string_new_with_latin1_chars(&s, "Mellon"); + CHECK(godot_string_length(&s) == 6); + godot_string_destroy(&s); + + godot_string_new_with_latin1_chars(&s, "Mellon1"); + CHECK(godot_string_length(&s) == 7); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Testing for empty string") { + godot_string s; + + godot_string_new_with_latin1_chars(&s, "Mellon"); + CHECK(!godot_string_empty(&s)); + godot_string_destroy(&s); + + godot_string_new_with_latin1_chars(&s, ""); + CHECK(godot_string_empty(&s)); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Test chr") { + godot_string s; + + s = godot_string_chr('H'); + CHECK(u32scmp(godot_string_get_data(&s), U"H") == 0); + godot_string_destroy(&s); + + s = godot_string_chr(0x3012); + CHECK(godot_string_operator_index_const(&s, 0) == 0x3012); + godot_string_destroy(&s); + + ERR_PRINT_OFF + s = godot_string_chr(0xd812); + CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Unpaired UTF-16 surrogate + godot_string_destroy(&s); + + s = godot_string_chr(0x20d812); + CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Outside UTF-32 range + godot_string_destroy(&s); + ERR_PRINT_ON +} + +TEST_CASE("[GDNative String] Operator []") { + godot_string s; + + godot_string_new_with_latin1_chars(&s, "Hello"); + CHECK(*godot_string_operator_index(&s, 1) == 'e'); + CHECK(godot_string_operator_index_const(&s, 0) == 'H'); + CHECK(godot_string_ord_at(&s, 0) == 'H'); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Case function test") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "MoMoNgA"); + + t = godot_string_to_upper(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"MOMONGA") == 0); + godot_string_destroy(&t); + + t = godot_string_to_lower(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"momonga") == 0); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Case compare function test") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "MoMoNgA"); + godot_string_new_with_latin1_chars(&t, "momonga"); + + CHECK(godot_string_casecmp_to(&s, &t) != 0); + CHECK(godot_string_nocasecmp_to(&s, &t) == 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] Natural compare function test") { + godot_string s, t; + + godot_string_new_with_latin1_chars(&s, "img2.png"); + godot_string_new_with_latin1_chars(&t, "img10.png"); + + CHECK(godot_string_nocasecmp_to(&s, &t) > 0); + CHECK(godot_string_naturalnocasecmp_to(&s, &t) < 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] hex_encode_buffer") { + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 }; + godot_string s = godot_string_hex_encode_buffer(u8str, 6); + CHECK(u32scmp(godot_string_get_data(&s), U"45e3818a8fe3") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Substr") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Killer Baby"); + t = godot_string_substr(&s, 3, 4); + CHECK(u32scmp(godot_string_get_data(&t), U"ler ") == 0); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Find") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Pretty Woman Woman"); + + godot_string_new_with_latin1_chars(&t, "Revenge of the Monster Truck"); + CHECK(godot_string_find(&s, &t) == -1); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "tty"); + CHECK(godot_string_find(&s, &t) == 3); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Wo"); + CHECK(godot_string_find_from(&s, &t, 9) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "man"); + CHECK(godot_string_rfind(&s, &t) == 15); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Find no case") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Pretty Whale Whale"); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_findn(&s, &t) == 7); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_findn_from(&s, &t, 9) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "WHA"); + CHECK(godot_string_rfindn(&s, &t) == 13); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Revenge of the Monster SawFish"); + CHECK(godot_string_findn(&s, &t) == -1); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Find MK") { + godot_packed_string_array keys; + godot_packed_string_array_new(&keys); + +#define PUSH_KEY(x) \ + { \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + godot_packed_string_array_push_back(&keys, &t); \ + godot_string_destroy(&t); \ + } + + PUSH_KEY("sty") + PUSH_KEY("tty") + PUSH_KEY("man") + + godot_string s; + godot_string_new_with_latin1_chars(&s, "Pretty Woman"); + godot_int key = 0; + + CHECK(godot_string_findmk(&s, &keys) == 3); + CHECK(godot_string_findmk_from_in_place(&s, &keys, 0, &key) == 3); + CHECK(key == 1); + + CHECK(godot_string_findmk_from(&s, &keys, 5) == 9); + CHECK(godot_string_findmk_from_in_place(&s, &keys, 5, &key) == 9); + CHECK(key == 2); + + godot_string_destroy(&s); + godot_packed_string_array_destroy(&keys); + +#undef PUSH_KEY +} + +TEST_CASE("[GDNative String] Find and replace") { + godot_string s, c, w; + godot_string_new_with_latin1_chars(&s, "Happy Birthday, Anna!"); + godot_string_new_with_latin1_chars(&c, "Birthday"); + godot_string_new_with_latin1_chars(&w, "Halloween"); + godot_string t = godot_string_replace(&s, &c, &w); + CHECK(u32scmp(godot_string_get_data(&t), U"Happy Halloween, Anna!") == 0); + godot_string_destroy(&s); + godot_string_destroy(&c); + godot_string_destroy(&w); + + godot_string_new_with_latin1_chars(&c, "H"); + godot_string_new_with_latin1_chars(&w, "W"); + s = godot_string_replace_first(&t, &c, &w); + godot_string_destroy(&t); + godot_string_destroy(&c); + godot_string_destroy(&w); + + CHECK(u32scmp(godot_string_get_data(&s), U"Wappy Halloween, Anna!") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Insertion") { + godot_string s, t, r, u; + godot_string_new_with_latin1_chars(&s, "Who is Frederic?"); + godot_string_new_with_latin1_chars(&t, "?"); + godot_string_new_with_latin1_chars(&r, " Chopin"); + + u = godot_string_insert(&s, godot_string_find(&s, &t), &r); + CHECK(u32scmp(godot_string_get_data(&u), U"Who is Frederic Chopin?") == 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); + godot_string_destroy(&r); + godot_string_destroy(&u); +} + +TEST_CASE("[GDNative String] Number to string") { + godot_string s; + s = godot_string_num(3.141593); + CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_with_decimals(3.141593, 3); + CHECK(u32scmp(godot_string_get_data(&s), U"3.142") == 0); + godot_string_destroy(&s); + + s = godot_string_num_real(3.141593); + CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_scientific(30000000); + CHECK(u32scmp(godot_string_get_data(&s), U"3e+07") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64(3141593, 10); + CHECK(u32scmp(godot_string_get_data(&s), U"3141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64(0xA141593, 16); + CHECK(u32scmp(godot_string_get_data(&s), U"a141593") == 0); + godot_string_destroy(&s); + + s = godot_string_num_int64_capitalized(0xA141593, 16, true); + CHECK(u32scmp(godot_string_get_data(&s), U"A141593") == 0); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] String to integer") { + static const wchar_t *wnums[4] = { L"1237461283", L"- 22", L"0", L" - 1123412" }; + static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" }; + static const int num[4] = { 1237461283, -22, 0, -1123412 }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK(godot_string_to_int(&s) == num[i]); + godot_string_destroy(&s); + + CHECK(godot_string_char_to_int(nums[i]) == num[i]); + CHECK(godot_string_wchar_to_int(wnums[i]) == num[i]); + } +} + +TEST_CASE("[GDNative String] Hex to integer") { + static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" }; + static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD }; + static const bool wo_prefix[4] = { false, true, true, true }; + static const bool w_prefix[4] = { true, false, true, false }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK((godot_string_hex_to_int_with_prefix(&s) == num[i]) == w_prefix[i]); + CHECK((godot_string_hex_to_int(&s) == num[i]) == wo_prefix[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNative String] String to float") { + static const wchar_t *wnums[4] = { L"-12348298412.2", L"0.05", L"2.0002", L" -0.0001" }; + static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" }; + static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 }; + + for (int i = 0; i < 4; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, nums[i]); + CHECK(!(ABS(godot_string_to_float(&s) - num[i]) > 0.00001)); + godot_string_destroy(&s); + + CHECK(!(ABS(godot_string_char_to_float(nums[i]) - num[i]) > 0.00001)); + CHECK(!(ABS(godot_string_wchar_to_float(wnums[i], nullptr) - num[i]) > 0.00001)); + } +} + +TEST_CASE("[GDNative String] CamelCase to underscore") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "TestTestStringGD"); + + t = godot_string_camelcase_to_underscore(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"Test_Test_String_GD") == 0); + godot_string_destroy(&t); + + t = godot_string_camelcase_to_underscore_lowercased(&s); + CHECK(u32scmp(godot_string_get_data(&t), U"test_test_string_gd") == 0); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Slicing") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus"); + godot_string_new_with_latin1_chars(&c, ","); + + const char32_t *slices[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" }; + for (int i = 0; i < godot_string_get_slice_count(&s, &c); i++) { + godot_string t; + t = godot_string_get_slice(&s, &c, i); + CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0); + godot_string_destroy(&t); + + t = godot_string_get_slicec(&s, U',', i); + CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0); + godot_string_destroy(&t); + } + + godot_string_destroy(&c); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Splitting") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus"); + godot_string_new_with_latin1_chars(&c, ","); + + godot_packed_string_array l; + + const char32_t *slices_l[3] = { U"Mars", U"Jupiter", U"Saturn,Uranus" }; + const char32_t *slices_r[3] = { U"Mars,Jupiter", U"Saturn", U"Uranus" }; + + l = godot_string_split_with_maxsplit(&s, &c, true, 2); + CHECK(godot_packed_string_array_size(&l) == 3); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_l[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + + l = godot_string_rsplit_with_maxsplit(&s, &c, true, 2); + CHECK(godot_packed_string_array_size(&l) == 3); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_r[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + godot_string_destroy(&s); + + godot_string_new_with_latin1_chars(&s, "Mars Jupiter Saturn Uranus"); + const char32_t *slices_s[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" }; + l = godot_string_split_spaces(&s); + for (int i = 0; i < godot_packed_string_array_size(&l); i++) { + godot_string t = godot_packed_string_array_get(&l, i); + CHECK(u32scmp(godot_string_get_data(&t), slices_s[i]) == 0); + godot_string_destroy(&t); + } + godot_packed_string_array_destroy(&l); + godot_string_destroy(&s); + + godot_string c1, c2; + godot_string_new_with_latin1_chars(&c1, ";"); + godot_string_new_with_latin1_chars(&c2, " "); + + godot_string_new_with_latin1_chars(&s, "1.2;2.3 4.5"); + const double slices_d[3] = { 1.2, 2.3, 4.5 }; + + godot_packed_float32_array lf = godot_string_split_floats_allow_empty(&s, &c1); + CHECK(godot_packed_float32_array_size(&lf) == 2); + for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) { + CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001); + } + godot_packed_float32_array_destroy(&lf); + + godot_packed_string_array keys; + godot_packed_string_array_new(&keys); + godot_packed_string_array_push_back(&keys, &c1); + godot_packed_string_array_push_back(&keys, &c2); + + lf = godot_string_split_floats_mk_allow_empty(&s, &keys); + CHECK(godot_packed_float32_array_size(&lf) == 3); + for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) { + CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001); + } + godot_packed_float32_array_destroy(&lf); + + godot_string_destroy(&s); + godot_string_new_with_latin1_chars(&s, "1;2 4"); + const int slices_i[3] = { 1, 2, 4 }; + + godot_packed_int32_array li = godot_string_split_ints_allow_empty(&s, &c1); + CHECK(godot_packed_int32_array_size(&li) == 2); + for (int i = 0; i < godot_packed_int32_array_size(&li); i++) { + CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]); + } + godot_packed_int32_array_destroy(&li); + + li = godot_string_split_ints_mk_allow_empty(&s, &keys); + CHECK(godot_packed_int32_array_size(&li) == 3); + for (int i = 0; i < godot_packed_int32_array_size(&li); i++) { + CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]); + } + godot_packed_int32_array_destroy(&li); + + godot_string_destroy(&s); + godot_string_destroy(&c); + godot_string_destroy(&c1); + godot_string_destroy(&c2); + godot_packed_string_array_destroy(&keys); +} + +TEST_CASE("[GDNative String] Erasing") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "Josephine is such a cute girl!"); + godot_string_new_with_latin1_chars(&t, "cute "); + + godot_string_erase(&s, godot_string_find(&s, &t), godot_string_length(&t)); + + CHECK(u32scmp(godot_string_get_data(&s), U"Josephine is such a girl!") == 0); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +struct test_27_data { + char const *data; + char const *part; + bool expected; +}; + +TEST_CASE("[GDNative String] Begins with") { + test_27_data tc[] = { + { "res://foobar", "res://", true }, + { "res", "res://", false }, + { "abc", "abc", true } + }; + size_t count = sizeof(tc) / sizeof(tc[0]); + bool state = true; + for (size_t i = 0; state && i < count; ++i) { + godot_string s; + godot_string_new_with_latin1_chars(&s, tc[i].data); + + state = godot_string_begins_with_char_array(&s, tc[i].part) == tc[i].expected; + if (state) { + godot_string t; + godot_string_new_with_latin1_chars(&t, tc[i].part); + state = godot_string_begins_with(&s, &t) == tc[i].expected; + godot_string_destroy(&t); + } + godot_string_destroy(&s); + + CHECK(state); + if (!state) { + break; + } + }; + CHECK(state); +} + +TEST_CASE("[GDNative String] Ends with") { + test_27_data tc[] = { + { "res://foobar", "foobar", true }, + { "res", "res://", false }, + { "abc", "abc", true } + }; + size_t count = sizeof(tc) / sizeof(tc[0]); + bool state = true; + for (size_t i = 0; state && i < count; ++i) { + godot_string s; + godot_string_new_with_latin1_chars(&s, tc[i].data); + + state = godot_string_ends_with_char_array(&s, tc[i].part) == tc[i].expected; + if (state) { + godot_string t; + godot_string_new_with_latin1_chars(&t, tc[i].part); + state = godot_string_ends_with(&s, &t) == tc[i].expected; + godot_string_destroy(&t); + } + godot_string_destroy(&s); + + CHECK(state); + if (!state) { + break; + } + }; + CHECK(state); +} + +TEST_CASE("[GDNative String] format") { + godot_string value_format, t; + godot_string_new_with_latin1_chars(&value_format, "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""); + + godot_variant key_v, val_v; + godot_dictionary value_dictionary; + godot_dictionary_new(&value_dictionary); + + godot_string_new_with_latin1_chars(&t, "red"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_int(&val_v, 10); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "green"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_int(&val_v, 20); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "blue"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "bla"); + godot_variant_new_string(&val_v, &t); + godot_string_destroy(&t); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_string_new_with_latin1_chars(&t, "alpha"); + godot_variant_new_string(&key_v, &t); + godot_string_destroy(&t); + godot_variant_new_real(&val_v, 0.4); + godot_dictionary_set(&value_dictionary, &key_v, &val_v); + godot_variant_destroy(&key_v); + godot_variant_destroy(&val_v); + + godot_variant dict_v; + godot_variant_new_dictionary(&dict_v, &value_dictionary); + godot_string s = godot_string_format_with_custom_placeholder(&value_format, &dict_v, "$_"); + + CHECK(u32scmp(godot_string_get_data(&s), U"red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"") == 0); + + godot_dictionary_destroy(&value_dictionary); + godot_string_destroy(&s); + godot_variant_destroy(&dict_v); + godot_string_destroy(&value_format); +} + +TEST_CASE("[GDNative String] sprintf") { + //godot_string GDAPI (const godot_string *p_self, const godot_array *p_values, godot_bool *p_error); + godot_string format, output; + godot_array args; + bool error; + +#define ARRAY_PUSH_STRING(x) \ + { \ + godot_variant v; \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + godot_variant_new_string(&v, &t); \ + godot_string_destroy(&t); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + +#define ARRAY_PUSH_INT(x) \ + { \ + godot_variant v; \ + godot_variant_new_int(&v, x); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + +#define ARRAY_PUSH_REAL(x) \ + { \ + godot_variant v; \ + godot_variant_new_real(&v, x); \ + godot_array_push_back(&args, &v); \ + godot_variant_destroy(&v); \ + } + + godot_array_new(&args); + + // %% + godot_string_new_with_latin1_chars(&format, "fish %% frog"); + godot_array_clear(&args); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish % frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + //////// INTS + + // Int + godot_string_new_with_latin1_chars(&format, "fish %d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int left padded with zeroes. + godot_string_new_with_latin1_chars(&format, "fish %05d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 00005 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int left padded with spaces. + godot_string_new_with_latin1_chars(&format, "fish %5d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int right padded with spaces. + godot_string_new_with_latin1_chars(&format, "fish %-5d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int with sign (positive). + godot_string_new_with_latin1_chars(&format, "fish %+d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish +5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Negative int. + godot_string_new_with_latin1_chars(&format, "fish %d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(-5); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish -5 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Hex (lower) + godot_string_new_with_latin1_chars(&format, "fish %x frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(45); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 2d frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Hex (upper) + godot_string_new_with_latin1_chars(&format, "fish %X frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(45); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 2D frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Octal + godot_string_new_with_latin1_chars(&format, "fish %o frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 143 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + ////// REALS + + // Real + godot_string_new_with_latin1_chars(&format, "fish %f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real left-padded + godot_string_new_with_latin1_chars(&format, "fish %11f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real right-padded + godot_string_new_with_latin1_chars(&format, "fish %-11f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real given int. + godot_string_new_with_latin1_chars(&format, "fish %f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.000000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with sign (positive). + godot_string_new_with_latin1_chars(&format, "fish %+f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish +99.990000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with 1 decimals. + godot_string_new_with_latin1_chars(&format, "fish %.1f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 100.0 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with 12 decimals. + godot_string_new_with_latin1_chars(&format, "fish %.12f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000000000 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Real with no decimals. + godot_string_new_with_latin1_chars(&format, "fish %.f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 100 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + /////// Strings. + + // String + godot_string_new_with_latin1_chars(&format, "fish %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // String left-padded + godot_string_new_with_latin1_chars(&format, "fish %10s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // String right-padded + godot_string_new_with_latin1_chars(&format, "fish %-10s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Characters + + // Character as string. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("A"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character as int. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(65); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Dynamic width + + // String dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*s frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + REQUIRE(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Int dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*d frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_INT(99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + REQUIRE(u32scmp(godot_string_get_data(&output), U"fish 99 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Float dynamic width + godot_string_new_with_latin1_chars(&format, "fish %*.*f frog"); + godot_array_clear(&args); + ARRAY_PUSH_INT(10); + ARRAY_PUSH_INT(3); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error == false); + CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990 frog") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + ///// Errors + + // More formats than arguments. + godot_string_new_with_latin1_chars(&format, "fish %s %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"not enough arguments for format string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // More arguments than formats. + godot_string_new_with_latin1_chars(&format, "fish %s frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("hello"); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"not all arguments converted during string formatting") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Incomplete format. + godot_string_new_with_latin1_chars(&format, "fish %10"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"incomplete format") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Bad character in format string + godot_string_new_with_latin1_chars(&format, "fish %&f frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"unsupported format character") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Too many decimals. + godot_string_new_with_latin1_chars(&format, "fish %2.2.2f frog"); + godot_array_clear(&args); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"too many decimal points in format") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // * not a number + godot_string_new_with_latin1_chars(&format, "fish %*f frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("cheese"); + ARRAY_PUSH_REAL(99.99); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"* wants number") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character too long. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + ARRAY_PUSH_STRING("sc"); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + // Character bad type. + godot_string_new_with_latin1_chars(&format, "fish %c frog"); + godot_array_clear(&args); + godot_array t; + godot_array_new(&t); + godot_variant v; + godot_variant_new_array(&v, &t); + godot_array_destroy(&t); + godot_array_push_back(&args, &v); + godot_variant_destroy(&v); + output = godot_string_sprintf(&format, &args, &error); + REQUIRE(error); + CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0); + godot_string_destroy(&format); + godot_string_destroy(&output); + + godot_array_destroy(&args); +#undef ARRAY_PUSH_INT +#undef ARRAY_PUSH_REAL +#undef ARRAY_PUSH_STRING +} + +TEST_CASE("[GDNative String] is_numeric") { +#define IS_NUM_TEST(x, r) \ + { \ + godot_string t; \ + godot_string_new_with_latin1_chars(&t, x); \ + CHECK(godot_string_is_numeric(&t) == r); \ + godot_string_destroy(&t); \ + } + + IS_NUM_TEST("12", true); + IS_NUM_TEST("1.2", true); + IS_NUM_TEST("AF", false); + IS_NUM_TEST("-12", true); + IS_NUM_TEST("-1.2", true); + +#undef IS_NUM_TEST +} + +TEST_CASE("[GDNative String] pad") { + godot_string s, c; + godot_string_new_with_latin1_chars(&s, "test"); + godot_string_new_with_latin1_chars(&c, "x"); + + godot_string l = godot_string_lpad_with_custom_character(&s, 10, &c); + CHECK(u32scmp(godot_string_get_data(&l), U"xxxxxxtest") == 0); + godot_string_destroy(&l); + + godot_string r = godot_string_rpad_with_custom_character(&s, 10, &c); + CHECK(u32scmp(godot_string_get_data(&r), U"testxxxxxx") == 0); + godot_string_destroy(&r); + + godot_string_destroy(&s); + godot_string_destroy(&c); + + godot_string_new_with_latin1_chars(&s, "10.10"); + c = godot_string_pad_decimals(&s, 4); + CHECK(u32scmp(godot_string_get_data(&c), U"10.1000") == 0); + godot_string_destroy(&c); + c = godot_string_pad_zeros(&s, 4); + CHECK(u32scmp(godot_string_get_data(&c), U"0010.10") == 0); + godot_string_destroy(&c); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] is_subsequence_of") { + godot_string a, t; + godot_string_new_with_latin1_chars(&a, "is subsequence of"); + + godot_string_new_with_latin1_chars(&t, "sub"); + CHECK(godot_string_is_subsequence_of(&t, &a)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Sub"); + CHECK(!godot_string_is_subsequence_of(&t, &a)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Sub"); + CHECK(godot_string_is_subsequence_ofi(&t, &a)); + godot_string_destroy(&t); + + godot_string_destroy(&a); +} + +TEST_CASE("[GDNative String] match") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "*.png"); + + godot_string_new_with_latin1_chars(&t, "img1.png"); + CHECK(godot_string_match(&t, &s)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "img1.jpeg"); + CHECK(!godot_string_match(&t, &s)); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "img1.Png"); + CHECK(!godot_string_match(&t, &s)); + CHECK(godot_string_matchn(&t, &s)); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] IPVX address to string") { + godot_string ip; + + godot_string_new_with_latin1_chars(&ip, "192.168.0.1"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "192.368.0.1"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85j3:0000:0000:8a2e:0370:7334"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8:85f345:0000:0000:8a2e:0370:7334"); + CHECK(!godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "2001:0db8::0:8a2e:370:7334"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); + + godot_string_new_with_latin1_chars(&ip, "::ffff:192.168.0.1"); + CHECK(godot_string_is_valid_ip_address(&ip)); + godot_string_destroy(&ip); +} + +TEST_CASE("[GDNative String] Capitalize against many strings") { +#define CAP_TEST(i, o) \ + godot_string_new_with_latin1_chars(&input, i); \ + godot_string_new_with_latin1_chars(&output, o); \ + test = godot_string_capitalize(&input); \ + CHECK(u32scmp(godot_string_get_data(&output), godot_string_get_data(&test)) == 0); \ + godot_string_destroy(&input); \ + godot_string_destroy(&output); \ + godot_string_destroy(&test); + + godot_string input, output, test; + + CAP_TEST("bytes2var", "Bytes 2 Var"); + CAP_TEST("linear2db", "Linear 2 Db"); + CAP_TEST("vector3", "Vector 3"); + CAP_TEST("sha256", "Sha 256"); + CAP_TEST("2db", "2 Db"); + CAP_TEST("PascalCase", "Pascal Case"); + CAP_TEST("PascalPascalCase", "Pascal Pascal Case"); + CAP_TEST("snake_case", "Snake Case"); + CAP_TEST("snake_snake_case", "Snake Snake Case"); + CAP_TEST("sha256sum", "Sha 256 Sum"); + CAP_TEST("cat2dog", "Cat 2 Dog"); + CAP_TEST("function(name)", "Function(name)"); + CAP_TEST("snake_case_function(snake_case_arg)", "Snake Case Function(snake Case Arg)"); + CAP_TEST("snake_case_function( snake_case_arg )", "Snake Case Function( Snake Case Arg )"); + +#undef CAP_TEST +} + +TEST_CASE("[GDNative String] lstrip and rstrip") { +#define LSTRIP_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_latin1_chars(&xx, x); \ + godot_string_new_with_latin1_chars(&yy, y); \ + godot_string_new_with_latin1_chars(&zz, z); \ + rr = godot_string_lstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define RSTRIP_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_latin1_chars(&xx, x); \ + godot_string_new_with_latin1_chars(&yy, y); \ + godot_string_new_with_latin1_chars(&zz, z); \ + rr = godot_string_rstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define LSTRIP_UTF8_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_utf8_chars(&xx, x); \ + godot_string_new_with_utf8_chars(&yy, y); \ + godot_string_new_with_utf8_chars(&zz, z); \ + rr = godot_string_lstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + +#define RSTRIP_UTF8_TEST(x, y, z) \ + { \ + godot_string xx, yy, zz, rr; \ + godot_string_new_with_utf8_chars(&xx, x); \ + godot_string_new_with_utf8_chars(&yy, y); \ + godot_string_new_with_utf8_chars(&zz, z); \ + rr = godot_string_rstrip(&xx, &yy); \ + state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \ + godot_string_destroy(&xx); \ + godot_string_destroy(&yy); \ + godot_string_destroy(&zz); \ + godot_string_destroy(&rr); \ + } + + bool state = true; + + // strip none + LSTRIP_TEST("abc", "", "abc"); + RSTRIP_TEST("abc", "", "abc"); + // strip one + LSTRIP_TEST("abc", "a", "bc"); + RSTRIP_TEST("abc", "c", "ab"); + // strip lots + LSTRIP_TEST("bababbababccc", "ab", "ccc"); + RSTRIP_TEST("aaabcbcbcbbcbbc", "cb", "aaa"); + // strip empty string + LSTRIP_TEST("", "", ""); + RSTRIP_TEST("", "", ""); + // strip to empty string + LSTRIP_TEST("abcabcabc", "bca", ""); + RSTRIP_TEST("abcabcabc", "bca", ""); + // don't strip wrong end + LSTRIP_TEST("abc", "c", "abc"); + LSTRIP_TEST("abca", "a", "bca"); + RSTRIP_TEST("abc", "a", "abc"); + RSTRIP_TEST("abca", "a", "abc"); + // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5) + // and the same second as "ÿ" (\u00ff) + LSTRIP_UTF8_TEST("¿", "µÿ", "¿"); + RSTRIP_UTF8_TEST("¿", "µÿ", "¿"); + LSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "¿ÿ"); + RSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "µ¿"); + + // the above tests repeated with additional superfluous strip chars + + // strip none + LSTRIP_TEST("abc", "qwjkl", "abc"); + RSTRIP_TEST("abc", "qwjkl", "abc"); + // strip one + LSTRIP_TEST("abc", "qwajkl", "bc"); + RSTRIP_TEST("abc", "qwcjkl", "ab"); + // strip lots + LSTRIP_TEST("bababbababccc", "qwabjkl", "ccc"); + RSTRIP_TEST("aaabcbcbcbbcbbc", "qwcbjkl", "aaa"); + // strip empty string + LSTRIP_TEST("", "qwjkl", ""); + RSTRIP_TEST("", "qwjkl", ""); + // strip to empty string + LSTRIP_TEST("abcabcabc", "qwbcajkl", ""); + RSTRIP_TEST("abcabcabc", "qwbcajkl", ""); + // don't strip wrong end + LSTRIP_TEST("abc", "qwcjkl", "abc"); + LSTRIP_TEST("abca", "qwajkl", "bca"); + RSTRIP_TEST("abc", "qwajkl", "abc"); + RSTRIP_TEST("abca", "qwajkl", "abc"); + // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5) + // and the same second as "ÿ" (\u00ff) + LSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿"); + RSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿"); + LSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "¿ÿ"); + RSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "µ¿"); + + CHECK(state); + +#undef LSTRIP_TEST +#undef RSTRIP_TEST +#undef LSTRIP_UTF8_TEST +#undef RSTRIP_UTF8_TEST +} + +TEST_CASE("[GDNative String] Cyrillic to_lower()") { + godot_string upper, lower, test; + godot_string_new_with_utf8_chars(&upper, "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"); + godot_string_new_with_utf8_chars(&lower, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"); + + test = godot_string_to_lower(&upper); + + CHECK((u32scmp(godot_string_get_data(&test), godot_string_get_data(&lower)) == 0)); + + godot_string_destroy(&upper); + godot_string_destroy(&lower); + godot_string_destroy(&test); +} + +TEST_CASE("[GDNative String] Count and countn functionality") { +#define COUNT_TEST(x, y, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_count(&s, &t, 0, 0) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTR_TEST(x, y, a, b, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_count(&s, &t, a, b) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTN_TEST(x, y, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_countn(&s, &t, 0, 0) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + +#define COUNTNR_TEST(x, y, a, b, r) \ + { \ + godot_string s, t; \ + godot_string_new_with_latin1_chars(&s, x); \ + godot_string_new_with_latin1_chars(&t, y); \ + state = state && (godot_string_countn(&s, &t, a, b) == r); \ + godot_string_destroy(&s); \ + godot_string_destroy(&t); \ + } + bool state = true; + + COUNT_TEST("", "Test", 0); + COUNT_TEST("Test", "", 0); + COUNT_TEST("Test", "test", 0); + COUNT_TEST("Test", "TEST", 0); + COUNT_TEST("TEST", "TEST", 1); + COUNT_TEST("Test", "Test", 1); + COUNT_TEST("aTest", "Test", 1); + COUNT_TEST("Testa", "Test", 1); + COUNT_TEST("TestTestTest", "Test", 3); + COUNT_TEST("TestTestTest", "TestTest", 1); + COUNT_TEST("TestGodotTestGodotTestGodot", "Test", 3); + + COUNTR_TEST("TestTestTestTest", "Test", 4, 8, 1); + COUNTR_TEST("TestTestTestTest", "Test", 4, 12, 2); + COUNTR_TEST("TestTestTestTest", "Test", 4, 16, 3); + COUNTR_TEST("TestTestTestTest", "Test", 4, 0, 3); + + COUNTN_TEST("Test", "test", 1); + COUNTN_TEST("Test", "TEST", 1); + COUNTN_TEST("testTest-Testatest", "tEst", 4); + COUNTNR_TEST("testTest-TeStatest", "tEsT", 4, 16, 2); + + CHECK(state); + +#undef COUNT_TEST +#undef COUNTR_TEST +#undef COUNTN_TEST +#undef COUNTNR_TEST +} + +TEST_CASE("[GDNative String] Bigrams") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, "abcd"); + godot_packed_string_array bigr = godot_string_bigrams(&s); + godot_string_destroy(&s); + + CHECK(godot_packed_string_array_size(&bigr) == 3); + + t = godot_packed_string_array_get(&bigr, 0); + CHECK(u32scmp(godot_string_get_data(&t), U"ab") == 0); + godot_string_destroy(&t); + + t = godot_packed_string_array_get(&bigr, 1); + CHECK(u32scmp(godot_string_get_data(&t), U"bc") == 0); + godot_string_destroy(&t); + + t = godot_packed_string_array_get(&bigr, 2); + CHECK(u32scmp(godot_string_get_data(&t), U"cd") == 0); + godot_string_destroy(&t); + + godot_packed_string_array_destroy(&bigr); +} + +TEST_CASE("[GDNative String] c-escape/unescape") { + godot_string s; + godot_string_new_with_latin1_chars(&s, "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\""); + godot_string t = godot_string_c_escape(&s); + godot_string u = godot_string_c_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] dedent") { + godot_string s, t; + godot_string_new_with_latin1_chars(&s, " aaa\n bbb"); + godot_string_new_with_latin1_chars(&t, "aaa\nbbb"); + godot_string u = godot_string_dedent(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Path functions") { + static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" }; + static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" }; + static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" }; + static const char *ext[4] = { "tscn", "xscn", "scn", "doc" }; + static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" }; + static const bool abs[4] = { true, true, false, false }; + + for (int i = 0; i < 4; i++) { + godot_string s, t, u, f; + godot_string_new_with_latin1_chars(&s, path[i]); + + t = godot_string_get_base_dir(&s); + godot_string_new_with_latin1_chars(&u, base_dir[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_basename(&s); + godot_string_new_with_latin1_chars(&u, base_name[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_extension(&s); + godot_string_new_with_latin1_chars(&u, ext[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_get_file(&s); + godot_string_new_with_latin1_chars(&u, file[i]); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string s_simp; + s_simp = godot_string_simplify_path(&s); + t = godot_string_get_base_dir(&s_simp); + godot_string_new_with_latin1_chars(&u, file[i]); + f = godot_string_plus_file(&t, &u); + CHECK(u32scmp(godot_string_get_data(&f), godot_string_get_data(&s_simp)) == 0); + godot_string_destroy(&f); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&s_simp); + + CHECK(godot_string_is_abs_path(&s) == abs[i]); + CHECK(godot_string_is_rel_path(&s) != abs[i]); + + godot_string_destroy(&s); + } + + static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" }; + static const bool valid[3] = { true, false, false }; + for (int i = 0; i < 3; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, file_name[i]); + CHECK(godot_string_is_valid_filename(&s) == valid[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNative String] hash") { + godot_string a, b, c; + godot_string_new_with_latin1_chars(&a, "Test"); + godot_string_new_with_latin1_chars(&b, "Test"); + godot_string_new_with_latin1_chars(&c, "West"); + CHECK(godot_string_hash(&a) == godot_string_hash(&b)); + CHECK(godot_string_hash(&a) != godot_string_hash(&c)); + + CHECK(godot_string_hash64(&a) == godot_string_hash64(&b)); + CHECK(godot_string_hash64(&a) != godot_string_hash64(&c)); + + godot_string_destroy(&a); + godot_string_destroy(&b); + godot_string_destroy(&c); +} + +TEST_CASE("[GDNative String] http_escape/unescape") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'"); + godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3A%27docs%27"); + + u = godot_string_http_escape(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + u = godot_string_http_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] percent_encode/decode") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'"); + godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3a%27docs%27"); + + u = godot_string_percent_encode(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + u = godot_string_percent_decode(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&s); + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] xml_escape/unescape") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\"Test\" <test@test&'test'>"); + + t = godot_string_xml_escape_with_quotes(&s); + u = godot_string_xml_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + t = godot_string_xml_escape(&s); + u = godot_string_xml_unescape(&t); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Strip escapes") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\t\tTest Test\r\n Test"); + godot_string_new_with_latin1_chars(&t, "Test Test Test"); + + u = godot_string_strip_escapes(&s); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + + godot_string_destroy(&t); + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Strip edges") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "\t Test Test "); + + godot_string_new_with_latin1_chars(&t, "Test Test "); + u = godot_string_strip_edges(&s, true, false); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "\t Test Test"); + u = godot_string_strip_edges(&s, false, true); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "Test Test"); + u = godot_string_strip_edges(&s, true, true); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Similarity") { + godot_string a, b, c; + godot_string_new_with_latin1_chars(&a, "Test"); + godot_string_new_with_latin1_chars(&b, "West"); + godot_string_new_with_latin1_chars(&c, "Toad"); + + CHECK(godot_string_similarity(&a, &b) > godot_string_similarity(&a, &c)); + + godot_string_destroy(&a); + godot_string_destroy(&b); + godot_string_destroy(&c); +} + +TEST_CASE("[GDNative String] Trim") { + godot_string s, t, u, p; + godot_string_new_with_latin1_chars(&s, "aaaTestbbb"); + + godot_string_new_with_latin1_chars(&p, "aaa"); + godot_string_new_with_latin1_chars(&t, "Testbbb"); + u = godot_string_trim_prefix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&p); + + godot_string_new_with_latin1_chars(&p, "bbb"); + godot_string_new_with_latin1_chars(&t, "aaaTest"); + u = godot_string_trim_suffix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + godot_string_destroy(&p); + + godot_string_new_with_latin1_chars(&p, "Test"); + u = godot_string_trim_suffix(&s, &p); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&p); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Right/Left") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, "aaaTestbbb"); + // ^ + + godot_string_new_with_latin1_chars(&t, "tbbb"); + u = godot_string_right(&s, 6); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&t, "aaaTes"); + u = godot_string_left(&s, 6); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); +} + +TEST_CASE("[GDNative String] Repeat") { + godot_string t, u; + godot_string_new_with_latin1_chars(&t, "ab"); + + u = godot_string_repeat(&t, 4); + CHECK(u32scmp(godot_string_get_data(&u), U"abababab") == 0); + godot_string_destroy(&u); + + godot_string_destroy(&t); +} + +TEST_CASE("[GDNative String] SHA1/SHA256/MD5") { + godot_string s, t, sha1, sha256, md5; + godot_string_new_with_latin1_chars(&s, "Godot"); + godot_string_new_with_latin1_chars(&sha1, "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201"); + static uint8_t sha1_buf[20] = { + 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3, + 0x9D, 0xC2, 0xD2, 0x01 + }; + godot_string_new_with_latin1_chars(&sha256, "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8"); + static uint8_t sha256_buf[32] = { + 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF, + 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8 + }; + godot_string_new_with_latin1_chars(&md5, "4a336d087aeb0390da10ee2ea7cb87f8"); + static uint8_t md5_buf[16] = { + 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8 + }; + + godot_packed_byte_array buf = godot_string_sha1_buffer(&s); + CHECK(memcmp(sha1_buf, godot_packed_byte_array_ptr(&buf), 20) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_sha1_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha1)) == 0); + godot_string_destroy(&t); + + buf = godot_string_sha256_buffer(&s); + CHECK(memcmp(sha256_buf, godot_packed_byte_array_ptr(&buf), 32) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_sha256_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha256)) == 0); + godot_string_destroy(&t); + + buf = godot_string_md5_buffer(&s); + CHECK(memcmp(md5_buf, godot_packed_byte_array_ptr(&buf), 16) == 0); + godot_packed_byte_array_destroy(&buf); + + t = godot_string_md5_text(&s); + CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&md5)) == 0); + godot_string_destroy(&t); + + godot_string_destroy(&s); + godot_string_destroy(&sha1); + godot_string_destroy(&sha256); + godot_string_destroy(&md5); +} + +TEST_CASE("[GDNative String] Join") { + godot_string s, t, u; + godot_string_new_with_latin1_chars(&s, ", "); + + godot_packed_string_array parts; + godot_packed_string_array_new(&parts); + godot_string_new_with_latin1_chars(&t, "One"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "B"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + godot_string_new_with_latin1_chars(&t, "C"); + godot_packed_string_array_push_back(&parts, &t); + godot_string_destroy(&t); + + godot_string_new_with_latin1_chars(&u, "One, B, C"); + t = godot_string_join(&s, &parts); + CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0); + godot_string_destroy(&u); + godot_string_destroy(&t); + + godot_string_destroy(&s); + godot_packed_string_array_destroy(&parts); +} + +TEST_CASE("[GDNative String] Is_*") { + static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" }; + static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false }; + static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false }; + static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false }; + static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false }; + static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false }; + static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false }; + + for (int i = 0; i < 12; i++) { + godot_string s; + godot_string_new_with_latin1_chars(&s, data[i]); + CHECK(godot_string_is_numeric(&s) == isnum[i]); + CHECK(godot_string_is_valid_integer(&s) == isint[i]); + CHECK(godot_string_is_valid_hex_number(&s, false) == ishex[i]); + CHECK(godot_string_is_valid_hex_number(&s, true) == ishex_p[i]); + CHECK(godot_string_is_valid_float(&s) == isflt[i]); + CHECK(godot_string_is_valid_identifier(&s) == isid[i]); + godot_string_destroy(&s); + } +} + +TEST_CASE("[GDNative String] humanize_size") { + godot_string s; + + s = godot_string_humanize_size(1000); + CHECK(u32scmp(godot_string_get_data(&s), U"1000 B") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(1025); + CHECK(u32scmp(godot_string_get_data(&s), U"1.00 KiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(1025300); + CHECK(u32scmp(godot_string_get_data(&s), U"1001.2 KiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(100523550); + CHECK(u32scmp(godot_string_get_data(&s), U"95.86 MiB") == 0); + godot_string_destroy(&s); + + s = godot_string_humanize_size(5345555000); + CHECK(u32scmp(godot_string_get_data(&s), U"4.97 GiB") == 0); + godot_string_destroy(&s); +} + +} // namespace TestGDNativeString + +#endif // TEST_GDNATIVE_STRING_H diff --git a/modules/gdnative/xr/config.py b/modules/gdnative/xr/config.py deleted file mode 100644 index d22f9454ed..0000000000 --- a/modules/gdnative/xr/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - return True - - -def configure(env): - pass diff --git a/modules/gdnavigation/rvo_agent.h b/modules/gdnavigation/rvo_agent.h index f5c579ba84..1ad9d3ed76 100644 --- a/modules/gdnavigation/rvo_agent.h +++ b/modules/gdnavigation/rvo_agent.h @@ -31,8 +31,9 @@ #ifndef RVO_AGENT_H #define RVO_AGENT_H -#include "core/object.h" +#include "core/class_db.h" #include "nav_rid.h" + #include <Agent.h> /** diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index e58a1d8edc..5c8cbdf869 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -17,3 +17,7 @@ if env["tools"]: # Using a define in the disabled case, to avoid having an extra define # in regular builds where all modules are enabled. env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"]) + +if env["tests"]: + env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"]) + env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp") diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index e528fc6623..e170667a30 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -7,6 +7,7 @@ List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.) </description> <tutorials> + <link title="Random number generation">https://docs.godotengine.org/en/latest/tutorials/math/random_number_generation.html</link> </tutorials> <methods> <method name="Color8"> @@ -369,24 +370,19 @@ <description> Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative. [codeblock] - var i = -6 - while i < 5: - prints(i, fposmod(i, 3)) - i += 1 + for i in 7: + var x = 0.5 * i - 1.5 + print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)]) [/codeblock] Produces: [codeblock] - -6 0 - -5 1 - -4 2 - -3 0 - -2 1 - -1 2 - 0 0 - 1 1 - 2 2 - 3 0 - 4 1 + -1.5 -0.0 0.0 + -1.0 -1.0 0.5 + -0.5 -0.5 1.0 + 0.0 0.0 0.0 + 0.5 0.5 0.5 + 1.0 1.0 1.0 + 1.5 0.0 0.0 [/codeblock] </description> </method> @@ -632,6 +628,7 @@ var main = load("res://main.tscn") # main will contain a PackedScene resource. [/codeblock] [b]Important:[/b] The path must be absolute, a local path will just return [code]null[/code]. + This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. </description> </method> <method name="log"> @@ -771,24 +768,18 @@ <description> Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative. [codeblock] - var i = -6 - while i < 5: - prints(i, posmod(i, 3)) - i += 1 + for i in range(-3, 4): + print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)]) [/codeblock] Produces: [codeblock] - -6 0 - -5 1 - -4 2 - -3 0 - -2 1 - -1 2 - 0 0 - 1 1 - 2 2 - 3 0 - 4 1 + -3 0 0 + -2 -2 1 + -1 -1 2 + 0 0 0 + 1 1 1 + 2 2 2 + 3 0 0 [/codeblock] </description> </method> @@ -829,6 +820,7 @@ a = [1, 2, 3] print("a", "b", a) # Prints ab[1, 2, 3] [/codeblock] + [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. </description> </method> <method name="print_debug" qualifiers="vararg"> @@ -902,6 +894,7 @@ [codeblock] push_error("test error") # Prints "test error" to debugger and terminal as error call [/codeblock] + [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. </description> </method> <method name="push_warning"> @@ -991,27 +984,15 @@ <description> Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment). [codeblock] - for i in range(4): - print(i) - for i in range(2, 5): - print(i) - for i in range(0, 6, 2): - print(i) + print(range(4)) + print(range(2, 5)) + print(range(0, 6, 2)) [/codeblock] Output: [codeblock] - 0 - 1 - 2 - 3 - - 2 - 3 - 4 - - 0 - 2 - 4 + [0, 1, 2, 3] + [2, 3, 4] + [0, 2, 4] [/codeblock] </description> </method> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index ae1f2893f1..7f7410a92c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -33,15 +33,15 @@ #include "../gdscript_tokenizer.h" #include "editor/editor_settings.h" -static bool _is_char(CharType c) { +static bool _is_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } -static bool _is_hex_symbol(CharType c) { +static bool _is_hex_symbol(char32_t c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } -static bool _is_bin_symbol(CharType c) { +static bool _is_bin_symbol(char32_t c) { return (c == '0' || c == '1'); } @@ -73,6 +73,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) color_region_cache[p_line] = -1; int in_region = -1; if (p_line != 0) { + int prev_region_line = p_line - 1; + while (prev_region_line > 0 && !color_region_cache.has(prev_region_line)) { + prev_region_line--; + } + for (int i = prev_region_line; i < p_line - 1; i++) { + get_line_syntax_highlighting(i); + } if (!color_region_cache.has(p_line - 1)) { get_line_syntax_highlighting(p_line - 1); } @@ -119,7 +126,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) /* search the line */ bool match = true; - const CharType *start_key = color_regions[c].start_key.c_str(); + const char32_t *start_key = color_regions[c].start_key.get_data(); for (int k = 0; k < start_key_length; k++) { if (start_key[k] != str[from + k]) { match = false; @@ -153,18 +160,16 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) /* if we are in one find the end key */ if (in_region != -1) { - /* check there is enough room */ - int chars_left = line_length - from; - int end_key_length = color_regions[in_region].end_key.length(); - if (chars_left < end_key_length) { - continue; - } - /* search the line */ int region_end_index = -1; - const CharType *end_key = color_regions[in_region].start_key.c_str(); + int end_key_length = color_regions[in_region].end_key.length(); + const char32_t *end_key = color_regions[in_region].end_key.get_data(); for (; from < line_length; from++) { - if (!is_a_symbol) { + if (line_length - from < end_key_length) { + break; + } + + if (!is_symbol(str[from])) { continue; } @@ -173,9 +178,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) continue; } + region_end_index = from; for (int k = 0; k < end_key_length; k++) { - if (end_key[k] == str[from + k]) { - region_end_index = from; + if (end_key[k] != str[from + k]) { + region_end_index = -1; break; } } @@ -192,7 +198,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) previous_type = REGION; previous_text = ""; previous_column = j; - j = from; + j = from + (end_key_length - 1); if (region_end_index == -1) { color_region_cache[p_line] = in_region; } @@ -572,8 +578,12 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons } } + int at = 0; for (int i = 0; i < color_regions.size(); i++) { ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists."); + if (p_start_key.length() < color_regions[i].start_key.length()) { + at++; + } } ColorRegion color_region; @@ -581,7 +591,8 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons color_region.start_key = p_start_key; color_region.end_key = p_end_key; color_region.line_only = p_line_only; - color_regions.push_back(color_region); + color_regions.insert(at, color_region); + clear_highlighting_cache(); } Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 0263e32c5b..e70e3f7272 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1044,8 +1044,10 @@ GDScript::~GDScript() { MutexLock lock(GDScriptLanguage::get_singleton()->lock); while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { - E->self()->_clear_stack(); + // Order matters since clearing the stack may already cause + // the GDSCriptFunctionState to be destroyed and thus removed from the list. pending_func_states.remove(E); + E->self()->_clear_stack(); } } @@ -1053,7 +1055,9 @@ GDScript::~GDScript() { memdelete(E->get()); } - GDScriptCache::remove_script(get_path()); + if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown. + GDScriptCache::remove_script(get_path()); + } _save_orphaned_subclasses(); @@ -1449,8 +1453,10 @@ GDScriptInstance::~GDScriptInstance() { MutexLock lock(GDScriptLanguage::get_singleton()->lock); while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { - E->self()->_clear_stack(); + // Order matters since clearing the stack may already cause + // the GDSCriptFunctionState to be destroyed and thus removed from the list. pending_func_states.remove(E); + E->self()->_clear_stack(); } if (script.is_valid() && owner) { @@ -2035,7 +2041,33 @@ GDScriptLanguage::~GDScriptLanguage() { if (_call_stack) { memdelete_arr(_call_stack); } - singleton = nullptr; + + // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit). + SelfList<GDScript> *s = script_list.first(); + while (s) { + GDScript *script = s->self(); + // This ensures the current script is not released before we can check what's the next one + // in the list (we can't get the next upfront because we don't know if the reference breaking + // will cause it -or any other after it, for that matter- to be released so the next one + // is not the same as before). + script->reference(); + + for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) { + GDScriptFunction *func = E->get(); + for (int i = 0; i < func->argument_types.size(); i++) { + func->argument_types.write[i].script_type_ref = Ref<Script>(); + } + func->return_type.script_type_ref = Ref<Script>(); + } + for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) { + E->get().data_type.script_type_ref = Ref<Script>(); + } + + s = s->next(); + script->unreference(); + } + + singleton = NULL; } void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 9c148e2148..ab4edb04b9 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -577,6 +577,12 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas GDScriptParser::DataType datatype = member.constant->get_datatype(); if (member.constant->initializer) { + if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer)); + } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer)); + } + if (!member.constant->initializer->is_constant) { push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer); } @@ -794,7 +800,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) { resolve_match_branch(static_cast<GDScriptParser::MatchBranchNode *>(p_node), nullptr); break; case GDScriptParser::Node::PARAMETER: - resolve_pararameter(static_cast<GDScriptParser::ParameterNode *>(p_node)); + resolve_parameter(static_cast<GDScriptParser::ParameterNode *>(p_node)); break; case GDScriptParser::Node::PATTERN: resolve_match_pattern(static_cast<GDScriptParser::PatternNode *>(p_node), nullptr); @@ -848,7 +854,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * parser->current_function = p_function; for (int i = 0; i < p_function->parameters.size(); i++) { - resolve_pararameter(p_function->parameters[i]); + resolve_parameter(p_function->parameters[i]); #ifdef DEBUG_ENABLED if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) { parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name); @@ -1113,6 +1119,11 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant GDScriptParser::DataType type; reduce_expression(p_constant->initializer); + if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer)); + } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer)); + } if (!p_constant->initializer->is_constant) { push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer); @@ -1264,14 +1275,18 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc p_match_pattern->set_datatype(result); } -void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_parameter) { +void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) { GDScriptParser::DataType result; result.kind = GDScriptParser::DataType::VARIANT; if (p_parameter->default_value != nullptr) { reduce_expression(p_parameter->default_value); result = p_parameter->default_value->get_datatype(); - result.type_source = GDScriptParser::DataType::INFERRED; + if (p_parameter->infer_datatype) { + result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + } else { + result.type_source = GDScriptParser::DataType::INFERRED; + } result.is_constant = false; } @@ -1282,7 +1297,7 @@ void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_para if (p_parameter->default_value != nullptr) { if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) { - push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with paremeter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value); + push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value); } else if (p_parameter->default_value->get_datatype().is_variant()) { mark_node_unsafe(p_parameter); } @@ -1418,22 +1433,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre } void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) { - bool all_is_constant = true; - for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; reduce_expression(element); - all_is_constant = all_is_constant && element->is_constant; - } - - if (all_is_constant) { - Array array; - array.resize(p_array->elements.size()); - for (int i = 0; i < p_array->elements.size(); i++) { - array[i] = p_array->elements[i]->reduced_value; - } - p_array->is_constant = true; - p_array->reduced_value = array; } // It's array in any case. @@ -1980,8 +1982,6 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { - bool all_is_constant = true; - HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements; for (int i = 0; i < p_dictionary->elements.size(); i++) { @@ -1990,7 +1990,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti reduce_expression(element.key); } reduce_expression(element.value); - all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; if (element.key->is_constant) { if (elements.has(element.key->reduced_value)) { @@ -2001,16 +2000,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti } } - if (all_is_constant) { - Dictionary dict; - for (int i = 0; i < p_dictionary->elements.size(); i++) { - const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - dict[element.key->reduced_value] = element.value->reduced_value; - } - p_dictionary->is_constant = true; - p_dictionary->reduced_value = dict; - } - // It's dictionary in any case. GDScriptParser::DataType dict_type; dict_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2073,18 +2062,32 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); } } else { - Callable::CallError temp; - Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp); - List<PropertyInfo> properties; - dummy.get_property_list(&properties); - for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) { - const PropertyInfo &prop = E->get(); - if (prop.name == name) { - p_identifier->set_datatype(type_from_property(prop)); + switch (base.builtin_type) { + case Variant::NIL: { + push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + return; + } + case Variant::DICTIONARY: { + GDScriptParser::DataType dummy; + dummy.kind = GDScriptParser::DataType::VARIANT; + p_identifier->set_datatype(dummy); return; } + default: { + Callable::CallError temp; + Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp); + List<PropertyInfo> properties; + dummy.get_property_list(&properties); + for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) { + const PropertyInfo &prop = E->get(); + if (prop.name == name) { + p_identifier->set_datatype(type_from_property(prop)); + return; + } + } + push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + } } - push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); } return; } @@ -2721,6 +2724,46 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } +void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { + bool all_is_constant = true; + + for (int i = 0; i < p_array->elements.size(); i++) { + GDScriptParser::ExpressionNode *element = p_array->elements[i]; + all_is_constant = all_is_constant && element->is_constant; + if (!all_is_constant) { + return; + } + } + + Array array; + array.resize(p_array->elements.size()); + for (int i = 0; i < p_array->elements.size(); i++) { + array[i] = p_array->elements[i]->reduced_value; + } + p_array->is_constant = true; + p_array->reduced_value = array; +} + +void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { + bool all_is_constant = true; + + for (int i = 0; i < p_dictionary->elements.size(); i++) { + const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; + if (!all_is_constant) { + return; + } + } + + Dictionary dict; + for (int i = 0; i < p_dictionary->elements.size(); i++) { + const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + dict[element.key->reduced_value] = element.value->reduced_value; + } + p_dictionary->is_constant = true; + p_dictionary->reduced_value = dict; +} + GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { GDScriptParser::DataType result; result.is_constant = true; @@ -2971,7 +3014,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p if (arg_type.is_variant()) { // Argument can be anything, so this is unsafe. mark_node_unsafe(p_call->arguments[i]); - } else if (!is_type_compatible(par_type, arg_type, true)) { + } else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) { // Supertypes are acceptable for dynamic compliance, but it's unsafe. mark_node_unsafe(p_call); if (!is_type_compatible(arg_type, par_type)) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 4e06e0a530..f3cbb320b7 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -67,7 +67,7 @@ class GDScriptAnalyzer { void resolve_match(GDScriptParser::MatchNode *p_match); void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test); void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test); - void resolve_pararameter(GDScriptParser::ParameterNode *p_parameter); + void resolve_parameter(GDScriptParser::ParameterNode *p_parameter); void resolve_return(GDScriptParser::ReturnNode *p_return); // Reduction functions. @@ -89,6 +89,9 @@ class GDScriptAnalyzer { void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); + void const_fold_array(GDScriptParser::ArrayNode *p_array); + void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary); + // Helpers. GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 8f0ce99de6..cc9e87b882 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -529,6 +529,7 @@ void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant append(p_arguments[i]); } append(p_target); + alloc_call(p_arguments.size()); } void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) { @@ -579,8 +580,8 @@ void GDScriptByteCodeGenerator::write_endif() { } void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) { - int counter_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); - int container_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); + int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); + int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); current_breaks_to_patch.push_back(List<int>()); @@ -628,7 +629,9 @@ void GDScriptByteCodeGenerator::write_endfor() { } current_breaks_to_patch.pop_back(); - current_stack_size -= 2; // Remove loop temporaries. + // Remove loop temporaries. + pop_temporary(); + pop_temporary(); } void GDScriptByteCodeGenerator::start_while_condition() { diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 992f8f4b58..57b95f5b21 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -108,20 +108,20 @@ GDScriptParserRef::~GDScriptParserRef() { if (analyzer != nullptr) { memdelete(analyzer); } - MutexLock(GDScriptCache::singleton->lock); + MutexLock lock(GDScriptCache::singleton->lock); GDScriptCache::singleton->parser_map.erase(path); } GDScriptCache *GDScriptCache::singleton = nullptr; void GDScriptCache::remove_script(const String &p_path) { - MutexLock(singleton->lock); + MutexLock lock(singleton->lock); singleton->shallow_gdscript_cache.erase(p_path); singleton->full_gdscript_cache.erase(p_path); } Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) { - MutexLock(singleton->lock); + MutexLock lock(singleton->lock); Ref<GDScriptParserRef> ref; if (p_owner != String()) { singleton->dependencies[p_owner].insert(p_path); @@ -168,7 +168,7 @@ String GDScriptCache::get_source_code(const String &p_path) { } Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) { - MutexLock(singleton->lock); + MutexLock lock(singleton->lock); if (p_owner != String()) { singleton->dependencies[p_owner].insert(p_path); } @@ -190,7 +190,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri } Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner) { - MutexLock(singleton->lock); + MutexLock lock(singleton->lock); if (p_owner != String()) { singleton->dependencies[p_owner].insert(p_path); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7a6a7bcf48..bad450c9f9 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -76,7 +76,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N } } -GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const { +GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) const { if (!p_datatype.is_set() || !p_datatype.is_hard_type()) { return GDScriptDataType(); } @@ -98,7 +98,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } break; case GDScriptParser::DataType::SCRIPT: { result.kind = GDScriptDataType::SCRIPT; - result.script_type = p_datatype.script_type; + result.script_type = Ref<Script>(p_datatype.script_type).ptr(); result.native_type = result.script_type->get_instance_base_type(); } break; case GDScriptParser::DataType::CLASS: { @@ -124,11 +124,11 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D names.pop_back(); } result.kind = GDScriptDataType::GDSCRIPT; - result.script_type = script; + result.script_type = script.ptr(); result.native_type = script->get_instance_base_type(); } else { result.kind = GDScriptDataType::GDSCRIPT; - result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path); + result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr(); result.native_type = p_datatype.native_type; } } @@ -149,6 +149,12 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } + // Only hold strong reference to the script if it's not the owner of the + // element qualified with this type, to avoid cyclic references (leaks). + if (result.script_type && result.script_type != p_owner) { + result.script_type_ref = Ref<Script>(result.script_type); + } + return result; } @@ -1668,7 +1674,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser func_name = p_func->identifier->name; is_static = p_func->is_static; rpc_mode = p_func->rpc_mode; - return_type = _gdtype_from_datatype(p_func->get_datatype()); + return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script); } else { if (p_for_ready) { func_name = "_ready"; @@ -1685,7 +1691,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser if (p_func) { for (int i = 0; i < p_func->parameters.size(); i++) { const GDScriptParser::ParameterNode *parameter = p_func->parameters[i]; - GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype()); + GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script); uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type); codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type); @@ -1718,6 +1724,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true); if (error) { + memdelete(codegen.generator); return error; } GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype())); @@ -1738,6 +1745,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser const GDScriptParser::ParameterNode *parameter = p_func->parameters[i]; GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true); if (error) { + memdelete(codegen.generator); return error; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; @@ -1751,6 +1759,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser Error err = _parse_block(codegen, p_func->body); if (err) { + memdelete(codegen.generator); return err; } } @@ -1800,6 +1809,8 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser p_script->member_functions[func_name] = gd_function; + memdelete(codegen.generator); + return OK; } @@ -1825,7 +1836,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP return_type.kind = GDScriptDataType::BUILTIN; return_type.builtin_type = Variant::NIL; } else { - return_type = _gdtype_from_datatype(p_variable->get_datatype()); + return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script); } codegen.generator->write_start(p_script, func_name, false, p_variable->rpc_mode, return_type); @@ -1837,6 +1848,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter); if (error) { + memdelete(codegen.generator); return error; } @@ -1870,6 +1882,7 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP #ifdef TOOLS_ENABLED p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line; #endif + memdelete(codegen.generator); return OK; } @@ -1920,7 +1933,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->native = native; } break; case GDScriptDataType::GDSCRIPT: { - Ref<GDScript> base = base_type.script_type; + Ref<GDScript> base = Ref<GDScript>(base_type.script_type); p_script->base = base; p_script->_base = base.ptr(); @@ -1987,7 +2000,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar break; } minfo.rpc_mode = variable->rpc_mode; - minfo.data_type = _gdtype_from_datatype(variable->get_datatype()); + minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script); PropertyInfo prop_info = minfo.data_type; prop_info.name = name; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 80fba6a934..fe34d6cba3 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -51,12 +51,11 @@ class GDScriptCompiler { GDScriptCodeGenerator *generator = nullptr; Map<StringName, GDScriptCodeGenerator::Address> parameters; Map<StringName, GDScriptCodeGenerator::Address> locals; - List<Set<StringName>> locals_in_scope; + List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack; GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) { uint32_t addr = generator->add_local(p_name, p_type); locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type); - locals_in_scope.back()->get().insert(p_name); return locals[p_name]; } @@ -84,7 +83,7 @@ class GDScriptCompiler { Ref<Script> script = obj->get_script(); if (script.is_valid()) { - type.script_type = script; + type.script_type = script.ptr(); Ref<GDScript> gdscript = script; if (gdscript.is_valid()) { type.kind = GDScriptDataType::GDSCRIPT; @@ -102,17 +101,14 @@ class GDScriptCompiler { } void start_block() { - Set<StringName> scope; - locals_in_scope.push_back(scope); + Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals; + locals_stack.push_back(old_locals); generator->start_block(); } void end_block() { - Set<StringName> &scope = locals_in_scope.back()->get(); - for (Set<StringName>::Element *E = scope.front(); E; E = E->next()) { - locals.erase(E->get()); - } - locals_in_scope.pop_back(); + locals = locals_stack.back()->get(); + locals_stack.pop_back(); generator->end_block(); } }; @@ -125,7 +121,7 @@ class GDScriptCompiler { Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); - GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const; + GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner = nullptr) const; GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 2e372575da..aec05b6771 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -792,6 +792,9 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, continue; } option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT); + if (member.constant->initializer) { + option.default_value = member.constant->initializer->reduced_value; + } break; case GDScriptParser::ClassNode::Member::CLASS: if (p_only_functions) { @@ -2404,6 +2407,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path Variant::get_constants_for_type(completion_context.builtin_type, &constants); for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) { ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT); + bool valid = false; + Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E->get(), &valid); + if (valid) { + option.default_value = default_value; + } options.insert(option.display, option); } } break; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index aa48a7cdb4..e59f99fc56 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1058,7 +1058,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a err_text = "Got a freed object as a result of the call."; OPCODE_BREAK; } - if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) { + if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) { err_text = R"(Trying to call an async function without "await".)"; OPCODE_BREAK; } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index d1c98a5456..c98ac09310 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -56,7 +56,8 @@ struct GDScriptDataType { bool has_type = false; Variant::Type builtin_type = Variant::NIL; StringName native_type; - Ref<Script> script_type; + Script *script_type = nullptr; + Ref<Script> script_type_ref; bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const { if (!has_type) { diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index fefbf906f0..31ce63bc6e 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -635,7 +635,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ case TEXT_CHAR: { VALIDATE_ARG_COUNT(1); VALIDATE_ARG_NUM(0); - CharType result[2] = { *p_args[0], 0 }; + char32_t result[2] = { *p_args[0], 0 }; r_ret = String(result); } break; case TEXT_ORD: { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index d890103f15..2a69db130b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -327,7 +327,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_ bool found = false; const String &line = lines[i]; for (int j = 0; j < line.size(); j++) { - if (line[j] == CharType(0xFFFF)) { + if (line[j] == char32_t(0xFFFF)) { found = true; break; } else if (line[j] == '\t') { @@ -1042,7 +1042,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) { break; // Allow trailing comma. } - if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifer for enum key.)")) { + if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) { EnumNode::Value item; item.identifier = parse_identifier(); item.parent_enum = enum_node; @@ -2516,7 +2516,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { if (match(GDScriptTokenizer::Token::LITERAL)) { if (previous.literal.get_type() != Variant::STRING) { - push_error(R"(Expect node path as string or identifer after "$".)"); + push_error(R"(Expect node path as string or identifier after "$".)"); return nullptr; } GetNodeNode *get_node = alloc_node<GetNodeNode>(); @@ -2539,7 +2539,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p } while (match(GDScriptTokenizer::Token::SLASH)); return get_node; } else { - push_error(R"(Expect node path as string or identifer after "$".)"); + push_error(R"(Expect node path as string or identifier after "$".)"); return nullptr; } } diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index f5cf1e29f0..9a40aa50ac 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -222,7 +222,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { void GDScriptTokenizer::set_source_code(const String &p_source_code) { source = p_source_code; if (source.empty()) { - _source = L""; + _source = U""; } else { _source = source.ptr(); } @@ -263,7 +263,7 @@ bool GDScriptTokenizer::is_past_cursor() const { return true; } -CharType GDScriptTokenizer::_advance() { +char32_t GDScriptTokenizer::_advance() { if (unlikely(_is_at_end())) { return '\0'; } @@ -282,15 +282,15 @@ CharType GDScriptTokenizer::_advance() { return _peek(-1); } -void GDScriptTokenizer::push_paren(CharType p_char) { +void GDScriptTokenizer::push_paren(char32_t p_char) { paren_stack.push_back(p_char); } -bool GDScriptTokenizer::pop_paren(CharType p_expected) { +bool GDScriptTokenizer::pop_paren(char32_t p_expected) { if (paren_stack.empty()) { return false; } - CharType actual = paren_stack.back()->get(); + char32_t actual = paren_stack.back()->get(); paren_stack.pop_back(); return actual == p_expected; @@ -302,19 +302,19 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() { return error; } -static bool _is_alphanumeric(CharType c) { +static bool _is_alphanumeric(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_digit(CharType c) { +static bool _is_digit(char32_t c) { return (c >= '0' && c <= '9'); } -static bool _is_hex_digit(CharType c) { +static bool _is_hex_digit(char32_t c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -static bool _is_binary_digit(CharType c) { +static bool _is_binary_digit(char32_t c) { return (c == '0' || c == '1'); } @@ -404,7 +404,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) { error_stack.push_back(p_error); } -GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) { +GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) { if (paren_stack.empty()) { return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren)); } @@ -413,8 +413,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) { return error; } -GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(CharType p_test, Token::Type p_double_type) { - const CharType *next = _current + 1; +GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, Token::Type p_double_type) { + const char32_t *next = _current + 1; int chars = 2; // Two already matched. // Test before consuming characters, since we don't want to consume more than needed. @@ -578,7 +578,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() { } void GDScriptTokenizer::newline(bool p_make_token) { - // Don't overwrite previous newline, nor create if we want a line contination. + // Don't overwrite previous newline, nor create if we want a line continuation. if (p_make_token && !pending_newline && !line_continuation) { Token newline(Token::NEWLINE); newline.start_line = line; @@ -602,7 +602,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() { bool has_decimal = false; bool has_exponent = false; bool has_error = false; - bool (*digit_check_func)(CharType) = _is_digit; + bool (*digit_check_func)(char32_t) = _is_digit; if (_peek(-1) == '.') { has_decimal = true; @@ -762,7 +762,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { _advance(); } - CharType quote_char = _peek(-1); + char32_t quote_char = _peek(-1); if (_peek() == quote_char && _peek(1) == quote_char) { is_multiline = true; @@ -779,7 +779,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { return make_error("Unterminated string."); } - CharType ch = _peek(); + char32_t ch = _peek(); if (ch == '\\') { // Escape pattern. @@ -789,13 +789,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { } // Grab escape character. - CharType code = _peek(); + char32_t code = _peek(); _advance(); if (_is_at_end()) { return make_error("Unterminated string."); } - CharType escaped = 0; + char32_t escaped = 0; bool valid_escape = true; switch (code) { @@ -836,8 +836,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() { return make_error("Unterminated string."); } - CharType digit = _peek(); - CharType value = 0; + char32_t digit = _peek(); + char32_t value = 0; if (digit >= '0' && digit <= '9') { value = digit - '0'; } else if (digit >= 'a' && digit <= 'f') { @@ -940,7 +940,7 @@ void GDScriptTokenizer::check_indent() { } for (;;) { - CharType current_indent_char = _peek(); + char32_t current_indent_char = _peek(); int indent_count = 0; if (current_indent_char != ' ' && current_indent_char != '\t' && current_indent_char != '\r' && current_indent_char != '\n' && current_indent_char != '#') { @@ -970,7 +970,7 @@ void GDScriptTokenizer::check_indent() { // Check indent level. bool mixed = false; while (!_is_at_end()) { - CharType space = _peek(); + char32_t space = _peek(); if (space == '\t') { // Consider individual tab columns. column += tab_size - 1; @@ -1103,7 +1103,7 @@ void GDScriptTokenizer::_skip_whitespace() { } for (;;) { - CharType c = _peek(); + char32_t c = _peek(); switch (c) { case ' ': _advance(); @@ -1192,7 +1192,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { return make_token(Token::TK_EOF); } - const CharType c = _advance(); + const char32_t c = _advance(); if (c == '\\') { // Line continuation with backslash. diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 100ed3f132..4453982d08 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -183,14 +183,14 @@ public: private: String source; - const CharType *_source = nullptr; - const CharType *_current = nullptr; + const char32_t *_source = nullptr; + const char32_t *_current = nullptr; int line = -1, column = -1; int cursor_line = -1, cursor_column = -1; int tab_size = 4; // Keep track of multichar tokens. - const CharType *_start = nullptr; + const char32_t *_start = nullptr; int start_line = 0, start_column = 0; int leftmost_column = 0, rightmost_column = 0; @@ -202,30 +202,30 @@ private: Token last_newline; int pending_indents = 0; List<int> indent_stack; - List<CharType> paren_stack; - CharType indent_char = '\0'; + List<char32_t> paren_stack; + char32_t indent_char = '\0'; int position = 0; int length = 0; _FORCE_INLINE_ bool _is_at_end() { return position >= length; } - _FORCE_INLINE_ CharType _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; } + _FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; } int indent_level() const { return indent_stack.size(); } bool has_error() const { return !error_stack.empty(); } Token pop_error(); - CharType _advance(); + char32_t _advance(); void _skip_whitespace(); void check_indent(); Token make_error(const String &p_message); void push_error(const String &p_message); void push_error(const Token &p_error); - Token make_paren_error(CharType p_paren); + Token make_paren_error(char32_t p_paren); Token make_token(Token::Type p_type); Token make_literal(const Variant &p_literal); Token make_identifier(const StringName &p_identifier); - Token check_vcs_marker(CharType p_test, Token::Type p_double_type); - void push_paren(CharType p_char); - bool pop_paren(CharType p_expected); + Token check_vcs_marker(char32_t p_test, Token::Type p_double_type); + void push_paren(char32_t p_char); + bool pop_paren(char32_t p_expected); void newline(bool p_make_token); Token number(); diff --git a/modules/gdscript/icons/GDScript.svg b/modules/gdscript/icons/GDScript.svg index 953bb9ae9e..aa59125ea9 100644 --- a/modules/gdscript/icons/GDScript.svg +++ b/modules/gdscript/icons/GDScript.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539h2l0.56445-2.2578a5 5 0 0 0 0.6875 -0.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 0.28516 -0.68555l2.2539-0.5625v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" fill="#e0e0e0"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539h2l.56445-2.2578a5 5 0 0 0 .6875-.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 .28516-.68555l2.2539-.5625v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0"/></svg> diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 4d79d9d395..668dfd4835 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int start_pos = p_position.character; for (int c = p_position.character; c >= 0; c--) { start_pos = c; - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -500,7 +500,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int end_pos = p_position.character; for (int c = p_position.character; c < line.length(); c++) { - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -552,7 +552,7 @@ Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_positi } while (c >= 0) { - const CharType &character = line[c]; + const char32_t &character = line[c]; if (character == ')') { ++bracket_stack; } else if (character == '(') { diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index c554cbac05..da4cbe34c7 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -39,6 +39,11 @@ #include "gdscript_cache.h" #include "gdscript_tokenizer.h" +#ifdef TESTS_ENABLED +#include "tests/test_gdscript.h" +#include "tests/test_macros.h" +#endif + GDScriptLanguage *script_language_gd = nullptr; Ref<ResourceFormatLoaderGDScript> resource_loader_gd; Ref<ResourceFormatSaverGDScript> resource_saver_gd; @@ -79,7 +84,7 @@ public: return; } - // TODO: Readd compiled/encrypted GDScript on export. + // TODO: Readd compiled GDScript on export. return; } }; @@ -153,3 +158,26 @@ void unregister_gdscript_types() { GDScriptParser::cleanup(); GDScriptAnalyzer::cleanup(); } + +#ifdef TESTS_ENABLED +void test_tokenizer() { + TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER); +} + +void test_parser() { + TestGDScript::test(TestGDScript::TestType::TEST_PARSER); +} + +void test_compiler() { + TestGDScript::test(TestGDScript::TestType::TEST_COMPILER); +} + +void test_bytecode() { + TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE); +} + +REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer); +REGISTER_TEST_COMMAND("gdscript-parser", &test_parser); +REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler); +REGISTER_TEST_COMMAND("gdscript-bytecode", &test_bytecode); +#endif diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp new file mode 100644 index 0000000000..931b683a44 --- /dev/null +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -0,0 +1,307 @@ +/*************************************************************************/ +/* test_gdscript.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "test_gdscript.h" + +#include "core/io/file_access_pack.h" +#include "core/os/file_access.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/project_settings.h" +#include "core/string_builder.h" +#include "scene/resources/packed_scene.h" + +#include "modules/gdscript/gdscript_analyzer.h" +#include "modules/gdscript/gdscript_compiler.h" +#include "modules/gdscript/gdscript_parser.h" +#include "modules/gdscript/gdscript_tokenizer.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + +namespace TestGDScript { + +static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) { + GDScriptTokenizer tokenizer; + tokenizer.set_source_code(p_code); + + int tab_size = 4; +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); + } +#endif // TOOLS_ENABLED + String tab = String(" ").repeat(tab_size); + + GDScriptTokenizer::Token current = tokenizer.scan(); + while (current.type != GDScriptTokenizer::Token::TK_EOF) { + StringBuilder token; + token += " --> "; // Padding for line number. + + for (int l = current.start_line; l <= current.end_line; l++) { + print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab)); + } + + { + // Print carets to point at the token. + StringBuilder pointer; + pointer += " "; // Padding for line number. + int rightmost_column = current.rightmost_column; + if (current.end_line > current.start_line) { + rightmost_column--; // Don't point to the newline as a column. + } + for (int col = 1; col < rightmost_column; col++) { + if (col < current.leftmost_column) { + pointer += " "; + } else { + pointer += "^"; + } + } + print_line(pointer.as_string()); + } + + token += current.get_name(); + + if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) { + token += "("; + token += Variant::get_type_name(current.literal.get_type()); + token += ") "; + token += current.literal; + } + + print_line(token.as_string()); + + print_line("-------------------------------------------------------"); + + current = tokenizer.scan(); + } + + print_line(current.get_name()); // Should be EOF +} + +static void test_parser(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) { + GDScriptParser parser; + Error err = parser.parse(p_code, p_script_path, false); + + if (err != OK) { + const List<GDScriptParser::ParserError> &errors = parser.get_errors(); + for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + } + + GDScriptParser::TreePrinter printer; + + printer.print_tree(parser); +} + +static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) { + GDScriptParser parser; + Error err = parser.parse(p_code, p_script_path, false); + + if (err != OK) { + print_line("Error in parser:"); + const List<GDScriptParser::ParserError> &errors = parser.get_errors(); + for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + return; + } + + GDScriptAnalyzer analyzer(&parser); + err = analyzer.analyze(); + + if (err != OK) { + print_line("Error in analyzer:"); + const List<GDScriptParser::ParserError> &errors = parser.get_errors(); + for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + return; + } + + GDScriptCompiler compiler; + Ref<GDScript> script; + script.instance(); + script->set_path(p_script_path); + + err = compiler.compile(&parser, script.ptr(), false); + + if (err) { + print_line("Error in compiler:"); + print_line(vformat("%02d:%02d: %s", compiler.get_error_line(), compiler.get_error_column(), compiler.get_error())); + return; + } + + for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) { + const GDScriptFunction *func = E->value(); + + String signature = "Disassembling " + func->get_name().operator String() + "("; + for (int i = 0; i < func->get_argument_count(); i++) { + if (i > 0) { + signature += ", "; + } + signature += func->get_argument_name(i); + } + print_line(signature + ")"); + + func->disassemble(p_lines); + print_line(""); + print_line(""); + } +} + +void init_autoloads() { + Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + + // First pass, add the constants so they exist before any script is loaded. + for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { + const ProjectSettings::AutoloadInfo &info = E->get(); + + if (info.is_singleton) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(info.name, Variant()); + } + } + } + + // Second pass, load into global constants. + for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { + const ProjectSettings::AutoloadInfo &info = E->get(); + + if (!info.is_singleton) { + // Skip non-singletons since we don't have a scene tree here anyway. + continue; + } + + RES res = ResourceLoader::load(info.path); + ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path); + Node *n = nullptr; + if (res->is_class("PackedScene")) { + Ref<PackedScene> ps = res; + n = ps->instance(); + } else if (res->is_class("Script")) { + Ref<Script> script_res = res; + StringName ibt = script_res->get_instance_base_type(); + bool valid_type = ClassDB::is_parent_class(ibt, "Node"); + ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path); + + Object *obj = ClassDB::instance(ibt); + + ERR_CONTINUE_MSG(obj == nullptr, + "Cannot instance script for autoload, expected 'Node' inheritance, got: " + + String(ibt)); + + n = Object::cast_to<Node>(obj); + n->set_script(script_res); + } + + ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path); + n->set_name(info.name); + + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(info.name, n); + } + } +} + +void test(TestType p_type) { + List<String> cmdlargs = OS::get_singleton()->get_cmdline_args(); + + if (cmdlargs.empty()) { + return; + } + + String test = cmdlargs.back()->get(); + if (!test.ends_with(".gd")) { + print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test); + return; + } + + FileAccessRef fa = FileAccess::open(test, FileAccess::READ); + ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test); + + // Init PackedData since it's used by ProjectSettings. + PackedData *packed_data = memnew(PackedData); + + // Setup project settings since it's needed by the languages to get the global scripts. + // This also sets up the base resource path. + Error err = ProjectSettings::get_singleton()->setup(fa->get_path_absolute().get_base_dir(), String(), true); + if (err) { + print_line("Could not load project settings."); + // Keep going since some scripts still work without this. + } + + // Initialize the language for the test routine. + ScriptServer::init_languages(); + init_autoloads(); + + Vector<uint8_t> buf; + int flen = fa->get_len(); + buf.resize(fa->get_len() + 1); + fa->get_buffer(buf.ptrw(), flen); + buf.write[flen] = 0; + + String code; + code.parse_utf8((const char *)&buf[0]); + + Vector<String> lines; + int last = 0; + for (int i = 0; i <= code.length(); i++) { + if (code[i] == '\n' || code[i] == 0) { + lines.push_back(code.substr(last, i - last)); + last = i + 1; + } + } + + switch (p_type) { + case TEST_TOKENIZER: + test_tokenizer(code, lines); + break; + case TEST_PARSER: + test_parser(code, test, lines); + break; + case TEST_COMPILER: + test_compiler(code, test, lines); + break; + case TEST_BYTECODE: + print_line("Not implemented."); + } + + // Destroy stuff we set up earlier. + ScriptServer::finish_languages(); + memdelete(packed_data); +} + +} // namespace TestGDScript diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h new file mode 100644 index 0000000000..5aa962dcf8 --- /dev/null +++ b/modules/gdscript/tests/test_gdscript.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* test_gdscript.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 TEST_GDSCRIPT_H +#define TEST_GDSCRIPT_H + +namespace TestGDScript { + +enum TestType { + TEST_TOKENIZER, + TEST_PARSER, + TEST_COMPILER, + TEST_BYTECODE, +}; + +void test(TestType p_type); + +} // namespace TestGDScript + +#endif // TEST_GDSCRIPT_H diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub index c1d23a138b..58c033c75d 100644 --- a/modules/glslang/SCsub +++ b/modules/glslang/SCsub @@ -9,6 +9,7 @@ env_glslang = env_modules.Clone() if env["builtin_glslang"]: thirdparty_dir = "#thirdparty/glslang/" thirdparty_sources = [ + "glslang/CInterface/glslang_c_interface.cpp", "glslang/MachineIndependent/RemoveTree.cpp", "glslang/MachineIndependent/ParseHelper.cpp", "glslang/MachineIndependent/iomapper.cpp", @@ -30,7 +31,6 @@ if env["builtin_glslang"]: "glslang/MachineIndependent/intermOut.cpp", "glslang/MachineIndependent/SymbolTable.cpp", "glslang/MachineIndependent/glslang_tab.cpp", - "glslang/MachineIndependent/pch.cpp", "glslang/MachineIndependent/Versions.cpp", "glslang/MachineIndependent/ShaderLang.cpp", "glslang/MachineIndependent/parseConst.cpp", @@ -40,6 +40,7 @@ if env["builtin_glslang"]: "glslang/GenericCodeGen/Link.cpp", "glslang/GenericCodeGen/CodeGen.cpp", "OGLCompilersDLL/InitializeDll.cpp", + "SPIRV/CInterface/spirv_c_interface.cpp", "SPIRV/InReadableOrder.cpp", "SPIRV/GlslangToSpv.cpp", "SPIRV/SpvBuilder.cpp", @@ -49,6 +50,7 @@ if env["builtin_glslang"]: "SPIRV/SPVRemapper.cpp", "SPIRV/SpvPostProcess.cpp", "SPIRV/Logger.cpp", + "StandAlone/ResourceLimits.cpp", ] if env["platform"] == "windows": @@ -64,6 +66,8 @@ if env["builtin_glslang"]: else: env_glslang.Prepend(CPPPATH=[thirdparty_dir]) + env_glslang.Append(CPPDEFINES=["ENABLE_OPT=0"]) + env_thirdparty = env_glslang.Clone() env_thirdparty.disable_warnings() env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index dd28c4ad4a..3238e0108e 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -33,115 +33,10 @@ #include "servers/rendering/rendering_device.h" #include <SPIRV/GlslangToSpv.h> +#include <StandAlone/ResourceLimits.h> #include <glslang/Include/Types.h> #include <glslang/Public/ShaderLang.h> -static const TBuiltInResource default_builtin_resource = { - /*maxLights*/ 32, - /*maxClipPlanes*/ 6, - /*maxTextureUnits*/ 32, - /*maxTextureCoords*/ 32, - /*maxVertexAttribs*/ 64, - /*maxVertexUniformComponents*/ 4096, - /*maxVaryingFloats*/ 64, - /*maxVertexTextureImageUnits*/ 32, - /*maxCombinedTextureImageUnits*/ 80, - /*maxTextureImageUnits*/ 32, - /*maxFragmentUniformComponents*/ 4096, - /*maxDrawBuffers*/ 32, - /*maxVertexUniformVectors*/ 128, - /*maxVaryingVectors*/ 8, - /*maxFragmentUniformVectors*/ 16, - /*maxVertexOutputVectors*/ 16, - /*maxFragmentInputVectors*/ 15, - /*minProgramTexelOffset*/ -8, - /*maxProgramTexelOffset*/ 7, - /*maxClipDistances*/ 8, - /*maxComputeWorkGroupCountX*/ 65535, - /*maxComputeWorkGroupCountY*/ 65535, - /*maxComputeWorkGroupCountZ*/ 65535, - /*maxComputeWorkGroupSizeX*/ 1024, - /*maxComputeWorkGroupSizeY*/ 1024, - /*maxComputeWorkGroupSizeZ*/ 64, - /*maxComputeUniformComponents*/ 1024, - /*maxComputeTextureImageUnits*/ 16, - /*maxComputeImageUniforms*/ 8, - /*maxComputeAtomicCounters*/ 8, - /*maxComputeAtomicCounterBuffers*/ 1, - /*maxVaryingComponents*/ 60, - /*maxVertexOutputComponents*/ 64, - /*maxGeometryInputComponents*/ 64, - /*maxGeometryOutputComponents*/ 128, - /*maxFragmentInputComponents*/ 128, - /*maxImageUnits*/ 8, - /*maxCombinedImageUnitsAndFragmentOutputs*/ 8, - /*maxCombinedShaderOutputResources*/ 8, - /*maxImageSamples*/ 0, - /*maxVertexImageUniforms*/ 0, - /*maxTessControlImageUniforms*/ 0, - /*maxTessEvaluationImageUniforms*/ 0, - /*maxGeometryImageUniforms*/ 0, - /*maxFragmentImageUniforms*/ 8, - /*maxCombinedImageUniforms*/ 8, - /*maxGeometryTextureImageUnits*/ 16, - /*maxGeometryOutputVertices*/ 256, - /*maxGeometryTotalOutputComponents*/ 1024, - /*maxGeometryUniformComponents*/ 1024, - /*maxGeometryVaryingComponents*/ 64, - /*maxTessControlInputComponents*/ 128, - /*maxTessControlOutputComponents*/ 128, - /*maxTessControlTextureImageUnits*/ 16, - /*maxTessControlUniformComponents*/ 1024, - /*maxTessControlTotalOutputComponents*/ 4096, - /*maxTessEvaluationInputComponents*/ 128, - /*maxTessEvaluationOutputComponents*/ 128, - /*maxTessEvaluationTextureImageUnits*/ 16, - /*maxTessEvaluationUniformComponents*/ 1024, - /*maxTessPatchComponents*/ 120, - /*maxPatchVertices*/ 32, - /*maxTessGenLevel*/ 64, - /*maxViewports*/ 16, - /*maxVertexAtomicCounters*/ 0, - /*maxTessControlAtomicCounters*/ 0, - /*maxTessEvaluationAtomicCounters*/ 0, - /*maxGeometryAtomicCounters*/ 0, - /*maxFragmentAtomicCounters*/ 8, - /*maxCombinedAtomicCounters*/ 8, - /*maxAtomicCounterBindings*/ 1, - /*maxVertexAtomicCounterBuffers*/ 0, - /*maxTessControlAtomicCounterBuffers*/ 0, - /*maxTessEvaluationAtomicCounterBuffers*/ 0, - /*maxGeometryAtomicCounterBuffers*/ 0, - /*maxFragmentAtomicCounterBuffers*/ 1, - /*maxCombinedAtomicCounterBuffers*/ 1, - /*maxAtomicCounterBufferSize*/ 16384, - /*maxTransformFeedbackBuffers*/ 4, - /*maxTransformFeedbackInterleavedComponents*/ 64, - /*maxCullDistances*/ 8, - /*maxCombinedClipAndCullDistances*/ 8, - /*maxSamples*/ 4, - /*maxMeshOutputVerticesNV*/ 0, - /*maxMeshOutputPrimitivesNV*/ 0, - /*maxMeshWorkGroupSizeX_NV*/ 0, - /*maxMeshWorkGroupSizeY_NV*/ 0, - /*maxMeshWorkGroupSizeZ_NV*/ 0, - /*maxTaskWorkGroupSizeX_NV*/ 0, - /*maxTaskWorkGroupSizeY_NV*/ 0, - /*maxTaskWorkGroupSizeZ_NV*/ 0, - /*maxMeshViewCountNV*/ 0, - /*limits*/ { - /*nonInductiveForLoops*/ true, - /*whileLoops*/ true, - /*doWhileLoops*/ true, - /*generalUniformIndexing*/ true, - /*generalAttributeMatrixVectorIndexing*/ true, - /*generalVaryingIndexing*/ true, - /*generalSamplerIndexing*/ true, - /*generalVariableIndexing*/ true, - /*generalConstantMatrixVectorIndexing*/ true, - } -}; - static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error) { Vector<uint8_t> ret; @@ -175,7 +70,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage std::string pre_processed_code; //preprocess - if (!shader.preprocess(&default_builtin_resource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) { + if (!shader.preprocess(&glslang::DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) { if (r_error) { (*r_error) = "Failed pre-process:\n"; (*r_error) += shader.getInfoLog(); @@ -190,7 +85,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage shader.setStrings(&cs_strings, 1); //parse - if (!shader.parse(&default_builtin_resource, DefaultVersion, false, messages)) { + if (!shader.parse(&glslang::DefaultTBuiltInResource, DefaultVersion, false, messages)) { if (r_error) { (*r_error) = "Failed parse:\n"; (*r_error) += shader.getInfoLog(); diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 57fbc5bfc0..4dccb03369 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -11,6 +11,8 @@ </description> <tutorials> <link title="Using gridmaps">https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> + <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link> + <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link> </tutorials> <methods> <method name="clear"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index ccf1e49693..21fa83278b 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -706,7 +706,7 @@ void GridMap::_update_visibility() { Octant *octant = e->value(); for (int i = 0; i < octant->multimesh_instances.size(); i++) { const Octant::MultimeshInstance &mi = octant->multimesh_instances[i]; - RS::get_singleton()->instance_set_visible(mi.instance, is_visible()); + RS::get_singleton()->instance_set_visible(mi.instance, is_visible_in_tree()); } } } diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 7e136ff9bb..ee17a52d31 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -93,7 +93,7 @@ class GridMapEditor : public VBoxContainer { List<SetItem> set_items; - GridMap *node; + GridMap *node = nullptr; MeshLibrary *last_mesh_library; ClipMode clip_mode; diff --git a/modules/gridmap/icons/GridMap.svg b/modules/gridmap/icons/GridMap.svg index eafe1211f2..7a36fd888c 100644 --- a/modules/gridmap/icons/GridMap.svg +++ b/modules/gridmap/icons/GridMap.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/></svg> diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp index 7bb70b098f..320da182f8 100644 --- a/modules/jsonrpc/jsonrpc.cpp +++ b/modules/jsonrpc/jsonrpc.cpp @@ -98,6 +98,10 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem if (p_action.get_type() == Variant::DICTIONARY) { Dictionary dict = p_action; String method = dict.get("method", ""); + if (method.begins_with("$/")) { + return ret; + } + Array args; if (dict.has("params")) { Variant params = dict.get("params", Variant()); diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h index e2b7ab0975..6f3f8003e0 100644 --- a/modules/jsonrpc/jsonrpc.h +++ b/modules/jsonrpc/jsonrpc.h @@ -31,7 +31,7 @@ #ifndef GODOT_JSON_RPC_H #define GODOT_JSON_RPC_H -#include "core/object.h" +#include "core/class_db.h" #include "core/variant.h" class JSONRPC : public Object { diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index 501bfff075..12a982df6e 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -43,8 +43,8 @@ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" #define PEM_END_CRT "-----END CERTIFICATE-----\n" -#include "mbedtls/pem.h" #include <mbedtls/debug.h> +#include <mbedtls/pem.h> CryptoKey *CryptoKeyMbedTLS::create() { return memnew(CryptoKeyMbedTLS); @@ -294,20 +294,15 @@ Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoK unsigned char buf[4096]; memset(buf, 0, 4096); - Ref<X509CertificateMbedTLS> out; - out.instance(); - mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg); - - int err = mbedtls_x509_crt_parse(&(out->cert), buf, 4096); - if (err != 0) { - mbedtls_mpi_free(&serial); - mbedtls_x509write_crt_free(&crt); - ERR_PRINT("Generated invalid certificate: " + itos(err)); - return nullptr; - } - + int ret = mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg); mbedtls_mpi_free(&serial); mbedtls_x509write_crt_free(&crt); + ERR_FAIL_COND_V_MSG(ret != 0, nullptr, "Failed to generate certificate: " + itos(ret)); + buf[4095] = '\0'; // Make sure strlen can't fail. + + Ref<X509CertificateMbedTLS> out; + out.instance(); + out->load_from_memory(buf, strlen((char *)buf) + 1); // Use strlen to find correct output size. return out; } diff --git a/modules/mono/SCsub b/modules/mono/SCsub index e8f3174a0a..3b94949470 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -40,6 +40,11 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]: godot_tools_build.build(env_mono, api_sln_cmd) + # Build Godot.NET.Sdk + import build_scripts.godot_net_sdk_build as godot_net_sdk_build + + godot_net_sdk_build.build(env_mono) + # Add sources env_mono.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py new file mode 100644 index 0000000000..3bfba0f0f6 --- /dev/null +++ b/modules/mono/build_scripts/godot_net_sdk_build.py @@ -0,0 +1,45 @@ +# Build Godot.NET.Sdk solution + +import os + +from SCons.Script import Dir + + +def build_godot_net_sdk(source, target, env): + # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str + + module_dir = env["module_dir"] + + solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln") + build_config = "Release" + + from .solution_builder import build_solution + + extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]] + + build_solution(env, solution_path, build_config, extra_msbuild_args) + # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them. + + +def build(env_mono): + assert env_mono["tools"] + + output_dir = Dir("#bin").abspath + editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools") + nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs") + + module_dir = os.getcwd() + + package_version_file = os.path.join( + module_dir, "editor", "Godot.NET.Sdk", "Godot.NET.Sdk", "Godot.NET.Sdk_PackageVersion.txt" + ) + + with open(package_version_file, mode="r") as f: + version = f.read().strip() + + target_filenames = ["Godot.NET.Sdk.%s.nupkg" % version] + + targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames] + + cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir) + env_mono.AlwaysBuild(cmd) diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 3e771e06f0..6057004166 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,6 +1,5 @@ import os import os.path -import sys import subprocess from SCons.Script import Dir, Environment @@ -397,9 +396,8 @@ def configure(env, env_mono): mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() if tools_enabled: + # Only supported for editor builds. copy_mono_root_files(env, mono_root) - else: - print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.") def make_template_dir(env, mono_root): diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py index 3090a4759a..0ec7e2f433 100644 --- a/modules/mono/build_scripts/mono_reg_utils.py +++ b/modules/mono/build_scripts/mono_reg_utils.py @@ -9,7 +9,7 @@ if os.name == "nt": def _reg_open_key(key, subkey): try: return winreg.OpenKey(key, subkey) - except (WindowsError, OSError): + except OSError: if platform.architecture()[0] == "32bit": bitness_sam = winreg.KEY_WOW64_64KEY else: @@ -37,7 +37,7 @@ def _find_mono_in_reg(subkey, bits): with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey: value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0] return value - except (WindowsError, OSError): + except OSError: return None @@ -48,7 +48,7 @@ def _find_mono_in_reg_old(subkey, bits): if default_clr: return _find_mono_in_reg(subkey + "\\" + default_clr, bits) return None - except (WindowsError, EnvironmentError): + except OSError: return None @@ -97,7 +97,7 @@ def find_msbuild_tools_path_reg(): raise ValueError("Cannot find `installationPath` entry") except ValueError as e: print("Error reading output from vswhere: " + e.message) - except WindowsError: + except OSError: pass # Fine, vswhere not found except (subprocess.CalledProcessError, OSError): pass @@ -109,5 +109,5 @@ def find_msbuild_tools_path_reg(): with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey: value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0] return value - except (WindowsError, OSError): + except OSError: return "" diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py index 03f4e57f02..6a621c3c8b 100644 --- a/modules/mono/build_scripts/solution_builder.py +++ b/modules/mono/build_scripts/solution_builder.py @@ -8,9 +8,6 @@ def find_dotnet_cli(): import os.path if os.name == "nt": - windows_exts = os.environ["PATHEXT"] - windows_exts = windows_exts.split(os.pathsep) if windows_exts else [] - for hint_dir in os.environ["PATH"].split(os.pathsep): hint_dir = hint_dir.strip('"') hint_path = os.path.join(hint_dir, "dotnet") diff --git a/modules/mono/config.py b/modules/mono/config.py index cd659057ef..d060ae9b28 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -23,18 +23,16 @@ def configure(env): envvars.Add( PathVariable( "mono_prefix", - "Path to the mono installation directory for the target platform and architecture", + "Path to the Mono installation directory for the target platform and architecture", "", PathVariable.PathAccept, ) ) - envvars.Add(BoolVariable("mono_static", "Statically link mono", default_mono_static)) - envvars.Add(BoolVariable("mono_glue", "Build with the mono glue sources", True)) + envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static)) + envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True)) envvars.Add(BoolVariable("build_cil", "Build C# solutions", True)) envvars.Add( - BoolVariable( - "copy_mono_root", "Make a copy of the mono installation directory to bundle with the editor", False - ) + BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True) ) # TODO: It would be great if this could be detected automatically instead diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index bbdec224f0..8928b6e5e3 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -940,8 +940,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { // Use a placeholder for now to avoid losing the state when saving a scene - obj->set_script(scr); - PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj); obj->set_script_instance(placeholder); @@ -968,14 +966,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) { Ref<CSharpScript> script = E->get(); - if (!script->get_path().empty()) { #ifdef TOOLS_ENABLED - script->exports_invalidated = true; + script->exports_invalidated = true; #endif - script->signals_invalidated = true; + script->signals_invalidated = true; + if (!script->get_path().empty()) { script->reload(p_soft_reload); - script->update_exports(); if (!script->valid) { script->pending_reload_instances.clear(); @@ -1950,6 +1947,7 @@ MonoObject *CSharpInstance::_internal_new_managed() { } void CSharpInstance::mono_object_disposed(MonoObject *p_obj) { + // Must make sure event signals are not left dangling disconnect_event_signals(); #ifdef DEBUG_ENABLED @@ -1965,6 +1963,9 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f CRASH_COND(gchandle.is_released()); #endif + // Must make sure event signals are not left dangling + disconnect_event_signals(); + r_remove_script_instance = false; if (_unreference_owner_unsafe()) { @@ -2224,6 +2225,9 @@ CSharpInstance::~CSharpInstance() { destructing_script_instance = true; + // Must make sure event signals are not left dangling + disconnect_event_signals(); + if (!gchandle.is_released()) { if (!predelete_notified && !ref_dying) { // This destructor is not called from the owners destructor. @@ -2702,7 +2706,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect if (!property->has_getter()) { #ifdef TOOLS_ENABLED if (exported) { - ERR_PRINT("Read-only property cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); + ERR_PRINT("Cannot export a property without a getter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); } #endif return false; @@ -2710,7 +2714,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect if (!property->has_setter()) { #ifdef TOOLS_ENABLED if (exported) { - ERR_PRINT("Write-only property (without getter) cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); + ERR_PRINT("Cannot export a property without a setter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'."); } #endif return false; @@ -2955,13 +2959,24 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon CRASH_COND(p_script->native == nullptr); + p_script->valid = true; + + update_script_class_info(p_script); + +#ifdef TOOLS_ENABLED + p_script->_update_member_info_no_exports(); +#endif +} + +// Extract information about the script using the mono class. +void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) { GDMonoClass *base = p_script->script_class->get_parent_class(); + // `base` should only be set if the script is a user defined type. if (base != p_script->native) { p_script->base = base; } - p_script->valid = true; p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute)); if (!p_script->tool) { @@ -2996,17 +3011,74 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native); - // Need to fetch method from base classes as well + p_script->rpc_functions.clear(); + p_script->rpc_variables.clear(); + GDMonoClass *top = p_script->script_class; while (top && top != p_script->native) { + // Fetch methods from base classes as well top->fetch_methods_with_godot_api_checks(p_script->native); + + // Update RPC info + { + Vector<GDMonoMethod *> methods = top->get_all_methods(); + for (int i = 0; i < methods.size(); i++) { + if (!methods[i]->is_static()) { + MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]); + if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { + ScriptNetData nd; + nd.name = methods[i]->get_name(); + nd.mode = mode; + if (-1 == p_script->rpc_functions.find(nd)) { + p_script->rpc_functions.push_back(nd); + } + } + } + } + } + + { + Vector<GDMonoField *> fields = top->get_all_fields(); + for (int i = 0; i < fields.size(); i++) { + if (!fields[i]->is_static()) { + MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(fields[i]); + if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { + ScriptNetData nd; + nd.name = fields[i]->get_name(); + nd.mode = mode; + if (-1 == p_script->rpc_variables.find(nd)) { + p_script->rpc_variables.push_back(nd); + } + } + } + } + } + + { + Vector<GDMonoProperty *> properties = top->get_all_properties(); + for (int i = 0; i < properties.size(); i++) { + if (!properties[i]->is_static()) { + MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(properties[i]); + if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { + ScriptNetData nd; + nd.name = properties[i]->get_name(); + nd.mode = mode; + if (-1 == p_script->rpc_variables.find(nd)) { + p_script->rpc_variables.push_back(nd); + } + } + } + } + } + top = top->get_parent_class(); } + // Sort so we are 100% that they are always the same. + p_script->rpc_functions.sort_custom<SortNetData>(); + p_script->rpc_variables.sort_custom<SortNetData>(); + p_script->load_script_signals(p_script->script_class, p_script->native); -#ifdef TOOLS_ENABLED - p_script->_update_member_info_no_exports(); -#endif } bool CSharpScript::can_instance() const { @@ -3305,124 +3377,15 @@ Error CSharpScript::reload(bool p_keep_state) { print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path()); #endif - tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); - - if (!tool) { - GDMonoClass *nesting_class = script_class->get_nesting_class(); - tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute)); - } - -#if TOOLS_ENABLED - if (!tool) { - tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly(); - } -#endif - native = GDMonoUtils::get_class_native_base(script_class); CRASH_COND(native == nullptr); - GDMonoClass *base_class = script_class->get_parent_class(); - - if (base_class != native) { - base = base_class; - } - -#ifdef DEBUG_ENABLED - // For debug builds, we must fetch from all native base methods as well. - // Native base methods must be fetched before the current class. - // Not needed if the script class itself is a native class. - - if (script_class != native) { - GDMonoClass *native_top = native; - while (native_top) { - native_top->fetch_methods_with_godot_api_checks(native); - - if (native_top == CACHED_CLASS(GodotObject)) { - break; - } - - native_top = native_top->get_parent_class(); - } - } -#endif - - script_class->fetch_methods_with_godot_api_checks(native); + update_script_class_info(this); - // Need to fetch method from base classes as well - GDMonoClass *top = script_class; - while (top && top != native) { - top->fetch_methods_with_godot_api_checks(native); - top = top->get_parent_class(); - } - - load_script_signals(script_class, native); _update_exports(); } - rpc_functions.clear(); - rpc_variables.clear(); - - GDMonoClass *top = script_class; - while (top && top != native) { - { - Vector<GDMonoMethod *> methods = top->get_all_methods(); - for (int i = 0; i < methods.size(); i++) { - if (!methods[i]->is_static()) { - MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(methods[i]); - if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { - ScriptNetData nd; - nd.name = methods[i]->get_name(); - nd.mode = mode; - if (-1 == rpc_functions.find(nd)) { - rpc_functions.push_back(nd); - } - } - } - } - } - - { - Vector<GDMonoField *> fields = top->get_all_fields(); - for (int i = 0; i < fields.size(); i++) { - if (!fields[i]->is_static()) { - MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(fields[i]); - if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { - ScriptNetData nd; - nd.name = fields[i]->get_name(); - nd.mode = mode; - if (-1 == rpc_variables.find(nd)) { - rpc_variables.push_back(nd); - } - } - } - } - } - - { - Vector<GDMonoProperty *> properties = top->get_all_properties(); - for (int i = 0; i < properties.size(); i++) { - if (!properties[i]->is_static()) { - MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(properties[i]); - if (MultiplayerAPI::RPC_MODE_DISABLED != mode) { - ScriptNetData nd; - nd.name = properties[i]->get_name(); - nd.mode = mode; - if (-1 == rpc_variables.find(nd)) { - rpc_variables.push_back(nd); - } - } - } - } - } - - top = top->get_parent_class(); - } - - // Sort so we are 100% that they are always the same. - rpc_functions.sort_custom<SortNetData>(); - rpc_variables.sort_custom<SortNetData>(); - return OK; } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index f0b43a40f9..316fd78f2a 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -164,6 +164,7 @@ private: // Do not use unless you know what you are doing friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *); static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native); + static void update_script_class_info(Ref<CSharpScript> p_script); static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native); MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const; @@ -287,7 +288,7 @@ public: void mono_object_disposed(MonoObject *p_obj); /* - * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal + * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if * 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner. */ void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj index 86a0a4393e..8304d9e321 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj @@ -1,13 +1,13 @@ <Project Sdk="Microsoft.Build.NoTargets/2.0.1"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <Description>MSBuild .NET Sdk for Godot projects.</Description> <Authors>Godot Engine contributors</Authors> <PackageId>Godot.NET.Sdk</PackageId> <Version>4.0.0</Version> - <PackageVersion>4.0.0-dev2</PackageVersion> <PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl> <PackageType>MSBuildSdk</PackageType> <PackageTags>MSBuildSdk</PackageTags> @@ -19,7 +19,13 @@ <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn> </PropertyGroup> - <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') "> + <Target Name="ReadGodotNETSdkVersion" BeforeTargets="BeforeBuild;BeforeRebuild;CoreCompile"> + <PropertyGroup> + <PackageVersion>$([System.IO.File]::ReadAllText('$(ProjectDir)Godot.NET.Sdk_PackageVersion.txt').Trim())</PackageVersion> + </PropertyGroup> + </Target> + + <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') " DependsOnTargets="ReadGodotNETSdkVersion"> <PropertyGroup> <NuspecProperties> id=$(PackageId); @@ -32,4 +38,13 @@ </NuspecProperties> </PropertyGroup> </Target> + + <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack"> + <PropertyGroup> + <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath> + <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir> + </PropertyGroup> + <Copy SourceFiles="$(OutputPath)$(PackageId).$(PackageVersion).nupkg" + DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" /> + </Target> </Project> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec index 5b5cefe80e..ba68a4da43 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec @@ -17,6 +17,6 @@ <repository url="$projecturl$" /> </metadata> <files> - <file src="Sdk\**" target="Sdk" />\ + <file src="Sdk\**" target="Sdk" /> </files> </package> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt new file mode 100644 index 0000000000..34749489b9 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt @@ -0,0 +1 @@ +4.0.0-dev3 diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props index dfc59e6ccb..5febcf3175 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props @@ -14,15 +14,15 @@ <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir> <GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir> - <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.mono\temp\'. --> - <BaseOutputPath>$(GodotProjectDir).mono\temp\bin\</BaseOutputPath> - <OutputPath>$(GodotProjectDir).mono\temp\bin\$(Configuration)\</OutputPath> + <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. --> + <BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath> + <OutputPath>$(GodotProjectDir).godot\mono\temp\bin\$(Configuration)\</OutputPath> <!-- Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set. Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet. --> - <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\$(Configuration)\</IntermediateOutputPath> - <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\</BaseIntermediateOutputPath> + <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\$(Configuration)\</IntermediateOutputPath> + <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\</BaseIntermediateOutputPath> <!-- Do not append the target framework name to the output path. --> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> @@ -88,7 +88,7 @@ <PropertyGroup> <!-- ExportDebug also defines DEBUG like Debug does. --> <DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants> - <!-- Debug defines TOOLS to differenciate between Debug and ExportDebug configurations. --> + <!-- Debug defines TOOLS to differentiate between Debug and ExportDebug configurations. --> <DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants> <DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants> @@ -102,11 +102,11 @@ --> <Reference Include="GodotSharp"> <Private>false</Private> - <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath> + <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath> </Reference> <Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' "> <Private>false</Private> - <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath> + <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath> </Reference> </ItemGroup> </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs index 5edf72d63e..9b7b422276 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs @@ -15,14 +15,14 @@ namespace GodotTools.BuildLogger public void Initialize(IEventSource eventSource) { if (null == Parameters) - throw new LoggerException("Log directory was not set."); + throw new LoggerException("Log directory parameter not specified."); var parameters = Parameters.Split(new[] { ';' }); string logDir = parameters[0]; if (string.IsNullOrEmpty(logDir)) - throw new LoggerException("Log directory was not set."); + throw new LoggerException("Log directory parameter is empty."); if (parameters.Length > 1) throw new LoggerException("Too many parameters passed."); @@ -51,22 +51,31 @@ namespace GodotTools.BuildLogger { throw new LoggerException("Failed to create log file: " + ex.Message); } - else - { - // Unexpected failure - throw; - } + + // Unexpected failure + throw; } eventSource.ProjectStarted += eventSource_ProjectStarted; - eventSource.TaskStarted += eventSource_TaskStarted; + eventSource.ProjectFinished += eventSource_ProjectFinished; eventSource.MessageRaised += eventSource_MessageRaised; eventSource.WarningRaised += eventSource_WarningRaised; eventSource.ErrorRaised += eventSource_ErrorRaised; - eventSource.ProjectFinished += eventSource_ProjectFinished; } - void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e) + private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) + { + WriteLine(e.Message); + indent++; + } + + private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) + { + indent--; + WriteLine(e.Message); + } + + private void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e) { string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}"; @@ -81,7 +90,7 @@ namespace GodotTools.BuildLogger issuesStreamWriter.WriteLine(errorLine); } - void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) + private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e) { string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}"; @@ -108,40 +117,6 @@ namespace GodotTools.BuildLogger } } - private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e) - { - // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName - // To keep this log clean, this logger will ignore these events. - } - - private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e) - { - WriteLine(e.Message); - indent++; - } - - private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e) - { - indent--; - WriteLine(e.Message); - } - - /// <summary> - /// Write a line to the log, adding the SenderName - /// </summary> - private void WriteLineWithSender(string line, BuildEventArgs e) - { - if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase)) - { - // Well, if the sender name is MSBuild, let's leave it out for prettiness - WriteLine(line); - } - else - { - WriteLine(e.SenderName + ": " + line); - } - } - /// <summary> /// Write a line to the log, adding the SenderName and Message /// (these parameters are on all MSBuild event argument objects) diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index e6b0e8f1df..b217ae4bf7 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; namespace GodotTools.Core { @@ -14,14 +15,18 @@ namespace GodotTools.Core if (Path.DirectorySeparatorChar == '\\') dir = dir.Replace("/", "\\") + "\\"; - Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute); - Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute); + var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute); + var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute); - return relRoot.MakeRelativeUri(fullPath).ToString(); + // MakeRelativeUri converts spaces to %20, hence why we need UnescapeDataString + return Uri.UnescapeDataString(relRoot.MakeRelativeUri(fullPath).ToString()); } public static string NormalizePath(this string path) { + if (string.IsNullOrEmpty(path)) + return path; + bool rooted = path.IsAbsolutePath(); path = path.Replace('\\', '/'); @@ -31,7 +36,17 @@ namespace GodotTools.Core path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim(); - return rooted ? Path.DirectorySeparatorChar + path : path; + if (!rooted) + return path; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string maybeDrive = parts[0]; + if (maybeDrive.Length == 2 && maybeDrive[1] == ':') + return path; // Already has drive letter + } + + return Path.DirectorySeparatorChar + path; } private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory); @@ -45,7 +60,7 @@ namespace GodotTools.Core public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false) { - var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" }; + var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"}; if (allowDirSeparator) { diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index 572c541412..0f50c90531 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -121,7 +121,7 @@ namespace GodotTools.IdeMessaging this.messageHandler = messageHandler; this.logger = logger; - string projectMetadataDir = Path.Combine(godotProjectDir, ".mono", "metadata"); + string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata"); MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs index 6f318aab4a..cc0da44a13 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -2,6 +2,7 @@ using GodotTools.Core; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; namespace GodotTools.ProjectEditor @@ -88,7 +89,7 @@ namespace GodotTools.ProjectEditor string solutionPath = Path.Combine(DirectoryPath, Name + ".sln"); string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg); - File.WriteAllText(solutionPath, content); + File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM } public DotNetSolution(string name) diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj index 41c94f19c8..37123ba2b2 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj @@ -10,6 +10,7 @@ </ItemGroup> <ItemGroup> <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" /> + <ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" /> </ItemGroup> <!-- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described @@ -20,7 +21,7 @@ <ItemGroup> <None Include="MSBuild.exe" CopyToOutputDirectory="Always" /> </ItemGroup> - <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition="$([MSBuild]::IsOsPlatform(Windows))"> + <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) "> <PropertyGroup> <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath> <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs index 01d7c99662..7d49d251dd 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs @@ -3,14 +3,13 @@ using System.IO; using System.Text; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using GodotTools.Shared; namespace GodotTools.ProjectEditor { public static class ProjectGenerator { - public const string GodotSdkVersionToUse = "4.0.0-dev2"; - - public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}"; + public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}"; public static ProjectRootElement GenGameProject(string name) { diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index 4041c56597..4e2c0f17cc 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -61,10 +61,9 @@ namespace GodotTools.ProjectEditor if (item.ItemType != itemType) continue; - string normalizedExclude = item.Exclude.NormalizePath(); - - var glob = MSBuildGlob.Parse(normalizedExclude); + string normalizedRemove = item.Remove.NormalizePath(); + var glob = MSBuildGlob.Parse(normalizedRemove); excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile))); } diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets new file mode 100644 index 0000000000..1d382dcb43 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets @@ -0,0 +1,36 @@ +<Project> + <!-- Generate C# file with the version of all the nupkgs bundled with Godot --> + + <Target Name="SetPropertiesForGenerateGodotNupkgsVersions"> + <PropertyGroup> + <GodotNETSdkPackageVersionFile>$(SolutionDir)..\Godot.NET.Sdk\Godot.NET.Sdk\Godot.NET.Sdk_PackageVersion.txt</GodotNETSdkPackageVersionFile> + <GeneratedGodotNupkgsVersionsFile>$(IntermediateOutputPath)GodotNupkgsVersions.g.cs</GeneratedGodotNupkgsVersionsFile> + </PropertyGroup> + </Target> + + <Target Name="GenerateGodotNupkgsVersionsFile" + DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile" + BeforeTargets="BeforeCompile;CoreCompile"> + <ItemGroup> + <Compile Include="$(GeneratedGodotNupkgsVersionsFile)" /> + <FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" /> + </ItemGroup> + </Target> + <Target Name="_GenerateGodotNupkgsVersionsFile" + DependsOnTargets="SetPropertiesForGenerateGodotNupkgsVersions" + Inputs="$(MSBuildProjectFile);@(GodotNETSdkPackageVersionFile)" + Outputs="$(GeneratedGodotNupkgsVersionsFile)"> + <PropertyGroup> + <GenerateGodotNupkgsVersionsCode><![CDATA[ +namespace $(RootNamespace) { + public class GeneratedGodotNupkgsVersions { + public const string GodotNETSdk = "$([System.IO.File]::ReadAllText('$(GodotNETSdkPackageVersionFile)').Trim())"%3b + } +} +]]></GenerateGodotNupkgsVersionsCode> + </PropertyGroup> + <WriteLinesToFile Lines="$(GenerateGodotNupkgsVersionsCode)" + File="$(GeneratedGodotNupkgsVersionsFile)" + Overwrite="True" WriteOnlyWhenDifferent="True" /> + </Target> +</Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj new file mode 100644 index 0000000000..3bc1698c15 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj @@ -0,0 +1,6 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + </PropertyGroup> + <Import Project="GenerateGodotNupkgsVersions.targets" /> +</Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln index ba5379e562..d3107a69db 100644 --- a/modules/mono/editor/GodotTools/GodotTools.sln +++ b/modules/mono/editor/GodotTools/GodotTools.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,5 +45,9 @@ Global {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU + {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs deleted file mode 100644 index 3ab669a9f3..0000000000 --- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs +++ /dev/null @@ -1,339 +0,0 @@ -using Godot; -using System; -using System.IO; -using Godot.Collections; -using GodotTools.Internals; -using static GodotTools.Internals.Globals; -using File = GodotTools.Utils.File; -using Path = System.IO.Path; - -namespace GodotTools -{ - public class BottomPanel : VBoxContainer - { - private EditorInterface editorInterface; - - private TabContainer panelTabs; - - private VBoxContainer panelBuildsTab; - - private ItemList buildTabsList; - private TabContainer buildTabs; - - private Button warningsBtn; - private Button errorsBtn; - private Button viewLogBtn; - - private void _UpdateBuildTab(int index, int? currentTab) - { - var tab = (BuildTab)buildTabs.GetChild(index); - - string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution); - itemName += " [" + tab.BuildInfo.Configuration + "]"; - - buildTabsList.AddItem(itemName, tab.IconTexture); - - string itemTooltip = "Solution: " + tab.BuildInfo.Solution; - itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration; - itemTooltip += "\nStatus: "; - - if (tab.BuildExited) - itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored"; - else - itemTooltip += "Running"; - - if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error) - itemTooltip += $"\nErrors: {tab.ErrorCount}"; - - itemTooltip += $"\nWarnings: {tab.WarningCount}"; - - buildTabsList.SetItemTooltip(index, itemTooltip); - - // If this tab was already selected before the changes or if no tab was selected - if (currentTab == null || currentTab == index) - { - buildTabsList.Select(index); - _BuildTabsItemSelected(index); - } - } - - private void _UpdateBuildTabsList() - { - buildTabsList.Clear(); - - int? currentTab = buildTabs.CurrentTab; - - if (currentTab < 0 || currentTab >= buildTabs.GetTabCount()) - currentTab = null; - - for (int i = 0; i < buildTabs.GetChildCount(); i++) - _UpdateBuildTab(i, currentTab); - } - - public BuildTab GetBuildTabFor(BuildInfo buildInfo) - { - foreach (var buildTab in new Array<BuildTab>(buildTabs.GetChildren())) - { - if (buildTab.BuildInfo.Equals(buildInfo)) - return buildTab; - } - - var newBuildTab = new BuildTab(buildInfo); - AddBuildTab(newBuildTab); - - return newBuildTab; - } - - private void _BuildTabsItemSelected(int idx) - { - if (idx < 0 || idx >= buildTabs.GetTabCount()) - throw new IndexOutOfRangeException(); - - buildTabs.CurrentTab = idx; - if (!buildTabs.Visible) - buildTabs.Visible = true; - - warningsBtn.Visible = true; - errorsBtn.Visible = true; - viewLogBtn.Visible = true; - } - - private void _BuildTabsNothingSelected() - { - if (buildTabs.GetTabCount() != 0) - { - // just in case - buildTabs.Visible = false; - - // This callback is called when clicking on the empty space of the list. - // ItemList won't deselect the items automatically, so we must do it ourselves. - buildTabsList.UnselectAll(); - } - - warningsBtn.Visible = false; - errorsBtn.Visible = false; - viewLogBtn.Visible = false; - } - - private void _WarningsToggled(bool pressed) - { - int currentTab = buildTabs.CurrentTab; - - if (currentTab < 0 || currentTab >= buildTabs.GetTabCount()) - throw new InvalidOperationException("No tab selected"); - - var buildTab = (BuildTab)buildTabs.GetChild(currentTab); - buildTab.WarningsVisible = pressed; - buildTab.UpdateIssuesList(); - } - - private void _ErrorsToggled(bool pressed) - { - int currentTab = buildTabs.CurrentTab; - - if (currentTab < 0 || currentTab >= buildTabs.GetTabCount()) - throw new InvalidOperationException("No tab selected"); - - var buildTab = (BuildTab)buildTabs.GetChild(currentTab); - buildTab.ErrorsVisible = pressed; - buildTab.UpdateIssuesList(); - } - - public void BuildProjectPressed() - { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) - return; // No solution to build - - string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor"); - string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player"); - - CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath); - - if (File.Exists(editorScriptsMetadataPath)) - { - try - { - File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); - } - catch (IOException e) - { - GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}"); - return; - } - } - - bool buildSuccess = BuildManager.BuildProjectBlocking("Debug"); - - if (!buildSuccess) - return; - - // Notify running game for hot-reload - Internal.EditorDebuggerNodeReloadScripts(); - - // Hot-reload in the editor - GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); - - if (Internal.IsAssembliesReloadingNeeded()) - Internal.ReloadAssemblies(softReload: false); - } - - private void _ViewLogPressed() - { - if (!buildTabsList.IsAnythingSelected()) - return; - - var selectedItems = buildTabsList.GetSelectedItems(); - - if (selectedItems.Length != 1) - throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}"); - - int selectedItem = selectedItems[0]; - - var buildTab = (BuildTab)buildTabs.GetTabControl(selectedItem); - - OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, BuildManager.MsBuildLogFileName)); - } - - public override void _Notification(int what) - { - base._Notification(what); - - if (what == EditorSettings.NotificationEditorSettingsChanged) - { - var editorBaseControl = editorInterface.GetBaseControl(); - panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles")); - panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles")); - panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles")); - } - } - - public void AddBuildTab(BuildTab buildTab) - { - buildTabs.AddChild(buildTab); - RaiseBuildTab(buildTab); - } - - public void RaiseBuildTab(BuildTab buildTab) - { - if (buildTab.GetParent() != buildTabs) - throw new InvalidOperationException("Build tab is not in the tabs list"); - - buildTabs.MoveChild(buildTab, 0); - _UpdateBuildTabsList(); - } - - public void ShowBuildTab() - { - for (int i = 0; i < panelTabs.GetTabCount(); i++) - { - if (panelTabs.GetTabControl(i) == panelBuildsTab) - { - panelTabs.CurrentTab = i; - GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this); - return; - } - } - - GD.PushError("Builds tab not found"); - } - - public override void _Ready() - { - base._Ready(); - - editorInterface = GodotSharpEditor.Instance.GetEditorInterface(); - - var editorBaseControl = editorInterface.GetBaseControl(); - - SizeFlagsVertical = (int)SizeFlags.ExpandFill; - SetAnchorsAndMarginsPreset(LayoutPreset.Wide); - - panelTabs = new TabContainer - { - TabAlign = TabContainer.TabAlignEnum.Left, - RectMinSize = new Vector2(0, 228) * EditorScale, - SizeFlagsVertical = (int)SizeFlags.ExpandFill - }; - panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles")); - panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles")); - panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles")); - AddChild(panelTabs); - - { - // Builds tab - panelBuildsTab = new VBoxContainer - { - Name = "Builds".TTR(), - SizeFlagsHorizontal = (int)SizeFlags.ExpandFill - }; - panelTabs.AddChild(panelBuildsTab); - - var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill}; - panelBuildsTab.AddChild(toolBarHBox); - - var buildProjectBtn = new Button - { - Text = "Build Project".TTR(), - FocusMode = FocusModeEnum.None - }; - buildProjectBtn.PressedSignal += BuildProjectPressed; - toolBarHBox.AddChild(buildProjectBtn); - - toolBarHBox.AddSpacer(begin: false); - - warningsBtn = new Button - { - Text = "Warnings".TTR(), - ToggleMode = true, - Pressed = true, - Visible = false, - FocusMode = FocusModeEnum.None - }; - warningsBtn.Toggled += _WarningsToggled; - toolBarHBox.AddChild(warningsBtn); - - errorsBtn = new Button - { - Text = "Errors".TTR(), - ToggleMode = true, - Pressed = true, - Visible = false, - FocusMode = FocusModeEnum.None - }; - errorsBtn.Toggled += _ErrorsToggled; - toolBarHBox.AddChild(errorsBtn); - - toolBarHBox.AddSpacer(begin: false); - - viewLogBtn = new Button - { - Text = "View log".TTR(), - FocusMode = FocusModeEnum.None, - Visible = false - }; - viewLogBtn.PressedSignal += _ViewLogPressed; - toolBarHBox.AddChild(viewLogBtn); - - var hsc = new HSplitContainer - { - SizeFlagsHorizontal = (int)SizeFlags.ExpandFill, - SizeFlagsVertical = (int)SizeFlags.ExpandFill - }; - panelBuildsTab.AddChild(hsc); - - buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill}; - buildTabsList.ItemSelected += _BuildTabsItemSelected; - buildTabsList.NothingSelected += _BuildTabsNothingSelected; - hsc.AddChild(buildTabsList); - - buildTabs = new TabContainer - { - TabAlign = TabContainer.TabAlignEnum.Left, - SizeFlagsHorizontal = (int)SizeFlags.ExpandFill, - TabsVisible = false - }; - hsc.AddChild(buildTabs); - } - } - } -} diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs index ab090c46e7..51055dc9b9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs @@ -4,7 +4,7 @@ using Godot.Collections; using GodotTools.Internals; using Path = System.IO.Path; -namespace GodotTools +namespace GodotTools.Build { [Serializable] public sealed class BuildInfo : Reference // TODO Remove Reference once we have proper serialization @@ -20,7 +20,9 @@ namespace GodotTools public override bool Equals(object obj) { if (obj is BuildInfo other) - return other.Solution == Solution && other.Configuration == Configuration; + return other.Solution == Solution && other.Targets == Targets && + other.Configuration == Configuration && other.Restore == Restore && + other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath; return false; } @@ -31,7 +33,11 @@ namespace GodotTools { int hash = 17; hash = hash * 29 + Solution.GetHashCode(); + hash = hash * 29 + Targets.GetHashCode(); hash = hash * 29 + Configuration.GetHashCode(); + hash = hash * 29 + Restore.GetHashCode(); + hash = hash * 29 + CustomProperties.GetHashCode(); + hash = hash * 29 + LogsDirPath.GetHashCode(); return hash; } } diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index ff7ce97c47..b96b0c8175 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -1,20 +1,18 @@ using System; -using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using GodotTools.Build; using GodotTools.Ides.Rider; using GodotTools.Internals; -using GodotTools.Utils; using JetBrains.Annotations; using static GodotTools.Internals.Globals; using File = GodotTools.Utils.File; +using OS = GodotTools.Utils.OS; -namespace GodotTools +namespace GodotTools.Build { public static class BuildManager { - private static readonly List<BuildInfo> BuildsInProgress = new List<BuildInfo>(); + private static BuildInfo _buildInProgress; public const string PropNameMSBuildMono = "MSBuild (Mono)"; public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)"; @@ -24,6 +22,14 @@ namespace GodotTools public const string MsBuildIssuesFileName = "msbuild_issues.csv"; public const string MsBuildLogFileName = "msbuild_log.txt"; + public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason); + + public static event BuildLaunchFailedEventHandler BuildLaunchFailed; + public static event Action<BuildInfo> BuildStarted; + public static event Action<BuildResult> BuildFinished; + public static event Action<string> StdOutputReceived; + public static event Action<string> StdErrorReceived; + private static void RemoveOldIssuesFile(BuildInfo buildInfo) { var issuesFile = GetIssuesFilePath(buildInfo); @@ -36,12 +42,13 @@ namespace GodotTools private static void ShowBuildErrorDialog(string message) { - GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error"); - GodotSharpEditor.Instance.BottomPanel.ShowBuildTab(); + var plugin = GodotSharpEditor.Instance; + plugin.ShowErrorDialog(message, "Build error"); + plugin.MakeBottomPanelItemVisible(plugin.MSBuildPanel); } - public static void RestartBuild(BuildTab buildTab) => throw new NotImplementedException(); - public static void StopBuild(BuildTab buildTab) => throw new NotImplementedException(); + public static void RestartBuild(BuildOutputView buildOutputView) => throw new NotImplementedException(); + public static void StopBuild(BuildOutputView buildOutputView) => throw new NotImplementedException(); private static string GetLogFilePath(BuildInfo buildInfo) { @@ -61,15 +68,14 @@ namespace GodotTools public static bool Build(BuildInfo buildInfo) { - if (BuildsInProgress.Contains(buildInfo)) + if (_buildInProgress != null) throw new InvalidOperationException("A build is already in progress"); - BuildsInProgress.Add(buildInfo); + _buildInProgress = buildInfo; try { - BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo); - buildTab.OnBuildStart(); + BuildStarted?.Invoke(buildInfo); // Required in order to update the build tasks list Internal.GodotMainIteration(); @@ -80,44 +86,44 @@ namespace GodotTools } catch (IOException e) { - buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); + BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); Console.Error.WriteLine(e); } try { - int exitCode = BuildSystem.Build(buildInfo); + int exitCode = BuildSystem.Build(buildInfo, StdOutputReceived, StdErrorReceived); if (exitCode != 0) PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}"); - buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error); + BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error); return exitCode == 0; } catch (Exception e) { - buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); + BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); Console.Error.WriteLine(e); return false; } } finally { - BuildsInProgress.Remove(buildInfo); + _buildInProgress = null; } } public static async Task<bool> BuildAsync(BuildInfo buildInfo) { - if (BuildsInProgress.Contains(buildInfo)) + if (_buildInProgress != null) throw new InvalidOperationException("A build is already in progress"); - BuildsInProgress.Add(buildInfo); + _buildInProgress = buildInfo; try { - BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo); + BuildStarted?.Invoke(buildInfo); try { @@ -125,43 +131,57 @@ namespace GodotTools } catch (IOException e) { - buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); + BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}"); Console.Error.WriteLine(e); } try { - int exitCode = await BuildSystem.BuildAsync(buildInfo); + int exitCode = await BuildSystem.BuildAsync(buildInfo, StdOutputReceived, StdErrorReceived); if (exitCode != 0) PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}"); - buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error); + BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error); return exitCode == 0; } catch (Exception e) { - buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); + BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}"); Console.Error.WriteLine(e); return false; } } finally { - BuildsInProgress.Remove(buildInfo); + _buildInProgress = null; } } - public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null) + public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null) { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true); + + // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it. + if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform)) + buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}"); + + if (Internal.GodotIsRealTDouble()) + buildInfo.CustomProperties.Add("GodotRealTIsDouble=true"); + + return BuildProjectBlocking(buildInfo); + } + + private static bool BuildProjectBlocking(BuildInfo buildInfo) + { + if (!File.Exists(buildInfo.Solution)) return true; // No solution to build // Make sure the API assemblies are up to date before building the project. // We may not have had the chance to update the release API assemblies, and the debug ones // may have been deleted by the user at some point after they were loaded by the Godot editor. - string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug"); + string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug"); if (!string.IsNullOrEmpty(apiAssembliesUpdateError)) { @@ -173,15 +193,6 @@ namespace GodotTools { pr.Step("Building project solution", 0); - var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true); - - // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it. - if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform)) - buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}"); - - if (Internal.GodotIsRealTDouble()) - buildInfo.CustomProperties.Add("GodotRealTIsDouble=true"); - if (!Build(buildInfo)) { ShowBuildErrorDialog("Failed to build project solution"); @@ -197,18 +208,51 @@ namespace GodotTools if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) return true; // No solution to build + try + { + // Make sure our packages are added to the fallback folder + NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath); + } + catch (Exception e) + { + Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message); + } + + GenerateEditorScriptMetadata(); + + if (GodotSharpEditor.Instance.SkipBuildBeforePlaying) + return true; // Requested play from an external editor/IDE which already built the project + + return BuildProjectBlocking("Debug"); + } + + // NOTE: This will be replaced with C# source generators in 4.0 + public static void GenerateEditorScriptMetadata() + { string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor"); string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player"); CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath); - if (File.Exists(editorScriptsMetadataPath)) - File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); + if (!File.Exists(editorScriptsMetadataPath)) + return; - if (GodotSharpEditor.Instance.SkipBuildBeforePlaying) - return true; // Requested play from an external editor/IDE which already built the project + try + { + File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath); + } + catch (IOException e) + { + throw new IOException("Failed to copy scripts metadata file.", innerException: e); + } + } - return BuildProjectBlocking("Debug"); + // NOTE: This will be replaced with C# source generators in 4.0 + public static string GenerateExportedGameScriptMetadata(bool isDebug) + { + string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}"); + CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath); + return scriptsMetadataPath; } public static void Initialize() @@ -254,8 +298,6 @@ namespace GodotTools ["hint"] = Godot.PropertyHint.Enum, ["hint_string"] = hintString }); - - EditorDef("mono/builds/print_build_output", false); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs index 8596cd24af..9514cc9622 100644 --- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs @@ -5,16 +5,10 @@ using GodotTools.Internals; using File = GodotTools.Utils.File; using Path = System.IO.Path; -namespace GodotTools +namespace GodotTools.Build { - public class BuildTab : VBoxContainer + public class BuildOutputView : VBoxContainer, ISerializationListener { - public enum BuildResults - { - Error, - Success - } - [Serializable] private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization { @@ -29,10 +23,14 @@ namespace GodotTools private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization private ItemList issuesList; + private TextEdit buildLog; + private PopupMenu issuesListContextMenu; - public bool BuildExited { get; private set; } = false; + [Signal] public event Action BuildStateChanged; - public BuildResults? BuildResult { get; private set; } = null; + public bool HasBuildExited { get; private set; } = false; + + public BuildResult? BuildResult { get; private set; } = null; public int ErrorCount { get; private set; } = 0; @@ -41,23 +39,31 @@ namespace GodotTools public bool ErrorsVisible { get; set; } = true; public bool WarningsVisible { get; set; } = true; - public Texture2D IconTexture + public Texture2D BuildStateIcon { get { - if (!BuildExited) + if (!HasBuildExited) return GetThemeIcon("Stop", "EditorIcons"); - if (BuildResult == BuildResults.Error) - return GetThemeIcon("StatusError", "EditorIcons"); + if (BuildResult == Build.BuildResult.Error) + return GetThemeIcon("Error", "EditorIcons"); + + if (WarningCount > 1) + return GetThemeIcon("Warning", "EditorIcons"); - return GetThemeIcon("StatusSuccess", "EditorIcons"); + return null; } } - public BuildInfo BuildInfo { get; private set; } + private BuildInfo BuildInfo { get; set; } - private void _LoadIssuesFromFile(string csvFile) + public bool LogVisible + { + set => buildLog.Visible = value; + } + + private void LoadIssuesFromFile(string csvFile) { using (var file = new Godot.File()) { @@ -107,7 +113,7 @@ namespace GodotTools } } - private void _IssueActivated(int idx) + private void IssueActivated(int idx) { if (idx < 0 || idx >= issuesList.GetItemCount()) throw new IndexOutOfRangeException("Item list index out of range"); @@ -190,49 +196,79 @@ namespace GodotTools } } - public void OnBuildStart() + private void BuildLaunchFailed(BuildInfo buildInfo, string cause) + { + HasBuildExited = true; + BuildResult = Build.BuildResult.Error; + + issuesList.Clear(); + + var issue = new BuildIssue {Message = cause, Warning = false}; + + ErrorCount += 1; + issues.Add(issue); + + UpdateIssuesList(); + + EmitSignal(nameof(BuildStateChanged)); + } + + private void BuildStarted(BuildInfo buildInfo) { - BuildExited = false; + BuildInfo = buildInfo; + HasBuildExited = false; issues.Clear(); WarningCount = 0; ErrorCount = 0; + buildLog.Text = string.Empty; + UpdateIssuesList(); - GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this); + EmitSignal(nameof(BuildStateChanged)); } - public void OnBuildExit(BuildResults result) + private void BuildFinished(BuildResult result) { - BuildExited = true; + HasBuildExited = true; BuildResult = result; - _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName)); + LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName)); + UpdateIssuesList(); - GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this); + EmitSignal(nameof(BuildStateChanged)); } - public void OnBuildExecFailed(string cause) + private void StdOutputReceived(string text) { - BuildExited = true; - BuildResult = BuildResults.Error; - - issuesList.Clear(); + buildLog.Text += text + "\n"; + ScrollToLastNonEmptyLogLine(); + } - var issue = new BuildIssue { Message = cause, Warning = false }; + private void StdErrorReceived(string text) + { + buildLog.Text += text + "\n"; + ScrollToLastNonEmptyLogLine(); + } - ErrorCount += 1; - issues.Add(issue); + private void ScrollToLastNonEmptyLogLine() + { + int line; + for (line = buildLog.GetLineCount(); line > 0; line--) + { + string lineText = buildLog.GetLine(line); - UpdateIssuesList(); + if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim())) + break; + } - GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this); + buildLog.CursorSetLine(line); } public void RestartBuild() { - if (!BuildExited) + if (!HasBuildExited) throw new InvalidOperationException("Build already started"); BuildManager.RestartBuild(this); @@ -240,28 +276,118 @@ namespace GodotTools public void StopBuild() { - if (!BuildExited) + if (!HasBuildExited) throw new InvalidOperationException("Build is not in progress"); BuildManager.StopBuild(this); } + private enum IssuesContextMenuOption + { + Copy + } + + private void IssuesListContextOptionPressed(int id) + { + switch ((IssuesContextMenuOption)id) + { + case IssuesContextMenuOption.Copy: + { + // We don't allow multi-selection but just in case that changes later... + string text = null; + + foreach (int issueIndex in issuesList.GetSelectedItems()) + { + if (text != null) + text += "\n"; + text += issuesList.GetItemText(issueIndex); + } + + if (text != null) + DisplayServer.ClipboardSet(text); + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid issue context menu option"); + } + } + + private void IssuesListRmbSelected(int index, Vector2 atPosition) + { + _ = index; // Unused + + issuesListContextMenu.Clear(); + issuesListContextMenu.Size = new Vector2i(1, 1); + + if (issuesList.IsAnythingSelected()) + { + // Add menu entries for the selected item + issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"), + label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy); + } + + if (issuesListContextMenu.GetItemCount() > 0) + { + issuesListContextMenu.Position = (Vector2i)(issuesList.RectGlobalPosition + atPosition); + issuesListContextMenu.Popup(); + } + } + public override void _Ready() { base._Ready(); - issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill }; - issuesList.ItemActivated += _IssueActivated; - AddChild(issuesList); + SizeFlagsVertical = (int)SizeFlags.ExpandFill; + + var hsc = new HSplitContainer + { + SizeFlagsHorizontal = (int)SizeFlags.ExpandFill, + SizeFlagsVertical = (int)SizeFlags.ExpandFill + }; + AddChild(hsc); + + issuesList = new ItemList + { + SizeFlagsVertical = (int)SizeFlags.ExpandFill, + SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log + }; + issuesList.ItemActivated += IssueActivated; + issuesList.AllowRmbSelect = true; + issuesList.ItemRmbSelected += IssuesListRmbSelected; + hsc.AddChild(issuesList); + + issuesListContextMenu = new PopupMenu(); + issuesListContextMenu.IdPressed += IssuesListContextOptionPressed; + issuesList.AddChild(issuesListContextMenu); + + buildLog = new TextEdit + { + Readonly = true, + SizeFlagsVertical = (int)SizeFlags.ExpandFill, + SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list + }; + hsc.AddChild(buildLog); + + AddBuildEventListeners(); } - private BuildTab() + private void AddBuildEventListeners() { + BuildManager.BuildLaunchFailed += BuildLaunchFailed; + BuildManager.BuildStarted += BuildStarted; + BuildManager.BuildFinished += BuildFinished; + // StdOutput/Error can be received from different threads, so we need to use CallDeferred + BuildManager.StdOutputReceived += line => CallDeferred(nameof(StdOutputReceived), line); + BuildManager.StdErrorReceived += line => CallDeferred(nameof(StdErrorReceived), line); } - public BuildTab(BuildInfo buildInfo) + public void OnBeforeSerialize() { - BuildInfo = buildInfo; + } + + public void OnAfterDeserialize() + { + AddBuildEventListeners(); // Re-add them } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs new file mode 100644 index 0000000000..59055b60c3 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs @@ -0,0 +1,8 @@ +namespace GodotTools.Build +{ + public enum BuildResult + { + Error, + Success + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs index d9862ae361..bac7a2e6db 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs @@ -44,10 +44,7 @@ namespace GodotTools.Build } } - private static bool PrintBuildOutput => - (bool)EditorSettings.GetSetting("mono/builds/print_build_output"); - - private static Process LaunchBuild(BuildInfo buildInfo) + private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler) { (string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild(); @@ -58,13 +55,13 @@ namespace GodotTools.Build var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs); - bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput; - - if (!redirectOutput || Godot.OS.IsStdoutVerbose()) - Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}"); + string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}"; + stdOutHandler?.Invoke(launchMessage); + if (Godot.OS.IsStdoutVerbose()) + Console.WriteLine(launchMessage); - startInfo.RedirectStandardOutput = redirectOutput; - startInfo.RedirectStandardError = redirectOutput; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; startInfo.UseShellExecute = false; if (UsingMonoMsBuildOnWindows) @@ -82,20 +79,22 @@ namespace GodotTools.Build var process = new Process {StartInfo = startInfo}; + if (stdOutHandler != null) + process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data); + if (stdErrHandler != null) + process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data); + process.Start(); - if (redirectOutput) - { - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); return process; } - public static int Build(BuildInfo buildInfo) + public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler) { - using (var process = LaunchBuild(buildInfo)) + using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler)) { process.WaitForExit(); @@ -103,9 +102,9 @@ namespace GodotTools.Build } } - public static async Task<int> BuildAsync(BuildInfo buildInfo) + public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler) { - using (var process = LaunchBuild(buildInfo)) + using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler)) { await process.WaitForExitAsync(); @@ -152,10 +151,5 @@ namespace GodotTools.Build foreach (string env in platformEnvironmentVariables) environmentVariables.Remove(env); } - - private static bool IsDebugMsBuildRequested() - { - return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1"; - } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs new file mode 100644 index 0000000000..708ec73454 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -0,0 +1,185 @@ +using System; +using Godot; +using GodotTools.Internals; +using JetBrains.Annotations; +using static GodotTools.Internals.Globals; +using File = GodotTools.Utils.File; + +namespace GodotTools.Build +{ + public class MSBuildPanel : VBoxContainer + { + public BuildOutputView BuildOutputView { get; private set; } + + private Button errorsBtn; + private Button warningsBtn; + private Button viewLogBtn; + + private void WarningsToggled(bool pressed) + { + BuildOutputView.WarningsVisible = pressed; + BuildOutputView.UpdateIssuesList(); + } + + private void ErrorsToggled(bool pressed) + { + BuildOutputView.ErrorsVisible = pressed; + BuildOutputView.UpdateIssuesList(); + } + + [UsedImplicitly] + public void BuildSolution() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return; // No solution to build + + try + { + // Make sure our packages are added to the fallback folder + NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath); + } + catch (Exception e) + { + GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message); + } + + BuildManager.GenerateEditorScriptMetadata(); + + if (!BuildManager.BuildProjectBlocking("Debug")) + return; // Build failed + + // Notify running game for hot-reload + Internal.EditorDebuggerNodeReloadScripts(); + + // Hot-reload in the editor + GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); + + if (Internal.IsAssembliesReloadingNeeded()) + Internal.ReloadAssemblies(softReload: false); + } + + [UsedImplicitly] + private void RebuildSolution() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return; // No solution to build + + try + { + // Make sure our packages are added to the fallback folder + NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath); + } + catch (Exception e) + { + GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message); + } + + BuildManager.GenerateEditorScriptMetadata(); + + if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"})) + return; // Build failed + + // Notify running game for hot-reload + Internal.EditorDebuggerNodeReloadScripts(); + + // Hot-reload in the editor + GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); + + if (Internal.IsAssembliesReloadingNeeded()) + Internal.ReloadAssemblies(softReload: false); + } + + [UsedImplicitly] + private void CleanSolution() + { + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + return; // No solution to build + + BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Clean"}); + } + + private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed; + + private void BuildMenuOptionPressed(int id) + { + switch ((BuildMenuOptions)id) + { + case BuildMenuOptions.BuildSolution: + BuildSolution(); + break; + case BuildMenuOptions.RebuildSolution: + RebuildSolution(); + break; + case BuildMenuOptions.CleanSolution: + CleanSolution(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option"); + } + } + + private enum BuildMenuOptions + { + BuildSolution, + RebuildSolution, + CleanSolution + } + + public override void _Ready() + { + base._Ready(); + + RectMinSize = new Vector2(0, 228) * EditorScale; + SizeFlagsVertical = (int)SizeFlags.ExpandFill; + + var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill}; + AddChild(toolBarHBox); + + var buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")}; + toolBarHBox.AddChild(buildMenuBtn); + + var buildMenu = buildMenuBtn.GetPopup(); + buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution); + buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution); + buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution); + buildMenu.IdPressed += BuildMenuOptionPressed; + + errorsBtn = new Button + { + HintTooltip = "Show Errors".TTR(), + Icon = GetThemeIcon("StatusError", "EditorIcons"), + ExpandIcon = false, + ToggleMode = true, + Pressed = true, + FocusMode = FocusModeEnum.None + }; + errorsBtn.Toggled += ErrorsToggled; + toolBarHBox.AddChild(errorsBtn); + + warningsBtn = new Button + { + HintTooltip = "Show Warnings".TTR(), + Icon = GetThemeIcon("NodeWarning", "EditorIcons"), + ExpandIcon = false, + ToggleMode = true, + Pressed = true, + FocusMode = FocusModeEnum.None + }; + warningsBtn.Toggled += WarningsToggled; + toolBarHBox.AddChild(warningsBtn); + + viewLogBtn = new Button + { + Text = "Show Output".TTR(), + ToggleMode = true, + Pressed = true, + FocusMode = FocusModeEnum.None + }; + viewLogBtn.Toggled += ViewLogToggled; + toolBarHBox.AddChild(viewLogBtn); + + BuildOutputView = new BuildOutputView(); + AddChild(BuildOutputView); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs index 7bfba779fb..e2feb66e35 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs @@ -31,7 +31,7 @@ namespace GodotTools.Build string dotnetCliPath = OS.PathWhich("dotnet"); if (!string.IsNullOrEmpty(dotnetCliPath)) return (dotnetCliPath, BuildTool.DotnetCli); - GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Visual Studio."); + GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio."); goto case BuildTool.MsBuildVs; } case BuildTool.MsBuildVs: @@ -89,7 +89,7 @@ namespace GodotTools.Build string dotnetCliPath = OS.PathWhich("dotnet"); if (!string.IsNullOrEmpty(dotnetCliPath)) return (dotnetCliPath, BuildTool.DotnetCli); - GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Mono."); + GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono."); goto case BuildTool.MsBuildMono; } case BuildTool.MsBuildMono: @@ -119,7 +119,7 @@ namespace GodotTools.Build { var result = new List<string>(); - if (OS.IsOSX) + if (OS.IsMacOS) { result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/"); result.Add("/usr/local/var/homebrew/linked/mono/bin/"); @@ -161,7 +161,7 @@ namespace GodotTools.Build // Try to find 15.0 with vswhere - var envNames = Internal.GodotIs32Bits() ? new[] { "ProgramFiles", "ProgramW6432" } : new[] { "ProgramFiles(x86)", "ProgramFiles" }; + var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"}; string vsWherePath = null; foreach (var envName in envNames) diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs new file mode 100644 index 0000000000..793ef7fd71 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs @@ -0,0 +1,296 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using Godot; +using GodotTools.Internals; +using GodotTools.Shared; +using Directory = GodotTools.Utils.Directory; +using Environment = System.Environment; +using File = GodotTools.Utils.File; + +namespace GodotTools.Build +{ + public static class NuGetUtils + { + public const string GodotFallbackFolderName = "Godot Offline Packages"; + + public static string GodotFallbackFolderPath + => Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder"); + + private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path) + { + var xmlDoc = new XmlDocument(); + xmlDoc.Load(nuGetConfigPath); + + const string nuGetConfigRootName = "configuration"; + + var rootNode = xmlDoc.DocumentElement; + + if (rootNode == null) + { + // No root node, create it + rootNode = xmlDoc.CreateElement(nuGetConfigRootName); + xmlDoc.AppendChild(rootNode); + + // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well + XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add"); + nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org"; + nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json"; + nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3"; + rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry); + } + else + { + // Check that the root node is the expected one + if (rootNode.Name != nuGetConfigRootName) + throw new Exception("Invalid root Xml node for NuGet.Config. " + + $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'."); + } + + var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ?? + rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders")); + + // Check if it already has our fallback package folder + for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling) + { + if (xmlNode.NodeType != XmlNodeType.Element) + continue; + + var xmlElement = (XmlElement)xmlNode; + if (xmlElement.Name == "add" && + xmlElement.Attributes["key"]?.Value == name && + xmlElement.Attributes["value"]?.Value == path) + { + return; + } + } + + XmlElement newEntry = xmlDoc.CreateElement("add"); + newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name; + newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path; + + fallbackFoldersNode.AppendChild(newEntry); + + xmlDoc.Save(nuGetConfigPath); + } + + /// <summary> + /// Returns all the paths where the user NuGet.Config files can be found. + /// Does not determine whether the returned files exist or not. + /// </summary> + private static string[] GetAllUserNuGetConfigFilePaths() + { + // Where to find 'NuGet/NuGet.Config': + // + // - Mono/.NETFramework (standalone NuGet): + // Uses Environment.SpecialFolder.ApplicationData + // - Windows: '%APPDATA%' + // - Linux/macOS: '$HOME/.config' + // - CoreCLR (dotnet CLI NuGet): + // - Windows: '%APPDATA%' + // - Linux/macOS: '$DOTNET_CLI_HOME/.nuget' otherwise '$HOME/.nuget' + + string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + + if (Utils.OS.IsWindows) + { + // %APPDATA% for both + return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")}; + } + + var paths = new string[2]; + + // CoreCLR (dotnet CLI NuGet) + + string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME"); + if (!string.IsNullOrEmpty(dotnetCliHome)) + { + paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config"); + } + else + { + string home = Environment.GetEnvironmentVariable("HOME"); + if (string.IsNullOrEmpty(home)) + throw new InvalidOperationException("Required environment variable 'HOME' is not set."); + paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config"); + } + + // Mono/.NETFramework (standalone NuGet) + + // ApplicationData is $HOME/.config on Linux/macOS + paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config"); + + return paths; + } + + // nupkg extraction + // + // Exclude: (NuGet.Client -> NuGet.Packaging.PackageHelper.ExcludePaths) + // package/ + // _rels/ + // [Content_Types].xml + // + // Don't ignore files that begin with a dot (.) + // + // The nuspec is not lower case inside the nupkg but must be made lower case when extracted. + + /// <summary> + /// Adds the specified fallback folder to the user NuGet.Config files, + /// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet. + /// </summary> + public static void AddFallbackFolderToUserNuGetConfigs(string name, string path) + { + foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths()) + { + if (!System.IO.File.Exists(nuGetConfigPath)) + { + // It doesn't exist, so we create a default one + const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?> +<configuration> + <packageSources> + <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" /> + </packageSources> +</configuration> +"; + System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM + } + + AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path); + } + } + + private static void AddPackageToFallbackFolder(string fallbackFolder, + string nupkgPath, string packageId, string packageVersion) + { + // dotnet CLI provides no command for this, but we can do it manually. + // + // - The expected structure is as follows: + // fallback_folder/ + // <package.name>/<version>/ + // <package.name>.<version>.nupkg + // <package.name>.<version>.nupkg.sha512 + // <package.name>.nuspec + // ... extracted nupkg files (check code for excluded files) ... + // + // - <package.name> and <version> must be in lower case. + // - The sha512 of the nupkg is base64 encoded. + // - We can get the nuspec from the nupkg which is a Zip file. + + string packageIdLower = packageId.ToLower(); + string packageVersionLower = packageVersion.ToLower(); + + string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower); + string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg"); + string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512"); + + if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath)) + return; // Already added (for speed we don't check if every file is properly extracted) + + Directory.CreateDirectory(destDir); + + // Generate .nupkg.sha512 file + + using (var alg = SHA512.Create()) + { + alg.ComputeHash(File.ReadAllBytes(nupkgPath)); + string base64Hash = Convert.ToBase64String(alg.Hash); + File.WriteAllText(nupkgSha512DestPath, base64Hash); + } + + // Extract nupkg + ExtractNupkg(destDir, nupkgPath, packageId, packageVersion); + + // Copy .nupkg + File.Copy(nupkgPath, nupkgDestPath); + } + + private static readonly string[] NupkgExcludePaths = + { + "_rels/", + "package/", + "[Content_Types].xml" + }; + + private static void ExtractNupkg(string destDir, string nupkgPath, string packageId, string packageVersion) + { + // NOTE: Must use SimplifyGodotPath to make sure we don't extract files outside the destination directory. + + using (var archive = ZipFile.OpenRead(nupkgPath)) + { + // Extract .nuspec manually as it needs to be in lower case + + var nuspecEntry = archive.GetEntry(packageId + ".nuspec"); + + if (nuspecEntry == null) + throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file."); + + nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath())); + + // Extract the other package files + + foreach (var entry in archive.Entries) + { + // NOTE: SimplifyGodotPath() removes trailing slash and backslash, + // so we can't use the result to check if the entry is a directory. + + string entryFullName = entry.FullName.Replace('\\', '/'); + + // Check if the file must be ignored + if ( // Excluded files. + NupkgExcludePaths.Any(e => entryFullName.StartsWith(e, StringComparison.OrdinalIgnoreCase)) || + // Nupkg hash files and nupkg metadata files on all directory. + entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) || + entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) || + // Nuspec at root level. We already extracted it previously but in lower case. + entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec")) + { + continue; + } + + string entryFullNameSimplified = entryFullName.SimplifyGodotPath(); + string destFilePath = Path.Combine(destDir, entryFullNameSimplified); + bool isDir = entryFullName.EndsWith("/"); + + if (isDir) + { + Directory.CreateDirectory(destFilePath); + } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(destFilePath)); + entry.ExtractToFile(destFilePath, overwrite: true); + } + } + } + } + + /// <summary> + /// Copies and extracts all the Godot bundled packages to the Godot NuGet fallback folder. + /// Does nothing if the packages were already copied. + /// </summary> + public static void AddBundledPackagesToFallbackFolder(string fallbackFolder) + { + GD.Print("Copying Godot Offline Packages..."); + + string nupkgsLocation = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "nupkgs"); + + void AddPackage(string packageId, string packageVersion) + { + string nupkgPath = Path.Combine(nupkgsLocation, $"{packageId}.{packageVersion}.nupkg"); + AddPackageToFallbackFolder(fallbackFolder, nupkgPath, packageId, packageVersion); + } + + foreach (var (packageId, packageVersion) in PackagesToAdd) + AddPackage(packageId, packageVersion); + } + + private static readonly (string packageId, string packageVersion)[] PackagesToAdd = + { + ("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk) + }; + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs index a8afb38728..1d800b8151 100644 --- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs +++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs @@ -48,7 +48,7 @@ namespace GodotTools var firstMatch = classes.FirstOrDefault(classDecl => classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object. - classDecl.SearchName != searchName // Filter by the name we're looking for + classDecl.SearchName == searchName // Filter by the name we're looking for ); if (firstMatch == null) diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index f60e469503..5bb8d444c2 100755 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -120,7 +120,7 @@ namespace GodotTools.Export string assemblyPath = assembly.Value; string outputFileExtension = platform == OS.Platforms.Windows ? ".dll" : - platform == OS.Platforms.OSX ? ".dylib" : + platform == OS.Platforms.MacOS ? ".dylib" : ".so"; string outputFileName = assemblyName + ".dll" + outputFileExtension; @@ -132,7 +132,7 @@ namespace GodotTools.Export ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir); - if (platform == OS.Platforms.OSX) + if (platform == OS.Platforms.MacOS) { exporter.AddSharedObject(tempOutputFilePath, tags: null); } @@ -328,7 +328,7 @@ MONO_AOT_MODE_LAST = 1000, if (lipoExitCode != 0) throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}"); - // TODO: Add the AOT lib and interpreter libs as device only to supress warnings when targeting the simulator + // TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator // Add the fat AOT static library to the Xcode project exporter.AddIosProjectStaticLib(fatOutputFilePath); @@ -581,7 +581,7 @@ MONO_AOT_MODE_LAST = 1000, string arch = bits == "64" ? "x86_64" : "i686"; return $"windows-{arch}"; } - case OS.Platforms.OSX: + case OS.Platforms.MacOS: { Debug.Assert(bits == null || bits == "64"); string arch = "x86_64"; diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 599ca94699..e18ed7f107 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using GodotTools.Build; using GodotTools.Core; using GodotTools.Internals; using JetBrains.Annotations; @@ -143,6 +144,8 @@ namespace GodotTools.Export private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags) { + _ = flags; // Unused + if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) return; @@ -154,12 +157,10 @@ namespace GodotTools.Export string buildConfig = isDebug ? "ExportDebug" : "ExportRelease"; - string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}"); - CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath); - + string scriptsMetadataPath = BuildManager.GenerateExportedGameScriptMetadata(isDebug); AddFile(scriptsMetadataPath, scriptsMetadataPath); - if (!BuildManager.BuildProjectBlocking(buildConfig, platform)) + if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform)) throw new Exception("Failed to build project"); // Add dependency assemblies @@ -340,7 +341,7 @@ namespace GodotTools.Export private static bool PlatformHasTemplateDir(string platform) { // OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest. - return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform); + return !new[] {OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform); } private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform) @@ -411,7 +412,7 @@ namespace GodotTools.Export case OS.Platforms.Windows: case OS.Platforms.UWP: return "net_4_x_win"; - case OS.Platforms.OSX: + case OS.Platforms.MacOS: case OS.Platforms.LinuxBSD: case OS.Platforms.Server: case OS.Platforms.Haiku: diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 3148458d7e..58561c5097 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -4,9 +4,9 @@ using GodotTools.Export; using GodotTools.Utils; using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using GodotTools.Build; using GodotTools.Ides; using GodotTools.Ides.Rider; using GodotTools.Internals; @@ -19,7 +19,6 @@ using Path = System.IO.Path; namespace GodotTools { - [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] public class GodotSharpEditor : EditorPlugin, ISerializationListener { private EditorSettings editorSettings; @@ -31,12 +30,13 @@ namespace GodotTools private CheckBox aboutDialogCheckBox; private Button bottomPanelBtn; + private Button toolBarBuildButton; public GodotIdeManager GodotIdeManager { get; private set; } private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization - public BottomPanel BottomPanel { get; private set; } + public MSBuildPanel MSBuildPanel { get; private set; } public bool SkipBuildBeforePlaying { get; set; } = false; @@ -127,6 +127,7 @@ namespace GodotTools { menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln)); bottomPanelBtn.Show(); + toolBarBuildButton.Show(); } private void _ShowAboutDialog() @@ -146,12 +147,27 @@ namespace GodotTools case MenuOptions.AboutCSharp: _ShowAboutDialog(); break; + case MenuOptions.SetupGodotNugetFallbackFolder: + { + try + { + string fallbackFolder = NuGetUtils.GodotFallbackFolderPath; + NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder); + NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder); + } + catch (Exception e) + { + ShowErrorDialog("Failed to setup Godot NuGet Offline Packages: " + e.Message); + } + + break; + } default: throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option"); } } - private void _BuildSolutionPressed() + private void BuildSolutionPressed() { if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) { @@ -159,23 +175,22 @@ namespace GodotTools return; // Failed to create solution } - Instance.BottomPanel.BuildProjectPressed(); + Instance.MSBuildPanel.BuildSolution(); } - public override void _Notification(int what) + public override void _Ready() { - base._Notification(what); + base._Ready(); - if (what == NotificationReady) + MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged; + + bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start"); + if (showInfoDialog) { - bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start"); - if (showInfoDialog) - { - aboutDialog.Exclusive = true; - _ShowAboutDialog(); - // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on. - aboutDialog.Exclusive = false; - } + aboutDialog.Exclusive = true; + _ShowAboutDialog(); + // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on. + aboutDialog.Exclusive = false; } } @@ -183,6 +198,7 @@ namespace GodotTools { CreateSln, AboutCSharp, + SetupGodotNugetFallbackFolder, } public void ShowErrorDialog(string message, string title = "Error") @@ -270,7 +286,7 @@ namespace GodotTools bool osxAppBundleInstalled = false; - if (OS.IsOSX) + if (OS.IsMacOS) { // The package path is '/Applications/Visual Studio Code.app' const string vscodeBundleId = "com.microsoft.VSCode"; @@ -310,7 +326,7 @@ namespace GodotTools string command; - if (OS.IsOSX) + if (OS.IsMacOS) { if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath)) { @@ -391,6 +407,12 @@ namespace GodotTools } } + private void BuildStateChanged() + { + if (bottomPanelBtn != null) + bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon; + } + public override void EnablePlugin() { base.EnablePlugin(); @@ -407,20 +429,20 @@ namespace GodotTools errorDialog = new AcceptDialog(); editorBaseControl.AddChild(errorDialog); - BottomPanel = new BottomPanel(); - - bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR()); + MSBuildPanel = new MSBuildPanel(); + bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR()); AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"}); menuPopup = new PopupMenu(); menuPopup.Hide(); - AddToolSubmenuItem("Mono", menuPopup); + AddToolSubmenuItem("C#", menuPopup); // TODO: Remove or edit this info dialog once Mono support is no longer in alpha { menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp); + menuPopup.AddItem("Setup Godot NuGet Offline Packages".TTR(), (int)MenuOptions.SetupGodotNugetFallbackFolder); aboutDialog = new AcceptDialog(); editorBaseControl.AddChild(aboutDialog); aboutDialog.Title = "Important: C# support is not feature-complete"; @@ -468,6 +490,15 @@ namespace GodotTools aboutVBox.AddChild(aboutDialogCheckBox); } + toolBarBuildButton = new Button + { + Text = "Build", + HintTooltip = "Build solution", + FocusMode = Control.FocusModeEnum.None + }; + toolBarBuildButton.PressedSignal += BuildSolutionPressed; + AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton); + if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath)) { ApplyNecessaryChangesToSolution(); @@ -475,20 +506,12 @@ namespace GodotTools else { bottomPanelBtn.Hide(); + toolBarBuildButton.Hide(); menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln); } menuPopup.IdPressed += _MenuOptionPressed; - var buildButton = new Button - { - Text = "Build", - HintTooltip = "Build solution", - FocusMode = Control.FocusModeEnum.None - }; - buildButton.PressedSignal += _BuildSolutionPressed; - AddControlToContainer(CustomControlContainer.Toolbar, buildButton); - // External editor settings EditorDef("mono/editor/external_editor", ExternalEditorId.None); @@ -501,7 +524,7 @@ namespace GodotTools $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" + $",JetBrains Rider:{(int)ExternalEditorId.Rider}"; } - else if (OS.IsOSX) + else if (OS.IsMacOS) { settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" + $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + @@ -529,6 +552,16 @@ namespace GodotTools exportPlugin.RegisterExportSettings(); exportPluginWeak = WeakRef(exportPlugin); + try + { + // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included + NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath); + } + catch (Exception e) + { + GD.PushError("Failed to add Godot NuGet Offline Packages to NuGet.Config: " + e.Message); + } + BuildManager.Initialize(); RiderPathManager.Initialize(); @@ -567,6 +600,7 @@ namespace GodotTools public static GodotSharpEditor Instance { get; private set; } + [UsedImplicitly] private GodotSharpEditor() { } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs index e4932ca217..451ce39f5c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs @@ -111,7 +111,7 @@ namespace GodotTools.Ides { MonoDevelop.Instance GetMonoDevelopInstance(string solutionPath) { - if (Utils.OS.IsOSX && editorId == ExternalEditorId.VisualStudioForMac) + if (Utils.OS.IsMacOS && editorId == ExternalEditorId.VisualStudioForMac) { vsForMacInstance = (vsForMacInstance?.IsDisposed ?? true ? null : vsForMacInstance) ?? new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac); diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs index d6fa2eeba7..fd7bbd5578 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs @@ -26,7 +26,7 @@ namespace GodotTools.Ides.MonoDevelop string command; - if (OS.IsOSX) + if (OS.IsMacOS) { string bundleId = BundleIds[editorId]; @@ -85,7 +85,7 @@ namespace GodotTools.Ides.MonoDevelop public Instance(string solutionFile, EditorId editorId) { - if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX) + if (editorId == EditorId.VisualStudioForMac && !OS.IsMacOS) throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform"); this.solutionFile = solutionFile; @@ -103,7 +103,7 @@ namespace GodotTools.Ides.MonoDevelop static Instance() { - if (OS.IsOSX) + if (OS.IsMacOS) { ExecutableNames = new Dictionary<EditorId, string> { diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs index e22e9af919..94fc5da425 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs @@ -32,7 +32,7 @@ namespace GodotTools.Ides.Rider { return CollectRiderInfosWindows(); } - if (OS.IsOSX) + if (OS.IsMacOS) { return CollectRiderInfosMac(); } @@ -138,7 +138,7 @@ namespace GodotTools.Ides.Rider return GetToolboxRiderRootPath(localAppData); } - if (OS.IsOSX) + if (OS.IsMacOS) { var home = Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(home)) @@ -211,7 +211,7 @@ namespace GodotTools.Ides.Rider { if (OS.IsWindows || OS.IsUnixLike) return "../../build.txt"; - if (OS.IsOSX) + if (OS.IsMacOS) return "Contents/Resources/build.txt"; throw new Exception("Unknown OS."); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index 6c05891f2c..e745966435 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -21,7 +21,7 @@ namespace GodotTools.Utils public static class Names { public const string Windows = "Windows"; - public const string OSX = "OSX"; + public const string MacOS = "macOS"; public const string Linux = "Linux"; public const string FreeBSD = "FreeBSD"; public const string NetBSD = "NetBSD"; @@ -37,7 +37,7 @@ namespace GodotTools.Utils public static class Platforms { public const string Windows = "windows"; - public const string OSX = "osx"; + public const string MacOS = "osx"; public const string LinuxBSD = "linuxbsd"; public const string Server = "server"; public const string UWP = "uwp"; @@ -50,7 +50,7 @@ namespace GodotTools.Utils public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string> { [Names.Windows] = Platforms.Windows, - [Names.OSX] = Platforms.OSX, + [Names.MacOS] = Platforms.MacOS, [Names.Linux] = Platforms.LinuxBSD, [Names.FreeBSD] = Platforms.LinuxBSD, [Names.NetBSD] = Platforms.LinuxBSD, @@ -77,11 +77,11 @@ namespace GodotTools.Utils new[] {Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD}; private static readonly IEnumerable<string> UnixLikePlatforms = - new[] {Names.OSX, Names.Server, Names.Haiku, Names.Android, Names.iOS} + new[] {Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS} .Concat(LinuxBSDPlatforms).ToArray(); private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows)); - private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX)); + private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS)); private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms)); private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server)); private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP)); @@ -92,7 +92,7 @@ namespace GodotTools.Utils private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms)); public static bool IsWindows => _isWindows.Value || IsUWP; - public static bool IsOSX => _isOSX.Value; + public static bool IsMacOS => _isMacOS.Value; public static bool IsLinuxBSD => _isLinuxBSD.Value; public static bool IsServer => _isServer.Value; public static bool IsUWP => _isUWP.Value; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index a17c371117..4c137a2ba4 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -97,7 +97,7 @@ #define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable" #define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info" -#define BINDINGS_GENERATOR_VERSION UINT32_C(11) +#define BINDINGS_GENERATOR_VERSION UINT32_C(12) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); @@ -1368,7 +1368,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); output.append("." + ctor_method); - output.append("(this);\n" CLOSE_BLOCK_L2); + output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2); } else { // Hide the constructor output.append(MEMBER_BEGIN "internal "); diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp index 430c82953e..f7d6e7e302 100644 --- a/modules/mono/editor/script_class_parser.cpp +++ b/modules/mono/editor/script_class_parser.cpp @@ -151,7 +151,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { case '"': { bool verbatim = idx != 0 && code[idx - 1] == '@'; - CharType begin_str = code[idx]; + char32_t begin_str = code[idx]; idx++; String tk_string = String(); while (true) { @@ -170,13 +170,13 @@ ScriptClassParser::Token ScriptClassParser::get_token() { } else if (code[idx] == '\\' && !verbatim) { //escaped characters... idx++; - CharType next = code[idx]; + char32_t next = code[idx]; if (next == 0) { error_str = "Unterminated String"; error = true; return TK_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -234,7 +234,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() { if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { //a number - const CharType *rptr; + const char32_t *rptr; double number = String::to_float(&code[idx], &rptr); idx += (rptr - &code[idx]); value = number; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index a963810d63..f77d3052f4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -44,6 +44,15 @@ namespace Godot.Collections Add(element); } + public Array(params object[] array) : this() + { + if (array == null) + { + throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); + } + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array)); + } + internal Array(ArraySafeHandle handle) { safeHandle = handle; @@ -72,6 +81,11 @@ namespace Godot.Collections return godot_icall_Array_Resize(GetPtr(), newSize); } + public static Array operator +(Array left, Array right) + { + return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr())); + } + // IDisposable public void Dispose() @@ -155,6 +169,9 @@ namespace Godot.Collections internal extern static IntPtr godot_icall_Array_Ctor(); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Dtor(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] @@ -176,6 +193,9 @@ namespace Godot.Collections internal extern static void godot_icall_Array_Clear(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] @@ -231,6 +251,15 @@ namespace Godot.Collections objectArray = new Array(collection); } + public Array(params T[] array) : this() + { + if (array == null) + { + throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'"); + } + objectArray = new Array(array); + } + public Array(Array array) { objectArray = array; @@ -266,6 +295,11 @@ namespace Godot.Collections return objectArray.Resize(newSize); } + public static Array<T> operator +(Array<T> left, Array<T> right) + { + return new Array<T>(left.objectArray + right.objectArray); + } + // IList<T> public T this[int index] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 5aba31c622..3f1120575f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -207,7 +207,7 @@ namespace Godot } } - internal Quat RotationQuat() + public Quat RotationQuat() { Basis orthonormalizedBasis = Orthonormalized(); real_t det = orthonormalizedBasis.Determinant(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 42610c5ef7..48582d5ad8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -15,14 +15,13 @@ namespace Godot public Object() : this(false) { if (ptr == IntPtr.Zero) - { ptr = godot_icall_Object_Ctor(this); - } - else - { - // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case. - godot_icall_Object_ConnectEventSignals(ptr); - } + _InitializeGodotScriptInstanceInternals(); + } + + internal void _InitializeGodotScriptInstanceInternals() + { + godot_icall_Object_ConnectEventSignals(ptr); } internal Object(bool memoryOwn) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index 41b4e9367f..d63db0f905 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -393,6 +393,35 @@ namespace Godot return instance.Substring(sep + 1); } + /// <summary> + /// Converts the given byte array of ASCII encoded text to a string. + /// Faster alternative to <see cref="GetStringFromUTF8"/> if the + /// content is ASCII-only. Unlike the UTF-8 function this function + /// maps every byte to a character in the array. Multibyte sequences + /// will not be interpreted correctly. For parsing user input always + /// use <see cref="GetStringFromUTF8"/>. + /// </summary> + /// <param name="bytes">A byte array of ASCII characters (on the range of 0-127).</param> + /// <returns>A string created from the bytes.</returns> + public static string GetStringFromASCII(this byte[] bytes) + { + return Encoding.ASCII.GetString(bytes); + } + + /// <summary> + /// Converts the given byte array of UTF-8 encoded text to a string. + /// Slower than <see cref="GetStringFromASCII"/> but supports UTF-8 + /// encoded data. Use this function if you are unsure about the + /// source of the data. For user input this function + /// should always be preferred. + /// </summary> + /// <param name="bytes">A byte array of UTF-8 characters (a character may take up multiple bytes).</param> + /// <returns>A string created from the bytes.</returns> + public static string GetStringFromUTF8(this byte[] bytes) + { + return Encoding.UTF8.GetString(bytes); + } + // <summary> // Hash the string and return a 32 bits integer. // </summary> @@ -440,7 +469,12 @@ namespace Godot // </summary> public static bool IsAbsPath(this string instance) { - return System.IO.Path.IsPathRooted(instance); + if (string.IsNullOrEmpty(instance)) + return false; + else if (instance.Length > 1) + return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/") || instance.Contains(":\\"); + else + return instance[0] == '/' || instance[0] == '\\'; } // <summary> @@ -448,7 +482,7 @@ namespace Godot // </summary> public static bool IsRelPath(this string instance) { - return !System.IO.Path.IsPathRooted(instance); + return !IsAbsPath(instance); } // <summary> @@ -624,41 +658,46 @@ namespace Godot return instance.Length; } - // <summary> - // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. - // </summary> - public static bool ExprMatch(this string instance, string expr, bool caseSensitive) + /// <summary> + /// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. + /// </summary> + private static bool ExprMatch(this string instance, string expr, bool caseSensitive) { - if (expr.Length == 0 || instance.Length == 0) - return false; + // case '\0': + if (expr.Length == 0) + return instance.Length == 0; switch (expr[0]) { - case '\0': - return instance[0] == 0; case '*': - return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); + return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive)); case '?': - return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); + return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); default: - return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && - ExprMatch(expr + 1, instance + 1, caseSensitive); + if (instance.Length == 0) return false; + return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive); } } - // <summary> - // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). - // </summary> + /// <summary> + /// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). + /// </summary> public static bool Match(this string instance, string expr, bool caseSensitive = true) { + if (instance.Length == 0 || expr.Length == 0) + return false; + return instance.ExprMatch(expr, caseSensitive); } - // <summary> - // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). - // </summary> + /// <summary> + /// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). + /// </summary> public static bool MatchN(this string instance, string expr) { + if (instance.Length == 0 || expr.Length == 0) + return false; + return instance.ExprMatch(expr, caseSensitive: false); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 26bd828a5b..d536b14eac 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -437,8 +437,11 @@ namespace Godot /// <returns>The rotated vector.</returns> public Vector2 Rotated(real_t phi) { - real_t rads = Angle() + phi; - return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); + real_t sine = Mathf.Sin(phi); + real_t cosi = Mathf.Cos(phi); + return new Vector2( + x * cosi - y * sine, + x * sine + y * cosi); } /// <summary> @@ -670,41 +673,37 @@ namespace Godot public static bool operator <(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y < right.y; } - return left.x < right.x; } public static bool operator >(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y > right.y; } - return left.x > right.x; } public static bool operator <=(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y <= right.y; } - return left.x <= right.x; } public static bool operator >=(Vector2 left, Vector2 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { return left.y >= right.y; } - return left.x >= right.x; } @@ -714,7 +713,6 @@ namespace Godot { return Equals((Vector2)obj); } - return false; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index d9b16a6afd..4a4a2a43cd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -713,49 +713,53 @@ namespace Godot public static bool operator <(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z < right.z; + } return left.y < right.y; } - return left.x < right.x; } public static bool operator >(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z > right.z; + } return left.y > right.y; } - return left.x > right.x; } public static bool operator <=(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z <= right.z; + } return left.y < right.y; } - return left.x < right.x; } public static bool operator >=(Vector3 left, Vector3 right) { - if (Mathf.IsEqualApprox(left.x, right.x)) + if (left.x == right.x) { - if (Mathf.IsEqualApprox(left.y, right.y)) + if (left.y == right.y) + { return left.z >= right.z; + } return left.y > right.y; } - return left.x > right.x; } diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index ebcd6d5e9c..439fb1ab0a 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -31,7 +31,6 @@ #ifdef MONO_GLUE_ENABLED #include "core/class_db.h" -#include "core/object.h" #include "core/reference.h" #include "core/string_name.h" diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 766b00d612..3313e8cb77 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -104,10 +104,31 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { } } +Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) { + Array *godot_array = memnew(Array); + unsigned int count = mono_array_length(mono_array); + godot_array->resize(count); + for (unsigned int i = 0; i < count; i++) { + MonoObject *item = mono_array_get(mono_array, MonoObject *, i); + godot_icall_Array_SetAt(godot_array, i, item); + } + return godot_array; +} + Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) { return memnew(Array(ptr->duplicate(deep))); } +Array *godot_icall_Array_Concatenate(Array *left, Array *right) { + int count = left->size() + right->size(); + Array *new_array = memnew(Array(left->duplicate(false))); + new_array->resize(count); + for (unsigned int i = 0; i < (unsigned int)right->size(); i++) { + new_array->operator[](i + left->size()) = right->operator[](i); + } + return new_array; +} + int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); } @@ -284,6 +305,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic); @@ -291,6 +313,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate); diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index c1f1936711..dc3bf47838 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -64,7 +64,6 @@ void godot_register_glue_header_icalls() { #include "core/engine.h" #include "core/method_bind.h" #include "core/node_path.h" -#include "core/object.h" #include "core/reference.h" #include "core/typedefs.h" #include "core/ustring.h" diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp index 6d2e6b559f..a7b18c36dd 100644 --- a/modules/mono/glue/rid_glue.cpp +++ b/modules/mono/glue/rid_glue.cpp @@ -30,7 +30,7 @@ #ifdef MONO_GLUE_ENABLED -#include "core/object.h" +#include "core/class_db.h" #include "core/resource.h" #include "core/rid.h" diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 692da991c7..df31823deb 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -122,7 +122,7 @@ public: private: _GodotSharpDirs() { - res_data_dir = "res://.mono"; + res_data_dir = "res://.godot/mono"; res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); diff --git a/modules/mono/icons/CSharpScript.svg b/modules/mono/icons/CSharpScript.svg index 69664ca553..0b2cc840f8 100644 --- a/modules/mono/icons/CSharpScript.svg +++ b/modules/mono/icons/CSharpScript.svg @@ -1,5 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-0.55228 0-1-0.4478-1-1 0-0.5523 0.44772-1 1-1h1v-2zm1-9-0.56445 2.2578c-0.23643 0.076-0.46689 0.1692-0.68945 0.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-0.11191 0.2211-0.20723 0.4502-0.28516 0.6855l-2.2539 0.5625v2h5.2715c-0.17677-0.3037-0.27041-0.6486-0.27148-1 9.6e-6 -1.1046 0.89543-2 2-2s2 0.8954 2 2c-4.817e-4 0.3512-0.093442 0.6961-0.26953 1h5.2695v-2l-2.2578-0.5645c-0.07594-0.2357-0.1693-0.4655-0.2793-0.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-0.22113-0.1119-0.45028-0.2073-0.68555-0.2852l-0.5625-2.2539zm4 9c-0.71466-1e-4 -1.3751 0.3811-1.7324 1-0.35727 0.6188-0.35727 1.3812 0 2 0.35733 0.6189 1.0178 1.0001 1.7324 1h-2v2h2c0.71466 1e-4 1.3751-0.3811 1.7324-1 0.35727-0.6188 0.35727-1.3812 0-2-0.35733-0.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-.55228 0-1-.4478-1-1 0-.5523.44772-1 1-1h1v-2zm1-9-.56445 2.2578c-.23643.076-.46689.1692-.68945.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-.11191.2211-.20723.4502-.28516.6855l-2.2539.5625v2h5.2715c-.17677-.3037-.27041-.6486-.27148-1 .0000096-1.1046.89543-2 2-2s2 .8954 2 2c-.0004817.3512-.093442.6961-.26953 1h5.2695v-2l-2.2578-.5645c-.07594-.2357-.1693-.4655-.2793-.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-.22113-.1119-.45028-.2073-.68555-.2852l-.5625-2.2539zm4 9c-.71466-.0001-1.3751.3811-1.7324 1-.35727.6188-.35727 1.3812 0 2 .35733.6189 1.0178 1.0001 1.7324 1h-2v2h2c.71466.0001 1.3751-.3811 1.7324-1 .35727-.6188.35727-1.3812 0-2-.35733-.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 -1036.4)"/></svg> diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 9dbeee57ce..6e351001d4 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -107,7 +107,7 @@ void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly)); #ifdef GD_MONO_HOT_RELOAD - const char *path = mono_image_get_filename(image); + String path = String::utf8(mono_image_get_filename(image)); if (FileAccess::exists(path)) { gdassembly->modified_time = FileAccess::get_modified_time(path); } @@ -464,7 +464,9 @@ GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_a if (!assembly) { assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs); - ERR_FAIL_NULL_V(assembly, nullptr); + if (!assembly) { + return nullptr; + } } GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); @@ -487,7 +489,9 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_ if (!assembly) { assembly = _real_load_assembly_from(p_path, p_refonly); - ERR_FAIL_NULL_V(assembly, nullptr); + if (!assembly) { + return nullptr; + } } GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name); diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index fe1c2d28dd..0ed9e441ef 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -109,8 +109,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle); unmanaged->set_script_and_instance(script, csharp_instance); - - csharp_instance->connect_event_signals(); } void unhandled_exception(MonoException *p_exc) { diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index 038d17f782..cf7efdecd6 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -35,7 +35,7 @@ #include "../utils/macros.h" -#include "core/object.h" +#include "core/class_db.h" namespace GDMonoInternals { diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index c5a988b8c3..b8ee0204c4 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -64,25 +64,32 @@ static int get_log_level_id(const char *p_log_level) { return -1; } +static String make_text(const char *log_domain, const char *log_level, const char *message) { + String text(message); + text += " (in domain "; + text += log_domain; + if (log_level) { + text += ", "; + text += log_level; + } + text += ")"; + return text; +} + void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) { FileAccess *f = GDMonoLog::get_singleton()->log_file; if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) { - String text(message); - text += " (in domain "; - text += log_domain; - if (log_level) { - text += ", "; - text += log_level; - } - text += ")\n"; + String text = make_text(log_domain, log_level, message); + text += "\n"; f->seek_end(); f->store_string(text); } if (fatal) { - ERR_PRINT("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); + String text = make_text(log_domain, log_level, message); + ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'."); // Make sure to flush before aborting f->flush(); f->close(); diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 6d7d5f76cd..c460e283ea 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -311,44 +311,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ return false; } -String mono_to_utf8_string(MonoString *p_mono_string) { - MonoError error; - char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); - - if (!mono_error_ok(&error)) { - ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&error) + "'."); - mono_error_cleanup(&error); - return String(); - } - - String ret = String::utf8(utf8); - - mono_free(utf8); - - return ret; -} - -String mono_to_utf16_string(MonoString *p_mono_string) { - int len = mono_string_length(p_mono_string); - String ret; - - if (len == 0) { - return ret; - } - - ret.resize(len + 1); - ret.set(len, 0); - - CharType *src = (CharType *)mono_string_chars(p_mono_string); - CharType *dst = ret.ptrw(); - - for (int i = 0; i < len; i++) { - dst[i] = src[i]; - } - - return ret; -} - MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 4ff330fd43..a1fd975916 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -69,15 +69,11 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ // String -String mono_to_utf8_string(MonoString *p_mono_string); -String mono_to_utf16_string(MonoString *p_mono_string); - _FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_to_utf16_string(p_mono_string); - } - - return mono_to_utf8_string(p_mono_string); + char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string); + String ret = String(utf32); + mono_free(utf32); + return ret; } _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { @@ -88,20 +84,8 @@ _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) { return mono_string_to_godot_not_null(p_mono_string); } -_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) { - return mono_string_new(mono_domain_get(), p_string.utf8().get_data()); -} - -_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) { - return mono_string_from_utf16((mono_unichar2 *)p_string.c_str()); -} - _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { - if constexpr (sizeof(CharType) == 2) { - return mono_from_utf16_string(p_string); - } - - return mono_from_utf8_string(p_string); + return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data())); } // Variant diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 5958bf3cc1..80569cb1c7 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -37,7 +37,7 @@ #include "../utils/macros.h" #include "gd_mono_header.h" -#include "core/object.h" +#include "core/class_db.h" #include "core/reference.h" #define UNHANDLED_EXCEPTION(m_exc) \ diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm index e3d1a647fd..dc23c06eba 100644 --- a/modules/mono/mono_gd/support/ios_support.mm +++ b/modules/mono/mono_gd/support/ios_support.mm @@ -131,8 +131,7 @@ GD_PINVOKE_EXPORT void *xamarin_timezone_get_data(const char *p_name, uint32_t * NSTimeZone *tz = nil; if (p_name) { NSString *n = [[NSString alloc] initWithUTF8String:p_name]; - tz = [[[NSTimeZone alloc] initWithName:n] autorelease]; - [n release]; + tz = [[NSTimeZone alloc] initWithName:n]; } else { tz = [NSTimeZone localTimeZone]; } diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h index 7fd0d24eb0..e30d9a8abd 100644 --- a/modules/mono/register_types.h +++ b/modules/mono/register_types.h @@ -28,5 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef MONO_REGISTER_TYPES_H +#define MONO_REGISTER_TYPES_H + void register_mono_types(); void unregister_mono_types(); + +#endif // MONO_REGISTER_TYPES_H diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index e0cf916a01..a619f0b975 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -71,12 +71,12 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) buffer.resize(512); DWORD dwBufferSize = buffer.size(); - LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); if (res == ERROR_MORE_DATA) { // dwBufferSize now contains the actual size buffer.resize(dwBufferSize); - res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); + res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize); } if (res == ERROR_SUCCESS) { @@ -90,7 +90,7 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) { HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; @@ -127,7 +127,7 @@ LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) { String default_clr; HKEY hKey; - LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey); + LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey); if (res != ERROR_SUCCESS) goto cleanup; diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ccfaf5aba7..5d1abd0c09 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -54,12 +54,16 @@ String cwd() { #ifdef WINDOWS_ENABLED const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr); - String buffer; + Char16String buffer; buffer.resize((int)expected_size); - if (::GetCurrentDirectoryW(expected_size, buffer.ptrw()) == 0) + if (::GetCurrentDirectoryW(expected_size, (wchar_t *)buffer.ptrw()) == 0) return "."; - return buffer.simplify_path(); + String result; + if (result.parse_utf16(buffer.ptr())) { + return "."; + } + return result.simplify_path(); #else char buffer[PATH_MAX]; if (::getcwd(buffer, sizeof(buffer)) == nullptr) { @@ -86,7 +90,7 @@ String abspath(const String &p_path) { String realpath(const String &p_path) { #ifdef WINDOWS_ENABLED // Open file without read/write access - HANDLE hFile = ::CreateFileW(p_path.c_str(), 0, + HANDLE hFile = ::CreateFileW((LPCWSTR)(p_path.utf16().get_data()), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -100,12 +104,18 @@ String realpath(const String &p_path) { return p_path; } - String buffer; + Char16String buffer; buffer.resize((int)expected_size); - ::GetFinalPathNameByHandleW(hFile, buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED); + ::GetFinalPathNameByHandleW(hFile, (wchar_t *)buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED); ::CloseHandle(hFile); - return buffer.simplify_path(); + + String result; + if (result.parse_utf16(buffer.ptr())) { + return p_path; + } + + return result.simplify_path(); #elif UNIX_ENABLED char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr); @@ -130,7 +140,7 @@ String join(const String &p_a, const String &p_b) { return p_b; } - const CharType a_last = p_a[p_a.length() - 1]; + const char32_t a_last = p_a[p_a.length() - 1]; if ((a_last == '/' || a_last == '\\') || (p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) { return p_a + p_b; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index f8d9804de4..65da4328f6 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -49,7 +49,7 @@ int sfind(const String &p_text, int p_from) { return -1; } - const CharType *src = p_text.c_str(); + const char32_t *src = p_text.get_data(); for (int i = p_from; i <= (len - src_len); i++) { bool found = true; @@ -64,7 +64,7 @@ int sfind(const String &p_text, int p_from) { found = src[read_pos] == '%'; break; case 1: { - CharType c = src[read_pos]; + char32_t c = src[read_pos]; found = src[read_pos] == 's' || (c >= '0' && c <= '4'); break; } @@ -121,7 +121,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const int result = 0; while ((result = sfind(p_text, search_from)) >= 0) { - CharType c = p_text[result + 1]; + char32_t c = p_text[result + 1]; int req_index = (c == 's' ? findex++ : c - '0'); diff --git a/modules/opensimplex/icons/NoiseTexture.svg b/modules/opensimplex/icons/NoiseTexture.svg index 5908c2b2d4..479684cde2 100644 --- a/modules/opensimplex/icons/NoiseTexture.svg +++ b/modules/opensimplex/icons/NoiseTexture.svg @@ -1,3 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<path d="m2 1c-0.55228 0-1 0.44772-1 1v12c0 0.55228 0.44772 1 1 1h12c0.55228 0 1-0.44772 1-1v-12c0-0.55228-0.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/></svg> diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 36c0913f62..050dce1aab 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -213,10 +213,12 @@ static void _compress_pvrtc4(Image *p_img) { int ofs, size, w, h; img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); Javelin::RgbaBitmap bm(w, h); + void *dst = (void *)bm.GetData(); + copymem(dst, &r[ofs], size); + Javelin::ColorRgba<unsigned char> *dp = bm.GetData(); for (int j = 0; j < size / 4; j++) { - Javelin::ColorRgba<unsigned char> *dp = bm.GetData(); - /* red and Green colors are swapped. */ - new (dp) Javelin::ColorRgba<unsigned char>(r[ofs + 4 * j + 2], r[ofs + 4 * j + 1], r[ofs + 4 * j], r[ofs + 4 * j + 3]); + /* red and blue colors are swapped. */ + SWAP(dp[j].r, dp[j].b); } new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm); diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 50ca01067b..c10a276eae 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -156,26 +156,13 @@ void RegExMatch::_bind_methods() { } void RegEx::_pattern_info(uint32_t what, void *where) const { - if (sizeof(CharType) == 2) { - pcre2_pattern_info_16((pcre2_code_16 *)code, what, where); - - } else { - pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); - } + pcre2_pattern_info_32((pcre2_code_32 *)code, what, where); } void RegEx::clear() { - if (sizeof(CharType) == 2) { - if (code) { - pcre2_code_free_16((pcre2_code_16 *)code); - code = nullptr; - } - - } else { - if (code) { - pcre2_code_free_32((pcre2_code_32 *)code); - code = nullptr; - } + if (code) { + pcre2_code_free_32((pcre2_code_32 *)code); + code = nullptr; } } @@ -187,39 +174,20 @@ Error RegEx::compile(const String &p_pattern) { PCRE2_SIZE offset; uint32_t flags = PCRE2_DUPNAMES; - if (sizeof(CharType) == 2) { - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx); - PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str(); - - code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx); + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); + PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.get_data(); - pcre2_compile_context_free_16(cctx); + code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - if (!code) { - PCRE2_UCHAR16 buf[256]; - pcre2_get_error_message_16(err, buf, 256); - String message = String::num(offset) + ": " + String((const CharType *)buf); - ERR_PRINT(message.utf8()); - return FAILED; - } + pcre2_compile_context_free_32(cctx); - } else { - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx); - PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str(); - - code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx); - - pcre2_compile_context_free_32(cctx); - - if (!code) { - PCRE2_UCHAR32 buf[256]; - pcre2_get_error_message_32(err, buf, 256); - String message = String::num(offset) + ": " + String((const CharType *)buf); - ERR_PRINT(message.utf8()); - return FAILED; - } + if (!code) { + PCRE2_UCHAR32 buf[256]; + pcre2_get_error_message_32(err, buf, 256); + String message = String::num(offset) + ": " + String((const char32_t *)buf); + ERR_PRINT(message.utf8()); + return FAILED; } return OK; } @@ -234,69 +202,39 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) length = p_end; } - if (sizeof(CharType) == 2) { - pcre2_code_16 *c = (pcre2_code_16 *)code; - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); - PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); - - pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - - int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx); + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data(); - if (res < 0) { - pcre2_match_data_free_16(match); - return nullptr; - } - - uint32_t size = pcre2_get_ovector_count_16(match); - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match); - - result->data.resize(size); - - for (uint32_t i = 0; i < size; i++) { - result->data.write[i].start = ovector[i * 2]; - result->data.write[i].end = ovector[i * 2 + 1]; - } - - pcre2_match_data_free_16(match); - pcre2_match_context_free_16(mctx); - - } else { - pcre2_code_32 *c = (pcre2_code_32 *)code; - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); - PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); + int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); - int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx); - - if (res < 0) { - pcre2_match_data_free_32(match); - pcre2_match_context_free_32(mctx); + if (res < 0) { + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); - return nullptr; - } + return nullptr; + } - uint32_t size = pcre2_get_ovector_count_32(match); - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); + uint32_t size = pcre2_get_ovector_count_32(match); + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match); - result->data.resize(size); + result->data.resize(size); - for (uint32_t i = 0; i < size; i++) { - result->data.write[i].start = ovector[i * 2]; - result->data.write[i].end = ovector[i * 2 + 1]; - } - - pcre2_match_data_free_32(match); - pcre2_match_context_free_32(mctx); + for (uint32_t i = 0; i < size; i++) { + result->data.write[i].start = ovector[i * 2]; + result->data.write[i].end = ovector[i * 2 + 1]; } + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); + result->subject = p_subject; uint32_t count; - const CharType *table; + const char32_t *table; uint32_t entry_size; _pattern_info(PCRE2_INFO_NAMECOUNT, &count); @@ -304,7 +242,7 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) _pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size); for (uint32_t i = 0; i < count; i++) { - CharType id = table[i * entry_size]; + char32_t id = table[i * entry_size]; if (result->data[id].start == -1) { continue; } @@ -344,7 +282,7 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a const int safety_zone = 1; PCRE2_SIZE olength = p_subject.length() + 1; // space for output string and one terminating \0 character - Vector<CharType> output; + Vector<char32_t> output; output.resize(olength + safety_zone); uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH; @@ -357,55 +295,28 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a length = p_end; } - if (sizeof(CharType) == 2) { - pcre2_code_16 *c = (pcre2_code_16 *)code; - pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx; - pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx); - PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str(); - PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str(); - PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.ptrw(); + pcre2_code_32 *c = (pcre2_code_32 *)code; + pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; + pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); + PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data(); + PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.get_data(); + PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw(); - pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx); - - int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - - if (res == PCRE2_ERROR_NOMEMORY) { - output.resize(olength + safety_zone); - o = (PCRE2_UCHAR16 *)output.ptrw(); - res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - } + pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - pcre2_match_data_free_16(match); - pcre2_match_context_free_16(mctx); + int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - if (res < 0) { - return String(); - } - - } else { - pcre2_code_32 *c = (pcre2_code_32 *)code; - pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx; - pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx); - PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str(); - PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str(); - PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw(); - - pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx); - - int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - - if (res == PCRE2_ERROR_NOMEMORY) { - output.resize(olength + safety_zone); - o = (PCRE2_UCHAR32 *)output.ptrw(); - res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); - } + if (res == PCRE2_ERROR_NOMEMORY) { + output.resize(olength + safety_zone); + o = (PCRE2_UCHAR32 *)output.ptrw(); + res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength); + } - pcre2_match_data_free_32(match); - pcre2_match_context_free_32(mctx); + pcre2_match_data_free_32(match); + pcre2_match_context_free_32(mctx); - if (res < 0) { - return String(); - } + if (res < 0) { + return String(); } return String(output.ptr(), olength); @@ -435,7 +346,7 @@ Array RegEx::get_names() const { ERR_FAIL_COND_V(!is_valid(), result); uint32_t count; - const CharType *table; + const char32_t *table; uint32_t entry_size; _pattern_info(PCRE2_INFO_NAMECOUNT, &count); @@ -453,39 +364,21 @@ Array RegEx::get_names() const { } RegEx::RegEx() { - if (sizeof(CharType) == 2) { - general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr); - - } else { - general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); - } + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); code = nullptr; } RegEx::RegEx(const String &p_pattern) { - if (sizeof(CharType) == 2) { - general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr); - - } else { - general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); - } + general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr); code = nullptr; compile(p_pattern); } RegEx::~RegEx() { - if (sizeof(CharType) == 2) { - if (code) { - pcre2_code_free_16((pcre2_code_16 *)code); - } - pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx); - - } else { - if (code) { - pcre2_code_free_32((pcre2_code_32 *)code); - } - pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); + if (code) { + pcre2_code_free_32((pcre2_code_32 *)code); } + pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx); } void RegEx::_bind_methods() { diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 3aceaf11c5..346833ab9c 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -158,14 +158,16 @@ void AudioStreamOGGVorbis::clear_data() { void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) { int src_data_len = p_data.size(); -#define MAX_TEST_MEM (1 << 20) - uint32_t alloc_try = 1024; Vector<char> alloc_mem; char *w; stb_vorbis *ogg_stream = nullptr; stb_vorbis_alloc ogg_alloc; + // Vorbis comments may be up to UINT32_MAX, but that's arguably pretty rare. + // Let's go with 2^30 so we don't risk going out of bounds. + const uint32_t MAX_TEST_MEM = 1 << 30; + while (alloc_try < MAX_TEST_MEM) { alloc_mem.resize(alloc_try); w = alloc_mem.ptrw(); @@ -205,6 +207,8 @@ void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) { break; } } + + ERR_FAIL_COND_MSG(alloc_try == MAX_TEST_MEM, vformat("Couldn't set vorbis data even with an alloc buffer of %d bytes, report bug.", MAX_TEST_MEM)); } Vector<uint8_t> AudioStreamOGGVorbis::get_data() const { diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index 92244a4d28..cb8852d5ef 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -4,7 +4,8 @@ [VideoStream] resource for Ogg Theora videos. </brief_description> <description> - [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. + [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is less efficient than [VideoStreamWebm]'s VP8 and VP9, but it requires less CPU resources to decode. The Theora codec is decoded on the CPU. + [b]Note:[/b] While Ogg Theora videos can also have an [code].ogg[/code] extension, you will have to rename the extension to [code].ogv[/code] to use those videos within Godot. </description> <tutorials> </tutorials> @@ -22,7 +23,7 @@ <argument index="0" name="file" type="String"> </argument> <description> - Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].o[/code] extension. + Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension. </description> </method> </methods> diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 9e7266b95a..5bdcb84244 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -73,8 +73,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f } // Read HALF channel as FLOAT. (GH-13490) + bool use_float16 = false; for (int i = 0; i < exr_header.num_channels; i++) { if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { + use_float16 = true; exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; } } @@ -102,33 +104,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f idxB = c; } else if (strcmp(exr_header.channels[c].name, "A") == 0) { idxA = c; - } - } - - if (exr_header.num_channels == 1) { - // Grayscale channel only. - idxR = 0; - idxG = 0; - idxB = 0; - idxA = 0; - } else { - // Assume RGB(A) - if (idxR == -1) { - ERR_PRINT("TinyEXR: R channel not found."); - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } - - if (idxG == -1) { - ERR_PRINT("TinyEXR: G channel not found."); - // @todo { free exr_image } - return ERR_FILE_CORRUPT; - } - - if (idxB == -1) { - ERR_PRINT("TinyEXR: B channel not found."); - // @todo { free exr_image } - return ERR_FILE_CORRUPT; + } else if (strcmp(exr_header.channels[c].name, "Y") == 0) { + idxR = c; + idxG = c; + idxB = c; } } @@ -138,14 +117,27 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f Image::Format format; int output_channels = 0; + int channel_size = use_float16 ? 2 : 4; if (idxA != -1) { - imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16 - format = Image::FORMAT_RGBAH; + imgdata.resize(exr_image.width * exr_image.height * 4 * channel_size); //RGBA + format = use_float16 ? Image::FORMAT_RGBAH : Image::FORMAT_RGBAF; output_channels = 4; - } else { - imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16 - format = Image::FORMAT_RGBH; + } else if (idxB != -1) { + ERR_FAIL_COND_V(idxG == -1, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT); + imgdata.resize(exr_image.width * exr_image.height * 3 * channel_size); //RGB + format = use_float16 ? Image::FORMAT_RGBH : Image::FORMAT_RGBF; output_channels = 3; + } else if (idxG != -1) { + ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT); + imgdata.resize(exr_image.width * exr_image.height * 2 * channel_size); //RG + format = use_float16 ? Image::FORMAT_RGH : Image::FORMAT_RGF; + output_channels = 2; + } else { + ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT); + imgdata.resize(exr_image.width * exr_image.height * 1 * channel_size); //R + format = use_float16 ? Image::FORMAT_RH : Image::FORMAT_RF; + output_channels = 1; } EXRTile single_image_tile; @@ -175,9 +167,11 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f exr_tiles = exr_image.tiles; } + //print_line("reading format: " + Image::get_format_name(format)); { uint8_t *wd = imgdata.ptrw(); - uint16_t *iw = (uint16_t *)wd; + uint16_t *iw16 = (uint16_t *)wd; + float *iw32 = (float *)wd; // Assume `out_rgba` have enough memory allocated. for (int tile_index = 0; tile_index < num_tiles; tile_index++) { @@ -187,41 +181,99 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f int th = tile.height; const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]); - const float *g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]); - const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]); + const float *g_channel_start = nullptr; + const float *b_channel_start = nullptr; const float *a_channel_start = nullptr; + if (idxG != -1) { + g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]); + } + if (idxB != -1) { + b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]); + } if (idxA != -1) { a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]); } - uint16_t *first_row_w = iw + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels; + uint16_t *first_row_w16 = iw16 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels; + float *first_row_w32 = iw32 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels; for (int y = 0; y < th; y++) { const float *r_channel = r_channel_start + y * tile_width; - const float *g_channel = g_channel_start + y * tile_width; - const float *b_channel = b_channel_start + y * tile_width; + const float *g_channel = nullptr; + const float *b_channel = nullptr; const float *a_channel = nullptr; - + if (g_channel_start) { + g_channel = g_channel_start + y * tile_width; + } + if (b_channel_start) { + b_channel = b_channel_start + y * tile_width; + } if (a_channel_start) { a_channel = a_channel_start + y * tile_width; } - uint16_t *row_w = first_row_w + (y * exr_image.width * output_channels); - - for (int x = 0; x < tw; x++) { - Color color(*r_channel++, *g_channel++, *b_channel++); - - if (p_force_linear) { - color = color.to_linear(); + if (use_float16) { + uint16_t *row_w = first_row_w16 + (y * exr_image.width * output_channels); + + for (int x = 0; x < tw; x++) { + Color color; + color.r = *r_channel++; + if (g_channel) { + color.g = *g_channel++; + } + if (b_channel) { + color.b = *b_channel++; + } + if (a_channel) { + color.a = *a_channel++; + } + + if (p_force_linear) { + color = color.to_linear(); + } + + *row_w++ = Math::make_half_float(color.r); + if (g_channel) { + *row_w++ = Math::make_half_float(color.g); + } + if (b_channel) { + *row_w++ = Math::make_half_float(color.b); + } + if (a_channel) { + *row_w++ = Math::make_half_float(color.a); + } } - - *row_w++ = Math::make_half_float(color.r); - *row_w++ = Math::make_half_float(color.g); - *row_w++ = Math::make_half_float(color.b); - - if (idxA != -1) { - *row_w++ = Math::make_half_float(*a_channel++); + } else { + float *row_w = first_row_w32 + (y * exr_image.width * output_channels); + + for (int x = 0; x < tw; x++) { + Color color; + color.r = *r_channel++; + if (g_channel) { + color.g = *g_channel++; + } + if (b_channel) { + color.b = *b_channel++; + } + if (a_channel) { + color.a = *a_channel++; + } + + if (p_force_linear) { + color = color.to_linear(); + } + + *row_w++ = color.r; + if (g_channel) { + *row_w++ = color.g; + } + if (b_channel) { + *row_w++ = color.b; + } + if (a_channel) { + *row_w++ = color.a; + } } } } diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg index f6475d590e..2352ba5d87 100644 --- a/modules/visual_script/icons/VisualScript.svg +++ b/modules/visual_script/icons/VisualScript.svg @@ -1,6 +1 @@ -<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<ellipse cx="3" cy="1039.4" r="2" fill="#6e6e6e"/> -<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2h5.2715a2 2 0 0 1 -0.27148 -1 2 2 0 0 1 2 -2 2 2 0 0 1 2 2 2 2 0 0 1 -0.26953 1h5.2695v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3 -3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324 -1 2 2 0 0 0 0 -2 2 2 0 0 0 -1.7324 -1h2v-2h-2z" fill="#e0e0e0"/> -</g> -</svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index a0dcd76d10..177f9192b8 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -1060,7 +1060,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in } break; case VisualScriptBuiltinFunc::TEXT_CHAR: { - CharType result[2] = { *p_inputs[0], 0 }; + char32_t result[2] = { *p_inputs[0], 0 }; *r_return = String(result); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 5581ea9318..01c674d086 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -30,8 +30,8 @@ #include "visual_script_editor.h" +#include "core/class_db.h" #include "core/input/input.h" -#include "core/object.h" #include "core/os/keyboard.h" #include "core/script_language.h" #include "core/variant.h" @@ -1124,8 +1124,8 @@ void VisualScriptEditor::_update_members() { TreeItem *ti = members->create_item(variables); ti->set_text(0, E->get()); - Variant var = script->get_variable_default_value(E->get()); - ti->set_suffix(0, "= " + String(var)); + + ti->set_suffix(0, "= " + _sanitized_variant_text(E->get())); ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]); ti->set_selectable(0, true); @@ -1167,6 +1167,18 @@ void VisualScriptEditor::_update_members() { updating_members = false; } +String VisualScriptEditor::_sanitized_variant_text(const StringName &property_name) { + Variant var = script->get_variable_default_value(property_name); + + if (script->get_variable_info(property_name).type != Variant::NIL) { + Callable::CallError ce; + const Variant *converted = &var; + var = Variant::construct(script->get_variable_info(property_name).type, &converted, 1, ce, false); + } + + return String(var); +} + void VisualScriptEditor::_member_selected() { if (updating_members) { return; @@ -2682,7 +2694,8 @@ void VisualScriptEditor::reload(bool p_soft) { _update_graph(); } -void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { +Array VisualScriptEditor::get_breakpoints() { + Array breakpoints; List<StringName> functions; script->get_function_list(&functions); for (List<StringName>::Element *E = functions.front(); E; E = E->next()) { @@ -2691,10 +2704,11 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { for (List<int>::Element *F = nodes.front(); F; F = F->next()) { Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get()); if (vsn->is_breakpoint()) { - p_breakpoints->push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero + breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero } } } + return breakpoints; } void VisualScriptEditor::add_callback(const String &p_function, PackedStringArray p_args) { @@ -4769,8 +4783,8 @@ VisualScriptEditor::VisualScriptEditor() { graph->set_v_size_flags(Control::SIZE_EXPAND_FILL); graph->set_anchors_and_margins_preset(Control::PRESET_WIDE); graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected)); - graph->connect("_begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); - graph->connect("_end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); + graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); + graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete)); graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate)); graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input)); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 0c5665cee8..66e435741f 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -146,6 +146,7 @@ class VisualScriptEditor : public ScriptEditorBase { bool updating_members; void _update_members(); + String _sanitized_variant_text(const StringName &property_name); StringName selected; @@ -312,7 +313,7 @@ public: virtual void ensure_focus() override; virtual void tag_saved_version() override; virtual void reload(bool p_soft) override; - virtual void get_breakpoints(List<int> *p_breakpoints) override; + virtual Array get_breakpoints() override; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; virtual bool show_members_overview() override; diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 2ac7793b8c..60a439b291 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -187,7 +187,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { while (true) { #define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) - CharType cchar = GET_CHAR(); + char32_t cchar = GET_CHAR(); if (cchar == 0) { r_token.type = TK_EOF; return OK; @@ -329,7 +329,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { case '"': { String str; while (true) { - CharType ch = GET_CHAR(); + char32_t ch = GET_CHAR(); if (ch == 0) { _set_error("Unterminated String"); @@ -340,13 +340,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) { } else if (ch == '\\') { //escaped characters... - CharType next = GET_CHAR(); + char32_t next = GET_CHAR(); if (next == 0) { _set_error("Unterminated String"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { case 'b': @@ -367,7 +367,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { case 'u': { // hex number for (int j = 0; j < 4; j++) { - CharType c = GET_CHAR(); + char32_t c = GET_CHAR(); if (c == 0) { _set_error("Unterminated String"); @@ -379,7 +379,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -431,7 +431,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - CharType c = cchar; + char32_t c = cchar; bool exp_sign = false; bool exp_beg = false; bool is_float = false; diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index f13377f3c8..68de686272 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -2088,6 +2088,7 @@ void VisualScriptPropertyGet::_bind_methods() { BIND_ENUM_CONSTANT(CALL_MODE_SELF); BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH); BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE); + BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE); } class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance { diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 87aa64211e..28122ade99 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -933,36 +933,36 @@ static const char *op_names[] = { }; String VisualScriptOperator::get_caption() const { - static const wchar_t *op_names[] = { + static const char32_t *op_names[] = { //comparison - L"A = B", //OP_EQUAL, - L"A \u2260 B", //OP_NOT_EQUAL, - L"A < B", //OP_LESS, - L"A \u2264 B", //OP_LESS_EQUAL, - L"A > B", //OP_GREATER, - L"A \u2265 B", //OP_GREATER_EQUAL, + U"A = B", //OP_EQUAL, + U"A \u2260 B", //OP_NOT_EQUAL, + U"A < B", //OP_LESS, + U"A \u2264 B", //OP_LESS_EQUAL, + U"A > B", //OP_GREATER, + U"A \u2265 B", //OP_GREATER_EQUAL, //mathematic - L"A + B", //OP_ADD, - L"A - B", //OP_SUBTRACT, - L"A \u00D7 B", //OP_MULTIPLY, - L"A \u00F7 B", //OP_DIVIDE, - L"\u00AC A", //OP_NEGATE, - L"+ A", //OP_POSITIVE, - L"A mod B", //OP_MODULE, - L"A .. B", //OP_STRING_CONCAT, + U"A + B", //OP_ADD, + U"A - B", //OP_SUBTRACT, + U"A \u00D7 B", //OP_MULTIPLY, + U"A \u00F7 B", //OP_DIVIDE, + U"\u00AC A", //OP_NEGATE, + U"+ A", //OP_POSITIVE, + U"A mod B", //OP_MODULE, + U"A .. B", //OP_STRING_CONCAT, //bitwise - L"A << B", //OP_SHIFT_LEFT, - L"A >> B", //OP_SHIFT_RIGHT, - L"A & B", //OP_BIT_AND, - L"A | B", //OP_BIT_OR, - L"A ^ B", //OP_BIT_XOR, - L"~A", //OP_BIT_NEGATE, + U"A << B", //OP_SHIFT_LEFT, + U"A >> B", //OP_SHIFT_RIGHT, + U"A & B", //OP_BIT_AND, + U"A | B", //OP_BIT_OR, + U"A ^ B", //OP_BIT_XOR, + U"~A", //OP_BIT_NEGATE, //logic - L"A and B", //OP_AND, - L"A or B", //OP_OR, - L"A xor B", //OP_XOR, - L"not A", //OP_NOT, - L"A in B", //OP_IN, + U"A and B", //OP_AND, + U"A or B", //OP_OR, + U"A xor B", //OP_XOR, + U"not A", //OP_NOT, + U"A in B", //OP_IN, }; return op_names[op]; @@ -1706,8 +1706,10 @@ public: virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) { bool valid; + // *p_output[0] points to the same place as *p_inputs[2] so we need a temp to store the value before the change in the next line + Variant temp = *p_inputs[2]; *p_outputs[0] = *p_inputs[0]; - p_outputs[0]->set(*p_inputs[1], *p_inputs[2], &valid); + p_outputs[0]->set(*p_inputs[1], temp, &valid); if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index dfa04720cf..2edbc08cc8 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -4,7 +4,8 @@ [VideoStream] resource for WebM videos. </brief_description> <description> - [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. + [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU. + [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora] </description> <tutorials> </tutorials> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 2054276655..e21dee8eff 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -40,7 +40,6 @@ <argument index="0" name="label" type="String"> </argument> <argument index="1" name="options" type="Dictionary" default="{ - }"> </argument> <description> @@ -82,7 +81,6 @@ <return type="int" enum="Error"> </return> <argument index="0" name="configuration" type="Dictionary" default="{ - }"> </argument> <description> @@ -97,7 +95,7 @@ { "urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers. "username": "a_username", # Optional username for the TURN server. - "credentials": "a_password", # Optional password for the TURN server. + "credential": "a_password", # Optional password for the TURN server. } ] } diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h index b578802250..03396d207d 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.h +++ b/modules/webrtc/webrtc_data_channel_gdnative.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WEBRTC_GDNATIVE_ENABLED - #ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H #define WEBRTC_DATA_CHANNEL_GDNATIVE_H +#ifdef WEBRTC_GDNATIVE_ENABLED + #include "modules/gdnative/include/net/godot_net.h" #include "webrtc_data_channel.h" @@ -75,6 +75,6 @@ public: ~WebRTCDataChannelGDNative(); }; -#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H - #endif // WEBRTC_GDNATIVE_ENABLED + +#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h index 455866cbf1..7545910e66 100644 --- a/modules/webrtc/webrtc_data_channel_js.h +++ b/modules/webrtc/webrtc_data_channel_js.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef JAVASCRIPT_ENABLED - #ifndef WEBRTC_DATA_CHANNEL_JS_H #define WEBRTC_DATA_CHANNEL_JS_H +#ifdef JAVASCRIPT_ENABLED + #include "webrtc_data_channel.h" class WebRTCDataChannelJS : public WebRTCDataChannel { @@ -88,6 +88,6 @@ public: ~WebRTCDataChannelJS(); }; -#endif // WEBRTC_DATA_CHANNEL_JS_H - #endif // JAVASCRIPT_ENABLED + +#endif // WEBRTC_DATA_CHANNEL_JS_H diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h index bfdcf6daa1..fb37bd7722 100644 --- a/modules/webrtc/webrtc_multiplayer.h +++ b/modules/webrtc/webrtc_multiplayer.h @@ -112,4 +112,4 @@ public: ConnectionStatus get_connection_status() const override; }; -#endif +#endif // WEBRTC_MULTIPLAYER_H diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h index 74b7db1307..846b65c466 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.h +++ b/modules/webrtc/webrtc_peer_connection_gdnative.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WEBRTC_GDNATIVE_ENABLED - #ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H #define WEBRTC_PEER_CONNECTION_GDNATIVE_H +#ifdef WEBRTC_GDNATIVE_ENABLED + #include "modules/gdnative/include/net/godot_net.h" #include "webrtc_peer_connection.h" @@ -68,6 +68,6 @@ public: ~WebRTCPeerConnectionGDNative(); }; -#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H - #endif // WEBRTC_GDNATIVE_ENABLED + +#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H |