diff options
Diffstat (limited to 'modules')
281 files changed, 5641 insertions, 3169 deletions
diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub index 61a357809a..0da7e432e2 100644 --- a/modules/assimp/SCsub +++ b/modules/assimp/SCsub @@ -4,19 +4,19 @@ Import('env') Import('env_modules') env_assimp = env_modules.Clone() -env_assimp.Append(CPPPATH=['#thirdparty/assimp']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/include']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/IFC']) -env_assimp.Append(CPPPATH=['#thirdparty/misc']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/code']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/irrXML/']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/unzip/']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/code/Importer/STEPParser']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/']) -env_assimp.Append(CPPPATH=['#thirdparty/zlib/']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/openddlparser/include']) -env_assimp.Append(CPPPATH=['#thirdparty/assimp/contrib/rapidjson/include']) -env_assimp.Append(CPPPATH=['.']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/include']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code/Importer/IFC']) +env_assimp.Prepend(CPPPATH=['#thirdparty/misc']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/irrXML/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/unzip/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/code/Importer/STEPParser']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/zlib/']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/openddlparser/include']) +env_assimp.Prepend(CPPPATH=['#thirdparty/assimp/contrib/rapidjson/include']) +env_assimp.Prepend(CPPPATH=['.']) #env_assimp.Append(CPPFLAGS=['-DASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support env_assimp.Append(CPPFLAGS=['-DASSIMP_BUILD_BOOST_WORKAROUND']) env_assimp.Append(CPPFLAGS=['-DOPENDDLPARSER_BUILD']) diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index 0330ab4604..8c3c04674b 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -126,8 +126,8 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f int32_t post_process_Steps = aiProcess_CalcTangentSpace | //aiProcess_FlipUVs | //aiProcess_FlipWindingOrder | - aiProcess_DropNormals | - aiProcess_GenSmoothNormals | + //aiProcess_DropNormals | + //aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality | aiProcess_LimitBoneWeights | @@ -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/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index a8172c7f52..bcc992db24 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -33,6 +33,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const uint8_t *p_color_buffer, + const uint32_t color_table_size, const bmp_header_s &p_header) { Error err = OK; @@ -46,32 +47,87 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, size_t height = (size_t)p_header.bmp_info_header.bmp_height; size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count; - if (p_header.bmp_info_header.bmp_compression != 0) { + if (p_header.bmp_info_header.bmp_compression != BI_RGB) { err = FAILED; } + // Check whether we can load it - if (!(bits_per_pixel == 24 || bits_per_pixel == 32)) { - err = FAILED; - } + if (bits_per_pixel == 1) { + // Requires bit unpacking... + ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE); + + } else if (bits_per_pixel == 4) { + // Requires bit unpacking... + ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE); + } else if (bits_per_pixel == 16) { + + ERR_FAIL_V(ERR_UNAVAILABLE); + } if (err == OK) { - uint32_t line_width = ((p_header.bmp_info_header.bmp_width * - p_header.bmp_info_header.bmp_bit_count / 8) + - 3) & - ~3; + // Image data (might be indexed) + PoolVector<uint8_t> data; + int data_len = 0; - PoolVector<uint8_t> image_data; - err = image_data.resize(width * height * 4); + if (bits_per_pixel <= 8) { // indexed + data_len = width * height; + } else { // color + data_len = width * height * 4; + } + ERR_FAIL_COND_V(data_len == 0, ERR_BUG); + err = data.resize(data_len); - PoolVector<uint8_t>::Write image_data_w = image_data.write(); - uint8_t *write_buffer = image_data_w.ptr(); + PoolVector<uint8_t>::Write data_w = data.write(); + uint8_t *write_buffer = data_w.ptr(); + const uint32_t width_bytes = width * bits_per_pixel / 8; + const uint32_t line_width = (width_bytes + 3) & ~3; + + // The actual data traversal is determined by + // the data width in case of 8/4/1 bit images + const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); + for (unsigned int i = 0; i < height; i++) { const uint8_t *line_ptr = line; - for (unsigned int j = 0; j < width; j++) { + + for (unsigned int j = 0; j < w; j++) { switch (bits_per_pixel) { + case 1: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 7) & 1; + write_buffer[index + 1] = (color_index >> 6) & 1; + write_buffer[index + 2] = (color_index >> 5) & 1; + write_buffer[index + 3] = (color_index >> 4) & 1; + write_buffer[index + 4] = (color_index >> 3) & 1; + write_buffer[index + 5] = (color_index >> 2) & 1; + write_buffer[index + 6] = (color_index >> 1) & 1; + write_buffer[index + 7] = (color_index >> 0) & 1; + + index += 8; + line_ptr += 1; + } break; + case 4: { + uint8_t color_index = *line_ptr; + + write_buffer[index + 0] = (color_index >> 4) & 0x0f; + write_buffer[index + 1] = color_index & 0x0f; + + index += 2; + line_ptr += 1; + } break; + case 8: { + uint8_t color_index = *line_ptr; + + write_buffer[index] = color_index; + + index += 1; + line_ptr += 1; + } break; case 24: { uint32_t color = *((uint32_t *)line_ptr); @@ -79,6 +135,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, write_buffer[index + 1] = (color >> 8) & 0xff; write_buffer[index + 0] = (color >> 16) & 0xff; write_buffer[index + 3] = 0xff; + index += 4; line_ptr += 3; } break; @@ -89,6 +146,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, write_buffer[index + 1] = (color >> 8) & 0xff; write_buffer[index + 0] = (color >> 16) & 0xff; write_buffer[index + 3] = color >> 24; + index += 4; line_ptr += 4; } break; @@ -96,7 +154,51 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, } line -= line_width; } - p_image->create(width, height, 0, Image::FORMAT_RGBA8, image_data); + + if (p_color_buffer == NULL || color_table_size == 0) { // regular pixels + + p_image->create(width, height, 0, Image::FORMAT_RGBA8, data); + + } else { // data is in indexed format, extend it + + // Palette data + PoolVector<uint8_t> palette_data; + palette_data.resize(color_table_size * 4); + + PoolVector<uint8_t>::Write palette_data_w = palette_data.write(); + uint8_t *pal = palette_data_w.ptr(); + + const uint8_t *cb = p_color_buffer; + + for (unsigned int i = 0; i < color_table_size; ++i) { + uint32_t color = *((uint32_t *)cb); + + pal[i * 4 + 0] = (color >> 16) & 0xff; + pal[i * 4 + 1] = (color >> 8) & 0xff; + pal[i * 4 + 2] = (color)&0xff; + pal[i * 4 + 3] = 0xff; + + cb += 4; + } + // Extend palette to image + PoolVector<uint8_t> extended_data; + extended_data.resize(data.size() * 4); + + PoolVector<uint8_t>::Write ex_w = extended_data.write(); + uint8_t *dest = ex_w.ptr(); + + const int num_pixels = width * height; + + for (int i = 0; i < num_pixels; i++) { + dest[0] = pal[write_buffer[i] * 4 + 0]; + dest[1] = pal[write_buffer[i] * 4 + 1]; + dest[2] = pal[write_buffer[i] * 4 + 2]; + dest[3] = pal[write_buffer[i] * 4 + 3]; + + dest += 4; + } + p_image->create(width, height, 0, Image::FORMAT_RGBA8, extended_data); + } } } return err; @@ -108,7 +210,9 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header_s bmp_header; Error err = ERR_INVALID_DATA; - if (f->get_len() > sizeof(bmp_header)) { + // A valid bmp file should always at least have a + // file header and a minimal info header + if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) { // File Header bmp_header.bmp_file_header.bmp_signature = f->get_16(); if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) { @@ -118,9 +222,14 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, // Info Header bmp_header.bmp_info_header.bmp_header_size = f->get_32(); + ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT); + bmp_header.bmp_info_header.bmp_width = f->get_32(); bmp_header.bmp_info_header.bmp_height = f->get_32(); + bmp_header.bmp_info_header.bmp_planes = f->get_16(); + ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT); + bmp_header.bmp_info_header.bmp_bit_count = f->get_16(); bmp_header.bmp_info_header.bmp_compression = f->get_32(); bmp_header.bmp_info_header.bmp_size_image = f->get_32(); @@ -129,35 +238,34 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header.bmp_info_header.bmp_colors_used = f->get_32(); bmp_header.bmp_info_header.bmp_important_colors = f->get_32(); - bmp_header.bmp_info_header.bmp_red_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_green_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_blue_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_alpha_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_cs_type = f->get_32(); - for (int i = 0; i < 9; i++) - bmp_header.bmp_info_header.bmp_endpoints[i] = f->get_32(); - - bmp_header.bmp_info_header.bmp_gamma_red = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_green = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_blue = f->get_32(); - - f->seek(sizeof(bmp_header.bmp_file_header) + - bmp_header.bmp_info_header.bmp_header_size); + // Compressed bitmaps not supported, stop parsing + if (bmp_header.bmp_info_header.bmp_compression != BI_RGB) { + ERR_EXPLAIN("Unsupported bmp file: " + f->get_path()); + f->close(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + // Don't rely on sizeof(bmp_file_header) as structure padding + // adds 2 bytes offset leading to misaligned color table reading + uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE + + bmp_header.bmp_info_header.bmp_header_size; + f->seek(ct_offset); uint32_t color_table_size = 0; - if (bmp_header.bmp_info_header.bmp_bit_count == 1) - color_table_size = 2; - else if (bmp_header.bmp_info_header.bmp_bit_count == 4) - color_table_size = 16; - else if (bmp_header.bmp_info_header.bmp_bit_count == 8) - color_table_size = 256; + + // bmp_colors_used may report 0 despite having a color table + // for 4 and 1 bit images, so don't rely on this information + if (bmp_header.bmp_info_header.bmp_bit_count <= 8) { + // Support 256 colors max + color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count; + } + ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG); PoolVector<uint8_t> bmp_color_table; if (color_table_size > 0) { + // Color table is usually 4 bytes per color -> [B][G][R][0] err = bmp_color_table.resize(color_table_size * 4); PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write(); - f->get_buffer(bmp_color_table_w.ptr(), - bmp_header.bmp_info_header.bmp_colors_used * 4); + f->get_buffer(bmp_color_table_w.ptr(), color_table_size * 4); } f->seek(bmp_header.bmp_file_header.bmp_file_offset); @@ -174,7 +282,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, PoolVector<uint8_t>::Read bmp_buffer_r = bmp_buffer.read(); PoolVector<uint8_t>::Read bmp_color_table_r = bmp_color_table.read(); err = convert_to_image(p_image, bmp_buffer_r.ptr(), - bmp_color_table_r.ptr(), bmp_header); + bmp_color_table_r.ptr(), color_table_size, bmp_header); } f->close(); } diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index d6899061d0..0082cf778a 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -37,6 +37,22 @@ class ImageLoaderBMP : public ImageFormatLoader { protected: static const unsigned BITMAP_SIGNATURE = 0x4d42; + static const unsigned BITMAP_FILE_HEADER_SIZE = 14; // bmp_file_header_s + static const unsigned BITMAP_INFO_HEADER_MIN_SIZE = 40; // bmp_info_header_s + + enum bmp_compression_s { + BI_RGB = 0x00, + BI_RLE8 = 0x01, + BI_RLE4 = 0x02, + BI_BITFIELDS = 0x03, + BI_JPEG = 0x04, + BI_PNG = 0x05, + BI_ALPHABITFIELDS = 0x06, + BI_CMYK = 0x0b, + BI_CMYKRLE8 = 0x0c, + BI_CMYKRLE4 = 0x0d + }; + struct bmp_header_s { struct bmp_file_header_s { uint16_t bmp_signature; @@ -57,21 +73,13 @@ protected: uint32_t bmp_pixels_per_meter_y; uint32_t bmp_colors_used; uint32_t bmp_important_colors; - uint32_t bmp_red_mask; - uint32_t bmp_green_mask; - uint32_t bmp_blue_mask; - uint32_t bmp_alpha_mask; - uint32_t bmp_cs_type; - uint32_t bmp_endpoints[9]; - uint32_t bmp_gamma_red; - uint32_t bmp_gamma_green; - uint32_t bmp_gamma_blue; } bmp_info_header; }; static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const uint8_t *p_color_buffer, + const uint32_t color_table_size, const bmp_header_s &p_header); public: diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 7e714ba43f..2fe7a1b4c0 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -186,9 +186,13 @@ if env['builtin_bullet']: thirdparty_sources = [thirdparty_dir + file for file in bullet2_src] - env_bullet.Append(CPPPATH=[thirdparty_dir]) + # Treat Bullet headers as system headers to avoid raising warnings. Not supported on MSVC. + if not env.msvc: + env_bullet.Append(CPPFLAGS=['-isystem', Dir(thirdparty_dir).path]) + else: + env_bullet.Prepend(CPPPATH=[thirdparty_dir]) # if env['target'] == "debug" or env['target'] == "release_debug": - # env_bullet.Append(CCFLAGS=['-DBT_DEBUG']) + # env_bullet.Append(CPPFLAGS=['-DBT_DEBUG']) env_thirdparty = env_bullet.Clone() env_thirdparty.disable_warnings() 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 44ea061f51..be4e0b88e8 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -279,14 +279,14 @@ PhysicsServer::AreaSpaceOverrideMode BulletPhysicsServer::area_get_space_overrid return area->get_spOv_mode(); } -void BulletPhysicsServer::area_add_shape(RID p_area, RID p_shape, const Transform &p_transform) { +void BulletPhysicsServer::area_add_shape(RID p_area, RID p_shape, const Transform &p_transform, bool p_disabled) { AreaBullet *area = area_owner.get(p_area); ERR_FAIL_COND(!area); ShapeBullet *shape = shape_owner.get(p_shape); ERR_FAIL_COND(!shape); - area->add_shape(shape, p_transform); + area->add_shape(shape, p_transform, p_disabled); } void BulletPhysicsServer::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) { @@ -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 { @@ -498,7 +498,7 @@ PhysicsServer::BodyMode BulletPhysicsServer::body_get_mode(RID p_body) const { return body->get_mode(); } -void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transform &p_transform) { +void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transform &p_transform, bool p_disabled) { RigidBodyBullet *body = rigid_body_owner.get(p_body); ERR_FAIL_COND(!body); @@ -506,7 +506,7 @@ void BulletPhysicsServer::body_add_shape(RID p_body, RID p_shape, const Transfor ShapeBullet *shape = shape_owner.get(p_shape); ERR_FAIL_COND(!shape); - body->add_shape(shape, p_transform); + body->add_shape(shape, p_transform, p_disabled); } void BulletPhysicsServer::body_set_shape(RID p_body, int p_shape_idx, RID p_shape) { @@ -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 1b74cbf3fc..0b8ad53658 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -133,7 +133,7 @@ public: virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode); virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const; - virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform()); + virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape); virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform); virtual int area_get_shape_count(RID p_area) const; @@ -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 @@ -174,7 +174,7 @@ public: virtual void body_set_mode(RID p_body, BodyMode p_mode); virtual BodyMode body_get_mode(RID p_body) const; - virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform()); + virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); // Not supported, Please remove and add new shape virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape); virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform); @@ -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 ef5f21fc21..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) @@ -216,8 +237,8 @@ RigidCollisionObjectBullet::~RigidCollisionObjectBullet() { } } -void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform) { - shapes.push_back(ShapeWrapper(p_shape, p_transform, true)); +void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform, bool p_disabled) { + shapes.push_back(ShapeWrapper(p_shape, p_transform, !p_disabled)); p_shape->add_owner(this); reload_shapes(); } @@ -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(); } @@ -299,6 +319,8 @@ Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const { } void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) { + if (shapes[p_index].active != p_disabled) + return; shapes.write[p_index].active = !p_disabled; shape_changed(p_index); } @@ -342,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(); @@ -356,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 2d4e5c4f1a..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); }; @@ -224,7 +225,7 @@ public: _FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; } - void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform()); + void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform(), bool p_disabled = false); void set_shape(int p_index, ShapeBullet *p_shape); int get_shape_count() const; diff --git a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml index 1f91349f32..078bcc45a8 100644 --- a/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml +++ b/modules/bullet/doc_classes/BulletPhysicsDirectBodyState.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/bullet/doc_classes/BulletPhysicsServer.xml b/modules/bullet/doc_classes/BulletPhysicsServer.xml index 8adc659b2c..2a37f6af5e 100644 --- a/modules/bullet/doc_classes/BulletPhysicsServer.xml +++ b/modules/bullet/doc_classes/BulletPhysicsServer.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> 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 22f2214898..733a900396 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -597,6 +597,8 @@ void RigidBodyBullet::set_state(PhysicsServer::BodyState p_state, const Variant if (!can_sleep) { // Can't sleep btBody->forceActivationState(DISABLE_DEACTIVATION); + } else { + btBody->forceActivationState(ACTIVE_TAG); } break; } @@ -739,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.); } } @@ -832,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 0eb539b182..3a61afa023 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -242,7 +242,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ //check if edge and poly share a vertex, of so, assign it to segment_idx for (int i = 0; i < points.size(); i++) { for (int j = 0; j < 2; j++) { - if (segment[j].distance_to(points[i].point) < CMP_EPSILON) { + if (Math::is_zero_approx(segment[j].distance_to(points[i].point))) { segment_idx[j] = i; inserted_points.push_back(i); break; @@ -310,7 +310,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point }; Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg); - if (closest.distance_to(segment[j]) < CMP_EPSILON) { + if (Math::is_zero_approx(closest.distance_to(segment[j]))) { //point rest of this edge res = closest; found = true; @@ -439,7 +439,7 @@ void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, Mes //transform A points to 2D - if (segment[0].distance_to(segment[1]) < CMP_EPSILON) + if (Math::is_zero_approx(segment[0].distance_to(segment[1]))) return; //too small _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B); @@ -461,10 +461,10 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map { //check if either is a degenerate - if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON) + if (Math::is_zero_approx(va[0].distance_to(va[1])) || Math::is_zero_approx(va[0].distance_to(va[2])) || Math::is_zero_approx(va[1].distance_to(va[2]))) return; - if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON) + if (Math::is_zero_approx(vb[0].distance_to(vb[1])) || Math::is_zero_approx(vb[0].distance_to(vb[2])) || Math::is_zero_approx(vb[1].distance_to(vb[2]))) return; } @@ -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 775ec67ba6..1d27b9b6f4 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -724,6 +724,7 @@ CSGBrush *CSGMesh::_build_brush() { PoolVector<bool> smooth; PoolVector<Ref<Material> > materials; PoolVector<Vector2> uvs; + Ref<Material> material = get_material(); for (int i = 0; i < mesh->get_surface_count(); i++) { @@ -760,7 +761,12 @@ CSGBrush *CSGMesh::_build_brush() { uvr_used = true; } - Ref<Material> mat = mesh->surface_get_material(i); + Ref<Material> mat; + if (material.is_valid()) { + mat = material; + } else { + mat = mesh->surface_get_material(i); + } PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX]; if (aindices.size()) { @@ -866,6 +872,18 @@ void CSGMesh::_mesh_changed() { update_gizmo(); } +void CSGMesh::set_material(const Ref<Material> &p_material) { + if (material == p_material) + return; + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGMesh::get_material() const { + + return material; +} + void CSGMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh); @@ -873,7 +891,11 @@ void CSGMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed); + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGMesh::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh::get_material); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); } void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) { @@ -2060,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/csg/csg_shape.h b/modules/csg/csg_shape.h index 1622fb3a15..a5b2238e6b 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -38,8 +38,8 @@ #include "scene/resources/concave_polygon_shape.h" #include "thirdparty/misc/mikktspace.h" -class CSGShape : public VisualInstance { - GDCLASS(CSGShape, VisualInstance); +class CSGShape : public GeometryInstance { + GDCLASS(CSGShape, GeometryInstance); public: enum Operation { @@ -187,6 +187,7 @@ class CSGMesh : public CSGPrimitive { virtual CSGBrush *_build_brush(); Ref<Mesh> mesh; + Ref<Material> material; void _mesh_changed(); @@ -196,6 +197,9 @@ protected: public: void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh(); + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; }; class CSGSphere : public CSGPrimitive { diff --git a/modules/csg/doc_classes/CSGBox.xml b/modules/csg/doc_classes/CSGBox.xml index 1684850f0a..e508468415 100644 --- a/modules/csg/doc_classes/CSGBox.xml +++ b/modules/csg/doc_classes/CSGBox.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml index 819a4a3a22..51428b25f8 100644 --- a/modules/csg/doc_classes/CSGCombiner.xml +++ b/modules/csg/doc_classes/CSGCombiner.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/csg/doc_classes/CSGCylinder.xml b/modules/csg/doc_classes/CSGCylinder.xml index 50a88d6773..24c3f8ba2e 100644 --- a/modules/csg/doc_classes/CSGCylinder.xml +++ b/modules/csg/doc_classes/CSGCylinder.xml @@ -8,13 +8,11 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="cone" type="bool" setter="set_cone" getter="is_cone"> - If true a cone is created, the [member radius] will only apply to one side. + If [code]true[/code] a cone is created, the [member radius] will only apply to one side. </member> <member name="height" type="float" setter="set_height" getter="get_height"> The height of the cylinder. @@ -29,7 +27,7 @@ The number of sides of the cylinder, the higher this number the more detail there will be in the cylinder. </member> <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. When false the cylinder will have a flat shaded look. + If [code]true[/code] the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. If [code]false[/code] the cylinder will have a flat shaded look. </member> </members> <constants> diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml index fc9815d7c0..afe0bc262d 100644 --- a/modules/csg/doc_classes/CSGMesh.xml +++ b/modules/csg/doc_classes/CSGMesh.xml @@ -8,11 +8,11 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> + <member name="material" type="Material" setter="set_material" getter="get_material"> + </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> The mesh resource to use as a CSG shape. </member> diff --git a/modules/csg/doc_classes/CSGPolygon.xml b/modules/csg/doc_classes/CSGPolygon.xml index ae75f7e01b..2c5d298222 100644 --- a/modules/csg/doc_classes/CSGPolygon.xml +++ b/modules/csg/doc_classes/CSGPolygon.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -23,16 +21,16 @@ Extrusion mode. </member> <member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u"> - If true the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH]. + If [code]true[/code] the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH]. </member> <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval"> Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH]. </member> <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined"> - If true the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH]. + If [code]true[/code] the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH]. </member> <member name="path_local" type="bool" setter="set_path_local" getter="is_path_local"> - If false we extrude centered on our path, if true we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH]. + If [code]false[/code] we extrude centered on our path, if [code]true[/code] we extrude in relation to the position of our CSGPolygon when [member mode] is [constant MODE_PATH]. </member> <member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node"> The [Shape] object containing the path along which we extrude when [member mode] is [constant MODE_PATH]. diff --git a/modules/csg/doc_classes/CSGPrimitive.xml b/modules/csg/doc_classes/CSGPrimitive.xml index 502a8230e4..869e4006fe 100644 --- a/modules/csg/doc_classes/CSGPrimitive.xml +++ b/modules/csg/doc_classes/CSGPrimitive.xml @@ -7,8 +7,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml index ccfc5a04c0..7fa7c78534 100644 --- a/modules/csg/doc_classes/CSGShape.xml +++ b/modules/csg/doc_classes/CSGShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGShape" inherits="VisualInstance" category="Core" version="3.2"> +<class name="CSGShape" inherits="GeometryInstance" category="Core" version="3.2"> <brief_description> The CSG base class. </brief_description> @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_collision_layer_bit" qualifiers="const"> <return type="bool"> @@ -39,7 +37,7 @@ <return type="bool"> </return> <description> - Returns true if this is a root shape and is thus the object that is rendered. + Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. </description> </method> <method name="set_collision_layer_bit"> diff --git a/modules/csg/doc_classes/CSGSphere.xml b/modules/csg/doc_classes/CSGSphere.xml index 088c9f14eb..2a12cf84db 100644 --- a/modules/csg/doc_classes/CSGSphere.xml +++ b/modules/csg/doc_classes/CSGSphere.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -26,7 +24,7 @@ Number of horizontal slices for the sphere. </member> <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the sphere are set to give a smooth effect making the sphere seem rounded. When false the sphere will have a flat shaded look. + If [code]true[/code] the normals of the sphere are set to give a smooth effect making the sphere seem rounded. If [code]false[/code] the sphere will have a flat shaded look. </member> </members> <constants> diff --git a/modules/csg/doc_classes/CSGTorus.xml b/modules/csg/doc_classes/CSGTorus.xml index 946637bd2c..0d4437d87f 100644 --- a/modules/csg/doc_classes/CSGTorus.xml +++ b/modules/csg/doc_classes/CSGTorus.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -29,7 +27,7 @@ The number of slices the torus is constructed of. </member> <member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces"> - If true the normals of the torus are set to give a smooth effect making the torus seem rounded. When false the torus will have a flat shaded look. + If [code]true[/code] the normals of the torus are set to give a smooth effect making the torus seem rounded. If [code]false[/code] the torus will have a flat shaded look. </member> </members> <constants> diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index fcc69d8371..142af0c800 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -14,7 +14,7 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_cvtt.Append(CPPPATH=[thirdparty_dir]) + env_cvtt.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_cvtt.Clone() env_thirdparty.disable_warnings() diff --git a/modules/enet/SCsub b/modules/enet/SCsub index a57a4b29ea..78d8d43414 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -21,7 +21,7 @@ if env['builtin_enet']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_enet.Append(CPPPATH=[thirdparty_dir]) + env_enet.Prepend(CPPPATH=[thirdparty_dir]) env_enet.Append(CPPFLAGS=["-DGODOT_ENET"]) env_thirdparty = env_enet.Clone() diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index c1bec533dd..d3d1e58b7b 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -10,8 +10,6 @@ <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> <link>http://enet.bespin.org/usergroup0.html</link> </tutorials> - <demos> - </demos> <methods> <method name="close_connection"> <return type="void"> @@ -62,7 +60,7 @@ <argument index="1" name="now" type="bool" default="false"> </argument> <description> - Disconnect the given peer. If "now" is set to true, the connection will be closed immediately without flushing queued messages. + Disconnect the given peer. If "now" is set to [code]true[/code], the connection will be closed immediately without flushing queued messages. </description> </method> <method name="get_last_packet_channel" qualifiers="const"> @@ -112,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 193f1bb48d..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); @@ -346,11 +346,10 @@ void NetworkedMultiplayerENet::poll() { uint32_t *id = (uint32_t *)event.peer->data; - ERR_CONTINUE(event.packet->dataLength < 12) + ERR_CONTINUE(event.packet->dataLength < 8) uint32_t source = decode_uint32(&event.packet->data[0]); int target = decode_uint32(&event.packet->data[4]); - uint32_t flags = decode_uint32(&event.packet->data[8]); packet.from = source; packet.channel = event.channelID; @@ -371,7 +370,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source) // Do not resend to self continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -385,7 +384,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -503,8 +502,8 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - *r_buffer = (const uint8_t *)(¤t_packet.packet->data[12]); - r_buffer_size = current_packet.packet->dataLength - 12; + *r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]); + r_buffer_size = current_packet.packet->dataLength - 8; return OK; } @@ -549,11 +548,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } } - ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags); + ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 8, packet_flags); encode_uint32(unique_id, &packet->data[0]); // Source ID encode_uint32(target_peer, &packet->data[4]); // Dest ID - encode_uint32(packet_flags, &packet->data[8]); // Dest ID - copymem(&packet->data[12], p_buffer, p_buffer_size); + copymem(&packet->data[8], p_buffer, p_buffer_size); if (server) { diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 6e963ef766..532b97b006 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -27,11 +27,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_etc.Append(CPPPATH=[thirdparty_dir]) +env_etc.Prepend(CPPPATH=[thirdparty_dir]) # upstream uses c++11 if not env.msvc: - env_etc.Append(CCFLAGS="-std=c++11") + env_etc.Append(CXXFLAGS="-std=c++11") env_thirdparty = env_etc.Clone() env_thirdparty.disable_warnings() diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index 3e2068b8db..0ea581220e 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -12,7 +12,6 @@ if env['builtin_freetype']: thirdparty_dir = "#thirdparty/freetype/" thirdparty_sources = [ "src/autofit/autofit.c", - "src/base/ftapi.c", "src/base/ftbase.c", "src/base/ftbbox.c", "src/base/ftbdf.c", @@ -62,24 +61,27 @@ 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.Append(CPPPATH=[thirdparty_dir + "/include"]) + env_freetype.Prepend(CPPPATH=[thirdparty_dir + "/include"]) # Also needed in main env for scene/ - env.Append(CPPPATH=[thirdparty_dir + "/include"]) + env.Prepend(CPPPATH=[thirdparty_dir + "/include"]) - env_freetype.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY', '-DFT_CONFIG_OPTION_USE_PNG']) + env_freetype.Append(CPPFLAGS=['-DFT2_BUILD_LIBRARY', '-DFT_CONFIG_OPTION_USE_PNG']) if (env['target'] != 'release'): - env_freetype.Append(CCFLAGS=['-DZLIB_DEBUG']) + env_freetype.Append(CPPFLAGS=['-DZLIB_DEBUG']) # Also requires libpng headers if env['builtin_libpng']: - env_freetype.Append(CPPPATH=["#thirdparty/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() @@ -101,4 +103,4 @@ if env['builtin_freetype']: # Godot source files env_freetype.add_source_files(env.modules_sources, "*.cpp") # Used in scene/, needs to be in main env -env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) +env.Append(CPPFLAGS=['-DFREETYPE_ENABLED']) diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 235f0b97bb..0cdd585558 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -12,7 +12,7 @@ env_gdnative.add_source_files(env.modules_sources, "nativescript/*.cpp") env_gdnative.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") env_gdnative.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") -env_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) +env_gdnative.Prepend(CPPPATH=['#modules/gdnative/include/']) Export('env_gdnative') @@ -36,7 +36,7 @@ if ARGUMENTS.get('gdnative_wrapper', False): gensource, = env_gdnative.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) gd_wrapper_env = env.Clone() - gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) + gd_wrapper_env.Prepend(CPPPATH=['#modules/gdnative/include/']) if gd_wrapper_env['use_lto']: if not env.msvc: diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index 8c602e0cba..c3f8688adb 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -222,6 +222,15 @@ void ARVRInterfaceGDNative::process() { interface->process(data); } +void ARVRInterfaceGDNative::notification(int p_what) { + ERR_FAIL_COND(interface == NULL); + + // this is only available in interfaces that implement 1.1 or later + if ((interface->version.major > 1) || ((interface->version.major == 1) && (interface->version.minor > 0))) { + interface->notification(data, p_what); + } +} + ///////////////////////////////////////////////////////////////////////////////////// // some helper callbacks diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h index 3f966ece51..86396b067a 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ b/modules/gdnative/arvr/arvr_interface_gdnative.h @@ -82,6 +82,7 @@ public: virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); + virtual void notification(int p_what); }; #endif // ARVR_INTERFACE_GDNATIVE_H diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index a36e76287a..7898de5523 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -16,6 +16,8 @@ def get_doc_classes(): "ResourceFormatLoaderVideoStreamGDNative", "StreamPeerGDNative", "VideoStreamGDNative", + "WebRTCPeerConnectionGDNative", + "WebRTCDataChannelGDNative", ] def get_doc_path(): diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml index afb014d608..efdb948660 100644 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index e5a59aad07..8750ddc56d 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="call_native"> <return type="Variant"> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index ba5278d440..8bfd386b8d 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_current_dependencies" qualifiers="const"> <return type="PoolStringArray"> diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml index ba481a6d6e..b9a01672a6 100644 --- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml +++ b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index c50f9eee22..ac8b793b22 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_class_documentation" qualifiers="const"> <return type="String"> diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml index f4d7d22f5b..acfb597cff 100644 --- a/modules/gdnative/doc_classes/PacketPeerGDNative.xml +++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 8510708124..b07122bbdf 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new" qualifiers="vararg"> <return type="Object"> diff --git a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml b/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml index 8e7f4698ff..cd8b336778 100644 --- a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml +++ b/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml index eddebf4889..f7e0d76fdb 100644 --- a/modules/gdnative/doc_classes/StreamPeerGDNative.xml +++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml index d5c5ed7ccf..ed7678b7be 100644 --- a/modules/gdnative/doc_classes/VideoStreamGDNative.xml +++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml new file mode 100644 index 0000000000..ac18ec6020 --- /dev/null +++ b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> 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/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp index 8fa29580d6..a2ac61b35e 100644 --- a/modules/gdnative/gdnative/vector2.cpp +++ b/modules/gdnative/gdnative/vector2.cpp @@ -119,6 +119,14 @@ godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, return dest; } +godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta) { + godot_vector2 dest; + const Vector2 *self = (const Vector2 *)p_self; + const Vector2 *to = (const Vector2 *)p_to; + *((Vector2 *)&dest) = self->move_toward(*to, p_delta); + return dest; +} + godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi) { godot_vector2 dest; const Vector2 *self = (const Vector2 *)p_self; diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp index ef86c6f7e9..894683ab38 100644 --- a/modules/gdnative/gdnative/vector3.cpp +++ b/modules/gdnative/gdnative/vector3.cpp @@ -124,6 +124,14 @@ godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, return dest; } +godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta) { + godot_vector3 dest; + const Vector3 *self = (const Vector3 *)p_self; + const Vector3 *to = (const Vector3 *)p_to; + *((Vector3 *)&dest) = self->move_toward(*to, p_delta); + return dest; +} + godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b) { const Vector3 *self = (const Vector3 *)p_self; const Vector3 *b = (const Vector3 *)p_b; diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 9882a89794..52c989037e 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -11,7 +11,42 @@ "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"] + ] + }, + { + "name": "godot_vector3_move_toward", + "return_type": "godot_vector3", + "arguments": [ + ["const godot_vector3 *", "p_self"], + ["const godot_vector3 *", "p_to"], + ["const godot_real", "p_delta"] + ] + }, + { + "name": "godot_vector2_move_toward", + "return_type": "godot_vector2", + "arguments": [ + ["const godot_vector2 *", "p_self"], + ["const godot_vector2 *", "p_to"], + ["const godot_real", "p_delta"] + ] + } + ] + }, "api": [ { "name": "godot_color_to_abgr32", @@ -6436,11 +6471,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/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h index 321b471d0e..657090fa70 100644 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ b/modules/gdnative/include/arvr/godot_arvr.h @@ -63,6 +63,7 @@ typedef struct { void (*process)(void *); // only in 1.1 onwards godot_int (*get_external_texture_for_eye)(void *, godot_int); + void (*notification)(void *, godot_int); } godot_arvr_interface_gdnative; void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); 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/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h index 9e37b8e0c6..7a5ae6afa9 100644 --- a/modules/gdnative/include/gdnative/vector2.h +++ b/modules/gdnative/include/gdnative/vector2.h @@ -83,6 +83,8 @@ godot_vector2 GDAPI godot_vector2_linear_interpolate(const godot_vector2 *p_self godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_vector2 *p_pre_a, const godot_vector2 *p_post_b, const godot_real p_t); +godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta); + godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi); godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self); diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h index 61f0c6c62e..70ec6422ac 100644 --- a/modules/gdnative/include/gdnative/vector3.h +++ b/modules/gdnative/include/gdnative/vector3.h @@ -90,6 +90,8 @@ godot_vector3 GDAPI godot_vector3_linear_interpolate(const godot_vector3 *p_self godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_vector3 *p_pre_a, const godot_vector3 *p_post_b, const godot_real p_t); +godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta); + godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b); godot_vector3 GDAPI godot_vector3_cross(const godot_vector3 *p_self, const godot_vector3 *p_b); 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/include/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h index 968f91ae9f..a9e83d1524 100644 --- a/modules/gdnative/include/pluginscript/godot_pluginscript.h +++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h @@ -136,7 +136,7 @@ typedef struct { godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions); int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be NULL godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_pool_string_array *p_args); - godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_base_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint); + godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint); void (*auto_indent_code)(godot_pluginscript_language_data *p_data, godot_string *p_code, int p_from_line, int p_to_line); void (*add_global_constant)(godot_pluginscript_language_data *p_data, const godot_string *p_variable, const godot_variant *p_value); diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 4577c7327a..e0cf990f83 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -139,6 +139,34 @@ static String get_type_name(const PropertyInfo &info) { } /* + * Some comparison helper functions we need + */ + +struct MethodInfoComparator { + StringName::AlphCompare compare; + bool operator()(const MethodInfo &p_a, const MethodInfo &p_b) const { + + return compare(p_a.name, p_b.name); + } +}; + +struct PropertyInfoComparator { + StringName::AlphCompare compare; + bool operator()(const PropertyInfo &p_a, const PropertyInfo &p_b) const { + + return compare(p_a.name, p_b.name); + } +}; + +struct ConstantAPIComparator { + NoCaseComparator compare; + bool operator()(const ConstantAPI &p_a, const ConstantAPI &p_b) const { + + return compare(p_a.constant_name, p_b.constant_name); + } +}; + +/* * Reads the entire Godot API to a list */ List<ClassAPI> generate_c_api_classes() { @@ -147,6 +175,7 @@ List<ClassAPI> generate_c_api_classes() { List<StringName> classes; ClassDB::get_class_list(&classes); + classes.sort_custom<StringName::AlphCompare>(); // Register global constants as a fake GlobalConstants singleton class { @@ -162,6 +191,7 @@ List<ClassAPI> generate_c_api_classes() { constant_api.constant_value = GlobalConstants::get_global_constant_value(i); global_constants_api.constants.push_back(constant_api); } + global_constants_api.constants.sort_custom<ConstantAPIComparator>(); api.push_back(global_constants_api); } @@ -193,6 +223,7 @@ List<ClassAPI> generate_c_api_classes() { { List<String> constant; ClassDB::get_integer_constant_list(class_name, &constant, true); + constant.sort_custom<NoCaseComparator>(); for (List<String>::Element *c = constant.front(); c != NULL; c = c->next()) { ConstantAPI constant_api; constant_api.constant_name = c->get(); @@ -206,6 +237,7 @@ List<ClassAPI> generate_c_api_classes() { { List<MethodInfo> signals_; ClassDB::get_signal_list(class_name, &signals_, true); + signals_.sort_custom<MethodInfoComparator>(); for (int i = 0; i < signals_.size(); i++) { SignalAPI signal; @@ -245,6 +277,7 @@ List<ClassAPI> generate_c_api_classes() { { List<PropertyInfo> properties; ClassDB::get_property_list(class_name, &properties, true); + properties.sort_custom<PropertyInfoComparator>(); for (List<PropertyInfo>::Element *p = properties.front(); p != NULL; p = p->next()) { PropertyAPI property_api; @@ -272,6 +305,7 @@ List<ClassAPI> generate_c_api_classes() { { List<MethodInfo> methods; ClassDB::get_method_list(class_name, &methods, true); + methods.sort_custom<MethodInfoComparator>(); for (List<MethodInfo>::Element *m = methods.front(); m != NULL; m = m->next()) { MethodAPI method_api; 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/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index c9d92c09ed..7cb47ec623 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -159,13 +159,13 @@ String PluginScriptLanguage::make_function(const String &p_class, const String & return String(); } -Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { +Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { if (_desc.complete_code) { Array options; godot_error tmp = _desc.complete_code( _data, (godot_string *)&p_code, - (godot_string *)&p_base_path, + (godot_string *)&p_path, (godot_object *)p_owner, (godot_array *)&options, &r_force, diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index 991be0bf12..a11e916975 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -81,7 +81,7 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint); virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); 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/SCsub b/modules/gdnative/videodecoder/SCsub index 8d9c1ff50e..04cc8ed604 100644 --- a/modules/gdnative/videodecoder/SCsub +++ b/modules/gdnative/videodecoder/SCsub @@ -5,5 +5,5 @@ Import('env_modules') env_vsdecoder_gdnative = env_modules.Clone() -env_vsdecoder_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) +env_vsdecoder_gdnative.Prepend(CPPPATH=['#modules/gdnative/include/']) env_vsdecoder_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp index 8fcebe7855..be131c5402 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp +++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp @@ -61,8 +61,8 @@ int64_t GDAPI godot_videodecoder_file_seek(void *ptr, int64_t pos, int whence) { // file FileAccess *file = reinterpret_cast<FileAccess *>(ptr); - size_t len = file->get_len(); if (file) { + size_t len = file->get_len(); switch (whence) { case SEEK_SET: { // Just for explicitness @@ -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/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 46796c68eb..d606a41fab 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -10,8 +10,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="get_as_byte_code" qualifiers="const"> <return type="PoolByteArray"> diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml index f38f39b612..690953108f 100644 --- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml +++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="is_valid" qualifiers="const"> <return type="bool"> diff --git a/modules/gdscript/doc_classes/GDScriptNativeClass.xml b/modules/gdscript/doc_classes/GDScriptNativeClass.xml index e86b69c31c..70583d47a7 100644 --- a/modules/gdscript/doc_classes/GDScriptNativeClass.xml +++ b/modules/gdscript/doc_classes/GDScriptNativeClass.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new"> <return type="Variant"> 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 0676317f6e..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; @@ -1945,6 +1967,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return "The local variable '" + symbols[0] + "' is declared but never used in the block."; } break; + case SHADOWED_VARIABLE: { + CHECK_SYMBOLS(2); + return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + "."; + } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); return "The class variable '" + symbols[0] + "' is declared but never used in the script."; @@ -2048,6 +2074,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNASSIGNED_VARIABLE", "UNASSIGNED_VARIABLE_OP_ASSIGN", "UNUSED_VARIABLE", + "SHADOWED_VARIABLE", "UNUSED_CLASS_VARIABLE", "UNUSED_ARGUMENT", "UNREACHABLE_CODE", diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index c67e390e32..40b773d99f 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; @@ -273,6 +274,7 @@ struct GDScriptWarning { UNASSIGNED_VARIABLE, // Variable used but never assigned UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) UNUSED_VARIABLE, // Local variable is declared but never used + SHADOWED_VARIABLE, // Variable name shadowed by other variable UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file UNUSED_ARGUMENT, // Function argument is never used UNREACHABLE_CODE, // Code after a return statement @@ -406,9 +408,10 @@ public: csi.resize(_debug_call_stack_pos); for (int i = 0; i < _debug_call_stack_pos; i++) { csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0; - if (_call_stack[i].function) + if (_call_stack[i].function) { csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name(); - csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); + csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); + } } return csi; } @@ -455,9 +458,9 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint); #ifdef TOOLS_ENABLED - virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result); + virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result); #endif virtual String _get_indentation() const; virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index f7be0ce37c..189317b163 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1977,12 +1977,12 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - - GDScript *subclass = p_script->subclasses[name].ptr(); + Ref<GDScript> &subclass = p_script->subclasses[name]; + GDScript *subclass_ptr = subclass.ptr(); // Subclass might still be parsing, just skip it - if (!parsed_classes.has(subclass) && !parsing_classes.has(subclass)) { - Error err = _parse_class_level(subclass, p_class->subclasses[i], p_keep_state); + if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) { + Error err = _parse_class_level(subclass_ptr, p_class->subclasses[i], p_keep_state); if (err) return err; } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index df8fc2267d..6c77968f44 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -511,8 +511,10 @@ struct GDScriptCompletionIdentifier { static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) { + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; + for (int i = 0; i < p_dir->get_file_count(); i++) { - r_list.insert("\"" + p_dir->get_file_path(i) + "\""); + r_list.insert(quote_style + p_dir->get_file_path(i) + quote_style); } for (int i = 0; i < p_dir->get_subdir_count(); i++) { @@ -1949,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; @@ -2052,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) { @@ -2176,7 +2178,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Set<String> &r_result, String &r_arghint) { Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; - bool _static = false; + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; while (base_type.has_type) { switch (base_type.kind) { @@ -2187,18 +2190,16 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con return; } } - if (!_static) { - for (int i = 0; i < base_type.class_type->functions.size(); i++) { - if (base_type.class_type->functions[i]->name == p_method) { - r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx); - return; - } + for (int i = 0; i < base_type.class_type->functions.size(); i++) { + if (base_type.class_type->functions[i]->name == p_method) { + r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx); + return; } } if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - r_result.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\""); + r_result.insert(quote_style + base_type.class_type->_signals[i].name.operator String() + quote_style); } } @@ -2211,7 +2212,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; gds->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().name + "\""); + r_result.insert(quote_style + E->get().name + quote_style); } } Ref<GDScript> base_script = gds->get_base_script(); @@ -2270,7 +2271,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().name + "\""); + r_result.insert(quote_style + E->get().name + quote_style); } } @@ -2285,7 +2286,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert("\"/root/" + name + "\""); + r_result.insert(quote_style + "/root/" + name + quote_style); } } @@ -2299,7 +2300,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert("\"" + name + "\""); + r_result.insert(quote_style + name + quote_style); } } @@ -2334,6 +2335,8 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) { + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; + if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) { return; } @@ -2451,18 +2454,20 @@ static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDS Set<String> methods; _find_identifiers_in_base(p_context, connect_base, true, methods); for (Set<String>::Element *E = methods.front(); E; E = E->next()) { - r_result.insert("\"" + E->get().replace("(", "").replace(")", "") + "\""); + r_result.insert(quote_style + E->get().replace("(", "").replace(")", "") + quote_style); } } r_forced = r_result.size() > 0; } -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; GDScriptParser parser; - parser.parse(p_code, p_base_path, false, "", true); + parser.parse(p_code, p_path.get_base_dir(), false, p_path, true); r_forced = false; Set<String> options; GDScriptCompletionContext context; @@ -2473,7 +2478,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base if (!context._class || context._class->owner == NULL) { context.base = p_owner; - context.base_path = p_base_path; + context.base_path = p_path.get_base_dir(); } bool is_function = false; @@ -2526,7 +2531,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base continue; } String name = s.get_slice("/", 1); - options.insert("\"/root/" + name + "\""); + options.insert(quote_style + "/root/" + name + quote_style); } } } break; @@ -2666,7 +2671,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - options.insert("\"" + base_type.class_type->_signals[i].name.operator String() + "\""); + options.insert(quote_style + base_type.class_type->_signals[i].name.operator String() + quote_style); } base_type = base_type.class_type->base_type; } break; @@ -2677,7 +2682,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base List<MethodInfo> signals; scr->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert("\"" + E->get().name + "\""); + options.insert(quote_style + E->get().name + quote_style); } Ref<Script> base_script = scr->get_base_script(); if (base_script.is_valid()) { @@ -2704,7 +2709,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert("\"" + E->get().name + "\""); + options.insert(quote_style + E->get().name + quote_style); } } break; default: { @@ -2879,7 +2884,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base #else -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { return OK; } @@ -2890,7 +2895,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base String GDScriptLanguage::_get_indentation() const { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { - bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0); + bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", false); if (use_space_indentation) { int indent_size = EDITOR_DEF("text_editor/indent/size", 4); @@ -3008,8 +3013,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co } } } + base_type = base_type.class_type->base_type; } - base_type = base_type.class_type->base_type; } break; case GDScriptParser::DataType::SCRIPT: case GDScriptParser::DataType::GDSCRIPT: { @@ -3150,7 +3155,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co return ERR_CANT_RESOLVE; } -Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_base_path, Object *p_owner, LookupResult &r_result) { +Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { //before parsing, try the usual stuff if (ClassDB::class_exists(p_symbol)) { @@ -3192,7 +3197,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol } GDScriptParser parser; - parser.parse(p_code, p_base_path, false, "", true); + parser.parse(p_code, p_path.get_base_dir(), false, p_path, true); if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) { return ERR_CANT_RESOLVE; @@ -3204,7 +3209,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol context.block = parser.get_completion_block(); context.line = parser.get_completion_line(); context.base = p_owner; - context.base_path = p_base_path; + context.base_path = p_path.get_base_dir(); if (context._class && context._class->extends_class.size() > 0) { bool success = false; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 4fd136d5cc..98263d43e5 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -68,13 +68,17 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "exp", "is_nan", "is_inf", + "is_equal_approx", + "is_zero_approx", "ease", "decimals", + "step_decimals", "stepify", "lerp", "inverse_lerp", "range_lerp", "smoothstep", + "move_toward", "dectime", "randomize", "randi", @@ -316,6 +320,17 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(0); r_ret = Math::is_inf((double)*p_args[0]); } break; + case MATH_ISEQUALAPPROX: { + VALIDATE_ARG_COUNT(2); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]); + } break; + case MATH_ISZEROAPPROX: { + VALIDATE_ARG_COUNT(1); + VALIDATE_ARG_NUM(0); + r_ret = Math::is_zero_approx((real_t)*p_args[0]); + } break; case MATH_EASE: { VALIDATE_ARG_COUNT(2); VALIDATE_ARG_NUM(0); @@ -326,6 +341,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_COUNT(1); VALIDATE_ARG_NUM(0); r_ret = Math::step_decimals((double)*p_args[0]); + ERR_EXPLAIN("GDScript method 'decimals' is deprecated and has been renamed to 'step_decimals', please update your code accordingly."); + WARN_DEPRECATED + } break; + case MATH_STEP_DECIMALS: { + VALIDATE_ARG_COUNT(1); + VALIDATE_ARG_NUM(0); + r_ret = Math::step_decimals((double)*p_args[0]); } break; case MATH_STEPIFY: { VALIDATE_ARG_COUNT(2); @@ -377,6 +399,13 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ VALIDATE_ARG_NUM(2); r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); } break; + case MATH_MOVE_TOWARD: { + VALIDATE_ARG_COUNT(3); + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + } break; case MATH_DECTIME: { VALIDATE_ARG_COUNT(3); VALIDATE_ARG_NUM(0); @@ -1439,11 +1468,13 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_ISINF: case MATH_EASE: case MATH_DECIMALS: + case MATH_STEP_DECIMALS: case MATH_STEPIFY: case MATH_LERP: case MATH_INVERSE_LERP: case MATH_RANGE_LERP: case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1596,6 +1627,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::BOOL; return mi; } break; + case MATH_ISEQUALAPPROX: { + MethodInfo mi("is_equal_approx", PropertyInfo(Variant::REAL, "a"), PropertyInfo(Variant::REAL, "b")); + mi.return_val.type = Variant::BOOL; + return mi; + } break; + case MATH_ISZEROAPPROX: { + MethodInfo mi("is_zero_approx", PropertyInfo(Variant::REAL, "s")); + mi.return_val.type = Variant::BOOL; + return mi; + } break; case MATH_EASE: { MethodInfo mi("ease", PropertyInfo(Variant::REAL, "s"), PropertyInfo(Variant::REAL, "curve")); mi.return_val.type = Variant::REAL; @@ -1603,7 +1644,12 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { } break; case MATH_DECIMALS: { MethodInfo mi("decimals", PropertyInfo(Variant::REAL, "step")); - mi.return_val.type = Variant::REAL; + mi.return_val.type = Variant::INT; + return mi; + } break; + case MATH_STEP_DECIMALS: { + MethodInfo mi("step_decimals", PropertyInfo(Variant::REAL, "step")); + mi.return_val.type = Variant::INT; return mi; } break; case MATH_STEPIFY: { @@ -1632,6 +1678,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::REAL; return mi; } break; + case MATH_MOVE_TOWARD: { + MethodInfo mi("move_toward", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "delta")); + mi.return_val.type = Variant::REAL; + return mi; + } break; case MATH_DECTIME: { MethodInfo mi("dectime", PropertyInfo(Variant::REAL, "value"), PropertyInfo(Variant::REAL, "amount"), PropertyInfo(Variant::REAL, "step")); mi.return_val.type = Variant::REAL; diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index 14bf3d7560..6ad70f2eb4 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -59,13 +59,17 @@ public: MATH_EXP, MATH_ISNAN, MATH_ISINF, + MATH_ISEQUALAPPROX, + MATH_ISZEROAPPROX, MATH_EASE, MATH_DECIMALS, + MATH_STEP_DECIMALS, MATH_STEPIFY, MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, MATH_SMOOTHSTEP, + MATH_MOVE_TOWARD, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 8a9eacd835..44a91303e9 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -282,7 +282,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s switch (tokenizer->get_token()) { case GDScriptTokenizer::TK_CURSOR: { - completion_cursor = StringName(); completion_type = COMPLETION_GET_NODE; completion_class = current_class; completion_function = current_function; @@ -2870,8 +2869,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { lv->assign_op = op; lv->assign = assigned; - lv->assign_op = op; - if (!_end_statement()) { _set_error("Expected end of statement (var)"); return; @@ -3372,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; @@ -3500,7 +3497,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("'class_name' is only valid for the main class namespace."); return; } - if (self_path.empty()) { + if (self_path.begins_with("res://") && self_path.find("::") != -1) { _set_error("'class_name' not allowed in built-in scripts."); return; } @@ -3509,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); @@ -4785,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); @@ -5240,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++) { @@ -5249,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]; @@ -5257,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); @@ -5265,7 +5280,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { } } - script = base_script; + script = find_subclass; } else if (!base_class) { @@ -6006,7 +6021,11 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data } GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { +#ifdef DEBUG_ENABLED + if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) { +#else if (p_node->get_datatype().has_type) { +#endif return p_node->get_datatype(); } @@ -6226,8 +6245,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { return DataType(); } #ifdef DEBUG_ENABLED - if (var_op == Variant::OP_DIVIDE && argument_a_type.has_type && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT && - argument_b_type.has_type && argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) { + if (var_op == Variant::OP_DIVIDE && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT && + argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) { _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line); } #endif // DEBUG_ENABLED @@ -6938,10 +6957,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat #ifdef DEBUG_ENABLED if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) { - if (current_function && current_function->_static && p_call->arguments[0]->type == Node::TYPE_SELF) { - _set_error("Can't call non-static function from a static function.", p_call->line); - return DataType(); - } + _set_error("Can't call non-static function from a static function.", p_call->line); + return DataType(); } if (check_types && !is_static && !is_initializer && base_type.is_meta_type) { @@ -7467,7 +7484,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; @@ -7496,30 +7513,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 @@ -7658,6 +7651,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) { _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); } + for (int j = 0; j < current_class->variables.size(); j++) { + if (current_class->variables[j].identifier == p_function->arguments[i]) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line)); + } + } #endif // DEBUG_ENABLED } @@ -7731,6 +7729,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { p_function->return_type.has_type = false; p_function->return_type.may_yield = true; } + +#ifdef DEBUG_ENABLED + for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == lv->name) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line)); + } + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { @@ -7839,14 +7848,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; @@ -7970,14 +7979,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 36503af4d7..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 (hexa_found || 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/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 655be4eb20..f8f9fc1af9 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -12,8 +12,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 17eb6f674c..890bd730f7 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -67,9 +67,6 @@ void GridMapEditor::_menu_option(int p_option) { floor->set_value(floor->get_value() + 1); } break; - case MENU_OPTION_CONFIGURE: { - - } break; case MENU_OPTION_LOCK_VIEW: { int index = options->get_popup()->get_item_index(MENU_OPTION_LOCK_VIEW); @@ -121,14 +118,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_Y: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -137,12 +135,12 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_X: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(1, 0, 0), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } @@ -154,12 +152,12 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_ROTATE_Z: { Basis r; - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - r.set_orthogonal_index(selection.duplicate_rot); + r.set_orthogonal_index(paste_indicator.orientation); r.rotate(Vector3(0, 0, 1), -Math_PI / 2.0); - selection.duplicate_rot = r.get_orthogonal_index(); - _update_duplicate_indicator(); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); break; } @@ -171,6 +169,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_Y: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(0, 1, 0), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 1, 0), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -179,6 +186,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_X: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(1, 0, 0), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(1, 0, 0), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -187,6 +203,15 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_CURSOR_BACK_ROTATE_Z: { Basis r; + if (input_action == INPUT_PASTE) { + + r.set_orthogonal_index(paste_indicator.orientation); + r.rotate(Vector3(0, 0, 1), Math_PI / 2.0); + paste_indicator.orientation = r.get_orthogonal_index(); + _update_paste_indicator(); + break; + } + r.set_orthogonal_index(cursor_rot); r.rotate(Vector3(0, 0, 1), Math_PI / 2.0); cursor_rot = r.get_orthogonal_index(); @@ -194,10 +219,10 @@ void GridMapEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CURSOR_CLEAR_ROTATION: { - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - selection.duplicate_rot = 0; - _update_duplicate_indicator(); + paste_indicator.orientation = 0; + _update_paste_indicator(); break; } @@ -205,28 +230,33 @@ void GridMapEditor::_menu_option(int p_option) { _update_cursor_transform(); } break; - case MENU_OPTION_DUPLICATE_SELECTS: { - int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); + case MENU_OPTION_PASTE_SELECTS: { + int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS); options->get_popup()->set_item_checked(idx, !options->get_popup()->is_item_checked(idx)); } break; - case MENU_OPTION_SELECTION_DUPLICATE: + + case MENU_OPTION_SELECTION_DUPLICATE: // fallthrough + case MENU_OPTION_SELECTION_CUT: { if (!(selection.active && input_action == INPUT_NONE)) - return; - if (last_mouseover == Vector3(-1, -1, -1)) //nono mouseovering anythin break; - last_mouseover = selection.begin; - VS::get_singleton()->instance_set_transform(grid_instance[edit_axis], Transform(Basis(), grid_ofs)); + _set_clipboard_data(); - input_action = INPUT_DUPLICATE; - selection.click = last_mouseover; - selection.current = last_mouseover; - selection.duplicate_rot = 0; - _update_duplicate_indicator(); - break; + if (p_option == MENU_OPTION_SELECTION_CUT) { + _delete_selection(); + } + + input_action = INPUT_PASTE; + paste_indicator.click = selection.begin; + paste_indicator.current = selection.begin; + paste_indicator.begin = selection.begin; + paste_indicator.end = selection.end; + paste_indicator.orientation = 0; + _update_paste_indicator(); + } break; case MENU_OPTION_SELECTION_CLEAR: { if (!selection.active) - return; + break; _delete_selection(); @@ -315,17 +345,28 @@ void GridMapEditor::_validate_selection() { _update_selection_transform(); } +void GridMapEditor::_set_selection(bool p_active, const Vector3 p_begin, const Vector3 p_end) { + + selection.active = p_active; + selection.begin = p_begin; + selection.end = p_end; + selection.click = p_begin; + selection.current = p_end; + + _update_selection_transform(); +} + bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click) { if (!spatial_editor) return false; - if (selected_palette < 0 && input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE) + if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) return false; Ref<MeshLibrary> mesh_library = node->get_mesh_library(); if (mesh_library.is_null()) return false; - if (input_action != INPUT_COPY && input_action != INPUT_SELECT && input_action != INPUT_DUPLICATE && !mesh_library->has_item(selected_palette)) + if (input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) return false; Camera *camera = p_camera; @@ -386,13 +427,17 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo cursor_origin = (Vector3(cell[0], cell[1], cell[2]) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size(); cursor_visible = true; + if (input_action == INPUT_SELECT || input_action == INPUT_PASTE) { + cursor_visible = false; + } + _update_cursor_transform(); } - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { - selection.current = Vector3(cell[0], cell[1], cell[2]); - _update_duplicate_indicator(); + paste_indicator.current = Vector3(cell[0], cell[1], cell[2]); + _update_paste_indicator(); } else if (input_action == INPUT_SELECT) { @@ -403,7 +448,7 @@ bool GridMapEditor::do_input_action(Camera *p_camera, const Point2 &p_point, boo _validate_selection(); return true; - } else if (input_action == INPUT_COPY) { + } else if (input_action == INPUT_PICK) { int item = node->get_cell_item(cell[0], cell[1], cell[2]); if (item >= 0) { @@ -456,10 +501,9 @@ void GridMapEditor::_delete_selection() { } } } + undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->commit_action(); - - selection.active = false; - _validate_selection(); } void GridMapEditor::_fill_selection() { @@ -479,97 +523,124 @@ void GridMapEditor::_fill_selection() { } } } + undo_redo->add_do_method(this, "_set_selection", !selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->commit_action(); +} - selection.active = false; - _validate_selection(); +void GridMapEditor::_clear_clipboard_data() { + + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { + + VisualServer::get_singleton()->free(E->get().instance); + } + + clipboard_items.clear(); } -void GridMapEditor::_update_duplicate_indicator() { +void GridMapEditor::_set_clipboard_data() { + + _clear_clipboard_data(); - if (!selection.active || input_action != INPUT_DUPLICATE) { + Ref<MeshLibrary> meshLibrary = node->get_mesh_library(); + + for (int i = selection.begin.x; i <= selection.end.x; i++) { + + for (int j = selection.begin.y; j <= selection.end.y; j++) { + + for (int k = selection.begin.z; k <= selection.end.z; k++) { + + int itm = node->get_cell_item(i, j, k); + if (itm == GridMap::INVALID_CELL_ITEM) + continue; + + Ref<Mesh> mesh = meshLibrary->get_item_mesh(itm); + + ClipboardItem item; + item.cell_item = itm; + item.grid_offset = Vector3(i, j, k) - selection.begin; + item.orientation = node->get_cell_item_orientation(i, j, k); + item.instance = VisualServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world()->get_scenario()); + + clipboard_items.push_back(item); + } + } + } +} + +void GridMapEditor::_update_paste_indicator() { + + if (input_action != INPUT_PASTE) { Transform xf; xf.basis.set_zero(); - VisualServer::get_singleton()->instance_set_transform(duplicate_instance, xf); + VisualServer::get_singleton()->instance_set_transform(paste_instance, xf); return; } + Vector3 center = 0.5 * Vector3(node->get_center_x(), node->get_center_y(), node->get_center_z()); + Vector3 scale = (Vector3(1, 1, 1) + (paste_indicator.end - paste_indicator.begin)) * node->get_cell_size(); Transform xf; - xf.scale(Vector3(1, 1, 1) * (Vector3(1, 1, 1) + (selection.end - selection.begin)) * node->get_cell_size()); - xf.origin = (selection.begin + (selection.current - selection.click)) * node->get_cell_size(); + xf.scale(scale); + xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size(); Basis rot; - rot.set_orthogonal_index(selection.duplicate_rot); + rot.set_orthogonal_index(paste_indicator.orientation); xf.basis = rot * xf.basis; + xf.translate((-center * node->get_cell_size()) / scale); - VisualServer::get_singleton()->instance_set_transform(duplicate_instance, node->get_global_transform() * xf); -} + VisualServer::get_singleton()->instance_set_transform(paste_instance, node->get_global_transform() * xf); -struct __Item { - Vector3 pos; - int rot; - int item; -}; -void GridMapEditor::_duplicate_paste() { + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { - if (!selection.active) - return; + ClipboardItem &item = E->get(); - int idx = options->get_popup()->get_item_index(MENU_OPTION_DUPLICATE_SELECTS); - bool reselect = options->get_popup()->is_item_checked(idx); + xf = Transform(); + xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size(); + xf.basis = rot * xf.basis; + xf.translate(item.grid_offset * node->get_cell_size()); - List<__Item> items; + Basis item_rot; + item_rot.set_orthogonal_index(item.orientation); + xf.basis = item_rot * xf.basis; + + VisualServer::get_singleton()->instance_set_transform(item.instance, node->get_global_transform() * xf); + } +} + +void GridMapEditor::_do_paste() { + + int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS); + bool reselect = options->get_popup()->is_item_checked(idx); Basis rot; - rot.set_orthogonal_index(selection.duplicate_rot); + rot.set_orthogonal_index(paste_indicator.orientation); - for (int i = selection.begin.x; i <= selection.end.x; i++) { + Vector3 ofs = paste_indicator.current - paste_indicator.click; + undo_redo->create_action(TTR("GridMap Paste Selection")); - for (int j = selection.begin.y; j <= selection.end.y; j++) { + for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) { - for (int k = selection.begin.z; k <= selection.end.z; k++) { + ClipboardItem &item = E->get(); - int itm = node->get_cell_item(i, j, k); - if (itm == GridMap::INVALID_CELL_ITEM) - continue; - int orientation = node->get_cell_item_orientation(i, j, k); - __Item item; - Vector3 rel = Vector3(i, j, k) - selection.begin; - rel = rot.xform(rel); - - Basis orm; - orm.set_orthogonal_index(orientation); - orm = rot * orm; - - item.pos = selection.begin + rel; - item.item = itm; - item.rot = orm.get_orthogonal_index(); - items.push_back(item); - } - } - } + Vector3 pos = rot.xform(item.grid_offset) + paste_indicator.begin + ofs; - Vector3 ofs = selection.current - selection.click; - if (items.size()) { - undo_redo->create_action(TTR("GridMap Duplicate Selection")); - for (List<__Item>::Element *E = items.front(); E; E = E->next()) { - __Item &it = E->get(); - Vector3 pos = it.pos + ofs; + Basis orm; + orm.set_orthogonal_index(item.orientation); + orm = rot * orm; - undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, it.item, it.rot); - undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z)); - } - undo_redo->commit_action(); + undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, item.cell_item, orm.get_orthogonal_index()); + undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z)); } if (reselect) { - selection.begin += ofs; - selection.end += ofs; - selection.click = selection.begin; - selection.current = selection.end; - _validate_selection(); + undo_redo->add_do_method(this, "_set_selection", true, paste_indicator.begin + ofs, paste_indicator.end + ofs); + undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end); } + + undo_redo->commit_action(); + + _clear_clipboard_data(); } bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<InputEvent> &p_event) { @@ -596,28 +667,31 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu if (mb->get_button_index() == BUTTON_LEFT) { - if (input_action == INPUT_DUPLICATE) { - //paste - _duplicate_paste(); + if (input_action == INPUT_PASTE) { + _do_paste(); input_action = INPUT_NONE; - _update_duplicate_indicator(); + _update_paste_indicator(); } else if (mb->get_shift()) { input_action = INPUT_SELECT; + last_selection = selection; } else if (mb->get_command()) { - input_action = INPUT_COPY; + input_action = INPUT_PICK; } else { input_action = INPUT_PAINT; set_items.clear(); } } else if (mb->get_button_index() == BUTTON_RIGHT) { - if (input_action == INPUT_DUPLICATE) { + if (input_action == INPUT_PASTE) { + _clear_clipboard_data(); input_action = INPUT_NONE; - _update_duplicate_indicator(); - } else if (mb->get_shift()) { + _update_paste_indicator(); + return true; + } else if (selection.active) { + _set_selection(false); + return true; + } else { input_action = INPUT_ERASE; set_items.clear(); - } else { - return false; } } else { return false; @@ -650,13 +724,21 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return set_items.size() > 0; } + if (mb->get_button_index() == BUTTON_LEFT && input_action == INPUT_SELECT) { + + undo_redo->create_action("GridMap Selection"); + undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end); + undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end); + undo_redo->commit_action(); + } + if (mb->get_button_index() == BUTTON_LEFT && input_action != INPUT_NONE) { set_items.clear(); input_action = INPUT_NONE; return true; } - if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_DUPLICATE)) { + if (mb->get_button_index() == BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) { input_action = INPUT_NONE; return true; } @@ -670,6 +752,45 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu return do_input_action(p_camera, mm->get_position(), false); } + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (k->is_pressed()) { + if (k->get_scancode() == KEY_ESCAPE) { + + if (input_action == INPUT_PASTE) { + _clear_clipboard_data(); + input_action = INPUT_NONE; + _update_paste_indicator(); + return true; + } else if (selection.active) { + _set_selection(false); + return true; + } else { + selected_palette = -1; + mesh_library_palette->unselect_all(); + update_palette(); + _update_cursor_instance(); + return true; + } + } + + if (k->get_shift() && selection.active && input_action != INPUT_PASTE) { + + if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) { + selection.click[edit_axis]--; + _validate_selection(); + return true; + } + if (k->get_scancode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) { + selection.click[edit_axis]++; + _validate_selection(); + return true; + } + } + } + } + Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { @@ -818,7 +939,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { input_action = INPUT_NONE; selection.active = false; _update_selection_transform(); - _update_duplicate_indicator(); + _update_paste_indicator(); spatial_editor = Object::cast_to<SpatialEditorPlugin>(editor->get_editor_plugin_screen()); @@ -953,13 +1074,15 @@ void GridMapEditor::_notification(int p_what) { } selection_instance = VisualServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world()->get_scenario()); - duplicate_instance = VisualServer::get_singleton()->instance_create2(duplicate_mesh, get_tree()->get_root()->get_world()->get_scenario()); + paste_instance = VisualServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world()->get_scenario()); _update_selection_transform(); - _update_duplicate_indicator(); + _update_paste_indicator(); } break; case NOTIFICATION_EXIT_TREE: { + _clear_clipboard_data(); + for (int i = 0; i < 3; i++) { VS::get_singleton()->free(grid_instance[i]); @@ -970,9 +1093,9 @@ void GridMapEditor::_notification(int p_what) { } VisualServer::get_singleton()->free(selection_instance); - VisualServer::get_singleton()->free(duplicate_instance); + VisualServer::get_singleton()->free(paste_instance); selection_instance = RID(); - duplicate_instance = RID(); + paste_instance = RID(); } break; case NOTIFICATION_PROCESS: { @@ -1056,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); @@ -1065,6 +1192,8 @@ 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); } @@ -1097,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)); @@ -1128,15 +1259,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KEY_MASK_SHIFT + KEY_D); options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, KEY_W); options->get_popup()->add_separator(); - options->get_popup()->add_check_item("Duplicate Selects", MENU_OPTION_DUPLICATE_SELECTS); - options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Create Area"), MENU_OPTION_SELECTION_MAKE_AREA, KEY_CONTROL + KEY_C); - options->get_popup()->add_item(TTR("Create Exterior Connector"), MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR); - options->get_popup()->add_item(TTR("Erase Area"), MENU_OPTION_REMOVE_AREA); + options->get_popup()->add_check_item("Paste Selects", MENU_OPTION_PASTE_SELECTS); options->get_popup()->add_separator(); - options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C); - options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X); - options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F); + options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_CTRL + KEY_C); + options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KEY_MASK_CTRL + KEY_X); + options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_DELETE); + options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_CTRL + KEY_F); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS); @@ -1211,7 +1339,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { last_mouseover = Vector3(-1, -1, -1); selection_mesh = VisualServer::get_singleton()->mesh_create(); - duplicate_mesh = VisualServer::get_singleton()->mesh_create(); + paste_mesh = VisualServer::get_singleton()->mesh_create(); { //selection mesh create @@ -1319,12 +1447,12 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { VisualServer::get_singleton()->mesh_surface_set_material(selection_mesh, 1, outer_mat->get_rid()); d[VS::ARRAY_VERTEX] = triangles; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_TRIANGLES, d); - VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 0, inner_mat->get_rid()); + VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_TRIANGLES, d); + VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 0, inner_mat->get_rid()); d[VS::ARRAY_VERTEX] = lines; - VisualServer::get_singleton()->mesh_add_surface_from_arrays(duplicate_mesh, VS::PRIMITIVE_LINES, d); - VisualServer::get_singleton()->mesh_surface_set_material(duplicate_mesh, 1, outer_mat->get_rid()); + VisualServer::get_singleton()->mesh_add_surface_from_arrays(paste_mesh, VS::PRIMITIVE_LINES, d); + VisualServer::get_singleton()->mesh_surface_set_material(paste_mesh, 1, outer_mat->get_rid()); for (int i = 0; i < 3; i++) { d[VS::ARRAY_VERTEX] = square[i]; @@ -1341,6 +1469,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { GridMapEditor::~GridMapEditor() { + _clear_clipboard_data(); + for (int i = 0; i < 3; i++) { if (grid[i].is_valid()) @@ -1359,9 +1489,9 @@ GridMapEditor::~GridMapEditor() { if (selection_instance.is_valid()) VisualServer::get_singleton()->free(selection_instance); - VisualServer::get_singleton()->free(duplicate_mesh); - if (duplicate_instance.is_valid()) - VisualServer::get_singleton()->free(duplicate_instance); + VisualServer::get_singleton()->free(paste_mesh); + if (paste_instance.is_valid()) + VisualServer::get_singleton()->free(paste_instance); } void GridMapEditorPlugin::_notification(int p_what) { diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 59b8ac13da..da36165d4e 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -54,9 +54,9 @@ class GridMapEditor : public VBoxContainer { INPUT_NONE, INPUT_PAINT, INPUT_ERASE, - INPUT_COPY, + INPUT_PICK, INPUT_SELECT, - INPUT_DUPLICATE, + INPUT_PASTE, }; enum ClipMode { @@ -116,8 +116,17 @@ class GridMapEditor : public VBoxContainer { RID selection_instance; RID selection_level_mesh[3]; RID selection_level_instance[3]; - RID duplicate_mesh; - RID duplicate_instance; + RID paste_mesh; + RID paste_instance; + + struct ClipboardItem { + int cell_item; + Vector3 grid_offset; + int orientation; + RID instance; + }; + + List<ClipboardItem> clipboard_items; Ref<SpatialMaterial> indicator_mat; Ref<SpatialMaterial> inner_mat; @@ -132,9 +141,19 @@ class GridMapEditor : public VBoxContainer { Vector3 current; Vector3 begin; Vector3 end; - int duplicate_rot; bool active; } selection; + Selection last_selection; + + struct PasteIndicator { + + Vector3 click; + Vector3 current; + Vector3 begin; + Vector3 end; + int orientation; + }; + PasteIndicator paste_indicator; bool cursor_visible; Transform cursor_transform; @@ -148,7 +167,6 @@ class GridMapEditor : public VBoxContainer { enum Menu { - MENU_OPTION_CONFIGURE, MENU_OPTION_NEXT_LEVEL, MENU_OPTION_PREV_LEVEL, MENU_OPTION_LOCK_VIEW, @@ -165,13 +183,11 @@ class GridMapEditor : public VBoxContainer { MENU_OPTION_CURSOR_BACK_ROTATE_X, MENU_OPTION_CURSOR_BACK_ROTATE_Z, MENU_OPTION_CURSOR_CLEAR_ROTATION, - MENU_OPTION_DUPLICATE_SELECTS, - MENU_OPTION_SELECTION_MAKE_AREA, - MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR, + MENU_OPTION_PASTE_SELECTS, MENU_OPTION_SELECTION_DUPLICATE, + MENU_OPTION_SELECTION_CUT, MENU_OPTION_SELECTION_CLEAR, MENU_OPTION_SELECTION_FILL, - MENU_OPTION_REMOVE_AREA, MENU_OPTION_GRIDMAP_SETTINGS }; @@ -200,12 +216,16 @@ class GridMapEditor : public VBoxContainer { void _icon_size_changed(float p_value); - void _update_duplicate_indicator(); - void _duplicate_paste(); + void _clear_clipboard_data(); + void _set_clipboard_data(); + void _update_paste_indicator(); + void _do_paste(); void _update_selection_transform(); void _validate_selection(); + 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/jpg/SCsub b/modules/jpg/SCsub index d5f87905eb..96e8e704dd 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -13,7 +13,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_jpg.Append(CPPPATH=[thirdparty_dir]) +env_jpg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_jpg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index dbebaae38d..8876bcbe9d 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index dc7ed03548..fe107d3683 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -440,6 +440,12 @@ void MobileVRInterface::process() { }; }; +void MobileVRInterface::notification(int p_what){ + _THREAD_SAFE_METHOD_ + + // nothing to do here, I guess we could pauze our sensors... +} + MobileVRInterface::MobileVRInterface() { initialized = false; diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index e595daf16e..7fa51eecb7 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -142,6 +142,7 @@ public: virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); + virtual void notification(int p_what); MobileVRInterface(); ~MobileVRInterface(); diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 341d57f3e4..6c3ecee272 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -20,11 +20,6 @@ if env['tools']: 'glue/cs_glue_version.gen.h' ) -vars = Variables() -vars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) -vars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) -vars.Update(env_mono) - # Glue sources if env_mono['mono_glue']: env_mono.Append(CPPDEFINES=['MONO_GLUE_ENABLED']) diff --git a/modules/mono/build_scripts/godotsharptools_build.py b/modules/mono/build_scripts/godotsharptools_build.py index af3a5cb5c6..17f9a990af 100644 --- a/modules/mono/build_scripts/godotsharptools_build.py +++ b/modules/mono/build_scripts/godotsharptools_build.py @@ -1,6 +1,5 @@ # Build GodotSharpTools solution - import os from SCons.Script import Builder, Dir @@ -53,21 +52,9 @@ def find_nuget_windows(env): if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK): return hint_path - from . import mono_reg_utils as monoreg - - mono_root = '' - bits = env['bits'] + from . mono_reg_utils import find_mono_root_dir - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) + mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits']) if mono_root: mono_bin_dir = os.path.join(mono_root, 'bin') @@ -114,21 +101,9 @@ def find_msbuild_unix(filename): def find_msbuild_windows(env): - from . import mono_reg_utils as monoreg + from . mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg - mono_root = '' - bits = env['bits'] - - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - else: - mono_root = monoreg.find_mono_root_dir(bits) + mono_root = env['mono_prefix'] or find_mono_root_dir(env['bits']) if not mono_root: raise RuntimeError('Cannot find mono root directory') @@ -148,7 +123,7 @@ def find_msbuild_windows(env): } return (msbuild_mono, framework_path, mono_msbuild_env) - msbuild_tools_path = monoreg.find_msbuild_tools_path_reg() + msbuild_tools_path = find_msbuild_tools_path_reg() if msbuild_tools_path: return (os.path.join(msbuild_tools_path, 'MSBuild.exe'), framework_path, {}) diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 160580e116..c549640d61 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -1,15 +1,30 @@ import imp import os +import os.path import sys import subprocess from distutils.version import LooseVersion -from SCons.Script import BoolVariable, Dir, Environment, Variables +from SCons.Script import Dir, Environment if os.name == 'nt': from . import mono_reg_utils as monoreg +android_arch_dirs = { + 'armv7': 'armeabi-v7a', + 'arm64v8': 'arm64-v8a', + 'x86': 'x86', + 'x86_64': 'x86_64' +} + + +def get_android_out_dir(env): + return os.path.join(Dir('#platform/android/java/libs').abspath, + 'release' if env['target'] == 'release' else 'debug', + android_arch_dirs[env['android_arch']]) + + def find_file_in_dir(directory, files, prefix='', extension=''): if not extension.startswith('.'): extension = '.' + extension @@ -20,47 +35,47 @@ def find_file_in_dir(directory, files, prefix='', extension=''): def copy_file(src_dir, dst_dir, name): - from shutil import copyfile + from shutil import copy - src_path = os.path.join(src_dir, name) - dst_path = os.path.join(dst_dir, name) + src_path = os.path.join(Dir(src_dir).abspath, name) + dst_dir = Dir(dst_dir).abspath if not os.path.isdir(dst_dir): os.mkdir(dst_dir) - copyfile(src_path, dst_path) + copy(src_path, dst_dir) def configure(env, env_mono): - envvars = Variables() - envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) - envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) - envvars.Update(env) - bits = env['bits'] + is_android = env['platform'] == 'android' tools_enabled = env['tools'] mono_static = env['mono_static'] copy_mono_root = env['copy_mono_root'] + mono_prefix = env['mono_prefix'] + mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] + if is_android and not env['android_arch'] in android_arch_dirs: + raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch']) + + if is_android and tools_enabled: + # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders. + raise RuntimeError('This module does not currently support building for android with tools enabled') + + if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix: + print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead") + if env['platform'] == 'windows': - mono_root = '' + mono_root = mono_prefix - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') - elif os.name == 'nt': - mono_root = monoreg.find_mono_root_dir(bits) + if not mono_root and os.name == 'nt': + mono_root = monoreg.find_mono_root_dir(bits) if not mono_root: - raise RuntimeError('Mono installation directory not found') + raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter") print('Found Mono root directory: ' + mono_root) @@ -70,7 +85,7 @@ def configure(env, env_mono): mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) - env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) if mono_static: lib_suffix = Environment()['LIBSUFFIX'] @@ -113,21 +128,18 @@ def configure(env, env_mono): if not mono_dll_name: raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + copy_file(mono_bin_path, '#bin', mono_dll_name + '.dll') else: is_apple = (sys.platform == 'darwin' or "osxcross" in env) sharedlib_ext = '.dylib' if is_apple else '.so' - mono_root = '' + mono_root = mono_prefix mono_lib_path = '' + mono_so_name = '' - if bits == '32': - if os.getenv('MONO32_PREFIX'): - mono_root = os.getenv('MONO32_PREFIX') - else: - if os.getenv('MONO64_PREFIX'): - mono_root = os.getenv('MONO64_PREFIX') + if not mono_root and is_android: + raise RuntimeError("Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter") if not mono_root and is_apple: # Try with some known directories under OSX @@ -142,7 +154,8 @@ def configure(env, env_mono): if not mono_root and mono_static: mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) if not mono_root: - raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') + raise RuntimeError("Building with mono_static=yes, but failed to find the mono prefix with pkg-config; " + \ + "specify one manually with the 'mono_prefix' SCons parameter") if mono_root: print('Found Mono root directory: ' + mono_root) @@ -153,7 +166,7 @@ def configure(env, env_mono): mono_lib_path = os.path.join(mono_root, 'lib') env.Append(LIBPATH=mono_lib_path) - env_mono.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) + env_mono.Prepend(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) mono_lib = find_file_in_dir(mono_lib_path, mono_lib_names, prefix='lib', extension='.a') @@ -174,6 +187,8 @@ def configure(env, env_mono): if is_apple: env.Append(LIBS=['iconv', 'pthread']) + elif is_android: + env.Append(LIBS=['m', 'dl']) else: env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) @@ -183,7 +198,7 @@ def configure(env, env_mono): if not mono_so_name: raise RuntimeError('Could not find mono shared library in: ' + mono_lib_path) - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + copy_file(mono_lib_path, '#bin', 'lib' + mono_so_name + sharedlib_ext) else: assert not mono_static @@ -196,9 +211,6 @@ def configure(env, env_mono): env.ParseConfig('pkg-config monosgen-2 --libs') env_mono.ParseConfig('pkg-config monosgen-2 --cflags') - mono_lib_path = '' - mono_so_name = '' - tmpenv = Environment() tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L') @@ -213,11 +225,13 @@ def configure(env, env_mono): if not mono_so_name: raise RuntimeError('Could not find mono shared library in: ' + str(tmpenv['LIBPATH'])) - copy_file(mono_lib_path, 'bin', 'lib' + mono_so_name + sharedlib_ext) + if not mono_static: + libs_output_dir = get_android_out_dir(env) if is_android else '#bin' + copy_file(mono_lib_path, libs_output_dir, 'lib' + mono_so_name + sharedlib_ext) env.Append(LINKFLAGS='-rdynamic') - if not tools_enabled: + if not tools_enabled and not is_android: if not mono_root: mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() @@ -241,7 +255,7 @@ def make_template_dir(env, mono_root): template_dir_name = '' - if platform in ['windows', 'osx', 'x11']: + if platform in ['windows', 'osx', 'x11', 'android']: template_dir_name = 'data.mono.%s.%s.%s' % (platform, env['bits'], target) else: assert False @@ -256,12 +270,13 @@ def make_template_dir(env, mono_root): # Copy etc/mono/ - template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + if platform != 'android': + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) # Copy the required shared libraries - copy_mono_shared_libs(mono_root, template_mono_root_dir, env['platform']) + copy_mono_shared_libs(env, mono_root, template_mono_root_dir) def copy_mono_root_files(env, mono_root): @@ -285,7 +300,7 @@ def copy_mono_root_files(env, mono_root): # Copy the required shared libraries - copy_mono_shared_libs(mono_root, editor_mono_root_dir, env['platform']) + copy_mono_shared_libs(env, mono_root, editor_mono_root_dir) # Copy framework assemblies @@ -332,39 +347,55 @@ def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform): copy_tree(os.path.join(mono_etc_dir, '2.0'), os.path.join(target_mono_config_dir, '2.0')) copy_tree(os.path.join(mono_etc_dir, '4.0'), os.path.join(target_mono_config_dir, '4.0')) copy_tree(os.path.join(mono_etc_dir, '4.5'), os.path.join(target_mono_config_dir, '4.5')) - copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) + if os.path.isdir(os.path.join(mono_etc_dir, 'mconfig')): + copy_tree(os.path.join(mono_etc_dir, 'mconfig'), os.path.join(target_mono_config_dir, 'mconfig')) for file in glob(os.path.join(mono_etc_dir, '*')): if os.path.isfile(file): copy(file, target_mono_config_dir) -def copy_mono_shared_libs(mono_root, target_mono_root_dir, platform): +def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): from shutil import copy + def copy_if_exists(src, dst): + if os.path.isfile(src): + copy(src, dst) + + platform = env['platform'] + if platform == 'windows': target_mono_bin_dir = os.path.join(target_mono_root_dir, 'bin') if not os.path.isdir(target_mono_bin_dir): os.makedirs(target_mono_bin_dir) - copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), os.path.join(target_mono_bin_dir, 'MonoPosixHelper.dll')) + copy(os.path.join(mono_root, 'bin', 'MonoPosixHelper.dll'), target_mono_bin_dir) else: - target_mono_lib_dir = os.path.join(target_mono_root_dir, 'lib') + target_mono_lib_dir = get_android_out_dir(env) if platform == 'android' else os.path.join(target_mono_root_dir, 'lib') if not os.path.isdir(target_mono_lib_dir): os.makedirs(target_mono_lib_dir) if platform == 'osx': - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.dylib')) - elif platform == 'x11': - copy(os.path.join(mono_root, 'lib', 'libmono-btls-shared.so'), os.path.join(target_mono_lib_dir, 'libmono-btls-shared.so')) - copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.so'), os.path.join(target_mono_lib_dir, 'libMonoPosixHelper.so')) + # TODO: Make sure nothing is missing + copy(os.path.join(mono_root, 'lib', 'libMonoPosixHelper.dylib'), target_mono_lib_dir) + elif platform == 'x11' or platform == 'android': + lib_file_names = [lib_name + '.so' for lib_name in [ + 'libmono-btls-shared', 'libmono-ee-interp', 'libmono-native', 'libMonoPosixHelper', + 'libmono-profiler-aot', 'libmono-profiler-coverage', 'libmono-profiler-log', 'libMonoSupportW' + ]] + + for lib_file_name in lib_file_names: + copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir) def configure_for_mono_version(env, mono_version): if mono_version is None: - raise RuntimeError('Mono JIT compiler version not found') + if os.getenv('MONO_VERSION'): + mono_version = os.getenv('MONO_VERSION') + else: + raise RuntimeError("Mono JIT compiler version not found; specify one manually with the 'MONO_VERSION' environment variable") print('Found Mono JIT compiler version: ' + str(mono_version)) if mono_version >= LooseVersion('5.12.0'): env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) diff --git a/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff new file mode 100644 index 0000000000..05f8dcadcc --- /dev/null +++ b/modules/mono/build_scripts/patches/fix-mono-android-tkill.diff @@ -0,0 +1,70 @@ +diff --git a/libgc/include/private/gcconfig.h b/libgc/include/private/gcconfig.h +index e2bdf13ac3e..f962200ba4e 100644 +--- a/libgc/include/private/gcconfig.h ++++ b/libgc/include/private/gcconfig.h +@@ -2255,6 +2255,14 @@ + # define GETPAGESIZE() getpagesize() + # endif + ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID ++#endif ++ + # if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. Probably others also qualify. */ + # define SVR4 +diff --git a/libgc/pthread_stop_world.c b/libgc/pthread_stop_world.c +index f93ce26b562..4a49a6d578c 100644 +--- a/libgc/pthread_stop_world.c ++++ b/libgc/pthread_stop_world.c +@@ -336,7 +336,7 @@ void GC_push_all_stacks() + pthread_t GC_stopping_thread; + int GC_stopping_pid; + +-#ifdef HOST_ANDROID ++#ifdef USE_TKILL_ON_ANDROID + static + int android_thread_kill(pid_t tid, int sig) + { +diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c +index ad9b8823f8f..3542b32b540 100644 +--- a/mono/metadata/threads.c ++++ b/mono/metadata/threads.c +@@ -77,8 +77,12 @@ mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); + #include <zircon/syscalls.h> + #endif + +-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) +-#define USE_TKILL_ON_ANDROID 1 ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID + #endif + + #ifdef HOST_ANDROID +diff --git a/mono/utils/mono-threads-posix.c b/mono/utils/mono-threads-posix.c +index 3e4bf93de5f..79c9f731fe7 100644 +--- a/mono/utils/mono-threads-posix.c ++++ b/mono/utils/mono-threads-posix.c +@@ -31,8 +31,12 @@ + + #include <errno.h> + +-#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64) +-#define USE_TKILL_ON_ANDROID 1 ++#if defined(HOST_ANDROID) && !(__ANDROID_API__ >= 23) \ ++ && ((defined(MIPS) && (CPP_WORDSZ == 32)) \ ++ || defined(ARM32) || defined(I386) /* but not x32 */) ++ /* tkill() exists only on arm32/mips(32)/x86. */ ++ /* NDK r11+ deprecates tkill() but keeps it for Mono clients. */ ++# define USE_TKILL_ON_ANDROID + #endif + + #ifdef USE_TKILL_ON_ANDROID diff --git a/modules/mono/config.py b/modules/mono/config.py index 3b2e96765e..9adf4ee6e5 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -8,6 +8,16 @@ def configure(env): env.use_ptrcall = True env.add_module_version_string('mono') + from SCons.Script import BoolVariable, PathVariable, Variables + + envvars = Variables() + envvars.Add(PathVariable('mono_prefix', 'Path to the mono installation directory for the target platform and architecture', '', PathVariable.PathAccept)) + envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) + envvars.Add(BoolVariable('mono_glue', 'Build with the mono glue sources', True)) + envvars.Add(BoolVariable('copy_mono_root', 'Make a copy of the mono installation directory to bundle with the editor', False)) + envvars.Add(BoolVariable('xbuild_fallback', 'If MSBuild is not found, fallback to xbuild', False)) + envvars.Update(env) + def get_doc_classes(): return [ diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 3c9644127c..72199281ff 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -131,14 +131,6 @@ void CSharpLanguage::finish() { finalizing = true; -#ifdef TOOLS_ENABLED - // Must be here, to avoid StringName leaks - if (BindingsGenerator::singleton) { - memdelete(BindingsGenerator::singleton); - BindingsGenerator::singleton = NULL; - } -#endif - // Make sure all script binding gchandles are released before finalizing GDMono for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { CSharpScriptBinding &script_binding = E->value(); @@ -919,7 +911,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { } #endif -void CSharpLanguage::project_assembly_loaded() { +void CSharpLanguage::_load_scripts_metadata() { scripts_metadata.clear(); @@ -953,6 +945,7 @@ void CSharpLanguage::project_assembly_loaded() { } scripts_metadata = old_dict_var.operator Dictionary(); + scripts_metadata_invalidated = false; print_verbose("Successfully loaded scripts metadata"); } else { @@ -1024,11 +1017,13 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } -void CSharpLanguage::_uninitialize_script_bindings() { +void CSharpLanguage::_on_scripts_domain_unloaded() { for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) { CSharpScriptBinding &script_binding = E->value(); script_binding.inited = false; } + + scripts_metadata_invalidated = true; } void CSharpLanguage::set_language_index(int p_idx) { @@ -1086,6 +1081,8 @@ CSharpLanguage::CSharpLanguage() { #endif lang_idx = -1; + + scripts_metadata_invalidated = true; } CSharpLanguage::~CSharpLanguage() { @@ -1858,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; @@ -2023,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) { @@ -2044,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) { @@ -2171,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; @@ -2194,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; } } @@ -2217,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; @@ -2239,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: @@ -2254,53 +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) { - hint = PROPERTY_HINT_TYPE_STRING; - hint_string = itos(export_info.array.element_type) + ":"; - } 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 050527d52b..4a1fb8e5ed 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(); @@ -309,14 +312,17 @@ class CSharpLanguage : public ScriptLanguage { int lang_idx; Dictionary scripts_metadata; + bool scripts_metadata_invalidated; // For debug_break and debug_break_parse int _debug_parse_err_line; String _debug_parse_err_file; String _debug_error; + void _load_scripts_metadata(); + friend class GDMono; - void _uninitialize_script_bindings(); + void _on_scripts_domain_unloaded(); public: StringNameCache string_names; @@ -341,9 +347,15 @@ public: void reload_assemblies(bool p_soft_reload); #endif - void project_assembly_loaded(); + _FORCE_INLINE_ Dictionary get_scripts_metadata_or_nothing() { + return scripts_metadata_invalidated ? Dictionary() : scripts_metadata; + } - _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { return scripts_metadata; } + _FORCE_INLINE_ const Dictionary &get_scripts_metadata() { + if (scripts_metadata_invalidated) + _load_scripts_metadata(); + return scripts_metadata; + } virtual String get_name() const; @@ -368,7 +380,6 @@ public: virtual bool supports_builtin_mode() const; /* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; } virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - /* TODO? */ Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint) { return ERR_UNAVAILABLE; } virtual String _get_indentation() const; /* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {} /* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {} diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml index a821713d0d..826c106d7e 100644 --- a/modules/mono/doc_classes/@C#.xml +++ b/modules/mono/doc_classes/@C#.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 7f22388132..de2e246ea9 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="new" qualifiers="vararg"> <return type="Object"> diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 21835e639c..18556a84ba 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="attach_thread"> <return type="void"> 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/Editor/GodotSharpExport.cs b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs index e45dd2025b..44a43f0ddd 100644 --- a/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs +++ b/modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; namespace GodotSharpTools.Editor @@ -62,7 +63,7 @@ namespace GodotSharpTools.Editor { // OSX export templates are contained in a zip, so we place // our custom template inside it and let Godot do the rest. - return !featureSet.Contains("OSX"); + return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f)); } [MethodImpl(MethodImplOptions.InternalCall)] 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 fe7ced060d..cd7774e7a1 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -38,7 +38,6 @@ #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/string_builder.h" #include "core/ucaps.h" #include "../glue/cs_compressed.gen.h" @@ -98,14 +97,10 @@ #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define BINDINGS_GENERATOR_VERSION UINT32_C(8) +#define BINDINGS_GENERATOR_VERSION UINT32_C(9) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n"); -bool BindingsGenerator::verbose_output = false; - -BindingsGenerator *BindingsGenerator::singleton = NULL; - static String fix_doc_description(const String &p_bbcode) { // This seems to be the correct way to do this. It's the same EditorHelp does. @@ -757,47 +752,47 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { } } -void BindingsGenerator::_generate_global_constants(List<String> &p_output) { +void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) { // Constants (in partial GD class) - p_output.push_back("\n#pragma warning disable CS1591 // Disable warning: " - "'Missing XML comment for publicly visible type or member'\n"); + p_output.append("\n#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n"); - p_output.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - p_output.push_back(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{"); + p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{"); for (const List<ConstantInterface>::Element *E = global_constants.front(); E; E = E->next()) { const ConstantInterface &iconstant = E->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + p_output.append(MEMBER_BEGIN "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(summary_lines[i]); - p_output.push_back("\n"); + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - p_output.push_back(INDENT2 "/// </summary>"); + p_output.append(INDENT2 "/// </summary>"); } } - p_output.push_back(MEMBER_BEGIN "public const int "); - p_output.push_back(iconstant.proxy_name); - p_output.push_back(" = "); - p_output.push_back(itos(iconstant.value)); - p_output.push_back(";"); + p_output.append(MEMBER_BEGIN "public const int "); + p_output.append(iconstant.proxy_name); + p_output.append(" = "); + p_output.append(itos(iconstant.value)); + p_output.append(";"); } if (!global_constants.empty()) - p_output.push_back("\n"); + p_output.append("\n"); - p_output.push_back(INDENT1 CLOSE_BLOCK); // end of GD class + p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class // Enums @@ -817,60 +812,56 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) { CRASH_COND(enum_class_name != "Variant"); // Hard-coded... - if (verbose_output) { - WARN_PRINTS("Declaring global enum `" + enum_proxy_name + "` inside static class `" + enum_class_name + "`"); - } + _log("Declaring global enum `%s` inside static class `%s`\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data()); - p_output.push_back("\n" INDENT1 "public static partial class "); - p_output.push_back(enum_class_name); - p_output.push_back("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" INDENT1 "public static partial class "); + p_output.append(enum_class_name); + p_output.append("\n" INDENT1 OPEN_BLOCK); } - p_output.push_back("\n" INDENT1 "public enum "); - p_output.push_back(enum_proxy_name); - p_output.push_back("\n" INDENT1 OPEN_BLOCK); + p_output.append("\n" INDENT1 "public enum "); + p_output.append(enum_proxy_name); + p_output.append("\n" INDENT1 OPEN_BLOCK); for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) { const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - p_output.push_back(INDENT2 "/// <summary>\n"); + p_output.append(INDENT2 "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(summary_lines[i]); - p_output.push_back("\n"); + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - p_output.push_back(INDENT2 "/// </summary>\n"); + p_output.append(INDENT2 "/// </summary>\n"); } } - p_output.push_back(INDENT2); - p_output.push_back(iconstant.proxy_name); - p_output.push_back(" = "); - p_output.push_back(itos(iconstant.value)); - p_output.push_back(F != ienum.constants.back() ? ",\n" : "\n"); + p_output.append(INDENT2); + p_output.append(iconstant.proxy_name); + p_output.append(" = "); + p_output.append(itos(iconstant.value)); + p_output.append(F != ienum.constants.back() ? ",\n" : "\n"); } - p_output.push_back(INDENT1 CLOSE_BLOCK); + p_output.append(INDENT1 CLOSE_BLOCK); if (enum_in_static_class) - p_output.push_back(INDENT1 CLOSE_BLOCK); + p_output.append(INDENT1 CLOSE_BLOCK); } - p_output.push_back(CLOSE_BLOCK); // end of namespace + p_output.append(CLOSE_BLOCK); // end of namespace - p_output.push_back("\n#pragma warning restore CS1591\n"); + p_output.append("\n#pragma warning restore CS1591\n"); } -Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { - - verbose_output = p_verbose_output; +Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution) { String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME); @@ -893,7 +884,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, // Generate source file for global scope constants and enums { - List<String> constants_source; + StringBuilder constants_source; _generate_global_constants(constants_source); String output_file = path_join(core_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs"); Error save_err = _save_file(output_file, constants_source); @@ -951,28 +942,28 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, compile_items.push_back(output_file); } - List<String> cs_icalls_content; - - cs_icalls_content.push_back("using System;\n" - "using System.Runtime.CompilerServices;\n" - "\n"); - cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); - - cs_icalls_content.push_back(MEMBER_BEGIN "internal static ulong godot_api_hash = "); - cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); - cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint bindings_version = "); - cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.push_back(MEMBER_BEGIN "internal static uint cs_glue_version = "); - cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); - -#define ADD_INTERNAL_CALL(m_icall) \ - if (!m_icall.editor_only) { \ - cs_icalls_content.push_back(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.push_back(INDENT2 "internal extern static "); \ - cs_icalls_content.push_back(m_icall.im_type_out + " "); \ - cs_icalls_content.push_back(m_icall.name + "("); \ - cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + StringBuilder cs_icalls_content; + + cs_icalls_content.append("using System;\n" + "using System.Runtime.CompilerServices;\n" + "\n"); + cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = "); + cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n"); + cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = "); + cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = "); + cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (!m_icall.editor_only) { \ + cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(m_icall.im_type_out + " "); \ + cs_icalls_content.append(m_icall.name + "("); \ + cs_icalls_content.append(m_icall.im_sig + ");\n"); \ } for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) @@ -982,7 +973,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, #undef ADD_INTERNAL_CALL - cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS ".cs"); @@ -1002,15 +993,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info); - if (verbose_output) - OS::get_singleton()->print("The solution and C# project for the Core API was generated successfully\n"); + _log("The solution and C# project for the Core API was generated successfully\n"); return OK; } -Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { - - verbose_output = p_verbose_output; +Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution) { String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME); @@ -1049,29 +1037,29 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir compile_items.push_back(output_file); } - List<String> cs_icalls_content; - - cs_icalls_content.push_back("using System;\n" - "using System.Runtime.CompilerServices;\n" - "\n"); - cs_icalls_content.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); - cs_icalls_content.push_back(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); - - cs_icalls_content.push_back(INDENT2 "internal static ulong godot_api_hash = "); - cs_icalls_content.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint bindings_version = "); - cs_icalls_content.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); - cs_icalls_content.push_back(INDENT2 "internal static uint cs_glue_version = "); - cs_icalls_content.push_back(String::num_uint64(CS_GLUE_VERSION) + ";\n"); - cs_icalls_content.push_back("\n"); - -#define ADD_INTERNAL_CALL(m_icall) \ - if (m_icall.editor_only) { \ - cs_icalls_content.push_back(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ - cs_icalls_content.push_back(INDENT2 "internal extern static "); \ - cs_icalls_content.push_back(m_icall.im_type_out + " "); \ - cs_icalls_content.push_back(m_icall.name + "("); \ - cs_icalls_content.push_back(m_icall.im_sig + ");\n"); \ + StringBuilder cs_icalls_content; + + cs_icalls_content.append("using System;\n" + "using System.Runtime.CompilerServices;\n" + "\n"); + cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK); + + cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = "); + cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n"); + cs_icalls_content.append(INDENT2 "internal static uint bindings_version = "); + cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n"); + cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = "); + cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n"); + cs_icalls_content.append("\n"); + +#define ADD_INTERNAL_CALL(m_icall) \ + if (m_icall.editor_only) { \ + cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \ + cs_icalls_content.append(INDENT2 "internal extern static "); \ + cs_icalls_content.append(m_icall.im_type_out + " "); \ + cs_icalls_content.append(m_icall.name + "("); \ + cs_icalls_content.append(m_icall.im_sig + ");\n"); \ } for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) @@ -1081,7 +1069,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir #undef ADD_INTERNAL_CALL - cs_icalls_content.push_back(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); + cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK); String internal_methods_file = path_join(core_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs"); @@ -1101,13 +1089,12 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info); - if (verbose_output) - OS::get_singleton()->print("The solution and C# project for the Editor API was generated successfully\n"); + _log("The solution and C# project for the Editor API was generated successfully\n"); return OK; } -Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verbose_output) { +Error BindingsGenerator::generate_cs_api(const String &p_output_dir) { DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); @@ -1124,13 +1111,13 @@ Error BindingsGenerator::generate_cs_api(const String &p_output_dir, bool p_verb Error proj_err; - proj_err = generate_cs_core_project(p_output_dir, solution, p_verbose_output); + proj_err = generate_cs_core_project(p_output_dir, solution); if (proj_err != OK) { ERR_PRINT("Generation of the Core API C# project failed"); return proj_err; } - proj_err = generate_cs_editor_project(p_output_dir, solution, p_verbose_output); + proj_err = generate_cs_editor_project(p_output_dir, solution); if (proj_err != OK) { ERR_PRINT("Generation of the Editor API C# project failed"); return proj_err; @@ -1169,65 +1156,64 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls; - if (verbose_output) - OS::get_singleton()->print("Generating %s.cs...\n", itype.proxy_name.utf8().get_data()); + _log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data()); String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types - List<String> output; + StringBuilder output; - output.push_back("using System;\n"); // IntPtr - output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable + output.append("using System;\n"); // IntPtr + output.append("using System.Diagnostics;\n"); // DebuggerBrowsable - output.push_back("\n" - "#pragma warning disable CS1591 // Disable warning: " - "'Missing XML comment for publicly visible type or member'\n" - "#pragma warning disable CS1573 // Disable warning: " - "'Parameter has no matching param tag in the XML comment'\n"); + output.append("\n" + "#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n" + "#pragma warning disable CS1573 // Disable warning: " + "'Parameter has no matching param tag in the XML comment'\n"); - output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); + output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); const DocData::ClassDoc *class_doc = itype.class_doc; if (class_doc && class_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - output.push_back(INDENT1 "/// <summary>\n"); + output.append(INDENT1 "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - output.push_back(INDENT1 "/// "); - output.push_back(summary_lines[i]); - output.push_back("\n"); + output.append(INDENT1 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - output.push_back(INDENT1 "/// </summary>\n"); + output.append(INDENT1 "/// </summary>\n"); } } - output.push_back(INDENT1 "public "); + output.append(INDENT1 "public "); if (itype.is_singleton) { - output.push_back("static partial class "); + output.append("static partial class "); } else { - output.push_back(itype.is_instantiable ? "partial class " : "abstract partial class "); + output.append(itype.is_instantiable ? "partial class " : "abstract partial class "); } - output.push_back(itype.proxy_name); + output.append(itype.proxy_name); if (itype.is_singleton) { - output.push_back("\n"); + output.append("\n"); } else if (is_derived_type) { if (obj_types.has(itype.base_name)) { - output.push_back(" : "); - output.push_back(obj_types[itype.base_name].proxy_name); - output.push_back("\n"); + output.append(" : "); + output.append(obj_types[itype.base_name].proxy_name); + output.append("\n"); } else { ERR_PRINTS("Base type '" + itype.base_name.operator String() + "' does not exist, for class " + itype.name); return ERR_INVALID_DATA; } } - output.push_back(INDENT1 "{"); + output.append(INDENT1 "{"); if (class_doc) { @@ -1238,30 +1224,30 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (iconstant.const_doc && iconstant.const_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - output.push_back(MEMBER_BEGIN "/// <summary>\n"); + output.append(MEMBER_BEGIN "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - output.push_back(INDENT2 "/// "); - output.push_back(summary_lines[i]); - output.push_back("\n"); + output.append(INDENT2 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - output.push_back(INDENT2 "/// </summary>"); + output.append(INDENT2 "/// </summary>"); } } - output.push_back(MEMBER_BEGIN "public const int "); - output.push_back(iconstant.proxy_name); - output.push_back(" = "); - output.push_back(itos(iconstant.value)); - output.push_back(";"); + output.append(MEMBER_BEGIN "public const int "); + output.append(iconstant.proxy_name); + output.append(" = "); + output.append(itos(iconstant.value)); + output.append(";"); } if (itype.constants.size()) - output.push_back("\n"); + output.append("\n"); // Add enums @@ -1270,38 +1256,38 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG); - output.push_back(MEMBER_BEGIN "public enum "); - output.push_back(ienum.cname.operator String()); - output.push_back(MEMBER_BEGIN OPEN_BLOCK); + output.append(MEMBER_BEGIN "public enum "); + output.append(ienum.cname.operator String()); + output.append(MEMBER_BEGIN OPEN_BLOCK); for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) { const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - output.push_back(INDENT3 "/// <summary>\n"); + output.append(INDENT3 "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - output.push_back(INDENT3 "/// "); - output.push_back(summary_lines[i]); - output.push_back("\n"); + output.append(INDENT3 "/// "); + output.append(summary_lines[i]); + output.append("\n"); } - output.push_back(INDENT3 "/// </summary>\n"); + output.append(INDENT3 "/// </summary>\n"); } } - output.push_back(INDENT3); - output.push_back(iconstant.proxy_name); - output.push_back(" = "); - output.push_back(itos(iconstant.value)); - output.push_back(F != ienum.constants.back() ? ",\n" : "\n"); + output.append(INDENT3); + output.append(iconstant.proxy_name); + output.append(" = "); + output.append(itos(iconstant.value)); + output.append(F != ienum.constants.back() ? ",\n" : "\n"); } - output.push_back(INDENT2 CLOSE_BLOCK); + output.append(INDENT2 CLOSE_BLOCK); } // Add properties @@ -1322,53 +1308,53 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str if (itype.is_singleton) { // Add the type name and the singleton pointer as static fields - output.push_back(MEMBER_BEGIN "private static Godot.Object singleton;\n"); - output.push_back(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3 - "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5 - "singleton = Engine.GetSingleton(" BINDINGS_NATIVE_NAME_FIELD ");\n" INDENT4 - "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); - - output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.push_back(itype.name); - output.push_back("\";\n"); - - output.push_back(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); - output.push_back("." ICALL_PREFIX); - output.push_back(itype.name); - output.push_back(SINGLETON_ICALL_SUFFIX "();\n"); + output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n"); + output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3 + "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5 + "singleton = Engine.GetSingleton(" BINDINGS_NATIVE_NAME_FIELD ");\n" INDENT4 + "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n"); + + output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); + + output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = "); + output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); + output.append("." ICALL_PREFIX); + output.append(itype.name); + output.append(SINGLETON_ICALL_SUFFIX "();\n"); } else if (is_derived_type) { // Add member fields - output.push_back(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.push_back(itype.name); - output.push_back("\";\n"); + output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); // Add default constructor if (itype.is_instantiable) { - output.push_back(MEMBER_BEGIN "public "); - output.push_back(itype.proxy_name); - output.push_back("() : this("); - output.push_back(itype.memory_own ? "true" : "false"); + output.append(MEMBER_BEGIN "public "); + output.append(itype.proxy_name); + output.append("() : this("); + output.append(itype.memory_own ? "true" : "false"); // The default constructor may also be called by the engine when instancing existing native objects // The engine will initialize the pointer field of the managed side before calling the constructor // This is why we only allocate a new native object from the constructor if the pointer field is not set - output.push_back(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); - output.push_back(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); - output.push_back("." + ctor_method); - output.push_back("(this);\n" CLOSE_BLOCK_L2); + output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = "); + output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); + output.append("." + ctor_method); + output.append("(this);\n" CLOSE_BLOCK_L2); } else { // Hide the constructor - output.push_back(MEMBER_BEGIN "internal "); - output.push_back(itype.proxy_name); - output.push_back("() {}\n"); + output.append(MEMBER_BEGIN "internal "); + output.append(itype.proxy_name); + output.append("() {}\n"); } // Add.. em.. trick constructor. Sort of. - output.push_back(MEMBER_BEGIN "internal "); - output.push_back(itype.proxy_name); - output.push_back("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); + output.append(MEMBER_BEGIN "internal "); + output.append(itype.proxy_name); + output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n"); } int method_bind_count = 0; @@ -1395,17 +1381,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str custom_icalls.push_back(ctor_icall); } - output.push_back(INDENT1 CLOSE_BLOCK /* class */ + output.append(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); - output.push_back("\n" - "#pragma warning restore CS1591\n" - "#pragma warning restore CS1573\n"); + output.append("\n" + "#pragma warning restore CS1591\n" + "#pragma warning restore CS1573\n"); return _save_file(p_output_file, output); } -Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output) { +Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output) { const MethodInterface *setter = p_itype.find_method_by_name(p_iprop.setter); @@ -1452,72 +1438,94 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); if (summary_lines.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + p_output.append(MEMBER_BEGIN "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(summary_lines[i]); - p_output.push_back("\n"); + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - p_output.push_back(INDENT2 "/// </summary>"); + p_output.append(INDENT2 "/// </summary>"); } } - p_output.push_back(MEMBER_BEGIN "public "); + p_output.append(MEMBER_BEGIN "public "); if (p_itype.is_singleton) - p_output.push_back("static "); + p_output.append("static "); - p_output.push_back(prop_itype->cs_type); - p_output.push_back(" "); - p_output.push_back(p_iprop.proxy_name); - p_output.push_back("\n" INDENT2 OPEN_BLOCK); + p_output.append(prop_itype->cs_type); + p_output.append(" "); + p_output.append(p_iprop.proxy_name); + p_output.append("\n" INDENT2 OPEN_BLOCK); if (getter) { - p_output.push_back(INDENT3 "get\n" OPEN_BLOCK_L3); - p_output.push_back("return "); - p_output.push_back(getter->proxy_name + "("); + p_output.append(INDENT3 "get\n" + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning disable CS0618 // Disable warning about obsolete method\n" + + OPEN_BLOCK_L3); + + p_output.append("return "); + p_output.append(getter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = getter->arguments.front()->get(); if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); - p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); + p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); } else { - p_output.push_back(itos(p_iprop.index)); + p_output.append(itos(p_iprop.index)); } } - p_output.push_back(");\n" CLOSE_BLOCK_L3); + p_output.append(");\n" + + CLOSE_BLOCK_L3 + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning restore CS0618\n"); } if (setter) { - p_output.push_back(INDENT3 "set\n" OPEN_BLOCK_L3); - p_output.push_back(setter->proxy_name + "("); + p_output.append(INDENT3 "set\n" + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning disable CS0618 // Disable warning about obsolete method\n" + + OPEN_BLOCK_L3); + + p_output.append(setter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = setter->arguments.front()->get(); if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); - p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); + p_output.append("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); } else { - p_output.push_back(itos(p_iprop.index) + ", "); + p_output.append(itos(p_iprop.index) + ", "); } } - p_output.push_back("value);\n" CLOSE_BLOCK_L3); + p_output.append("value);\n" + + CLOSE_BLOCK_L3 + + // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that) + "#pragma warning restore CS0618\n"); } - p_output.push_back(CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); return OK; } -Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { +Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) { const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); @@ -1529,7 +1537,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf String icall_params = method_bind_field + ", "; icall_params += sformat(p_itype.cs_in, "this"); - List<String> default_args_doc; + StringBuilder default_args_doc; // Retrieve information from the arguments for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { @@ -1598,7 +1606,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Apparently the name attribute must not include the @ String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name; - default_args_doc.push_back(INDENT2 "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>\n"); + default_args_doc.append(INDENT2 "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>\n"); } else { icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } @@ -1607,61 +1615,67 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Generate method { if (!p_imethod.is_virtual && !p_imethod.requires_object_call) { - p_output.push_back(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); - p_output.push_back(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); - p_output.push_back(p_imethod.name); - p_output.push_back("\");\n"); + p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static IntPtr "); + p_output.append(method_bind_field + " = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \""); + p_output.append(p_imethod.name); + p_output.append("\");\n"); } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype); - Vector<String> summary_lines = xml_summary.split("\n"); + Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); - if (summary_lines.size() || default_args_doc.size()) { - p_output.push_back(MEMBER_BEGIN "/// <summary>\n"); + if (summary_lines.size() || default_args_doc.get_string_length()) { + p_output.append(MEMBER_BEGIN "/// <summary>\n"); for (int i = 0; i < summary_lines.size(); i++) { - p_output.push_back(INDENT2 "/// "); - p_output.push_back(summary_lines[i]); - p_output.push_back("\n"); + p_output.append(INDENT2 "/// "); + p_output.append(summary_lines[i]); + p_output.append("\n"); } - for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) { - p_output.push_back(E->get()); - } - - p_output.push_back(INDENT2 "/// </summary>"); + p_output.append(default_args_doc.as_string()); + p_output.append(INDENT2 "/// </summary>"); } } if (!p_imethod.is_internal) { - p_output.push_back(MEMBER_BEGIN "[GodotMethod(\""); - p_output.push_back(p_imethod.name); - p_output.push_back("\")]"); + p_output.append(MEMBER_BEGIN "[GodotMethod(\""); + p_output.append(p_imethod.name); + p_output.append("\")]"); } - p_output.push_back(MEMBER_BEGIN); - p_output.push_back(p_imethod.is_internal ? "internal " : "public "); + if (p_imethod.is_deprecated) { + if (p_imethod.deprecation_message.empty()) + WARN_PRINTS("An empty deprecation message is discouraged. Method: " + p_imethod.proxy_name); + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_imethod.deprecation_message); + p_output.append("\")]"); + } + + p_output.append(MEMBER_BEGIN); + p_output.append(p_imethod.is_internal ? "internal " : "public "); if (p_itype.is_singleton) { - p_output.push_back("static "); + p_output.append("static "); } else if (p_imethod.is_virtual) { - p_output.push_back("virtual "); + p_output.append("virtual "); } - p_output.push_back(return_type->cs_type + " "); - p_output.push_back(p_imethod.proxy_name + "("); - p_output.push_back(arguments_sig + ")\n" OPEN_BLOCK_L2); + p_output.append(return_type->cs_type + " "); + p_output.append(p_imethod.proxy_name + "("); + p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2); if (p_imethod.is_virtual) { // Godot virtual method must be overridden, therefore we return a default value by default. if (return_type->cname == name_cache.type_void) { - p_output.push_back("return;\n" CLOSE_BLOCK_L2); + p_output.append("return;\n" CLOSE_BLOCK_L2); } else { - p_output.push_back("return default("); - p_output.push_back(return_type->cs_type); - p_output.push_back(");\n" CLOSE_BLOCK_L2); + p_output.append("return default("); + p_output.append(return_type->cs_type); + p_output.append(");\n" CLOSE_BLOCK_L2); } return OK; // Won't increment method bind count @@ -1670,16 +1684,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf if (p_imethod.requires_object_call) { // Fallback to Godot's object.Call(string, params) - p_output.push_back(CS_METHOD_CALL "(\""); - p_output.push_back(p_imethod.name); - p_output.push_back("\""); + p_output.append(CS_METHOD_CALL "(\""); + p_output.append(p_imethod.name); + p_output.append("\""); for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { - p_output.push_back(", "); - p_output.push_back(F->get().name); + p_output.append(", "); + p_output.append(F->get().name); } - p_output.push_back(");\n" CLOSE_BLOCK_L2); + p_output.append(");\n" CLOSE_BLOCK_L2); return OK; // Won't increment method bind count } @@ -1693,37 +1707,36 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf im_call += "." + im_icall->name + "(" + icall_params + ")"; if (p_imethod.arguments.size()) - p_output.push_back(cs_in_statements); + p_output.append(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.push_back(im_call + ";\n"); + p_output.append(im_call + ";\n"); } else if (return_type->cs_out.empty()) { - p_output.push_back("return " + im_call + ";\n"); + p_output.append("return " + im_call + ";\n"); } else { - p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); - p_output.push_back("\n"); + p_output.append(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.append("\n"); } - p_output.push_back(CLOSE_BLOCK_L2); + p_output.append(CLOSE_BLOCK_L2); } p_method_bind_count++; + return OK; } Error BindingsGenerator::generate_glue(const String &p_output_dir) { - verbose_output = true; - bool dir_exists = DirAccess::exists(p_output_dir); ERR_EXPLAIN("The output directory does not exist."); ERR_FAIL_COND_V(!dir_exists, ERR_FILE_BAD_PATH); - List<String> output; + StringBuilder output; - output.push_back("/* THIS FILE IS GENERATED DO NOT EDIT */\n"); - output.push_back("#include \"" GLUE_HEADER_FILE "\"\n"); - output.push_back("\n#ifdef MONO_GLUE_ENABLED\n"); + output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n"); + output.append("#include \"" GLUE_HEADER_FILE "\"\n"); + output.append("\n#ifdef MONO_GLUE_ENABLED\n"); generated_icall_funcs.clear(); @@ -1763,11 +1776,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (!find_icall_by_name(singleton_icall.name, custom_icalls)) custom_icalls.push_back(singleton_icall); - output.push_back("Object* "); - output.push_back(singleton_icall_name); - output.push_back("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\""); - output.push_back(itype.proxy_name); - output.push_back("\");\n" CLOSE_BLOCK "\n"); + output.append("Object* "); + output.append(singleton_icall_name); + output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\""); + output.append(itype.proxy_name); + output.append("\");\n" CLOSE_BLOCK "\n"); } if (is_derived_type && itype.is_instantiable) { @@ -1776,43 +1789,43 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (!find_icall_by_name(ctor_icall.name, custom_icalls)) custom_icalls.push_back(ctor_icall); - output.push_back("Object* "); - output.push_back(ctor_method); - output.push_back("(MonoObject* obj) " OPEN_BLOCK - "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); - output.push_back(itype.name); - output.push_back("\");\n" - "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" - "\treturn instance;\n" CLOSE_BLOCK "\n"); + output.append("Object* "); + output.append(ctor_method); + output.append("(MonoObject* obj) " OPEN_BLOCK + "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \""); + output.append(itype.name); + output.append("\");\n" + "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n" + "\treturn instance;\n" CLOSE_BLOCK "\n"); } } - output.push_back("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); + output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n"); - output.push_back("uint64_t get_core_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); + output.append("uint64_t get_core_api_hash() { return "); + output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n"); - output.push_back("#ifdef TOOLS_ENABLED\n" - "uint64_t get_editor_api_hash() { return "); - output.push_back(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n"); - output.push_back("#endif // TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n" + "uint64_t get_editor_api_hash() { return "); + output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n"); + output.append("#endif // TOOLS_ENABLED\n"); - output.push_back("uint32_t get_bindings_version() { return "); - output.push_back(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); + output.append("uint32_t get_bindings_version() { return "); + output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n"); - output.push_back("\nvoid register_generated_icalls() " OPEN_BLOCK); - output.push_back("\tgodot_register_glue_header_icalls();\n"); + output.append("\nvoid register_generated_icalls() " OPEN_BLOCK); + output.append("\tgodot_register_glue_header_icalls();\n"); -#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ - { \ - output.push_back("\tmono_add_internal_call("); \ - output.push_back("\"" BINDINGS_NAMESPACE "."); \ - output.push_back(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ - output.push_back("::"); \ - output.push_back(m_icall.name); \ - output.push_back("\", (void*)"); \ - output.push_back(m_icall.name); \ - output.push_back(");\n"); \ +#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \ + { \ + output.append("\tmono_add_internal_call("); \ + output.append("\"" BINDINGS_NAMESPACE "."); \ + output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \ + output.append("::"); \ + output.append(m_icall.name); \ + output.append("\", (void*)"); \ + output.append(m_icall.name); \ + output.append(");\n"); \ } bool tools_sequence = false; @@ -1821,11 +1834,11 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { if (!E->get().editor_only) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } } else { if (E->get().editor_only) { - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); tools_sequence = true; } } @@ -1835,23 +1848,23 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) ADD_INTERNAL_CALL_REGISTRATION(E->get()); - output.push_back("#endif // TOOLS_ENABLED\n"); + output.append("#endif // TOOLS_ENABLED\n"); for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) { if (tools_sequence) { if (!E->get().editor_only) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } } else { if (E->get().editor_only) { - output.push_back("#ifdef TOOLS_ENABLED\n"); + output.append("#ifdef TOOLS_ENABLED\n"); tools_sequence = true; } } @@ -1861,14 +1874,14 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) { if (tools_sequence) { tools_sequence = false; - output.push_back("#endif\n"); + output.append("#endif\n"); } #undef ADD_INTERNAL_CALL_REGISTRATION - output.push_back(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n"); + output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n"); - output.push_back("\n#endif // MONO_GLUE_ENABLED\n"); + output.append("\n#endif // MONO_GLUE_ENABLED\n"); Error save_err = _save_file(path_join(p_output_dir, "mono_glue.gen.cpp"), output); if (save_err != OK) @@ -1883,23 +1896,20 @@ uint32_t BindingsGenerator::get_version() { return BINDINGS_GENERATOR_VERSION; } -Error BindingsGenerator::_save_file(const String &p_path, const List<String> &p_content) { +Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) { FileAccessRef file = FileAccess::open(p_path, FileAccess::WRITE); ERR_EXPLAIN("Cannot open file: " + p_path); ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); - for (const List<String>::Element *E = p_content.front(); E; E = E->next()) { - file->store_string(E->get()); - } - + file->store_string(p_content.as_string()); file->close(); return OK; } -Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, List<String> &p_output) { +Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) { if (p_imethod.is_virtual) return OK; // Ignore @@ -1955,15 +1965,15 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte generated_icall_funcs.push_back(im_icall); if (im_icall->editor_only) - p_output.push_back("#ifdef TOOLS_ENABLED\n"); + p_output.append("#ifdef TOOLS_ENABLED\n"); // Generate icall function - p_output.push_back(ret_void ? "void " : return_type->c_type_out + " "); - p_output.push_back(icall_method); - p_output.push_back("("); - p_output.push_back(c_func_sig); - p_output.push_back(") " OPEN_BLOCK); + p_output.append(ret_void ? "void " : return_type->c_type_out + " "); + p_output.append(icall_method); + p_output.append("("); + p_output.append(c_func_sig); + p_output.append(") " OPEN_BLOCK); String fail_ret = ret_void ? "" : ", " + (return_type->c_type_out.ends_with("*") ? "NULL" : return_type->c_type_out + "()"); @@ -1977,7 +1987,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr, // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT. // Alternatively, we could just return Variant, but that would result in a worse API. - p_output.push_back("\tVariant " C_LOCAL_VARARG_RET ";\n"); + p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n"); } if (return_type->is_object_type) { @@ -1987,83 +1997,82 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte ptrcall_return_type = return_type->c_type; } - p_output.push_back("\t" + ptrcall_return_type); - p_output.push_back(" " C_LOCAL_RET); - p_output.push_back(initialization + ";\n"); - p_output.push_back("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); - p_output.push_back(fail_ret); - p_output.push_back(");\n"); + p_output.append("\t" + ptrcall_return_type); + p_output.append(" " C_LOCAL_RET); + p_output.append(initialization + ";\n"); + p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE); + p_output.append(fail_ret); + p_output.append(");\n"); } else { - p_output.push_back("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); + p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n"); } if (p_imethod.arguments.size()) { if (p_imethod.is_vararg) { - String err_fail_macro = ret_void ? "ERR_FAIL_COND" : "ERR_FAIL_COND_V"; String vararg_arg = "arg" + argc_str; String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg - p_output.push_back("\tint vararg_length = mono_array_length("); - p_output.push_back(vararg_arg); - p_output.push_back(");\n\tint total_length = "); - p_output.push_back(real_argc_str); - p_output.push_back(" + vararg_length;\n" - "\tArgumentsVector<Variant> varargs(vararg_length);\n" - "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n"); - p_output.push_back(c_in_statements); - p_output.push_back("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK - "\t\tMonoObject* elem = mono_array_get("); - p_output.push_back(vararg_arg); - p_output.push_back(", MonoObject*, i);\n" - "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" - "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); - p_output.push_back(real_argc_str); - p_output.push_back(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK); + p_output.append("\tint vararg_length = mono_array_length("); + p_output.append(vararg_arg); + p_output.append(");\n\tint total_length = "); + p_output.append(real_argc_str); + p_output.append(" + vararg_length;\n" + "\tArgumentsVector<Variant> varargs(vararg_length);\n" + "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n"); + p_output.append(c_in_statements); + p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK + "\t\tMonoObject* elem = mono_array_get("); + p_output.append(vararg_arg); + p_output.append(", MonoObject*, i);\n" + "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" + "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); + p_output.append(real_argc_str); + p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK); } else { - p_output.push_back(c_in_statements); - p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); - p_output.push_back(argc_str + "] = { "); - p_output.push_back(c_args_var_content + " };\n"); + p_output.append(c_in_statements); + p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); + p_output.append(argc_str + "] = { "); + p_output.append(c_args_var_content + " };\n"); } } if (p_imethod.is_vararg) { - p_output.push_back("\tVariant::CallError vcall_error;\n\t"); + p_output.append("\tVariant::CallError vcall_error;\n\t"); if (!ret_void) { // See the comment on the C_LOCAL_VARARG_RET declaration if (return_type->cname != name_cache.type_Variant) { - p_output.push_back(C_LOCAL_VARARG_RET " = "); + p_output.append(C_LOCAL_VARARG_RET " = "); } else { - p_output.push_back(C_LOCAL_RET " = "); + p_output.append(C_LOCAL_RET " = "); } } - p_output.push_back(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); - p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); - p_output.push_back(", total_length, vcall_error);\n"); + p_output.append(CS_PARAM_METHODBIND "->call(" CS_PARAM_INSTANCE ", "); + p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "NULL"); + p_output.append(", total_length, vcall_error);\n"); // See the comment on the C_LOCAL_VARARG_RET declaration if (return_type->cname != name_cache.type_Variant) { - p_output.push_back("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n"); + p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n"); } } else { - p_output.push_back("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); - p_output.push_back(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); - p_output.push_back(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n"); + p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(" CS_PARAM_INSTANCE ", "); + p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "NULL, "); + p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "NULL);\n"); } if (!ret_void) { if (return_type->c_out.empty()) - p_output.push_back("\treturn " C_LOCAL_RET ";\n"); + p_output.append("\treturn " C_LOCAL_RET ";\n"); else - p_output.push_back(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); + p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name)); } - p_output.push_back(CLOSE_BLOCK "\n"); + p_output.append(CLOSE_BLOCK "\n"); if (im_icall->editor_only) - p_output.push_back("#endif // TOOLS_ENABLED\n"); + p_output.append("#endif // TOOLS_ENABLED\n"); } return OK; @@ -2116,6 +2125,58 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } +StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { + + switch (p_meta) { + case GodotTypeInfo::METADATA_INT_IS_INT8: + return "sbyte"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT16: + return "short"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT32: + return "int"; + break; + case GodotTypeInfo::METADATA_INT_IS_INT64: + return "long"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT8: + return "byte"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT16: + return "ushort"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT32: + return "uint"; + break; + case GodotTypeInfo::METADATA_INT_IS_UINT64: + return "ulong"; + break; + default: + // Assume INT32 + return "int"; + } +} + +StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta) { + + switch (p_meta) { + case GodotTypeInfo::METADATA_REAL_IS_FLOAT: + return "float"; + break; + case GodotTypeInfo::METADATA_REAL_IS_DOUBLE: + return "double"; + break; + default: + // Assume real_t (float or double depending of REAL_T_IS_DOUBLE) +#ifdef REAL_T_IS_DOUBLE + return "double"; +#else + return "float"; +#endif + } +} + void BindingsGenerator::_populate_object_type_interfaces() { obj_types.clear(); @@ -2135,15 +2196,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } if (!ClassDB::is_class_exposed(type_cname)) { - if (verbose_output) - WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not exposed"); + _log("Ignoring type `%s` because it's not exposed\n", String(type_cname).utf8().get_data()); class_list.pop_front(); continue; } if (!ClassDB::is_class_enabled(type_cname)) { - if (verbose_output) - WARN_PRINTS("Ignoring type " + type_cname.operator String() + " because it's not enabled"); + _log("Ignoring type `%s` because it's not enabled\n", String(type_cname).utf8().get_data()); class_list.pop_front(); continue; } @@ -2171,10 +2230,12 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.im_type_in = "IntPtr"; itype.im_type_out = itype.proxy_name; + // Populate properties + List<PropertyInfo> property_list; ClassDB::get_property_list(type_cname, &property_list, true); - // Populate properties + Map<StringName, StringName> accessor_methods; for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { const PropertyInfo &property = E->get(); @@ -2187,18 +2248,21 @@ void BindingsGenerator::_populate_object_type_interfaces() { iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname); iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname); + if (iprop.setter != StringName()) + accessor_methods[iprop.setter] = iprop.cname; + if (iprop.getter != StringName()) + accessor_methods[iprop.getter] = iprop.cname; + bool valid = false; iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid); ERR_FAIL_COND(!valid); iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); - // Prevent property and enclosing type from sharing the same name + // Prevent the property and its enclosing type from sharing the same name if (iprop.proxy_name == itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of property `" + iprop.proxy_name + "` is ambiguous with the name of its class `" + - itype.proxy_name + "`. Renaming property to `" + iprop.proxy_name + "_`"); - } + _log("Name of property `%s` is ambiguous with the name of its enclosing class `%s`. Renaming property to `%s_`\n", + iprop.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), iprop.proxy_name.utf8().get_data()); iprop.proxy_name += "_"; } @@ -2236,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; @@ -2265,14 +2334,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { // which could actually will return something different. // Let's put this to notify us if that ever happens. if (itype.cname != name_cache.type_Object || imethod.name != "free") { - if (verbose_output) { - WARN_PRINTS("Notification: New unexpected virtual non-overridable method found.\n" - "We only expected Object.free, but found " + - itype.name + "." + imethod.name); - } + ERR_PRINTS("Notification: New unexpected virtual non-overridable method found.\n" + "We only expected Object.free, but found " + + itype.name + "." + imethod.name); } } else { - ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); + ERR_EXPLAIN("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); + ERR_FAIL(); } } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { imethod.return_type.cname = return_info.class_name; @@ -2286,7 +2354,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else if (return_info.type == Variant::NIL) { imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type.cname = Variant::get_type_name(return_info.type); + if (return_info.type == Variant::INT) { + imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); + } else if (return_info.type == Variant::REAL) { + imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE); + } else { + imethod.return_type.cname = Variant::get_type_name(return_info.type); + } } for (int i = 0; i < argc; i++) { @@ -2305,7 +2379,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else if (arginfo.type == Variant::NIL) { iarg.type.cname = name_cache.type_Variant; } else { - iarg.type.cname = Variant::get_type_name(arginfo.type); + if (arginfo.type == Variant::INT) { + iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); + } else if (arginfo.type == Variant::REAL) { + iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE); + } else { + iarg.type.cname = Variant::get_type_name(arginfo.type); + } } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); @@ -2326,16 +2406,24 @@ void BindingsGenerator::_populate_object_type_interfaces() { imethod.proxy_name = escape_csharp_keyword(snake_to_pascal_case(imethod.name)); - // Prevent naming the property and its enclosing type from sharing the same name + // Prevent the method and its enclosing type from sharing the same name if (imethod.proxy_name == itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of method `" + imethod.proxy_name + "` is ambiguous with the name of its class `" + - itype.proxy_name + "`. Renaming method to `" + imethod.proxy_name + "_`"); - } + _log("Name of method `%s` is ambiguous with the name of its enclosing class `%s`. Renaming method to `%s_`\n", + imethod.proxy_name.utf8().get_data(), itype.proxy_name.utf8().get_data(), imethod.proxy_name.utf8().get_data()); imethod.proxy_name += "_"; } + Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname); + if (accessor) { + const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value()); + + // We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also + // we don't know if an accessor method in a different class could have other purposes, so better leave those untouched. + imethod.is_deprecated = true; + imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead."; + } + if (itype.class_doc) { for (int i = 0; i < itype.class_doc->methods.size(); i++) { if (itype.class_doc->methods[i].name == imethod.name) { @@ -2362,8 +2450,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { // Populate enums and constants - List<String> constant_list; - ClassDB::get_integer_constant_list(type_cname, &constant_list, true); + List<String> constants; + ClassDB::get_integer_constant_list(type_cname, &constants, true); const HashMap<StringName, List<StringName> > &enum_map = class_info->enum_map; const StringName *k = NULL; @@ -2378,13 +2466,13 @@ void BindingsGenerator::_populate_object_type_interfaces() { enum_proxy_cname = StringName(enum_proxy_name); } EnumInterface ienum(enum_proxy_cname); - const List<StringName> &constants = enum_map.get(*k); - for (const List<StringName>::Element *E = constants.front(); E; E = E->next()) { + const List<StringName> &enum_constants = enum_map.get(*k); + for (const List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) { const StringName &constant_cname = E->get(); String constant_name = constant_cname.operator String(); int *value = class_info->constant_map.getptr(constant_cname); ERR_FAIL_NULL(value); - constant_list.erase(constant_name); + constants.erase(constant_name); ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); @@ -2416,7 +2504,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { enum_types.insert(enum_itype.cname, enum_itype); } - for (const List<String>::Element *E = constant_list.front(); E; E = E->next()) { + for (const List<String>::Element *E = constants.front(); E; E = E->next()) { const String &constant_name = E->get(); int *value = class_info->constant_map.getptr(StringName(E->get())); ERR_FAIL_NULL(value); @@ -2563,7 +2651,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { // bool itype = TypeInterface::create_value_type(String("bool")); - { // MonoBoolean <---> bool itype.c_in = "\t%0 %1_in = (%0)%1;\n"; @@ -2577,45 +2664,73 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.im_type_out = itype.name; builtin_types.insert(itype.cname, itype); - // int - // C interface is the same as that of enums. Remember to apply any - // changes done here to TypeInterface::postsetup_enum_type as well - itype = TypeInterface::create_value_type(String("int")); - itype.c_arg_in = "&%s_in"; + // Integer types { - // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'. - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; - itype.c_type = "int64_t"; + // C interface for 'uint32_t' is the same as that of enums. Remember to apply + // any of the changes done here to 'TypeInterface::postsetup_enum_type' as well. +#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \ + { \ + itype = TypeInterface::create_value_type(String(m_name)); \ + { \ + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \ + itype.c_out = "\treturn (%0)%1;\n"; \ + itype.c_type = #m_c_type; \ + itype.c_arg_in = "&%s_in"; \ + } \ + itype.c_type_in = #m_c_type_in_out; \ + itype.c_type_out = itype.c_type_in; \ + itype.im_type_in = itype.name; \ + itype.im_type_out = itype.name; \ + builtin_types.insert(itype.cname, itype); \ } - itype.c_type_in = "int32_t"; - itype.c_type_out = itype.c_type_in; - itype.im_type_in = itype.name; - itype.im_type_out = itype.name; - builtin_types.insert(itype.cname, itype); - // real_t - itype = TypeInterface(); - itype.name = "float"; // The name is always "float" in Variant, even with REAL_T_IS_DOUBLE. - itype.cname = itype.name; -#ifdef REAL_T_IS_DOUBLE - itype.proxy_name = "double"; -#else - itype.proxy_name = "float"; -#endif + // The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type' + + INSERT_INT_TYPE("sbyte", int8_t, int64_t); + INSERT_INT_TYPE("short", int16_t, int64_t); + INSERT_INT_TYPE("int", int32_t, int64_t); + INSERT_INT_TYPE("long", int64_t, int64_t); + INSERT_INT_TYPE("byte", uint8_t, int64_t); + INSERT_INT_TYPE("ushort", uint16_t, int64_t); + INSERT_INT_TYPE("uint", uint32_t, int64_t); + INSERT_INT_TYPE("ulong", uint64_t, int64_t); + } + + // Floating point types { - // The expected type for parameters and return value in ptrcall is 'double'. - itype.c_in = "\t%0 %1_in = (%0)%1;\n"; - itype.c_out = "\treturn (%0)%1;\n"; + // float + itype = TypeInterface(); + itype.name = "float"; + itype.cname = itype.name; + itype.proxy_name = "float"; + { + // The expected type for 'float' in ptrcall is 'double' + itype.c_in = "\t%0 %1_in = (%0)%1;\n"; + itype.c_out = "\treturn (%0)%1;\n"; + itype.c_type = "double"; + itype.c_type_in = "float"; + itype.c_type_out = "float"; + itype.c_arg_in = "&%s_in"; + } + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.cname, itype); + + // double + itype = TypeInterface(); + itype.name = "double"; + itype.cname = itype.name; + itype.proxy_name = "double"; itype.c_type = "double"; - itype.c_type_in = "real_t"; - itype.c_type_out = "real_t"; - itype.c_arg_in = "&%s_in"; + itype.c_type_in = "double"; + itype.c_type_out = "double"; + itype.c_arg_in = "&%s"; + itype.cs_type = itype.proxy_name; + itype.im_type_in = itype.proxy_name; + itype.im_type_out = itype.proxy_name; + builtin_types.insert(itype.cname, itype); } - itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; - builtin_types.insert(itype.cname, itype); // String itype = TypeInterface(); @@ -2865,12 +2980,32 @@ void BindingsGenerator::_populate_global_constants() { } } -void BindingsGenerator::initialize() { +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) { + va_list list; + + va_start(list, p_format); + OS::get_singleton()->print("%s", str_format(p_format, list).utf8().get_data()); + va_end(list); + } +} + +void BindingsGenerator::_initialize() { EditorHelp::generate_doc(); enum_types.clear(); + _initialize_blacklisted_methods(); + _populate_object_type_interfaces(); _populate_builtin_type_interfaces(); @@ -2888,38 +3023,33 @@ void BindingsGenerator::initialize() { void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) { const int NUM_OPTIONS = 2; - int options_left = NUM_OPTIONS; - String mono_glue_option = "--generate-mono-glue"; String cs_api_option = "--generate-cs-api"; - verbose_output = true; + String mono_glue_path; + String cs_api_path; + + int options_left = NUM_OPTIONS; const List<String>::Element *elem = p_cmdline_args.front(); while (elem && options_left) { - if (elem->get() == mono_glue_option) { - const List<String>::Element *path_elem = elem->next(); if (path_elem) { - if (get_singleton()->generate_glue(path_elem->get()) != OK) - ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue"); + mono_glue_path = path_elem->get(); elem = elem->next(); } else { ERR_PRINTS(mono_glue_option + ": No output directory specified"); } --options_left; - } else if (elem->get() == cs_api_option) { - const List<String>::Element *path_elem = elem->next(); if (path_elem) { - if (get_singleton()->generate_cs_api(path_elem->get()) != OK) - ERR_PRINTS(cs_api_option + ": Failed to generate the C# API"); + cs_api_path = path_elem->get(); elem = elem->next(); } else { ERR_PRINTS(cs_api_option + ": No output directory specified"); @@ -2931,10 +3061,23 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) elem = elem->next(); } - verbose_output = false; + if (mono_glue_path.length() || cs_api_path.length()) { + BindingsGenerator bindings_generator; + bindings_generator.set_log_print_enabled(true); + + if (mono_glue_path.length()) { + if (bindings_generator.generate_glue(mono_glue_path) != OK) + ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue"); + } - if (options_left != NUM_OPTIONS) + if (cs_api_path.length()) { + if (bindings_generator.generate_cs_api(cs_api_path) != OK) + ERR_PRINTS(cs_api_option + ": Failed to generate the C# API"); + } + + // Exit once done ::exit(0); + } } #endif diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 42071f9c0d..ffc73a7e3e 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -32,6 +32,7 @@ #define BINDINGS_GENERATOR_H #include "core/class_db.h" +#include "core/string_builder.h" #include "dotnet_solution.h" #include "editor/doc/doc_data.h" #include "editor/editor_help.h" @@ -158,17 +159,20 @@ class BindingsGenerator { const DocData::MethodDoc *method_doc; + bool is_deprecated; + String deprecation_message; + void add_argument(const ArgumentInterface &argument) { arguments.push_back(argument); } MethodInterface() { - return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void; is_vararg = false; is_virtual = false; requires_object_call = false; is_internal = false; method_doc = NULL; + is_deprecated = false; } }; @@ -399,8 +403,8 @@ class BindingsGenerator { } static void postsetup_enum_type(TypeInterface &r_enum_itype) { - // C interface is the same as that of 'int'. Remember to apply any - // changes done here to the 'int' type interface as well + // C interface for enums is the same as that of 'uint32_t'. Remember to apply + // any of the changes done here to the 'uint32_t' type interface as well. r_enum_itype.c_arg_in = "&%s_in"; { @@ -468,7 +472,7 @@ class BindingsGenerator { } }; - static bool verbose_output; + bool log_print_enabled; OrderedHashMap<StringName, TypeInterface> obj_types; @@ -487,9 +491,12 @@ 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_int; StringName type_Array; StringName type_Dictionary; StringName type_Variant; @@ -500,9 +507,19 @@ class BindingsGenerator { StringName type_at_GlobalScope; StringName enum_Error; + StringName type_sbyte; + StringName type_short; + StringName type_int; + StringName type_long; + StringName type_byte; + StringName type_ushort; + StringName type_uint; + StringName type_ulong; + StringName type_float; + StringName type_double; + NameCache() { type_void = StaticCString::create("void"); - type_int = StaticCString::create("int"); type_Array = StaticCString::create("Array"); type_Dictionary = StaticCString::create("Dictionary"); type_Variant = StaticCString::create("Variant"); @@ -512,8 +529,20 @@ class BindingsGenerator { type_String = StaticCString::create("String"); type_at_GlobalScope = StaticCString::create("@GlobalScope"); enum_Error = StaticCString::create("Error"); + + type_sbyte = StaticCString::create("sbyte"); + type_short = StaticCString::create("short"); + type_int = StaticCString::create("int"); + type_long = StaticCString::create("long"); + type_byte = StaticCString::create("byte"); + type_ushort = StaticCString::create("ushort"); + type_uint = StaticCString::create("uint"); + type_ulong = StaticCString::create("ulong"); + type_float = StaticCString::create("float"); + type_double = StaticCString::create("double"); } + private: NameCache(const NameCache &); NameCache &operator=(const NameCache &); }; @@ -559,6 +588,9 @@ class BindingsGenerator { const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); + StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta); + StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta); + void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); void _populate_object_type_interfaces(); @@ -568,42 +600,35 @@ class BindingsGenerator { Error _generate_cs_type(const TypeInterface &itype, const String &p_output_file); - Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, List<String> &p_output); - Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output); + Error _generate_cs_property(const TypeInterface &p_itype, const PropertyInterface &p_iprop, StringBuilder &p_output); + Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output); - void _generate_global_constants(List<String> &p_output); + void _generate_global_constants(StringBuilder &p_output); - Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, List<String> &p_output); + Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output); - Error _save_file(const String &p_path, const List<String> &p_content); + Error _save_file(const String &p_path, const StringBuilder &p_content); - BindingsGenerator() {} + void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; - BindingsGenerator(const BindingsGenerator &); - BindingsGenerator &operator=(const BindingsGenerator &); - - friend class CSharpLanguage; - static BindingsGenerator *singleton; + void _initialize(); public: - Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); - Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output = true); - Error generate_cs_api(const String &p_output_dir, bool p_verbose_output = true); + Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution); + Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution); + Error generate_cs_api(const String &p_output_dir); Error generate_glue(const String &p_output_dir); + void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; } + static uint32_t get_version(); - void initialize(); + static void handle_cmdline_args(const List<String> &p_cmdline_args); - _FORCE_INLINE_ static BindingsGenerator *get_singleton() { - if (!singleton) { - singleton = memnew(BindingsGenerator); - singleton->initialize(); - } - return singleton; + BindingsGenerator() : + log_print_enabled(true) { + _initialize(); } - - static void handle_cmdline_args(const List<String> &p_cmdline_args); }; #endif diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index beeff51bc2..fe79286556 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -158,7 +158,7 @@ Error generate_scripts_metadata(const String &p_project_path, const String &p_ou PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret); PoolStringArray::Read r = project_files.read(); - Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata(); + Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing(); Dictionary new_dict; for (int i = 0; i < project_files.size(); i++) { diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 00c780d1b7..a962d6df27 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" @@ -323,10 +324,13 @@ bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) { String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln"); if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) { - BindingsGenerator *gen = BindingsGenerator::get_singleton(); - bool gen_verbose = OS::get_singleton()->is_stdout_verbose(); + BindingsGenerator bindings_generator; - Error err = gen->generate_cs_api(api_sln_dir, gen_verbose); + if (!OS::get_singleton()->is_stdout_verbose()) { + bindings_generator.set_log_print_enabled(false); + } + + Error err = bindings_generator.generate_cs_api(api_sln_dir); if (err != OK) { show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err)); return false; @@ -348,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 @@ -363,6 +367,29 @@ 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 +#ifdef WINDOWS_ENABLED + String constants = "GodotDefineConstants=\""; +#else + String constants = "GodotDefineConstants=\\\""; +#endif + + 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 + +#ifdef WINDOWS_ENABLED + constants += "\""; +#else + constants += "\\\""; +#endif + 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; @@ -390,7 +417,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..126178125f 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 @@ -120,11 +125,21 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, project_dll_src_path, &scripts_assembly, /* refonly: */ true); - ERR_EXPLAIN("Cannot load refonly assembly: " + project_dll_name); + ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name); ERR_FAIL_COND(!load_success); Vector<String> search_dirs; - GDMonoAssembly::fill_search_dirs(search_dirs, build_config); + String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG); + String android_bcl_dir = templates_dir.plus_file("android-bcl"); + + String custom_lib_dir; + + if (p_features.find("Android") && DirAccess::exists(android_bcl_dir)) { + custom_lib_dir = android_bcl_dir; + } + + GDMonoAssembly::fill_search_dirs(search_dirs, build_config, custom_lib_dir); + Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies); ERR_FAIL_COND(depend_error != OK); } @@ -147,7 +162,7 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug int i = 0; for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) { MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get()); - mono_array_set(features, MonoString *, i, boxed); + mono_array_setref(features, i, boxed); i++; } @@ -229,8 +244,10 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c r_dependencies.insert(ref_name, ref_assembly->get_path()); Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); - if (err != OK) - return err; + if (err != OK) { + ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name); + ERR_FAIL_V(err); + } } return OK; 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/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index 2277c7bf18..0e7b0362e0 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -143,6 +143,11 @@ namespace Godot.Collections } } + public override string ToString() + { + return godot_icall_Array_ToString(GetPtr()); + } + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Array_Ctor(); @@ -190,6 +195,9 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Array_ToString(IntPtr ptr); } public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> @@ -353,5 +361,7 @@ namespace Godot.Collections { return GetEnumerator(); } + + public override string ToString() => objectArray.ToString(); } } 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/Color.cs b/modules/mono/glue/Managed/Files/Color.cs index 88fa3323c2..84ff19fc54 100644 --- a/modules/mono/glue/Managed/Files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -168,7 +168,7 @@ namespace Godot int max = Mathf.Max(color.r8, Mathf.Max(color.g8, color.b8)); int min = Mathf.Min(color.r8, Mathf.Min(color.g8, color.b8)); - float delta = max - min; + int delta = max - min; if (delta == 0) { @@ -591,11 +591,11 @@ namespace Godot public static bool operator <(Color left, Color right) { - if (left.r == right.r) + if (Mathf.IsEqualApprox(left.r, right.r)) { - if (left.g == right.g) + if (Mathf.IsEqualApprox(left.g, right.g)) { - if (left.b == right.b) + if (Mathf.IsEqualApprox(left.b, right.b)) return left.a < right.a; return left.b < right.b; } @@ -608,11 +608,11 @@ namespace Godot public static bool operator >(Color left, Color right) { - if (left.r == right.r) + if (Mathf.IsEqualApprox(left.r, right.r)) { - if (left.g == right.g) + if (Mathf.IsEqualApprox(left.g, right.g)) { - if (left.b == right.b) + if (Mathf.IsEqualApprox(left.b, right.b)) return left.a > right.a; return left.b > right.b; } @@ -635,7 +635,7 @@ namespace Godot public bool Equals(Color other) { - return r == other.r && g == other.g && b == other.b && a == other.a; + return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index af1782b79a..6ab8549a01 100644 --- a/modules/mono/glue/Managed/Files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -193,6 +193,11 @@ namespace Godot.Collections } } + public override string ToString() + { + return godot_icall_Dictionary_ToString(GetPtr()); + } + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Dictionary_Ctor(); @@ -243,6 +248,9 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); } public class Dictionary<TKey, TValue> : @@ -442,5 +450,7 @@ namespace Godot.Collections { return GetEnumerator(); } + + public override string ToString() => objectDict.ToString(); } } diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs index 9504415664..a0f105d55e 100644 --- a/modules/mono/glue/Managed/Files/DynamicObject.cs +++ b/modules/mono/glue/Managed/Files/DynamicObject.cs @@ -26,7 +26,7 @@ namespace Godot /// dynamic sprite = GetNode("Sprite").DynamicGodotObject; /// sprite.add_child(this); /// - /// if ((sprite.hframes * sprite.vframes) > 0) + /// if ((sprite.hframes * sprite.vframes) > 0) /// sprite.frame = 0; /// </code> /// </example> @@ -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/GD.cs b/modules/mono/glue/Managed/Files/GD.cs index d968f8a78f..2068099ac6 100644 --- a/modules/mono/glue/Managed/Files/GD.cs +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -83,7 +83,7 @@ namespace Godot public static void Print(params object[] what) { - godot_icall_GD_print(what); + godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString())); } public static void PrintStack() @@ -93,25 +93,25 @@ namespace Godot public static void PrintErr(params object[] what) { - godot_icall_GD_printerr(what); + godot_icall_GD_printerr(Array.ConvertAll(what, x => x.ToString())); } public static void PrintRaw(params object[] what) { - godot_icall_GD_printraw(what); + godot_icall_GD_printraw(Array.ConvertAll(what, x => x.ToString())); } public static void PrintS(params object[] what) { - godot_icall_GD_prints(what); + godot_icall_GD_prints(Array.ConvertAll(what, x => x.ToString())); } public static void PrintT(params object[] what) { - godot_icall_GD_printt(what); + godot_icall_GD_printt(Array.ConvertAll(what, x => x.ToString())); } - public static double Randf() + public static float Randf() { return godot_icall_GD_randf(); } @@ -224,7 +224,7 @@ namespace Godot internal extern static void godot_icall_GD_printt(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static double godot_icall_GD_randf(); + internal extern static float godot_icall_GD_randf(); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static uint godot_icall_GD_randi(); @@ -232,6 +232,7 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_GD_randomize(); + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static double godot_icall_GD_rand_range(double from, double to); 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 a064278237..2d8c63fe7f 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) @@ -79,14 +79,27 @@ namespace Godot return (real_t)Math.Cosh(s); } - public static int Decimals(real_t step) - { - return Decimals((decimal)step); - } - - public static int Decimals(decimal step) - { - return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; + public static int StepDecimals(real_t step) + { + double[] sd = new double[] { + 0.9999, + 0.09999, + 0.009999, + 0.0009999, + 0.00009999, + 0.000009999, + 0.0000009999, + 0.00000009999, + 0.000000009999, + }; + double abs = Mathf.Abs(step); + double decs = abs - (int)abs; // Strip away integer part + for (int i = 0; i < sd.Length; i++) { + if (decs >= sd[i]) { + return i; + } + } + return 0; } public static real_t Deg2Rad(real_t deg) @@ -143,6 +156,15 @@ namespace Godot return (weight - from) / (to - from); } + public static bool IsEqualApprox(real_t a, real_t b) + { + real_t tolerance = Epsilon * Abs(a); + if (tolerance < Epsilon) { + tolerance = Epsilon; + } + return Abs(a - b) < tolerance; + } + public static bool IsInf(real_t s) { return real_t.IsInfinity(s); @@ -153,6 +175,11 @@ namespace Godot return real_t.IsNaN(s); } + public static bool IsZeroApprox(real_t s) + { + return Abs(s) < Epsilon; + } + public static real_t Lerp(real_t from, real_t to, real_t weight) { return from + (to - from) * weight; @@ -183,6 +210,11 @@ namespace Godot return a < b ? a : b; } + public static real_t MoveToward(real_t from, real_t to, real_t delta) + { + return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta; + } + public static int NearestPo2(int value) { value--; diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 414762f7b1..b96f01bc2e 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -21,6 +21,16 @@ namespace Godot public const real_t Epsilon = 1e-06f; #endif + public static int DecimalCount(real_t s) + { + return DecimalCount((decimal)s); + } + + public static int DecimalCount(decimal s) + { + return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; + } + public static int CeilToInt(real_t s) { return (int)Math.Ceiling(s); @@ -36,9 +46,9 @@ namespace Godot return (int)Math.Round(s); } - public static bool IsEqualApprox(real_t a, real_t b, real_t ratio = Mathf.Epsilon) + public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) { - return Abs(a - b) < ratio; + return Abs(a - b) < tolerance; } } }
\ No newline at end of file diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs index e152d56871..de80f7fddc 100644 --- a/modules/mono/glue/Managed/Files/Object.base.cs +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -73,6 +73,11 @@ namespace Godot disposed = true; } + public override string ToString() + { + return godot_icall_Object_ToString(GetPtr(this)); + } + /// <summary> /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter. @@ -88,7 +93,7 @@ namespace Godot /// <code> /// public override void _Ready() /// { - /// for (int i = 0; i < 100; i++) + /// for (int i = 0; i < 100; i++) /// { /// await ToSignal(GetTree(), "idle_frame"); /// GD.Print($"Frame {i}"); @@ -115,6 +120,9 @@ namespace Godot [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Object_ToString(IntPtr ptr); + // Used by the generated API [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index f11cd490a9..e16d4315be 100644 --- a/modules/mono/glue/Managed/Files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -200,7 +200,7 @@ namespace Godot public bool Equals(Plane other) { - return _normal == other._normal && D == other.D; + return _normal == other._normal && Mathf.IsEqualApprox(D, other.D); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index d0c15146a5..0d4349084a 100644 --- a/modules/mono/glue/Managed/Files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -358,7 +358,7 @@ namespace Godot public bool Equals(Quat other) { - return x == other.x && y == other.y && z == other.z && w == other.w; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs index f1268c8518..12064beca2 100644 --- a/modules/mono/glue/Managed/Files/RID.cs +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -70,6 +70,8 @@ namespace Godot return godot_icall_RID_get_id(RID.GetPtr(this)); } + public override string ToString() => "[RID]"; + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); 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/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 908162ec45..a7f26283a7 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -52,11 +52,15 @@ namespace Godot internal void Normalize() { - real_t length = x * x + y * y; + real_t lengthsq = LengthSquared(); - if (length != 0f) + if (lengthsq == 0) { - length = Mathf.Sqrt(length); + x = y = 0f; + } + else + { + real_t length = Mathf.Sqrt(lengthsq); x /= length; y /= length; } @@ -182,11 +186,19 @@ namespace Godot return res; } + public Vector2 MoveToward(Vector2 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + public Vector2 Normalized() { - var result = this; - result.Normalize(); - return result; + var v = this; + v.Normalize(); + return v; } public Vector2 Project(Vector2 onNormal) @@ -343,7 +355,7 @@ namespace Godot public static bool operator <(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y < right.y; } @@ -353,7 +365,7 @@ namespace Godot public static bool operator >(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y > right.y; } @@ -363,7 +375,7 @@ namespace Godot public static bool operator <=(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y <= right.y; } @@ -373,7 +385,7 @@ namespace Godot public static bool operator >=(Vector2 left, Vector2 right) { - if (left.x.Equals(right.x)) + if (Mathf.IsEqualApprox(left.x, right.x)) { return left.y >= right.y; } @@ -393,7 +405,7 @@ namespace Godot public bool Equals(Vector2 other) { - return x == other.x && y == other.y; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 0c96d346a9..16803ae55c 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -65,14 +65,15 @@ namespace Godot internal void Normalize() { - real_t length = Length(); + real_t lengthsq = LengthSquared(); - if (length == 0f) + if (lengthsq == 0) { x = y = z = 0f; } else { + real_t length = Mathf.Sqrt(lengthsq); x /= length; y /= length; z /= length; @@ -189,6 +190,14 @@ namespace Godot ); } + public Vector3 MoveToward(Vector3 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + public Axis MaxAxis() { return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); @@ -397,9 +406,9 @@ namespace Godot public static bool operator <(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z < right.z; return left.y < right.y; } @@ -409,9 +418,9 @@ namespace Godot public static bool operator >(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z > right.z; return left.y > right.y; } @@ -421,9 +430,9 @@ namespace Godot public static bool operator <=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z <= right.z; return left.y < right.y; } @@ -433,9 +442,9 @@ namespace Godot public static bool operator >=(Vector3 left, Vector3 right) { - if (left.x == right.x) + if (Mathf.IsEqualApprox(left.x, right.x)) { - if (left.y == right.y) + if (Mathf.IsEqualApprox(left.y, right.y)) return left.z >= right.z; return left.y > right.y; } @@ -455,7 +464,7 @@ namespace Godot public bool Equals(Vector3 other) { - return x == other.x && y == other.y && z == other.z; + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); } public override int GetHashCode() 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/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 7f367fa095..75b2dfce9a 100644 --- a/modules/mono/glue/base_object_glue.cpp +++ b/modules/mono/glue/base_object_glue.cpp @@ -166,7 +166,7 @@ MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) { int i = 0; for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name); - mono_array_set(result, MonoString *, i, boxed); + mono_array_setref(result, i, boxed); i++; } @@ -218,11 +218,16 @@ MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString * return valid; } +MonoString *godot_icall_Object_ToString(Object *p_ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(p_ptr).operator String()); +} + void godot_register_object_icalls() { mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor); mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed); mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method); + mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString); mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref); mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect); mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList); diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h index 9b5224a347..e4ac9fb99d 100644 --- a/modules/mono/glue/base_object_glue.h +++ b/modules/mono/glue/base_object_glue.h @@ -60,6 +60,8 @@ MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString * MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value); +MonoString *godot_icall_Object_ToString(Object *p_ptr); + // Register internal calls void godot_register_object_icalls(); diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 84c3f354d8..47239f1260 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -143,6 +143,10 @@ void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Array_ToString(Array *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + Dictionary *godot_icall_Dictionary_Ctor() { return memnew(Dictionary); } @@ -158,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; } @@ -172,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; } @@ -264,6 +268,10 @@ void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltyp *type_class = GDMono::get_singleton()->get_class(type_class_raw); } +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) { + return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String()); +} + void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); @@ -281,6 +289,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); @@ -299,6 +308,7 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index 69768500d8..54ed42c3b6 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -71,6 +71,8 @@ Error godot_icall_Array_Resize(Array *ptr, int new_size); void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Array_ToString(Array *ptr); + // Dictionary Dictionary *godot_icall_Dictionary_Ctor(); @@ -107,6 +109,8 @@ MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObje void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); +MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr); + // Register internal calls void godot_register_collections_icalls(); diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp index d756131ac9..7c30092855 100644 --- a/modules/mono/glue/gd_glue.cpp +++ b/modules/mono/glue/gd_glue.cpp @@ -115,7 +115,7 @@ void godot_icall_GD_printt(MonoArray *p_what) { print_line(str); } -double godot_icall_GD_randf() { +float godot_icall_GD_randf() { return Math::randf(); } diff --git a/modules/mono/glue/gd_glue.h b/modules/mono/glue/gd_glue.h index 910979aae3..d4e20e2887 100644 --- a/modules/mono/glue/gd_glue.h +++ b/modules/mono/glue/gd_glue.h @@ -53,7 +53,7 @@ void godot_icall_GD_prints(MonoArray *p_what); void godot_icall_GD_printt(MonoArray *p_what); -double godot_icall_GD_randf(); +float godot_icall_GD_randf(); uint32_t godot_icall_GD_randi(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 48bea4addf..7699e0d0cd 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -30,6 +30,7 @@ #include "gd_mono.h" +#include <mono/metadata/environment.h> #include <mono/metadata/exception.h> #include <mono/metadata/mono-config.h> #include <mono/metadata/mono-debug.h> @@ -250,17 +251,19 @@ void GDMono::initialize() { String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir(); #ifdef TOOLS_ENABLED - if (DirAccess::exists(bundled_assembly_rootdir) && DirAccess::exists(bundled_config_dir)) { + if (DirAccess::exists(bundled_assembly_rootdir)) { assembly_rootdir = bundled_assembly_rootdir; + } + + if (DirAccess::exists(bundled_config_dir)) { config_dir = bundled_config_dir; } #ifdef WINDOWS_ENABLED if (assembly_rootdir.empty() || config_dir.empty()) { + ERR_PRINT("Cannot find Mono in the registry"); // Assertion: if they are not set, then they weren't found in the registry CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0); - - ERR_PRINT("Cannot find Mono in the registry"); } #endif // WINDOWS_ENABLED @@ -657,8 +660,6 @@ bool GDMono::_load_project_assembly() { if (success) { mono_assembly_set_main(project_assembly->get_assembly()); - - CSharpLanguage::get_singleton()->project_assembly_loaded(); } else { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load project assembly"); @@ -808,6 +809,8 @@ Error GDMono::_unload_scripts_domain() { mono_gc_collect(mono_gc_max_generation()); + GDMonoUtils::clear_godot_api_cache(); + _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); core_api_assembly = NULL; @@ -865,7 +868,7 @@ Error GDMono::reload_scripts_domain() { } } - CSharpLanguage::get_singleton()->_uninitialize_script_bindings(); + CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded(); Error err = _load_scripts_domain(); if (err != OK) { @@ -927,6 +930,7 @@ Error GDMono::reload_scripts_domain() { Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { CRASH_COND(p_domain == NULL); + CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead String domain_name = mono_domain_get_friendly_name(p_domain); @@ -1008,7 +1012,9 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) { if (ScriptDebugger::get_singleton()) ScriptDebugger::get_singleton()->idle_poll(); #endif - abort(); + + exit(mono_environment_exitcode_get()); + GD_UNREACHABLE(); } @@ -1077,8 +1083,6 @@ GDMono::~GDMono() { } assemblies.clear(); - GDMonoUtils::clear_cache(); - print_verbose("Mono: Runtime cleanup..."); mono_jit_cleanup(root_domain); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 8fec28b186..f1f0015ac9 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -46,11 +46,17 @@ bool GDMonoAssembly::in_preload = false; Vector<String> GDMonoAssembly::search_dirs; -void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config) { +void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) { - const char *rootdir = mono_assembly_getrootdir(); - if (rootdir) { - String framework_dir = String::utf8(rootdir).plus_file("mono").plus_file("4.5"); + String framework_dir; + + if (!p_custom_bcl_dir.empty()) { + framework_dir = p_custom_bcl_dir; + } else if (mono_assembly_getrootdir()) { + framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5"); + } + + if (!framework_dir.empty()) { r_search_dirs.push_back(framework_dir); r_search_dirs.push_back(framework_dir.plus_file("Facades")); } diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h index 32432af37d..39749dfc1d 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.h +++ b/modules/mono/mono_gd/gd_mono_assembly.h @@ -122,7 +122,7 @@ public: GDMonoClass *get_object_derived_class(const StringName &p_class); - static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String()); + static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String()); static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly); 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_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index c8cba5cf1b..63bcfe053c 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -111,7 +111,8 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { void unhandled_exception(MonoException *p_exc) { mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well - abort(); + // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders + GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL); GD_UNREACHABLE(); } diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index ec89df1959..a6e04e561d 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -37,6 +37,7 @@ #include "core/os/os.h" #include "../godotsharp_dirs.h" +#include "../utils/string_utils.h" static int log_level_get_id(const char *p_log_level) { @@ -72,8 +73,11 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con if (fatal) { ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n"); - // If we were to abort without flushing, the log wouldn't get written. + // Make sure to flush before aborting f->flush(); + f->close(); + memdelete(f); + abort(); } } @@ -93,17 +97,9 @@ bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { return true; } -void GDMonoLog::_open_log_file(const String &p_file_path) { - - log_file = FileAccess::open(p_file_path, FileAccess::WRITE); - - ERR_EXPLAIN("Failed to create log file"); - ERR_FAIL_COND(!log_file); -} - void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { - static const uint64_t MAX_SECS = 5 * 86400; + static const uint64_t MAX_SECS = 5 * 86400; // 5 days DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND(!da); @@ -120,10 +116,9 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { if (!current.ends_with(".txt")) continue; - String name = current.get_basename(); - uint64_t unixtime = (uint64_t)name.to_int64(); + uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current)); - if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) { + if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) { da->remove(current); } } @@ -133,26 +128,47 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { void GDMonoLog::initialize() { + CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); + + if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) { + ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): " + log_level.get_data()); + log_level = CharString(); + } + + if (log_level.length() == 0) { #ifdef DEBUG_ENABLED - const char *log_level = "debug"; + log_level = String("info").utf8(); #else - const char *log_level = "warning"; + log_level = String("warning").utf8(); #endif + } String logs_dir = GodotSharpDirs::get_mono_logs_dir(); if (_try_create_logs_dir(logs_dir)) { _delete_old_log_files(logs_dir); - log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt"); - _open_log_file(log_file_path); + OS::Date date_now = OS::get_singleton()->get_date(); + OS::Time time_now = OS::get_singleton()->get_time(); + int pid = OS::get_singleton()->get_process_id(); + + String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d (%d).txt", + date_now.year, date_now.month, date_now.day, + time_now.hour, time_now.min, time_now.sec, pid); + + log_file_path = logs_dir.plus_file(log_file_name); + + log_file = FileAccess::open(log_file_path, FileAccess::WRITE); + if (!log_file) { + ERR_PRINT("Mono: Cannot create log file"); + } } - mono_trace_set_level_string(log_level); - log_level_id = log_level_get_id(log_level); + mono_trace_set_level_string(log_level.get_data()); + log_level_id = log_level_get_id(log_level.get_data()); if (log_file) { - print_verbose("Mono: Logfile is " + log_file_path); + OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data()); mono_trace_set_log_handler(mono_log_callback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index a477e5edee..91d0557aa3 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -41,7 +41,6 @@ class GDMonoLog { String log_file_path; bool _try_create_logs_dir(const String &p_logs_dir); - void _open_log_file(const String &p_file_path); void _delete_old_log_files(const String &p_logs_dir); static GDMonoLog *singleton; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index de4f3650bd..87157ed233 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,52 +181,26 @@ 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; - - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes), - reftype, &key_reftype, &value_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - r_export_info->dictionary.key_type = managed_to_variant_type(ManagedType::from_reftype(key_reftype)); - r_export_info->dictionary.value_type = managed_to_variant_type(ManagedType::from_reftype(value_reftype)); - } - + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { 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 (is_array) { - if (r_export_info) { - MonoReflectionType *elem_reftype; - - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType), - reftype, &elem_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - r_export_info->array.element_type = managed_to_variant_type(ManagedType::from_reftype(elem_reftype)); - } - + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { return Variant::ARRAY; } + // 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 Variant::DICTIONARY; + if (p_type.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 (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return Variant::ARRAY; } @@ -228,6 +214,63 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e return Variant::NIL; } +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()); + + if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { + MonoReflectionType *elem_reftype; + + GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); + + r_elem_type = ManagedType::from_reftype(elem_reftype); + return true; + } + + 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; + } + + return false; +} + String mono_to_utf8_string(MonoString *p_mono_string) { MonoError error; char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error); @@ -494,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)); } @@ -595,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)); } @@ -779,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; } @@ -962,7 +1019,7 @@ MonoArray *PoolStringArray_to_mono_array(const PoolStringArray &p_array) { for (int i = 0; i < p_array.size(); i++) { MonoString *boxed = mono_string_from_godot(r[i]); - mono_array_set(ret, MonoString *, i, boxed); + mono_array_setref(ret, i, boxed); } return ret; @@ -1067,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 4a73f9e3e6..3fa958ac32 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -57,25 +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; - - ArrayInfo() : - element_type(Variant::NIL) {} - } array; - struct DictionaryInfo { - Variant::Type key_type; - Variant::Type value_type; - - 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..968b316a3e 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; } @@ -105,7 +109,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, for (int i = 0; i < params_count; i++) { MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]); - mono_array_set(params, MonoObject *, i, boxed_param); + mono_array_setref(params, i, boxed_param); } MonoException *exc = NULL; 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.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 5842e26241..f1da00638f 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -142,7 +142,7 @@ bool GDMonoProperty::has_setter() { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); - mono_array_set(params, MonoObject *, 0, p_value); + mono_array_setref(params, 0, p_value); MonoException *exc = NULL; GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); if (exc) { 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 429d4f68d9..1b32f1126e 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -50,6 +50,7 @@ MonoCache mono_cache; #define CACHE_AND_CHECK(m_var, m_val) \ { \ + CRASH_COND(m_var != NULL); \ m_var = m_val; \ if (!m_var) { \ ERR_EXPLAIN("Mono Cache: Member " #m_var " is null"); \ @@ -65,7 +66,9 @@ MonoCache mono_cache; #define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val) #define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.property_##m_class##_##m_property, m_val) -void MonoCache::clear_members() { +void MonoCache::clear_corlib_cache() { + + corlib_cache_updated = false; class_MonoObject = NULL; class_bool = NULL; @@ -93,6 +96,11 @@ void MonoCache::clear_members() { #endif class_KeyNotFoundException = NULL; +} + +void MonoCache::clear_godot_api_cache() { + + godot_api_cache_updated = false; rawclass_Dictionary = NULL; @@ -109,7 +117,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,20 +159,29 @@ 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; - task_scheduler_handle = Ref<MonoGCHandle>(); -} - -void MonoCache::cleanup() { + // End of MarshalUtils methods - corlib_cache_updated = false; - godot_api_cache_updated = false; + task_scheduler_handle = Ref<MonoGCHandle>(); } #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) @@ -217,7 +234,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 +275,27 @@ 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, 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, GenericIDictionaryIsAssignableFromType_with_info, (GenericIDictionaryIsAssignableFromType_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,17 +303,12 @@ 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; } -void clear_cache() { - mono_cache.cleanup(); - mono_cache.clear_members(); -} - MonoObject *unmanaged_get_managed(Object *unmanaged) { if (!unmanaged) @@ -372,11 +399,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 +464,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 +474,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 +486,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))); @@ -632,11 +658,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) { } void debug_unhandled_exception(MonoException *p_exc) { -#ifdef DEBUG_ENABLED - GDMonoUtils::debug_send_unhandled_exception_error(p_exc); - if (ScriptDebugger::get_singleton()) - ScriptDebugger::get_singleton()->idle_poll(); -#endif GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well GD_UNREACHABLE(); } @@ -732,4 +753,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..00e1ffdd31 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,26 +191,39 @@ 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; bool corlib_cache_updated; bool godot_api_cache_updated; - void clear_members(); - void cleanup(); + void clear_corlib_cache(); + void clear_godot_api_cache(); MonoCache() { - corlib_cache_updated = false; - godot_api_cache_updated = false; - - clear_members(); + clear_corlib_cache(); + clear_godot_api_cache(); } }; @@ -183,7 +231,13 @@ extern MonoCache mono_cache; void update_corlib_cache(); void update_godot_api_cache(); -void clear_cache(); + +inline void clear_corlib_cache() { + mono_cache.clear_corlib_cache(); +} +inline void clear_godot_api_cache() { + mono_cache.clear_godot_api_cache(); +} _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); @@ -205,7 +259,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/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index 5d37e8212f..0e1739b754 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -95,7 +95,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc for (int i = 0; i < signal_argc; i++) { MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]); - mono_array_set(signal_args, MonoObject *, i, boxed); + mono_array_setref(signal_args, i, boxed); } MonoException *exc = NULL; diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp index c390f8b9c2..877122985d 100644 --- a/modules/mono/utils/string_utils.cpp +++ b/modules/mono/utils/string_utils.cpp @@ -32,6 +32,9 @@ #include "core/os/file_access.h" +#include <stdio.h> +#include <stdlib.h> + namespace { int sfind(const String &p_text, int p_from) { @@ -63,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: @@ -184,3 +187,50 @@ Error read_all_file_utf8(const String &p_path, String &r_content) { r_content = source; return OK; } + +// TODO: Move to variadic templates once we upgrade to C++11 + +String str_format(const char *p_format, ...) { + va_list list; + + va_start(list, p_format); + String res = str_format(p_format, list); + va_end(list); + + return res; +} +// va_copy was defined in the C99, but not in C++ standards before C++11. +// When you compile C++ without --std=c++<XX> option, compilers still define +// va_copy, otherwise you have to use the internal version (__va_copy). +#if !defined(va_copy) +#if defined(__GNUC__) +#define va_copy(d, s) __va_copy((d), (s)) +#else +#define va_copy(d, s) ((d) = (s)) +#endif +#endif + +#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 + +String str_format(const char *p_format, va_list p_list) { + va_list list; + + va_copy(list, p_list); + int len = vsnprintf(NULL, 0, p_format, list); + va_end(list); + + len += 1; // for the trailing '/0' + + char *buffer(memnew_arr(char, len)); + + va_copy(list, p_list); + vsnprintf(buffer, len, p_format, list); + va_end(list); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h index 61765ccfd8..565b9bb644 100644 --- a/modules/mono/utils/string_utils.h +++ b/modules/mono/utils/string_utils.h @@ -34,6 +34,8 @@ #include "core/ustring.h" #include "core/variant.h" +#include <stdarg.h> + String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); #ifdef TOOLS_ENABLED @@ -44,4 +46,15 @@ String escape_csharp_keyword(const String &p_name); Error read_all_file_utf8(const String &p_path, String &r_content); +#if defined(__GNUC__) +#define _PRINTF_FORMAT_ATTRIBUTE_1_0 __attribute__((format(printf, 1, 0))) +#define _PRINTF_FORMAT_ATTRIBUTE_1_2 __attribute__((format(printf, 1, 2))) +#else +#define _PRINTF_FORMAT_ATTRIBUTE_1_0 +#define _PRINTF_FORMAT_ATTRIBUTE_1_2 +#endif + +String str_format(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2; +String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0; + #endif // STRING_FORMAT_H diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 765a9fc11a..6a72a519fe 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -14,7 +14,7 @@ if env['builtin_libogg']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_ogg.Append(CPPPATH=[thirdparty_dir]) + env_ogg.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_ogg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub index 4235f6a0b9..311d33b047 100644 --- a/modules/opensimplex/SCsub +++ b/modules/opensimplex/SCsub @@ -12,7 +12,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_opensimplex.Append(CPPPATH=[thirdparty_dir]) +env_opensimplex.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_opensimplex.Clone() env_thirdparty.disable_warnings() diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index 25f104b221..c789724fc4 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -9,13 +9,11 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap"> - If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map. + If [code]true[/code], the resulting texture contains a normal map created from the original noise interpreted as a bump map. </member> <member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength"> </member> diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index 06d22a2d65..dc7fc7fe31 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -23,8 +23,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_image"> <return type="Image"> diff --git a/modules/opus/SCsub b/modules/opus/SCsub index b72144c679..f3c981dd45 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -139,7 +139,7 @@ if env['builtin_opus']: opus_sources_silk = [] if env["platform"] in ["android", "iphone", "javascript"]: - env_opus.Append(CFLAGS=["-DFIXED_POINT"]) + env_opus.Append(CPPFLAGS=["-DFIXED_POINT"]) opus_sources_silk = [ "silk/fixed/LTP_analysis_filter_FIX.c", "silk/fixed/LTP_scale_ctrl_FIX.c", @@ -206,9 +206,9 @@ if env['builtin_opus']: # also requires libogg if env['builtin_libogg']: - env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_opus.Prepend(CPPPATH=["#thirdparty/libogg"]) - env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"]) + env_opus.Append(CPPFLAGS=["-DHAVE_CONFIG_H"]) thirdparty_include_paths = [ "", @@ -218,18 +218,18 @@ if env['builtin_opus']: "silk/fixed", "silk/float", ] - env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) + env_opus.Prepend(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) if env["platform"] == "android": if ("android_arch" in env and env["android_arch"] in ["armv6", "armv7"]): - env_opus.Append(CFLAGS=["-DOPUS_ARM_OPT"]) + env_opus.Append(CPPFLAGS=["-DOPUS_ARM_OPT"]) elif ("android_arch" in env and env["android_arch"] == "arm64v8"): - env_opus.Append(CFLAGS=["-DOPUS_ARM64_OPT"]) + env_opus.Append(CPPFLAGS=["-DOPUS_ARM64_OPT"]) elif env["platform"] == "iphone": if ("arch" in env and env["arch"] == "arm"): - env_opus.Append(CFLAGS=["-DOPUS_ARM_OPT"]) + env_opus.Append(CPPFLAGS=["-DOPUS_ARM_OPT"]) elif ("arch" in env and env["arch"] == "arm64"): - env_opus.Append(CFLAGS=["-DOPUS_ARM64_OPT"]) + env_opus.Append(CPPFLAGS=["-DOPUS_ARM64_OPT"]) env_thirdparty = env_opus.Clone() env_thirdparty.disable_warnings() diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub index 2e4a792a36..18da38fbbd 100644 --- a/modules/pvr/SCsub +++ b/modules/pvr/SCsub @@ -17,7 +17,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_pvr.Append(CPPPATH=[thirdparty_dir]) +env_pvr.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_pvr.Clone() env_thirdparty.disable_warnings() diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 82f323e8cf..8f6ffcc83f 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -192,9 +192,9 @@ static void _compress_pvrtc4(Image *p_img) { Ref<Image> img = p_img->duplicate(); bool make_mipmaps = false; - if (img->get_width() % 8 || img->get_height() % 8) { + if (!img->is_size_po2() || img->get_width() != img->get_height()) { make_mipmaps = img->has_mipmaps(); - img->resize(img->get_width() + (8 - (img->get_width() % 8)), img->get_height() + (8 - (img->get_height() % 8))); + img->resize_to_po2(true); } img->convert(Image::FORMAT_RGBA8); if (!img->has_mipmaps() && make_mipmaps) @@ -204,7 +204,7 @@ static void _compress_pvrtc4(Image *p_img) { Ref<Image> new_img; new_img.instance(); - new_img->create(img->get_width(), img->get_height(), true, use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); + new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4); PoolVector<uint8_t> data = new_img->get_data(); { @@ -221,7 +221,6 @@ static void _compress_pvrtc4(Image *p_img) { /* red and Green colors are swapped. */ new (dp) Javelin::ColorRgba<unsigned char>(r[ofs + 4 * j + 2], r[ofs + 4 * j + 1], r[ofs + 4 * j], r[ofs + 4 * j + 3]); } - new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h); Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm); } diff --git a/modules/recast/SCsub b/modules/recast/SCsub index 4a06653968..94d9968164 100644 --- a/modules/recast/SCsub +++ b/modules/recast/SCsub @@ -23,7 +23,7 @@ if env['builtin_recast']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"]) + env_recast.Prepend(CPPPATH=[thirdparty_dir + "/Include"]) env_thirdparty = env_recast.Clone() env_thirdparty.disable_warnings() diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index a068f3b0f9..eadc11fcee 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -54,18 +54,18 @@ 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); if (node) { node->update_gizmo(); @@ -75,7 +75,7 @@ void NavigationMeshEditor::_bake_pressed() { 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/SCsub b/modules/regex/SCsub index 99c25add45..65f354ffa7 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -46,7 +46,7 @@ if env['builtin_pcre2']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_regex.Append(CPPPATH=[thirdparty_dir]) + env_regex.Prepend(CPPPATH=[thirdparty_dir]) env_regex.Append(CPPFLAGS=thirdparty_flags) def pcre2_builtin(width): diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 20857572f3..cc8205e400 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -38,8 +38,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="clear"> <return type="void"> @@ -125,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<name>[/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/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 9efec91bdc..245201006d 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_end" qualifiers="const"> <return type="int"> diff --git a/modules/squish/SCsub b/modules/squish/SCsub index 3be85a1efa..15320bcd0c 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -22,7 +22,7 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_squish.Append(CPPPATH=[thirdparty_dir]) + env_squish.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_squish.Clone() env_thirdparty.disable_warnings() 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/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml index 574ff1ff2a..388c5e81ed 100644 --- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml index ade485e717..4dd77fed34 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/svg/SCsub b/modules/svg/SCsub index 22f0b1e3eb..90bfe22abb 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -12,11 +12,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_svg.Append(CPPPATH=[thirdparty_dir]) +env_svg.Prepend(CPPPATH=[thirdparty_dir]) # FIXME: Needed in editor/editor_themes.cpp for now, but ideally there # shouldn't be a dependency on modules/ and its own 3rd party deps. -env.Append(CPPPATH=[thirdparty_dir]) -env.Append(CCFLAGS=["-DSVG_ENABLED"]) +env.Prepend(CPPPATH=[thirdparty_dir]) +env.Append(CPPFLAGS=["-DSVG_ENABLED"]) env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index 419229677b..a3c0f5ded7 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -148,9 +148,11 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff uint8_t a = 0xff; if (p_header.color_map_depth == 24) { - r = (p_palette[(index * 3) + 0]); + // Due to low-high byte order, the color table must be + // read in the same order as image data (little endian) + r = (p_palette[(index * 3) + 2]); g = (p_palette[(index * 3) + 1]); - b = (p_palette[(index * 3) + 2]); + b = (p_palette[(index * 3) + 0]); } else { return ERR_INVALID_DATA; } diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub deleted file mode 100644 index c47c760d5f..0000000000 --- a/modules/thekla_unwrap/SCsub +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -import platform - -Import('env') -Import('env_modules') - -env_thekla_unwrap = env_modules.Clone() - -# Thirdparty source files -if env['builtin_thekla_atlas']: - thirdparty_dir = "#thirdparty/thekla_atlas/" - thirdparty_sources = [ - "nvcore/Memory.cpp", - "nvcore/Debug.cpp", - "nvcore/StrLib.cpp", - "nvcore/FileSystem.cpp", - "nvcore/RadixSort.cpp", - "nvmath/Basis.cpp", - "nvmath/ConvexHull.cpp", - "nvmath/Fitting.cpp", - "nvmath/Plane.cpp", - "nvmath/ProximityGrid.cpp", - "nvmath/Random.cpp", - "nvmath/Solver.cpp", - "nvmath/Sparse.cpp", - "nvmath/TypeSerialization.cpp", - "poshlib/posh.c", - "nvimage/BitMap.cpp", - "nvimage/Image.cpp", - "nvmesh/BaseMesh.cpp", - "nvmesh/MeshBuilder.cpp", - "nvmesh/TriMesh.cpp", - "nvmesh/QuadTriMesh.cpp", - "nvmesh/MeshTopology.cpp", - "nvmesh/halfedge/Edge.cpp", - "nvmesh/halfedge/Mesh.cpp", - "nvmesh/halfedge/Face.cpp", - "nvmesh/halfedge/Vertex.cpp", - "nvmesh/geometry/Bounds.cpp", - "nvmesh/geometry/Measurements.cpp", - "nvmesh/raster/Raster.cpp", - "nvmesh/param/Atlas.cpp", - "nvmesh/param/AtlasBuilder.cpp", - "nvmesh/param/AtlasPacker.cpp", - "nvmesh/param/LeastSquaresConformalMap.cpp", - "nvmesh/param/OrthogonalProjectionMap.cpp", - "nvmesh/param/ParameterizationQuality.cpp", - "nvmesh/param/SingleFaceMap.cpp", - "nvmesh/param/Util.cpp", - "nvmesh/weld/VertexWeld.cpp", - "nvmesh/weld/Snap.cpp", - "thekla/thekla_atlas.cpp" - ] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - - env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"]) - - # upstream uses c++11 - if (not env.msvc): - env_thekla_unwrap.Append(CXXFLAGS="-std=c++11") - - if env["platform"] == 'x11': - # if not specifically one of the *BSD, then use LINUX as default - if platform.system() == "FreeBSD": - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) - elif platform.system() == "OpenBSD": - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) - else: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'osx': - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'windows': - if env.msvc: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) - else: - env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) - env.Append(LIBS=["dbghelp"]) - - env_thirdparty = env_thekla_unwrap.Clone() - env_thirdparty.disable_warnings() - env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) - -# Godot source files -env_thekla_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py deleted file mode 100644 index fad6095064..0000000000 --- a/modules/thekla_unwrap/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - #return (env['tools'] and platform not in ["android", "ios"]) - return False - -def configure(env): - pass diff --git a/modules/thekla_unwrap/register_types.cpp b/modules/thekla_unwrap/register_types.cpp deleted file mode 100644 index 2c35adfb83..0000000000 --- a/modules/thekla_unwrap/register_types.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************/ -/* register_types.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 "register_types.h" -#include "core/error_macros.h" -#include "thirdparty/thekla_atlas/thekla/thekla_atlas.h" - -#include <stdio.h> -#include <stdlib.h> -extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); - -bool thekla_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { - - //set up input mesh - Thekla::Atlas_Input_Mesh input_mesh; - input_mesh.face_array = new Thekla::Atlas_Input_Face[p_index_count / 3]; - for (int i = 0; i < p_index_count / 3; i++) { - input_mesh.face_array[i].vertex_index[0] = p_indices[i * 3 + 0]; - input_mesh.face_array[i].vertex_index[1] = p_indices[i * 3 + 1]; - input_mesh.face_array[i].vertex_index[2] = p_indices[i * 3 + 2]; - //printf("face %i - %i, %i, %i - mat %i\n", i, input_mesh.face_array[i].vertex_index[0], input_mesh.face_array[i].vertex_index[1], input_mesh.face_array[i].vertex_index[2], p_face_materials[i]); - input_mesh.face_array[i].material_index = p_face_materials[i]; - } - input_mesh.vertex_array = new Thekla::Atlas_Input_Vertex[p_vertex_count]; - for (int i = 0; i < p_vertex_count; i++) { - input_mesh.vertex_array[i].first_colocal = i; //wtf - for (int j = 0; j < 3; j++) { - input_mesh.vertex_array[i].position[j] = p_vertices[i * 3 + j]; - input_mesh.vertex_array[i].normal[j] = p_normals[i * 3 + j]; - } - input_mesh.vertex_array[i].uv[0] = 0; - input_mesh.vertex_array[i].uv[1] = 0; - //printf("vertex %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].position[0], input_mesh.vertex_array[i].position[1], input_mesh.vertex_array[i].position[2]); - //printf("normal %i - %f, %f, %f\n", i, input_mesh.vertex_array[i].normal[0], input_mesh.vertex_array[i].normal[1], input_mesh.vertex_array[i].normal[2]); - } - input_mesh.face_count = p_index_count / 3; - input_mesh.vertex_count = p_vertex_count; - - //set up options - Thekla::Atlas_Options options; - Thekla::atlas_set_default_options(&options); - options.packer_options.witness.packing_quality = 1; - options.packer_options.witness.texel_area = 1.0 / p_texel_size; - options.packer_options.witness.conservative = false; - - //generate - Thekla::Atlas_Error err; - Thekla::Atlas_Output_Mesh *output = atlas_generate(&input_mesh, &options, &err); - - delete[] input_mesh.face_array; - delete[] input_mesh.vertex_array; - - if (output == NULL) { - ERR_PRINT("could not generate atlas output mesh"); - return false; - } - - if (err != Thekla::Atlas_Error_Success) { - printf("error with atlas\n"); - } else { - *r_vertex = (int *)malloc(sizeof(int) * output->vertex_count); - *r_uv = (float *)malloc(sizeof(float) * output->vertex_count * 3); - *r_index = (int *)malloc(sizeof(int) * output->index_count); - - // printf("w: %i, h: %i\n", output->atlas_width, output->atlas_height); - for (int i = 0; i < output->vertex_count; i++) { - (*r_vertex)[i] = output->vertex_array[i].xref; - (*r_uv)[i * 2 + 0] = output->vertex_array[i].uv[0] / output->atlas_width; - (*r_uv)[i * 2 + 1] = output->vertex_array[i].uv[1] / output->atlas_height; - // printf("uv: %f,%f\n", (*r_uv)[i * 2 + 0], (*r_uv)[i * 2 + 1]); - } - *r_vertex_count = output->vertex_count; - - for (int i = 0; i < output->index_count; i++) { - (*r_index)[i] = output->index_array[i]; - } - - *r_index_count = output->index_count; - - *r_size_hint_x = output->atlas_height; - *r_size_hint_y = output->atlas_width; - } - - if (output) { - atlas_free(output); - } - - return err == Thekla::Atlas_Error_Success; -} - -void register_thekla_unwrap_types() { - - array_mesh_lightmap_unwrap_callback = thekla_mesh_lightmap_unwrap_callback; -} - -void unregister_thekla_unwrap_types() { -} diff --git a/modules/theora/SCsub b/modules/theora/SCsub index 98c4274a7e..785eca4c41 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -66,17 +66,17 @@ if env['builtin_libtheora']: thirdparty_sources += thirdparty_sources_x86_vc if (env["x86_libtheora_opt_gcc"] or env["x86_libtheora_opt_vc"]): - env_theora.Append(CCFLAGS=["-DOC_X86_ASM"]) + env_theora.Append(CPPFLAGS=["-DOC_X86_ASM"]) thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_theora.Append(CPPPATH=[thirdparty_dir]) + env_theora.Prepend(CPPPATH=[thirdparty_dir]) # also requires libogg and libvorbis if env['builtin_libogg']: - env_theora.Append(CPPPATH=["#thirdparty/libogg"]) + env_theora.Prepend(CPPPATH=["#thirdparty/libogg"]) if env['builtin_libvorbis']: - env_theora.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_theora.Prepend(CPPPATH=["#thirdparty/libvorbis"]) env_thirdparty = env_theora.Clone() env_thirdparty.disable_warnings() diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index 2bd8ad862f..696101e252 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 14c5ddd7f2..ae542713ea 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -94,15 +94,15 @@ void VideoStreamPlaybackTheora::video_write(void) { if (px_fmt == TH_PF_444) { - yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); } else if (px_fmt == TH_PF_422) { - yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); } else if (px_fmt == TH_PF_420) { - yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[2].data, (uint8_t *)yuv[1].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2, 0); + yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2); }; format = Image::FORMAT_RGBA8; @@ -365,7 +365,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { float VideoStreamPlaybackTheora::get_time() const { - return time - AudioServer::get_singleton()->get_output_delay() - delay_compensation; //-((get_total())/(float)vi.rate); + return time - AudioServer::get_singleton()->get_output_latency() - delay_compensation; //-((get_total())/(float)vi.rate); }; Ref<Texture> VideoStreamPlaybackTheora::get_texture() { diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub index 3e7bda2bca..97f9797b58 100644 --- a/modules/tinyexr/SCsub +++ b/modules/tinyexr/SCsub @@ -13,7 +13,7 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_tinyexr.Append(CPPPATH=[thirdparty_dir]) +env_tinyexr.Prepend(CPPPATH=[thirdparty_dir]) env_thirdparty = env_tinyexr.Clone() env_thirdparty.disable_warnings() diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub index 2b15f7aee2..6d669ab73b 100644 --- a/modules/upnp/SCsub +++ b/modules/upnp/SCsub @@ -25,7 +25,7 @@ if env['builtin_miniupnpc']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_upnp.Append(CPPPATH=[thirdparty_dir]) + env_upnp.Prepend(CPPPATH=[thirdparty_dir]) env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"]) env_thirdparty = env_upnp.Clone() diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index 0f967c993b..0516ea9d54 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_device"> <return type="void"> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index c9b695a651..1d9a728365 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_port_mapping" qualifiers="const"> <return type="int"> diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub index 11cd5f4743..161aed2eb9 100644 --- a/modules/vhacd/SCsub +++ b/modules/vhacd/SCsub @@ -24,12 +24,12 @@ thirdparty_sources = [ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_vhacd.Append(CPPPATH=[thirdparty_dir+"/inc"]) +env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "/inc"]) env_vhacd.Append(CPPFLAGS=["-DGODOT_ENET"]) # upstream uses c++11 if not env.msvc: - env_vhacd.Append(CCFLAGS="-std=c++11") + env_vhacd.Append(CXXFLAGS="-std=c++11") env_thirdparty = env_vhacd.Clone() env_thirdparty.disable_warnings() diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index f4a9bc68e6..8f0d881f30 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -11,8 +11,6 @@ <tutorials> <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_signal"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml index ce49cdf3a0..b591dc9a51 100644 --- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 104a90059e..21e8a38c16 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> @@ -104,114 +102,117 @@ </constant> <constant name="MATH_RANGE_LERP" value="28" enum="BuiltinFunc"> </constant> - <constant name="MATH_DECTIME" value="29" enum="BuiltinFunc"> + <constant name="MATH_MOVE_TOWARD" value="29" enum="BuiltinFunc"> + Moves the number toward a value, based on the third input. + </constant> + <constant name="MATH_DECTIME" value="30" enum="BuiltinFunc"> Return the result of 'value' decreased by 'step' * 'amount'. </constant> - <constant name="MATH_RANDOMIZE" value="30" enum="BuiltinFunc"> + <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc"> Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. </constant> - <constant name="MATH_RAND" value="31" enum="BuiltinFunc"> + <constant name="MATH_RAND" value="32" enum="BuiltinFunc"> Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function. </constant> - <constant name="MATH_RANDF" value="32" enum="BuiltinFunc"> + <constant name="MATH_RANDF" value="33" enum="BuiltinFunc"> Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication. </constant> - <constant name="MATH_RANDOM" value="33" enum="BuiltinFunc"> + <constant name="MATH_RANDOM" value="34" enum="BuiltinFunc"> Return a random floating-point value between the two inputs. </constant> - <constant name="MATH_SEED" value="34" enum="BuiltinFunc"> + <constant name="MATH_SEED" value="35" enum="BuiltinFunc"> Set the seed for the random number generator. </constant> - <constant name="MATH_RANDSEED" value="35" enum="BuiltinFunc"> + <constant name="MATH_RANDSEED" value="36" enum="BuiltinFunc"> Return a random value from the given seed, along with the new seed. </constant> - <constant name="MATH_DEG2RAD" value="36" enum="BuiltinFunc"> + <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc"> Convert the input from degrees to radians. </constant> - <constant name="MATH_RAD2DEG" value="37" enum="BuiltinFunc"> + <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc"> Convert the input from radians to degrees. </constant> - <constant name="MATH_LINEAR2DB" value="38" enum="BuiltinFunc"> + <constant name="MATH_LINEAR2DB" value="39" enum="BuiltinFunc"> Convert the input from linear volume to decibel volume. </constant> - <constant name="MATH_DB2LINEAR" value="39" enum="BuiltinFunc"> + <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_POLAR2CARTESIAN" value="40" enum="BuiltinFunc"> + <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc"> Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (x and y axis). </constant> - <constant name="MATH_CARTESIAN2POLAR" value="41" enum="BuiltinFunc"> + <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc"> Converts a 2D point expressed in the cartesian coordinate system (x and y axis) to the polar coordinate system (a distance from the origin and an angle). </constant> - <constant name="MATH_WRAP" value="42" enum="BuiltinFunc"> + <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAPF" value="43" enum="BuiltinFunc"> + <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> </constant> - <constant name="LOGIC_MAX" value="44" enum="BuiltinFunc"> + <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="45" enum="BuiltinFunc"> + <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="46" enum="BuiltinFunc"> + <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc"> Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code]. </constant> - <constant name="LOGIC_NEAREST_PO2" value="47" enum="BuiltinFunc"> + <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="48" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="FUNC_FUNCREF" value="49" enum="BuiltinFunc"> + <constant name="FUNC_FUNCREF" value="50" enum="BuiltinFunc"> Create a [FuncRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="51" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="52" enum="BuiltinFunc"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="53" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="55" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="59" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> + <constant name="STR_TO_VAR" value="60" enum="BuiltinFunc"> Deserialize a [Variant] from a string serialized using [code]VAR_TO_STR[/code]. </constant> - <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="61" enum="BuiltinFunc"> Serialize a [Variant] to a [PoolByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> + <constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc"> Deserialize a [Variant] from a [PoolByteArray] serialized using [code]VAR_TO_BYTES[/code]. </constant> - <constant name="COLORN" value="62" enum="BuiltinFunc"> + <constant name="COLORN" value="63" enum="BuiltinFunc"> Return the [Color] with the given name and alpha ranging from 0 to 1. Note: names are defined in color_names.inc. </constant> - <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="64" enum="BuiltinFunc"> Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [code]MATH_LERP[/code], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: [codeblock] var t = clamp((weight - from) / (to - from), 0.0, 1.0) return t * t * (3.0 - 2.0 * t) [/codeblock] </constant> - <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> + <constant name="FUNC_MAX" value="65" enum="BuiltinFunc"> The maximum value the [member function] property can have. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml index 32b5924cdc..6e48e7c416 100644 --- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml index 990e0ecb85..9652654610 100644 --- a/modules/visual_script/doc_classes/VisualScriptComment.xml +++ b/modules/visual_script/doc_classes/VisualScriptComment.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml index 94c075205d..12d85429cf 100644 --- a/modules/visual_script/doc_classes/VisualScriptCondition.xml +++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml index 0fc4e87db4..b15af8e4ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index 05fc3f318d..8da5055d83 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_constructor" qualifiers="const"> <return type="Dictionary"> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 0ad4e7c1f5..1ab9f807fb 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_get_caption" qualifiers="virtual"> <return type="String"> @@ -145,7 +143,7 @@ </constant> <constant name="STEP_PUSH_STACK_BIT" value="16777216"> Hint used by [method _step] to tell that control should return to it when there is no other node left to execute. - This is used by [VisualScriptCondition] to redirect the sequence to the "Done" port after the true/false branch has finished execution. + This is used by [VisualScriptCondition] to redirect the sequence to the "Done" port after the [code]true[/code]/[code]false[/code] branch has finished execution. </constant> <constant name="STEP_GO_BACK_BIT" value="33554432"> Hint used by [method _step] to tell that control should return back, either hitting a previous STEP_PUSH_STACK_BIT or exiting the function. diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml index b933a25f1d..a0eed5b753 100644 --- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml +++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index be4606b57c..add2eb2275 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="add_custom_node"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml index 3282269811..623aa1ba86 100644 --- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml index 3e52fb818c..525389366a 100644 --- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml +++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml index 4760685bfb..eb6cdbb302 100644 --- a/modules/visual_script/doc_classes/VisualScriptExpression.xml +++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml index dc021196cd..152b48ca89 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunction.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml index e978437542..e4d0bb7672 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index a5e15b8da2..a8e820e6ea 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_signal"> <return type="void"> diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml index 2d609ed262..4099a351e6 100644 --- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml index 16499e9ec9..ba0dc1a3d0 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml index 06844ac4ae..09a8f2c8ad 100644 --- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml index 01887e0764..4bd1430cf6 100644 --- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml +++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml index 496c24dee4..782bbfae20 100644 --- a/modules/visual_script/doc_classes/VisualScriptIterator.xml +++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml @@ -15,8 +15,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml index cd7286b59b..5d061ea79f 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml index f8fe2f4b6b..049195d890 100644 --- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml index 733b48203d..430f9ee7d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml +++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index 86bd469d5c..89fb5c81f8 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_default_input_value" qualifiers="const"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml index d722477653..a0827972e0 100644 --- a/modules/visual_script/doc_classes/VisualScriptOperator.xml +++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml index b811252c42..b3b39691c9 100644 --- a/modules/visual_script/doc_classes/VisualScriptPreload.xml +++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml index 7f652b7012..697a31ca46 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml index ef9938c6a7..c41e3781d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml +++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml index 2a5c56cf69..40862b810b 100644 --- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml +++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml index 7daddc7639..40c8963efe 100644 --- a/modules/visual_script/doc_classes/VisualScriptReturn.xml +++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml @@ -13,8 +13,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml index 8604a0f5eb..81ef4ceedc 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml index 72a2faaa78..84718ba119 100644 --- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml +++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml index c87f77ea65..3b1e7f7f02 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelect.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml index 42c75e56a6..231e7d8f0d 100644 --- a/modules/visual_script/doc_classes/VisualScriptSelf.xml +++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml index c26c72dd50..26af15b404 100644 --- a/modules/visual_script/doc_classes/VisualScriptSequence.xml +++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 712b4ed09b..51b0093209 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="_subcall" qualifiers="virtual"> <return type="Variant"> diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml index 0053733b34..35929b16ac 100644 --- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml +++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml @@ -17,8 +17,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml index 85b3980e21..a9e920bca6 100644 --- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml +++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml index 27bf223aac..e1c7c4e4d4 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml @@ -12,8 +12,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml index c55c72d55e..7339482908 100644 --- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml +++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml @@ -13,8 +13,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml index b7ed56e7d2..77211fa088 100644 --- a/modules/visual_script/doc_classes/VisualScriptWhile.xml +++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml @@ -14,8 +14,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <constants> diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml index 5aa0f76a55..9f61a5f88c 100644 --- a/modules/visual_script/doc_classes/VisualScriptYield.xml +++ b/modules/visual_script/doc_classes/VisualScriptYield.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml index 8e3b2aec1d..6791375182 100644 --- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml +++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> </methods> <members> 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_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index d207656705..75b79f8929 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -68,6 +68,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "lerp", "inverse_lerp", "range_lerp", + "move_toward", "dectime", "randomize", "randi", @@ -206,6 +207,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_LERP: case MATH_INVERSE_LERP: case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: @@ -347,6 +349,14 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const else return PropertyInfo(Variant::REAL, "weight"); } break; + case MATH_MOVE_TOWARD: { + if (p_idx == 0) + return PropertyInfo(Variant::REAL, "from"); + else if (p_idx == 1) + return PropertyInfo(Variant::REAL, "to"); + else + return PropertyInfo(Variant::REAL, "delta"); + } break; case MATH_DECTIME: { if (p_idx == 0) return PropertyInfo(Variant::REAL, "value"); @@ -580,6 +590,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_INVERSE_LERP: case MATH_RANGE_LERP: case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: { t = Variant::REAL; @@ -916,6 +927,13 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(2); *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); } break; + case VisualScriptBuiltinFunc::MATH_MOVE_TOWARD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; case VisualScriptBuiltinFunc::MATH_DECTIME: { VALIDATE_ARG_NUM(0); @@ -1363,6 +1381,7 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_LERP); BIND_ENUM_CONSTANT(MATH_INVERSE_LERP); BIND_ENUM_CONSTANT(MATH_RANGE_LERP); + BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD); BIND_ENUM_CONSTANT(MATH_DECTIME); BIND_ENUM_CONSTANT(MATH_RANDOMIZE); BIND_ENUM_CONSTANT(MATH_RAND); @@ -1453,6 +1472,7 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>); + VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 50854c16b1..3e452cd94f 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -67,6 +67,7 @@ public: MATH_LERP, MATH_INVERSE_LERP, MATH_RANGE_LERP, + MATH_MOVE_TOWARD, MATH_DECTIME, MATH_RANDOMIZE, MATH_RAND, diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 5bd152d08d..f84f2c90cd 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -37,6 +37,7 @@ #include "core/variant.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" +#include "scene/main/viewport.h" #include "visual_script_expression.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" @@ -1329,8 +1330,9 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { } } -void VisualScriptEditor::_generic_search() { - new_connect_node_select->select_from_visual_script(String(""), false); +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(p_base_type, false); } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1785,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; } @@ -2040,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; } @@ -2099,6 +2100,14 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { } } +void VisualScriptEditor::set_executing_line(int p_line) { + // todo: add a way to show which node is executing right now. +} + +void VisualScriptEditor::clear_executing_line() { + // todo: add a way to show which node is executing right now. +} + void VisualScriptEditor::trim_trailing_whitespace() { } @@ -2212,7 +2221,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() { @@ -2720,93 +2729,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); - } 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 (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 (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { - vsfc->set_base_type(base_type); + } 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 (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())) { - - Ref<VisualScriptPropertySet> vsp = vsn; + // 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; - 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); @@ -3149,7 +3163,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: { @@ -3567,7 +3581,7 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("scroll_offset_changed", this, "_graph_ofs_changed"); select_func_text = memnew(Label); - select_func_text->set_text(TTR("Select or create a function to edit graph")); + select_func_text->set_text(TTR("Select or create a function to edit its graph.")); select_func_text->set_align(Label::ALIGN_CENTER); select_func_text->set_valign(Label::VALIGN_CENTER); select_func_text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -3608,7 +3622,6 @@ VisualScriptEditor::VisualScriptEditor() { edit_signal_dialog = memnew(AcceptDialog); edit_signal_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_signal_dialog); - edit_signal_dialog->set_title(TTR("Edit Signal Arguments:")); signal_editor = memnew(VisualScriptEditorSignalEdit); edit_signal_edit = memnew(EditorInspector); @@ -3619,7 +3632,6 @@ VisualScriptEditor::VisualScriptEditor() { edit_variable_dialog = memnew(AcceptDialog); edit_variable_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_variable_dialog); - edit_variable_dialog->set_title(TTR("Edit Variable:")); variable_editor = memnew(VisualScriptEditorVariableEdit); edit_variable_edit = memnew(EditorInspector); @@ -3630,7 +3642,6 @@ VisualScriptEditor::VisualScriptEditor() { select_base_type = memnew(CreateDialog); select_base_type->set_base_type("Object"); //anything goes select_base_type->connect("create", this, "_change_base_type_callback"); - select_base_type->get_ok()->set_text(TTR("Change")); add_child(select_base_type); undo_redo = EditorNode::get_singleton()->get_undo_redo(); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index ff9c23d507..6072e77342 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(); @@ -263,6 +263,8 @@ public: virtual Variant get_edit_state(); virtual void set_edit_state(const Variant &p_state); virtual void goto_line(int p_line, bool p_with_error = false); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void trim_trailing_whitespace(); virtual void insert_final_newline(); virtual void convert_indent_to_spaces(); 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/vorbis/SCsub b/modules/vorbis/SCsub index 19587563ab..3824fdd789 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -40,11 +40,11 @@ if env['builtin_libvorbis']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_vorbis.Append(CPPPATH=[thirdparty_dir]) + env_vorbis.Prepend(CPPPATH=[thirdparty_dir]) # also requires libogg if env['builtin_libogg']: - env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) + env_vorbis.Prepend(CPPPATH=["#thirdparty/libogg"]) env_thirdparty = env_vorbis.Clone() env_thirdparty.disable_warnings() diff --git a/modules/webm/SCsub b/modules/webm/SCsub index cb35b926ab..e57437229f 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -15,22 +15,22 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_webm.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) +env_webm.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) # upstream uses c++11 if (not env_webm.msvc): - env_webm.Append(CCFLAGS="-std=c++11") + env_webm.Append(CXXFLAGS="-std=c++11") # also requires libogg, libvorbis and libopus if env['builtin_libogg']: - env_webm.Append(CPPPATH=["#thirdparty/libogg"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libogg"]) if env['builtin_libvorbis']: - env_webm.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libvorbis"]) if env['builtin_opus']: - env_webm.Append(CPPPATH=["#thirdparty/opus"]) + env_webm.Prepend(CPPPATH=["#thirdparty/opus"]) if env['builtin_libvpx']: - env_webm.Append(CPPPATH=["#thirdparty/libvpx"]) + env_webm.Prepend(CPPPATH=["#thirdparty/libvpx"]) SConscript("libvpx/SCsub") env_thirdparty = env_webm.Clone() diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index 33dff0e93b..ff11bbb37d 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -6,8 +6,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_file"> <return type="String"> diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 2639d20620..a6be1380a6 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -256,7 +256,7 @@ libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_source env_libvpx = env_modules.Clone() env_libvpx.disable_warnings() -env_libvpx.Append(CPPPATH=[libvpx_dir]) +env_libvpx.Prepend(CPPPATH=[libvpx_dir]) webm_multithread = env["platform"] != 'javascript' @@ -323,7 +323,7 @@ if webm_cpu_x86: elif cpu_bits == '64': env_libvpx["ASCPU"] = 'X86_64' - env_libvpx.Append(CCFLAGS=['-DWEBM_X86ASM']) + env_libvpx.Append(CPPFLAGS=['-DWEBM_X86ASM']) webm_simd_optimizations = True @@ -337,7 +337,7 @@ if webm_cpu_arm: env_libvpx["ASFLAGS"] = '' env_libvpx["ASCOM"] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env_libvpx.Append(CCFLAGS=['-DWEBM_ARMASM']) + env_libvpx.Append(CPPFLAGS=['-DWEBM_ARMASM']) webm_simd_optimizations = True @@ -380,7 +380,7 @@ if webm_cpu_x86: elif webm_cpu_arm: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm) if env["platform"] == 'android': - env_libvpx.Append(CPPPATH=[libvpx_dir + "third_party/android"]) + env_libvpx.Prepend(CPPPATH=[libvpx_dir + "third_party/android"]) env_libvpx.add_source_files(env.modules_sources, [libvpx_dir + "third_party/android/cpu-features.c"]) env_libvpx_neon = env_libvpx.Clone() diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 06f9e39dc7..6485c95360 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -32,6 +32,7 @@ #include "OpusVorbisDecoder.hpp" #include "VPXDecoder.hpp" +#include <vpx/vpx_image.h> #include "mkvparser/mkvparser.h" @@ -314,19 +315,37 @@ void VideoStreamPlaybackWebm::update(float p_delta) { PoolVector<uint8_t>::Write w = frame_data.write(); bool converted = false; - if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { + if (image.chromaShiftW == 0 && image.chromaShiftH == 0 && image.cs == VPX_CS_SRGB) { + + uint8_t *wp = w.ptr(); + unsigned char *rRow = image.planes[2]; + unsigned char *gRow = image.planes[0]; + unsigned char *bRow = image.planes[1]; + for (int i = 0; i < image.h; i++) { + for (int j = 0; j < image.w; j++) { + *wp++ = rRow[j]; + *wp++ = gRow[j]; + *wp++ = bRow[j]; + *wp++ = 255; + } + rRow += image.linesize[2]; + gRow += image.linesize[0]; + bRow += image.linesize[1]; + } + converted = true; + } else if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { - yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { - yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { - yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2); // libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); converted = true; } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { @@ -375,7 +394,7 @@ int VideoStreamPlaybackWebm::get_mix_rate() const { inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { if (video_frames_pos > 0) { - const double audio_delay = AudioServer::get_singleton()->get_output_delay(); + const double audio_delay = AudioServer::get_singleton()->get_output_latency(); const double video_time = video_frames[video_frames_pos - 1]->time; return video_time >= time + audio_delay + delay_compensation; } @@ -383,7 +402,7 @@ inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { } bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) { - const double audio_delay = AudioServer::get_singleton()->get_output_delay(); + const double audio_delay = AudioServer::get_singleton()->get_output_latency(); return video_frame.time >= time + audio_delay + delay_compensation; } diff --git a/modules/webp/SCsub b/modules/webp/SCsub index fa3896c457..666628bb44 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -126,7 +126,7 @@ if env['builtin_libwebp']: ] thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources] - env_webp.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) + env_webp.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) env_thirdparty = env_webp.Clone() env_thirdparty.disable_warnings() diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub index 446bd530c2..868553b879 100644 --- a/modules/webrtc/SCsub +++ b/modules/webrtc/SCsub @@ -10,7 +10,6 @@ use_gdnative = env_webrtc["module_gdnative_enabled"] if use_gdnative: # GDNative is retained in Javascript for export compatibility env_webrtc.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) - gdnative_includes = ["#modules/gdnative/include/"] - env_webrtc.Append(CPPPATH=gdnative_includes) + env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"]) env_webrtc.add_source_files(env.modules_sources, "*.cpp") 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/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml new file mode 100644 index 0000000000..8b14c60deb --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_ice_candidate"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="media" type="String"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <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> + <description> + </description> + </method> + <method name="get_connection_state" qualifiers="const"> + <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> + <method name="poll"> + <return type="int" enum="Error"> + </return> + <description> + </description> + </method> + <method name="set_local_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_remote_description"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <signals> + <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"> + </argument> + <argument index="2" name="name" type="String"> + </argument> + <description> + </description> + </signal> + <signal name="session_description_created"> + <argument index="0" name="type" type="String"> + </argument> + <argument index="1" name="sdp" type="String"> + </argument> + <description> + </description> + </signal> + </signals> + <constants> + <constant name="STATE_NEW" value="0" enum="ConnectionState"> + </constant> + <constant name="STATE_CONNECTING" value="1" enum="ConnectionState"> + </constant> + <constant name="STATE_CONNECTED" value="2" enum="ConnectionState"> + </constant> + <constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState"> + </constant> + <constant name="STATE_FAILED" value="4" enum="ConnectionState"> + </constant> + <constant name="STATE_CLOSED" value="5" enum="ConnectionState"> + </constant> + </constants> +</class> 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/thekla_unwrap/register_types.h b/modules/webrtc/webrtc_peer_connection_js.h index b911eed622..43c0e3d6ee 100644 --- a/modules/thekla_unwrap/register_types.h +++ b/modules/webrtc/webrtc_peer_connection_js.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* register_types.h */ +/* webrtc_peer_connection_js.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,5 +28,39 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -void register_thekla_unwrap_types(); -void unregister_thekla_unwrap_types(); +#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 0345e533bc..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.Append(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.Append(CPPPATH=[helper_dir]) + env_lws.Prepend(CPPPATH=[thirdparty_dir + helper_dir]) if env["platform"] == "uwp": - env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) + env_lws.Append(CPPFLAGS=["/DLWS_MINGW_SUPPORT"]) env_thirdparty = env_lws.Clone() env_thirdparty.disable_warnings() - env_thirdparty.Append(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/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index cb85fe864d..26122dfad4 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -11,8 +11,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="connect_to_url"> <return type="int" enum="Error"> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 139480c31f..8548d21323 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -8,8 +8,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="get_peer" qualifiers="const"> <return type="WebSocketPeer"> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 3b3692dd87..8a6868d311 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -9,8 +9,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="close"> <return type="void"> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 4740bd6dcf..51945d410a 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -10,8 +10,6 @@ </description> <tutorials> </tutorials> - <demos> - </demos> <methods> <method name="disconnect_peer"> <return type="void"> 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) { diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub index ad364d5aaf..50e3cb1551 100644 --- a/modules/xatlas_unwrap/SCsub +++ b/modules/xatlas_unwrap/SCsub @@ -1,7 +1,5 @@ #!/usr/bin/env python -import platform - Import('env') Import('env_modules') @@ -15,29 +13,12 @@ if env['builtin_xatlas']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_xatlas_unwrap.Append(CPPPATH=[thirdparty_dir]) + env_xatlas_unwrap.Prepend(CPPPATH=[thirdparty_dir]) # upstream uses c++11 if (not env.msvc): env_xatlas_unwrap.Append(CXXFLAGS="-std=c++11") - if env["platform"] == 'x11': - # if not specifically one of the *BSD, then use LINUX as default - if platform.system() == "FreeBSD": - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) - elif platform.system() == "OpenBSD": - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) - else: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'osx': - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) - elif env["platform"] == 'windows': - if env.msvc: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) - else: - env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) - env.Append(LIBS=["dbghelp"]) - env_thirdparty = env_xatlas_unwrap.Clone() env_thirdparty.disable_warnings() env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py index 2dda5db7e0..bd092bdc16 100644 --- a/modules/xatlas_unwrap/config.py +++ b/modules/xatlas_unwrap/config.py @@ -1,5 +1,4 @@ def can_build(env, platform): - #return False #xatlas is buggy return (env['tools'] and platform not in ["android", "ios"]) def configure(env): |