summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp12
-rw-r--r--modules/bullet/btRayShape.cpp12
-rw-r--r--modules/bullet/btRayShape.h2
-rw-r--r--modules/bullet/bullet_physics_server.cpp8
-rw-r--r--modules/bullet/bullet_physics_server.h4
-rw-r--r--modules/bullet/collision_object_bullet.cpp29
-rw-r--r--modules/bullet/collision_object_bullet.h1
-rw-r--r--modules/bullet/godot_ray_world_algorithm.cpp7
-rw-r--r--modules/bullet/rigid_body_bullet.cpp10
-rw-r--r--modules/bullet/shape_bullet.cpp8
-rw-r--r--modules/bullet/space_bullet.cpp301
-rw-r--r--modules/bullet/space_bullet.h2
-rw-r--r--modules/csg/csg.cpp2
-rw-r--r--modules/csg/csg_shape.cpp3
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml2
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp2
-rw-r--r--modules/freetype/SCsub17
-rw-r--r--modules/gdnative/config.py3
-rw-r--r--modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml (renamed from modules/gdnative/doc_classes/WebRTCPeerGDNative.xml)2
-rw-r--r--modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml13
-rw-r--r--modules/gdnative/gdnative.cpp4
-rw-r--r--modules/gdnative/gdnative.h4
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp9
-rw-r--r--modules/gdnative/gdnative/variant.cpp2
-rw-r--r--modules/gdnative/gdnative_api.json38
-rw-r--r--modules/gdnative/gdnative_library_singleton_editor.cpp134
-rw-r--r--modules/gdnative/gdnative_library_singleton_editor.h12
-rw-r--r--modules/gdnative/include/gdnative/dictionary.h2
-rw-r--r--modules/gdnative/include/net/godot_net.h32
-rw-r--r--modules/gdnative/include/net/godot_webrtc.h122
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp29
-rw-r--r--modules/gdnative/nativescript/nativescript.h1
-rw-r--r--modules/gdnative/net/webrtc_gdnative.cpp (renamed from modules/gdnative/net/webrtc_peer_gdnative.cpp)23
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h2
-rw-r--r--modules/gdnative/register_types.cpp122
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp34
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp21
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h2
-rw-r--r--modules/gdscript/gdscript.cpp26
-rw-r--r--modules/gdscript/gdscript.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp4
-rw-r--r--modules/gdscript/gdscript_parser.cpp70
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp27
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp7
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h1
-rw-r--r--modules/mono/csharp_script.cpp164
-rw-r--r--modules/mono/csharp_script.h5
-rw-r--r--modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs4
-rw-r--r--modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs5
-rw-r--r--modules/mono/editor/bindings_generator.cpp16
-rw-r--r--modules/mono/editor/bindings_generator.h4
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp23
-rw-r--r--modules/mono/editor/godotsharp_builds.h2
-rw-r--r--modules/mono/editor/godotsharp_export.cpp7
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp5
-rw-r--r--modules/mono/glue/Managed/Files/AABB.cs15
-rw-r--r--modules/mono/glue/Managed/Files/Basis.cs73
-rw-r--r--modules/mono/glue/Managed/Files/DynamicObject.cs2
-rw-r--r--modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs4
-rw-r--r--modules/mono/glue/Managed/Files/MarshalUtils.cs154
-rw-r--r--modules/mono/glue/Managed/Files/Mathf.cs4
-rw-r--r--modules/mono/glue/Managed/Files/Transform2D.cs1
-rw-r--r--modules/mono/glue/Managed/IgnoredFiles/Node.cs5
-rw-r--r--modules/mono/glue/collections_glue.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp52
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp234
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h24
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp165
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h58
-rw-r--r--modules/mono/mono_gd/i_mono_class_member.h6
-rw-r--r--modules/mono/utils/string_utils.cpp4
-rw-r--r--modules/recast/navigation_mesh_editor_plugin.cpp12
-rw-r--r--modules/recast/navigation_mesh_generator.cpp171
-rw-r--r--modules/recast/navigation_mesh_generator.h24
-rw-r--r--modules/recast/register_types.cpp17
-rw-r--r--modules/regex/doc_classes/RegEx.xml2
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp1
-rw-r--r--modules/visual_script/visual_script.cpp37
-rw-r--r--modules/visual_script/visual_script.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp154
-rw-r--r--modules/visual_script/visual_script_editor.h2
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp363
-rw-r--r--modules/webrtc/config.py3
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannel.xml95
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml (renamed from modules/webrtc/doc_classes/WebRTCPeer.xml)56
-rw-r--r--modules/webrtc/register_types.cpp22
-rw-r--r--modules/webrtc/register_types.h4
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp64
-rw-r--r--modules/webrtc/webrtc_data_channel.h (renamed from modules/webrtc/webrtc_peer.h)51
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.cpp (renamed from modules/webrtc/webrtc_peer_gdnative.cpp)96
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.h (renamed from modules/webrtc/webrtc_peer_gdnative.h)44
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp352
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h (renamed from modules/webrtc/webrtc_peer_js.h)63
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp (renamed from modules/webrtc/webrtc_peer.cpp)52
-rw-r--r--modules/webrtc/webrtc_peer_connection.h74
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.cpp124
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.h73
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp314
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h66
-rw-r--r--modules/webrtc/webrtc_peer_js.cpp455
-rw-r--r--modules/websocket/SCsub117
-rw-r--r--modules/websocket/lws_client.cpp5
107 files changed, 3480 insertions, 1668 deletions
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index 8d82fb2eeb..8c3c04674b 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -310,8 +310,8 @@ void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const
const aiBone *bone = mesh->mBones[j];
String name = _assimp_get_string(bone->mName);
ownership[name] = owned_by;
- //store the actuall full path for the bone transform
- //when skeleton finds it's place in the tree, it will be restored
+ //store the actual full path for the bone transform
+ //when skeleton finds its place in the tree, it will be restored
bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix);
}
}
@@ -855,9 +855,7 @@ Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportSta
Ref<Texture> texture = _load_texture(state, path);
if (texture != NULL) {
- if (map_mode != NULL) {
- _set_texture_mapping_mode(map_mode, texture);
- }
+ _set_texture_mapping_mode(map_mode, texture);
mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
}
@@ -1460,7 +1458,7 @@ void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode
int mesh_index = p_assimp_node->mMeshes[i];
surface_indices.push_back(mesh_index);
- //take the chane and attempt to find the skeleton from the bones
+ //take the chance and attempt to find the skeleton from the bones
if (!skeleton) {
aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]];
for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) {
@@ -1598,7 +1596,7 @@ void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode
skeleton->localize_rests();
node_name = "Skeleton"; //don't use the bone root name
- node_transform = Transform(); //dont transform
+ node_transform = Transform(); //don't transform
new_node = skeleton;
} else {
diff --git a/modules/bullet/btRayShape.cpp b/modules/bullet/btRayShape.cpp
index b902d08eca..b60d6ba693 100644
--- a/modules/bullet/btRayShape.cpp
+++ b/modules/bullet/btRayShape.cpp
@@ -54,6 +54,11 @@ void btRayShape::setLength(btScalar p_length) {
reload_cache();
}
+void btRayShape::setMargin(btScalar margin) {
+ btConvexInternalShape::setMargin(margin);
+ reload_cache();
+}
+
void btRayShape::setSlipsOnSlope(bool p_slipsOnSlope) {
slipsOnSlope = p_slipsOnSlope;
@@ -77,10 +82,9 @@ void btRayShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVecto
}
void btRayShape::getAabb(const btTransform &t, btVector3 &aabbMin, btVector3 &aabbMax) const {
-#define MARGIN_BROADPHASE 0.1
btVector3 localAabbMin(0, 0, 0);
- btVector3 localAabbMax(m_shapeAxis * (m_cacheScaledLength + m_collisionMargin));
- btTransformAabb(localAabbMin, localAabbMax, MARGIN_BROADPHASE, t, aabbMin, aabbMax);
+ btVector3 localAabbMax(m_shapeAxis * m_cacheScaledLength);
+ btTransformAabb(localAabbMin, localAabbMax, m_collisionMargin, t, aabbMin, aabbMax);
}
void btRayShape::calculateLocalInertia(btScalar mass, btVector3 &inertia) const {
@@ -100,5 +104,5 @@ void btRayShape::reload_cache() {
m_cacheScaledLength = m_length * m_localScaling[2];
m_cacheSupportPoint.setIdentity();
- m_cacheSupportPoint.setOrigin(m_shapeAxis * (m_cacheScaledLength + m_collisionMargin));
+ m_cacheSupportPoint.setOrigin(m_shapeAxis * m_cacheScaledLength);
}
diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h
index 7fedb74083..7f3229b3e8 100644
--- a/modules/bullet/btRayShape.h
+++ b/modules/bullet/btRayShape.h
@@ -60,6 +60,8 @@ public:
void setLength(btScalar p_length);
btScalar getLength() const { return m_length; }
+ virtual void setMargin(btScalar margin);
+
void setSlipsOnSlope(bool p_slipOnSlope);
bool getSlipsOnSlope() const { return slipsOnSlope; }
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index b21bdee052..be4e0b88e8 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -348,13 +348,13 @@ void BulletPhysicsServer::area_set_shape_disabled(RID p_area, int p_shape_idx, b
area->set_shape_disabled(p_shape_idx, p_disabled);
}
-void BulletPhysicsServer::area_attach_object_instance_id(RID p_area, ObjectID p_ID) {
+void BulletPhysicsServer::area_attach_object_instance_id(RID p_area, ObjectID p_id) {
if (space_owner.owns(p_area)) {
return;
}
AreaBullet *area = area_owner.get(p_area);
ERR_FAIL_COND(!area);
- area->set_instance_id(p_ID);
+ area->set_instance_id(p_id);
}
ObjectID BulletPhysicsServer::area_get_object_instance_id(RID p_area) const {
@@ -569,11 +569,11 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) {
body->remove_all_shapes();
}
-void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) {
+void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_id) {
CollisionObjectBullet *body = get_collisin_object(p_body);
ERR_FAIL_COND(!body);
- body->set_instance_id(p_ID);
+ body->set_instance_id(p_id);
}
uint32_t BulletPhysicsServer::body_get_object_instance_id(RID p_body) const {
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 5bcb0d244b..0b8ad53658 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -142,7 +142,7 @@ public:
virtual void area_remove_shape(RID p_area, int p_shape_idx);
virtual void area_clear_shapes(RID p_area);
virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled);
- virtual void area_attach_object_instance_id(RID p_area, ObjectID p_ID);
+ virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id);
virtual ObjectID area_get_object_instance_id(RID p_area) const;
/// If you pass as p_area the SpaceBullet you can set some parameters as specified below
@@ -189,7 +189,7 @@ public:
virtual void body_clear_shapes(RID p_body);
// Used for Rigid and Soft Bodies
- virtual void body_attach_object_instance_id(RID p_body, uint32_t p_ID);
+ virtual void body_attach_object_instance_id(RID p_body, uint32_t p_id);
virtual uint32_t body_get_object_instance_id(RID p_body) const;
virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable);
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 3a90bdc6ae..166d7e6158 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -43,7 +43,9 @@
@author AndreaCatania
*/
-#define enableDynamicAabbTree false
+// We enable dynamic AABB tree so that we can actually perform a broadphase on bodies with compound collision shapes.
+// This is crucial for the performance of kinematic bodies and for bodies with transforming shapes.
+#define enableDynamicAabbTree true
CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {}
@@ -57,6 +59,25 @@ void CollisionObjectBullet::ShapeWrapper::set_transform(const btTransform &p_tra
transform = p_transform;
}
+btTransform CollisionObjectBullet::ShapeWrapper::get_adjusted_transform() const {
+ if (shape->get_type() == PhysicsServer::SHAPE_HEIGHTMAP) {
+ const HeightMapShapeBullet *hm_shape = (const HeightMapShapeBullet *)shape; // should be safe to cast now
+ btTransform adjusted_transform;
+
+ // Bullet centers our heightmap:
+ // https://github.com/bulletphysics/bullet3/blob/master/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h#L33
+ // This is really counter intuitive so we're adjusting for it
+
+ adjusted_transform.setIdentity();
+ adjusted_transform.setOrigin(btVector3(0.0, hm_shape->min_height + ((hm_shape->max_height - hm_shape->min_height) * 0.5), 0.0));
+ adjusted_transform *= transform;
+
+ return adjusted_transform;
+ } else {
+ return transform;
+ }
+}
+
void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) {
if (!bt_shape) {
if (active)
@@ -284,7 +305,6 @@ void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transfor
ERR_FAIL_INDEX(p_index, get_shape_count());
shapes.write[p_index].set_transform(p_transform);
- // Note, enableDynamicAabbTree is false because on transform change compound is destroyed
reload_shapes();
}
@@ -344,7 +364,8 @@ void RigidCollisionObjectBullet::reload_shapes() {
// Try to optimize by not using compound
if (1 == shape_count) {
shpWrapper = &shapes.write[0];
- if (shpWrapper->transform.getOrigin().isZero() && shpWrapper->transform.getBasis() == shpWrapper->transform.getBasis().getIdentity()) {
+ btTransform transform = shpWrapper->get_adjusted_transform();
+ if (transform.getOrigin().isZero() && transform.getBasis() == transform.getBasis().getIdentity()) {
shpWrapper->claim_bt_shape(body_scale);
mainShape = shpWrapper->bt_shape;
main_shape_changed();
@@ -358,7 +379,7 @@ void RigidCollisionObjectBullet::reload_shapes() {
for (int i(0); i < shape_count; ++i) {
shpWrapper = &shapes.write[i];
shpWrapper->claim_bt_shape(body_scale);
- btTransform scaled_shape_transform(shpWrapper->transform);
+ btTransform scaled_shape_transform(shpWrapper->get_adjusted_transform());
scaled_shape_transform.getOrigin() *= body_scale;
compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape);
}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
index e65bc52caf..c9430bec18 100644
--- a/modules/bullet/collision_object_bullet.h
+++ b/modules/bullet/collision_object_bullet.h
@@ -109,6 +109,7 @@ public:
void set_transform(const Transform &p_transform);
void set_transform(const btTransform &p_transform);
+ btTransform get_adjusted_transform() const;
void claim_bt_shape(const btVector3 &body_scale);
};
diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp
index 3e06239453..2ba75b9a98 100644
--- a/modules/bullet/godot_ray_world_algorithm.cpp
+++ b/modules/bullet/godot_ray_world_algorithm.cpp
@@ -39,6 +39,9 @@
@author AndreaCatania
*/
+// Epsilon to account for floating point inaccuracies
+#define RAY_PENETRATION_DEPTH_EPSILON 0.01
+
GodotRayWorldAlgorithm::CreateFunc::CreateFunc(const btDiscreteDynamicsWorld *world) :
m_world(world) {}
@@ -100,8 +103,8 @@ void GodotRayWorldAlgorithm::processCollision(const btCollisionObjectWrapper *bo
btScalar depth(ray_shape->getScaledLength() * (btResult.m_closestHitFraction - 1));
- if (depth >= -ray_shape->getMargin() * 0.5)
- depth = 0;
+ if (depth > -RAY_PENETRATION_DEPTH_EPSILON)
+ depth = 0.0;
if (ray_shape->getSlipsOnSlope())
resultOut->addContactPoint(btResult.m_hitNormalWorld, btResult.m_hitPointWorld, depth);
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index e5f70a0b34..733a900396 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -741,22 +741,20 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) {
if (p_enable) {
// This threshold enable CCD if the object moves more than
// 1 meter in one simulation frame
- btBody->setCcdMotionThreshold(0.1);
+ btBody->setCcdMotionThreshold(1e-7);
/// Calculate using the rule writte below the CCD swept sphere radius
/// CCD works on an embedded sphere of radius, make sure this radius
/// is embedded inside the convex objects, preferably smaller:
/// for an object of dimensions 1 meter, try 0.2
- btScalar radius;
+ btScalar radius(1.0);
if (btBody->getCollisionShape()) {
btVector3 center;
btBody->getCollisionShape()->getBoundingSphere(center, radius);
- } else {
- radius = 0;
}
btBody->setCcdSweptSphereRadius(radius * 0.2);
} else {
- btBody->setCcdMotionThreshold(0.);
+ btBody->setCcdMotionThreshold(10000.0);
btBody->setCcdSweptSphereRadius(0.);
}
}
@@ -834,7 +832,7 @@ void RigidBodyBullet::reload_shapes() {
btBody->updateInertiaTensor();
reload_kinematic_shapes();
-
+ set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0);
reload_body();
}
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index b590d63167..f15bcec914 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -148,7 +148,13 @@ btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<rea
const bool flipQuadEdges = false;
const void *heightsPtr = p_heights.read().ptr();
- return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
+ btHeightfieldTerrainShape *heightfield = bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
+
+ // The shape can be created without params when you do PhysicsServer.shape_create(PhysicsServer.SHAPE_HEIGHTMAP)
+ if (heightsPtr)
+ heightfield->buildAccelerator(16);
+
+ return heightfield;
}
btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) {
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 8fb8eba057..738b415d16 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -1043,23 +1043,16 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p
btVector3 recover_motion(0, 0, 0);
int rays_found = 0;
+ int rays_found_this_round = 0;
for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- int last_ray_index = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max, recover_motion, r_results);
+ PhysicsServer::SeparationResult *next_results = &r_results[rays_found];
+ rays_found_this_round = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max - rays_found, recover_motion, next_results);
- rays_found = MAX(last_ray_index, rays_found);
- if (!rays_found) {
- break;
- } else {
+ rays_found += rays_found_this_round;
+ if (rays_found_this_round == 0) {
body_transform.getOrigin() += recover_motion;
- }
- }
-
- //optimize results (remove non colliding)
- for (int i = 0; i < rays_found; i++) {
- if (r_results[i].collision_depth >= 0) {
- rays_found--;
- SWAP(r_results[i], r_results[rays_found]);
+ break;
}
}
@@ -1069,18 +1062,47 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p
struct RecoverPenetrationBroadPhaseCallback : public btBroadphaseAabbCallback {
private:
+ btDbvtVolume bounds;
+
const btCollisionObject *self_collision_object;
uint32_t collision_layer;
uint32_t collision_mask;
+ struct CompoundLeafCallback : btDbvt::ICollide {
+ private:
+ RecoverPenetrationBroadPhaseCallback *parent_callback;
+ btCollisionObject *collision_object;
+
+ public:
+ CompoundLeafCallback(RecoverPenetrationBroadPhaseCallback *p_parent_callback, btCollisionObject *p_collision_object) :
+ parent_callback(p_parent_callback),
+ collision_object(p_collision_object) {
+ }
+
+ void Process(const btDbvtNode *leaf) {
+ BroadphaseResult result;
+ result.collision_object = collision_object;
+ result.compound_child_index = leaf->dataAsInt;
+ parent_callback->results.push_back(result);
+ }
+ };
+
public:
- Vector<btCollisionObject *> result_collision_objects;
+ struct BroadphaseResult {
+ btCollisionObject *collision_object;
+ int compound_child_index;
+ };
+
+ Vector<BroadphaseResult> results;
public:
- RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, uint32_t p_collision_mask) :
+ RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, uint32_t p_collision_mask, btVector3 p_aabb_min, btVector3 p_aabb_max) :
self_collision_object(p_self_collision_object),
collision_layer(p_collision_layer),
- collision_mask(p_collision_mask) {}
+ collision_mask(p_collision_mask) {
+
+ bounds = btDbvtVolume::FromMM(p_aabb_min, p_aabb_max);
+ }
virtual ~RecoverPenetrationBroadPhaseCallback() {}
@@ -1089,35 +1111,98 @@ public:
btCollisionObject *co = static_cast<btCollisionObject *>(proxy->m_clientObject);
if (co->getInternalType() <= btCollisionObject::CO_RIGID_BODY) {
if (self_collision_object != proxy->m_clientObject && GodotFilterCallback::test_collision_filters(collision_layer, collision_mask, proxy->m_collisionFilterGroup, proxy->m_collisionFilterMask)) {
- result_collision_objects.push_back(co);
+ if (co->getCollisionShape()->isCompound()) {
+ const btCompoundShape *cs = static_cast<btCompoundShape *>(co->getCollisionShape());
+
+ if (cs->getNumChildShapes() > 1) {
+ const btDbvt *tree = cs->getDynamicAabbTree();
+ ERR_FAIL_COND_V(tree == NULL, true);
+
+ // Transform bounds into compound shape local space
+ const btTransform other_in_compound_space = co->getWorldTransform().inverse();
+ const btMatrix3x3 abs_b = other_in_compound_space.getBasis().absolute();
+ const btVector3 local_center = other_in_compound_space(bounds.Center());
+ const btVector3 local_extent = bounds.Extents().dot3(abs_b[0], abs_b[1], abs_b[2]);
+ const btVector3 local_aabb_min = local_center - local_extent;
+ const btVector3 local_aabb_max = local_center + local_extent;
+ const btDbvtVolume local_bounds = btDbvtVolume::FromMM(local_aabb_min, local_aabb_max);
+
+ // Test collision against compound child shapes using its AABB tree
+ CompoundLeafCallback compound_leaf_callback(this, co);
+ tree->collideTV(tree->m_root, local_bounds, compound_leaf_callback);
+ } else {
+ // If there's only a single child shape then there's no need to search any more, we know which child overlaps
+ BroadphaseResult result;
+ result.collision_object = co;
+ result.compound_child_index = 0;
+ results.push_back(result);
+ }
+ } else {
+ BroadphaseResult result;
+ result.collision_object = co;
+ result.compound_child_index = -1;
+ results.push_back(result);
+ }
return true;
}
}
return false;
}
-
- void reset() {
- result_collision_objects.clear();
- }
};
bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) {
- RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask());
+ // Calculate the cumulative AABB of all shapes of the kinematic body
+ btVector3 aabb_min, aabb_max;
+ bool shapes_found = false;
+
+ for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
+
+ const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]);
+ if (!kin_shape.is_active()) {
+ continue;
+ }
+
+ if (kin_shape.shape->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) {
+ // Skip rayshape in order to implement custom separation process
+ continue;
+ }
+
+ btTransform shape_transform = p_body_position * kin_shape.transform;
+ shape_transform.getOrigin() += r_delta_recover_movement;
+
+ btVector3 shape_aabb_min, shape_aabb_max;
+ kin_shape.shape->getAabb(shape_transform, shape_aabb_min, shape_aabb_max);
+
+ if (!shapes_found) {
+ aabb_min = shape_aabb_min;
+ aabb_max = shape_aabb_max;
+ shapes_found = true;
+ } else {
+ aabb_min.setX((aabb_min.x() < shape_aabb_min.x()) ? aabb_min.x() : shape_aabb_min.x());
+ aabb_min.setY((aabb_min.y() < shape_aabb_min.y()) ? aabb_min.y() : shape_aabb_min.y());
+ aabb_min.setZ((aabb_min.z() < shape_aabb_min.z()) ? aabb_min.z() : shape_aabb_min.z());
+
+ aabb_max.setX((aabb_max.x() > shape_aabb_max.x()) ? aabb_max.x() : shape_aabb_max.x());
+ aabb_max.setY((aabb_max.y() > shape_aabb_max.y()) ? aabb_max.y() : shape_aabb_max.y());
+ aabb_max.setZ((aabb_max.z() > shape_aabb_max.z()) ? aabb_max.z() : shape_aabb_max.z());
+ }
+ }
- btTransform body_shape_position;
- btTransform body_shape_position_recovered;
+ // If there are no shapes then there is no penetration either
+ if (!shapes_found) {
+ return false;
+ }
- // Broad phase support
- btVector3 minAabb, maxAabb;
+ // Perform broadphase test
+ RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max);
+ dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result);
bool penetration = false;
- // For each shape
+ // Perform narrowphase per shape
for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
- recover_broad_result.reset();
-
const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]);
if (!kin_shape.is_active()) {
continue;
@@ -1128,15 +1213,11 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
continue;
}
- body_shape_position = p_body_position * kin_shape.transform;
- body_shape_position_recovered = body_shape_position;
- body_shape_position_recovered.getOrigin() += r_delta_recover_movement;
+ btTransform shape_transform = p_body_position * kin_shape.transform;
+ shape_transform.getOrigin() += r_delta_recover_movement;
- kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb);
- dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result);
-
- for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
- btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
+ for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) {
+ btCollisionObject *otherObject = recover_broad_result.results[i].collision_object;
if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
otherObject->activate(); // Force activation of hitten rigid, soft body
continue;
@@ -1144,30 +1225,28 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
continue;
if (otherObject->getCollisionShape()->isCompound()) {
+ const btCompoundShape *cs = static_cast<const btCompoundShape *>(otherObject->getCollisionShape());
+ int shape_idx = recover_broad_result.results[i].compound_child_index;
+ ERR_FAIL_COND_V(shape_idx < 0 || shape_idx >= cs->getNumChildShapes(), false);
- // Each convex shape
- btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape());
- for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) {
-
- if (cs->getChildShape(x)->isConvex()) {
- if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(x)), otherObject, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
+ if (cs->getChildShape(shape_idx)->isConvex()) {
+ if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(cs->getChildShape(shape_idx)), otherObject, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
- penetration = true;
- }
- } else {
- if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
+ penetration = true;
+ }
+ } else {
+ if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(shape_idx), p_body->get_bt_collision_object(), otherObject, kinIndex, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
- penetration = true;
- }
+ penetration = true;
}
}
} else if (otherObject->getCollisionShape()->isConvex()) { /// Execute GJK test against object shape
- if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), otherObject, 0, body_shape_position, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
+ if (RFP_convex_convex_test(kin_shape.shape, static_cast<const btConvexShape *>(otherObject->getCollisionShape()), otherObject, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
penetration = true;
}
} else {
- if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, body_shape_position, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
+ if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, r_recover_result)) {
penetration = true;
}
@@ -1183,7 +1262,6 @@ bool SpaceBullet::RFP_convex_convex_test(const btConvexShape *p_shapeA, const bt
// Initialize GJK input
btGjkPairDetector::ClosestPointInput gjk_input;
gjk_input.m_transformA = p_transformA;
- gjk_input.m_transformA.getOrigin() += r_delta_recover_movement;
gjk_input.m_transformB = p_transformB;
// Perform GJK test
@@ -1214,7 +1292,6 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
/// Contact test
btTransform tA(p_transformA);
- tA.getOrigin() += r_delta_recover_movement;
btCollisionObjectWrapper obA(NULL, p_shapeA, p_objectA, tA, -1, p_shapeId_A);
btCollisionObjectWrapper obB(NULL, p_shapeB, p_objectB, p_transformB, -1, p_shapeId_B);
@@ -1246,39 +1323,81 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
return false;
}
-void SpaceBullet::convert_to_separation_result(PhysicsServer::SeparationResult *r_result, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const {
+int SpaceBullet::add_separation_result(PhysicsServer::SeparationResult *r_result, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const {
+
+ // optimize results (ignore non-colliding)
+ if (p_recover_result.penetration_distance < 0.0) {
+ const btRigidBody *btRigid = static_cast<const btRigidBody *>(p_other_object);
+ CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(p_other_object->getUserPointer());
- const btRigidBody *btRigid = static_cast<const btRigidBody *>(p_other_object);
- CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(p_other_object->getUserPointer());
+ r_result->collision_depth = p_recover_result.penetration_distance;
+ B_TO_G(p_recover_result.pointWorld, r_result->collision_point);
+ B_TO_G(p_recover_result.normal, r_result->collision_normal);
+ B_TO_G(btRigid->getVelocityInLocalPoint(p_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity);
+ r_result->collision_local_shape = p_shape_id;
+ r_result->collider_id = collisionObject->get_instance_id();
+ r_result->collider = collisionObject->get_self();
+ r_result->collider_shape = p_recover_result.other_compound_shape_index;
- r_result->collision_depth = p_recover_result.penetration_distance;
- B_TO_G(p_recover_result.pointWorld, r_result->collision_point);
- B_TO_G(p_recover_result.normal, r_result->collision_normal);
- B_TO_G(btRigid->getVelocityInLocalPoint(p_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_result->collider_velocity);
- r_result->collision_local_shape = p_shape_id;
- r_result->collider_id = collisionObject->get_instance_id();
- r_result->collider = collisionObject->get_self();
- r_result->collider_shape = p_recover_result.other_compound_shape_index;
+ return 1;
+ } else {
+ return 0;
+ }
}
int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) {
- RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask());
+ // Calculate the cumulative AABB of all shapes of the kinematic body
+ btVector3 aabb_min, aabb_max;
+ bool shapes_found = false;
+
+ for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
+
+ const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]);
+ if (!kin_shape.is_active()) {
+ continue;
+ }
+
+ if (kin_shape.shape->getShapeType() != CUSTOM_CONVEX_SHAPE_TYPE) {
+ continue;
+ }
+
+ btTransform shape_transform = p_body_position * kin_shape.transform;
+ shape_transform.getOrigin() += r_delta_recover_movement;
- btTransform body_shape_position;
- btTransform body_shape_position_recovered;
+ btVector3 shape_aabb_min, shape_aabb_max;
+ kin_shape.shape->getAabb(shape_transform, shape_aabb_min, shape_aabb_max);
- // Broad phase support
- btVector3 minAabb, maxAabb;
+ if (!shapes_found) {
+ aabb_min = shape_aabb_min;
+ aabb_max = shape_aabb_max;
+ shapes_found = true;
+ } else {
+ aabb_min.setX((aabb_min.x() < shape_aabb_min.x()) ? aabb_min.x() : shape_aabb_min.x());
+ aabb_min.setY((aabb_min.y() < shape_aabb_min.y()) ? aabb_min.y() : shape_aabb_min.y());
+ aabb_min.setZ((aabb_min.z() < shape_aabb_min.z()) ? aabb_min.z() : shape_aabb_min.z());
- int ray_index = 0;
+ aabb_max.setX((aabb_max.x() > shape_aabb_max.x()) ? aabb_max.x() : shape_aabb_max.x());
+ aabb_max.setY((aabb_max.y() > shape_aabb_max.y()) ? aabb_max.y() : shape_aabb_max.y());
+ aabb_max.setZ((aabb_max.z() > shape_aabb_max.z()) ? aabb_max.z() : shape_aabb_max.z());
+ }
+ }
- // For each shape
- for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
+ // If there are no shapes then there is no penetration either
+ if (!shapes_found) {
+ return 0;
+ }
+
+ // Perform broadphase test
+ RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max);
+ dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result);
- recover_broad_result.reset();
+ int ray_count = 0;
+
+ // Perform narrowphase per shape
+ for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) {
- if (ray_index >= p_result_max) {
+ if (ray_count >= p_result_max) {
break;
}
@@ -1291,15 +1410,11 @@ int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btT
continue;
}
- body_shape_position = p_body_position * kin_shape.transform;
- body_shape_position_recovered = body_shape_position;
- body_shape_position_recovered.getOrigin() += r_delta_recover_movement;
+ btTransform shape_transform = p_body_position * kin_shape.transform;
+ shape_transform.getOrigin() += r_delta_recover_movement;
- kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb);
- dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result);
-
- for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
- btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
+ for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) {
+ btCollisionObject *otherObject = recover_broad_result.results[i].collision_object;
if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
otherObject->activate(); // Force activation of hitten rigid, soft body
continue;
@@ -1307,29 +1422,25 @@ int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btT
continue;
if (otherObject->getCollisionShape()->isCompound()) {
+ const btCompoundShape *cs = static_cast<const btCompoundShape *>(otherObject->getCollisionShape());
+ int shape_idx = recover_broad_result.results[i].compound_child_index;
+ ERR_FAIL_COND_V(shape_idx < 0 || shape_idx >= cs->getNumChildShapes(), false);
- // Each convex shape
- btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape());
- for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) {
-
- RecoverResult recover_result;
- if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) {
+ RecoverResult recover_result;
+ if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(shape_idx), p_body->get_bt_collision_object(), otherObject, kinIndex, shape_idx, shape_transform, otherObject->getWorldTransform() * cs->getChildTransform(shape_idx), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) {
- convert_to_separation_result(&r_results[ray_index], recover_result, kinIndex, otherObject);
- }
+ ray_count = add_separation_result(&r_results[ray_count], recover_result, kinIndex, otherObject);
}
} else {
RecoverResult recover_result;
- if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, body_shape_position, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) {
+ if (RFP_convex_world_test(kin_shape.shape, otherObject->getCollisionShape(), p_body->get_bt_collision_object(), otherObject, kinIndex, 0, shape_transform, otherObject->getWorldTransform(), p_recover_movement_scale, r_delta_recover_movement, &recover_result)) {
- convert_to_separation_result(&r_results[ray_index], recover_result, kinIndex, otherObject);
+ ray_count = add_separation_result(&r_results[ray_count], recover_result, kinIndex, otherObject);
}
}
}
-
- ++ray_index;
}
- return ray_index;
+ return ray_count;
}
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 7bf6a216b5..6b3d65edf6 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -212,7 +212,7 @@ private:
/// 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_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL);
- void convert_to_separation_result(PhysicsServer::SeparationResult *r_result, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const;
+ int add_separation_result(PhysicsServer::SeparationResult *r_results, const SpaceBullet::RecoverResult &p_recover_result, int p_shape_id, const btCollisionObject *p_other_object) const;
int recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results);
};
#endif
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 7e1cc937cd..3a61afa023 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -611,7 +611,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in
{
EdgeSort es;
- es.angle = 0; //wont be checked here
+ es.angle = 0; //won't be checked here
es.edge = p_edge;
es.prev_point = p_from_point;
es.edge_point = p_to_point;
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index e70773d914..1d27b9b6f4 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -2082,6 +2082,9 @@ CSGBrush *CSGPolygon::_build_brush() {
for (int i = 0; i <= splits; i++) {
float ofs = i * path_interval;
+ if (ofs > bl) {
+ ofs = bl;
+ }
if (i == splits && path_joined) {
ofs = 0.0;
}
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 894c17c684..d3d1e58b7b 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -110,7 +110,7 @@
Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system.
</member>
<member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count">
- The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis.
+ The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per channel basis.
</member>
<member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode">
The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 492b365128..18dfe08e85 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -231,7 +231,7 @@ void NetworkedMultiplayerENet::poll() {
break;
}
- // A client joined with an invalid ID (neagtive values, 0, and 1 are reserved).
+ // A client joined with an invalid ID (negative values, 0, and 1 are reserved).
// Probably trying to exploit us.
if (server && ((int)event.data < 2 || peer_map.has((int)event.data))) {
enet_peer_reset(event.peer);
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 0c8b223a9e..0ea581220e 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -61,13 +61,6 @@ if env['builtin_freetype']:
# Globally too, as freetype is used in scene (see bottom)
env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"'])
- sfnt = thirdparty_dir + 'src/sfnt/sfnt.c'
- if env['platform'] == 'javascript':
- # Forcibly undefine this macro so SIMD is not used in this file,
- # since currently unsupported in WASM
- sfnt = env_freetype.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__'])
- thirdparty_sources += [sfnt]
-
env_freetype.Prepend(CPPPATH=[thirdparty_dir + "/include"])
# Also needed in main env for scene/
env.Prepend(CPPPATH=[thirdparty_dir + "/include"])
@@ -80,6 +73,16 @@ if env['builtin_freetype']:
if env['builtin_libpng']:
env_freetype.Prepend(CPPPATH=["#thirdparty/libpng"])
+ sfnt = thirdparty_dir + 'src/sfnt/sfnt.c'
+ # Must be done after all CPPFLAGS are being set so we can copy them.
+ if env['platform'] == 'javascript':
+ # Forcibly undefine this macro so SIMD is not used in this file,
+ # since currently unsupported in WASM
+ tmp_env = env_freetype.Clone()
+ tmp_env.Append(CPPFLAGS=['-U__OPTIMIZE__'])
+ sfnt = tmp_env.Object(sfnt)
+ thirdparty_sources += [sfnt]
+
env_thirdparty = env_freetype.Clone()
env_thirdparty.disable_warnings()
lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources)
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index fde7f1a6e0..7898de5523 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -16,7 +16,8 @@ def get_doc_classes():
"ResourceFormatLoaderVideoStreamGDNative",
"StreamPeerGDNative",
"VideoStreamGDNative",
- "WebRTCPeerGDNative",
+ "WebRTCPeerConnectionGDNative",
+ "WebRTCDataChannelGDNative",
]
def get_doc_path():
diff --git a/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
index 478889e031..ac18ec6020 100644
--- a/modules/gdnative/doc_classes/WebRTCPeerGDNative.xml
+++ b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeerGDNative" inherits="WebRTCPeer" category="Core" version="3.2">
+<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
diff --git a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
new file mode 100644
index 0000000000..44cb8e5db8
--- /dev/null
+++ b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCPeerConnectionGDNative" inherits="WebRTCPeerConnection" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index e8278825bc..a27935bfe2 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -243,7 +243,7 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile", 0), "set_config_file", "get_config_file");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
@@ -404,7 +404,7 @@ bool GDNative::terminate() {
} else if (gdnatives->size() == 1) {
// we're the last one, terminate!
gdnatives->clear();
- // wew this looks scary, but all it does is remove the entry completely
+ // whew this looks scary, but all it does is remove the entry completely
GDNativeLibrary::loaded_libraries->erase(GDNativeLibrary::loaded_libraries->find(library->get_current_library_path()));
}
}
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 492dc5beaa..ef57387059 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -99,16 +99,20 @@ public:
}
_FORCE_INLINE_ void set_load_once(bool p_load_once) {
+ config_file->set_value("general", "load_once", p_load_once);
load_once = p_load_once;
}
_FORCE_INLINE_ void set_singleton(bool p_singleton) {
+ config_file->set_value("general", "singleton", p_singleton);
singleton = p_singleton;
}
_FORCE_INLINE_ void set_symbol_prefix(String p_symbol_prefix) {
+ config_file->set_value("general", "symbol_prefix", p_symbol_prefix);
symbol_prefix = p_symbol_prefix;
}
_FORCE_INLINE_ void set_reloadable(bool p_reloadable) {
+ config_file->set_value("general", "reloadable", p_reloadable);
reloadable = p_reloadable;
}
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index 2c6c9e2de2..fff3fc3625 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -55,6 +55,15 @@ void GDAPI godot_dictionary_destroy(godot_dictionary *p_self) {
self->~Dictionary();
}
+godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep) {
+ const Dictionary *self = (const Dictionary *)p_self;
+ godot_dictionary res;
+ Dictionary *val = (Dictionary *)&res;
+ memnew_placement(val, Dictionary);
+ *val = self->duplicate(p_deep);
+ return res;
+}
+
godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self) {
const Dictionary *self = (const Dictionary *)p_self;
return self->size();
diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp
index 8f0d5a2db4..ac4d5a86b2 100644
--- a/modules/gdnative/gdnative/variant.cpp
+++ b/modules/gdnative/gdnative/variant.cpp
@@ -518,7 +518,7 @@ void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_varia
const Variant *a = (const Variant *)p_a;
const Variant *b = (const Variant *)p_b;
Variant *ret = (Variant *)r_ret;
- Variant::evaluate(op, a, b, *ret, *r_valid);
+ Variant::evaluate(op, *a, *b, *ret, *r_valid);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 9882a89794..8afe988102 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -11,7 +11,24 @@
"major": 1,
"minor": 1
},
- "next": null,
+ "next": {
+ "type": "CORE",
+ "version": {
+ "major": 1,
+ "minor": 2
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_dictionary_duplicate",
+ "return_type": "godot_dictionary",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_bool", "p_deep"]
+ ]
+ }
+ ]
+ },
"api": [
{
"name": "godot_color_to_abgr32",
@@ -6436,11 +6453,26 @@
"next": null,
"api": [
{
- "name": "godot_net_bind_webrtc_peer",
+ "name": "godot_net_set_webrtc_library",
+ "return_type": "godot_error",
+ "arguments": [
+ ["const godot_net_webrtc_library *", "p_library"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_peer_connection",
+ "return_type": "void",
+ "arguments": [
+ ["godot_object *", "p_obj"],
+ ["const godot_net_webrtc_peer_connection *", "p_interface"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_data_channel",
"return_type": "void",
"arguments": [
["godot_object *", "p_obj"],
- ["const godot_net_webrtc_peer *", "p_interface"]
+ ["const godot_net_webrtc_data_channel *", "p_interface"]
]
}
]
diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp
index 55bc16fccc..389b353a51 100644
--- a/modules/gdnative/gdnative_library_singleton_editor.cpp
+++ b/modules/gdnative/gdnative_library_singleton_editor.cpp
@@ -32,11 +32,16 @@
#include "gdnative_library_singleton_editor.h"
#include "gdnative.h"
-void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list) {
+#include "editor/editor_node.h"
+
+Set<String> GDNativeLibrarySingletonEditor::_find_singletons_recursive(EditorFileSystemDirectory *p_dir) {
+
+ Set<String> file_paths;
// check children
for (int i = 0; i < p_dir->get_file_count(); i++) {
+ String file_name = p_dir->get_file(i);
String file_type = p_dir->get_file_type(i);
if (file_type != "GDNativeLibrary") {
@@ -45,23 +50,57 @@ void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemD
Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
if (lib.is_valid() && lib->is_singleton()) {
- String path = p_dir->get_file_path(i);
- TreeItem *ti = libraries->create_item(libraries->get_root());
- ti->set_text(0, path.get_file());
- ti->set_tooltip(0, path);
- ti->set_metadata(0, path);
- ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
- ti->set_text(1, "Disabled,Enabled");
- bool enabled = enabled_list.has(path) ? true : false;
-
- ti->set_range(1, enabled ? 1 : 0);
- ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0));
+ file_paths.insert(p_dir->get_file_path(i));
}
}
// check subdirectories
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- _find_gdnative_singletons(p_dir->get_subdir(i), enabled_list);
+ Set<String> paths = _find_singletons_recursive(p_dir->get_subdir(i));
+
+ for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
+ file_paths.insert(E->get());
+ }
+ }
+
+ return file_paths;
+}
+
+void GDNativeLibrarySingletonEditor::_discover_singletons() {
+
+ EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem();
+
+ Set<String> file_paths = _find_singletons_recursive(dir);
+
+ bool changed = false;
+ Array current_files;
+ if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
+ current_files = ProjectSettings::get_singleton()->get("gdnative/singletons");
+ }
+ Array files;
+ for (Set<String>::Element *E = file_paths.front(); E; E = E->next()) {
+ if (!current_files.has(E->get())) {
+ changed = true;
+ }
+ files.append(E->get());
+ }
+
+ // Check for removed files
+ if (!changed) {
+ // Removed singleton
+ for (int j = 0; j < current_files.size(); j++) {
+ if (!files.has(current_files[j])) {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed) {
+
+ ProjectSettings::get_singleton()->set("gdnative/singletons", files);
+ _update_libraries(); // So singleton options (i.e. disabled) updates too
+ ProjectSettings::get_singleton()->save();
}
}
@@ -69,22 +108,40 @@ void GDNativeLibrarySingletonEditor::_update_libraries() {
updating = true;
libraries->clear();
- libraries->create_item(); //rppt
+ libraries->create_item(); // root item
- Vector<String> enabled_paths;
+ Array singletons;
if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
- enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons");
+ singletons = ProjectSettings::get_singleton()->get("gdnative/singletons");
}
- Set<String> enabled_list;
- for (int i = 0; i < enabled_paths.size(); i++) {
- enabled_list.insert(enabled_paths[i]);
+ Array singletons_disabled;
+ if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) {
+ singletons_disabled = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled");
}
- EditorFileSystemDirectory *fs = EditorFileSystem::get_singleton()->get_filesystem();
- if (fs) {
- _find_gdnative_singletons(fs, enabled_list);
+ Array updated_disabled;
+ for (int i = 0; i < singletons.size(); i++) {
+ bool enabled = true;
+ String path = singletons[i];
+ if (singletons_disabled.has(path)) {
+ enabled = false;
+ updated_disabled.push_back(path);
+ }
+ TreeItem *ti = libraries->create_item(libraries->get_root());
+ ti->set_text(0, path.get_file());
+ ti->set_tooltip(0, path);
+ ti->set_metadata(0, path);
+ ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE);
+ ti->set_text(1, "Disabled,Enabled");
+ ti->set_range(1, enabled ? 1 : 0);
+ ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0));
+ ti->set_editable(1, true);
}
+ // The singletons list changed, we must update the settings
+ if (updated_disabled.size() != singletons_disabled.size())
+ ProjectSettings::get_singleton()->set("gdnative/singletons_disabled", updated_disabled);
+
updating = false;
}
@@ -99,24 +156,29 @@ void GDNativeLibrarySingletonEditor::_item_edited() {
bool enabled = item->get_range(1);
String path = item->get_metadata(0);
- Vector<String> enabled_paths;
- if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
- enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons");
+ Array disabled_paths;
+ Array undo_paths;
+ if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) {
+ disabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled");
+ // Duplicate so redo works (not a reference)
+ disabled_paths = disabled_paths.duplicate();
+ // For undo, so we can reset the property.
+ undo_paths = disabled_paths.duplicate();
}
if (enabled) {
- if (enabled_paths.find(path) == -1) {
- enabled_paths.push_back(path);
- }
+ disabled_paths.erase(path);
} else {
- enabled_paths.erase(path);
+ if (disabled_paths.find(path) == -1)
+ disabled_paths.push_back(path);
}
- if (enabled_paths.size()) {
- ProjectSettings::get_singleton()->set("gdnative/singletons", enabled_paths);
- } else {
- ProjectSettings::get_singleton()->set("gdnative/singletons", Variant());
- }
+ undo_redo->create_action(enabled ? TTR("Enabled GDNative Singleton") : TTR("Disabled GDNative Singleton"));
+ undo_redo->add_do_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", disabled_paths);
+ undo_redo->add_do_method(this, "_update_libraries");
+ undo_redo->add_undo_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", undo_paths);
+ undo_redo->add_undo_method(this, "_update_libraries");
+ undo_redo->commit_action();
}
void GDNativeLibrarySingletonEditor::_notification(int p_what) {
@@ -131,9 +193,12 @@ void GDNativeLibrarySingletonEditor::_notification(int p_what) {
void GDNativeLibrarySingletonEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_item_edited"), &GDNativeLibrarySingletonEditor::_item_edited);
+ ClassDB::bind_method(D_METHOD("_discover_singletons"), &GDNativeLibrarySingletonEditor::_discover_singletons);
+ ClassDB::bind_method(D_METHOD("_update_libraries"), &GDNativeLibrarySingletonEditor::_update_libraries);
}
GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() {
+ undo_redo = EditorNode::get_singleton()->get_undo_redo();
libraries = memnew(Tree);
libraries->set_columns(2);
libraries->set_column_titles_visible(true);
@@ -143,6 +208,7 @@ GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() {
add_margin_child(TTR("Libraries: "), libraries, true);
updating = false;
libraries->connect("item_edited", this, "_item_edited");
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_discover_singletons");
}
#endif // TOOLS_ENABLED
diff --git a/modules/gdnative/gdnative_library_singleton_editor.h b/modules/gdnative/gdnative_library_singleton_editor.h
index cf5ab23501..b43080dfdb 100644
--- a/modules/gdnative/gdnative_library_singleton_editor.h
+++ b/modules/gdnative/gdnative_library_singleton_editor.h
@@ -36,18 +36,24 @@
#include "editor/project_settings_editor.h"
class GDNativeLibrarySingletonEditor : public VBoxContainer {
+ GDCLASS(GDNativeLibrarySingletonEditor, VBoxContainer);
+
+private:
Tree *libraries;
+ UndoRedo *undo_redo;
bool updating;
- void _update_libraries();
- void _find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list);
- void _item_edited();
+ static Set<String> _find_singletons_recursive(EditorFileSystemDirectory *p_dir);
protected:
void _notification(int p_what);
static void _bind_methods();
+ void _discover_singletons();
+ void _item_edited();
+ void _update_libraries();
+
public:
GDNativeLibrarySingletonEditor();
};
diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h
index 14e35b4692..483cd9c4e3 100644
--- a/modules/gdnative/include/gdnative/dictionary.h
+++ b/modules/gdnative/include/gdnative/dictionary.h
@@ -63,6 +63,8 @@ void GDAPI godot_dictionary_new(godot_dictionary *r_dest);
void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src);
void GDAPI godot_dictionary_destroy(godot_dictionary *p_self);
+godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep);
+
godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self);
godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self);
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
index c1bc9daab5..3a411755c1 100644
--- a/modules/gdnative/include/net/godot_net.h
+++ b/modules/gdnative/include/net/godot_net.h
@@ -111,37 +111,11 @@ typedef struct {
/* Binds a MultiplayerPeerGDNative to the provided interface */
void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is PacketPeer */
- godot_error (*get_packet)(void *, const uint8_t **, int *);
- godot_error (*put_packet)(void *, const uint8_t *, int);
- godot_int (*get_available_packet_count)(const void *);
- godot_int (*get_max_packet_size)(const void *);
-
- /* This is WebRTCPeer */
- void (*set_write_mode)(void *, godot_int);
- godot_int (*get_write_mode)(const void *);
- bool (*was_string_packet)(const void *);
- godot_int (*get_connection_state)(const void *);
-
- godot_error (*create_offer)(void *);
- godot_error (*set_remote_description)(void *, const char *, const char *);
- godot_error (*set_local_description)(void *, const char *, const char *);
- godot_error (*add_ice_candidate)(void *, const char *, int, const char *);
- godot_error (*poll)(void *);
-
- void *next; /* For extension? */
-} godot_net_webrtc_peer;
-
-/* Binds a PacketPeerGDNative to the provided interface */
-void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *);
-
#ifdef __cplusplus
}
#endif
+// WebRTC Bindings
+#include "net/godot_webrtc.h"
+
#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h
new file mode 100644
index 0000000000..783f7b727d
--- /dev/null
+++ b/modules/gdnative/include/net/godot_webrtc.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* godot_webrtc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 GODOT_NATIVEWEBRTC_H
+#define GODOT_NATIVEWEBRTC_H
+
+#include <gdnative/gdnative.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GODOT_NET_WEBRTC_API_MAJOR 3
+#define GODOT_NET_WEBRTC_API_MINOR 2
+
+/* Library Interface (used to set default GDNative WebRTC implementation */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ /* Called when the library is unset as default interface via godot_net_set_webrtc_library */
+ void (*unregistered)();
+
+ /* Used by WebRTCPeerConnection create when GDNative is the default implementation. */
+ /* Takes a pointer to WebRTCPeerConnectionGDNative, should bind and return OK, failure if binding was unsuccessful. */
+ godot_error (*create_peer_connection)(godot_object *);
+
+ void *next; /* For extension */
+} godot_net_webrtc_library;
+
+/* WebRTCPeerConnection interface */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is WebRTCPeerConnection */
+ godot_int (*get_connection_state)(const void *);
+
+ godot_error (*initialize)(void *, const godot_dictionary *);
+ godot_object *(*create_data_channel)(void *, const char *p_channel_name, const godot_dictionary *);
+ godot_error (*create_offer)(void *);
+ godot_error (*create_answer)(void *); /* unused for now, should be done automatically on set_local_description */
+ godot_error (*set_remote_description)(void *, const char *, const char *);
+ godot_error (*set_local_description)(void *, const char *, const char *);
+ godot_error (*add_ice_candidate)(void *, const char *, int, const char *);
+ godot_error (*poll)(void *);
+ void (*close)(void *);
+
+ void *next; /* For extension? */
+} godot_net_webrtc_peer_connection;
+
+/* WebRTCDataChannel interface */
+typedef struct {
+ godot_gdnative_api_version version; /* version of our API */
+
+ godot_object *data; /* User reference */
+
+ /* This is PacketPeer */
+ godot_error (*get_packet)(void *, const uint8_t **, int *);
+ godot_error (*put_packet)(void *, const uint8_t *, int);
+ godot_int (*get_available_packet_count)(const void *);
+ godot_int (*get_max_packet_size)(const void *);
+
+ /* This is WebRTCDataChannel */
+ void (*set_write_mode)(void *, godot_int);
+ godot_int (*get_write_mode)(const void *);
+ bool (*was_string_packet)(const void *);
+
+ godot_int (*get_ready_state)(const void *);
+ const char *(*get_label)(const void *);
+ bool (*is_ordered)(const void *);
+ int (*get_id)(const void *);
+ int (*get_max_packet_life_time)(const void *);
+ int (*get_max_retransmits)(const void *);
+ const char *(*get_protocol)(const void *);
+ bool (*is_negotiated)(const void *);
+
+ godot_error (*poll)(void *);
+ void (*close)(void *);
+
+ void *next; /* For extension? */
+} godot_net_webrtc_data_channel;
+
+/* Set the default GDNative library */
+godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *);
+/* Binds a WebRTCPeerConnectionGDNative to the provided interface */
+void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *);
+/* Binds a WebRTCDataChannelGDNative to the provided interface */
+void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 5cf144d4fe..f30c9da4c1 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -32,6 +32,7 @@
#include "gdnative/gdnative.h"
+#include "core/core_string_names.h"
#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
@@ -771,6 +772,27 @@ void NativeScriptInstance::notification(int p_notification) {
call_multilevel("_notification", args, 1);
}
+String NativeScriptInstance::to_string(bool *r_valid) {
+ if (has_method(CoreStringNames::get_singleton()->_to_string)) {
+ Variant::CallError ce;
+ Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
+ if (ce.error == Variant::CallError::CALL_OK) {
+ if (ret.get_type() != Variant::STRING) {
+ if (r_valid)
+ *r_valid = false;
+ ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
+ ERR_FAIL_V(String());
+ }
+ if (r_valid)
+ *r_valid = true;
+ return ret.operator String();
+ }
+ }
+ if (r_valid)
+ *r_valid = false;
+ return String();
+}
+
void NativeScriptInstance::refcount_incremented() {
Variant::CallError err;
call("_refcount_incremented", NULL, 0, err);
@@ -1309,7 +1331,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) {
for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) {
Vector<void *> &binding_data = *E->get();
- if (binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data)
+ if (p_idx < binding_data.size() && binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data)
binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]);
}
@@ -1345,7 +1367,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
if (!(*binding_data)[p_idx]) {
- const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+ const void *global_type_tag = get_global_type_tag(p_idx, p_object->get_class_name());
// no binding data yet, soooooo alloc new one \o/
(*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
@@ -1454,6 +1476,9 @@ const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_cl
const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+ if (!tags.has(p_class_name))
+ return NULL;
+
const void *tag = tags.get(p_class_name);
return tag;
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index a6865c6243..be1c499714 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -208,6 +208,7 @@ public:
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void notification(int p_notification);
+ String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
diff --git a/modules/gdnative/net/webrtc_peer_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp
index 60b1ed4fe4..d77fa057c5 100644
--- a/modules/gdnative/net/webrtc_peer_gdnative.cpp
+++ b/modules/gdnative/net/webrtc_gdnative.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* packet_peer_gdnative.cpp */
+/* webrtc_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -32,14 +32,29 @@
#include "modules/gdnative/include/net/godot_net.h"
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "modules/webrtc/webrtc_peer_gdnative.h"
+#include "modules/webrtc/webrtc_data_channel_gdnative.h"
+#include "modules/webrtc/webrtc_peer_connection_gdnative.h"
#endif
extern "C" {
-void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *p_impl) {
+void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *p_impl) {
#ifdef WEBRTC_GDNATIVE_ENABLED
- ((WebRTCPeerGDNative *)p_obj)->set_native_webrtc_peer(p_impl);
+ ((WebRTCPeerConnectionGDNative *)p_obj)->set_native_webrtc_peer_connection(p_impl);
+#endif
+}
+
+void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *p_impl) {
+#ifdef WEBRTC_GDNATIVE_ENABLED
+ ((WebRTCDataChannelGDNative *)p_obj)->set_native_webrtc_data_channel(p_impl);
+#endif
+}
+
+godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p_lib) {
+#ifdef WEBRTC_GDNATIVE_ENABLED
+ return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib);
+#else
+ return ERR_UNAVAILABLE;
#endif
}
}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index b279fdad8b..381c334231 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -62,7 +62,7 @@ public:
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
#if 0
// Rely on default implementations provided by ScriptInstance for the moment.
- // Note that multilevel call could be removed in 3.0 release, so stay tunned
+ // Note that multilevel call could be removed in 3.0 release, so stay tuned
// (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes)
virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 2094dca6e4..55d44ceec8 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -50,97 +50,6 @@
#include "editor/editor_node.h"
#include "gdnative_library_editor_plugin.h"
#include "gdnative_library_singleton_editor.h"
-// Class used to discover singleton gdnative files
-
-static void actual_discoverer_handler();
-
-class GDNativeSingletonDiscover : public Object {
- // GDCLASS(GDNativeSingletonDiscover, Object)
-
- virtual String get_class() const {
- // okay, this is a really dirty hack.
- // We're overriding get_class so we can connect it to a signal
- // This works because get_class is a virtual method, so we don't
- // need to register a new class to ClassDB just for this one
- // little signal.
-
- actual_discoverer_handler();
-
- return "Object";
- }
-};
-
-static Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) {
-
- Set<String> file_paths;
-
- // check children
-
- for (int i = 0; i < p_dir->get_file_count(); i++) {
- String file_name = p_dir->get_file(i);
- String file_type = p_dir->get_file_type(i);
-
- if (file_type != "GDNativeLibrary") {
- continue;
- }
-
- Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i));
- if (lib.is_valid() && lib->is_singleton()) {
- file_paths.insert(p_dir->get_file_path(i));
- }
- }
-
- // check subdirectories
- for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- Set<String> paths = get_gdnative_singletons(p_dir->get_subdir(i));
-
- for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
- file_paths.insert(E->get());
- }
- }
-
- return file_paths;
-}
-
-static void actual_discoverer_handler() {
-
- EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem();
-
- Set<String> file_paths = get_gdnative_singletons(dir);
-
- bool changed = false;
- Array current_files;
- if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
- current_files = ProjectSettings::get_singleton()->get("gdnative/singletons");
- }
- Array files;
- files.resize(file_paths.size());
- int i = 0;
- for (Set<String>::Element *E = file_paths.front(); E; i++, E = E->next()) {
- if (!current_files.has(E->get())) {
- changed = true;
- }
- files.set(i, E->get());
- }
-
- // Check for removed files
- if (!changed) {
- for (int j = 0; j < current_files.size(); j++) {
- if (!file_paths.has(current_files[j])) {
- changed = true;
- break;
- }
- }
- }
-
- if (changed) {
-
- ProjectSettings::get_singleton()->set("gdnative/singletons", files);
- ProjectSettings::get_singleton()->save();
- }
-}
-
-static GDNativeSingletonDiscover *discoverer = NULL;
class GDNativeExportPlugin : public EditorExportPlugin {
@@ -275,9 +184,6 @@ static void editor_init_callback() {
library_editor->set_name(TTR("GDNative"));
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(library_editor);
- discoverer = memnew(GDNativeSingletonDiscover);
- EditorFileSystem::get_singleton()->connect("filesystem_changed", discoverer, "get_class");
-
Ref<GDNativeExportPlugin> export_plugin;
export_plugin.instance();
@@ -335,30 +241,36 @@ void register_gdnative_types() {
if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) {
singletons = ProjectSettings::get_singleton()->get("gdnative/singletons");
}
-
- singleton_gdnatives.resize(singletons.size());
+ Array excluded = Array();
+ if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) {
+ excluded = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled");
+ }
for (int i = 0; i < singletons.size(); i++) {
String path = singletons[i];
- Ref<GDNativeLibrary> lib = ResourceLoader::load(path);
+ if (excluded.has(path))
+ continue;
- singleton_gdnatives.write[i].instance();
- singleton_gdnatives.write[i]->set_library(lib);
+ Ref<GDNativeLibrary> lib = ResourceLoader::load(path);
+ Ref<GDNative> singleton;
+ singleton.instance();
+ singleton->set_library(lib);
- if (!singleton_gdnatives.write[i]->initialize()) {
+ if (!singleton->initialize()) {
// Can't initialize. Don't make a native_call then
continue;
}
void *proc_ptr;
- Error err = singleton_gdnatives[i]->get_symbol(
+ Error err = singleton->get_symbol(
lib->get_symbol_prefix() + "gdnative_singleton",
proc_ptr);
if (err != OK) {
- ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_current_library_path()) + "\" found").utf8().get_data());
+ ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data());
} else {
+ singleton_gdnatives.push_back(singleton);
((void (*)())proc_ptr)();
}
}
@@ -388,12 +300,6 @@ void unregister_gdnative_types() {
memdelete(GDNativeCallRegistry::singleton);
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() && discoverer != NULL) {
- memdelete(discoverer);
- }
-#endif
-
ResourceLoader::remove_resource_format_loader(resource_loader_gdnlib);
resource_loader_gdnlib.unref();
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index 9bb1186269..be131c5402 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -146,23 +146,25 @@ void VideoStreamPlaybackGDNative::update(float p_delta) {
ERR_FAIL_COND(interface == NULL);
interface->update(data_struct, p_delta);
- if (pcm_write_idx >= 0) {
- // Previous remains
- int mixed = mix_callback(mix_udata, pcm, samples_decoded);
- if (mixed == samples_decoded) {
- pcm_write_idx = -1;
- } else {
- samples_decoded -= mixed;
- pcm_write_idx += mixed;
+ if (mix_callback) {
+ if (pcm_write_idx >= 0) {
+ // Previous remains
+ int mixed = mix_callback(mix_udata, pcm, samples_decoded);
+ if (mixed == samples_decoded) {
+ pcm_write_idx = -1;
+ } else {
+ samples_decoded -= mixed;
+ pcm_write_idx += mixed;
+ }
}
- }
- if (pcm_write_idx < 0) {
- samples_decoded = interface->get_audioframe(data_struct, pcm, AUX_BUFFER_SIZE);
- pcm_write_idx = mix_callback(mix_udata, pcm, samples_decoded);
- if (pcm_write_idx == samples_decoded) {
- pcm_write_idx = -1;
- } else {
- samples_decoded -= pcm_write_idx;
+ if (pcm_write_idx < 0) {
+ samples_decoded = interface->get_audioframe(data_struct, pcm, AUX_BUFFER_SIZE);
+ pcm_write_idx = mix_callback(mix_udata, pcm, samples_decoded);
+ if (pcm_write_idx == samples_decoded) {
+ pcm_write_idx = -1;
+ } else {
+ samples_decoded -= pcm_write_idx;
+ }
}
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 4f59b06ae6..62b65fe96b 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -56,6 +56,10 @@ static bool _is_hex_symbol(CharType c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
+static bool _is_bin_symbol(CharType c) {
+ return (c == '0' || c == '1');
+}
+
Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
Map<int, TextEdit::HighlighterInfo> color_map;
@@ -76,6 +80,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
+ bool is_bin_notation = false;
bool expect_type = false;
Color keyword_color;
Color color;
@@ -118,14 +123,26 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
is_hex_notation = false;
}
+ // disallow anything not a 0 or 1
+ if (is_bin_notation && (_is_bin_symbol(str[j]))) {
+ is_number = true;
+ } else if (is_bin_notation) {
+ is_bin_notation = false;
+ is_number = false;
+ } else {
+ is_bin_notation = false;
+ }
+
// check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
- if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == 'b' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
is_number = true;
is_symbol = false;
is_char = false;
if (str[j] == 'x' && str[j - 1] == '0') {
is_hex_notation = true;
+ } else if (str[j] == 'b' && str[j - 1] == '0') {
+ is_bin_notation = true;
}
}
@@ -330,7 +347,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
return color_map;
}
-String GDScriptSyntaxHighlighter::get_name() {
+String GDScriptSyntaxHighlighter::get_name() const {
return "GDScript";
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 9dc10a5d1b..9ba2c80552 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -65,7 +65,7 @@ public:
virtual void _update_cache();
virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
- virtual String get_name();
+ virtual String get_name() const;
virtual List<String> get_supported_languages();
};
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index eada389c51..3fb9268702 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -30,6 +30,7 @@
#include "gdscript.h"
+#include "core/core_string_names.h"
#include "core/engine.h"
#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
@@ -226,7 +227,7 @@ void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
const GDScript *current = this;
while (current) {
- for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
+ for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) {
GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
@@ -1064,7 +1065,7 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool
}
void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- // exported members, not doen yet!
+ // exported members, not done yet!
const GDScript *sptr = script.ptr();
List<PropertyInfo> props;
@@ -1234,6 +1235,27 @@ void GDScriptInstance::notification(int p_notification) {
}
}
+String GDScriptInstance::to_string(bool *r_valid) {
+ if (has_method(CoreStringNames::get_singleton()->_to_string)) {
+ Variant::CallError ce;
+ Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
+ if (ce.error == Variant::CallError::CALL_OK) {
+ if (ret.get_type() != Variant::STRING) {
+ if (r_valid)
+ *r_valid = false;
+ ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
+ ERR_FAIL_V(String());
+ }
+ if (r_valid)
+ *r_valid = true;
+ return ret.operator String();
+ }
+ }
+ if (r_valid)
+ *r_valid = false;
+ return String();
+}
+
Ref<Script> GDScriptInstance::get_script() const {
return script;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 38009b878d..716f536e89 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -251,6 +251,7 @@ public:
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
virtual void notification(int p_notification);
+ String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index bcf3140fa6..fa80c31984 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1951,7 +1951,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context
} else {
base_type.has_type = script->get_instance_base_type() != StringName();
base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.script_type = script->get_instance_base_type();
+ base_type.native_type = script->get_instance_base_type();
}
} else {
return;
@@ -2054,7 +2054,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context
if (!p_only_functions) {
List<PropertyInfo> members;
- tmp.get_property_list(&members);
+ p_base.value.get_property_list(&members);
for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
if (String(E->get().name).find("/") == -1) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 51ec0c1442..9590009a54 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3369,7 +3369,7 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) {
return;
}
- if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty()) {
+ if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty() || p_class->classname_used) {
_set_error("'extends' must be used before anything else.");
return;
@@ -3506,6 +3506,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
_set_error("'class_name' syntax: 'class_name <UniqueName>'");
return;
}
+ if (p_class->classname_used) {
+ _set_error("'class_name' already used for this class.");
+ return;
+ }
+
+ p_class->classname_used = true;
p_class->name = tokenizer->get_token_identifier(1);
@@ -4782,19 +4788,30 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- if (member._export.type != Variant::NIL) {
+ Variant::Type initial_type = member.data_type.has_type ? member.data_type.builtin_type : member._export.type;
+
+ if (initial_type != Variant::NIL && initial_type != Variant::OBJECT) {
IdentifierNode *id = alloc_node<IdentifierNode>();
id->name = member.identifier;
- ConstantNode *cn = alloc_node<ConstantNode>();
+ Node *expr;
- Variant::CallError ce2;
- cn->value = Variant::construct(member._export.type, NULL, 0, ce2);
+ // Make sure arrays and dictionaries are not shared
+ if (initial_type == Variant::ARRAY) {
+ expr = alloc_node<ArrayNode>();
+ } else if (initial_type == Variant::DICTIONARY) {
+ expr = alloc_node<DictionaryNode>();
+ } else {
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ Variant::CallError ce2;
+ cn->value = Variant::construct(initial_type, NULL, 0, ce2);
+ expr = cn;
+ }
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OperatorNode::OP_INIT_ASSIGN;
op->arguments.push_back(id);
- op->arguments.push_back(cn);
+ op->arguments.push_back(expr);
p_class->initializer->statements.push_back(op);
@@ -5237,6 +5254,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
if (base_script.is_valid()) {
String ident = base;
+ Ref<GDScript> find_subclass = base_script;
for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
@@ -5246,7 +5264,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
if (base_script->get_subclasses().has(subclass)) {
- base_script = base_script->get_subclasses()[subclass];
+ find_subclass = base_script->get_subclasses()[subclass];
} else if (base_script->get_constants().has(subclass)) {
Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
@@ -5254,7 +5272,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
_set_error("Constant is not a class: " + ident, p_class->line);
return;
}
- base_script = new_base_class;
+ find_subclass = new_base_class;
} else {
_set_error("Could not find subclass: " + ident, p_class->line);
@@ -5262,7 +5280,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
}
}
- script = base_script;
+ script = find_subclass;
} else if (!base_class) {
@@ -7462,7 +7480,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
return;
}
- // Replace assignment with implict conversion
+ // Replace assignment with implicit conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = v.line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
@@ -7491,30 +7509,6 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
v.data_type = expr_type;
v.data_type.is_constant = false;
}
- } else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) {
- // Create default value based on the type
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->line = v.line;
- id->name = v.identifier;
-
- ConstantNode *init = alloc_node<ConstantNode>();
- init->line = v.line;
- Variant::CallError err;
- init->value = Variant::construct(v.data_type.builtin_type, NULL, 0, err);
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->line = v.line;
- op->op = OperatorNode::OP_INIT_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(init);
-
- p_class->initializer->statements.push_front(op);
- v.initial_assignment = op;
-#ifdef DEBUG_ENABLED
- NewLineNode *nl = alloc_node<NewLineNode>();
- nl->line = v.line - 1;
- p_class->initializer->statements.push_front(nl);
-#endif
}
// Check export hint
@@ -7850,14 +7844,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
if (_is_type_compatible(assign_type, lv->datatype)) {
_mark_line_as_unsafe(lv->line);
} else {
- // Try implict conversion
+ // Try implicit conversion
if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) {
_set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" +
lv->datatype.to_string() + ").",
lv->line);
return;
}
- // Replace assignment with implict conversion
+ // Replace assignment with implicit conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = lv->line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
@@ -7981,14 +7975,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
if (_is_type_compatible(rh_type, lh_type)) {
_mark_line_as_unsafe(op->line);
} else {
- // Try implict conversion
+ // Try implicit conversion
if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) {
_set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" +
lh_type.to_string() + ").",
op->line);
return;
}
- // Replace assignment with implict conversion
+ // Replace assignment with implicit conversion
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
convert->line = op->line;
convert->function = GDScriptFunctions::TYPE_CONVERT;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 809bff8f20..5e4de11357 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -149,6 +149,7 @@ public:
bool tool;
StringName name;
bool extends_used;
+ bool classname_used;
StringName extends_file;
Vector<StringName> extends_class;
DataType base_type;
@@ -198,6 +199,7 @@ public:
tool = false;
type = TYPE_CLASS;
extends_used = false;
+ classname_used = false;
end_line = -1;
owner = NULL;
}
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index b8048fb5dd..a93d1ceebb 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -376,6 +376,11 @@ static bool _is_hex(CharType c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
+static bool _is_bin(CharType c) {
+
+ return (c == '0' || c == '1');
+}
+
void GDScriptTokenizerText::_make_token(Token p_type) {
TokenData &tk = tk_rb[tk_rb_pos];
@@ -877,6 +882,7 @@ void GDScriptTokenizerText::_advance() {
bool period_found = false;
bool exponent_found = false;
bool hexa_found = false;
+ bool bin_found = false;
bool sign_found = false;
String str;
@@ -887,16 +893,28 @@ void GDScriptTokenizerText::_advance() {
if (period_found || exponent_found) {
_make_error("Invalid numeric constant at '.'");
return;
+ } else if (bin_found) {
+ _make_error("Invalid binary constant at '.'");
+ return;
+ } else if (hexa_found) {
+ _make_error("Invalid hexadecimal constant at '.'");
+ return;
}
period_found = true;
} else if (GETCHAR(i) == 'x') {
- if (hexa_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) {
+ if (hexa_found || bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) {
_make_error("Invalid numeric constant at 'x'");
return;
}
hexa_found = true;
+ } else if (GETCHAR(i) == 'b') {
+ if (hexa_found || bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) {
+ _make_error("Invalid numeric constant at 'b'");
+ return;
+ }
+ bin_found = true;
} else if (!hexa_found && GETCHAR(i) == 'e') {
- if (exponent_found) {
+ if (exponent_found || bin_found) {
_make_error("Invalid numeric constant at 'e'");
return;
}
@@ -905,6 +923,8 @@ void GDScriptTokenizerText::_advance() {
//all ok
} else if (hexa_found && _is_hex(GETCHAR(i))) {
+ } else if (bin_found && _is_bin(GETCHAR(i))) {
+
} else if ((GETCHAR(i) == '-' || GETCHAR(i) == '+') && exponent_found) {
if (sign_found) {
_make_error("Invalid numeric constant at '-'");
@@ -930,6 +950,9 @@ void GDScriptTokenizerText::_advance() {
if (hexa_found) {
int64_t val = str.hex_to_int64();
_make_constant(val);
+ } else if (bin_found) {
+ int64_t val = str.bin_to_int64();
+ _make_constant(val);
} else if (period_found || exponent_found) {
double val = str.to_double();
_make_constant(val);
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 657aa1f9ce..890bd730f7 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1179,6 +1179,10 @@ void GridMapEditor::_floor_changed(float p_value) {
_update_selection_transform();
}
+void GridMapEditor::_floor_mouse_exited() {
+ floor->get_line_edit()->release_focus();
+}
+
void GridMapEditor::_bind_methods() {
ClassDB::bind_method("_text_changed", &GridMapEditor::_text_changed);
@@ -1188,6 +1192,7 @@ void GridMapEditor::_bind_methods() {
ClassDB::bind_method("_configure", &GridMapEditor::_configure);
ClassDB::bind_method("_item_selected_cbk", &GridMapEditor::_item_selected_cbk);
ClassDB::bind_method("_floor_changed", &GridMapEditor::_floor_changed);
+ ClassDB::bind_method("_floor_mouse_exited", &GridMapEditor::_floor_mouse_exited);
ClassDB::bind_method("_set_selection", &GridMapEditor::_set_selection);
ClassDB::bind_method(D_METHOD("_set_display_mode", "mode"), &GridMapEditor::_set_display_mode);
@@ -1221,6 +1226,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
spatial_editor_hb->add_child(floor);
floor->connect("value_changed", this, "_floor_changed");
+ floor->connect("mouse_exited", this, "_floor_mouse_exited");
+ floor->get_line_edit()->connect("mouse_exited", this, "_floor_mouse_exited");
spatial_editor_hb->add_child(memnew(VSeparator));
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 8e1948ea7d..da36165d4e 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -225,6 +225,7 @@ class GridMapEditor : public VBoxContainer {
void _set_selection(bool p_active, const Vector3 p_begin = Vector3(), const Vector3 p_end = Vector3());
void _floor_changed(float p_value);
+ void _floor_mouse_exited();
void _delete_selection();
void _fill_selection();
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index ef09e76d11..72199281ff 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1855,6 +1855,34 @@ void CSharpInstance::_call_notification(int p_notification) {
}
}
+String CSharpInstance::to_string(bool *r_valid) {
+ MonoObject *mono_object = get_mono_object();
+
+ if (mono_object == NULL) {
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ MonoException *exc = NULL;
+ MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ if (result == NULL) {
+ if (r_valid)
+ *r_valid = false;
+ return String();
+ }
+
+ return GDMonoMarshal::mono_string_to_godot(result);
+}
+
Ref<Script> CSharpInstance::get_script() const {
return script;
@@ -2020,7 +2048,7 @@ bool CSharpScript::_update_exports() {
for (int i = fields.size() - 1; i >= 0; i--) {
GDMonoField *field = fields[i];
- if (_get_member_export(top, field, prop_info, exported)) {
+ if (_get_member_export(field, prop_info, exported)) {
StringName name = field->get_name();
if (exported) {
@@ -2041,7 +2069,7 @@ bool CSharpScript::_update_exports() {
for (int i = properties.size() - 1; i >= 0; i--) {
GDMonoProperty *property = properties[i];
- if (_get_member_export(top, property, prop_info, exported)) {
+ if (_get_member_export(property, prop_info, exported)) {
StringName name = property->get_name();
if (exported) {
@@ -2168,17 +2196,19 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve
* Returns false if there was an error, otherwise true.
* If there was an error, r_prop_info and r_exported are not assigned any value.
*/
-bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
+bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
- StringName name = p_member->get_name();
+ // Goddammit, C++. All I wanted was some nested functions.
+#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
+ (m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
if (p_member->is_static()) {
if (p_member->has_attribute(CACHED_CLASS(ExportAttribute)))
- ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String());
+ ERR_PRINTS("Cannot export member because it is static: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
}
- if (member_info.has(name))
+ if (member_info.has(p_member->get_name()))
return false;
ManagedType type;
@@ -2191,19 +2221,22 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
CRASH_NOW();
}
- GDMonoMarshal::ExportInfo export_info;
- Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info);
+ Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
- r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+ r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
r_exported = false;
return true;
}
if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
- if (!property->has_getter() || !property->has_setter()) {
- ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String());
+ if (!property->has_getter()) {
+ ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+ return false;
+ }
+ if (!property->has_setter()) {
+ ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
}
}
@@ -2214,16 +2247,38 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
String hint_string;
if (variant_type == Variant::NIL) {
- ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String());
+ ERR_PRINTS("Unknown exported member type: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
return false;
- } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) {
- // TODO: Move to ExportInfo?
- variant_type = Variant::INT;
- hint = PROPERTY_HINT_ENUM;
+ }
- Vector<MonoClassField *> fields = type.type_class->get_enum_fields();
+ int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
- MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr());
+ if (hint_res == -1) {
+ ERR_EXPLAIN("Error while trying to determine information about the exported member: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+ ERR_FAIL_V(false);
+ }
+
+ if (hint_res == 0) {
+ hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
+ hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
+ }
+
+ r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
+ r_exported = true;
+
+ return true;
+
+#undef MEMBER_FULL_QUALIFIED_NAME
+}
+
+int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
+
+ if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
+ r_hint = PROPERTY_HINT_ENUM;
+
+ Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
+
+ MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr());
String name_only_hint_string;
@@ -2236,12 +2291,12 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
MonoClassField *field = fields[i];
if (i > 0) {
- hint_string += ",";
+ r_hint_string += ",";
name_only_hint_string += ",";
}
String enum_field_name = mono_field_get_name(field);
- hint_string += enum_field_name;
+ r_hint_string += enum_field_name;
name_only_hint_string += enum_field_name;
// TODO:
@@ -2251,54 +2306,73 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_
MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL);
if (val_obj == NULL) {
- ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " +
- p_class->get_full_name() + "." + name.operator String());
- return false;
+ ERR_EXPLAIN("Failed to get '" + enum_field_name + "' constant enum value");
+ ERR_FAIL_V(-1);
}
bool r_error;
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
if (r_error) {
- ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " +
- p_class->get_full_name() + "." + name.operator String());
- return false;
+ ERR_EXPLAIN("Failed to unbox '" + enum_field_name + "' constant enum value");
+ ERR_FAIL_V(-1);
}
if (val != (unsigned int)i) {
uses_default_values = false;
}
- hint_string += ":";
- hint_string += String::num_uint64(val);
+ r_hint_string += ":";
+ r_hint_string += String::num_uint64(val);
}
if (uses_default_values) {
// If we use the format NAME:VAL, that's what the editor displays.
// That's annoying if the user is not using custom values for the enum constants.
// This may not be needed in the future if the editor is changed to not display values.
- hint_string = name_only_hint_string;
+ r_hint_string = name_only_hint_string;
}
- } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) {
- GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(type.type_class);
+ } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) {
+ GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
CRASH_COND(field_native_class == NULL);
- hint = PROPERTY_HINT_RESOURCE_TYPE;
- hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
- } else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) {
- String elem_type_str = itos(export_info.array.element_type);
- hint = PROPERTY_HINT_TYPE_STRING;
- hint_string = elem_type_str + "/" + elem_type_str + ":" + export_info.array.element_native_name;
- } else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) {
- // TODO: There is no hint for this yet
+ r_hint = PROPERTY_HINT_RESOURCE_TYPE;
+ r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
+ } else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
+ // Nested arrays are not supported in the inspector
+
+ ManagedType elem_type;
+
+ if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type))
+ return 0;
+
+ Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type);
+
+ PropertyHint elem_hint = PROPERTY_HINT_NONE;
+ String elem_hint_string;
+
+ if (elem_variant_type == Variant::NIL) {
+ ERR_EXPLAIN("Unknown array element type");
+ ERR_FAIL_V(-1);
+ }
+
+ int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
+
+ if (hint_res == -1) {
+ ERR_EXPLAIN("Error while trying to determine information about the array element type");
+ ERR_FAIL_V(-1);
+ }
+
+ // Format: type/hint:hint_string
+ r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
+ r_hint = PROPERTY_HINT_TYPE_STRING;
+
+ } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
+ // TODO: Dictionaries are not supported in the inspector
} else {
- hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
- hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
+ return 0;
}
- r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE);
- r_exported = true;
-
- return true;
+ return 1;
}
#endif
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index fe4eed2e24..e735e0f741 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -127,7 +127,8 @@ class CSharpScript : public Script {
bool _update_exports();
#ifdef TOOLS_ENABLED
- bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
+ bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
+ static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error);
@@ -260,6 +261,8 @@ public:
virtual void notification(int p_notification);
void _call_notification(int p_notification);
+ virtual String to_string(bool *r_valid);
+
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
diff --git a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
index 967e3bcc19..e5044feb75 100644
--- a/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
@@ -186,7 +186,7 @@ namespace GodotSharpTools.Build
private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
{
- string arguments = string.Format(@"""{0}"" /v:normal /t:Build ""/p:{1}"" ""/l:{2},{3};{4}""",
+ string arguments = string.Format(@"""{0}"" /v:normal /t:Rebuild ""/p:{1}"" ""/l:{2},{3};{4}""",
solution,
"Configuration=" + config,
typeof(GodotBuildLogger).FullName,
@@ -196,7 +196,7 @@ namespace GodotSharpTools.Build
foreach (string customProperty in customProperties)
{
- arguments += " \"/p:" + customProperty + "\"";
+ arguments += " /p:" + customProperty;
}
return arguments;
diff --git a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
index 89279c69a6..f4ab11a222 100644
--- a/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs
@@ -80,7 +80,7 @@ namespace GodotSharpTools.Project
toolsGroup.AddProperty("DebugSymbols", "true");
toolsGroup.AddProperty("DebugType", "portable");
toolsGroup.AddProperty("Optimize", "false");
- toolsGroup.AddProperty("DefineConstants", "DEBUG;TOOLS;");
+ toolsGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
toolsGroup.AddProperty("ErrorReport", "prompt");
toolsGroup.AddProperty("WarningLevel", "4");
toolsGroup.AddProperty("ConsolePause", "false");
@@ -161,7 +161,7 @@ namespace GodotSharpTools.Project
debugGroup.AddProperty("DebugSymbols", "true");
debugGroup.AddProperty("DebugType", "portable");
debugGroup.AddProperty("Optimize", "false");
- debugGroup.AddProperty("DefineConstants", "DEBUG;");
+ debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
debugGroup.AddProperty("ErrorReport", "prompt");
debugGroup.AddProperty("WarningLevel", "4");
debugGroup.AddProperty("ConsolePause", "false");
@@ -170,6 +170,7 @@ namespace GodotSharpTools.Project
releaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ";
releaseGroup.AddProperty("DebugType", "portable");
releaseGroup.AddProperty("Optimize", "true");
+ releaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
releaseGroup.AddProperty("ErrorReport", "prompt");
releaseGroup.AddProperty("WarningLevel", "4");
releaseGroup.AddProperty("ConsolePause", "false");
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a408716641..cd7774e7a1 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2300,9 +2300,14 @@ void BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.name.empty())
continue;
+ String cname = method_info.name;
+
+ if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname))
+ continue;
+
MethodInterface imethod;
imethod.name = method_info.name;
- imethod.cname = imethod.name;
+ imethod.cname = cname;
if (method_info.flags & METHOD_FLAG_VIRTUAL)
imethod.is_virtual = true;
@@ -2975,6 +2980,13 @@ void BindingsGenerator::_populate_global_constants() {
}
}
+void BindingsGenerator::_initialize_blacklisted_methods() {
+
+ blacklisted_methods["Object"].push_back("to_string"); // there is already ToString
+ blacklisted_methods["Object"].push_back("_to_string"); // override ToString instead
+ blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it)
+}
+
void BindingsGenerator::_log(const char *p_format, ...) {
if (log_print_enabled) {
@@ -2992,6 +3004,8 @@ void BindingsGenerator::_initialize() {
enum_types.clear();
+ _initialize_blacklisted_methods();
+
_populate_object_type_interfaces();
_populate_builtin_type_interfaces();
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index bdba28c267..ffc73a7e3e 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -491,6 +491,10 @@ class BindingsGenerator {
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
+ Map<StringName, List<StringName> > blacklisted_methods;
+
+ void _initialize_blacklisted_methods();
+
struct NameCache {
StringName type_void;
StringName type_Array;
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index de3fd91223..9f132825fb 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -30,6 +30,7 @@
#include "godotsharp_builds.h"
+#include "core/os/os.h"
#include "core/vector.h"
#include "main/main.h"
@@ -351,7 +352,7 @@ bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
return true;
}
-bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
+bool GodotSharpBuilds::build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines) {
if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
return true; // No solution to build
@@ -366,6 +367,21 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config) {
pr.step("Building project solution", 0);
MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
+
+ // Add Godot defines
+ String constants = "GodotDefineConstants=\"";
+
+ for (int i = 0; i < p_godot_defines.size(); i++) {
+ constants += "GODOT_" + p_godot_defines[i].to_upper().replace("-", "_").replace(" ", "_").replace(";", "_") + ";";
+ }
+
+#ifdef REAL_T_IS_DOUBLE
+ constants += "GODOT_REAL_T_IS_DOUBLE;";
+#endif
+
+ constants += "\"";
+ build_info.custom_props.push_back(constants);
+
if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
return false;
@@ -393,7 +409,10 @@ bool GodotSharpBuilds::editor_build_callback() {
ERR_FAIL_COND_V(copy_err != OK, false);
}
- return build_project_blocking("Tools");
+ Vector<String> godot_defines;
+ godot_defines.push_back(OS::get_singleton()->get_name());
+ godot_defines.push_back(sizeof(void *) == 4 ? "32" : "64");
+ return build_project_blocking("Tools", godot_defines);
}
GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
diff --git a/modules/mono/editor/godotsharp_builds.h b/modules/mono/editor/godotsharp_builds.h
index 652d30538a..2e9050e12e 100644
--- a/modules/mono/editor/godotsharp_builds.h
+++ b/modules/mono/editor/godotsharp_builds.h
@@ -92,7 +92,7 @@ public:
static bool make_api_assembly(APIAssembly::Type p_api_type);
- static bool build_project_blocking(const String &p_config);
+ static bool build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines);
static bool editor_build_callback();
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index ee5fed1a0c..ae5b939b26 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -94,7 +94,12 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path));
- ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config));
+ // Turn export features into defines
+ Vector<String> godot_defines;
+ for (Set<String>::Element *E = p_features.front(); E; E = E->next()) {
+ godot_defines.push_back(E->get());
+ }
+ ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config, godot_defines));
// Add dependency assemblies
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 21ce9ca5c4..5d9e39b6c2 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -175,7 +175,10 @@ void MonoBottomPanel::_build_project_pressed() {
ERR_FAIL_COND(copy_err != OK);
}
- bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools");
+ Vector<String> godot_defines;
+ godot_defines.push_back(OS::get_singleton()->get_name());
+ godot_defines.push_back((sizeof(void *) == 4 ? "32" : "64"));
+ bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools", godot_defines);
if (build_success) {
// Notify running game for hot-reload
diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs
index 33b2b46712..a2ebbc0736 100644
--- a/modules/mono/glue/Managed/Files/AABB.cs
+++ b/modules/mono/glue/Managed/Files/AABB.cs
@@ -414,6 +414,21 @@ namespace Godot
_position = position;
_size = size;
}
+ public AABB(Vector3 position, real_t width, real_t height, real_t depth)
+ {
+ _position = position;
+ _size = new Vector3(width, height, depth);
+ }
+ public AABB(real_t x, real_t y, real_t z, Vector3 size)
+ {
+ _position = new Vector3(x, y, z);
+ _size = size;
+ }
+ public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth)
+ {
+ _position = new Vector3(x, y, z);
+ _size = new Vector3(width, height, depth);
+ }
public static bool operator ==(AABB left, AABB right)
{
diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs
index ac9576cebd..9cc31a0557 100644
--- a/modules/mono/glue/Managed/Files/Basis.cs
+++ b/modules/mono/glue/Managed/Files/Basis.cs
@@ -260,13 +260,13 @@ namespace Godot
Vector3 euler;
euler.z = 0.0f;
- real_t mxy = m.Row1[2];
+ real_t mzy = m.Row1[2];
- if (mxy < 1.0f)
+ if (mzy < 1.0f)
{
- if (mxy > -1.0f)
+ if (mzy > -1.0f)
{
- euler.x = Mathf.Asin(-mxy);
+ euler.x = Mathf.Asin(-mzy);
euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]);
euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]);
}
@@ -418,19 +418,11 @@ namespace Godot
public Basis Scaled(Vector3 scale)
{
- var m = this;
-
- m.Row0[0] *= scale.x;
- m.Row0[1] *= scale.x;
- m.Row0[2] *= scale.x;
- m.Row1[0] *= scale.y;
- m.Row1[1] *= scale.y;
- m.Row1[2] *= scale.y;
- m.Row2[0] *= scale.z;
- m.Row2[1] *= scale.z;
- m.Row2[2] *= scale.z;
-
- return m;
+ var b = this;
+ b.Row0 *= scale.x;
+ b.Row1 *= scale.y;
+ b.Row2 *= scale.z;
+ return b;
}
public real_t Tdotx(Vector3 with)
@@ -583,31 +575,29 @@ namespace Godot
public Basis(Vector3 axis, real_t phi)
{
- var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
-
+ Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
real_t cosine = Mathf.Cos(phi);
+ Row0.x = axisSq.x + cosine * (1.0f - axisSq.x);
+ Row1.y = axisSq.y + cosine * (1.0f - axisSq.y);
+ Row2.z = axisSq.z + cosine * (1.0f - axisSq.z);
+
real_t sine = Mathf.Sin(phi);
+ real_t t = 1.0f - cosine;
- Row0 = new Vector3
- (
- axis_sq.x + cosine * (1.0f - axis_sq.x),
- axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
- axis.z * axis.x * (1.0f - cosine) + axis.y * sine
- );
+ real_t xyzt = axis.x * axis.y * t;
+ real_t zyxs = axis.z * sine;
+ Row0.y = xyzt - zyxs;
+ Row1.x = xyzt + zyxs;
- Row1 = new Vector3
- (
- axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
- axis_sq.y + cosine * (1.0f - axis_sq.y),
- axis.y * axis.z * (1.0f - cosine) - axis.x * sine
- );
+ xyzt = axis.x * axis.z * t;
+ zyxs = axis.y * sine;
+ Row0.z = xyzt + zyxs;
+ Row2.x = xyzt - zyxs;
- Row2 = new Vector3
- (
- axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
- axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
- axis_sq.z + cosine * (1.0f - axis_sq.z)
- );
+ xyzt = axis.y * axis.z * t;
+ zyxs = axis.x * sine;
+ Row1.z = xyzt - zyxs;
+ Row2.y = xyzt + zyxs;
}
public Basis(Vector3 column0, Vector3 column1, Vector3 column2)
@@ -622,11 +612,12 @@ namespace Godot
// We need to assign the struct fields here first so we can't do it that way...
}
- internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
+ // Arguments are named such that xy is equal to calling x.y
+ internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
{
- Row0 = new Vector3(xx, xy, xz);
- Row1 = new Vector3(yx, yy, yz);
- Row2 = new Vector3(zx, zy, zz);
+ Row0 = new Vector3(xx, yx, zx);
+ Row1 = new Vector3(xy, yy, zy);
+ Row2 = new Vector3(xz, yz, zz);
}
public static Basis operator *(Basis left, Basis right)
diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs
index 9860feafdd..a0f105d55e 100644
--- a/modules/mono/glue/Managed/Files/DynamicObject.cs
+++ b/modules/mono/glue/Managed/Files/DynamicObject.cs
@@ -202,7 +202,7 @@ namespace Godot
//public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
//public override bool TryDeleteMember(DeleteMemberBinder binder);
- // Invokation on the object itself, e.g.: obj(param)
+ // Invocation on the object itself, e.g.: obj(param)
//public override bool TryInvoke(InvokeBinder binder, object[] args, out object result);
// No unnary operations to handle
diff --git a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
index 366d89b1c2..5023725f17 100644
--- a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs
@@ -24,12 +24,12 @@ namespace Godot
public T GetOwner<T>() where T : class
{
- return (T)(object)GetOwner();
+ return (T)(object)Owner;
}
public T GetOwnerOrNull<T>() where T : class
{
- return GetOwner() as T;
+ return Owner as T;
}
public T GetParent<T>() where T : class
diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs
index 7e72b0edb5..a1d63a62ef 100644
--- a/modules/mono/glue/Managed/Files/MarshalUtils.cs
+++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Collections.Generic;
namespace Godot
{
@@ -8,29 +9,151 @@ namespace Godot
static class MarshalUtils
{
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">
+ /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+ /// </exception>
static bool TypeIsGenericArray(Type type)
{
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">
+ /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+ /// </exception>
static bool TypeIsGenericDictionary(Type type)
{
return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
}
- static void ArrayGetElementType(Type type, out Type elementType)
+ static void ArrayGetElementType(Type arrayType, out Type elementType)
{
- elementType = type.GetGenericArguments()[0];
+ elementType = arrayType.GetGenericArguments()[0];
}
- static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType)
+ static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
{
- var genericArgs = type.GetGenericArguments();
-
+ var genericArgs = dictionaryType.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
}
+ static bool GenericIEnumerableIsAssignableFromType(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ return true;
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ return true;
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ return false;
+
+ return GenericIEnumerableIsAssignableFromType(baseType);
+ }
+
+ static bool GenericIDictionaryIsAssignableFromType(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ return true;
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ return true;
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ return false;
+
+ return GenericIDictionaryIsAssignableFromType(baseType);
+ }
+
+ static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ {
+ elementType = type.GetGenericArguments()[0];
+ return true;
+ }
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ {
+ elementType = interfaceType.GetGenericArguments()[0];
+ return true;
+ }
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ {
+ elementType = null;
+ return false;
+ }
+
+ return GenericIEnumerableIsAssignableFromType(baseType, out elementType);
+ }
+
+ static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ {
+ var genericArgs = type.GetGenericArguments();
+ keyType = genericArgs[0];
+ valueType = genericArgs[1];
+ return true;
+ }
+
+ foreach (var interfaceType in type.GetInterfaces())
+ {
+ if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+ {
+ var genericArgs = interfaceType.GetGenericArguments();
+ keyType = genericArgs[0];
+ valueType = genericArgs[1];
+ return true;
+ }
+ }
+
+ Type baseType = type.BaseType;
+
+ if (baseType == null)
+ {
+ keyType = null;
+ valueType = null;
+ return false;
+ }
+
+ return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType);
+ }
+
+ static Type MakeGenericArrayType(Type elemType)
+ {
+ return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
+ }
+
+ static Type MakeGenericDictionaryType(Type keyType, Type valueType)
+ {
+ return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
+ }
+
// TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue>
// TODO: EnumerableToArray and IDictionaryToDictionary can be optimized
@@ -64,5 +187,26 @@ namespace Godot
Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
}
}
+
+ internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr)
+ {
+#if DEBUG
+ if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType()))
+ throw new InvalidOperationException("The type does not implement IDictionary<,>");
+#endif
+
+ // TODO: Can we optimize this?
+
+ var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator();
+ var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator();
+
+ while (keys.MoveNext() && values.MoveNext())
+ {
+ object key = keys.Current;
+ object value = values.Current;
+
+ Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value);
+ }
+ }
}
}
diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs
index ff26c7fddf..8fb8730b88 100644
--- a/modules/mono/glue/Managed/Files/Mathf.cs
+++ b/modules/mono/glue/Managed/Files/Mathf.cs
@@ -44,9 +44,9 @@ namespace Godot
return (real_t)Math.Atan(s);
}
- public static real_t Atan2(real_t x, real_t y)
+ public static real_t Atan2(real_t y, real_t x)
{
- return (real_t)Math.Atan2(x, y);
+ return (real_t)Math.Atan2(y, x);
}
public static Vector2 Cartesian2Polar(real_t x, real_t y)
diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs
index f7bb41d523..33ff286769 100644
--- a/modules/mono/glue/Managed/Files/Transform2D.cs
+++ b/modules/mono/glue/Managed/Files/Transform2D.cs
@@ -298,6 +298,7 @@ namespace Godot
origin = originPos;
}
+ // Arguments are named such that xy is equal to calling x.y
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
x = new Vector2(xx, xy);
diff --git a/modules/mono/glue/Managed/IgnoredFiles/Node.cs b/modules/mono/glue/Managed/IgnoredFiles/Node.cs
index 99ba0f827a..cff61b1e0b 100644
--- a/modules/mono/glue/Managed/IgnoredFiles/Node.cs
+++ b/modules/mono/glue/Managed/IgnoredFiles/Node.cs
@@ -15,9 +15,10 @@ namespace Godot
throw new NotImplementedException();
}
- public Node GetOwner()
+ public Node Owner
{
- throw new NotImplementedException();
+ get => throw new NotImplementedException();
+ set => throw new NotImplementedException();
}
public Node GetParent()
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 4aef5684fd..47239f1260 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -162,7 +162,7 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
- GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
return NULL;
}
@@ -176,7 +176,7 @@ MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject
#ifdef DEBUG_ENABLED
CRASH_COND(!exc);
#endif
- GDMonoUtils::runtime_object_init(exc);
+ GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
GDMonoUtils::set_pending_exception((MonoException *)exc);
return NULL;
}
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 9779797d1a..2e79f87625 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -313,12 +313,32 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+ MonoReflectionType *key_reftype, *value_reftype;
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
+ GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
mono_field_set_value(p_object, mono_field, managed);
break;
}
+ MonoReflectionType *elem_reftype;
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
+ GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
@@ -432,26 +452,24 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
- MonoException *exc = NULL;
-
- GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_dict) {
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
mono_field_set_value(p_object, mono_field, managed);
break;
}
- exc = NULL;
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
- GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
- if (is_array) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
+ MonoReflectionType *key_reftype, *value_reftype;
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(),
+ GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
mono_field_set_value(p_object, mono_field, managed);
break;
}
@@ -462,6 +480,14 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ MonoReflectionType *elem_reftype;
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(),
+ GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+ mono_field_set_value(p_object, mono_field, managed);
+ break;
+ }
+
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
mono_field_set_value(p_object, mono_field, managed);
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
index e348583370..a7727ddf34 100644
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -47,9 +47,11 @@ class GDMonoField : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
- virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; }
+ virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
- virtual StringName get_name() GD_FINAL { return name; }
+ virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; }
+
+ virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;
virtual Visibility get_visibility() GD_FINAL;
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index d586b73cf9..c462b8f71d 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -35,7 +35,7 @@
namespace GDMonoMarshal {
-Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) {
+Variant::Type managed_to_variant_type(const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return Variant::BOOL;
@@ -157,10 +157,22 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
return Variant::ARRAY;
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
+ return Variant::DICTIONARY;
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
+ return Variant::ARRAY;
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return Variant::ARRAY;
}
@@ -169,71 +181,94 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
- MonoException *exc = NULL;
- GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_dict) {
- if (r_export_info) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+ return Variant::DICTIONARY;
+ }
- exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes),
- reftype, &key_reftype, &value_reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+ return Variant::ARRAY;
+ }
- ManagedType key_type = ManagedType::from_reftype(key_reftype);
- ManagedType value_type = ManagedType::from_reftype(value_reftype);
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
- r_export_info->dictionary.key_type = managed_to_variant_type(key_type);
- r_export_info->dictionary.key_native_name = NATIVE_GDMONOCLASS_NAME(key_type.type_class);
- r_export_info->dictionary.value_type = managed_to_variant_type(value_type);
- r_export_info->dictionary.value_native_name = NATIVE_GDMONOCLASS_NAME(value_type.type_class);
- }
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype))
+ return Variant::DICTIONARY;
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return Variant::DICTIONARY;
}
- exc = NULL;
- GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype))
+ return Variant::ARRAY;
- if (is_array) {
- if (r_export_info) {
- MonoReflectionType *elem_reftype;
+ if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
+ return Variant::ARRAY;
+ }
+ } break;
+
+ default: {
+ } break;
+ }
- exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType),
- reftype, &elem_reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ // Unknown
+ return Variant::NIL;
+}
- ManagedType elem_type = ManagedType::from_reftype(elem_reftype);
+bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
+ switch (p_array_type.type_encoding) {
+ case MONO_TYPE_GENERICINST: {
+ MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type());
- r_export_info->array.element_type = managed_to_variant_type(elem_type);
- r_export_info->array.element_native_name = NATIVE_GDMONOCLASS_NAME(elem_type.type_class);
- }
+ if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
+ MonoReflectionType *elem_reftype;
- return Variant::ARRAY;
- }
+ GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
- if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
- return Variant::DICTIONARY;
+ r_elem_type = ManagedType::from_reftype(elem_reftype);
+ return true;
}
- if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
- return Variant::ARRAY;
+ MonoReflectionType *elem_reftype;
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) {
+ r_elem_type = ManagedType::from_reftype(elem_reftype);
+ return true;
}
} break;
+ default: {
+ } break;
+ }
+
+ return false;
+}
+bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
+ switch (p_dictionary_type.type_encoding) {
+ case MONO_TYPE_GENERICINST: {
+ MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type());
+
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
+ MonoReflectionType *key_reftype;
+ MonoReflectionType *value_reftype;
+
+ GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype);
+
+ r_key_type = ManagedType::from_reftype(key_reftype);
+ r_value_type = ManagedType::from_reftype(value_reftype);
+ return true;
+ }
+
+ MonoReflectionType *key_reftype, *value_reftype;
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) {
+ r_key_type = ManagedType::from_reftype(key_reftype);
+ r_value_type = ManagedType::from_reftype(value_reftype);
+ return true;
+ }
+ } break;
default: {
} break;
}
- // Unknown
- return Variant::NIL;
+ return false;
}
String mono_to_utf8_string(MonoString *p_mono_string) {
@@ -502,10 +537,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+ MonoReflectionType *key_reftype, *value_reftype;
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
+ GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
+ MonoReflectionType *elem_reftype;
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(),
+ GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
@@ -603,28 +654,32 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
- MonoException *exc = NULL;
- GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_dict) {
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
}
- exc = NULL;
- GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_array) {
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ MonoReflectionType *key_reftype, *value_reftype;
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
+ return GDMonoUtils::create_managed_from(p_var->operator Dictionary(),
+ GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype));
+ }
+
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
}
+ MonoReflectionType *elem_reftype;
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) {
+ return GDMonoUtils::create_managed_from(p_var->operator Array(),
+ GDMonoUtils::Marshal::make_generic_array_type(elem_reftype));
+ }
+
if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
}
@@ -787,66 +842,60 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
return ptr ? Variant(*ptr) : Variant();
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
+ return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
+ }
+
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
- Dictionary dict;
- MonoException *exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
- return dict;
+ return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
+ }
+
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
+ return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
- Array array;
- MonoException *exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
- return array;
+ return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
} break;
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
- MonoException *exc = NULL;
-
- GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
- MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_dict) {
- exc = NULL;
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+ MonoException *exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Dictionary *>(ret);
}
- exc = NULL;
-
- GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
- MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
-
- if (is_array) {
- exc = NULL;
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+ MonoException *exc = NULL;
MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
UNLIKELY_UNHANDLED_EXCEPTION(exc);
return *unbox<Array *>(ret);
}
+ // The order in which we check the following interfaces is very important (dictionaries and generics first)
+
+ if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
+ return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
+ }
+
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) {
- Dictionary dict;
- exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
- return dict;
+ return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj);
+ }
+
+ if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) {
+ return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
- Array array;
- exc = NULL;
- invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc);
- UNLIKELY_UNHANDLED_EXCEPTION(exc);
- return array;
+ return GDMonoUtils::Marshal::enumerable_to_array(p_obj);
}
} break;
}
@@ -1075,4 +1124,5 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) {
return ret;
}
+
} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 8d3fd4b349..3fa958ac32 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -57,28 +57,10 @@ T unbox(MonoObject *p_obj) {
#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
-// FIXME: Made this struct in a hurry. It could be done differently.
-struct ExportInfo {
- struct ArrayInfo {
- Variant::Type element_type;
- String element_native_name;
-
- ArrayInfo() :
- element_type(Variant::NIL) {}
- } array;
- struct DictionaryInfo {
- Variant::Type key_type;
- String key_native_name;
- Variant::Type value_type;
- String value_native_name;
-
- DictionaryInfo() :
- key_type(Variant::NIL),
- value_type(Variant::NIL) {}
- } dictionary;
-};
+Variant::Type managed_to_variant_type(const ManagedType &p_type);
-Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL);
+bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
+bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
// String
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 7f11e4671d..f290c6c8ac 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -74,6 +74,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
method_info = MethodInfo();
}
+GDMonoClass *GDMonoMethod::get_enclosing_class() const {
+ return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
+}
+
bool GDMonoMethod::is_static() {
return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC;
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index f74cef438d..2fc8628f27 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -57,9 +57,11 @@ class GDMonoMethod : public IMonoClassMember {
MonoMethod *mono_method;
public:
- virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; }
+ virtual GDMonoClass *get_enclosing_class() const GD_FINAL;
- virtual StringName get_name() GD_FINAL { return name; }
+ virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; }
+
+ virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index 2700c460b0..d6efa60412 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -47,9 +47,11 @@ class GDMonoProperty : public IMonoClassMember {
MonoCustomAttrInfo *attributes;
public:
- virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+ virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; }
- virtual StringName get_name() GD_FINAL { return name; }
+ virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; }
+
+ virtual StringName get_name() const GD_FINAL { return name; }
virtual bool is_static() GD_FINAL;
virtual Visibility get_visibility() GD_FINAL;
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index bcf5712d16..413c8cba85 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -109,7 +109,7 @@ void MonoCache::clear_members() {
class_NodePath = NULL;
class_RID = NULL;
class_GodotObject = NULL;
- class_GodotReference = NULL;
+ class_GodotResource = NULL;
class_Node = NULL;
class_Control = NULL;
class_Spatial = NULL;
@@ -151,12 +151,27 @@ void MonoCache::clear_members() {
methodthunk_SignalAwaiter_FailureCallback = NULL;
methodthunk_GodotTaskScheduler_Activate = NULL;
+ // Start of MarshalUtils methods
+
methodthunk_MarshalUtils_TypeIsGenericArray = NULL;
methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL;
+
methodthunk_MarshalUtils_ArrayGetElementType = NULL;
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL;
+
+ methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL;
+ methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL;
+ methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info = NULL;
+ methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info = NULL;
+
+ methodthunk_MarshalUtils_MakeGenericArrayType = NULL;
+ methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL;
+
methodthunk_MarshalUtils_EnumerableToArray = NULL;
methodthunk_MarshalUtils_IDictionaryToDictionary = NULL;
+ methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL;
+
+ // End of MarshalUtils methods
task_scheduler_handle = Ref<MonoGCHandle>();
}
@@ -217,7 +232,7 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(NodePath, 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(GodotResource, GODOT_API_CLASS(Resource));
CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial));
@@ -258,12 +273,30 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0));
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0));
+ // Start of MarshalUtils methods
+
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1));
+
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
+
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3));
+
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3));
+
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2));
+
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2));
+
+ // End of MarshalUtils methods
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4));
@@ -271,7 +304,7 @@ void update_godot_api_cache() {
// TODO Move to CSharpLanguage::init() and do handle disposal
MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- GDMonoUtils::runtime_object_init(task_scheduler);
+ GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
mono_cache.godot_api_cache_updated = true;
@@ -372,11 +405,10 @@ MonoThread *get_current_thread() {
return mono_thread_current();
}
-void runtime_object_init(MonoObject *p_this_obj) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- // FIXME: Do not use mono_runtime_object_init, it aborts if an exception is thrown
- mono_runtime_object_init(p_this_obj);
- GD_MONO_END_RUNTIME_INVOKE;
+void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
+ GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
+ ERR_FAIL_NULL(ctor);
+ ctor->invoke_raw(p_this_obj, NULL, r_exc);
}
GDMonoClass *get_object_class(MonoObject *p_object) {
@@ -438,7 +470,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, p_class);
return mono_object;
}
@@ -448,7 +480,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath));
CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
@@ -460,7 +492,7 @@ MonoObject *create_managed_from(const RID &p_from) {
ERR_FAIL_NULL_V(mono_object, NULL);
// Construct
- GDMonoUtils::runtime_object_init(mono_object);
+ GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID));
CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
@@ -727,4 +759,115 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc);
}
+namespace Marshal {
+
+MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) {
+ TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) {
+ TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
+ ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
+ MonoException *exc = NULL;
+ invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+}
+
+void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+ DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
+ MonoException *exc = NULL;
+ invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+}
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
+ GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
+ GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+ GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+ GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
+ MonoException *exc = NULL;
+ MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return res;
+}
+
+Array enumerable_to_array(MonoObject *p_enumerable) {
+ Array result;
+ EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
+ MonoException *exc = NULL;
+ invoke_method_thunk(thunk, p_enumerable, &result, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return result;
+}
+
+Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
+ Dictionary result;
+ IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
+ MonoException *exc = NULL;
+ invoke_method_thunk(thunk, p_idictionary, &result, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return result;
+}
+
+Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
+ Dictionary result;
+ GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
+ MonoException *exc = NULL;
+ invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return result;
+}
+
+GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
+ MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
+ MonoException *exc = NULL;
+ MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
+}
+
+GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
+ MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
+ MonoException *exc = NULL;
+ MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
+ UNLIKELY_UNHANDLED_EXCEPTION(exc);
+ return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
+}
+
+} // namespace Marshal
+
+// namespace Marshal
+
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 87610e286c..ee239be959 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -60,10 +60,45 @@ typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, Mo
typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **);
typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **);
-typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
-typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoException **);
+typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoException **);
+typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType_with_info)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **);
+
+typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **);
+typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **);
+
typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **);
typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
+typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **);
+
+namespace Marshal {
+
+MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype);
+MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype);
+
+void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
+void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
+
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
+MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
+MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
+
+GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
+GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
+
+Array enumerable_to_array(MonoObject *p_enumerable);
+Dictionary idictionary_to_dictionary(MonoObject *p_idictionary);
+Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary);
+
+} // namespace Marshal
+
+// End of MarshalUtils methods
struct MonoCache {
@@ -114,7 +149,7 @@ struct MonoCache {
GDMonoClass *class_NodePath;
GDMonoClass *class_RID;
GDMonoClass *class_GodotObject;
- GDMonoClass *class_GodotReference;
+ GDMonoClass *class_GodotResource;
GDMonoClass *class_Node;
GDMonoClass *class_Control;
GDMonoClass *class_Spatial;
@@ -156,12 +191,27 @@ struct MonoCache {
SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback;
GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate;
+ // Start of MarshalUtils methods
+
TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray;
TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary;
+
ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType;
DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
+
+ GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType;
+ GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType;
+ GenericIEnumerableIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info;
+ GenericIDictionaryIsAssignableFromType_with_info methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info;
+
+ MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType;
+ MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType;
+
EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray;
IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary;
+ GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary;
+
+ // End of MarshalUtils methods
Ref<MonoGCHandle> task_scheduler_handle;
@@ -205,7 +255,7 @@ _FORCE_INLINE_ bool is_main_thread() {
return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current();
}
-void runtime_object_init(MonoObject *p_this_obj);
+void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = NULL);
GDMonoClass *get_object_class(MonoObject *p_object);
GDMonoClass *type_get_proxy_class(const StringName &p_type);
diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h
index 553d9edc72..f4de4e3230 100644
--- a/modules/mono/mono_gd/i_mono_class_member.h
+++ b/modules/mono/mono_gd/i_mono_class_member.h
@@ -53,9 +53,11 @@ public:
virtual ~IMonoClassMember() {}
- virtual MemberType get_member_type() = 0;
+ virtual GDMonoClass *get_enclosing_class() const = 0;
- virtual StringName get_name() = 0;
+ virtual MemberType get_member_type() const = 0;
+
+ virtual StringName get_name() const = 0;
virtual bool is_static() = 0;
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 1fdddd3925..877122985d 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -66,7 +66,7 @@ int sfind(const String &p_text, int p_from) {
break;
case 1: {
CharType c = src[read_pos];
- found = src[read_pos] == 's' || (c >= '0' || c <= '4');
+ found = src[read_pos] == 's' || (c >= '0' && c <= '4');
break;
}
default:
@@ -210,7 +210,7 @@ String str_format(const char *p_format, ...) {
#endif
#endif
-#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#if defined(MINGW_ENABLED) || defined(_MSC_VER) && _MSC_VER < 1900
#define vsnprintf(m_buffer, m_count, m_format, m_argptr) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_argptr)
#endif
diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp
index 62108620bd..eadc11fcee 100644
--- a/modules/recast/navigation_mesh_editor_plugin.cpp
+++ b/modules/recast/navigation_mesh_editor_plugin.cpp
@@ -54,26 +54,28 @@ void NavigationMeshEditor::_notification(int p_option) {
}
void NavigationMeshEditor::_bake_pressed() {
+ button_bake->set_pressed(false);
ERR_FAIL_COND(!node);
const String conf_warning = node->get_configuration_warning();
if (!conf_warning.empty()) {
err_dialog->set_text(conf_warning);
err_dialog->popup_centered_minsize();
- button_bake->set_pressed(false);
return;
}
- NavigationMeshGenerator::clear(node->get_navigation_mesh());
- NavigationMeshGenerator::bake(node->get_navigation_mesh(), node);
+ EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
+ EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
- node->update_gizmo();
+ if (node) {
+ node->update_gizmo();
+ }
}
void NavigationMeshEditor::_clear_pressed() {
if (node)
- NavigationMeshGenerator::clear(node->get_navigation_mesh());
+ EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
button_bake->set_pressed(false);
bake_info->set_text("");
diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp
index 80e98a13a5..0cac07e3e7 100644
--- a/modules/recast/navigation_mesh_generator.cpp
+++ b/modules/recast/navigation_mesh_generator.cpp
@@ -29,14 +29,31 @@
/*************************************************************************/
#include "navigation_mesh_generator.h"
-
-void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
+#include "core/math/quick_hull.h"
+#include "core/os/thread.h"
+#include "editor/editor_settings.h"
+#include "scene/3d/collision_shape.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/physics_body.h"
+#include "scene/resources/box_shape.h"
+#include "scene/resources/capsule_shape.h"
+#include "scene/resources/concave_polygon_shape.h"
+#include "scene/resources/convex_polygon_shape.h"
+#include "scene/resources/cylinder_shape.h"
+#include "scene/resources/plane_shape.h"
+#include "scene/resources/primitive_meshes.h"
+#include "scene/resources/shape.h"
+#include "scene/resources/sphere_shape.h"
+
+EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL;
+
+void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
p_verticies.push_back(p_vec3.x);
p_verticies.push_back(p_vec3.y);
p_verticies.push_back(p_vec3.z);
}
-void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
int current_vertex_count = 0;
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
@@ -91,23 +108,132 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform
}
}
-void NavigationMeshGenerator::_parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+ int face_count = p_faces.size() / 3;
+ int current_vertex_count = p_verticies.size() / 3;
+
+ for (int j = 0; j < face_count; j++) {
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_verticies);
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_verticies);
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_verticies);
+
+ p_indices.push_back(current_vertex_count + (j * 3 + 0));
+ p_indices.push_back(current_vertex_count + (j * 3 + 2));
+ p_indices.push_back(current_vertex_count + (j * 3 + 1));
+ }
+}
+
+void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask) {
- if (Object::cast_to<MeshInstance>(p_node)) {
+ if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (mesh.is_valid()) {
- _add_mesh(mesh, p_base_inverse * mesh_instance->get_global_transform(), p_verticies, p_indices);
+ _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_verticies, p_indices);
}
}
+ if (Object::cast_to<StaticBody>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) {
+ StaticBody *static_body = Object::cast_to<StaticBody>(p_node);
+
+ if (static_body->get_collision_layer() & p_collision_mask) {
+
+ for (int i = 0; i < p_node->get_child_count(); ++i) {
+ Node *child = p_node->get_child(i);
+ if (Object::cast_to<CollisionShape>(child)) {
+ CollisionShape *col_shape = Object::cast_to<CollisionShape>(child);
+
+ Transform transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+
+ Ref<Mesh> mesh;
+ Ref<Shape> s = col_shape->get_shape();
+
+ BoxShape *box = Object::cast_to<BoxShape>(*s);
+ if (box) {
+ Ref<CubeMesh> cube_mesh;
+ cube_mesh.instance();
+ cube_mesh->set_size(box->get_extents() * 2.0);
+ mesh = cube_mesh;
+ }
+
+ CapsuleShape *capsule = Object::cast_to<CapsuleShape>(*s);
+ if (capsule) {
+ Ref<CapsuleMesh> capsule_mesh;
+ capsule_mesh.instance();
+ capsule_mesh->set_radius(capsule->get_radius());
+ capsule_mesh->set_mid_height(capsule->get_height() / 2.0);
+ mesh = capsule_mesh;
+ }
+
+ CylinderShape *cylinder = Object::cast_to<CylinderShape>(*s);
+ if (cylinder) {
+ Ref<CylinderMesh> cylinder_mesh;
+ cylinder_mesh.instance();
+ cylinder_mesh->set_height(cylinder->get_height());
+ cylinder_mesh->set_bottom_radius(cylinder->get_radius());
+ cylinder_mesh->set_top_radius(cylinder->get_radius());
+ mesh = cylinder_mesh;
+ }
+
+ SphereShape *sphere = Object::cast_to<SphereShape>(*s);
+ if (sphere) {
+ Ref<SphereMesh> sphere_mesh;
+ sphere_mesh.instance();
+ sphere_mesh->set_radius(sphere->get_radius());
+ sphere_mesh->set_height(sphere->get_radius() * 2.0);
+ mesh = sphere_mesh;
+ }
+
+ ConcavePolygonShape *concave_polygon = Object::cast_to<ConcavePolygonShape>(*s);
+ if (concave_polygon) {
+ _add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices);
+ }
+
+ ConvexPolygonShape *convex_polygon = Object::cast_to<ConvexPolygonShape>(*s);
+ if (convex_polygon) {
+ Vector<Vector3> varr = Variant(convex_polygon->get_points());
+ Geometry::MeshData md;
+
+ Error err = QuickHull::build(varr, md);
+
+ if (err == OK) {
+ PoolVector3Array faces;
+
+ for (int j = 0; j < md.faces.size(); ++j) {
+ Geometry::MeshData::Face face = md.faces[j];
+
+ for (int k = 2; k < face.indices.size(); ++k) {
+ faces.push_back(md.vertices[face.indices[0]]);
+ faces.push_back(md.vertices[face.indices[k - 1]]);
+ faces.push_back(md.vertices[face.indices[k]]);
+ }
+ }
+
+ _add_faces(faces, transform, p_verticies, p_indices);
+ }
+ }
+
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, transform, p_verticies, p_indices);
+ }
+ }
+ }
+ }
+ }
+
+ if (Object::cast_to<Spatial>(p_node)) {
+
+ Spatial *spatial = Object::cast_to<Spatial>(p_node);
+ p_accumulated_transform = p_accumulated_transform * spatial->get_transform();
+ }
+
for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_base_inverse, p_node->get_child(i), p_verticies, p_indices);
+ _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask);
}
}
-void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
+void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
PoolVector<Vector3> nav_vertices;
@@ -126,15 +252,16 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con
for (unsigned int j = 0; j < ntris; j++) {
Vector<int> nav_indices;
nav_indices.resize(3);
+ // Polygon order in recast is opposite than godot's
nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0]));
- nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 1]));
- nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 2]));
+ nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 2]));
+ nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 1]));
p_nav_mesh->add_polygon(nav_indices);
}
}
}
-void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
+void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh,
Vector<float> &vertices, Vector<int> &indices) {
rcContext ctx;
@@ -256,7 +383,18 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh>
detail_mesh = 0;
}
-void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
+EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() {
+ return singleton;
+}
+
+EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() {
+ singleton = this;
+}
+
+EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() {
+}
+
+void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
ERR_FAIL_COND(!p_nav_mesh.is_valid());
@@ -266,7 +404,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
Vector<float> vertices;
Vector<int> indices;
- _parse_geometry(Object::cast_to<Spatial>(p_node)->get_global_transform().affine_inverse(), p_node, vertices, indices);
+ _parse_geometry(Object::cast_to<Spatial>(p_node)->get_transform().affine_inverse(), p_node, vertices, indices, p_nav_mesh->get_parsed_geometry_type(), p_nav_mesh->get_collision_mask());
if (vertices.size() > 0 && indices.size() > 0) {
@@ -296,9 +434,14 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
ep.step(TTR("Done!"), 11);
}
-void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
+void EditorNavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
if (p_nav_mesh.is_valid()) {
p_nav_mesh->clear_polygons();
p_nav_mesh->set_vertices(PoolVector<Vector3>());
}
}
+
+void EditorNavigationMeshGenerator::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &EditorNavigationMeshGenerator::bake);
+ ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &EditorNavigationMeshGenerator::clear);
+}
diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h
index 3adc01ccda..30a6e3c835 100644
--- a/modules/recast/navigation_mesh_generator.h
+++ b/modules/recast/navigation_mesh_generator.h
@@ -31,20 +31,23 @@
#ifndef NAVIGATION_MESH_GENERATOR_H
#define NAVIGATION_MESH_GENERATOR_H
-#include "core/os/thread.h"
#include "editor/editor_node.h"
-#include "editor/editor_settings.h"
-#include "scene/3d/mesh_instance.h"
#include "scene/3d/navigation_mesh.h"
-#include "scene/resources/shape.h"
#include <Recast.h>
-class NavigationMeshGenerator {
+class EditorNavigationMeshGenerator : public Object {
+ GDCLASS(EditorNavigationMeshGenerator, Object);
+
+ static EditorNavigationMeshGenerator *singleton;
+
protected:
+ static void _bind_methods();
+
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies);
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
- static void _parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices);
+ static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
+ static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
@@ -52,8 +55,13 @@ protected:
rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices);
public:
- static void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
- static void clear(Ref<NavigationMesh> p_nav_mesh);
+ static EditorNavigationMeshGenerator *get_singleton();
+
+ EditorNavigationMeshGenerator();
+ ~EditorNavigationMeshGenerator();
+
+ void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
+ void clear(Ref<NavigationMesh> p_nav_mesh);
};
#endif // NAVIGATION_MESH_GENERATOR_H
diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp
index f272cc4236..247d7f6144 100644
--- a/modules/recast/register_types.cpp
+++ b/modules/recast/register_types.cpp
@@ -32,8 +32,23 @@
#include "navigation_mesh_editor_plugin.h"
+#ifdef TOOLS_ENABLED
+EditorNavigationMeshGenerator *_nav_mesh_generator = NULL;
+#endif
+
void register_recast_types() {
+#ifdef TOOLS_ENABLED
EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
+ _nav_mesh_generator = memnew(EditorNavigationMeshGenerator);
+ ClassDB::register_class<EditorNavigationMeshGenerator>();
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", EditorNavigationMeshGenerator::get_singleton()));
+#endif
}
-void unregister_recast_types() {}
+void unregister_recast_types() {
+#ifdef TOOLS_ENABLED
+ if (_nav_mesh_generator) {
+ memdelete(_nav_mesh_generator);
+ }
+#endif
+}
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index 0538753a47..cc8205e400 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -123,7 +123,7 @@
<argument index="4" name="end" type="int" default="-1">
</argument>
<description>
- Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]\1[/code] and [code]\g&lt;name&gt;[/code] expanded and resolved. By default only the first instance is replaced but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
+ Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default only the first instance is replaced but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
</methods>
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 292ac5e97e..b5f4718c72 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -273,6 +273,7 @@ void AudioStreamOGGVorbis::_bind_methods() {
AudioStreamOGGVorbis::AudioStreamOGGVorbis() {
data = NULL;
+ data_len = 0;
length = 0;
sample_rate = 1;
channels = 1;
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 581809fec9..85fc867901 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -30,6 +30,7 @@
#include "visual_script.h"
+#include "core/core_string_names.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "scene/main/node.h"
@@ -45,15 +46,7 @@ bool VisualScriptNode::is_breakpoint() const {
return breakpoint;
}
-void VisualScriptNode::_notification(int p_what) {
-
- if (p_what == NOTIFICATION_POSTINITIALIZE) {
- validate_input_default_values();
- }
-}
-
void VisualScriptNode::ports_changed_notify() {
- validate_input_default_values();
emit_signal("ports_changed");
}
@@ -272,11 +265,7 @@ void VisualScript::_node_ports_changed(int p_id) {
Function &func = functions[function];
Ref<VisualScriptNode> vsn = func.nodes[p_id].node;
- if (OS::get_singleton()->get_main_loop() &&
- Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()) &&
- Engine::get_singleton()->is_editor_hint()) {
- vsn->validate_input_default_values(); //force validate default values when editing on editor
- }
+ vsn->validate_input_default_values();
//must revalidate all the functions
@@ -352,6 +341,7 @@ void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<Visual
Ref<VisualScriptNode> vsn = p_node;
vsn->connect("ports_changed", this, "_node_ports_changed", varray(p_id));
vsn->scripts_used.insert(this);
+ vsn->validate_input_default_values(); // Validate when fully loaded
func.nodes[p_id] = nd;
}
@@ -1976,6 +1966,27 @@ void VisualScriptInstance::notification(int p_notification) {
call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); //do as call
}
+String VisualScriptInstance::to_string(bool *r_valid) {
+ if (has_method(CoreStringNames::get_singleton()->_to_string)) {
+ Variant::CallError ce;
+ Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
+ if (ce.error == Variant::CallError::CALL_OK) {
+ if (ret.get_type() != Variant::STRING) {
+ if (r_valid)
+ *r_valid = false;
+ ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
+ ERR_FAIL_V(String());
+ }
+ if (r_valid)
+ *r_valid = true;
+ return ret.operator String();
+ }
+ }
+ if (r_valid)
+ *r_valid = false;
+ return String();
+}
+
Ref<Script> VisualScriptInstance::get_script() const {
return script;
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 0171b8e6f1..89f32f54f7 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -54,7 +54,6 @@ class VisualScriptNode : public Resource {
void validate_input_default_values();
protected:
- void _notification(int p_what);
void ports_changed_notify();
static void _bind_methods();
@@ -405,6 +404,7 @@ public:
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void notification(int p_notification);
+ String to_string(bool *r_valid);
bool set_variable(const StringName &p_variable, const Variant &p_value) {
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 28c9a11cb8..6bbfb1ec5c 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -1330,9 +1330,9 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
}
}
-void VisualScriptEditor::_generic_search() {
+void VisualScriptEditor::_generic_search(String p_base_type) {
port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position();
- new_connect_node_select->select_from_visual_script(String(""), false);
+ new_connect_node_select->select_from_visual_script(p_base_type, false);
}
void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
@@ -1787,7 +1787,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
call->set_base_path(sn->get_path_to(node));
call->set_base_type(node->get_class());
n = call;
-
method_select->select_from_instance(node);
selecting_method_id = base_id;
}
@@ -2042,7 +2041,7 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) {
Dictionary d = p_state;
if (d.has("function")) {
- edited_func = p_state;
+ edited_func = d["function"];
selected = edited_func;
}
@@ -2219,7 +2218,7 @@ Control *VisualScriptEditor::get_edit_menu() {
void VisualScriptEditor::_change_base_type() {
- select_base_type->popup_create(true);
+ select_base_type->popup_create(true, true);
}
void VisualScriptEditor::clear_edit_menu() {
@@ -2727,93 +2726,98 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
Ref<VisualScriptFunctionCall> vsfc = vsn;
vsfc->set_function(p_text);
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
- vsfc->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsfc->set_base_type(tg.gdclass);
+ if (p_connecting) {
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+
+ if (tg.type == Variant::OBJECT) {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
+ vsfc->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsfc->set_base_type(tg.gdclass);
- } else if (script->get_node(edited_func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsfc->set_base_type(base_type);
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsfc->set_base_type(base_type);
+ }
+ if (p_text == "call" || p_text == "call_deferred") {
+ vsfc->set_function(String(""));
+ }
}
- if (p_text == "call" || p_text == "call_deferred") {
- vsfc->set_function(String(""));
+ if (tg.script.is_valid()) {
+ vsfc->set_base_script(tg.script->get_path());
}
+ } else if (tg.type == Variant::NIL) {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
+ vsfc->set_base_type(String(""));
+ } else {
+ vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
+ vsfc->set_basic_type(tg.type);
}
- if (tg.script.is_valid()) {
- vsfc->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
- vsfc->set_base_type(String(""));
- } else {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
- vsfc->set_basic_type(tg.type);
}
}
- if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
+ // if connecting from another node the call mode shouldn't be self
+ if (p_connecting) {
+ if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
+ Ref<VisualScriptPropertySet> vsp = vsn;
- Ref<VisualScriptPropertySet> vsp = vsn;
-
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsp->set_base_type(tg.gdclass);
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ if (tg.type == Variant::OBJECT) {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsp->set_base_type(tg.gdclass);
- } else if (script->get_node(edited_func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsp->set_base_type(base_type);
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsp->set_base_type(base_type);
+ }
}
+ if (tg.script.is_valid()) {
+ vsp->set_base_script(tg.script->get_path());
+ }
+ } else if (tg.type == Variant::NIL) {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ } else {
+ vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
+ vsp->set_basic_type(tg.type);
}
- if (tg.script.is_valid()) {
- vsp->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- } else {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
- vsp->set_basic_type(tg.type);
}
- }
-
- if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
- Ref<VisualScriptPropertyGet> vsp = vsn;
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsp->set_base_type(tg.gdclass);
+ if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
+ Ref<VisualScriptPropertyGet> vsp = vsn;
- } else if (script->get_node(edited_func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsp->set_base_type(base_type);
+ VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
+ if (tg.type == Variant::OBJECT) {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ if (tg.gdclass != StringName()) {
+ vsp->set_base_type(tg.gdclass);
+
+ } else if (script->get_node(edited_func, port_action_node).is_valid()) {
+ PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint;
+ String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ vsp->set_base_type(base_type);
+ }
}
+ if (tg.script.is_valid()) {
+ vsp->set_base_script(tg.script->get_path());
+ }
+ } else if (tg.type == Variant::NIL) {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
+ vsp->set_base_type(String(""));
+ } else {
+ vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
+ vsp->set_basic_type(tg.type);
}
- if (tg.script.is_valid()) {
- vsp->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- } else {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
- vsp->set_basic_type(tg.type);
}
}
Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node);
@@ -3156,7 +3160,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_FIND_NODE_TYPE: {
- _generic_search();
+ _generic_search(script->get_instance_base_type());
} break;
case EDIT_COPY_NODES:
case EDIT_CUT_NODES: {
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 9387a09c78..3d3a49f672 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -212,7 +212,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _input(const Ref<InputEvent> &p_event);
- void _generic_search();
+ void _generic_search(String p_base_type = "");
void _members_gui_input(const Ref<InputEvent> &p_event);
void _on_nodes_delete();
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index ac5f73d113..ceec79c0d5 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -87,38 +87,17 @@ void VisualScriptPropertySelector::_update_search() {
TreeItem *root = search_options->create_item();
bool found = false;
+ StringName base = base_type;
+ List<StringName> base_list;
+ while (base) {
+ base_list.push_back(base);
+ base = ClassDB::get_parent_class_nocheck(base);
+ }
- if (properties) {
-
+ for (List<StringName>::Element *E = base_list.front(); E; E = E->next()) {
+ List<MethodInfo> methods;
List<PropertyInfo> props;
-
- if (instance) {
- instance->get_property_list(&props, true);
- } else if (type != Variant::NIL) {
- Variant v;
- Variant::CallError ce;
- v = Variant::construct(type, NULL, 0, ce);
-
- v.get_property_list(&props);
- } else {
-
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
-
- props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));
- Object::cast_to<Script>(obj)->get_script_property_list(&props);
- }
-
- StringName base = base_type;
- while (base) {
- props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));
- ClassDB::get_property_list(base, &props, true);
- base = ClassDB::get_parent_class_nocheck(base);
- }
- }
-
TreeItem *category = NULL;
-
Ref<Texture> type_icons[Variant::VARIANT_MAX] = {
Control::get_icon("Variant", "EditorIcons"),
Control::get_icon("bool", "EditorIcons"),
@@ -148,85 +127,164 @@ void VisualScriptPropertySelector::_update_search() {
Control::get_icon("PoolVector3Array", "EditorIcons"),
Control::get_icon("PoolColorArray", "EditorIcons")
};
-
- if (!seq_connect && !visual_script_generic) {
- get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
- get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
- if (type == Variant::INT) {
- get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
- }
- if (type == Variant::BOOL) {
- get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box);
+ {
+ String b = String(E->get());
+ category = search_options->create_item(root);
+ category->set_text(0, b.replace_first("*", ""));
+ category->set_selectable(0, false);
+ Ref<Texture> icon;
+ String rep = b.replace("*", "");
+ icon = EditorNode::get_singleton()->get_class_icon(rep);
+ category->set_icon(0, icon);
+ }
+ if (properties || seq_connect) {
+ if (instance) {
+ instance->get_property_list(&props, true);
+ } else {
+ Object *obj = ObjectDB::get_instance(script);
+ if (Object::cast_to<Script>(obj)) {
+ Object::cast_to<Script>(obj)->get_script_property_list(&props);
+ } else {
+ ClassDB::get_property_list(E->get(), &props, true);
+ }
}
- if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) {
- get_visual_node_names("operators/math/", Set<String>(), found, root, search_box);
+ for (List<PropertyInfo>::Element *F = props.front(); F; F = F->next()) {
+ if (!(F->get().usage & PROPERTY_USAGE_EDITOR) && !(F->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE))
+ continue;
+
+ if (type_filter.size() && type_filter.find(F->get().type) == -1)
+ continue;
+
+ // capitalize() also converts underscore to space, we'll match again both possible styles
+ String get_text_raw = String(vformat(TTR("Get %s"), F->get().name));
+ String get_text = get_text_raw.capitalize();
+ String set_text_raw = String(vformat(TTR("Set %s"), F->get().name));
+ String set_text = set_text_raw.capitalize();
+ String input = search_box->get_text().capitalize();
+
+ if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
+ TreeItem *item = search_options->create_item(category ? category : root);
+ item->set_text(0, get_text);
+ item->set_metadata(0, F->get().name);
+ item->set_icon(0, type_icons[F->get().type]);
+ item->set_metadata(1, "get");
+ item->set_collapsed(1);
+ item->set_selectable(0, true);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
+
+ if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
+ TreeItem *item = search_options->create_item(category ? category : root);
+ item->set_text(0, set_text);
+ item->set_metadata(0, F->get().name);
+ item->set_icon(0, type_icons[F->get().type]);
+ item->set_metadata(1, "set");
+ item->set_selectable(0, true);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
+ }
}
}
+ bool script_methods = false;
+ {
+ if (type != Variant::NIL) {
+ Variant v;
+ Variant::CallError ce;
+ v = Variant::construct(type, NULL, 0, ce);
+ v.get_method_list(&methods);
+ } else {
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (E->get().usage == PROPERTY_USAGE_CATEGORY) {
- if (category && category->get_children() == NULL) {
- memdelete(category); //old category was unused
- }
- category = search_options->create_item(root);
- category->set_text(0, E->get().name);
- category->set_selectable(0, false);
+ Object *obj = ObjectDB::get_instance(script);
+ if (Object::cast_to<Script>(obj)) {
+ methods.push_back(MethodInfo("*Script Methods"));
+ Object::cast_to<Script>(obj)->get_script_method_list(&methods);
- Ref<Texture> icon;
- if (E->get().name == "Script Variables") {
- icon = get_icon("Script", "EditorIcons");
} else {
- icon = EditorNode::get_singleton()->get_class_icon(E->get().name);
+ methods.push_back(MethodInfo("*" + String(E->get())));
+ ClassDB::get_method_list(E->get(), &methods, true, true);
}
- category->set_icon(0, icon);
- continue;
}
+ }
+ for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
+
+ String name = M->get().name.get_slice(":", 0);
+ if (!script_methods && name.begins_with("_") && !(M->get().flags & METHOD_FLAG_VIRTUAL))
+ continue;
- if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE))
+ if (virtuals_only && !(M->get().flags & METHOD_FLAG_VIRTUAL))
continue;
- if (type_filter.size() && type_filter.find(E->get().type) == -1)
+ if (!virtuals_only && (M->get().flags & METHOD_FLAG_VIRTUAL))
continue;
- // capitalize() also converts underscore to space, we'll match again both possible styles
- String get_text_raw = String(vformat(TTR("Get %s"), E->get().name));
- String get_text = get_text_raw.capitalize();
- String set_text_raw = String(vformat(TTR("Set %s"), E->get().name));
- String set_text = set_text_raw.capitalize();
- String input = search_box->get_text().capitalize();
-
- if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, get_text);
- item->set_metadata(0, E->get().name);
- item->set_icon(0, type_icons[E->get().type]);
- item->set_metadata(1, "get");
- item->set_collapsed(1);
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+ MethodInfo mi = M->get();
+ String desc_arguments;
+ if (mi.arguments.size() > 0) {
+ desc_arguments = "(";
+ for (int i = 0; i < mi.arguments.size(); i++) {
+
+ if (i > 0) {
+ desc_arguments += ", ";
+ }
+ if (mi.arguments[i].type == Variant::NIL) {
+ desc_arguments += "var";
+ } else if (mi.arguments[i].name.find(":") != -1) {
+ desc_arguments += mi.arguments[i].name.get_slice(":", 1);
+ mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
+ } else {
+ desc_arguments += Variant::get_type_name(mi.arguments[i].type);
+ }
+ }
+ desc_arguments += ")";
}
+ String desc_raw = mi.name + desc_arguments;
+ String desc = desc_raw.capitalize().replace("( ", "(");
- if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, set_text);
- item->set_metadata(0, E->get().name);
- item->set_icon(0, type_icons[E->get().type]);
- item->set_metadata(1, "set");
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+ if (search_box->get_text() != String() &&
+ name.findn(search_box->get_text()) == -1 &&
+ desc.findn(search_box->get_text()) == -1 &&
+ desc_raw.findn(search_box->get_text()) == -1) {
+ continue;
}
+
+ TreeItem *item = search_options->create_item(category ? category : root);
+ item->set_text(0, desc);
+ item->set_icon(0, get_icon("MemberMethod", "EditorIcons"));
+ item->set_metadata(0, name);
+ item->set_selectable(0, true);
+
+ item->set_metadata(1, "method");
+ item->set_collapsed(1);
+ item->set_selectable(1, false);
+
+ item->set_selectable(2, false);
+ item->set_metadata(2, connecting);
}
if (category && category->get_children() == NULL) {
memdelete(category); //old category was unused
}
}
+ if (properties) {
+ if (!seq_connect && !visual_script_generic) {
+ get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
+ get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
+ get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
+ if (type == Variant::INT) {
+ get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
+ }
+ if (type == Variant::BOOL) {
+ get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box);
+ }
+ if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) {
+ get_visual_node_names("operators/math/", Set<String>(), found, root, search_box);
+ }
+ }
+ }
if (seq_connect && !visual_script_generic) {
String text = search_box->get_text();
@@ -240,127 +298,16 @@ void VisualScriptPropertySelector::_update_search() {
get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
}
- if (visual_script_generic) {
+ if ((properties || seq_connect) && visual_script_generic) {
get_visual_node_names("", Set<String>(), found, root, search_box);
}
- List<MethodInfo> methods;
-
- if (type != Variant::NIL) {
- Variant v;
- Variant::CallError ce;
- v = Variant::construct(type, NULL, 0, ce);
- v.get_method_list(&methods);
- } else {
-
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
-
- methods.push_back(MethodInfo("*Script Methods"));
- Object::cast_to<Script>(obj)->get_script_method_list(&methods);
- }
-
- StringName base = base_type;
- while (base) {
- methods.push_back(MethodInfo("*" + String(base)));
- ClassDB::get_method_list(base, &methods, true, true);
- base = ClassDB::get_parent_class_nocheck(base);
- }
- }
- TreeItem *category = NULL;
- bool script_methods = false;
-
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("*")) {
- if (category && category->get_children() == NULL) {
- memdelete(category); //old category was unused
- }
- category = search_options->create_item(root);
- category->set_text(0, E->get().name.replace_first("*", ""));
- category->set_selectable(0, false);
-
- Ref<Texture> icon;
- script_methods = false;
- String rep = E->get().name.replace("*", "");
- if (E->get().name == "*Script Methods") {
- icon = get_icon("Script", "EditorIcons");
- script_methods = true;
- } else {
- icon = EditorNode::get_singleton()->get_class_icon(rep);
- }
- category->set_icon(0, icon);
-
- continue;
- }
-
- String name = E->get().name.get_slice(":", 0);
- if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL))
- continue;
-
- if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL))
- continue;
-
- if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL))
- continue;
-
- MethodInfo mi = E->get();
- String desc_arguments;
- if (mi.arguments.size() > 0) {
- desc_arguments = "(";
- for (int i = 0; i < mi.arguments.size(); i++) {
-
- if (i > 0) {
- desc_arguments += ", ";
- }
- if (mi.arguments[i].type == Variant::NIL) {
- desc_arguments += "var";
- } else if (mi.arguments[i].name.find(":") != -1) {
- desc_arguments += mi.arguments[i].name.get_slice(":", 1);
- mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
- } else {
- desc_arguments += Variant::get_type_name(mi.arguments[i].type);
- }
- }
- desc_arguments += ")";
- }
- String desc_raw = mi.name + desc_arguments;
- String desc = desc_raw.capitalize().replace("( ", "(");
-
- if (search_box->get_text() != String() &&
- name.findn(search_box->get_text()) == -1 &&
- desc.findn(search_box->get_text()) == -1 &&
- desc_raw.findn(search_box->get_text()) == -1) {
- continue;
- }
-
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, desc);
- item->set_icon(0, get_icon("MemberMethod", "EditorIcons"));
- item->set_metadata(0, name);
- item->set_selectable(0, true);
-
- item->set_metadata(1, "method");
- item->set_collapsed(1);
- item->set_selectable(1, false);
-
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
-
- if (category && category->get_children() == NULL) {
- memdelete(category); //old category was unused
- }
- }
-
TreeItem *selected_item = search_options->search_item_text(search_box->get_text());
if (!found && selected_item != NULL) {
selected_item->select(0);
found = true;
}
- if (category && category->get_children() == NULL) {
- memdelete(category); //old category was unused
- }
-
get_ok()->set_disabled(root->get_children() == NULL);
}
@@ -488,23 +435,23 @@ void VisualScriptPropertySelector::_item_selected() {
while (at_class != String()) {
- Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
- if (E) {
- for (int i = 0; i < E->get().methods.size(); i++) {
- if (E->get().methods[i].name == name) {
- text = E->get().methods[i].description;
+ Map<String, DocData::ClassDoc>::Element *C = dd->class_list.find(at_class);
+ if (C) {
+ for (int i = 0; i < C->get().methods.size(); i++) {
+ if (C->get().methods[i].name == name) {
+ text = C->get().methods[i].description;
}
}
}
at_class = ClassDB::get_parent_class_nocheck(at_class);
}
- Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type);
- if (E) {
- for (int i = 0; i < E->get().methods.size(); i++) {
+ Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(class_type);
+ if (T) {
+ for (int i = 0; i < T->get().methods.size(); i++) {
Vector<String> functions = name.rsplit("/", false, 1);
- if (E->get().methods[i].name == functions[functions.size() - 1]) {
- text = E->get().methods[i].description;
+ if (T->get().methods[i].name == functions[functions.size() - 1]) {
+ text = T->get().methods[i].description;
}
}
}
diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py
index 5ed245bad2..2e3a18ad0e 100644
--- a/modules/webrtc/config.py
+++ b/modules/webrtc/config.py
@@ -6,7 +6,8 @@ def configure(env):
def get_doc_classes():
return [
- "WebRTCPeer"
+ "WebRTCPeerConnection",
+ "WebRTCDataChannel"
]
def get_doc_path():
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
new file mode 100644
index 0000000000..dcc14d4ddb
--- /dev/null
+++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCDataChannel" inherits="PacketPeer" category="Core" version="3.2">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_label" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_max_packet_life_time" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_max_retransmits" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_protocol" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_ready_state" qualifiers="const">
+ <return type="int" enum="WebRTCDataChannel.ChannelState">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_negotiated" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_ordered" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="poll">
+ <return type="int" enum="Error">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="was_string_packet" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode">
+ </member>
+ </members>
+ <constants>
+ <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
+ </constant>
+ <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
+ </constant>
+ <constant name="STATE_CONNECTING" value="0" enum="ChannelState">
+ </constant>
+ <constant name="STATE_OPEN" value="1" enum="ChannelState">
+ </constant>
+ <constant name="STATE_CLOSING" value="2" enum="ChannelState">
+ </constant>
+ <constant name="STATE_CLOSED" value="3" enum="ChannelState">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeer.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 18d1345623..8b14c60deb 100644
--- a/modules/webrtc/doc_classes/WebRTCPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeer" inherits="PacketPeer" category="Core" version="3.2">
+<class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2">
<brief_description>
</brief_description>
<description>
@@ -19,6 +19,24 @@
<description>
</description>
</method>
+ <method name="close">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="create_data_channel">
+ <return type="WebRTCDataChannel">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="options" type="Dictionary" default="{
+
+}">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="create_offer">
<return type="int" enum="Error">
</return>
@@ -26,8 +44,18 @@
</description>
</method>
<method name="get_connection_state" qualifiers="const">
- <return type="int" enum="WebRTCPeer.ConnectionState">
+ <return type="int" enum="WebRTCPeerConnection.ConnectionState">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="initialize">
+ <return type="int" enum="Error">
</return>
+ <argument index="0" name="configuration" type="Dictionary" default="{
+
+}">
+ </argument>
<description>
</description>
</method>
@@ -57,19 +85,15 @@
<description>
</description>
</method>
- <method name="was_string_packet" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
</methods>
- <members>
- <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCPeer.WriteMode">
- </member>
- </members>
<signals>
- <signal name="new_ice_candidate">
+ <signal name="data_channel_received">
+ <argument index="0" name="channel" type="Object">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="ice_candidate_created">
<argument index="0" name="media" type="String">
</argument>
<argument index="1" name="index" type="int">
@@ -79,7 +103,7 @@
<description>
</description>
</signal>
- <signal name="offer_created">
+ <signal name="session_description_created">
<argument index="0" name="type" type="String">
</argument>
<argument index="1" name="sdp" type="String">
@@ -89,10 +113,6 @@
</signal>
</signals>
<constants>
- <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
- </constant>
- <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
- </constant>
<constant name="STATE_NEW" value="0" enum="ConnectionState">
</constant>
<constant name="STATE_CONNECTING" value="1" enum="ConnectionState">
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index ee7a766bd9..44e072cc89 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -29,27 +29,31 @@
/*************************************************************************/
#include "register_types.h"
-#include "webrtc_peer.h"
+#include "webrtc_data_channel.h"
+#include "webrtc_peer_connection.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
-#include "webrtc_peer_js.h"
+#include "webrtc_peer_connection_js.h"
#endif
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "webrtc_peer_gdnative.h"
+#include "webrtc_data_channel_gdnative.h"
+#include "webrtc_peer_connection_gdnative.h"
#endif
void register_webrtc_types() {
#ifdef JAVASCRIPT_ENABLED
- WebRTCPeerJS::make_default();
+ WebRTCPeerConnectionJS::make_default();
#elif defined(WEBRTC_GDNATIVE_ENABLED)
- WebRTCPeerGDNative::make_default();
+ WebRTCPeerConnectionGDNative::make_default();
#endif
- ClassDB::register_custom_instance_class<WebRTCPeer>();
+ ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
#ifdef WEBRTC_GDNATIVE_ENABLED
- ClassDB::register_class<WebRTCPeerGDNative>();
+ ClassDB::register_class<WebRTCPeerConnectionGDNative>();
+ ClassDB::register_class<WebRTCDataChannelGDNative>();
#endif
+ ClassDB::register_virtual_class<WebRTCDataChannel>();
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h
index 18a5dcc5aa..4923547a95 100644
--- a/modules/webrtc/register_types.h
+++ b/modules/webrtc/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
new file mode 100644
index 0000000000..2bd30e68f5
--- /dev/null
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* webrtc_data_channel.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 "webrtc_data_channel.h"
+
+void WebRTCDataChannel::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCDataChannel::close);
+
+ ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCDataChannel::was_string_packet);
+ ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCDataChannel::set_write_mode);
+ ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCDataChannel::get_write_mode);
+ ClassDB::bind_method(D_METHOD("get_ready_state"), &WebRTCDataChannel::get_ready_state);
+ ClassDB::bind_method(D_METHOD("get_label"), &WebRTCDataChannel::get_label);
+ ClassDB::bind_method(D_METHOD("is_ordered"), &WebRTCDataChannel::is_ordered);
+ ClassDB::bind_method(D_METHOD("get_id"), &WebRTCDataChannel::get_id);
+ ClassDB::bind_method(D_METHOD("get_max_packet_life_time"), &WebRTCDataChannel::get_max_packet_life_time);
+ ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits);
+ ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol);
+ ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
+
+ BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
+ BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+
+ BIND_ENUM_CONSTANT(STATE_CONNECTING);
+ BIND_ENUM_CONSTANT(STATE_OPEN);
+ BIND_ENUM_CONSTANT(STATE_CLOSING);
+ BIND_ENUM_CONSTANT(STATE_CLOSED);
+}
+
+WebRTCDataChannel::WebRTCDataChannel() {
+}
+
+WebRTCDataChannel::~WebRTCDataChannel() {
+}
diff --git a/modules/webrtc/webrtc_peer.h b/modules/webrtc/webrtc_data_channel.h
index e141c14655..0b161da784 100644
--- a/modules/webrtc/webrtc_peer.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer.h */
+/* webrtc_data_channel.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_PEER_H
-#define WEBRTC_PEER_H
+#ifndef WEBRTC_DATA_CHANNEL_H
+#define WEBRTC_DATA_CHANNEL_H
#include "core/io/packet_peer.h"
-class WebRTCPeer : public PacketPeer {
- GDCLASS(WebRTCPeer, PacketPeer);
+class WebRTCDataChannel : public PacketPeer {
+ GDCLASS(WebRTCDataChannel, PacketPeer);
public:
enum WriteMode {
@@ -42,30 +42,32 @@ public:
WRITE_MODE_BINARY,
};
- enum ConnectionState {
- STATE_NEW,
+ enum ChannelState {
STATE_CONNECTING,
- STATE_CONNECTED,
- STATE_DISCONNECTED,
- STATE_FAILED,
+ STATE_OPEN,
+ STATE_CLOSING,
STATE_CLOSED
};
protected:
static void _bind_methods();
- static WebRTCPeer *(*_create)();
public:
virtual void set_write_mode(WriteMode mode) = 0;
virtual WriteMode get_write_mode() const = 0;
virtual bool was_string_packet() const = 0;
- virtual ConnectionState get_connection_state() const = 0;
- virtual Error create_offer() = 0;
- virtual Error set_remote_description(String type, String sdp) = 0;
- virtual Error set_local_description(String type, String sdp) = 0;
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0;
+ virtual ChannelState get_ready_state() const = 0;
+ virtual String get_label() const = 0;
+ virtual bool is_ordered() const = 0;
+ virtual int get_id() const = 0;
+ virtual int get_max_packet_life_time() const = 0;
+ virtual int get_max_retransmits() const = 0;
+ virtual String get_protocol() const = 0;
+ virtual bool is_negotiated() const = 0;
+
virtual Error poll() = 0;
+ virtual void close() = 0;
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const = 0;
@@ -74,13 +76,10 @@ public:
virtual int get_max_packet_size() const = 0;
- static Ref<WebRTCPeer> create_ref();
- static WebRTCPeer *create();
-
- WebRTCPeer();
- ~WebRTCPeer();
+ WebRTCDataChannel();
+ ~WebRTCDataChannel();
};
-VARIANT_ENUM_CAST(WebRTCPeer::WriteMode);
-VARIANT_ENUM_CAST(WebRTCPeer::ConnectionState);
-#endif // WEBRTC_PEER_H
+VARIANT_ENUM_CAST(WebRTCDataChannel::WriteMode);
+VARIANT_ENUM_CAST(WebRTCDataChannel::ChannelState);
+#endif // WEBRTC_DATA_CHANNEL_H
diff --git a/modules/webrtc/webrtc_peer_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp
index f782944980..4f33491af2 100644
--- a/modules/webrtc/webrtc_peer_gdnative.cpp
+++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_gdnative.cpp */
+/* webrtc_data_channel_gdnative.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -30,84 +30,106 @@
#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "webrtc_peer_gdnative.h"
+#include "webrtc_data_channel_gdnative.h"
+#include "core/io/resource_loader.h"
+#include "modules/gdnative/nativescript/nativescript.h"
-void WebRTCPeerGDNative::_bind_methods() {
+void WebRTCDataChannelGDNative::_bind_methods() {
}
-WebRTCPeerGDNative::WebRTCPeerGDNative() {
+WebRTCDataChannelGDNative::WebRTCDataChannelGDNative() {
interface = NULL;
}
-WebRTCPeerGDNative::~WebRTCPeerGDNative() {
+WebRTCDataChannelGDNative::~WebRTCDataChannelGDNative() {
}
-Error WebRTCPeerGDNative::create_offer() {
+Error WebRTCDataChannelGDNative::poll() {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->create_offer(interface->data);
+ return (Error)interface->poll(interface->data);
}
-Error WebRTCPeerGDNative::set_local_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+void WebRTCDataChannelGDNative::close() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->close(interface->data);
}
-Error WebRTCPeerGDNative::set_remote_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+void WebRTCDataChannelGDNative::set_write_mode(WriteMode p_mode) {
+ ERR_FAIL_COND(interface == NULL);
+ interface->set_write_mode(interface->data, p_mode);
}
-Error WebRTCPeerGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+WebRTCDataChannel::WriteMode WebRTCDataChannelGDNative::get_write_mode() const {
+ ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY);
+ return (WriteMode)interface->get_write_mode(interface->data);
}
-Error WebRTCPeerGDNative::poll() {
- ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
- return (Error)interface->poll(interface->data);
+bool WebRTCDataChannelGDNative::was_string_packet() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->was_string_packet(interface->data);
}
-void WebRTCPeerGDNative::set_write_mode(WriteMode p_mode) {
- ERR_FAIL_COND(interface == NULL);
- interface->set_write_mode(interface->data, p_mode);
+WebRTCDataChannel::ChannelState WebRTCDataChannelGDNative::get_ready_state() const {
+ ERR_FAIL_COND_V(interface == NULL, STATE_CLOSED);
+ return (ChannelState)interface->get_ready_state(interface->data);
}
-WebRTCPeer::WriteMode WebRTCPeerGDNative::get_write_mode() const {
- ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY);
- return (WriteMode)interface->get_write_mode(interface->data);
+String WebRTCDataChannelGDNative::get_label() const {
+ ERR_FAIL_COND_V(interface == NULL, "");
+ return String(interface->get_label(interface->data));
}
-bool WebRTCPeerGDNative::was_string_packet() const {
+bool WebRTCDataChannelGDNative::is_ordered() const {
ERR_FAIL_COND_V(interface == NULL, false);
- return interface->was_string_packet(interface->data);
+ return interface->is_ordered(interface->data);
}
-WebRTCPeer::ConnectionState WebRTCPeerGDNative::get_connection_state() const {
- ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED);
- return STATE_DISCONNECTED;
+int WebRTCDataChannelGDNative::get_id() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_id(interface->data);
+}
+
+int WebRTCDataChannelGDNative::get_max_packet_life_time() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_max_packet_life_time(interface->data);
+}
+
+int WebRTCDataChannelGDNative::get_max_retransmits() const {
+ ERR_FAIL_COND_V(interface == NULL, -1);
+ return interface->get_max_retransmits(interface->data);
+}
+
+String WebRTCDataChannelGDNative::get_protocol() const {
+ ERR_FAIL_COND_V(interface == NULL, "");
+ return String(interface->get_protocol(interface->data));
+}
+
+bool WebRTCDataChannelGDNative::is_negotiated() const {
+ ERR_FAIL_COND_V(interface == NULL, false);
+ return interface->is_negotiated(interface->data);
}
-Error WebRTCPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size);
}
-Error WebRTCPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+Error WebRTCDataChannelGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
}
-int WebRTCPeerGDNative::get_max_packet_size() const {
+int WebRTCDataChannelGDNative::get_max_packet_size() const {
ERR_FAIL_COND_V(interface == NULL, 0);
return interface->get_max_packet_size(interface->data);
}
-int WebRTCPeerGDNative::get_available_packet_count() const {
+int WebRTCDataChannelGDNative::get_available_packet_count() const {
ERR_FAIL_COND_V(interface == NULL, 0);
return interface->get_available_packet_count(interface->data);
}
-void WebRTCPeerGDNative::set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl) {
+void WebRTCDataChannelGDNative::set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl) {
interface = p_impl;
}
diff --git a/modules/webrtc/webrtc_peer_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h
index 6786cec8ea..3685f86353 100644
--- a/modules/webrtc/webrtc_peer_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_gdnative.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_gdnative.h */
+/* webrtc_data_channel_gdnative.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -30,37 +30,39 @@
#ifdef WEBRTC_GDNATIVE_ENABLED
-#ifndef WEBRTC_PEER_GDNATIVE_H
-#define WEBRTC_PEER_GDNATIVE_H
+#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#define WEBRTC_DATA_CHANNEL_GDNATIVE_H
#include "modules/gdnative/include/net/godot_net.h"
-#include "webrtc_peer.h"
+#include "webrtc_data_channel.h"
-class WebRTCPeerGDNative : public WebRTCPeer {
- GDCLASS(WebRTCPeerGDNative, WebRTCPeer);
+class WebRTCDataChannelGDNative : public WebRTCDataChannel {
+ GDCLASS(WebRTCDataChannelGDNative, WebRTCDataChannel);
protected:
static void _bind_methods();
private:
- const godot_net_webrtc_peer *interface;
+ const godot_net_webrtc_data_channel *interface;
public:
- static WebRTCPeer *_create() { return memnew(WebRTCPeerGDNative); }
- static void make_default() { WebRTCPeer::_create = WebRTCPeerGDNative::_create; }
-
- void set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl);
+ void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl);
virtual void set_write_mode(WriteMode mode);
virtual WriteMode get_write_mode() const;
virtual bool was_string_packet() const;
- virtual ConnectionState get_connection_state() const;
- virtual Error create_offer();
- virtual Error set_remote_description(String type, String sdp);
- virtual Error set_local_description(String type, String sdp);
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual ChannelState get_ready_state() const;
+ virtual String get_label() const;
+ virtual bool is_ordered() const;
+ virtual int get_id() const;
+ virtual int get_max_packet_life_time() const;
+ virtual int get_max_retransmits() const;
+ virtual String get_protocol() const;
+ virtual bool is_negotiated() const;
+
virtual Error poll();
+ virtual void close();
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const;
@@ -69,10 +71,10 @@ public:
virtual int get_max_packet_size() const;
- WebRTCPeerGDNative();
- ~WebRTCPeerGDNative();
+ WebRTCDataChannelGDNative();
+ ~WebRTCDataChannelGDNative();
};
-#endif // WEBRTC_PEER_GDNATIVE_H
+#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
new file mode 100644
index 0000000000..2e7c64aa72
--- /dev/null
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -0,0 +1,352 @@
+/*************************************************************************/
+/* webrtc_data_channel_js.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 "webrtc_data_channel_js.h"
+#include "emscripten.h"
+
+extern "C" {
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_error();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_open();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_close();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
+ peer->_on_message(p_data, p_size, p_is_string);
+}
+}
+
+void WebRTCDataChannelJS::_on_open() {
+ in_buffer.resize(16);
+}
+
+void WebRTCDataChannelJS::_on_close() {
+ close();
+}
+
+void WebRTCDataChannelJS::_on_error() {
+ close();
+}
+
+void WebRTCDataChannelJS::_on_message(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++;
+}
+
+void WebRTCDataChannelJS::close() {
+ in_buffer.resize(0);
+ queue_count = 0;
+ _was_string = false;
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return;
+ var channel = dict["channel"];
+ channel.onopen = null;
+ channel.onclose = null;
+ channel.onerror = null;
+ channel.onmessage = null;
+ channel.close();
+ }, _js_id);
+ /* clang-format on */
+}
+
+Error WebRTCDataChannelJS::poll() {
+ return OK;
+}
+
+WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const {
+ /* clang-format off */
+ return (ChannelState) EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 3; // CLOSED
+ var channel = dict["channel"];
+ switch(channel.readyState) {
+ case "connecting":
+ return 0;
+ case "open":
+ return 1;
+ case "closing":
+ return 2;
+ case "closed":
+ return 3;
+ }
+ return 3; // CLOSED
+ }, _js_id);
+ /* clang-format on */
+}
+
+int WebRTCDataChannelJS::get_available_packet_count() const {
+ return queue_count;
+}
+
+Error WebRTCDataChannelJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
+
+ 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;
+}
+
+Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
+
+ int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0;
+
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var channel = dict["channel"];
+ var bytes_array = new Uint8Array($2);
+ var i = 0;
+
+ for(i=0; i<$2; i++) {
+ bytes_array[i] = getValue($1+i, 'i8');
+ }
+
+ if ($3) {
+ channel.send(bytes_array.buffer);
+ } else {
+ var string = new TextDecoder("utf-8").decode(bytes_array);
+ channel.send(string);
+ }
+ }, _js_id, p_buffer, p_buffer_size, is_bin);
+ /* clang-format on */
+
+ return OK;
+}
+
+int WebRTCDataChannelJS::get_max_packet_size() const {
+ return 1200;
+}
+
+void WebRTCDataChannelJS::set_write_mode(WriteMode p_mode) {
+ _write_mode = p_mode;
+}
+
+WebRTCDataChannel::WriteMode WebRTCDataChannelJS::get_write_mode() const {
+ return _write_mode;
+}
+
+bool WebRTCDataChannelJS::was_string_packet() const {
+ return _was_string;
+}
+
+String WebRTCDataChannelJS::get_label() const {
+ return _label;
+}
+
+/* clang-format off */
+#define _JS_GET(PROP) \
+EM_ASM_INT({ \
+ var dict = Module.IDHandler.get($0); \
+ if (!dict || !dict["channel"]) { \
+ return 0; \
+ }; \
+ return dict["channel"].PROP; \
+}, _js_id)
+/* clang-format on */
+
+bool WebRTCDataChannelJS::is_ordered() const {
+ return _JS_GET(ordered);
+}
+
+int WebRTCDataChannelJS::get_id() const {
+ return _JS_GET(id);
+}
+
+int WebRTCDataChannelJS::get_max_packet_life_time() const {
+ return _JS_GET(maxPacketLifeTime);
+}
+
+int WebRTCDataChannelJS::get_max_retransmits() const {
+ return _JS_GET(maxRetransmits);
+}
+
+String WebRTCDataChannelJS::get_protocol() const {
+ return _protocol;
+}
+
+bool WebRTCDataChannelJS::is_negotiated() const {
+ return _JS_GET(negotiated);
+}
+
+WebRTCDataChannelJS::WebRTCDataChannelJS() {
+ queue_count = 0;
+ _was_string = false;
+ _write_mode = WRITE_MODE_BINARY;
+ _js_id = 0;
+}
+
+WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
+ queue_count = 0;
+ _was_string = false;
+ _write_mode = WRITE_MODE_BINARY;
+ _js_id = js_id;
+
+ /* clang-format off */
+ EM_ASM({
+ var c_ptr = $0;
+ var dict = Module.IDHandler.get($1);
+ if (!dict) return;
+ var channel = dict["channel"];
+ dict["ptr"] = c_ptr;
+
+ channel.binaryType = "arraybuffer";
+ channel.onopen = function (evt) {
+ ccall("_emrtc_on_ch_open",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onclose = function (evt) {
+ ccall("_emrtc_on_ch_close",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onerror = function (evt) {
+ ccall("_emrtc_on_ch_error",
+ "void",
+ ["number"],
+ [c_ptr]
+ );
+ };
+ channel.onmessage = function(event) {
+ var buffer;
+ var is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ console.error("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 {
+ console.error("Unknown message type");
+ return;
+ }
+ var len = buffer.length*buffer.BYTES_PER_ELEMENT;
+ var out = Module._malloc(len);
+ Module.HEAPU8.set(buffer, out);
+ ccall("_emrtc_on_ch_message",
+ "void",
+ ["number", "number", "number", "number"],
+ [c_ptr, out, len, is_string]
+ );
+ Module._free(out);
+ }
+
+ }, this, js_id);
+ // Parse label
+ char *str;
+ str = (char *)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict || !dict["channel"]) return 0;
+ var str = dict["channel"].label;
+ var len = lengthBytesUTF8(str)+1;
+ var ptr = _malloc(str);
+ stringToUTF8(str, ptr, len+1);
+ return ptr;
+ }, js_id);
+ if(str != NULL) {
+ _label.parse_utf8(str);
+ EM_ASM({ _free($0) }, str);
+ }
+ str = (char *)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict || !dict["channel"]) return 0;
+ var str = dict["channel"].protocol;
+ var len = lengthBytesUTF8(str)+1;
+ var ptr = _malloc(str);
+ stringToUTF8(str, ptr, len+1);
+ return ptr;
+ }, js_id);
+ if(str != NULL) {
+ _protocol.parse_utf8(str);
+ EM_ASM({ _free($0) }, str);
+ }
+ /* clang-format on */
+}
+
+WebRTCDataChannelJS::~WebRTCDataChannelJS() {
+ close();
+ /* clang-format off */
+ EM_ASM({
+ Module.IDHandler.remove($0);
+ }, _js_id);
+ /* clang-format on */
+};
+#endif
diff --git a/modules/webrtc/webrtc_peer_js.h b/modules/webrtc/webrtc_data_channel_js.h
index 02f0c9b55d..b87f8e9326 100644
--- a/modules/webrtc/webrtc_peer_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_js.h */
+/* webrtc_data_channel_js.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -28,43 +28,53 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_PEER_JS_H
-#define WEBRTC_PEER_JS_H
-
#ifdef JAVASCRIPT_ENABLED
-#include "webrtc_peer.h"
+#ifndef WEBRTC_DATA_CHANNEL_JS_H
+#define WEBRTC_DATA_CHANNEL_JS_H
+
+#include "webrtc_data_channel.h"
-class WebRTCPeerJS : public WebRTCPeer {
+class WebRTCDataChannelJS : public WebRTCDataChannel {
+ GDCLASS(WebRTCDataChannelJS, WebRTCDataChannel);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
- };
+ String _label;
+ String _protocol;
bool _was_string;
WriteMode _write_mode;
+ enum {
+ PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
+ };
+
int _js_id;
RingBuffer<uint8_t> in_buffer;
int queue_count;
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- ConnectionState _conn_state;
public:
- static WebRTCPeer *_create() { return memnew(WebRTCPeerJS); }
- static void make_default() { WebRTCPeer::_create = WebRTCPeerJS::_create; }
+ void _on_open();
+ void _on_close();
+ void _on_error();
+ void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
virtual void set_write_mode(WriteMode mode);
virtual WriteMode get_write_mode() const;
virtual bool was_string_packet() const;
- virtual ConnectionState get_connection_state() const;
- virtual Error create_offer();
- virtual Error set_remote_description(String type, String sdp);
- virtual Error set_local_description(String type, String sdp);
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual ChannelState get_ready_state() const;
+ virtual String get_label() const;
+ virtual bool is_ordered() const;
+ virtual int get_id() const;
+ virtual int get_max_packet_life_time() const;
+ virtual int get_max_retransmits() const;
+ virtual String get_protocol() const;
+ virtual bool is_negotiated() const;
+
virtual Error poll();
+ virtual void close();
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const;
@@ -73,16 +83,11 @@ public:
virtual int get_max_packet_size() const;
- void close();
- void _on_open();
- void _on_close();
- void _on_error();
- void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
-
- WebRTCPeerJS();
- ~WebRTCPeerJS();
+ WebRTCDataChannelJS();
+ WebRTCDataChannelJS(int js_id);
+ ~WebRTCDataChannelJS();
};
-#endif
+#endif // WEBRTC_DATA_CHANNEL_JS_H
-#endif // WEBRTC_PEER_JS_H
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/webrtc/webrtc_peer.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 30c4505df9..69c7a51a40 100644
--- a/modules/webrtc/webrtc_peer.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer.cpp */
+/* webrtc_peer_connection.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 */
@@ -28,43 +28,37 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "webrtc_peer.h"
+#include "webrtc_peer_connection.h"
-WebRTCPeer *(*WebRTCPeer::_create)() = NULL;
+WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = NULL;
-Ref<WebRTCPeer> WebRTCPeer::create_ref() {
+Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() {
- if (!_create)
- return Ref<WebRTCPeer>();
- return Ref<WebRTCPeer>(_create());
+ return create();
}
-WebRTCPeer *WebRTCPeer::create() {
+WebRTCPeerConnection *WebRTCPeerConnection::create() {
if (!_create)
return NULL;
return _create();
}
-void WebRTCPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeer::create_offer);
- ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeer::set_local_description);
- ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeer::set_remote_description);
- ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeer::poll);
- ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeer::add_ice_candidate);
-
- ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCPeer::was_string_packet);
- ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCPeer::set_write_mode);
- ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCPeer::get_write_mode);
- ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeer::get_connection_state);
-
- ADD_SIGNAL(MethodInfo("offer_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
- ADD_SIGNAL(MethodInfo("new_ice_candidate", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
+void WebRTCPeerConnection::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
+ ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
+ ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
+ ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeerConnection::set_local_description);
+ ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeerConnection::set_remote_description);
+ ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeerConnection::add_ice_candidate);
+ ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeerConnection::poll);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
+ ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
- BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
- BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+ ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
+ ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
+ ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
@@ -74,8 +68,8 @@ void WebRTCPeer::_bind_methods() {
BIND_ENUM_CONSTANT(STATE_CLOSED);
}
-WebRTCPeer::WebRTCPeer() {
+WebRTCPeerConnection::WebRTCPeerConnection() {
}
-WebRTCPeer::~WebRTCPeer() {
+WebRTCPeerConnection::~WebRTCPeerConnection() {
}
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
new file mode 100644
index 0000000000..7be1390dab
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* webrtc_peer_connection.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 WEBRTC_PEER_CONNECTION_H
+#define WEBRTC_PEER_CONNECTION_H
+
+#include "core/io/packet_peer.h"
+#include "modules/webrtc/webrtc_data_channel.h"
+
+class WebRTCPeerConnection : public Reference {
+ GDCLASS(WebRTCPeerConnection, Reference);
+
+public:
+ enum ConnectionState {
+ STATE_NEW,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ STATE_DISCONNECTED,
+ STATE_FAILED,
+ STATE_CLOSED
+ };
+
+protected:
+ static void _bind_methods();
+ static WebRTCPeerConnection *(*_create)();
+
+public:
+ virtual ConnectionState get_connection_state() const = 0;
+
+ virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
+ virtual Error create_offer() = 0;
+ virtual Error set_remote_description(String type, String sdp) = 0;
+ virtual Error set_local_description(String type, String sdp) = 0;
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0;
+ virtual Error poll() = 0;
+ virtual void close() = 0;
+
+ static Ref<WebRTCPeerConnection> create_ref();
+ static WebRTCPeerConnection *create();
+
+ WebRTCPeerConnection();
+ ~WebRTCPeerConnection();
+};
+
+VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+#endif // WEBRTC_PEER_CONNECTION_H
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp
new file mode 100644
index 0000000000..af98aa750a
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.cpp
@@ -0,0 +1,124 @@
+/*************************************************************************/
+/* webrtc_peer_connection_gdnative.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 WEBRTC_GDNATIVE_ENABLED
+
+#include "webrtc_peer_connection_gdnative.h"
+
+#include "core/io/resource_loader.h"
+#include "modules/gdnative/nativescript/nativescript.h"
+#include "webrtc_data_channel_gdnative.h"
+
+const godot_net_webrtc_library *WebRTCPeerConnectionGDNative::default_library = NULL;
+
+Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_library *p_lib) {
+ if (default_library) {
+ const godot_net_webrtc_library *old = default_library;
+ default_library = NULL;
+ old->unregistered();
+ }
+ default_library = p_lib;
+ return OK; // Maybe add version check and fail accordingly
+}
+
+WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() {
+
+ WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative);
+ ERR_EXPLAIN("Default GDNative WebRTC implementation not defined.");
+ ERR_FAIL_COND_V(!default_library, obj);
+
+ // Call GDNative constructor
+ Error err = (Error)default_library->create_peer_connection(obj);
+ ERR_EXPLAIN("GDNative default library constructor returned an error");
+ ERR_FAIL_COND_V(err != OK, obj);
+
+ return obj;
+}
+
+void WebRTCPeerConnectionGDNative::_bind_methods() {
+}
+
+WebRTCPeerConnectionGDNative::WebRTCPeerConnectionGDNative() {
+ interface = NULL;
+}
+
+WebRTCPeerConnectionGDNative::~WebRTCPeerConnectionGDNative() {
+}
+
+Error WebRTCPeerConnectionGDNative::initialize(Dictionary p_config) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->initialize(interface->data, (const godot_dictionary *)&p_config);
+}
+
+Ref<WebRTCDataChannel> WebRTCPeerConnectionGDNative::create_data_channel(String p_label, Dictionary p_options) {
+ ERR_FAIL_COND_V(interface == NULL, NULL);
+ return (WebRTCDataChannel *)interface->create_data_channel(interface->data, p_label.utf8().get_data(), (const godot_dictionary *)&p_options);
+}
+
+Error WebRTCPeerConnectionGDNative::create_offer() {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->create_offer(interface->data);
+}
+
+Error WebRTCPeerConnectionGDNative::set_local_description(String p_type, String p_sdp) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::set_remote_description(String p_type, String p_sdp) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+}
+
+Error WebRTCPeerConnectionGDNative::poll() {
+ ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED);
+ return (Error)interface->poll(interface->data);
+}
+
+void WebRTCPeerConnectionGDNative::close() {
+ ERR_FAIL_COND(interface == NULL);
+ interface->close(interface->data);
+}
+
+WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionGDNative::get_connection_state() const {
+ ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED);
+ return (ConnectionState)interface->get_connection_state(interface->data);
+}
+
+void WebRTCPeerConnectionGDNative::set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl) {
+ interface = p_impl;
+}
+
+#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h
new file mode 100644
index 0000000000..0a281c3d89
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* webrtc_peer_connection_gdnative.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 WEBRTC_GDNATIVE_ENABLED
+
+#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#define WEBRTC_PEER_CONNECTION_GDNATIVE_H
+
+#include "modules/gdnative/include/net/godot_net.h"
+#include "webrtc_peer_connection.h"
+
+class WebRTCPeerConnectionGDNative : public WebRTCPeerConnection {
+ GDCLASS(WebRTCPeerConnectionGDNative, WebRTCPeerConnection);
+
+protected:
+ static void _bind_methods();
+ static WebRTCPeerConnection *_create();
+
+private:
+ static const godot_net_webrtc_library *default_library;
+ const godot_net_webrtc_peer_connection *interface;
+
+public:
+ static Error set_default_library(const godot_net_webrtc_library *p_library);
+ static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionGDNative::_create; }
+
+ void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl);
+
+ virtual ConnectionState get_connection_state() const;
+
+ virtual Error initialize(Dictionary p_config = Dictionary());
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary());
+ virtual Error create_offer();
+ virtual Error set_remote_description(String type, String sdp);
+ virtual Error set_local_description(String type, String sdp);
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual Error poll();
+ virtual void close();
+
+ WebRTCPeerConnectionGDNative();
+ ~WebRTCPeerConnectionGDNative();
+};
+
+#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
+
+#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
new file mode 100644
index 0000000000..9758ab3644
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* webrtc_peer_connection_js.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 "webrtc_peer_connection_js.h"
+
+#include "webrtc_data_channel_js.h"
+
+#include "core/io/json.h"
+#include "emscripten.h"
+
+extern "C" {
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName));
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("session_description_created", String(p_type), String(p_offer));
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->_on_connection_state_changed();
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() {
+ ERR_PRINT("RTCPeerConnection error!");
+}
+
+EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+ peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
+}
+}
+
+void _emrtc_create_pc(int p_id, const Dictionary &p_config) {
+ String config = JSON::print(p_config);
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var c_ptr = dict["ptr"];
+ var config = JSON.parse(UTF8ToString($1));
+ // Setup local connaction
+ var conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ console.log(e);
+ return;
+ }
+ conn.oniceconnectionstatechange = function(event) {
+ if (!Module.IDHandler.get($0)) return;
+ ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]);
+ };
+ conn.onicecandidate = function(event) {
+ if (!Module.IDHandler.get($0)) return;
+ if (!event.candidate) return;
+
+ var c = event.candidate;
+ // should emit on ice candidate
+ ccall("_emrtc_on_ice_candidate",
+ "void",
+ ["number", "string", "number", "string"],
+ [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
+ );
+ };
+ conn.ondatachannel = function (evt) {
+ var dict = Module.IDHandler.get($0);
+ if (!dict) {
+ return;
+ }
+ var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null});
+ ccall("_emrtc_emit_channel",
+ "void",
+ ["number", "number"],
+ [c_ptr, id]
+ );
+ };
+ dict["conn"] = conn;
+ }, p_id, config.utf8().get_data());
+ /* clang-format on */
+}
+
+void WebRTCPeerConnectionJS::_on_connection_state_changed() {
+ /* clang-format off */
+ _conn_state = (ConnectionState)EM_ASM_INT({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 5; // CLOSED
+ var conn = dict["conn"];
+ switch(conn.iceConnectionState) {
+ case "new":
+ return 0;
+ case "checking":
+ return 1;
+ case "connected":
+ case "completed":
+ return 2;
+ case "disconnected":
+ return 3;
+ case "failed":
+ return 4;
+ case "closed":
+ return 5;
+ }
+ return 5; // CLOSED
+ }, _js_id);
+ /* clang-format on */
+}
+
+void WebRTCPeerConnectionJS::close() {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return;
+ if (dict["conn"]) {
+ dict["conn"].close();
+ }
+ }, _js_id);
+ /* clang-format on */
+ _conn_state = STATE_CLOSED;
+}
+
+Error WebRTCPeerConnectionJS::create_offer() {
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
+
+ _conn_state = STATE_CONNECTING;
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ var onCreated = function(offer) {
+ ccall("_emrtc_session_description_created",
+ "void",
+ ["number", "string", "string"],
+ [c_ptr, offer.type, offer.sdp]
+ );
+ };
+ conn.createOffer().then(onCreated).catch(onError);
+ }, _js_id);
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var type = UTF8ToString($1);
+ var sdp = UTF8ToString($2);
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ conn.setLocalDescription({
+ "sdp": sdp,
+ "type": type
+ }).catch(onError);
+ }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) {
+ if (type == "offer") {
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
+ _conn_state = STATE_CONNECTING;
+ }
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var type = UTF8ToString($1);
+ var sdp = UTF8ToString($2);
+
+ var onError = function(error) {
+ console.error(error);
+ ccall("_emrtc_on_error", "void", [], []);
+ };
+ var onCreated = function(offer) {
+ ccall("_emrtc_session_description_created",
+ "void",
+ ["number", "string", "string"],
+ [c_ptr, offer.type, offer.sdp]
+ );
+ };
+ var onSet = function() {
+ if (type != "offer") {
+ return;
+ }
+ conn.createAnswer().then(onCreated);
+ };
+ conn.setRemoteDescription({
+ "sdp": sdp,
+ "type": type
+ }).then(onSet).catch(onError);
+ }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
+ /* clang-format off */
+ EM_ASM({
+ var dict = Module.IDHandler.get($0);
+ var conn = dict["conn"];
+ var c_ptr = dict["ptr"];
+ var sdpMidName = UTF8ToString($1);
+ var sdpMlineIndexName = UTF8ToString($2);
+ var sdpName = UTF8ToString($3);
+ conn.addIceCandidate(new RTCIceCandidate({
+ "candidate": sdpName,
+ "sdpMid": sdpMidName,
+ "sdpMlineIndex": sdpMlineIndexName
+ }));
+ }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
+ /* clang-format on */
+ return OK;
+}
+
+Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
+ _emrtc_create_pc(_js_id, p_config);
+ return OK;
+}
+
+Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) {
+ String config = JSON::print(p_channel_config);
+ /* clang-format off */
+ int id = EM_ASM_INT({
+ try {
+ var dict = Module.IDHandler.get($0);
+ if (!dict) return 0;
+ var label = UTF8ToString($1);
+ var config = JSON.parse(UTF8ToString($2));
+ var conn = dict["conn"];
+ return Module.IDHandler.add({
+ "channel": conn.createDataChannel(label, config),
+ "ptr": null
+ })
+ } catch (e) {
+ return 0;
+ }
+ }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data());
+ /* clang-format on */
+ ERR_FAIL_COND_V(id == 0, NULL);
+ return memnew(WebRTCDataChannelJS(id));
+}
+
+Error WebRTCPeerConnectionJS::poll() {
+ return OK;
+}
+
+WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
+ return _conn_state;
+}
+
+WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
+ _conn_state = STATE_NEW;
+
+ /* clang-format off */
+ _js_id = EM_ASM_INT({
+ return Module.IDHandler.add({"conn": null, "ptr": $0});
+ }, this);
+ /* clang-format on */
+ Dictionary config;
+ _emrtc_create_pc(_js_id, config);
+}
+
+WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() {
+ close();
+ /* clang-format off */
+ EM_ASM({
+ Module.IDHandler.remove($0);
+ }, _js_id);
+ /* clang-format on */
+};
+#endif
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
new file mode 100644
index 0000000000..43c0e3d6ee
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* webrtc_peer_connection_js.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 WEBRTC_PEER_CONNECTION_JS_H
+#define WEBRTC_PEER_CONNECTION_JS_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "webrtc_peer_connection.h"
+
+class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
+
+private:
+ int _js_id;
+ ConnectionState _conn_state;
+
+public:
+ static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); }
+ static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; }
+
+ void _on_connection_state_changed();
+ virtual ConnectionState get_connection_state() const;
+
+ virtual Error initialize(Dictionary configuration = Dictionary());
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary());
+ virtual Error create_offer();
+ virtual Error set_remote_description(String type, String sdp);
+ virtual Error set_local_description(String type, String sdp);
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
+ virtual Error poll();
+ virtual void close();
+
+ WebRTCPeerConnectionJS();
+ ~WebRTCPeerConnectionJS();
+};
+
+#endif
+
+#endif // WEBRTC_PEER_CONNECTION_JS_H
diff --git a/modules/webrtc/webrtc_peer_js.cpp b/modules/webrtc/webrtc_peer_js.cpp
deleted file mode 100644
index 1282e075ab..0000000000
--- a/modules/webrtc/webrtc_peer_js.cpp
+++ /dev/null
@@ -1,455 +0,0 @@
-/*************************************************************************/
-/* webrtc_peer_js.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "webrtc_peer_js.h"
-#include "emscripten.h"
-
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->emit_signal("new_ice_candidate", String(p_MidName), p_MlineIndexName, String(p_sdpName));
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_offer_created(void *obj, char *p_type, char *p_offer) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->emit_signal("offer_created", String(p_type), String(p_offer));
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_error(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_error();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_open(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_open();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_close(void *obj) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_close();
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj);
- peer->_on_message(p_data, p_size, p_is_string);
-}
-
-EMSCRIPTEN_KEEPALIVE void _emrtc_bind_channel(int p_id) {
- /* clang-format off */
- EM_ASM({
- if (!Module.IDHandler.has($0)) {
- return; // Godot Object is gone!
- }
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var c_ptr = dict["ptr"];
-
- channel.onopen = function (evt) {
- ccall("_emrtc_on_open",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onclose = function (evt) {
- ccall("_emrtc_on_close",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onerror = function (evt) {
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
-
- channel.binaryType = "arraybuffer";
- channel.onmessage = function(event) {
- 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("_emrtc_on_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- Module._free(out);
- }
- }, p_id);
- /* clang-format on */
-}
-}
-
-void _emrtc_create_pc(int p_id) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var c_ptr = dict["ptr"];
- // Setup local connaction
- var conn = new RTCPeerConnection();
- conn.onicecandidate = function(event) {
- if (!Module.IDHandler.get($0)) return;
- if (!event.candidate) return;
-
- var c = event.candidate;
- // should emit on ice candidate
- ccall("_emrtc_on_ice_candidate",
- "void",
- ["number", "string", "number", "string"],
- [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
- );
- };
- conn.ondatachannel = function (evt) {
- var dict = Module.IDHandler.get($0);
- if (!dict || dict["channel"]) {
- return;
- }
- var channel = evt.channel;
- dict["channel"] = channel;
- ccall("_emrtc_bind_channel",
- "void",
- ["number"],
- [$0]
- );
- };
- dict["conn"] = conn;
- }, p_id);
- /* clang-format on */
-}
-
-void WebRTCPeerJS::_on_open() {
- in_buffer.resize(16);
- _conn_state = STATE_CONNECTED;
-}
-
-void WebRTCPeerJS::_on_close() {
- close();
-}
-
-void WebRTCPeerJS::_on_error() {
- close();
- _conn_state = STATE_FAILED;
-}
-
-void WebRTCPeerJS::_on_message(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++;
-}
-
-void WebRTCPeerJS::close() {
- in_buffer.resize(0);
- queue_count = 0;
- _was_string = false;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- if (dict["channel"]) {
- dict["channel"].close();
- dict["channel"] = null;
- }
- if (dict["conn"]) {
- dict["conn"].close();
- }
- }, _js_id);
- /* clang-format on */
- _conn_state = STATE_CLOSED;
-}
-
-Error WebRTCPeerJS::create_offer() {
- ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
-
- _conn_state = STATE_CONNECTING;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- var onCreated = function(offer) {
- ccall("_emrtc_offer_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
-
- var channel = conn.createDataChannel("default");
- dict["channel"] = channel;
- ccall("_emrtc_bind_channel",
- "void",
- ["number"],
- [$0]
- );
- conn.createOffer().then(onCreated).catch(onError);
- }, _js_id);
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::set_local_description(String type, String sdp) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- conn.setLocalDescription({
- "sdp": sdp,
- "type": type
- }).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::set_remote_description(String type, String sdp) {
- if (type == "offer") {
- ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
- _conn_state = STATE_CONNECTING;
- }
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
-
- var onError = function(error) {
- console.log(error);
- ccall("_emrtc_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- var onCreated = function(offer) {
- ccall("_emrtc_offer_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- var onSet = function() {
- if (type != "offer") {
- return;
- }
- conn.createAnswer().then(onCreated);
- };
- conn.setRemoteDescription({
- "sdp": sdp,
- "type": type
- }).then(onSet).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var sdpMidName = UTF8ToString($1);
- var sdpMlineIndexName = UTF8ToString($2);
- var sdpName = UTF8ToString($3);
- conn.addIceCandidate(new RTCIceCandidate({
- "candidate": sdpName,
- "sdpMid": sdpMidName,
- "sdpMlineIndex": sdpMlineIndexName
- }));
- }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
- /* clang-format on */
- return OK;
-}
-
-Error WebRTCPeerJS::poll() {
- return OK;
-}
-
-WebRTCPeer::ConnectionState WebRTCPeerJS::get_connection_state() const {
- return _conn_state;
-}
-
-int WebRTCPeerJS::get_available_packet_count() const {
- return queue_count;
-}
-
-Error WebRTCPeerJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED);
-
- 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;
-}
-
-Error WebRTCPeerJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED);
-
- int is_bin = _write_mode == WebRTCPeer::WRITE_MODE_BINARY ? 1 : 0;
-
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- if ($3) {
- channel.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- channel.send(string);
- }
- }, _js_id, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
-
- return OK;
-}
-
-int WebRTCPeerJS::get_max_packet_size() const {
- return 1200;
-}
-
-void WebRTCPeerJS::set_write_mode(WriteMode p_mode) {
- _write_mode = p_mode;
-}
-
-WebRTCPeer::WriteMode WebRTCPeerJS::get_write_mode() const {
- return _write_mode;
-}
-
-bool WebRTCPeerJS::was_string_packet() const {
- return _was_string;
-}
-
-WebRTCPeerJS::WebRTCPeerJS() {
- queue_count = 0;
- _was_string = false;
- _write_mode = WRITE_MODE_BINARY;
- _conn_state = STATE_NEW;
-
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add({"conn": null, "ptr": $0, "channel": null});
- }, this);
- /* clang-format on */
- _emrtc_create_pc(_js_id);
-}
-
-WebRTCPeerJS::~WebRTCPeerJS() {
- close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
-#endif
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index 2d2de81220..d9e60eb6f1 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -9,90 +9,85 @@ env_lws = env_modules.Clone()
if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript
thirdparty_dir = "#thirdparty/libwebsockets/"
- helper_dir = "#thirdparty/libwebsockets/win32helpers/"
+ helper_dir = "win32helpers/"
thirdparty_sources = [
- "lib/core/adopt.c",
- "lib/core/alloc.c",
- "lib/core/connect.c",
- "lib/core/context.c",
- "lib/core/dummy-callback.c",
- "lib/core/libwebsockets.c",
- "lib/core/output.c",
- "lib/core/pollfd.c",
- "lib/core/service.c",
-
- "lib/event-libs/poll/poll.c",
-
- "lib/misc/base64-decode.c",
- "lib/misc/lejp.c",
- "lib/misc/sha-1.c",
-
- "lib/roles/h1/ops-h1.c",
- "lib/roles/http/header.c",
- "lib/roles/http/client/client.c",
- "lib/roles/http/client/client-handshake.c",
- "lib/roles/http/server/fops-zip.c",
- "lib/roles/http/server/lejp-conf.c",
- "lib/roles/http/server/parsers.c",
- "lib/roles/http/server/server.c",
- "lib/roles/listen/ops-listen.c",
- "lib/roles/pipe/ops-pipe.c",
- "lib/roles/raw-skt/ops-raw-skt.c",
- "lib/roles/raw-file/ops-raw-file.c",
-
- "lib/roles/ws/client-ws.c",
- "lib/roles/ws/client-parser-ws.c",
- "lib/roles/ws/ops-ws.c",
- "lib/roles/ws/server-ws.c",
-
- "lib/tls/tls.c",
- "lib/tls/tls-client.c",
- "lib/tls/tls-server.c",
-
- "lib/tls/mbedtls/wrapper/library/ssl_cert.c",
- "lib/tls/mbedtls/wrapper/library/ssl_pkey.c",
- "lib/tls/mbedtls/wrapper/library/ssl_stack.c",
- "lib/tls/mbedtls/wrapper/library/ssl_methods.c",
- "lib/tls/mbedtls/wrapper/library/ssl_lib.c",
- "lib/tls/mbedtls/wrapper/library/ssl_x509.c",
- "lib/tls/mbedtls/wrapper/platform/ssl_port.c",
- "lib/tls/mbedtls/wrapper/platform/ssl_pm.c",
- "lib/tls/mbedtls/lws-genhash.c",
- "lib/tls/mbedtls/mbedtls-client.c",
- "lib/tls/mbedtls/lws-genrsa.c",
- "lib/tls/mbedtls/ssl.c",
- "lib/tls/mbedtls/mbedtls-server.c"
+ "core/alloc.c",
+ "core/context.c",
+ "core/libwebsockets.c",
+ "core/output.c",
+ "core/pollfd.c",
+ "core/service.c",
+
+ "event-libs/poll/poll.c",
+
+ "misc/base64-decode.c",
+ "misc/lejp.c",
+ "misc/sha-1.c",
+
+ "roles/h1/ops-h1.c",
+ "roles/http/header.c",
+ "roles/http/client/client.c",
+ "roles/http/client/client-handshake.c",
+ "roles/http/server/fops-zip.c",
+ "roles/http/server/lejp-conf.c",
+ "roles/http/server/parsers.c",
+ "roles/http/server/server.c",
+ "roles/listen/ops-listen.c",
+ "roles/pipe/ops-pipe.c",
+ "roles/raw/ops-raw.c",
+
+ "roles/ws/client-ws.c",
+ "roles/ws/client-parser-ws.c",
+ "roles/ws/ops-ws.c",
+ "roles/ws/server-ws.c",
+
+ "tls/tls.c",
+ "tls/tls-client.c",
+ "tls/tls-server.c",
+
+ "tls/mbedtls/wrapper/library/ssl_cert.c",
+ "tls/mbedtls/wrapper/library/ssl_pkey.c",
+ "tls/mbedtls/wrapper/library/ssl_stack.c",
+ "tls/mbedtls/wrapper/library/ssl_methods.c",
+ "tls/mbedtls/wrapper/library/ssl_lib.c",
+ "tls/mbedtls/wrapper/library/ssl_x509.c",
+ "tls/mbedtls/wrapper/platform/ssl_port.c",
+ "tls/mbedtls/wrapper/platform/ssl_pm.c",
+ "tls/mbedtls/lws-genhash.c",
+ "tls/mbedtls/mbedtls-client.c",
+ "tls/mbedtls/lws-genrsa.c",
+ "tls/mbedtls/ssl.c",
+ "tls/mbedtls/mbedtls-server.c"
]
if env["platform"] == "android": # Builtin getifaddrs
- thirdparty_sources += ["lib/misc/getifaddrs.c"]
-
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+ thirdparty_sources += ["misc/getifaddrs.c"]
if env["platform"] == "windows" or env["platform"] == "uwp": # Winsock
- thirdparty_sources += Glob(thirdparty_dir + "lib/plat/windows/*.c") + [helper_dir + src for src in ["getopt.c", "getopt_long.c", "gettimeofday.c"]]
+ 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 += Glob(thirdparty_dir + "lib/plat/unix/*.c")
+ thirdparty_sources += ["plat/lws-plat-unix.c"]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_lws.Prepend(CPPPATH=[thirdparty_dir + 'include/'])
+ env_lws.Prepend(CPPPATH=[thirdparty_dir])
if env['builtin_mbedtls']:
mbedtls_includes = "#thirdparty/mbedtls/include"
env_lws.Prepend(CPPPATH=[mbedtls_includes])
- wrapper_includes = ["#thirdparty/libwebsockets/lib/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
+ wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
env_lws.Prepend(CPPPATH=wrapper_includes)
if env["platform"] == "windows" or env["platform"] == "uwp":
- env_lws.Prepend(CPPPATH=[helper_dir])
+ env_lws.Prepend(CPPPATH=[thirdparty_dir + helper_dir])
if env["platform"] == "uwp":
env_lws.Append(CPPFLAGS=["/DLWS_MINGW_SUPPORT"])
env_thirdparty = env_lws.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.Prepend(CPPPATH=[thirdparty_dir + 'lib/'])
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
env_lws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index d09558ab22..08df76293b 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -32,11 +32,10 @@
#include "lws_client.h"
#include "core/io/ip.h"
-#include "core/io/stream_peer_ssl.h"
#include "core/project_settings.h"
#if defined(LWS_OPENSSL_SUPPORT)
-// Not openssl, just the mbedtls wrapper
-#include "openssl/ssl.h"
+#include "core/io/stream_peer_ssl.h"
+#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
#endif
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {