diff options
Diffstat (limited to 'modules')
130 files changed, 3333 insertions, 215 deletions
diff --git a/modules/SCsub b/modules/SCsub index 7a467676cd..74a5267355 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -18,7 +18,7 @@ for x in env.module_list: SConscript(x + "/SCsub") if env.split_modules: - env.split_lib("modules") + env.split_lib("modules", env_lib = env_modules) else: lib = env_modules.add_library("modules", env.modules_sources) diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 1c94428a2a..e0e46cd369 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -154,7 +154,7 @@ public: /// AREA_PARAM_GRAVITY_VECTOR /// Otherwise you can set area parameters virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value); - virtual Variant area_get_param(RID p_parea, AreaParameter p_param) const; + virtual Variant area_get_param(RID p_area, AreaParameter p_param) const; virtual void area_set_transform(RID p_area, const Transform &p_transform); virtual Transform area_get_transform(RID p_area) const; @@ -301,7 +301,7 @@ public: virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B); virtual Vector3 pin_joint_get_local_b(RID p_joint) const; - virtual RID joint_create_hinge(RID p_body_A, const Transform &p_frame_A, RID p_body_B, const Transform &p_frame_B); + virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B); virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B); virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value); diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index f8732c5747..c7909c7d72 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.0-rc1"> +<class name="BulletPhysicsDirectBodyState" inherits="PhysicsDirectBodyState" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 5237556df3..a59abb0ebb 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.0-rc1"> +<class name="BulletPhysicsServer" inherits="PhysicsServer" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 709eed9e40..4a511b39a7 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -35,6 +35,8 @@ #include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h> +#define RAY_STABILITY_MARGIN 0.1 + /** @author AndreaCatania */ @@ -97,10 +99,15 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo m_world->rayTestSingleInternal(ray_transform, to, other_co_wrapper, btResult); if (btResult.hasHit()) { - btVector3 ray_normal(to.getOrigin() - ray_transform.getOrigin()); + + btVector3 ray_normal(ray_transform.getOrigin() - to.getOrigin()); ray_normal.normalize(); - ray_normal *= -1; - resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); + btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1)); + + if (depth >= -RAY_STABILITY_MARGIN) + depth = 0; + + resultOut->addContactPoint(ray_normal, btResult.m_hitPointWorld, depth); } } diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h index c716c1d88d..7383dad2bf 100644 --- a/modules/bullet/godot_ray_world_algorithm.h +++ b/modules/bullet/godot_ray_world_algorithm.h @@ -49,7 +49,7 @@ class GodotRayWorldAlgorithm : public btActivatingCollisionAlgorithm { bool m_isSwapped; public: - GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *m_world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped); + GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped); virtual ~GodotRayWorldAlgorithm(); virtual void processCollision(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, const btDispatcherInfo &dispatchInfo, btManifoldResult *resultOut); diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index b18965a5b8..e1b0b1b421 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -205,6 +205,6 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult { return m_pointCollisionObject; } - virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorld, btScalar depth); + virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth); }; #endif // GODOT_RESULT_CALLBACKS_H diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 3edc407e87..96a53f9f8b 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -955,7 +955,8 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { const bool isDynamic = p_mass != 0.f; if (isDynamic) { - ERR_FAIL_COND(PhysicsServer::BODY_MODE_RIGID != mode && PhysicsServer::BODY_MODE_CHARACTER != mode); + if (PhysicsServer::BODY_MODE_RIGID != mode && PhysicsServer::BODY_MODE_CHARACTER != mode) + return; m_isStatic = false; compoundShape->calculateLocalInertia(p_mass, localInertia); @@ -975,7 +976,8 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { } } else { - ERR_FAIL_COND(PhysicsServer::BODY_MODE_STATIC != mode && PhysicsServer::BODY_MODE_KINEMATIC != mode); + if (PhysicsServer::BODY_MODE_STATIC != mode && PhysicsServer::BODY_MODE_KINEMATIC != mode) + return; m_isStatic = true; if (PhysicsServer::BODY_MODE_STATIC == mode) { diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index aff6056ad9..c4a9676bdd 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -262,12 +262,12 @@ public: Variant get_state(PhysicsServer::BodyState p_state) const; void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); - void apply_central_impulse(const Vector3 &p_force); + void apply_central_impulse(const Vector3 &p_impulse); void apply_torque_impulse(const Vector3 &p_impulse); void apply_force(const Vector3 &p_force, const Vector3 &p_pos); void apply_central_force(const Vector3 &p_force); - void apply_torque(const Vector3 &p_force); + void apply_torque(const Vector3 &p_torque); void set_applied_force(const Vector3 &p_force); Vector3 get_applied_force() const; diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h index 4a03c0f014..e04a3c808a 100644 --- a/modules/bullet/shape_bullet.h +++ b/modules/bullet/shape_bullet.h @@ -99,7 +99,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Plane &p_plane); @@ -116,7 +116,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_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_radius); @@ -133,7 +133,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Vector3 &p_half_extents); @@ -152,7 +152,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_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); @@ -169,7 +169,7 @@ public: void get_vertices(Vector<Vector3> &out_vertices); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(const Vector<Vector3> &p_vertices); @@ -187,7 +187,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(PoolVector<Vector3> p_faces); @@ -206,7 +206,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_scale, real_t p_margin = 0); + virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0); private: void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size); @@ -222,7 +222,7 @@ public: virtual void set_data(const Variant &p_data); virtual Variant get_data() const; virtual PhysicsServer::ShapeType get_type() const; - virtual btCollisionShape *create_bt_shape(const btVector3 &p_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_length); diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index d60d8ba0e2..6f0cda8957 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -116,7 +116,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V } } -int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *p_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) { +int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) { if (p_result_max <= 0) return 0; @@ -138,7 +138,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra collision_object.setCollisionShape(btConvex); collision_object.setWorldTransform(bt_xform); - GodotAllContactResultCallback btQuery(&collision_object, p_results, p_result_max, &p_exclude); + GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude); btQuery.m_collisionFilterGroup = 0; btQuery.m_collisionFilterMask = p_collision_mask; btQuery.m_closestDistanceThreshold = 0; diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 0aeb407dcc..2b97f0b274 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -199,12 +199,12 @@ private: local_shape_most_recovered(0) {} }; - bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_from, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); /// This is an API that recover a kinematic object from penetration /// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions - bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); /// This is an API that recover a kinematic object from penetration /// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm - bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); }; #endif diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 3b1e8b73c4..23ee327cc5 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.0-rc1"> +<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" category="Core" version="3.0-stable"> <brief_description> PacketPeer implementation using the ENet library. </brief_description> diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index fbd5201470..bceb4f1f4c 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0-rc1"> +<class name="ARVRInterfaceGDNative" inherits="ARVRInterface" category="Core" version="3.0-stable"> <brief_description> GDNative wrapper for an ARVR interface </brief_description> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index b0b50e8378..7e4d956604 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNative" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDNative" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index d30acaf65d..a6874c9ae8 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="GDNativeLibrary" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 7145a16931..f713e4112e 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NativeScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="NativeScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 18857e1939..fbdd8f09e6 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="PluginScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="PluginScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h index 94bc2adc7d..04d2911d8b 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.h +++ b/modules/gdnative/gdnative_library_editor_plugin.h @@ -79,7 +79,7 @@ protected: void _on_library_selected(const String &file); void _on_dependencies_selected(const PoolStringArray &files); void _on_filter_selected(int id); - void _on_item_collapsed(Object *item); + void _on_item_collapsed(Object *p_item); void _on_item_activated(); void _on_create_new_entry(); void _set_target_value(const String §ion, const String &target, Variant file); diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h index 6729eecb32..1be9e907c2 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.h +++ b/modules/gdnative/pluginscript/pluginscript_script.h @@ -112,7 +112,7 @@ public: virtual void update_exports(); virtual void get_script_method_list(List<MethodInfo> *r_methods) const; - virtual void get_script_property_list(List<PropertyInfo> *r_propertieslist) const; + virtual void get_script_property_list(List<PropertyInfo> *r_properties) const; virtual int get_member_line(const StringName &p_member) const; diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index ba7d56a3b2..59cb00e3f6 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="GDScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> A script implemented in the GDScript programming language. </brief_description> @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/scripting/gdscript/index.html + http://docs.godotengine.org/en/3.0/getting_started/scripting/gdscript/index.html </tutorials> <demos> </demos> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index e670635aa4..8510136f68 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> State of a function call after yielding. </brief_description> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index dfd4703f36..48826ec1e0 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="GDScriptNativeClass" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index f380bedf7f..1649fb52f2 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -37,6 +37,9 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN if (!codegen.function_node || codegen.function_node->_static) return false; + if (codegen.stack_identifiers.has(p_name)) + return false; //shadowed + return _is_class_member_property(codegen.script, p_name); } @@ -184,6 +187,14 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: StringName identifier = in->name; + // TRY STACK! + if (!p_initializer && codegen.stack_identifiers.has(identifier)) { + + int pos = codegen.stack_identifiers[identifier]; + return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS); + } + + // TRY CLASS MEMBER if (_is_class_member_property(codegen, identifier)) { //get property codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_MEMBER); // perform operator @@ -194,12 +205,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: return dst_addr; } - // TRY STACK! - if (!p_initializer && codegen.stack_identifiers.has(identifier)) { - - int pos = codegen.stack_identifiers[identifier]; - return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS); - } //TRY MEMBERS! if (!codegen.function_node || !codegen.function_node->_static) { @@ -1336,10 +1341,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(s); - if (_is_class_member_property(codegen, lv->name)) { - _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv); - return ERR_ALREADY_EXISTS; - } + // since we are using properties now for most class access, allow shadowing of class members to make user's life easier. + // + //if (_is_class_member_property(codegen, lv->name)) { + // _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv); + // return ERR_ALREADY_EXISTS; + //} codegen.add_stack_identifier(lv->name, p_stack_level++); codegen.alloc_stack(p_stack_level); @@ -1376,10 +1383,13 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser if (p_func) { for (int i = 0; i < p_func->arguments.size(); i++) { - if (_is_class_member_property(p_script, p_func->arguments[i])) { - _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func); - return ERR_ALREADY_EXISTS; - } + // since we are using properties now for most class access, allow shadowing of class members to make user's life easier. + // + //if (_is_class_member_property(p_script, p_func->arguments[i])) { + // _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func); + // return ERR_ALREADY_EXISTS; + //} + codegen.add_stack_identifier(p_func->arguments[i], i); #ifdef TOOLS_ENABLED argnames.push_back(p_func->arguments[i]); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 75fb7e15c4..1392323d56 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -95,8 +95,6 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { int indent = tokenizer->get_token_line_indent(); int current = tab_level.back()->get(); if (indent <= current) { - print_line("current: " + itos(current) + " indent: " + itos(indent)); - print_line("less than current"); return false; } @@ -460,7 +458,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s //this can be too slow for just validating code if (for_completion && ScriptCodeCompletionCache::get_singleton()) { res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); - } else if (FileAccess::exists(path)) { + } else { // essential; see issue 15902 res = ResourceLoader::load(path); } if (!res.is_valid()) { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 0073631a8b..44685220b3 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridMap" inherits="Spatial" category="Core" version="3.0-rc1"> +<class name="GridMap" inherits="Spatial" category="Core" version="3.0-stable"> <brief_description> Node for 3D tile-based maps. </brief_description> @@ -10,7 +10,7 @@ A GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/features/3d/using_gridmaps.html + http://docs.godotengine.org/en/3.0/tutorials/3d/using_gridmaps.html </tutorials> <demos> </demos> diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index b17fe43ae8..ed36751fc8 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -247,7 +247,7 @@ public: int get_cell_item(int p_x, int p_y, int p_z) const; int get_cell_item_orientation(int p_x, int p_y, int p_z) const; - Vector3 world_to_map(const Vector3 &p_pos) const; + Vector3 world_to_map(const Vector3 &p_world_pos) const; Vector3 map_to_world(int p_x, int p_y, int p_z) const; void set_clip(bool p_enabled, bool p_clip_above = true, int p_floor = 0, Vector3::Axis p_axis = Vector3::AXIS_X); diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index bc0622f66f..82300e707a 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0-rc1"> +<class name="MobileVRInterface" inherits="ARVRInterface" category="Core" version="3.0-stable"> <brief_description> Generic mobile VR implementation </brief_description> diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index aee2481366..0dc0018224 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -176,7 +176,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "fixed", "float", "for", - "forech", + "foreach", "goto", "if", "implicit", @@ -222,14 +222,17 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "ushort", "using", "virtual", - "volatile", "void", + "volatile", "while", // Contextual keywords. Not reserved words, but I guess we should include // them because this seems to be used only for syntax highlighting. "add", + "alias", "ascending", + "async", + "await", "by", "descending", "dynamic", @@ -238,10 +241,10 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "get", "global", "group", - "in", "into", "join", "let", + "nameof", "on", "orderby", "partial", @@ -250,6 +253,7 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const { "set", "value", "var", + "when", "where", "yield", 0 @@ -447,6 +451,7 @@ String CSharpLanguage::_get_indentation() const { Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { +#ifdef DEBUG_ENABLED // Printing an error here will result in endless recursion, so we must be careful if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) @@ -463,8 +468,12 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() si = stack_trace_get_info(stack_trace); return si; +#else + return Vector<StackInfo>(); +#endif } +#ifdef DEBUG_ENABLED Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { // Printing an error here could result in endless recursion, so we must be careful @@ -514,6 +523,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec return si; } +#endif void CSharpLanguage::frame() { @@ -941,19 +951,6 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) { #endif } -void CSharpInstance::_ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *p_klass, const StringName &p_method, const Variant **p_args, int p_argcount) { - - GDMonoClass *base = p_klass->get_parent_class(); - if (base && base != script->native) - _ml_call_reversed(p_mono_object, base, p_method, p_args, p_argcount); - - GDMonoMethod *method = p_klass->get_method(p_method, p_argcount); - - if (method) { - method->invoke(p_mono_object, p_args); - } -} - CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const Ref<MonoGCHandle> &p_gchandle) { CSharpInstance *instance = memnew(CSharpInstance); @@ -1022,6 +1019,8 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret) == true) return true; + + break; } top = top->get_parent_class(); @@ -1082,6 +1081,8 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { r_ret = GDMonoMarshal::mono_object_to_variant(ret); return true; } + + break; } top = top->get_parent_class(); @@ -1133,10 +1134,13 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, MonoObject *mono_object = get_mono_object(); - ERR_FAIL_NULL_V(mono_object, Variant()); + if (!mono_object) { + r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; + ERR_FAIL_V(Variant()); + } if (!script.is_valid()) - return Variant(); + ERR_FAIL_V(Variant()); GDMonoClass *top = script->script_class; @@ -1146,6 +1150,8 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, if (method) { MonoObject *return_value = method->invoke(mono_object, p_args); + r_error.error = Variant::CallError::CALL_OK; + if (return_value) { return GDMonoMarshal::mono_object_to_variant(return_value); } else { @@ -1179,8 +1185,10 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam while (top && top != script->native) { GDMonoMethod *method = top->get_method(p_method, p_argcount); - if (method) + if (method) { method->invoke(p_mono_object, p_args); + return; + } top = top->get_parent_class(); } @@ -1188,13 +1196,9 @@ void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringNam void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - if (script.is_valid()) { - MonoObject *mono_object = get_mono_object(); - - ERR_FAIL_NULL(mono_object); + // Sorry, the method is the one that controls the call order - _ml_call_reversed(mono_object, script->script_class, p_method, p_args, p_argcount); - } + call_multilevel(p_method, p_args, p_argcount); } void CSharpInstance::_reference_owner_unsafe() { @@ -1541,6 +1545,7 @@ bool CSharpScript::_update_exports() { return false; } +#ifdef TOOLS_ENABLED bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { StringName name = p_member->get_name(); @@ -1611,6 +1616,7 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, GDMonoClassMember *p return true; } +#endif void CSharpScript::_clear() { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 3ce8a9b64e..f18e339e18 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -105,7 +105,9 @@ class CSharpScript : public Script { void _clear(); bool _update_exports(); +#ifdef TOOLS_ENABLED bool _get_member_export(GDMonoClass *p_class, GDMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); +#endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); Variant _new(const Variant **p_args, int p_argcount, Variant::CallError &r_error); @@ -170,8 +172,6 @@ class CSharpInstance : public ScriptInstance { bool base_ref; bool ref_dying; - void _ml_call_reversed(MonoObject *p_mono_object, GDMonoClass *klass, const StringName &p_method, const Variant **p_args, int p_argcount); - void _reference_owner_unsafe(); void _unreference_owner_unsafe(); @@ -335,7 +335,9 @@ public: virtual void *alloc_instance_binding_data(Object *p_object); virtual void free_instance_binding_data(void *p_data); +#ifdef DEBUG_ENABLED Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace); +#endif CSharpLanguage(); ~CSharpLanguage(); diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index a9dccea3c1..0f33c76eb2 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@C#" category="Core" version="3.0-rc1"> +<class name="@C#" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 9a3631d2f2..3efe71f1b3 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="CSharpScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 219d041b67..1e5edf2a2a 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" category="Core" version="3.0-rc1"> +<class name="GodotSharp" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 9bf81b52bb..62c7a94755 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -250,8 +250,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); - String im_sig = "IntPtr " CS_PARAM_METHODBIND ", IntPtr " CS_PARAM_INSTANCE; - String im_unique_sig = imethod.return_type.operator String() + ",IntPtr,IntPtr"; + String im_sig; + String im_unique_sig; + + if (p_itype.is_object_type) { + im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; + im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr"; + } + + im_sig += "IntPtr " CS_PARAM_INSTANCE; // Get arguments information int i = 0; @@ -263,25 +270,37 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { im_sig += " arg"; im_sig += itos(i + 1); - im_unique_sig += ","; - im_unique_sig += get_unique_sig(*arg_type); + if (p_itype.is_object_type) { + im_unique_sig += ","; + im_unique_sig += get_unique_sig(*arg_type); + } i++; } - // godot_icall_{argc}_{icallcount} - String icall_method = ICALL_PREFIX + itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); + String icall_method = ICALL_PREFIX; + + if (p_itype.is_object_type) { + icall_method += itos(imethod.arguments.size()) + "_" + itos(method_icalls.size()); // godot_icall_{argc}_{icallcount} + } else { + icall_method += p_itype.name + "_" + imethod.name; // godot_icall_{Type}_{method} + } InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, return_type->im_type_out, im_sig, im_unique_sig); - List<InternalCall>::Element *match = method_icalls.find(im_icall); + if (p_itype.is_object_type) { + List<InternalCall>::Element *match = method_icalls.find(im_icall); - if (match) { - if (p_itype.api_type != ClassDB::API_EDITOR) - match->get().editor_only = false; - method_icalls_map.insert(&E->get(), &match->get()); + if (match) { + if (p_itype.api_type != ClassDB::API_EDITOR) + match->get().editor_only = false; + method_icalls_map.insert(&E->get(), &match->get()); + } else { + List<InternalCall>::Element *added = method_icalls.push_back(im_icall); + method_icalls_map.insert(&E->get(), &added->get()); + } } else { - List<InternalCall>::Element *added = method_icalls.push_back(im_icall); + List<InternalCall>::Element *added = builtin_method_icalls.push_back(im_icall); method_icalls_map.insert(&E->get(), &added->get()); } } @@ -525,6 +544,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); + for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL(E->get()); #undef ADD_INTERNAL_CALL @@ -616,6 +637,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir, cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ } + // No need to add builtin_method_icalls. Builtin types are core only + for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL(E->get()); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) @@ -694,9 +717,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(itype.is_singleton ? "static class " : "class "); output.push_back(itype.proxy_name); - if (itype.is_singleton || !itype.is_object_type) { + if (itype.is_singleton) { output.push_back("\n"); - } else if (!is_derived_type) { + } else if (!is_derived_type || !itype.is_object_type /* assuming only object types inherit */) { output.push_back(" : IDisposable\n"); } else if (obj_types.has(itype.base_name)) { output.push_back(" : "); @@ -838,7 +861,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 "if (disposed) return;\n" INDENT3 "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "NativeCalls.godot_icall_"); output.push_back(itype.proxy_name); @@ -929,7 +952,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(MEMBER_BEGIN "public void Dispose()\n" OPEN_BLOCK_L2 "Dispose(true);\n" INDENT3 "GC.SuppressFinalize(this);\n" CLOSE_BLOCK_L2); // Add the virtual Dispose - output.push_back(MEMBER_BEGIN "public virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 + output.push_back(MEMBER_BEGIN "protected virtual void Dispose(bool disposing)\n" OPEN_BLOCK_L2 "if (disposed) return;\n" INDENT3 "if (" BINDINGS_PTR_FIELD " != IntPtr.Zero)\n" OPEN_BLOCK_L3 "if (" CS_FIELD_MEMORYOWN ")\n" OPEN_BLOCK_L4 CS_FIELD_MEMORYOWN @@ -1122,10 +1145,14 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String method_bind_field = "method_bind_" + itos(p_method_bind_count); - String icall_params = method_bind_field + ", " + sformat(p_itype.cs_in, "this"); String arguments_sig; String cs_in_statements; + String icall_params; + if (p_itype.is_object_type) + icall_params += method_bind_field + ", "; + icall_params += sformat(p_itype.cs_in, "this"); + List<String> default_args_doc; // Retrieve information from the arguments @@ -1200,9 +1227,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Generate method { - if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.push_back(MEMBER_BEGIN "private "); - p_output.push_back(p_itype.is_singleton ? "static IntPtr " : "IntPtr "); + if (p_itype.is_object_type && !p_imethod.is_virtual && !p_imethod.requires_object_call) { + p_output.push_back(MEMBER_BEGIN "private static IntPtr "); p_output.push_back(method_bind_field + " = " CS_CLASS_NATIVECALLS "." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); p_output.push_back(p_imethod.name); p_output.push_back("\");\n"); @@ -1381,6 +1407,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back(itos(GDMono::get_singleton()->get_api_editor_hash()) + "; }\n#endif // TOOLS_ENABLED\n"); output.push_back("void register_generated_icalls() " OPEN_BLOCK); + output.push_back("\tgodot_register_header_icalls();"); #define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ { \ @@ -1443,6 +1470,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { output.push_back("#endif\n"); } + for (const List<InternalCall>::Element *E = builtin_method_icalls.front(); E; E = E->next()) + ADD_INTERNAL_CALL_REGISTRATION(E->get()); + #undef ADD_INTERNAL_CALL_REGISTRATION output.push_back(CLOSE_BLOCK "}\n"); @@ -1518,6 +1548,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte i++; } + if (!p_itype.is_object_type) + return OK; // no auto-generated icall functions for builtin types + const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod); ERR_FAIL_NULL_V(match, ERR_BUG); @@ -2113,36 +2146,34 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #undef INSERT_STRUCT_TYPE -#define INSERT_PRIMITIVE_TYPE(m_type) \ - { \ - itype = TypeInterface::create_value_type(String(#m_type)); \ - itype.c_arg_in = "&%s"; \ - itype.c_type_in = #m_type; \ - itype.c_type_out = #m_type; \ - itype.im_type_in = #m_type; \ - itype.im_type_out = #m_type; \ - builtin_types.insert(itype.cname, itype); \ - } - - INSERT_PRIMITIVE_TYPE(bool) - //INSERT_PRIMITIVE_TYPE(int) + // bool + itype = TypeInterface::create_value_type(String("bool")); + itype.c_arg_in = "&%s"; + // /* MonoBoolean <---> bool + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "bool"; + // */ + itype.c_type_in = "MonoBoolean"; + itype.c_type_out = itype.c_type_in; + itype.im_type_in = itype.name; + itype.im_type_out = itype.name; + builtin_types.insert(itype.cname, itype); // int itype = TypeInterface::create_value_type(String("int")); itype.c_arg_in = "&%s_in"; - //* ptrcall only supports int64_t and uint64_t + // /* ptrcall only supports int64_t and uint64_t itype.c_in = "\t%0 %1_in = (%0)%1;\n"; itype.c_out = "\treturn (%0)%1;\n"; itype.c_type = "int64_t"; - //*/ - itype.c_type_in = itype.name; - itype.c_type_out = itype.name; + // */ + itype.c_type_in = "int32_t"; + itype.c_type_out = itype.c_type_in; itype.im_type_in = itype.name; itype.im_type_out = itype.name; builtin_types.insert(itype.cname, itype); -#undef INSERT_PRIMITIVE_TYPE - // real_t itype = TypeInterface(); #ifdef REAL_T_IS_DOUBLE diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 8929b45cce..9b5a9cea88 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -441,6 +441,7 @@ class BindingsGenerator { Map<StringName, String> extra_members; List<InternalCall> method_icalls; + List<InternalCall> builtin_method_icalls; Map<const MethodInterface *, const InternalCall *> method_icalls_map; List<const InternalCall *> generated_icall_funcs; @@ -503,8 +504,8 @@ class BindingsGenerator { const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname); const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname); - void _default_argument_from_variant(const Variant &p_var, ArgumentInterface &r_iarg); - void _populate_builtin_type(TypeInterface &r_type, Variant::Type vtype); + void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); + void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); void _populate_object_type_interfaces(); void _populate_builtin_type_interfaces(); @@ -513,14 +514,14 @@ class BindingsGenerator { Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); - Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_prop_doc, List<String> &p_output); + Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output); Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output); void _generate_global_constants(List<String> &p_output); Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output); - Error _save_file(const String &path, const List<String> &content); + Error _save_file(const String &p_path, const List<String> &p_content); BindingsGenerator() {} diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 9e48da68c1..0ef3adfdd0 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -112,6 +112,21 @@ void GodotSharpEditor::_remove_create_sln_menu_option() { bottom_panel_btn->show(); } +void GodotSharpEditor::_show_about_dialog() { + + bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); + about_dialog_checkbox->set_pressed(show_on_start); + about_dialog->popup_centered_minsize(); +} + +void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) { + + bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start"); + if (show_on_start != p_enabled) { + EditorSettings::get_singleton()->set_setting("mono/editor/show_info_on_start", p_enabled); + } +} + void GodotSharpEditor::_menu_option_pressed(int p_id) { switch (p_id) { @@ -119,15 +134,37 @@ void GodotSharpEditor::_menu_option_pressed(int p_id) { _create_project_solution(); } break; + case MENU_ABOUT_CSHARP: { + + _show_about_dialog(); + } break; default: ERR_FAIL(); } } +void GodotSharpEditor::_notification(int p_notification) { + + switch (p_notification) { + + case NOTIFICATION_READY: { + + bool show_info_dialog = EDITOR_GET("mono/editor/show_info_on_start"); + if (show_info_dialog) { + about_dialog->set_exclusive(true); + _show_about_dialog(); + // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive then. + about_dialog->set_exclusive(false); + } + } + } +} + void GodotSharpEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution); ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option); + ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start); ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed); } @@ -210,6 +247,55 @@ GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) { menu_button->set_text(TTR("Mono")); menu_popup = menu_button->get_popup(); + // TODO: Remove or edit this info dialog once Mono support is no longer in alpha + { + menu_popup->add_item(TTR("About C# support"), MENU_ABOUT_CSHARP); + about_dialog = memnew(AcceptDialog); + editor->get_gui_base()->add_child(about_dialog); + about_dialog->set_title("Important: C# support is not feature-complete"); + + // We don't use set_text() as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox + // we'll add. Instead we add containers and a new autowrapped Label inside. + + // Main VBoxContainer (icon + label on top, checkbox at bottom) + VBoxContainer *about_vbc = memnew(VBoxContainer); + about_dialog->add_child(about_vbc); + + // HBoxContainer for icon + label + HBoxContainer *about_hbc = memnew(HBoxContainer); + about_vbc->add_child(about_hbc); + + TextureRect *about_icon = memnew(TextureRect); + about_hbc->add_child(about_icon); + Ref<Texture> about_icon_tex = about_icon->get_icon("NodeWarning", "EditorIcons"); + about_icon->set_texture(about_icon_tex); + + Label *about_label = memnew(Label); + about_hbc->add_child(about_label); + about_label->set_custom_minimum_size(Size2(600, 150) * EDSCALE); + about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); + about_label->set_autowrap(true); + String about_text = + String("C# support in Godot Engine is a brand new feature and a work in progress.\n") + + "It is at the alpha stage and thus not suitable for use in production.\n\n" + + "As of Godot 3.0, C# support is not feature-complete and can crash in some situations. " + + "Bugs and usability issues will be addressed gradually over 3.0.x and 3.x releases.\n" + + "The main missing feature is the ability to export games using C# assemblies - you will therefore be able to develop and run games in the editor, " + + "but not to share them as standalone binaries. This feature is of course high on the priority list and should be available in 3.0.1.\n\n" + + "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, Mono version, IDE, etc.:\n\n" + + " https://github.com/godotengine/godot/issues\n\n" + + "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!"; + about_label->set_text(about_text); + + EDITOR_DEF("mono/editor/show_info_on_start", true); + + // CheckBox in main container + about_dialog_checkbox = memnew(CheckBox); + about_vbc->add_child(about_dialog_checkbox); + about_dialog_checkbox->set_text("Show this warning when starting the editor"); + about_dialog_checkbox->connect("toggled", this, "_toggle_about_dialog_on_start"); + } + String sln_path = GodotSharpDirs::get_project_sln_path(); String csproj_path = GodotSharpDirs::get_project_csproj_path(); diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index 1b83bae1cd..81c49aec30 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -44,6 +44,8 @@ class GodotSharpEditor : public Node { PopupMenu *menu_popup; AcceptDialog *error_dialog; + AcceptDialog *about_dialog; + CheckBox *about_dialog_checkbox; ToolButton *bottom_panel_btn; @@ -54,17 +56,21 @@ class GodotSharpEditor : public Node { bool _create_project_solution(); void _remove_create_sln_menu_option(); + void _show_about_dialog(); + void _toggle_about_dialog_on_start(bool p_enabled); void _menu_option_pressed(int p_id); static GodotSharpEditor *singleton; protected: + void _notification(int p_notification); static void _bind_methods(); public: enum MenuOptions { - MENU_CREATE_SLN + MENU_CREATE_SLN, + MENU_ABOUT_CSHARP, }; enum ExternalEditor { diff --git a/modules/mono/editor/monodevelop_instance.h b/modules/mono/editor/monodevelop_instance.h index 7e8a76b595..552c10a61d 100644 --- a/modules/mono/editor/monodevelop_instance.h +++ b/modules/mono/editor/monodevelop_instance.h @@ -43,7 +43,7 @@ class MonoDevelopInstance { public: void execute(const Vector<String> &p_files); - void execute(const String &p_files); + void execute(const String &p_file); MonoDevelopInstance(const String &p_solution); }; diff --git a/modules/mono/glue/builtin_types_glue.h b/modules/mono/glue/builtin_types_glue.h new file mode 100644 index 0000000000..460de84b65 --- /dev/null +++ b/modules/mono/glue/builtin_types_glue.h @@ -0,0 +1,59 @@ +#ifndef BUILTIN_TYPES_GLUE_H +#define BUILTIN_TYPES_GLUE_H + +#include "core/node_path.h" +#include "core/rid.h" + +#include <mono/metadata/object.h> + +#include "../mono_gd/gd_mono_marshal.h" + +MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) { + return (MonoBoolean)p_ptr->is_absolute(); +} + +uint32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) { + return p_ptr->get_name_count(); +} + +MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx)); +} + +uint32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) { + return p_ptr->get_subname_count(); +} + +MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx)); +} + +MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) { + return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames()); +} + +NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) { + return memnew(NodePath(p_ptr->get_as_property_path())); +} + +MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) { + return (MonoBoolean)p_ptr->is_empty(); +} + +uint32_t godot_icall_RID_get_id(RID *p_ptr) { + return p_ptr->get_id(); +} + +void godot_register_builtin_type_icalls() { + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute); + mono_add_internal_call("Godot.NativeCalls::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty); + mono_add_internal_call("Godot.NativeCalls::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id); +} + +#endif // BUILTIN_TYPES_GLUE_H diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 6951ace4fc..476396e9a3 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -71,7 +71,7 @@ namespace Godot public static int Decimals(float step) { - return Decimals(step); + return Decimals((decimal)step); } public static int Decimals(decimal step) diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index 32988c5afa..cedc8e9992 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "builtin_types_glue.h" + #include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" #include "../mono_gd/gd_mono_internals.h" @@ -91,12 +93,6 @@ MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) { return GDMonoMarshal::mono_string_from_godot(p_np->operator String()); } -MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { - Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); - // TODO Check possible Array/Vector<uint8_t> problem? - return GDMonoMarshal::Array_to_mono_array(Variant(ret)); -} - // -- RID -- RID *godot_icall_RID_Ctor(Object *p_from) { @@ -115,6 +111,12 @@ void godot_icall_RID_Dtor(RID *p_ptr) { // -- String -- +MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) { + Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer(); + // TODO Check possible Array/Vector<uint8_t> problem? + return GDMonoMarshal::Array_to_mono_array(Variant(ret)); +} + MonoString *godot_icall_String_md5_text(MonoString *p_str) { String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text(); return GDMonoMarshal::mono_string_from_godot(ret); @@ -303,3 +305,7 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) { return GDMonoUtils::create_managed_for_godot_object(CACHED_CLASS(WeakRef), Reference::get_class_static(), Object::cast_to<Object>(wref.ptr())); } + +void godot_register_header_icalls() { + godot_register_builtin_type_icalls(); +} diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 6c07c90f79..f5febd415b 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -696,11 +696,13 @@ bool _GodotSharp::is_domain_loaded() { return GDMono::get_singleton()->get_scripts_domain() != NULL; } -#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ - m_queue.push_back(m_inst); \ - if (queue_empty) { \ - queue_empty = false; \ - call_deferred("_dispose_callback"); \ +#define ENQUEUE_FOR_DISPOSAL(m_queue, m_inst) \ + m_queue.push_back(m_inst); \ + if (queue_empty) { \ + queue_empty = false; \ + if (!is_finalizing_domain()) { /* call_deferred may not be safe here */ \ + call_deferred("_dispose_callback"); \ + } \ } void _GodotSharp::queue_dispose(MonoObject *p_mono_object, Object *p_object) { diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index ba56ed6ed5..ef39b8549d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -116,6 +116,37 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse } } + String name = mono_assembly_name_get_name(aname); + bool has_extension = name.ends_with(".dll"); + + if (has_extension ? name == "mscorlib.dll" : name == "mscorlib") { + GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name); + if (stored_assembly) return (*stored_assembly)->get_assembly(); + + String path; + MonoAssembly *res = NULL; + + for (int i = 0; i < search_dirs.size(); i++) { + const String &search_dir = search_dirs[i]; + + if (has_extension) { + path = search_dir.plus_file(name); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name.get_basename(), path); + break; + } + } else { + path = search_dir.plus_file(name + ".dll"); + if (FileAccess::exists(path)) { + res = _load_assembly_from(name, path); + break; + } + } + } + + if (res) return res; + } + return NULL; } diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 8e7aa701bf..8a5fa19626 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -110,7 +110,7 @@ public: _FORCE_INLINE_ String get_path() const { return path; } _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; } - GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_class); + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); GDMonoClass *get_class(MonoClass *p_mono_class); GDMonoClass *get_object_derived_class(const StringName &p_class); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index f5895be144..afccf2fc63 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -125,7 +125,7 @@ public: GDMonoMethod *get_method(MonoMethod *p_raw_method); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name); GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); - GDMonoMethod *get_method_with_desc(const String &p_description, bool p_includes_namespace); + GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); GDMonoField *get_field(const StringName &p_name); const Vector<GDMonoField *> &get_all_fields(); diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index bc5a1d3a39..0fe527b199 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -139,7 +139,7 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { - MonoMethod *prop_method = mono_property_get_get_method(mono_property); + MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 835a4614c1..a2f0819a72 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -184,7 +184,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color)); CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane)); CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); - CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(NodePath)); + CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 2666433170..259da46c31 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -43,7 +43,7 @@ namespace GDMonoUtils { typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); -typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **); +typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 2671e9a970..b9d8520ac9 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -102,7 +102,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); MonoObject *ex = NULL; - thunk(get_target(), &signal_args, &ex); + thunk(get_target(), signal_args, &ex); if (ex) { mono_print_unhandled_exception(ex); diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index fb7d7e477c..2cf80acd28 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegEx" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="RegEx" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Class for searching text for patterns using regular expressions. </brief_description> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 6870c64246..9eba0f738b 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegExMatch" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="RegExMatch" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> Contains the results of a regex search. </brief_description> diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 6a6ee390cc..18ab616826 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -115,7 +115,7 @@ void AudioStreamPlaybackOGGVorbis::seek(float p_time) { if (!active) return; - if (p_time >= get_length()) { + if (p_time >= vorbis_stream->get_length()) { p_time = 0; } frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time); @@ -123,11 +123,6 @@ void AudioStreamPlaybackOGGVorbis::seek(float p_time) { stb_vorbis_seek(ogg_stream, frames_mixed); } -float AudioStreamPlaybackOGGVorbis::get_length() const { - - return vorbis_stream->length; -} - AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { if (ogg_alloc.alloc_buffer) { stb_vorbis_close(ogg_stream); @@ -261,6 +256,11 @@ float AudioStreamOGGVorbis::get_loop_offset() const { return loop_offset; } +float AudioStreamOGGVorbis::get_length() const { + + return length; +} + void AudioStreamOGGVorbis::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data", "data"), &AudioStreamOGGVorbis::set_data); diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index bb01c26902..d7bc7cc0d7 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -71,8 +71,6 @@ public: virtual float get_playback_position() const; virtual void seek(float p_time); - virtual float get_length() const; //if supported, otherwise return 0 - AudioStreamPlaybackOGGVorbis() {} ~AudioStreamPlaybackOGGVorbis(); }; @@ -112,6 +110,8 @@ public: void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; + virtual float get_length() const; //if supported, otherwise return 0 + AudioStreamOGGVorbis(); virtual ~AudioStreamOGGVorbis(); }; diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index 89cded2e91..827e947a79 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.0-rc1"> +<class name="AudioStreamOGGVorbis" inherits="AudioStream" category="Core" version="3.0-stable"> <brief_description> OGG Vorbis audio stream driver. </brief_description> diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml index 12598c436e..9a095c3ddd 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml index 266e38021e..a280d767c3 100644 --- a/modules/theora/doc_classes/ResourceImporterTheora.xml +++ b/modules/theora/doc_classes/ResourceImporterTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index b2762707d3..9da3dc0d02 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.0-rc1"> +<class name="VideoStreamTheora" inherits="VideoStream" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index a037344c70..a6a43f31b8 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScript" inherits="Script" category="Core" version="3.0-rc1"> +<class name="VisualScript" inherits="Script" category="Core" version="3.0-stable"> <brief_description> A script implemented in the Visual Script programming environment. </brief_description> @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - http://docs.godotengine.org/en/3.0/learning/scripting/visual_script/index.html + http://docs.godotengine.org/en/3.0/getting_started/scripting/visual_script/index.html </tutorials> <demos> </demos> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index 0e52d22b1c..d63a6ad524 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node representing a constant from the base types. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 13950ba6f9..da4db29086 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node used to call built-in functions. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index 3b5419871f..189a6f6ad8 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptClassConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a constant from a given class. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index 321f29ba65..5462c379ad 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptComment" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node used to annotate the script. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index 398ec40fe0..bb70a17357 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptCondition" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which branches the flow. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index 22e4b89fd6..e2ccb50bfd 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a contant's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index f6365c3e06..da6779b79d 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptConstructor" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which calls a base type constructor. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 14f32f5fa6..33d2f1437a 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptCustomNode" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A scripted Visual Script node. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index b062cf9ed7..09fcba4314 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node which deconstructs a base type instance into its parts. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index d39b3432d1..8e26758a31 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.0-rc1"> +<class name="VisualScriptEditor" inherits="Object" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index f73c0abb94..30f96011d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Emits a specified signal. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 4400889082..0dc0cdf5eb 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> A Visual Script node returning a singleton from [@GlobalScope] </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index dfb642d7c4..91f55edb2b 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptExpression" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index f9b43d359a..bd59d739ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunction" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index e431e1f15b..e2b732a250 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index 928c715562..614176498a 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.0-rc1"> +<class name="VisualScriptFunctionState" inherits="Reference" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index ac3ee0feaa..a36f7809c2 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 1b1aacabea..b2d0a194e0 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIndexGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index d9e4e858ff..7ad200afa4 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIndexSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index 9a37617545..45c493887b 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptInputAction" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index b42fb01be6..28e8a66182 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptIterator" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Steps through items in a given input. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index 7420912aad..66faf448cb 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptLocalVar" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index 992ba94bfd..8a816e5dd7 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Changes a local variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index 41855f312f..45fa471c41 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptMathConstant" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Commonly used mathematical constants. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index fd52b7fa18..e9d1cd949f 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.0-rc1"> +<class name="VisualScriptNode" inherits="Resource" category="Core" version="3.0-stable"> <brief_description> A node which is part of a [VisualScript]. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index 675fee9cbd..4538bd3c78 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptOperator" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index 4d4a42b8f0..3dae0e4b81 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPreload" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Creates a new [Resource] or loads one from the filesystem. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index c9ff8e40f4..7555c83960 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index 45032e391f..dc6a9efd83 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptPropertySet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index b98502946e..3789626ed0 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptResourcePath" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index 4b7141e6c7..1172b7555b 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptReturn" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Exits a function and returns an optional value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index c70badffc8..4c6181e040 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSceneNode" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Node reference. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index e9d8021652..68cc0d0b55 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSceneTree" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index 64fec422d9..017efdb07a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSelect" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Chooses between two input values. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index c0cd15f04f..e9b480bbae 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSelf" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Outputs a reference to the current instance. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index 29b77fbd01..be793ae36e 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSequence" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Executes a series of Sequence ports. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 9a34c5d252..85db63b78a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSubCall" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index c17be9b874..ec7565b31a 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptSwitch" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Branches program flow based on a given input's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index d2bebff7d4..d414a95657 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptTypeCast" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 52a3fb76be..ccd2918ec8 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptVariableGet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Gets a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index 12755784c0..e1fc1ba762 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptVariableSet" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Changes a variable's value. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index 505dfd00d2..de1ff45746 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptWhile" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> Conditional loop. </brief_description> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index aaa45fdb85..f21b53861a 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptYield" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index e0ae2c0bba..5075fb6ded 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.0-rc1"> +<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml index 0d04e59a70..20e0e48187 100644 --- a/modules/webm/doc_classes/ResourceImporterWebm.xml +++ b/modules/webm/doc_classes/ResourceImporterWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.0-rc1"> +<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index ceb2348d10..94aea5c8d2 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.0-rc1"> +<class name="VideoStreamWebm" inherits="VideoStream" category="Core" version="3.0-stable"> <brief_description> </brief_description> <description> diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub new file mode 100644 index 0000000000..067a99ffff --- /dev/null +++ b/modules/websocket/SCsub @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +# Thirdparty source files + +env_lws = env_modules.Clone() + +thirdparty_dir = "#thirdparty/lws/" +helper_dir = "win32helpers/" +openssl_dir = "#thirdparty/openssl/" +thirdparty_sources = [ + "client/client.c", + "client/client-handshake.c", + "client/client-parser.c", + "client/ssl-client.c", + + "ext/extension.c", + "ext/extension-permessage-deflate.c", + + "server/fops-zip.c", + "server/lejp-conf.c", + "server/parsers.c", + "server/ranges.c", + "server/server.c", + "server/server-handshake.c", + "server/ssl-server.c", + + "misc/base64-decode.c", + "misc/lejp.c", + "misc/sha-1.c", + + "alloc.c", + "context.c", + "handshake.c", + "header.c", + "libwebsockets.c", + "minilex.c", + "output.c", + "pollfd.c", + "service.c", + "ssl.c", + +] + +if env_lws["platform"] == "android": # Builtin getifaddrs + thirdparty_sources += ["misc/getifaddrs.c"] + +if env_lws["platform"] == "windows": # Winsock + thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"] +else: # Unix socket + thirdparty_sources += ["plat/lws-plat-unix.c"] + + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +if env_lws["platform"] == "javascript": # No need to add third party libraries at all + pass +else: + env_lws.add_source_files(env.modules_sources, thirdparty_sources) + env_lws.Append(CPPPATH=[thirdparty_dir]) + + if env['builtin_openssl']: + env_lws.Append(CPPPATH=[openssl_dir]) + + if env_lws["platform"] == "windows": + env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) + +env_lws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/config.py b/modules/websocket/config.py new file mode 100644 index 0000000000..fb920482f5 --- /dev/null +++ b/modules/websocket/config.py @@ -0,0 +1,7 @@ + +def can_build(platform): + return True + + +def configure(env): + pass diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp new file mode 100644 index 0000000000..38fe520fc1 --- /dev/null +++ b/modules/websocket/emws_client.cpp @@ -0,0 +1,224 @@ +/*************************************************************************/ +/* emws_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_client.h" +#include "core/io/ip.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_connect(String(proto)); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + + static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); + client->_on_peer_packet(); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_error(); +} + +EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) { + EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_is_connecting = false; + client->_on_disconnect(); +} +} + +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + + String str = "ws://"; + String proto_string = ""; + int i = 0; + + if (p_ssl) + str = "wss://"; + str += p_host + ":" + itos(p_port) + p_path; + for (int i = 0; i < p_protocols.size(); i++) { + proto_string += p_protocols[i]; + proto_string += ","; + } + if (proto_string == "") + proto_string = "binary,"; + + proto_string = proto_string.substr(0, proto_string.length() - 1); + + _is_connecting = true; + /* clang-format off */ + int peer_sock = EM_ASM_INT({ + var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(",")); + var c_ptr = Module.IDHandler.get($0); + socket.binaryType = "arraybuffer"; + + // Connection opened + socket.addEventListener("open", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + ccall("_esws_on_connect", + "void", + ["number", "string"], + [c_ptr, socket.protocol] + ); + }); + + // Listen for messages + socket.addEventListener("message", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + var buffer; + var is_string = 0; + if (event.data instanceof ArrayBuffer) { + + buffer = new Uint8Array(event.data); + + } else if (event.data instanceof Blob) { + + alert("Blob type not supported"); + return; + + } else if (typeof event.data === "string") { + + is_string = 1; + var enc = new TextEncoder("utf-8"); + buffer = new Uint8Array(enc.encode(event.data)); + + } else { + + alert("Unknown message type"); + return; + + } + var len = buffer.length*buffer.BYTES_PER_ELEMENT; + var out = Module._malloc(len); + Module.HEAPU8.set(buffer, out); + ccall("_esws_on_message", + "void", + ["number", "number", "number", "number"], + [c_ptr, out, len, is_string] + ); + Module._free(out); + }); + + socket.addEventListener("error", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + ccall("_esws_on_error", + "void", + ["number"], + [c_ptr] + ); + }); + + socket.addEventListener("close", function (event) { + if (!Module.IDHandler.has($0)) + return; // Godot Object is gone! + var was_clean = 0; + if (event.was_clean) + was_clean = 1; + ccall("_esws_on_close", + "void", + ["number", "number", "string", "number"], + [c_ptr, event.code, event.reason, was_clean] + ); + }); + + return Module.IDHandler.add(socket); + }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data()); + /* clang-format on */ + + static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock); + + return OK; +}; + +void EMWSClient::poll() { +} + +Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const { + + return _peer; +} + +NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const { + + if (_peer->is_connected_to_host()) + return CONNECTION_CONNECTED; + + if (_is_connecting) + return CONNECTION_CONNECTING; + + return CONNECTION_DISCONNECTED; +}; + +void EMWSClient::disconnect_from_host() { + + _peer->close(); +}; + +IP_Address EMWSClient::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t EMWSClient::get_connected_port() const { + + return 1025; +}; + +EMWSClient::EMWSClient() { + _is_connecting = false; + _peer = Ref<EMWSPeer>(memnew(EMWSPeer)); + /* clang-format off */ + _js_id = EM_ASM_INT({ + return Module.IDHandler.add($0); + }, this); + /* clang-format on */ +}; + +EMWSClient::~EMWSClient() { + + disconnect_from_host(); + _peer = Ref<EMWSPeer>(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h new file mode 100644 index 0000000000..8801f37007 --- /dev/null +++ b/modules/websocket/emws_client.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* emws_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 EMWSCLIENT_H +#define EMWSCLIENT_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "emws_peer.h" +#include "websocket_client.h" + +class EMWSClient : public WebSocketClient { + + GDCIIMPL(EMWSClient, WebSocketClient); + +private: + int _js_id; + +public: + bool _is_connecting; + + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Ref<WebSocketPeer> get_peer(int p_peer_id) const; + void disconnect_from_host(); + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + virtual ConnectionStatus get_connection_status() const; + virtual void poll(); + EMWSClient(); + ~EMWSClient(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // EMWSCLIENT_H diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp new file mode 100644 index 0000000000..93665e6428 --- /dev/null +++ b/modules/websocket/emws_peer.cpp @@ -0,0 +1,173 @@ +/*************************************************************************/ +/* emws_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_peer.h" +#include "core/io/ip.h" + +void EMWSPeer::set_sock(int p_sock) { + + peer_sock = p_sock; + in_buffer.clear(); + queue_count = 0; +} + +void EMWSPeer::set_write_mode(WriteMode p_mode) { + write_mode = p_mode; +} + +EMWSPeer::WriteMode EMWSPeer::get_write_mode() const { + return write_mode; +} + +void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) { + + if (in_buffer.space_left() < p_size + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL(); + } + + uint8_t is_string = p_is_string ? 1 : 0; + in_buffer.write((uint8_t *)&p_size, 4); + in_buffer.write((uint8_t *)&is_string, 1); + in_buffer.write(p_data, p_size); + queue_count++; +} + +Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; + + /* clang-format off */ + EM_ASM({ + var sock = Module.IDHandler.get($0); + var bytes_array = new Uint8Array($2); + var i = 0; + + for(i=0; i<$2; i++) { + bytes_array[i] = getValue($1+i, 'i8'); + } + + if ($3) { + sock.send(bytes_array.buffer); + } else { + var string = new TextDecoder("utf-8").decode(bytes_array); + sock.send(string); + } + }, peer_sock, p_buffer, p_buffer_size, is_bin); + /* clang-format on */ + + return OK; +}; + +Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + in_buffer.read((uint8_t *)&to_read, 4); + --queue_count; + left = in_buffer.data_left(); + + if (left < to_read + 1) { + in_buffer.advance_read(left); + return FAILED; + } + + in_buffer.read(&is_string, 1); + _was_string = is_string == 1; + in_buffer.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + + return OK; +}; + +int EMWSPeer::get_available_packet_count() const { + + return queue_count; +}; + +bool EMWSPeer::was_string_packet() const { + + return _was_string; +}; + +bool EMWSPeer::is_connected_to_host() const { + + return peer_sock != -1; +}; + +void EMWSPeer::close() { + + if (peer_sock != -1) { + /* clang-format off */ + EM_ASM({ + var sock = Module.IDHandler.get($0); + sock.close(); + Module.IDHandler.remove($0); + }, peer_sock); + /* clang-format on */ + } + peer_sock = -1; + queue_count = 0; + in_buffer.clear(); +}; + +IP_Address EMWSPeer::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t EMWSPeer::get_connected_port() const { + + return 1025; +}; + +EMWSPeer::EMWSPeer() { + peer_sock = -1; + queue_count = 0; + _was_string = false; + in_buffer.resize(16); + write_mode = WRITE_MODE_BINARY; +}; + +EMWSPeer::~EMWSPeer() { + + in_buffer.resize(0); + close(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h new file mode 100644 index 0000000000..a50d1874ba --- /dev/null +++ b/modules/websocket/emws_peer.h @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* emws_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 EMWSPEER_H +#define EMWSPEER_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "emscripten.h" +#include "websocket_peer.h" + +class EMWSPeer : public WebSocketPeer { + + GDCIIMPL(EMWSPeer, WebSocketPeer); + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type + }; + + int peer_sock; + WriteMode write_mode; + + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + RingBuffer<uint8_t> in_buffer; + int queue_count; + bool _was_string; + +public: + void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string); + void set_sock(int sock); + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + + virtual void close(); + virtual bool is_connected_to_host() const; + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual WriteMode get_write_mode() const; + virtual void set_write_mode(WriteMode p_mode); + virtual bool was_string_packet() const; + + void set_wsi(struct lws *wsi); + Error read_wsi(void *in, size_t len); + Error write_wsi(); + + EMWSPeer(); + ~EMWSPeer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LSWPEER_H diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp new file mode 100644 index 0000000000..60e9133225 --- /dev/null +++ b/modules/websocket/emws_server.cpp @@ -0,0 +1,67 @@ +/*************************************************************************/ +/* emws_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +#ifdef JAVASCRIPT_ENABLED + +#include "emws_server.h" +#include "core/os/os.h" + +Error EMWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { + + return FAILED; +} + +bool EMWSServer::is_listening() const { + return false; +} + +void EMWSServer::stop() { +} + +bool EMWSServer::has_peer(int p_id) const { + return false; +} + +Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const { + return NULL; +} + +PoolVector<String> EMWSServer::get_protocols() const { + PoolVector<String> out; + + return out; +} + +EMWSServer::EMWSServer() { +} + +EMWSServer::~EMWSServer() { +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h new file mode 100644 index 0000000000..59f1d76346 --- /dev/null +++ b/modules/websocket/emws_server.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* emws_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 EMWSSERVER_H +#define EMWSSERVER_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/reference.h" +#include "emws_peer.h" +#include "websocket_server.h" + +class EMWSServer : public WebSocketServer { + + GDCIIMPL(EMWSServer, WebSocketServer); + +public: + Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void stop(); + bool is_listening() const; + bool has_peer(int p_id) const; + Ref<WebSocketPeer> get_peer(int p_id) const; + virtual void poll(); + virtual PoolVector<String> get_protocols() const; + + EMWSServer(); + ~EMWSServer(); +}; + +#endif + +#endif // LWSSERVER_H diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp new file mode 100644 index 0000000000..604b1886ad --- /dev/null +++ b/modules/websocket/lws_client.cpp @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* lws_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 JAVASCRIPT_ENABLED + +#include "lws_client.h" +#include "core/io/ip.h" + +Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + + ERR_FAIL_COND_V(context != NULL, FAILED); + + IP_Address addr; + + if (!p_host.is_valid_ip_address()) { + addr = IP::get_singleton()->resolve_hostname(p_host); + } else { + addr = p_host; + } + + ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); + + // prepare protocols + if (p_protocols.size() == 0) // default to binary protocol + p_protocols.append("binary"); + _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref); + + // init lws client + struct lws_context_creation_info info; + struct lws_client_connect_info i; + + memset(&i, 0, sizeof i); + memset(&info, 0, sizeof info); + + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = _lws_ref->lws_structs; + info.gid = -1; + info.uid = -1; + //info.ws_ping_pong_interval = 5; + info.user = _lws_ref; + context = lws_create_context(&info); + + if (context == NULL) { + _lws_free_ref(_lws_ref); + _lws_ref = NULL; + ERR_EXPLAIN("Unable to create lws context"); + ERR_FAIL_V(FAILED); + } + + char abuf[1024]; + char hbuf[1024]; + char pbuf[2048]; + String addr_str = (String)addr; + strncpy(abuf, addr_str.ascii().get_data(), 1024); + strncpy(hbuf, p_host.utf8().get_data(), 1024); + strncpy(pbuf, p_path.utf8().get_data(), 2048); + + i.context = context; + i.protocol = _lws_ref->lws_names; + i.address = abuf; + i.host = hbuf; + i.path = pbuf; + i.port = p_port; + i.ssl_connection = p_ssl; + + lws_client_connect_via_info(&i); + return OK; +}; + +void LWSClient::poll() { + + _lws_poll(); +} + +int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + + Ref<LWSPeer> peer = static_cast<Ref<LWSPeer> >(_peer); + LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; + + switch (reason) { + + case LWS_CALLBACK_CLIENT_ESTABLISHED: + peer->set_wsi(wsi); + peer_data->peer_id = 0; + peer_data->in_size = 0; + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(16); + peer_data->rbr.resize(16); + peer_data->force_close = false; + _on_connect(lws_get_protocol(wsi)->name); + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + _on_error(); + destroy_context(); + return -1; // we should close the connection (would probably happen anyway) + + case LWS_CALLBACK_CLOSED: + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(0); + peer_data->rbr.resize(0); + peer->close(); + destroy_context(); + _on_disconnect(); + return 0; // we can end here + + case LWS_CALLBACK_CLIENT_RECEIVE: + peer->read_wsi(in, len); + if (peer->get_available_packet_count() > 0) + _on_peer_packet(); + break; + + case LWS_CALLBACK_CLIENT_WRITEABLE: + if (peer_data->force_close) + return -1; + + peer->write_wsi(); + break; + + default: + break; + } + + return 0; +} + +Ref<WebSocketPeer> LWSClient::get_peer(int p_peer_id) const { + + return _peer; +} + +NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() const { + + if (context == NULL) + return CONNECTION_DISCONNECTED; + + if (_peer->is_connected_to_host()) + return CONNECTION_CONNECTED; + + return CONNECTION_CONNECTING; +} + +void LWSClient::disconnect_from_host() { + + if (context == NULL) + return; + + _peer->close(); + destroy_context(); +}; + +IP_Address LWSClient::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t LWSClient::get_connected_port() const { + + return 1025; +}; + +LWSClient::LWSClient() { + context = NULL; + _lws_ref = NULL; + _peer = Ref<LWSPeer>(memnew(LWSPeer)); +}; + +LWSClient::~LWSClient() { + + invalidate_lws_ref(); // We do not want any more callback + disconnect_from_host(); + _peer = Ref<LWSPeer>(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h new file mode 100644 index 0000000000..2e082175df --- /dev/null +++ b/modules/websocket/lws_client.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* lws_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 LWSCLIENT_H +#define LWSCLIENT_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "lws_helper.h" +#include "lws_peer.h" +#include "websocket_client.h" + +class LWSClient : public WebSocketClient { + + GDCIIMPL(LWSClient, WebSocketClient); + + LWS_HELPER(LWSClient); + +public: + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Ref<WebSocketPeer> get_peer(int p_peer_id) const; + void disconnect_from_host(); + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + virtual ConnectionStatus get_connection_status() const; + virtual void poll(); + + LWSClient(); + ~LWSClient(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LWSCLIENT_H diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h new file mode 100644 index 0000000000..ac0c340aa9 --- /dev/null +++ b/modules/websocket/lws_helper.h @@ -0,0 +1,214 @@ +/*************************************************************************/ +/* lws_helper.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 LWS_HELPER_H +#define LWS_HELPER_H + +#include "core/io/stream_peer.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/ring_buffer.h" +#include "lws_peer.h" + +struct _LWSRef { + bool free_context; + bool is_polling; + bool is_valid; + bool is_destroying; + void *obj; + struct lws_protocols *lws_structs; + char *lws_names; +}; + +static _LWSRef *_lws_create_ref(void *obj) { + + _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); + out->is_destroying = false; + out->free_context = false; + out->is_polling = false; + out->obj = obj; + out->is_valid = true; + out->lws_structs = NULL; + out->lws_names = NULL; + return out; +} + +static void _lws_free_ref(_LWSRef *ref) { + // Free strings and structs + memfree(ref->lws_structs); + memfree(ref->lws_names); + // Free ref + memfree(ref); +} + +static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { + if (context == NULL || ref->is_destroying) + return false; + + if (ref->is_polling) { + ref->free_context = true; + return false; + } + + ref->is_destroying = true; + lws_context_destroy(context); + _lws_free_ref(ref); + return true; +} + +static bool _lws_poll(struct lws_context *context, _LWSRef *ref) { + + ERR_FAIL_COND_V(context == NULL, false); + ERR_FAIL_COND_V(ref == NULL, false); + + ref->is_polling = true; + lws_service(context, 0); + ref->is_polling = false; + + if (!ref->free_context) + return false; // Nothing to do + + bool is_valid = ref->is_valid; // Might have been destroyed by poll + + _lws_destroy(context, ref); // Will destroy context and ref + + return is_valid; // If the object should NULL its context and ref +} + +/* + * prepare the protocol_structs to be fed to context + * also prepare the protocol string used by the client + */ +static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { + /* the input strings might go away after this call, + * we need to copy them. Will clear them when + * detroying the context */ + int i; + int len = p_names.size(); + size_t data_size = sizeof(struct LWSPeer::PeerData); + PoolVector<String>::Read pnr = p_names.read(); + + /* + * This is a reference connecting the object with lws + * keep track of status, mallocs, etc. + * Must survive as long the context + * Must be freed manually when context creation fails. + */ + _LWSRef *ref = _lws_create_ref(p_obj); + + /* LWS protocol structs */ + ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); + + CharString strings = p_names.join(",").ascii(); + int str_len = strings.length(); + + /* Joined string of protocols, double the size: comma separated first, NULL separated last */ + ref->lws_names = (char *)memalloc((str_len + 1) * 2); /* plus the terminator */ + + char *names_ptr = ref->lws_names; + struct lws_protocols *structs_ptr = ref->lws_structs; + + copymem(names_ptr, strings.get_data(), str_len); + names_ptr[str_len] = '\0'; /* NULL terminator */ + /* NULL terminated strings to be used in protocol structs */ + copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); + names_ptr[(str_len * 2) + 1] = '\0'; /* NULL terminator */ + int pos = str_len + 1; + + /* the first protocol is always http-only */ + structs_ptr[0].name = "http-only"; + structs_ptr[0].callback = p_callback; + structs_ptr[0].per_session_data_size = data_size; + structs_ptr[0].rx_buffer_size = 0; + /* add user defined protocols */ + for (i = 0; i < len; i++) { + structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; + structs_ptr[i + 1].callback = p_callback; + structs_ptr[i + 1].per_session_data_size = data_size; + structs_ptr[i + 1].rx_buffer_size = 0; + pos += pnr[i].ascii().length() + 1; + names_ptr[pos - 1] = '\0'; + } + /* add protocols terminator */ + structs_ptr[len + 1].name = NULL; + structs_ptr[len + 1].callback = NULL; + structs_ptr[len + 1].per_session_data_size = 0; + structs_ptr[len + 1].rx_buffer_size = 0; + + *r_lws_ref = ref; +} + +/* clang-format off */ +#define LWS_HELPER(CNAME) \ +protected: \ + struct _LWSRef *_lws_ref; \ + struct lws_context *context; \ + \ + static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \ + \ + if (wsi == NULL) { \ + return 0; \ + } \ + \ + struct _LWSRef *ref = (struct _LWSRef *)lws_context_user(lws_get_context(wsi)); \ + if (!ref->is_valid) \ + return 0; \ + CNAME *helper = (CNAME *)ref->obj; \ + return helper->_handle_cb(wsi, reason, user, in, len); \ + } \ + \ + void invalidate_lws_ref() { \ + if (_lws_ref != NULL) \ + _lws_ref->is_valid = false; \ + } \ + \ + void destroy_context() { \ + if (_lws_destroy(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + } \ + } \ + \ +public: \ + virtual int _handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); \ + \ + void _lws_poll() { \ + ERR_FAIL_COND(context == NULL); \ + \ + if (::_lws_poll(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + } \ + } \ + \ +protected: + + /* clang-format on */ + +#endif // LWS_HELPER_H diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp new file mode 100644 index 0000000000..fdaa79f9d4 --- /dev/null +++ b/modules/websocket/lws_peer.cpp @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* lws_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 JAVASCRIPT_ENABLED + +#include "lws_peer.h" +#include "core/io/ip.h" + +void LWSPeer::set_wsi(struct lws *p_wsi) { + wsi = p_wsi; +}; + +void LWSPeer::set_write_mode(WriteMode p_mode) { + write_mode = p_mode; +} + +LWSPeer::WriteMode LWSPeer::get_write_mode() const { + return write_mode; +} + +Error LWSPeer::read_wsi(void *in, size_t len) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); + uint32_t size = peer_data->in_size; + uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + + if (peer_data->rbr.space_left() < len + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL_V(FAILED); + } + + copymem(&(peer_data->input_buffer[size]), in, len); + size += len; + + peer_data->in_size = size; + if (lws_is_final_fragment(wsi)) { + peer_data->rbr.write((uint8_t *)&size, 4); + peer_data->rbr.write((uint8_t *)&is_string, 1); + peer_data->rbr.write(peer_data->input_buffer, size); + peer_data->in_count++; + peer_data->in_size = 0; + } + + return OK; +} + +Error LWSPeer::write_wsi() { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); + PoolVector<uint8_t> tmp; + int left = peer_data->rbw.data_left(); + uint32_t to_write = 0; + + if (left == 0 || peer_data->out_count == 0) + return OK; + + peer_data->rbw.read((uint8_t *)&to_write, 4); + peer_data->out_count--; + + if (left < to_write) { + peer_data->rbw.advance_read(left); + return FAILED; + } + + tmp.resize(LWS_PRE + to_write); + peer_data->rbw.read(&(tmp.write()[LWS_PRE]), to_write); + lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode); + tmp.resize(0); + + if (peer_data->out_count > 0) + lws_callback_on_writable(wsi); // we want to write more! + + return OK; +} + +Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); + peer_data->rbw.write((uint8_t *)&p_buffer_size, 4); + peer_data->rbw.write(p_buffer, MIN(p_buffer_size, peer_data->rbw.space_left())); + peer_data->out_count++; + + lws_callback_on_writable(wsi); // notify that we want to write + return OK; +}; + +Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + + PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); + + if (peer_data->in_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + peer_data->rbr.read((uint8_t *)&to_read, 4); + peer_data->in_count--; + left = peer_data->rbr.data_left(); + + if (left < to_read + 1) { + peer_data->rbr.advance_read(left); + return FAILED; + } + + peer_data->rbr.read(&is_string, 1); + peer_data->rbr.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + _was_string = is_string; + + return OK; +}; + +int LWSPeer::get_available_packet_count() const { + + if (!is_connected_to_host()) + return 0; + + return ((PeerData *)lws_wsi_user(wsi))->in_count; +}; + +bool LWSPeer::was_string_packet() const { + + return _was_string; +}; + +bool LWSPeer::is_connected_to_host() const { + + return wsi != NULL; +}; + +void LWSPeer::close() { + if (wsi != NULL) { + struct lws *tmp = wsi; + PeerData *data = ((PeerData *)lws_wsi_user(wsi)); + data->force_close = true; + wsi = NULL; + lws_callback_on_writable(tmp); // notify that we want to disconnect + } +}; + +IP_Address LWSPeer::get_connected_host() const { + + return IP_Address(); +}; + +uint16_t LWSPeer::get_connected_port() const { + + return 1025; +}; + +LWSPeer::LWSPeer() { + wsi = NULL; + _was_string = false; + write_mode = WRITE_MODE_BINARY; +}; + +LWSPeer::~LWSPeer() { + + close(); +}; + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h new file mode 100644 index 0000000000..0a62b65d24 --- /dev/null +++ b/modules/websocket/lws_peer.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* lws_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 LWSPEER_H +#define LWSPEER_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "libwebsockets.h" +#include "lws_config.h" +#include "websocket_peer.h" + +class LWSPeer : public WebSocketPeer { + + GDCIIMPL(LWSPeer, WebSocketPeer); + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type + }; + + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + struct lws *wsi; + WriteMode write_mode; + bool _was_string; + +public: + struct PeerData { + uint32_t peer_id; + bool force_close; + RingBuffer<uint8_t> rbw; + RingBuffer<uint8_t> rbr; + mutable uint8_t input_buffer[PACKET_BUFFER_SIZE]; + uint32_t in_size; + int in_count; + int out_count; + }; + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; }; + + virtual void close(); + virtual bool is_connected_to_host() const; + virtual IP_Address get_connected_host() const; + virtual uint16_t get_connected_port() const; + + virtual WriteMode get_write_mode() const; + virtual void set_write_mode(WriteMode p_mode); + virtual bool was_string_packet() const; + + void set_wsi(struct lws *wsi); + Error read_wsi(void *in, size_t len); + Error write_wsi(); + + LWSPeer(); + ~LWSPeer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LSWPEER_H diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp new file mode 100644 index 0000000000..8a47ba557d --- /dev/null +++ b/modules/websocket/lws_server.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* lws_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 JAVASCRIPT_ENABLED + +#include "lws_server.h" +#include "core/os/os.h" + +Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { + + ERR_FAIL_COND_V(context != NULL, FAILED); + + _is_multiplayer = gd_mp_api; + + struct lws_context_creation_info info; + memset(&info, 0, sizeof info); + + if (p_protocols.size() == 0) // default to binary protocol + p_protocols.append(String("binary")); + + // Prepare lws protocol structs + _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref); + + info.port = p_port; + info.user = _lws_ref; + info.protocols = _lws_ref->lws_structs; + info.gid = -1; + info.uid = -1; + //info.ws_ping_pong_interval = 5; + + context = lws_create_context(&info); + + if (context == NULL) { + _lws_free_ref(_lws_ref); + _lws_ref = NULL; + ERR_EXPLAIN("Unable to create LWS context"); + ERR_FAIL_V(FAILED); + } + + return OK; +} + +bool LWSServer::is_listening() const { + return context != NULL; +} + +int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { + + LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; + + switch (reason) { + case LWS_CALLBACK_HTTP: + // no http for now + // closing immediately returning -1; + return -1; + + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + // check header here? + break; + + case LWS_CALLBACK_ESTABLISHED: { + int32_t id = _gen_unique_id(); + + Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); + peer->set_wsi(wsi); + _peer_map[id] = peer; + + peer_data->peer_id = id; + peer_data->in_size = 0; + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbw.resize(16); + peer_data->rbr.resize(16); + peer_data->force_close = false; + + _on_connect(id, lws_get_protocol(wsi)->name); + break; + } + + case LWS_CALLBACK_CLOSED: { + if (peer_data == NULL) + return 0; + int32_t id = peer_data->peer_id; + if (_peer_map.has(id)) { + _peer_map[id]->close(); + _peer_map.erase(id); + } + peer_data->in_count = 0; + peer_data->out_count = 0; + peer_data->rbr.resize(0); + peer_data->rbw.resize(0); + _on_disconnect(id); + return 0; // we can end here + } + + case LWS_CALLBACK_RECEIVE: { + int32_t id = peer_data->peer_id; + if (_peer_map.has(id)) { + static_cast<Ref<LWSPeer> >(_peer_map[id])->read_wsi(in, len); + if (_peer_map[id]->get_available_packet_count() > 0) + _on_peer_packet(id); + } + break; + } + + case LWS_CALLBACK_SERVER_WRITEABLE: { + if (peer_data->force_close) + return -1; + + int id = peer_data->peer_id; + if (_peer_map.has(id)) + static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi(); + break; + } + + default: + break; + } + + return 0; +} + +void LWSServer::stop() { + if (context == NULL) + return; + + _peer_map.clear(); + destroy_context(); + context = NULL; +} + +bool LWSServer::has_peer(int p_id) const { + return _peer_map.has(p_id); +} + +Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const { + ERR_FAIL_COND_V(!has_peer(p_id), NULL); + return _peer_map[p_id]; +} + +LWSServer::LWSServer() { + context = NULL; + _lws_ref = NULL; +} + +LWSServer::~LWSServer() { + invalidate_lws_ref(); // we do not want any more callbacks + stop(); +} + +#endif // JAVASCRIPT_ENABLED diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h new file mode 100644 index 0000000000..5f7ac4850a --- /dev/null +++ b/modules/websocket/lws_server.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* lws_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 LWSSERVER_H +#define LWSSERVER_H + +#ifndef JAVASCRIPT_ENABLED + +#include "core/reference.h" +#include "lws_helper.h" +#include "lws_peer.h" +#include "websocket_server.h" + +class LWSServer : public WebSocketServer { + + GDCIIMPL(LWSServer, WebSocketServer); + + LWS_HELPER(LWSServer); + +private: + Map<int, Ref<LWSPeer> > peer_map; + +public: + Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + void stop(); + bool is_listening() const; + bool has_peer(int p_id) const; + Ref<WebSocketPeer> get_peer(int p_id) const; + virtual void poll() { _lws_poll(); } + + LWSServer(); + ~LWSServer(); +}; + +#endif // JAVASCRIPT_ENABLED + +#endif // LWSSERVER_H diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp new file mode 100644 index 0000000000..39d03ff1f0 --- /dev/null +++ b/modules/websocket/register_types.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "register_types.h" +#include "error_macros.h" +#ifdef JAVASCRIPT_ENABLED +#include "emscripten.h" +#include "emws_client.h" +#include "emws_peer.h" +#include "emws_server.h" +#else +#include "lws_client.h" +#include "lws_peer.h" +#include "lws_server.h" +#endif + +void register_websocket_types() { +#ifdef JAVASCRIPT_ENABLED + EM_ASM({ + var IDHandler = {}; + IDHandler["ids"] = {}; + IDHandler["has"] = function(id) { + return IDHandler.ids.hasOwnProperty(id); + }; + IDHandler["add"] = function(obj) { + var id = crypto.getRandomValues(new Int32Array(32))[0]; + IDHandler.ids[id] = obj; + return id; + }; + IDHandler["get"] = function(id) { + return IDHandler.ids[id]; + }; + IDHandler["remove"] = function(id) { + delete IDHandler.ids[id]; + }; + Module["IDHandler"] = IDHandler; + }); + EMWSPeer::make_default(); + EMWSClient::make_default(); + EMWSServer::make_default(); +#else + LWSPeer::make_default(); + LWSClient::make_default(); + LWSServer::make_default(); +#endif + + ClassDB::register_virtual_class<WebSocketMultiplayerPeer>(); + ClassDB::register_custom_instance_class<WebSocketServer>(); + ClassDB::register_custom_instance_class<WebSocketClient>(); + ClassDB::register_custom_instance_class<WebSocketPeer>(); +} + +void unregister_websocket_types() {} diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h new file mode 100644 index 0000000000..010d88789b --- /dev/null +++ b/modules/websocket/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +void register_websocket_types(); +void unregister_websocket_types(); diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp new file mode 100644 index 0000000000..f92a386988 --- /dev/null +++ b/modules/websocket/websocket_client.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* websocket_client.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "websocket_client.h" + +GDCINULL(WebSocketClient); + +WebSocketClient::WebSocketClient() { +} + +WebSocketClient::~WebSocketClient() { +} + +Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) { + _is_multiplayer = gd_mp_api; + + String host = p_url; + String path = "/"; + int p_len = -1; + int port = 80; + bool ssl = false; + if (host.begins_with("wss://")) { + ssl = true; // we should implement this + host = host.substr(6, host.length() - 6); + port = 443; + } else { + ssl = false; + if (host.begins_with("ws://")) + host = host.substr(5, host.length() - 5); + } + + // Path + p_len = host.find("/"); + if (p_len != -1) { + path = host.substr(p_len, host.length() - p_len); + host = host.substr(0, p_len); + } + + // Port + p_len = host.find_last(":"); + if (p_len != -1 && p_len == host.find(":")) { + port = host.substr(p_len, host.length() - p_len).to_int(); + host = host.substr(0, p_len); + } + + return connect_to_host(host, path, port, ssl, p_protocols); +} + +bool WebSocketClient::is_server() const { + + return false; +} + +void WebSocketClient::_on_peer_packet() { + + if (_is_multiplayer) { + _process_multiplayer(get_peer(1), 1); + } else { + emit_signal("data_received"); + } +} + +void WebSocketClient::_on_connect(String p_protocol) { + + if (_is_multiplayer) { + // need to wait for ID confirmation... + } else { + emit_signal("connection_established", p_protocol); + } +} + +void WebSocketClient::_on_disconnect() { + + if (_is_multiplayer) { + emit_signal("connection_failed"); + } else { + emit_signal("connection_closed"); + } +} + +void WebSocketClient::_on_error() { + + if (_is_multiplayer) { + emit_signal("connection_failed"); + } else { + emit_signal("connection_error"); + } +} + +void WebSocketClient::_bind_methods() { + ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + + ADD_SIGNAL(MethodInfo("data_received")); + ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); + ADD_SIGNAL(MethodInfo("connection_closed")); + ADD_SIGNAL(MethodInfo("connection_error")); +} diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h new file mode 100644 index 0000000000..0e87825222 --- /dev/null +++ b/modules/websocket/websocket_client.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* websocket_client.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 WEBSOCKET_CLIENT_H +#define WEBSOCKET_CLIENT_H + +#include "core/error_list.h" +#include "websocket_multiplayer.h" +#include "websocket_peer.h" + +class WebSocketClient : public WebSocketMultiplayerPeer { + + GDCLASS(WebSocketClient, WebSocketMultiplayerPeer); + GDCICLASS(WebSocketClient); + +protected: + Ref<WebSocketPeer> _peer; + + static void _bind_methods(); + +public: + Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + + virtual void poll() = 0; + virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; + virtual void disconnect_from_host() = 0; + virtual IP_Address get_connected_host() const = 0; + virtual uint16_t get_connected_port() const = 0; + + virtual bool is_server() const; + virtual ConnectionStatus get_connection_status() const = 0; + + void _on_peer_packet(); + void _on_connect(String p_protocol); + void _on_disconnect(); + void _on_error(); + + WebSocketClient(); + ~WebSocketClient(); +}; + +#endif // WEBSOCKET_CLIENT_H diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h new file mode 100644 index 0000000000..b5c2159806 --- /dev/null +++ b/modules/websocket/websocket_macros.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* websocket_macros.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 WEBSOCKETMACTOS_H +#define WEBSOCKETMACTOS_H + +/* clang-format off */ +#define GDCICLASS(CNAME) \ +public:\ + static CNAME *(*_create)();\ +\ + static Ref<CNAME > create_ref() {\ +\ + if (!_create)\ + return Ref<CNAME >();\ + return Ref<CNAME >(_create());\ + }\ +\ + static CNAME *create() {\ +\ + if (!_create)\ + return NULL;\ + return _create();\ + }\ +protected:\ + +#define GDCINULL(CNAME) \ +CNAME *(*CNAME::_create)() = NULL; + +#define GDCIIMPL(IMPNAME, CNAME) \ +public:\ + static CNAME *_create() { return memnew(IMPNAME); }\ + static void make_default() { CNAME::_create = IMPNAME::_create; }\ +protected:\ +/* clang-format on */ + +#endif // WEBSOCKETMACTOS_H diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer.cpp new file mode 100644 index 0000000000..8cd4dff38b --- /dev/null +++ b/modules/websocket/websocket_multiplayer.cpp @@ -0,0 +1,361 @@ +/*************************************************************************/ +/* websocket_multiplayer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "websocket_multiplayer.h" +#include "core/os/os.h" + +WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() { + + _is_multiplayer = false; + _peer_id = 0; + _target_peer = 0; + _refusing = false; + + _current_packet.source = 0; + _current_packet.destination = 0; + _current_packet.size = 0; + _current_packet.data = NULL; +} + +WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() { + + _clear(); +} + +int WebSocketMultiplayerPeer::_gen_unique_id() const { + + uint32_t hash = 0; + + while (hash == 0 || hash == 1) { + + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_ticks_usec()); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_unix_time(), hash); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_data_path().hash64(), hash); + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)this), hash); //rely on aslr heap + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack + hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion + } + + return hash; +} +void WebSocketMultiplayerPeer::_clear() { + + _peer_map.clear(); + if (_current_packet.data != NULL) + memfree(_current_packet.data); + + for (List<Packet>::Element *E = _incoming_packets.front(); E; E = E->next()) { + memfree(E->get().data); + E->get().data = NULL; + } + + _incoming_packets.clear(); +} + +void WebSocketMultiplayerPeer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer); + + ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source"))); +} + +// +// PacketPeer +// +int WebSocketMultiplayerPeer::get_available_packet_count() const { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + return _incoming_packets.size(); +} + +int WebSocketMultiplayerPeer::get_max_packet_size() const { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + return MAX_PACKET_SIZE; +} + +Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + r_buffer_size = 0; + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + if (_current_packet.data != NULL) { + memfree(_current_packet.data); + _current_packet.data = NULL; + } + + _current_packet = _incoming_packets.front()->get(); + _incoming_packets.pop_front(); + + *r_buffer = _current_packet.data; + r_buffer_size = _current_packet.size; + + return OK; +} + +Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); + + PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size); + + if (is_server()) { + return _server_relay(1, _target_peer, &(buffer.read()[0]), buffer.size()); + } else { + return get_peer(1)->put_packet(&(buffer.read()[0]), buffer.size()); + } +} + +// +// NetworkedMultiplayerPeer +// +void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) { + + // Websocket uses TCP, reliable +} + +NetworkedMultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const { + + // Websocket uses TCP, reliable + return TRANSFER_MODE_RELIABLE; +} + +void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { + + _target_peer = p_target_peer; +} + +int WebSocketMultiplayerPeer::get_packet_peer() const { + + ERR_FAIL_COND_V(!_is_multiplayer, 1); + ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1); + + return _incoming_packets.front()->get().source; +} + +int WebSocketMultiplayerPeer::get_unique_id() const { + + return _peer_id; +} + +void WebSocketMultiplayerPeer::set_refuse_new_connections(bool p_enable) { + + _refusing = p_enable; +} + +bool WebSocketMultiplayerPeer::is_refusing_new_connections() const { + + return _refusing; +} + +void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) { + + ERR_FAIL_COND(!p_peer.is_valid()); + ERR_FAIL_COND(!p_peer->is_connected_to_host()); + + PoolVector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4); + p_peer->put_packet(&(message.read()[0]), message.size()); +} + +PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) { + + PoolVector<uint8_t> out; + out.resize(PROTO_SIZE + p_data_size); + + PoolVector<uint8_t>::Write w = out.write(); + copymem(&w[0], &p_type, 1); + copymem(&w[1], &p_from, 4); + copymem(&w[5], &p_to, 4); + copymem(&w[PROTO_SIZE], p_data, p_data_size); + + return out; +} + +void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) { + + // First of all, confirm the ID! + _send_sys(get_peer(p_peer_id), SYS_ID, p_peer_id); + + // Then send the server peer (which will trigger connection_succeded in client) + _send_sys(get_peer(p_peer_id), SYS_ADD, 1); + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + uint32_t id = E->key(); + if (p_peer_id == id) + continue; // Skip the newwly added peer (already confirmed) + + // Send new peer to others + _send_sys(get_peer(id), SYS_ADD, p_peer_id); + // Send others to new peer + _send_sys(get_peer(p_peer_id), SYS_ADD, id); + } +} + +void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) { + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + uint32_t id = E->key(); + if (p_peer_id != id) + _send_sys(get_peer(id), SYS_DEL, p_peer_id); + } +} + +void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) { + Packet packet; + packet.data = (uint8_t *)memalloc(p_data_size); + packet.size = p_data_size; + packet.source = p_source; + packet.destination = p_dest; + copymem(packet.data, &p_data[PROTO_SIZE], p_data_size); + _incoming_packets.push_back(packet); + emit_signal("peer_packet", p_source); +} + +Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) { + if (p_to == 1) { + + return OK; // Will not send to self + + } else if (p_to == 0) { + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + if (E->key() != p_from) + E->get()->put_packet(p_buffer, p_buffer_size); + } + return OK; // Sent to all but sender + + } else if (p_to < 0) { + + for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) { + if (E->key() != p_from && E->key() != -p_to) + E->get()->put_packet(p_buffer, p_buffer_size); + } + return OK; // Sent to all but sender and excluded + + } else { + + ERR_FAIL_COND_V(p_to == p_from, FAILED); + + return get_peer(p_to)->put_packet(p_buffer, p_buffer_size); // Sending to specific peer + } +} + +void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) { + + ERR_FAIL_COND(!p_peer.is_valid()); + + const uint8_t *in_buffer; + int size = 0; + int data_size = 0; + + Error err = p_peer->get_packet(&in_buffer, size); + + ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(size < PROTO_SIZE); + + data_size = size - PROTO_SIZE; + + uint8_t type = 0; + int32_t from = 0; + int32_t to = 0; + copymem(&type, in_buffer, 1); + copymem(&from, &in_buffer[1], 4); + copymem(&to, &in_buffer[5], 4); + + if (is_server()) { // Server can resend + + ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages + ERR_FAIL_COND(from != p_peer_id); // Someone is cheating + + _server_relay(from, to, in_buffer, size); // Relay if needed + + if (to == 1) { // This is for the server + + _store_pkt(from, to, in_buffer, data_size); + + } else if (to == 0) { + + // Broadcast, for us too + _store_pkt(from, to, in_buffer, data_size); + + } else if (to < 0) { + + // All but one, for us if not excluded + if (_peer_id != -p_peer_id) + _store_pkt(from, to, in_buffer, data_size); + + } else { + + // Send to specific peer + ERR_FAIL_COND(!_peer_map.has(to)); + get_peer(to)->put_packet(in_buffer, size); + } + + } else { + + if (type == SYS_NONE) { // Payload message + + _store_pkt(from, to, in_buffer, data_size); + return; + } + + // System message + ERR_FAIL_COND(data_size < 4); + int id = 0; + copymem(&id, &in_buffer[PROTO_SIZE], 4); + + switch (type) { + + case SYS_ADD: // Add peer + _peer_map[id] = Ref<WebSocketPeer>(); + emit_signal("peer_connected", id); + if (id == 1) // We just connected to the server + emit_signal("connection_succeeded"); + break; + + case SYS_DEL: // Remove peer + _peer_map.erase(id); + emit_signal("peer_disconnected", id); + break; + case SYS_ID: // Helo, server assigned ID + _peer_id = id; + break; + default: + ERR_EXPLAIN("Invalid multiplayer message"); + ERR_FAIL(); + break; + } + } +} diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer.h new file mode 100644 index 0000000000..e8e795e97f --- /dev/null +++ b/modules/websocket/websocket_multiplayer.h @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* websocket_multiplayer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 WEBSOCKET_MULTIPLAYER_PEER_H +#define WEBSOCKET_MULTIPLAYER_PEER_H + +#include "core/error_list.h" +#include "core/io/networked_multiplayer_peer.h" +#include "core/list.h" +#include "websocket_peer.h" + +class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer { + + GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer); + +private: + PoolVector<uint8_t> _make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size); + void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size); + Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size); + +protected: + enum { + SYS_NONE = 0, + SYS_ADD = 1, + SYS_DEL = 2, + SYS_ID = 3, + + PROTO_SIZE = 9, + SYS_PACKET_SIZE = 13, + MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer + }; + + struct Packet { + int source; + int destination; + uint8_t *data; + uint32_t size; + }; + + List<Packet> _incoming_packets; + Map<int, Ref<WebSocketPeer> > _peer_map; + Packet _current_packet; + + bool _is_multiplayer; + int _target_peer; + int _peer_id; + int _refusing; + + static void _bind_methods(); + + void _send_add(int32_t p_peer_id); + void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id); + void _send_del(int32_t p_peer_id); + int _gen_unique_id() const; + +public: + /* NetworkedMultiplayerPeer */ + void set_transfer_mode(TransferMode p_mode); + TransferMode get_transfer_mode() const; + void set_target_peer(int p_peer_id); + int get_packet_peer() const; + int get_unique_id() const; + virtual bool is_server() const = 0; + void set_refuse_new_connections(bool p_enable); + bool is_refusing_new_connections() const; + virtual ConnectionStatus get_connection_status() const = 0; + + /* PacketPeer */ + virtual int get_available_packet_count() const; + virtual int get_max_packet_size() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + /* WebSocketPeer */ + virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0; + + void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id); + void _clear(); + + WebSocketMultiplayerPeer(); + ~WebSocketMultiplayerPeer(); +}; + +#endif // WEBSOCKET_MULTIPLAYER_PEER_H diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp new file mode 100644 index 0000000000..a6fbb4481b --- /dev/null +++ b/modules/websocket/websocket_peer.cpp @@ -0,0 +1,49 @@ +/*************************************************************************/ +/* websocket_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "websocket_peer.h" + +GDCINULL(WebSocketPeer); + +WebSocketPeer::WebSocketPeer() { +} + +WebSocketPeer::~WebSocketPeer() { +} + +void WebSocketPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_write_mode"), &WebSocketPeer::get_write_mode); + ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode); + ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host); + ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet); + ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close); + + BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); + BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); +} diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h new file mode 100644 index 0000000000..f4d8ce3e38 --- /dev/null +++ b/modules/websocket/websocket_peer.h @@ -0,0 +1,73 @@ +/*************************************************************************/ +/* websocket_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 WEBSOCKETPEER_H +#define WEBSOCKETPEER_H + +#include "core/error_list.h" +#include "core/io/packet_peer.h" +#include "core/ring_buffer.h" +#include "websocket_macros.h" + +class WebSocketPeer : public PacketPeer { + + GDCLASS(WebSocketPeer, PacketPeer); + GDCICLASS(WebSocketPeer); + +public: + enum WriteMode { + WRITE_MODE_TEXT, + WRITE_MODE_BINARY, + }; + +protected: + static void _bind_methods(); + +public: + virtual int get_available_packet_count() const = 0; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; + virtual int get_max_packet_size() const = 0; + + virtual WriteMode get_write_mode() const = 0; + virtual void set_write_mode(WriteMode p_mode) = 0; + + virtual void close() = 0; + + virtual bool is_connected_to_host() const = 0; + virtual IP_Address get_connected_host() const = 0; + virtual uint16_t get_connected_port() const = 0; + virtual bool was_string_packet() const = 0; + + WebSocketPeer(); + ~WebSocketPeer(); +}; + +VARIANT_ENUM_CAST(WebSocketPeer::WriteMode); +#endif // WEBSOCKETPEER_H diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp new file mode 100644 index 0000000000..ba77019f55 --- /dev/null +++ b/modules/websocket/websocket_server.cpp @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* websocket_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "websocket_server.h" + +GDCINULL(WebSocketServer); + +WebSocketServer::WebSocketServer() { + _peer_id = 1; +} + +WebSocketServer::~WebSocketServer() { +} + +void WebSocketServer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); + ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); + ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); + + ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); + ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); +} + +NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { + if (is_listening()) + return CONNECTION_CONNECTED; + + return CONNECTION_DISCONNECTED; +}; + +bool WebSocketServer::is_server() const { + + return true; +} + +void WebSocketServer::_on_peer_packet(int32_t p_peer_id) { + + if (_is_multiplayer) { + _process_multiplayer(get_peer(p_peer_id), p_peer_id); + } else { + emit_signal("data_received", p_peer_id); + } +} + +void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) { + + if (_is_multiplayer) { + // Send add to clients + _send_add(p_peer_id); + emit_signal("peer_connected", p_peer_id); + } else { + emit_signal("client_connected", p_peer_id, p_protocol); + } +} + +void WebSocketServer::_on_disconnect(int32_t p_peer_id) { + + if (_is_multiplayer) { + // Send delete to clients + _send_del(p_peer_id); + emit_signal("peer_disconnected", p_peer_id); + } else { + emit_signal("client_disconnected", p_peer_id); + } +} diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h new file mode 100644 index 0000000000..db188811fd --- /dev/null +++ b/modules/websocket/websocket_server.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* websocket_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 WEBSOCKET_H +#define WEBSOCKET_H + +#include "core/reference.h" +#include "websocket_multiplayer.h" +#include "websocket_peer.h" + +class WebSocketServer : public WebSocketMultiplayerPeer { + + GDCLASS(WebSocketServer, WebSocketMultiplayerPeer); + GDCICLASS(WebSocketServer); + +protected: + static void _bind_methods(); + +public: + virtual void poll() = 0; + virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0; + virtual void stop() = 0; + virtual bool is_listening() const = 0; + virtual bool has_peer(int p_id) const = 0; + virtual Ref<WebSocketPeer> get_peer(int p_id) const = 0; + virtual bool is_server() const; + ConnectionStatus get_connection_status() const; + + void _on_peer_packet(int32_t p_peer_id); + void _on_connect(int32_t p_peer_id, String p_protocol); + void _on_disconnect(int32_t p_peer_id); + + WebSocketServer(); + ~WebSocketServer(); +}; + +#endif // WEBSOCKET_H |