diff options
Diffstat (limited to 'modules')
136 files changed, 2785 insertions, 401 deletions
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 16d694c238..2fe7a1b4c0 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -186,7 +186,11 @@ if env['builtin_bullet']: thirdparty_sources = [thirdparty_dir + file for file in bullet2_src] - env_bullet.Prepend(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(CPPFLAGS=['-DBT_DEBUG']) diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index be4e0b88e8..038001996d 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -267,7 +267,7 @@ RID BulletPhysicsServer::area_get_space(RID p_area) const { void BulletPhysicsServer::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) { AreaBullet *area = area_owner.get(p_area); - ERR_FAIL_COND(!area) + ERR_FAIL_COND(!area); area->set_spOv_mode(p_mode); } diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 0b8ad53658..4c598c84f2 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -45,7 +45,7 @@ */ class BulletPhysicsServer : public PhysicsServer { - GDCLASS(BulletPhysicsServer, PhysicsServer) + GDCLASS(BulletPhysicsServer, PhysicsServer); friend class BulletPhysicsDirectSpaceState; diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp index d9a82d6179..bc7fd52cf6 100644 --- a/modules/bullet/cone_twist_joint_bullet.cpp +++ b/modules/bullet/cone_twist_joint_bullet.cpp @@ -84,7 +84,7 @@ void ConeTwistJointBullet::set_param(PhysicsServer::ConeTwistJointParam p_param, break; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index 8fed933854..0d2c46c579 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -175,7 +175,7 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO break; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } @@ -256,7 +256,7 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF break; default: ERR_EXPLAIN("This flag " + itos(p_flag) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp index 7b99d3d89f..b7e1e1a4c2 100644 --- a/modules/bullet/hinge_joint_bullet.cpp +++ b/modules/bullet/hinge_joint_bullet.cpp @@ -118,7 +118,7 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t break; default: ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); - WARN_DEPRECATED + WARN_DEPRECATED; break; } } diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp index 58b090006a..c9c4d1af7e 100644 --- a/modules/bullet/pin_joint_bullet.cpp +++ b/modules/bullet/pin_joint_bullet.cpp @@ -86,7 +86,7 @@ real_t PinJointBullet::get_param(PhysicsServer::PinJointParam p_param) const { return p2pConstraint->m_setting.m_impulseClamp; default: ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); - WARN_DEPRECATED + WARN_DEPRECATED; return 0; } } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index 1e1bea846a..2c9bdb8b0b 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -54,7 +54,7 @@ class BulletPhysicsDirectBodyState; /// created by BulletPhysicsServer and is held by the "singleton" variable of this class /// Each time something require it, the body must be set again. class BulletPhysicsDirectBodyState : public PhysicsDirectBodyState { - GDCLASS(BulletPhysicsDirectBodyState, PhysicsDirectBodyState) + GDCLASS(BulletPhysicsDirectBodyState, PhysicsDirectBodyState); static BulletPhysicsDirectBodyState *singleton; diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 6b3d65edf6..eb4a065e54 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -68,7 +68,8 @@ class btGjkEpaPenetrationDepthSolver; extern ContactAddedCallback gContactAddedCallback; class BulletPhysicsDirectSpaceState : public PhysicsDirectSpaceState { - GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState) + GDCLASS(BulletPhysicsDirectSpaceState, PhysicsDirectSpaceState); + private: SpaceBullet *space; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 3db1f9e3e0..fd0d36eddf 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -45,7 +45,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo int vc = p_vertices.size(); - ERR_FAIL_COND((vc % 3) != 0) + ERR_FAIL_COND((vc % 3) != 0); PoolVector<Vector3>::Read rv = p_vertices.read(); int uvc = p_uvs.size(); diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index 0915d05111..d38dafc936 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -55,7 +55,8 @@ public: }; class EditorPluginCSG : public EditorPlugin { - GDCLASS(EditorPluginCSG, EditorPlugin) + GDCLASS(EditorPluginCSG, EditorPlugin); + public: EditorPluginCSG(EditorNode *p_editor); }; diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index a5b2238e6b..553a7553c6 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -116,9 +116,9 @@ protected: virtual void _validate_property(PropertyInfo &property) const; +public: Array get_meshes() const; -public: void set_operation(Operation p_operation); Operation get_operation() const; @@ -156,7 +156,8 @@ public: VARIANT_ENUM_CAST(CSGShape::Operation) class CSGCombiner : public CSGShape { - GDCLASS(CSGCombiner, CSGShape) + GDCLASS(CSGCombiner, CSGShape); + private: virtual CSGBrush *_build_brush(); @@ -165,7 +166,7 @@ public: }; class CSGPrimitive : public CSGShape { - GDCLASS(CSGPrimitive, CSGShape) + GDCLASS(CSGPrimitive, CSGShape); private: bool invert_faces; @@ -182,7 +183,7 @@ public: }; class CSGMesh : public CSGPrimitive { - GDCLASS(CSGMesh, CSGPrimitive) + GDCLASS(CSGMesh, CSGPrimitive); virtual CSGBrush *_build_brush(); @@ -204,7 +205,7 @@ public: class CSGSphere : public CSGPrimitive { - GDCLASS(CSGSphere, CSGPrimitive) + GDCLASS(CSGSphere, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -237,7 +238,7 @@ public: class CSGBox : public CSGPrimitive { - GDCLASS(CSGBox, CSGPrimitive) + GDCLASS(CSGBox, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -266,7 +267,7 @@ public: class CSGCylinder : public CSGPrimitive { - GDCLASS(CSGCylinder, CSGPrimitive) + GDCLASS(CSGCylinder, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -303,7 +304,7 @@ public: class CSGTorus : public CSGPrimitive { - GDCLASS(CSGTorus, CSGPrimitive) + GDCLASS(CSGTorus, CSGPrimitive); virtual CSGBrush *_build_brush(); Ref<Material> material; @@ -340,7 +341,7 @@ public: class CSGPolygon : public CSGPrimitive { - GDCLASS(CSGPolygon, CSGPrimitive) + GDCLASS(CSGPolygon, CSGPrimitive); public: enum Mode { diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 0a70ff535f..024e9ffc3b 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -145,7 +145,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS int h = p_image->get_height(); bool is_ldr = (p_image->get_format() <= Image::FORMAT_RGBA8); - bool is_hdr = (p_image->get_format() == Image::FORMAT_RGBH); + bool is_hdr = (p_image->get_format() >= Image::FORMAT_RH) && (p_image->get_format() <= Image::FORMAT_RGBE9995); if (!is_ldr && !is_hdr) { return; // Not a usable source format @@ -175,6 +175,10 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS bool is_signed = false; if (is_hdr) { + if (p_image->get_format() != Image::FORMAT_RGBH) { + p_image->convert(Image::FORMAT_RGBH); + } + PoolVector<uint8_t>::Read rb = p_image->get_data().read(); const uint16_t *source_data = reinterpret_cast<const uint16_t *>(&rb[0]); diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 059c06c37c..50fdc8ab20 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -31,6 +31,8 @@ #include "texture_loader_dds.h" #include "core/os/file_access.h" +#define PF_FOURCC(s) (((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])) + enum { DDS_MAGIC = 0x20534444, DDSD_CAPS = 0x00000001, @@ -51,6 +53,7 @@ enum DDSFormat { DDS_DXT5, DDS_ATI1, DDS_ATI2, + DDS_A2XY, DDS_BGRA8, DDS_BGR8, DDS_RGBA8, //flipped in dds @@ -74,9 +77,12 @@ struct DDSFormatInfo { }; static const DDSFormatInfo dds_format_info[DDS_MAX] = { - { "DXT1", true, false, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3", true, false, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5", true, false, 4, 16, Image::FORMAT_DXT5 }, + { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, + { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, + { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, + { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, + { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, + { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, @@ -158,22 +164,25 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, DDSFormat dds_format; - if (format_flags & DDPF_FOURCC && format_fourcc == 0x31545844) { //'1TXD' + if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { dds_format = DDS_DXT1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x33545844) { //'3TXD' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { dds_format = DDS_DXT3; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x35545844) { //'5TXD' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { dds_format = DDS_DXT5; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x31495441) { //'1ITA' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { dds_format = DDS_ATI1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == 0x32495441) { //'2ITA' + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { dds_format = DDS_ATI2; + } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { + + dds_format = DDS_A2XY; } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h index 585f2891bf..6ddef4e770 100644 --- a/modules/dds/texture_loader_dds.h +++ b/modules/dds/texture_loader_dds.h @@ -35,7 +35,6 @@ #include "scene/resources/texture.h" class ResourceFormatDDS : public ResourceFormatLoader { - GDCLASS(ResourceFormatDDS, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 18dfe08e85..dcb4b7fd75 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -80,6 +80,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER); ENetAddress address; + memset(&address, 0, sizeof(address)); #ifdef GODOT_ENET if (bind_ip.is_wildcard()) { @@ -346,7 +347,7 @@ void NetworkedMultiplayerENet::poll() { uint32_t *id = (uint32_t *)event.peer->data; - ERR_CONTINUE(event.packet->dataLength < 8) + ERR_CONTINUE(event.packet->dataLength < 8); uint32_t source = decode_uint32(&event.packet->data[0]); int target = decode_uint32(&event.packet->data[4]); @@ -462,7 +463,7 @@ void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) { ERR_FAIL_COND(!active); ERR_FAIL_COND(!is_server()); - ERR_FAIL_COND(!peer_map.has(p_peer)) + ERR_FAIL_COND(!peer_map.has(p_peer)); if (now) { enet_peer_disconnect_now(peer_map[p_peer], 0); diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index 957d0830cb..8dcb202314 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -38,7 +38,8 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer { - GDCLASS(NetworkedMultiplayerENet, NetworkedMultiplayerPeer) + GDCLASS(NetworkedMultiplayerENet, NetworkedMultiplayerPeer); + public: enum CompressionMode { COMPRESS_NONE, diff --git a/modules/etc/texture_loader_pkm.h b/modules/etc/texture_loader_pkm.h index 860fe8b5df..79c17953fc 100644 --- a/modules/etc/texture_loader_pkm.h +++ b/modules/etc/texture_loader_pkm.h @@ -35,7 +35,6 @@ #include "scene/resources/texture.h" class ResourceFormatPKM : public ResourceFormatLoader { - GDCLASS(ResourceFormatPKM, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/gdnative/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp index 8657935602..624ef19dec 100644 --- a/modules/gdnative/android/android_gdn.cpp +++ b/modules/gdnative/android/android_gdn.cpp @@ -34,6 +34,8 @@ // These entry points are only for the android platform and are simple stubs in all others. #ifdef __ANDROID__ +#include "platform/android/java_godot_wrapper.h" +#include "platform/android/os_android.h" #include "platform/android/thread_jandroid.h" #else #define JNIEnv void @@ -54,20 +56,31 @@ JNIEnv *GDAPI godot_android_get_env() { jobject GDAPI godot_android_get_activity() { #ifdef __ANDROID__ - JNIEnv *env = ThreadAndroid::get_env(); - - jclass activityThread = env->FindClass("android/app/ActivityThread"); - jmethodID currentActivityThread = env->GetStaticMethodID(activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;"); - jobject at = env->CallStaticObjectMethod(activityThread, currentActivityThread); - jmethodID getApplication = env->GetMethodID(activityThread, "getApplication", "()Landroid/app/Application;"); - jobject context = env->CallObjectMethod(at, getApplication); + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->get_activity(); +#else + return NULL; +#endif +} - return env->NewGlobalRef(context); +jobject GDAPI godot_android_get_surface() { +#ifdef __ANDROID__ + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->get_surface(); #else return NULL; #endif } +bool GDAPI godot_android_is_activity_resumed() { +#ifdef __ANDROID__ + OS_Android *os_android = (OS_Android *)OS::get_singleton(); + return os_android->get_godot_java()->is_activity_resumed(); +#else + return false; +#endif +} + #ifdef __cplusplus } #endif
\ No newline at end of file diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index c3f8688adb..da01f573ce 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -115,6 +115,17 @@ void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { interface->set_anchor_detection_is_enabled(data, p_enable); } +int ARVRInterfaceGDNative::get_camera_feed_id() { + + ERR_FAIL_COND_V(interface == NULL, 0); + + if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { + return (unsigned int)interface->get_camera_feed_id(data); + } else { + return 0; + } +} + bool ARVRInterfaceGDNative::is_stereo() { bool stereo; diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h index 86396b067a..651e5c8715 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ b/modules/gdnative/arvr/arvr_interface_gdnative.h @@ -41,7 +41,7 @@ */ class ARVRInterfaceGDNative : public ARVRInterface { - GDCLASS(ARVRInterfaceGDNative, ARVRInterface) + GDCLASS(ARVRInterfaceGDNative, ARVRInterface); void cleanup(); @@ -66,6 +66,7 @@ public: /** specific to AR **/ virtual bool get_anchor_detection_is_enabled() const; virtual void set_anchor_detection_is_enabled(bool p_enable); + virtual int get_camera_feed_id(); /** rendering and internal **/ virtual Size2 get_render_targetsize(); diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 7898de5523..b9e5afcdf3 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@NativeScript", "ARVRInterfaceGDNative", "GDNative", "GDNativeLibrary", @@ -13,7 +14,6 @@ def get_doc_classes(): "NativeScript", "PacketPeerGDNative", "PluginScript", - "ResourceFormatLoaderVideoStreamGDNative", "StreamPeerGDNative", "VideoStreamGDNative", "WebRTCPeerConnectionGDNative", diff --git a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml b/modules/gdnative/doc_classes/@NativeScript.xml index 4dd77fed34..cb5de198ac 100644 --- a/modules/stb_vorbis/doc_classes/ResourceImporterOGGVorbis.xml +++ b/modules/gdnative/doc_classes/@NativeScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterOGGVorbis" inherits="ResourceImporter" category="Core" version="3.2"> +<class name="@NativeScript" category="Core" version="3.2"> <brief_description> </brief_description> <description> diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index ef57387059..005d1d2bff 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -45,7 +45,7 @@ class GDNativeLibraryResourceLoader; class GDNative; class GDNativeLibrary : public Resource { - GDCLASS(GDNativeLibrary, Resource) + GDCLASS(GDNativeLibrary, Resource); static Map<String, Vector<Ref<GDNative> > > *loaded_libraries; @@ -137,7 +137,7 @@ struct GDNativeCallRegistry { }; class GDNative : public Reference { - GDCLASS(GDNative, Reference) + GDCLASS(GDNative, Reference); Ref<GDNativeLibrary> library; @@ -165,7 +165,6 @@ public: }; class GDNativeLibraryResourceLoader : public ResourceFormatLoader { - GDCLASS(GDNativeLibraryResourceLoader, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path, Error *r_error); virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -174,7 +173,6 @@ public: }; class GDNativeLibraryResourceSaver : public ResourceFormatSaver { - GDCLASS(GDNativeLibraryResourceSaver, ResourceFormatSaver) public: virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags); virtual bool recognize(const RES &p_resource) const; 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 8afe988102..6c12ee6534 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -26,6 +26,24 @@ ["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"] + ] } ] }, @@ -6286,7 +6304,7 @@ "type": "ANDROID", "version": { "major": 1, - "minor": 0 + "minor": 1 }, "next": null, "api": [ @@ -6301,6 +6319,18 @@ "return_type": "jobject", "arguments": [ ] + }, + { + "name": "godot_android_get_surface", + "return_type": "jobject", + "arguments": [ + ] + }, + { + "name": "godot_android_is_activity_resumed", + "return_type": "bool", + "arguments": [ + ] } ] }, diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h index 32e86838be..7063e1d2c5 100644 --- a/modules/gdnative/include/android/godot_android.h +++ b/modules/gdnative/include/android/godot_android.h @@ -46,6 +46,8 @@ extern "C" { JNIEnv *GDAPI godot_android_get_env(); jobject GDAPI godot_android_get_activity(); +jobject GDAPI godot_android_get_surface(); +bool GDAPI godot_android_is_activity_resumed(); #ifdef __cplusplus } diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h index 657090fa70..d465bbde54 100644 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ b/modules/gdnative/include/arvr/godot_arvr.h @@ -64,6 +64,7 @@ typedef struct { // only in 1.1 onwards godot_int (*get_external_texture_for_eye)(void *, godot_int); void (*notification)(void *, godot_int); + godot_int (*get_camera_feed_id)(void *); } godot_arvr_interface_gdnative; void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); 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/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/nativescript.h b/modules/gdnative/nativescript/nativescript.h index be1c499714..79a41df107 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -99,7 +99,7 @@ struct NativeScriptDesc { }; class NativeScript : public Script { - GDCLASS(NativeScript, Script) + GDCLASS(NativeScript, Script); #ifdef TOOLS_ENABLED Set<PlaceHolderScriptInstance *> placeholders; @@ -370,7 +370,7 @@ inline NativeScriptDesc *NativeScript::get_script_desc() const { } class NativeReloadNode : public Node { - GDCLASS(NativeReloadNode, Node) + GDCLASS(NativeReloadNode, Node); bool unloaded; public: @@ -382,7 +382,6 @@ public: }; class ResourceFormatLoaderNativeScript : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderNativeScript, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -391,7 +390,6 @@ public: }; class ResourceFormatSaverNativeScript : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverNativeScript, ResourceFormatSaver) virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); virtual bool recognize(const RES &p_resource) const; virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h index 7d48dc60d1..6fbc62aaa1 100644 --- a/modules/gdnative/net/multiplayer_peer_gdnative.h +++ b/modules/gdnative/net/multiplayer_peer_gdnative.h @@ -36,7 +36,7 @@ #include "modules/gdnative/include/net/godot_net.h" class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer { - GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer) + GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer); protected: static void _bind_methods(); diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h index 742fa4e7d5..8483217210 100644 --- a/modules/gdnative/net/packet_peer_gdnative.h +++ b/modules/gdnative/net/packet_peer_gdnative.h @@ -36,7 +36,7 @@ #include "modules/gdnative/include/net/godot_net.h" class PacketPeerGDNative : public PacketPeer { - GDCLASS(PacketPeerGDNative, PacketPeer) + GDCLASS(PacketPeerGDNative, PacketPeer); protected: static void _bind_methods(); 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/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h index 69a2ac6bfe..6218037a15 100644 --- a/modules/gdnative/pluginscript/pluginscript_loader.h +++ b/modules/gdnative/pluginscript/pluginscript_loader.h @@ -40,8 +40,6 @@ class PluginScriptLanguage; class ResourceFormatLoaderPluginScript : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderPluginScript, ResourceFormatLoader) - PluginScriptLanguage *_language; public: @@ -54,8 +52,6 @@ public: class ResourceFormatSaverPluginScript : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverPluginScript, ResourceFormatSaver) - PluginScriptLanguage *_language; public: diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp index 8dbbd2e4eb..1d6f9db349 100644 --- a/modules/gdnative/pluginscript/pluginscript_script.cpp +++ b/modules/gdnative/pluginscript/pluginscript_script.cpp @@ -39,12 +39,12 @@ #define ASSERT_SCRIPT_VALID() \ { \ ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ - ERR_FAIL_COND(!can_instance()) \ + ERR_FAIL_COND(!can_instance()); \ } -#define ASSERT_SCRIPT_VALID_V(ret) \ - { \ - ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ - ERR_FAIL_COND_V(!can_instance(), ret) \ +#define ASSERT_SCRIPT_VALID_V(ret) \ + { \ + ERR_EXPLAIN(__ASSERT_SCRIPT_REASON); \ + ERR_FAIL_COND_V(!can_instance(), ret); \ } #else #define ASSERT_SCRIPT_VALID() @@ -77,7 +77,7 @@ PluginScriptInstance *PluginScript::_create_instance(const Variant **p_args, int // There is currently no way to get the constructor function name of the script. // instance->call("__init__", p_args, p_argcount, r_error); if (p_argcount > 0) { - WARN_PRINT("PluginScript doesn't support arguments in the constructor") + WARN_PRINT("PluginScript doesn't support arguments in the constructor"); } return instance; diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h index aafd02f33d..b9f1c8e4da 100644 --- a/modules/gdnative/videodecoder/video_stream_gdnative.h +++ b/modules/gdnative/videodecoder/video_stream_gdnative.h @@ -197,7 +197,6 @@ public: }; class ResourceFormatLoaderVideoStreamGDNative : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderVideoStreamGDNative, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py index 95b40d90af..a525eedaaa 100644 --- a/modules/gdscript/config.py +++ b/modules/gdscript/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@GDScript", "GDScript", "GDScriptFunctionState", "GDScriptNativeClass", diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml new file mode 100644 index 0000000000..b6de5dbf62 --- /dev/null +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -0,0 +1,1301 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="@GDScript" category="Core" version="3.2"> + <brief_description> + Built-in GDScript functions. + </brief_description> + <description> + List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.) + </description> + <tutorials> + </tutorials> + <methods> + <method name="Color8"> + <return type="Color"> + </return> + <argument index="0" name="r8" type="int"> + </argument> + <argument index="1" name="g8" type="int"> + </argument> + <argument index="2" name="b8" type="int"> + </argument> + <argument index="3" name="a8" type="int" default="255"> + </argument> + <description> + Returns a 32 bit color with red, green, blue and alpha channels. Each channel has 8 bits of information ranging from 0 to 255. + [code]r8[/code] red channel + [code]g8[/code] green channel + [code]b8[/code] blue channel + [code]a8[/code] alpha channel + [codeblock] + red = Color8(255, 0, 0) + [/codeblock] + </description> + </method> + <method name="ColorN"> + <return type="Color"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="alpha" type="float" default="1.0"> + </argument> + <description> + Returns a color according to the standardised [code]name[/code] with [code]alpha[/code] ranging from 0 to 1. + [codeblock] + red = ColorN("red", 1) + [/codeblock] + Supported color names: + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflower", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "webgray", "green", "webgreen", "greenyellow", "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", "lightgoldenrod", "lightgray", "lightgreen", "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "webmaroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navyblue", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "webpurple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen". + </description> + </method> + <method name="abs"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). + [codeblock] + # a is 1 + a = abs(-1) + [/codeblock] + </description> + </method> + <method name="acos"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code]. + [codeblock] + # c is 0.523599 or 30 degrees if converted with rad2deg(s) + c = acos(0.866025) + [/codeblock] + </description> + </method> + <method name="asin"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code]. + [codeblock] + # s is 0.523599 or 30 degrees if converted with rad2deg(s) + s = asin(0.5) + [/codeblock] + </description> + </method> + <method name="assert"> + <return type="void"> + </return> + <argument index="0" name="condition" type="bool"> + </argument> + <description> + Asserts that the [code]condition[/code] is [code]true[/code] . If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development. + [codeblock] + # Imagine we always want speed to be between 0 and 20 + speed = -10 + assert(speed < 20) # True, the program will continue + assert(speed >= 0) # False, the program will stop + assert(speed >= 0 && speed < 20) # You can also combine the two conditional statements in one check + [/codeblock] + </description> + </method> + <method name="atan"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code]. + The method cannot know in which quadrant the angle should fall. See [method atan2] if you always want an exact angle. + [codeblock] + a = atan(0.5) # a is 0.463648 + [/codeblock] + </description> + </method> + <method name="atan2"> + <return type="float"> + </return> + <argument index="0" name="y" type="float"> + </argument> + <argument index="1" name="x" type="float"> + </argument> + <description> + Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant. + [codeblock] + a = atan2(0, -1) # a is 3.141593 + [/codeblock] + </description> + </method> + <method name="bytes2var"> + <return type="Variant"> + </return> + <argument index="0" name="bytes" type="PoolByteArray"> + </argument> + <argument index="1" name="allow_objects" type="bool" default="false"> + </argument> + <description> + Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed. + [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). + </description> + </method> + <method name="cartesian2polar"> + <return type="Vector2"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + 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). + </description> + </method> + <method name="ceil"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Rounds [code]s[/code] upward, returning the smallest integral value that is not less than [code]s[/code]. + [codeblock] + i = ceil(1.45) # i is 2 + i = ceil(1.001) # i is 2 + [/codeblock] + </description> + </method> + <method name="char"> + <return type="String"> + </return> + <argument index="0" name="ascii" type="int"> + </argument> + <description> + Returns a character as a String of the given ASCII code. + [codeblock] + # a is 'A' + a = char(65) + # a is 'a' + a = char(65 + 32) + [/codeblock] + </description> + </method> + <method name="clamp"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="min" type="float"> + </argument> + <argument index="2" name="max" type="float"> + </argument> + <description> + Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code]. + [codeblock] + speed = 1000 + # a is 20 + a = clamp(speed, 1, 20) + + speed = -10 + # a is 1 + a = clamp(speed, 1, 20) + [/codeblock] + </description> + </method> + <method name="convert"> + <return type="Variant"> + </return> + <argument index="0" name="what" type="Variant"> + </argument> + <argument index="1" name="type" type="int"> + </argument> + <description> + Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the enum TYPE_* in [@GlobalScope]. + [codeblock] + a = Vector2(1, 0) + # prints 1 + print(a.length()) + a = convert(a, TYPE_STRING) + # prints 6 + # (1, 0) is 6 characters + print(a.length()) + [/codeblock] + </description> + </method> + <method name="cos"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the cosine of angle [code]s[/code] in radians. + [codeblock] + # prints 1 and -1 + print(cos(PI * 2)) + print(cos(PI)) + [/codeblock] + </description> + </method> + <method name="cosh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic cosine of [code]s[/code] in radians. + [codeblock] + # prints 1.543081 + print(cosh(1)) + [/codeblock] + </description> + </method> + <method name="db2linear"> + <return type="float"> + </return> + <argument index="0" name="db" type="float"> + </argument> + <description> + Converts from decibels to linear energy (audio). + </description> + </method> + <method name="decimals"> + <return type="int"> + </return> + <argument index="0" name="step" type="float"> + </argument> + <description> + Deprecated alias for "[method step_decimals]". + </description> + </method> + <method name="dectime"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="amount" type="float"> + </argument> + <argument index="2" name="step" type="float"> + </argument> + <description> + Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code]. + [codeblock] + # a = 59 + a = dectime(60, 10, 0.1)) + [/codeblock] + </description> + </method> + <method name="deg2rad"> + <return type="float"> + </return> + <argument index="0" name="deg" type="float"> + </argument> + <description> + Returns degrees converted to radians. + [codeblock] + # r is 3.141593 + r = deg2rad(180) + [/codeblock] + </description> + </method> + <method name="dict2inst"> + <return type="Object"> + </return> + <argument index="0" name="dict" type="Dictionary"> + </argument> + <description> + Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing. + </description> + </method> + <method name="ease"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <argument index="1" name="curve" type="float"> + </argument> + <description> + Easing function, based on exponent. 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in. + </description> + </method> + <method name="exp"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it. + [b]e[/b] has an approximate value of 2.71828. + [codeblock] + a = exp(2) # approximately 7.39 + [/codeblock] + </description> + </method> + <method name="floor"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Rounds [code]s[/code] to the closest smaller integer and returns it. + [codeblock] + # a is 2 + a = floor(2.99) + # a is -3 + a = floor(-2.99) + [/codeblock] + </description> + </method> + <method name="fmod"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the floating-point remainder of [code]x/y[/code]. + [codeblock] + # remainder is 1.5 + var remainder = fmod(7, 5.5) + [/codeblock] + </description> + </method> + <method name="fposmod"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the floating-point remainder of [code]x/y[/code] that wraps equally in positive and negative. + [codeblock] + var i = -10 + while i < 0: + prints(i, fposmod(i, 10)) + i += 1 + [/codeblock] + Produces: + [codeblock] + -10 10 + -9 1 + -8 2 + -7 3 + -6 4 + -5 5 + -4 6 + -3 7 + -2 8 + -1 9 + [/codeblock] + </description> + </method> + <method name="funcref"> + <return type="FuncRef"> + </return> + <argument index="0" name="instance" type="Object"> + </argument> + <argument index="1" name="funcname" type="String"> + </argument> + <description> + Returns a reference to the specified function [code]funcname[/code] in the [code]instance[/code] node. As functions aren't first-class objects in GDscript, use [code]funcref[/code] to store a [FuncRef] in a variable and call it later. + [codeblock] + func foo(): + return("bar") + + a = funcref(self, "foo") + print(a.call_func()) # prints bar + [/codeblock] + </description> + </method> + <method name="get_stack"> + <return type="Array"> + </return> + <description> + Returns an array of dictionaries representing the current call stack. + [codeblock] + func _ready(): + foo() + + func foo(): + bar() + + func bar(): + print(get_stack()) + [/codeblock] + would print + [codeblock] + [{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}] + [/codeblock] + </description> + </method> + <method name="hash"> + <return type="int"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Returns the integer hash of the variable passed. + [codeblock] + print(hash("a")) # prints 177670 + [/codeblock] + </description> + </method> + <method name="inst2dict"> + <return type="Dictionary"> + </return> + <argument index="0" name="inst" type="Object"> + </argument> + <description> + Returns the passed instance converted to a dictionary (useful for serializing). + [codeblock] + var foo = "bar" + func _ready(): + var d = inst2dict(self) + print(d.keys()) + print(d.values()) + [/codeblock] + Prints out: + [codeblock] + [@subpath, @path, foo] + [, res://test.gd, bar] + [/codeblock] + </description> + </method> + <method name="instance_from_id"> + <return type="Object"> + </return> + <argument index="0" name="instance_id" type="int"> + </argument> + <description> + Returns the Object that corresponds to [code]instance_id[/code]. All Objects have a unique instance ID. + [codeblock] + var foo = "bar" + func _ready(): + var id = get_instance_id() + var inst = instance_from_id(id) + print(inst.foo) # prints bar + [/codeblock] + </description> + </method> + <method name="inverse_lerp"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Returns a normalized value considering the given range. + [codeblock] + inverse_lerp(3, 5, 4) # returns 0.5 + [/codeblock] + </description> + </method> + <method name="is_equal_approx"> + <return type="bool"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns True/False whether [code]a[/code] and [code]b[/code] are approximately equal to each other. + </description> + </method> + <method name="is_inf"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns whether [code]s[/code] is an infinity value (either positive infinity or negative infinity). + </description> + </method> + <method name="is_instance_valid"> + <return type="bool"> + </return> + <argument index="0" name="instance" type="Object"> + </argument> + <description> + Returns whether [code]instance[/code] is a valid object (e.g. has not been deleted from memory). + </description> + </method> + <method name="is_nan"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns whether [code]s[/code] is a NaN (Not-A-Number) value. + </description> + </method> + <method name="is_zero_approx"> + <return type="bool"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns True/False whether [code]s[/code] is zero or almost zero. + </description> + </method> + <method name="len"> + <return type="int"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc. + [b]Note:[/b] Generates a fatal error if Variant can not provide a length. + [codeblock] + a = [1, 2, 3, 4] + len(a) # returns 4 + [/codeblock] + </description> + </method> + <method name="lerp"> + <return type="Variant"> + </return> + <argument index="0" name="from" type="Variant"> + </argument> + <argument index="1" name="to" type="Variant"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Linearly interpolates between two values by a normalized value. + If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float]. + If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]linear_interpolate[/code] method). + [codeblock] + lerp(0, 4, 0.75) # returns 3.0 + lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # returns Vector2(2, 3.5) + [/codeblock] + </description> + </method> + <method name="linear2db"> + <return type="float"> + </return> + <argument index="0" name="nrg" type="float"> + </argument> + <description> + Converts from linear energy to decibels (audio). + </description> + </method> + <method name="load"> + <return type="Resource"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + Loads a resource from the filesystem located at [code]path[/code]. + [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". + [codeblock] + # load a scene called main located in the root of the project directory + var main = load("res://main.tscn") + [/codeblock] + </description> + </method> + <method name="log"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + [b]Note:[/b] This is not the same as the log function on your calculator which is a base 10 logarithm. + [codeblock] + log(10) # returns 2.302585 + [/codeblock] + </description> + </method> + <method name="max"> + <return type="float"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns the maximum of two values. + [codeblock] + max(1, 2) # returns 2 + max(-3.99, -4) # returns -3.99 + [/codeblock] + </description> + </method> + <method name="min"> + <return type="float"> + </return> + <argument index="0" name="a" type="float"> + </argument> + <argument index="1" name="b" type="float"> + </argument> + <description> + Returns the minimum of two values. + [codeblock] + min(1, 2) # returns 1 + min(-3.99, -4) # returns -4 + [/codeblock] + </description> + </method> + <method name="move_toward"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="delta" type="float"> + </argument> + <description> + Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value. + Use a negative [code]delta[/code] value to move away. + [codeblock] + move_toward(10, 5, 4) # returns 6 + [/codeblock] + </description> + </method> + <method name="nearest_po2"> + <return type="int"> + </return> + <argument index="0" name="value" type="int"> + </argument> + <description> + Returns the nearest larger power of 2 for integer [code]value[/code]. + [codeblock] + nearest_po2(3) # returns 4 + nearest_po2(4) # returns 4 + nearest_po2(5) # returns 8 + [/codeblock] + </description> + </method> + <method name="parse_json"> + <return type="Variant"> + </return> + <argument index="0" name="json" type="String"> + </argument> + <description> + Parse JSON text to a Variant (use [method typeof] to check if it is what you expect). + Be aware that the JSON specification does not define integer or float types, but only a number type. Therefore, parsing a JSON text will convert all numerical values to [float] types. + Note that JSON objects do not preserve key order like Godot dictionaries, thus you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements: + [codeblock] + p = parse_json('["a", "b", "c"]') + if typeof(p) == TYPE_ARRAY: + print(p[0]) # prints a + else: + print("unexpected results") + [/codeblock] + </description> + </method> + <method name="polar2cartesian"> + <return type="Vector2"> + </return> + <argument index="0" name="r" type="float"> + </argument> + <argument index="1" name="th" type="float"> + </argument> + <description> + 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). + </description> + </method> + <method name="pow"> + <return type="float"> + </return> + <argument index="0" name="x" type="float"> + </argument> + <argument index="1" name="y" type="float"> + </argument> + <description> + Returns the result of [code]x[/code] raised to the power of [code]y[/code]. + [codeblock] + pow(2, 5) # returns 32 + [/codeblock] + </description> + </method> + <method name="preload"> + <return type="Resource"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <description> + Returns a resource from the filesystem that is loaded during script parsing. + [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". + [codeblock] + # load a scene called main located in the root of the project directory + var main = preload("res://main.tscn") + [/codeblock] + </description> + </method> + <method name="print" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Converts one or more arguments to strings in the best way possible and prints them to the console. + [codeblock] + a = [1, 2, 3] + print("a", "b", a) # prints ab[1, 2, 3] + [/codeblock] + </description> + </method> + <method name="print_debug" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Like [method print], but prints only when used in debug mode. + </description> + </method> + <method name="print_stack"> + <return type="void"> + </return> + <description> + Prints a stack track at code location, only works when running with debugger turned on. + Output in the console would look something like this: + [codeblock] + Frame 0 - res://test.gd:16 in function '_process' + [/codeblock] + </description> + </method> + <method name="printerr" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to strings in the best way possible to standard error line. + [codeblock] + printerr("prints to stderr") + [/codeblock] + </description> + </method> + <method name="printraw" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to strings in the best way possible to console. No newline is added at the end. + [codeblock] + printraw("A") + printraw("B") + # prints AB + [/codeblock] + </description> + </method> + <method name="prints" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to the console with a space between each argument. + [codeblock] + prints("A", "B", "C") # prints A B C + [/codeblock] + </description> + </method> + <method name="printt" qualifiers="vararg"> + <return type="void"> + </return> + <description> + Prints one or more arguments to the console with a tab between each argument. + [codeblock] + printt("A", "B", "C") # prints A B C + [/codeblock] + </description> + </method> + <method name="push_error"> + <return type="void"> + </return> + <argument index="0" name="message" type="String"> + </argument> + <description> + Pushes an error message to Godot's built-in debugger and to the OS terminal. + [codeblock] + push_error("test error") # prints "test error" to debugger and terminal as error call + [/codeblock] + </description> + </method> + <method name="push_warning"> + <return type="void"> + </return> + <argument index="0" name="message" type="String"> + </argument> + <description> + Pushes a warning message to Godot's built-in debugger and to the OS terminal. + [codeblock] + push_warning("test warning") # prints "test warning" to debugger and terminal as warning call + [/codeblock] + </description> + </method> + <method name="rad2deg"> + <return type="float"> + </return> + <argument index="0" name="rad" type="float"> + </argument> + <description> + Converts from radians to degrees. + [codeblock] + rad2deg(0.523599) # returns 30 + [/codeblock] + </description> + </method> + <method name="rand_range"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <description> + Random range, any floating point value between [code]from[/code] and [code]to[/code]. + [codeblock] + prints(rand_range(0, 1), rand_range(0, 1)) # prints e.g. 0.135591 0.405263 + [/codeblock] + </description> + </method> + <method name="rand_seed"> + <return type="Array"> + </return> + <argument index="0" name="seed" type="int"> + </argument> + <description> + Random from seed: pass a [code]seed[/code], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits. + </description> + </method> + <method name="randf"> + <return type="float"> + </return> + <description> + Returns a random floating point value on the interval [code][0, 1][/code]. + [codeblock] + randf() # returns e.g. 0.375671 + [/codeblock] + </description> + </method> + <method name="randi"> + <return type="int"> + </return> + <description> + Returns a random unsigned 32 bit integer. Use remainder to obtain a random value in the interval [code][0, N][/code] (where N is smaller than 2^32 -1). + [codeblock] + randi() # returns random integer between 0 and 2^32 - 1 + randi() % 20 # returns random integer between 0 and 19 + randi() % 100 # returns random integer between 0 and 99 + randi() % 100 + 1 # returns random integer between 1 and 100 + [/codeblock] + </description> + </method> + <method name="randomize"> + <return type="void"> + </return> + <description> + Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. + [codeblock] + func _ready(): + randomize() + [/codeblock] + </description> + </method> + <method name="range" qualifiers="vararg"> + <return type="Array"> + </return> + <description> + Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment). + [codeblock] + for i in range(4): + print(i) + for i in range(2, 5): + print(i) + for i in range(0, 6, 2): + print(i) + [/codeblock] + Output: + [codeblock] + 0 + 1 + 2 + 3 + + 2 + 3 + 4 + + 0 + 2 + 4 + [/codeblock] + </description> + </method> + <method name="range_lerp"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="istart" type="float"> + </argument> + <argument index="2" name="istop" type="float"> + </argument> + <argument index="3" name="ostart" type="float"> + </argument> + <argument index="4" name="ostop" type="float"> + </argument> + <description> + Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. + [codeblock] + range_lerp(75, 0, 100, -1, 1) # returns 0.5 + [/codeblock] + </description> + </method> + <method name="round"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the integral value that is nearest to [code]s[/code], with halfway cases rounded away from zero. + [codeblock] + round(2.6) # returns 3 + [/codeblock] + </description> + </method> + <method name="seed"> + <return type="void"> + </return> + <argument index="0" name="seed" type="int"> + </argument> + <description> + Sets seed for the random number generator. + [codeblock] + my_seed = "Godot Rocks" + seed(my_seed.hash()) + [/codeblock] + </description> + </method> + <method name="sign"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the sign of [code]s[/code]: -1 or 1. Returns 0 if [code]s[/code] is 0. + [codeblock] + sign(-6) # returns -1 + sign(0) # returns 0 + sign(6) # returns 1 + [/codeblock] + </description> + </method> + <method name="sin"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the sine of angle [code]s[/code] in radians. + [codeblock] + sin(0.523599) # returns 0.5 + [/codeblock] + </description> + </method> + <method name="sinh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic sine of [code]s[/code]. + [codeblock] + a = log(2.0) # returns 0.693147 + sinh(a) # returns 0.75 + [/codeblock] + </description> + </method> + <method name="smoothstep"> + <return type="float"> + </return> + <argument index="0" name="from" type="float"> + </argument> + <argument index="1" name="to" type="float"> + </argument> + <argument index="2" name="weight" type="float"> + </argument> + <description> + Returns a number smoothly interpolated between the [code]from[/code] and [code]to[/code], based on the [code]weight[/code]. Similar to [method lerp], but interpolates faster at the beginning and slower at the end. + [codeblock] + smoothstep(0, 2, 0.5) # returns 0.15 + smoothstep(0, 2, 1.0) # returns 0.5 + smoothstep(0, 2, 2.0) # returns 1.0 + [/codeblock] + </description> + </method> + <method name="sqrt"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the square root of [code]s[/code]. + [codeblock] + sqrt(9) # returns 3 + [/codeblock] + </description> + </method> + <method name="step_decimals"> + <return type="int"> + </return> + <argument index="0" name="step" type="float"> + </argument> + <description> + Returns the position of the first non-zero digit, after the decimal point. + [codeblock] + # n is 0 + n = step_decimals(5) + # n is 4 + n = step_decimals(1.0005) + # n is 9 + n = step_decimals(0.000000005) + [/codeblock] + </description> + </method> + <method name="stepify"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <argument index="1" name="step" type="float"> + </argument> + <description> + Snaps float value [code]s[/code] to a given [code]step[/code]. + </description> + </method> + <method name="str" qualifiers="vararg"> + <return type="String"> + </return> + <description> + Converts one or more arguments to string in the best way possible. + [codeblock] + var a = [10, 20, 30] + var b = str(a); + len(a) # returns 3 + len(b) # returns 12 + [/codeblock] + </description> + </method> + <method name="str2var"> + <return type="Variant"> + </return> + <argument index="0" name="string" type="String"> + </argument> + <description> + Converts a formatted string that was returned by [method var2str] to the original value. + [codeblock] + a = '{ "a": 1, "b": 2 }' + b = str2var(a) + print(b['a']) # prints 1 + [/codeblock] + </description> + </method> + <method name="tan"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the tangent of angle [code]s[/code] in radians. + [codeblock] + tan(deg2rad(45)) # returns 1 + [/codeblock] + </description> + </method> + <method name="tanh"> + <return type="float"> + </return> + <argument index="0" name="s" type="float"> + </argument> + <description> + Returns the hyperbolic tangent of [code]s[/code]. + [codeblock] + a = log(2.0) # returns 0.693147 + tanh(a) # returns 0.6 + [/codeblock] + </description> + </method> + <method name="to_json"> + <return type="String"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Converts a Variant [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network. + [codeblock] + a = { 'a': 1, 'b': 2 } + b = to_json(a) + print(b) # {"a":1, "b":2} + [/codeblock] + </description> + </method> + <method name="type_exists"> + <return type="bool"> + </return> + <argument index="0" name="type" type="String"> + </argument> + <description> + Returns whether the given class exists in [ClassDB]. + [codeblock] + type_exists("Sprite") # returns true + type_exists("Variant") # returns false + [/codeblock] + </description> + </method> + <method name="typeof"> + <return type="int"> + </return> + <argument index="0" name="what" type="Variant"> + </argument> + <description> + Returns the internal type of the given Variant object, using the TYPE_* enum in [@GlobalScope]. + [codeblock] + p = parse_json('["a", "b", "c"]') + if typeof(p) == TYPE_ARRAY: + print(p[0]) # prints a + else: + print("unexpected results") + [/codeblock] + </description> + </method> + <method name="validate_json"> + <return type="String"> + </return> + <argument index="0" name="json" type="String"> + </argument> + <description> + Checks that [code]json[/code] is valid JSON data. Returns empty string if valid. Returns error message if not valid. + [codeblock] + j = to_json([1, 2, 3]) + v = validate_json(j) + if not v: + print("valid") + else: + prints("invalid", v) + [/codeblock] + </description> + </method> + <method name="var2bytes"> + <return type="PoolByteArray"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <argument index="1" name="full_objects" type="bool" default="false"> + </argument> + <description> + Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code). + </description> + </method> + <method name="var2str"> + <return type="String"> + </return> + <argument index="0" name="var" type="Variant"> + </argument> + <description> + Converts a Variant [code]var[/code] to a formatted string that can later be parsed using [method str2var]. + [codeblock] + a = { 'a': 1, 'b': 2 } + print(var2str(a)) + [/codeblock] + prints + [codeblock] + { + "a": 1, + "b": 2 + } + [/codeblock] + </description> + </method> + <method name="weakref"> + <return type="WeakRef"> + </return> + <argument index="0" name="obj" type="Object"> + </argument> + <description> + Returns a weak reference to an object. + A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it. + </description> + </method> + <method name="wrapf"> + <return type="float"> + </return> + <argument index="0" name="value" type="float"> + </argument> + <argument index="1" name="min" type="float"> + </argument> + <argument index="2" name="max" type="float"> + </argument> + <description> + Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code]. + Usable for creating loop-alike behavior or infinite surfaces. + [codeblock] + # a is 0.5 + a = wrapf(10.5, 0.0, 10.0) + [/codeblock] + [codeblock] + # a is 9.5 + a = wrapf(-0.5, 0.0, 10.0) + [/codeblock] + [codeblock] + # infinite loop between 0.0 and 0.99 + f = wrapf(f + 0.1, 0.0, 1.0) + [/codeblock] + </description> + </method> + <method name="wrapi"> + <return type="int"> + </return> + <argument index="0" name="value" type="int"> + </argument> + <argument index="1" name="min" type="int"> + </argument> + <argument index="2" name="max" type="int"> + </argument> + <description> + Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code]. + Usable for creating loop-alike behavior or infinite surfaces. + [codeblock] + # a is 0 + a = wrapi(10, 0, 10) + [/codeblock] + [codeblock] + # a is 9 + a = wrapi(-1, 0, 10) + [/codeblock] + [codeblock] + # infinite loop between 0 and 9 + frame = wrapi(frame + 1, 0, 10) + [/codeblock] + </description> + </method> + <method name="yield"> + <return type="GDScriptFunctionState"> + </return> + <argument index="0" name="object" type="Object" default="null"> + </argument> + <argument index="1" name="signal" type="String" default=""""> + </argument> + <description> + Stops the function execution and returns the current suspended state to the calling function. + From the caller, call [method GDScriptFunctionState.resume] on the state to resume execution. This invalidates the state. Within the resumed function, [code]yield()[/code] returns whatever was passed to the [code]resume()[/code] function call. + If passed an object and a signal, the execution is resumed when the object emits the given signal. In this case, [code]yield()[/code] returns the argument passed to [code]emit_signal()[/code] if the signal takes only one argument, or an array containing all the arguments passed to [code]emit_signal()[/code] if the signal takes multiple arguments. + </description> + </method> + </methods> + <constants> + <constant name="PI" value="3.141593"> + Constant that represents how many times the diameter of a circle fits around its perimeter. + </constant> + <constant name="TAU" value="6.283185"> + The circle constant, the circumference of the unit circle. + </constant> + <constant name="INF" value="inf"> + A positive infinity. (For negative infinity, use -INF). + </constant> + <constant name="NAN" value="nan"> + Macro constant that expands to an expression of type float that represents a NaN. + The NaN values are used to identify undefined or non-representable values for floating-point elements, such as the square root of negative numbers or the result of 0/0. + </constant> + </constants> +</class> diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 716f536e89..1756f6eabc 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -458,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; @@ -509,7 +509,6 @@ public: }; class ResourceFormatLoaderGDScript : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderGDScript, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -519,7 +518,6 @@ public: }; class ResourceFormatSaverGDScript : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverGDScript, ResourceFormatSaver) public: virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) 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 fa80c31984..6c77968f44 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2461,13 +2461,13 @@ static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDS 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; @@ -2478,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; @@ -2884,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; } @@ -3155,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)) { @@ -3197,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; @@ -3209,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_function.cpp b/modules/gdscript/gdscript_function.cpp index cff9ba55b8..bae5eca218 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -133,35 +133,13 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta return NULL; } -String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const { - - String err_text; - - if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { - int errorarg = p_err.argument; - err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + "."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { - err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { - err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { - err_text = "Invalid call. Nonexistent " + p_where + "."; - } else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { - err_text = "Attempt to call " + p_where + " on a null instance."; - } else { - err_text = "Bug, call error: #" + itos(p_err.error); - } - - return err_text; -} - #ifdef DEBUG_ENABLED -static String _get_var_type(const Variant *p_type) { +static String _get_var_type(const Variant *p_var) { String basestr; - if (p_type->get_type() == Variant::OBJECT) { - Object *bobj = *p_type; + if (p_var->get_type() == Variant::OBJECT) { + Object *bobj = *p_var; if (!bobj) { basestr = "null instance"; } else { @@ -176,12 +154,42 @@ static String _get_var_type(const Variant *p_type) { } } else { - basestr = Variant::get_type_name(p_type->get_type()); + basestr = Variant::get_type_name(p_var->get_type()); } return basestr; } -#endif +#endif // DEBUG_ENABLED + +String GDScriptFunction::_get_call_error(const Variant::CallError &p_err, const String &p_where, const Variant **argptrs) const { + + String err_text; + + if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { + int errorarg = p_err.argument; + // Handle the Object to Object case separately as we don't have further class details. +#ifdef DEBUG_ENABLED + if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) { + err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class."; + } else +#endif // DEBUG_ENABLED + { + err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(p_err.expected) + "."; + } + } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { + err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { + err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_INVALID_METHOD) { + err_text = "Invalid call. Nonexistent " + p_where + "."; + } else if (p_err.error == Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) { + err_text = "Attempt to call " + p_where + " on a null instance."; + } else { + err_text = "Bug, call error: #" + itos(p_err.error); + } + + return err_text; +} #if defined(__GNUC__) #define OPCODES_TABLE \ diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 5ebcddfd7c..0736f3d010 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -78,6 +78,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "inverse_lerp", "range_lerp", "smoothstep", + "move_toward", "dectime", "randomize", "randi", @@ -341,7 +342,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ 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 + WARN_DEPRECATED; } break; case MATH_STEP_DECIMALS: { VALIDATE_ARG_COUNT(1); @@ -398,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); @@ -1466,6 +1474,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { case MATH_INVERSE_LERP: case MATH_RANGE_LERP: case MATH_SMOOTHSTEP: + case MATH_MOVE_TOWARD: case MATH_DECTIME: case MATH_DEG2RAD: case MATH_RAD2DEG: @@ -1669,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 c594480ff8..6ad70f2eb4 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -69,6 +69,7 @@ public: 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 9590009a54..ec3e72eef7 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1995,7 +1995,6 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to } } - ERR_FAIL_V(op); } break; default: { return p_node; @@ -3497,7 +3496,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; } @@ -4025,7 +4024,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { ERR_EXPLAIN("Exporting bit flags hint requires string constants."); - WARN_DEPRECATED + WARN_DEPRECATED; break; } if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) { @@ -4068,6 +4067,50 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { break; } + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_RENDER") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 2D render hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_2D_RENDER; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_PHYSICS") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 2D physics hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_2D_PHYSICS; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 3D render hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_3D_RENDER; + break; + } + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_PHYSICS") { + + tokenizer->advance(); + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in layers 3D physics hint."); + return; + } + current_export.hint = PROPERTY_HINT_LAYERS_3D_PHYSICS; + break; + } + if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) { //enumeration current_export.hint = PROPERTY_HINT_ENUM; @@ -6021,7 +6064,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(); } diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 32a014e76d..3caa7b1d12 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -197,7 +197,7 @@ bool GridMap::get_collision_layer_bit(int p_bit) const { void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) { ERR_EXPLAIN("GridMap.theme/set_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/set_mesh_library() instead."); - WARN_DEPRECATED + WARN_DEPRECATED; set_mesh_library(p_theme); } @@ -205,7 +205,7 @@ void GridMap::set_theme(const Ref<MeshLibrary> &p_theme) { Ref<MeshLibrary> GridMap::get_theme() const { ERR_EXPLAIN("GridMap.theme/get_theme() is deprecated and will be removed in a future version. Use GridMap.mesh_library/get_mesh_library() instead."); - WARN_DEPRECATED + WARN_DEPRECATED; return get_mesh_library(); } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 890bd730f7..6b9a97efb4 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -664,8 +664,10 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu } if (mb->is_pressed()) { - - if (mb->get_button_index() == BUTTON_LEFT) { + SpatialEditorViewport::NavigationScheme nav_scheme = (SpatialEditorViewport::NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); + if ((nav_scheme == SpatialEditorViewport::NAVIGATION_MAYA || nav_scheme == SpatialEditorViewport::NAVIGATION_MODO) && mb->get_alt()) { + input_action = INPUT_NONE; + } else if (mb->get_button_index() == BUTTON_LEFT) { if (input_action == INPUT_PASTE) { _do_paste(); 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 4cfa2a5b93..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) @@ -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) @@ -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.h b/modules/mono/csharp_script.h index e735e0f741..64e3b40063 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -63,7 +63,7 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) { class CSharpScript : public Script { - GDCLASS(CSharpScript, Script) + GDCLASS(CSharpScript, Script); friend class CSharpInstance; friend class CSharpLanguage; @@ -380,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) {} @@ -441,7 +440,6 @@ public: }; class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderCSharpScript, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; @@ -450,7 +448,6 @@ public: }; class ResourceFormatSaverCSharpScript : public ResourceFormatSaver { - GDCLASS(ResourceFormatSaverCSharpScript, ResourceFormatSaver) public: virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; 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/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index cd7774e7a1..2d618f7891 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2536,13 +2536,8 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg switch (p_val.get_type()) { case Variant::NIL: - if (ClassDB::class_exists(r_iarg.type.cname)) { - // Object type - r_iarg.default_argument = "null"; - } else { - // Variant - r_iarg.default_argument = "null"; - } + // Either Object type or Variant + r_iarg.default_argument = "null"; break; // Atomic types case Variant::BOOL: diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 9f132825fb..a962d6df27 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -369,7 +369,11 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config, const Vect 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(";", "_") + ";"; @@ -379,7 +383,11 @@ bool GodotSharpBuilds::build_project_blocking(const String &p_config, const Vect 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)) { diff --git a/modules/mono/editor/godotsharp_editor.h b/modules/mono/editor/godotsharp_editor.h index d9523c384c..4a28492bad 100644 --- a/modules/mono/editor/godotsharp_editor.h +++ b/modules/mono/editor/godotsharp_editor.h @@ -35,7 +35,7 @@ #include "monodevelop_instance.h" class GodotSharpEditor : public Node { - GDCLASS(GodotSharpEditor, Object) + GDCLASS(GodotSharpEditor, Object); EditorNode *editor; @@ -109,7 +109,7 @@ public: }; class MonoReloadNode : public Node { - GDCLASS(MonoReloadNode, Node) + GDCLASS(MonoReloadNode, Node); Timer *reload_timer; diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index ae5b939b26..126178125f 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -125,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); } @@ -152,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++; } @@ -234,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.h b/modules/mono/editor/mono_bottom_panel.h index 406e46f7ce..9b362e51df 100644 --- a/modules/mono/editor/mono_bottom_panel.h +++ b/modules/mono/editor/mono_bottom_panel.h @@ -40,7 +40,7 @@ class MonoBuildTab; class MonoBottomPanel : public VBoxContainer { - GDCLASS(MonoBottomPanel, VBoxContainer) + GDCLASS(MonoBottomPanel, VBoxContainer); EditorNode *editor; @@ -87,7 +87,7 @@ public: class MonoBuildTab : public VBoxContainer { - GDCLASS(MonoBuildTab, VBoxContainer) + GDCLASS(MonoBuildTab, VBoxContainer); public: enum BuildResult { diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index 8fb8730b88..2d8c63fe7f 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -210,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/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index bb1950e1a8..a7f26283a7 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -186,6 +186,14 @@ 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 v = this; diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 283cb6341a..16803ae55c 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -190,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); diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp index 7385014a53..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++; } diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h index 63b61aff18..60a1eed212 100644 --- a/modules/mono/mono_gc_handle.h +++ b/modules/mono/mono_gc_handle.h @@ -37,7 +37,7 @@ class MonoGCHandle : public Reference { - GDCLASS(MonoGCHandle, Reference) + GDCLASS(MonoGCHandle, Reference); bool released; bool weak; diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 19e49d29f9..7699e0d0cd 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -251,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 @@ -807,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; @@ -926,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); @@ -1078,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.h b/modules/mono/mono_gd/gd_mono.h index 216c96a612..95340edcca 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -267,7 +267,7 @@ public: (void)__gdmono__scope__exit__domain__unload__; class _GodotSharp : public Object { - GDCLASS(_GodotSharp, Object) + GDCLASS(_GodotSharp, Object); friend class GDMono; 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_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index c462b8f71d..87157ed233 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -1019,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; diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index f290c6c8ac..968b316a3e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -109,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_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_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 413c8cba85..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; @@ -176,12 +184,6 @@ void MonoCache::clear_members() { task_scheduler_handle = Ref<MonoGCHandle>(); } -void MonoCache::cleanup() { - - corlib_cache_updated = false; - godot_api_cache_updated = false; -} - #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class)) #define GODOT_API_NS_CLAS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class)) @@ -281,13 +283,10 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, (GenericIEnumerableIsAssignableFromType_with_info)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, 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)); @@ -310,11 +309,6 @@ void update_godot_api_cache() { 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) diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index ee239be959..00e1ffdd31 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -218,14 +218,12 @@ struct MonoCache { 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(); } }; @@ -233,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); 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/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h index 098008ded7..4fb3cdb56d 100644 --- a/modules/mono/signal_awaiter_utils.h +++ b/modules/mono/signal_awaiter_utils.h @@ -41,7 +41,7 @@ Error connect_signal_awaiter(Object *p_source, const String &p_signal, Object *p class SignalAwaiterHandle : public MonoGCHandle { - GDCLASS(SignalAwaiterHandle, MonoGCHandle) + GDCLASS(SignalAwaiterHandle, MonoGCHandle); bool completed; diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index dc7fc7fe31..894a1b3ced 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -41,6 +41,8 @@ <argument index="0" name="x" type="float"> </argument> <description> + Returns the 1D noise value [code][-1,1][/code] at the given x-coordinate. + Note: This method actually returns the 2D noise value [code][-1,1][/code] with fixed y-coordinate value 0.0. </description> </method> <method name="get_noise_2d"> diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h index 0d00ee154d..445bf974b8 100644 --- a/modules/opensimplex/noise_texture.h +++ b/modules/opensimplex/noise_texture.h @@ -40,7 +40,7 @@ #include "editor/property_editor.h" class NoiseTexture : public Texture { - GDCLASS(NoiseTexture, Texture) + GDCLASS(NoiseTexture, Texture); private: Ref<Image> data; diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index 96885f5893..c31cbf68c3 100644 --- a/modules/opensimplex/open_simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -38,7 +38,7 @@ #include "thirdparty/misc/open-simplex-noise.h" class OpenSimplexNoise : public Resource { - GDCLASS(OpenSimplexNoise, Resource) + GDCLASS(OpenSimplexNoise, Resource); OBJ_SAVE_TYPE(OpenSimplexNoise); osn_context contexts[6]; diff --git a/modules/opus/SCsub b/modules/opus/SCsub index f3c981dd45..a4a431bab7 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -221,7 +221,7 @@ if env['builtin_opus']: 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"]): + if ("android_arch" in env and env["android_arch"] == "armv7"): env_opus.Append(CPPFLAGS=["-DOPUS_ARM_OPT"]) elif ("android_arch" in env and env["android_arch"] == "arm64v8"): env_opus.Append(CPPFLAGS=["-DOPUS_ARM64_OPT"]) diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp index fda82295de..70d0f770d8 100644 --- a/modules/opus/audio_stream_opus.cpp +++ b/modules/opus/audio_stream_opus.cpp @@ -313,7 +313,7 @@ int AudioStreamPlaybackOpus::mix(int16_t *p_buffer, int p_frames) { bool ok = op_pcm_seek(opus_file, (loop_restart_time * osrate) + pre_skip) == 0; if (!ok) { playing = false; - ERR_PRINT("loop restart time rejected") + ERR_PRINT("Loop restart time rejected"); } frames_mixed = (loop_restart_time * osrate) + pre_skip; diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h index f53bff0288..04c2e9d9f0 100644 --- a/modules/opus/audio_stream_opus.h +++ b/modules/opus/audio_stream_opus.h @@ -43,7 +43,7 @@ class AudioStreamPlaybackOpus : public AudioStreamPlayback { - GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback) + GDCLASS(AudioStreamPlaybackOpus, AudioStreamPlayback); enum { MIN_MIX = 1024 @@ -117,7 +117,7 @@ public: class AudioStreamOpus : public AudioStream { - GDCLASS(AudioStreamOpus, AudioStream) + GDCLASS(AudioStreamOpus, AudioStream); String file; @@ -132,7 +132,6 @@ public: }; class ResourceFormatLoaderAudioStreamOpus : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderAudioStreamOpus, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h index 2808b4ff03..606268e447 100644 --- a/modules/pvr/texture_loader_pvr.h +++ b/modules/pvr/texture_loader_pvr.h @@ -35,7 +35,6 @@ #include "scene/resources/texture.h" class ResourceFormatPVR : public ResourceFormatLoader { - GDCLASS(ResourceFormatPVR, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index eadc11fcee..9f30806925 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -67,9 +67,7 @@ void NavigationMeshEditor::_bake_pressed() { EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node); - if (node) { - node->update_gizmo(); - } + node->update_gizmo(); } void NavigationMeshEditor::_clear_pressed() { diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index 0cac07e3e7..14467dc5c7 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -45,6 +45,10 @@ #include "scene/resources/shape.h" #include "scene/resources/sphere_shape.h" +#ifdef MODULE_CSG_ENABLED +#include "modules/csg/csg_shape.h" +#endif + EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL; void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) { @@ -134,6 +138,20 @@ void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_tran } } +#ifdef MODULE_CSG_ENABLED + if (Object::cast_to<CSGShape>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { + + CSGShape *csg_shape = Object::cast_to<CSGShape>(p_node); + Array meshes = csg_shape->get_meshes(); + if (!meshes.empty()) { + Ref<Mesh> mesh = meshes[1]; + if (mesh.is_valid()) { + _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices); + } + } + } +#endif + if (Object::cast_to<StaticBody>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) { StaticBody *static_body = Object::cast_to<StaticBody>(p_node); diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index cbff9d47bb..c8c179554a 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -40,7 +40,7 @@ class AudioStreamOGGVorbis; class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { - GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled) + GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled); stb_vorbis *ogg_stream; stb_vorbis_alloc ogg_alloc; @@ -72,7 +72,7 @@ public: class AudioStreamOGGVorbis : public AudioStream { - GDCLASS(AudioStreamOGGVorbis, AudioStream) + GDCLASS(AudioStreamOGGVorbis, AudioStream); OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged RES_BASE_EXTENSION("oggstr"); diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py index d75e41797a..200b8dfd50 100644 --- a/modules/stb_vorbis/config.py +++ b/modules/stb_vorbis/config.py @@ -7,7 +7,6 @@ def configure(env): def get_doc_classes(): return [ "AudioStreamOGGVorbis", - "ResourceImporterOGGVorbis", ] def get_doc_path(): diff --git a/modules/stb_vorbis/register_types.cpp b/modules/stb_vorbis/register_types.cpp index 88a1766e47..ce64626e4b 100644 --- a/modules/stb_vorbis/register_types.cpp +++ b/modules/stb_vorbis/register_types.cpp @@ -29,15 +29,22 @@ /*************************************************************************/ #include "register_types.h" + #include "audio_stream_ogg_vorbis.h" + +#ifdef TOOLS_ENABLED +#include "core/engine.h" #include "resource_importer_ogg_vorbis.h" +#endif void register_stb_vorbis_types() { #ifdef TOOLS_ENABLED - Ref<ResourceImporterOGGVorbis> ogg_import; - ogg_import.instance(); - ResourceFormatImporter::get_singleton()->add_importer(ogg_import); + if (Engine::get_singleton()->is_editor_hint()) { + Ref<ResourceImporterOGGVorbis> ogg_import; + ogg_import.instance(); + ResourceFormatImporter::get_singleton()->add_importer(ogg_import); + } #endif ClassDB::register_class<AudioStreamOGGVorbis>(); } diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/stb_vorbis/resource_importer_ogg_vorbis.h index d3d0574d56..8e4fbde916 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.h @@ -35,7 +35,8 @@ #include "core/io/resource_importer.h" class ResourceImporterOGGVorbis : public ResourceImporter { - GDCLASS(ResourceImporterOGGVorbis, ResourceImporter) + GDCLASS(ResourceImporterOGGVorbis, ResourceImporter); + public: virtual String get_importer_name() const; virtual String get_visible_name() const; diff --git a/modules/theora/config.py b/modules/theora/config.py index 7504166237..c7713d7607 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -6,7 +6,6 @@ def configure(env): def get_doc_classes(): return [ - "ResourceImporterTheora", "VideoStreamTheora", ] diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 85d73d3c0d..0c37d33358 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -186,7 +186,6 @@ public: }; class ResourceFormatLoaderTheora : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderTheora, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index bd84a28c84..a9340b1498 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -122,13 +122,13 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f } if (idxG == -1) { - ERR_PRINT("TinyEXR: G channel not found.") + ERR_PRINT("TinyEXR: G channel not found."); // @todo { free exr_image } return ERR_FILE_CORRUPT; } if (idxB == -1) { - ERR_PRINT("TinyEXR: B channel not found.") + ERR_PRINT("TinyEXR: B channel not found."); // @todo { free exr_image } return ERR_FILE_CORRUPT; } diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index 07a0450734..04e1a40b81 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -6,6 +6,7 @@ def configure(env): def get_doc_classes(): return [ + "@VisualScript", "VisualScriptBasicTypeConstant", "VisualScriptBuiltinFunc", "VisualScriptClassConstant", diff --git a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml b/modules/visual_script/doc_classes/@VisualScript.xml index cd8b336778..8d9408e6d4 100644 --- a/modules/gdnative/doc_classes/ResourceFormatLoaderVideoStreamGDNative.xml +++ b/modules/visual_script/doc_classes/@VisualScript.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceFormatLoaderVideoStreamGDNative" inherits="ResourceFormatLoader" category="Core" version="3.2"> +<class name="@VisualScript" category="Core" version="3.2"> <brief_description> + Built-in visual script functions. </brief_description> <description> + A list of built-in visual script functions, see [VisualScriptBuiltinFunc] and [VisualScript]. </description> <tutorials> </tutorials> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 74d5f29c02..21e8a38c16 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -102,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/visual_script.h b/modules/visual_script/visual_script.h index 89f32f54f7..098c28370d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -39,7 +39,7 @@ class VisualScriptNodeInstance; class VisualScript; class VisualScriptNode : public Resource { - GDCLASS(VisualScriptNode, Resource) + GDCLASS(VisualScriptNode, Resource); friend class VisualScript; @@ -165,7 +165,7 @@ public: class VisualScript : public Script { - GDCLASS(VisualScript, Script) + GDCLASS(VisualScript, Script); RES_BASE_EXTENSION("vs"); 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..f009f49b5b 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -35,7 +35,8 @@ class VisualScriptBuiltinFunc : public VisualScriptNode { - GDCLASS(VisualScriptBuiltinFunc, VisualScriptNode) + GDCLASS(VisualScriptBuiltinFunc, VisualScriptNode); + public: enum BuiltinFunc { MATH_SIN, @@ -67,6 +68,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 6bbfb1ec5c..3ac170a935 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -46,7 +46,7 @@ #ifdef TOOLS_ENABLED class VisualScriptEditorSignalEdit : public Object { - GDCLASS(VisualScriptEditorSignalEdit, Object) + GDCLASS(VisualScriptEditorSignalEdit, Object); StringName sig; @@ -186,7 +186,7 @@ public: class VisualScriptEditorVariableEdit : public Object { - GDCLASS(VisualScriptEditorVariableEdit, Object) + GDCLASS(VisualScriptEditorVariableEdit, Object); StringName var; @@ -2111,6 +2111,9 @@ void VisualScriptEditor::clear_executing_line() { void VisualScriptEditor::trim_trailing_whitespace() { } +void VisualScriptEditor::insert_final_newline() { +} + void VisualScriptEditor::convert_indent_to_spaces() { } @@ -3619,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); @@ -3630,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); @@ -3641,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(); @@ -3759,4 +3759,7 @@ void _VisualScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &_VisualScriptEditor::remove_custom_node); ADD_SIGNAL(MethodInfo("custom_nodes_updated")); } + +void VisualScriptEditor::validate() { +} #endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 3d3a49f672..4f302d1d72 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -44,7 +44,7 @@ class VisualScriptEditorVariableEdit; #ifdef TOOLS_ENABLED class VisualScriptEditor : public ScriptEditorBase { - GDCLASS(VisualScriptEditor, ScriptEditorBase) + GDCLASS(VisualScriptEditor, ScriptEditorBase); enum { TYPE_SEQUENCE = 1000, @@ -266,6 +266,7 @@ public: 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(); virtual void convert_indent_to_tabs(); virtual void ensure_focus(); @@ -280,6 +281,7 @@ public: virtual Control *get_edit_menu(); virtual void clear_edit_menu(); virtual bool can_lose_focus_on_node_selection() { return false; } + virtual void validate(); static void register_editor(); diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h index 3b2e3b9f78..3a045d2cf7 100644 --- a/modules/visual_script/visual_script_expression.h +++ b/modules/visual_script/visual_script_expression.h @@ -36,7 +36,7 @@ class VisualScriptExpression : public VisualScriptNode { - GDCLASS(VisualScriptExpression, VisualScriptNode) + GDCLASS(VisualScriptExpression, VisualScriptNode); friend class VisualScriptNodeInstanceExpression; struct Input { diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h index a42605a50e..d3c7aa68e3 100644 --- a/modules/visual_script/visual_script_flow_control.h +++ b/modules/visual_script/visual_script_flow_control.h @@ -35,7 +35,7 @@ class VisualScriptReturn : public VisualScriptNode { - GDCLASS(VisualScriptReturn, VisualScriptNode) + GDCLASS(VisualScriptReturn, VisualScriptNode); Variant::Type type; bool with_value; @@ -72,7 +72,7 @@ public: class VisualScriptCondition : public VisualScriptNode { - GDCLASS(VisualScriptCondition, VisualScriptNode) + GDCLASS(VisualScriptCondition, VisualScriptNode); protected: static void _bind_methods(); @@ -100,7 +100,7 @@ public: class VisualScriptWhile : public VisualScriptNode { - GDCLASS(VisualScriptWhile, VisualScriptNode) + GDCLASS(VisualScriptWhile, VisualScriptNode); protected: static void _bind_methods(); @@ -128,7 +128,7 @@ public: class VisualScriptIterator : public VisualScriptNode { - GDCLASS(VisualScriptIterator, VisualScriptNode) + GDCLASS(VisualScriptIterator, VisualScriptNode); protected: static void _bind_methods(); @@ -156,7 +156,7 @@ public: class VisualScriptSequence : public VisualScriptNode { - GDCLASS(VisualScriptSequence, VisualScriptNode) + GDCLASS(VisualScriptSequence, VisualScriptNode); int steps; @@ -189,7 +189,7 @@ public: class VisualScriptSwitch : public VisualScriptNode { - GDCLASS(VisualScriptSwitch, VisualScriptNode) + GDCLASS(VisualScriptSwitch, VisualScriptNode); struct Case { Variant::Type type; @@ -231,7 +231,7 @@ public: class VisualScriptTypeCast : public VisualScriptNode { - GDCLASS(VisualScriptTypeCast, VisualScriptNode) + GDCLASS(VisualScriptTypeCast, VisualScriptNode); StringName base_type; String script; diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h index 7b1e7871b6..11eb96293b 100644 --- a/modules/visual_script/visual_script_func_nodes.h +++ b/modules/visual_script/visual_script_func_nodes.h @@ -35,7 +35,8 @@ class VisualScriptFunctionCall : public VisualScriptNode { - GDCLASS(VisualScriptFunctionCall, VisualScriptNode) + GDCLASS(VisualScriptFunctionCall, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -137,7 +138,8 @@ VARIANT_ENUM_CAST(VisualScriptFunctionCall::RPCCallMode); class VisualScriptPropertySet : public VisualScriptNode { - GDCLASS(VisualScriptPropertySet, VisualScriptNode) + GDCLASS(VisualScriptPropertySet, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -242,7 +244,8 @@ VARIANT_ENUM_CAST(VisualScriptPropertySet::AssignOp); class VisualScriptPropertyGet : public VisualScriptNode { - GDCLASS(VisualScriptPropertyGet, VisualScriptNode) + GDCLASS(VisualScriptPropertyGet, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, @@ -323,7 +326,7 @@ VARIANT_ENUM_CAST(VisualScriptPropertyGet::CallMode); class VisualScriptEmitSignal : public VisualScriptNode { - GDCLASS(VisualScriptEmitSignal, VisualScriptNode) + GDCLASS(VisualScriptEmitSignal, VisualScriptNode); private: StringName name; diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index b3d199fb38..762a1bdfb6 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -35,7 +35,7 @@ class VisualScriptFunction : public VisualScriptNode { - GDCLASS(VisualScriptFunction, VisualScriptNode) + GDCLASS(VisualScriptFunction, VisualScriptNode); struct Argument { String name; @@ -105,7 +105,7 @@ public: class VisualScriptOperator : public VisualScriptNode { - GDCLASS(VisualScriptOperator, VisualScriptNode) + GDCLASS(VisualScriptOperator, VisualScriptNode); Variant::Type typed; Variant::Operator op; @@ -141,7 +141,7 @@ public: class VisualScriptSelect : public VisualScriptNode { - GDCLASS(VisualScriptSelect, VisualScriptNode) + GDCLASS(VisualScriptSelect, VisualScriptNode); Variant::Type typed; @@ -174,7 +174,7 @@ public: class VisualScriptVariableGet : public VisualScriptNode { - GDCLASS(VisualScriptVariableGet, VisualScriptNode) + GDCLASS(VisualScriptVariableGet, VisualScriptNode); StringName variable; @@ -207,7 +207,7 @@ public: class VisualScriptVariableSet : public VisualScriptNode { - GDCLASS(VisualScriptVariableSet, VisualScriptNode) + GDCLASS(VisualScriptVariableSet, VisualScriptNode); StringName variable; @@ -240,7 +240,7 @@ public: class VisualScriptConstant : public VisualScriptNode { - GDCLASS(VisualScriptConstant, VisualScriptNode) + GDCLASS(VisualScriptConstant, VisualScriptNode); Variant::Type type; Variant value; @@ -277,7 +277,7 @@ public: class VisualScriptPreload : public VisualScriptNode { - GDCLASS(VisualScriptPreload, VisualScriptNode) + GDCLASS(VisualScriptPreload, VisualScriptNode); Ref<Resource> preload; @@ -309,7 +309,7 @@ public: class VisualScriptIndexGet : public VisualScriptNode { - GDCLASS(VisualScriptIndexGet, VisualScriptNode) + GDCLASS(VisualScriptIndexGet, VisualScriptNode); public: virtual int get_output_sequence_port_count() const; @@ -333,7 +333,7 @@ public: class VisualScriptIndexSet : public VisualScriptNode { - GDCLASS(VisualScriptIndexSet, VisualScriptNode) + GDCLASS(VisualScriptIndexSet, VisualScriptNode); public: virtual int get_output_sequence_port_count() const; @@ -357,7 +357,7 @@ public: class VisualScriptGlobalConstant : public VisualScriptNode { - GDCLASS(VisualScriptGlobalConstant, VisualScriptNode) + GDCLASS(VisualScriptGlobalConstant, VisualScriptNode); int index; @@ -388,7 +388,7 @@ public: class VisualScriptClassConstant : public VisualScriptNode { - GDCLASS(VisualScriptClassConstant, VisualScriptNode) + GDCLASS(VisualScriptClassConstant, VisualScriptNode); StringName base_type; StringName name; @@ -425,7 +425,7 @@ public: class VisualScriptBasicTypeConstant : public VisualScriptNode { - GDCLASS(VisualScriptBasicTypeConstant, VisualScriptNode) + GDCLASS(VisualScriptBasicTypeConstant, VisualScriptNode); Variant::Type type; StringName name; @@ -463,7 +463,8 @@ public: class VisualScriptMathConstant : public VisualScriptNode { - GDCLASS(VisualScriptMathConstant, VisualScriptNode) + GDCLASS(VisualScriptMathConstant, VisualScriptNode); + public: enum MathConstant { MATH_CONSTANT_ONE, @@ -512,7 +513,7 @@ VARIANT_ENUM_CAST(VisualScriptMathConstant::MathConstant) class VisualScriptEngineSingleton : public VisualScriptNode { - GDCLASS(VisualScriptEngineSingleton, VisualScriptNode) + GDCLASS(VisualScriptEngineSingleton, VisualScriptNode); String singleton; @@ -545,7 +546,7 @@ public: class VisualScriptSceneNode : public VisualScriptNode { - GDCLASS(VisualScriptSceneNode, VisualScriptNode) + GDCLASS(VisualScriptSceneNode, VisualScriptNode); NodePath path; @@ -580,7 +581,7 @@ public: class VisualScriptSceneTree : public VisualScriptNode { - GDCLASS(VisualScriptSceneTree, VisualScriptNode) + GDCLASS(VisualScriptSceneTree, VisualScriptNode); protected: virtual void _validate_property(PropertyInfo &property) const; @@ -610,7 +611,7 @@ public: class VisualScriptResourcePath : public VisualScriptNode { - GDCLASS(VisualScriptResourcePath, VisualScriptNode) + GDCLASS(VisualScriptResourcePath, VisualScriptNode); String path; @@ -642,7 +643,7 @@ public: class VisualScriptSelf : public VisualScriptNode { - GDCLASS(VisualScriptSelf, VisualScriptNode) + GDCLASS(VisualScriptSelf, VisualScriptNode); protected: static void _bind_methods(); @@ -671,7 +672,7 @@ public: class VisualScriptCustomNode : public VisualScriptNode { - GDCLASS(VisualScriptCustomNode, VisualScriptNode) + GDCLASS(VisualScriptCustomNode, VisualScriptNode); protected: static void _bind_methods(); @@ -719,7 +720,7 @@ VARIANT_ENUM_CAST(VisualScriptCustomNode::StartMode); class VisualScriptSubCall : public VisualScriptNode { - GDCLASS(VisualScriptSubCall, VisualScriptNode) + GDCLASS(VisualScriptSubCall, VisualScriptNode); protected: static void _bind_methods(); @@ -747,7 +748,7 @@ public: class VisualScriptComment : public VisualScriptNode { - GDCLASS(VisualScriptComment, VisualScriptNode) + GDCLASS(VisualScriptComment, VisualScriptNode); String title; String description; @@ -788,7 +789,7 @@ public: class VisualScriptConstructor : public VisualScriptNode { - GDCLASS(VisualScriptConstructor, VisualScriptNode) + GDCLASS(VisualScriptConstructor, VisualScriptNode); Variant::Type type; MethodInfo constructor; @@ -824,7 +825,7 @@ public: class VisualScriptLocalVar : public VisualScriptNode { - GDCLASS(VisualScriptLocalVar, VisualScriptNode) + GDCLASS(VisualScriptLocalVar, VisualScriptNode); StringName name; Variant::Type type; @@ -860,7 +861,7 @@ public: class VisualScriptLocalVarSet : public VisualScriptNode { - GDCLASS(VisualScriptLocalVarSet, VisualScriptNode) + GDCLASS(VisualScriptLocalVarSet, VisualScriptNode); StringName name; Variant::Type type; @@ -897,7 +898,8 @@ public: class VisualScriptInputAction : public VisualScriptNode { - GDCLASS(VisualScriptInputAction, VisualScriptNode) + GDCLASS(VisualScriptInputAction, VisualScriptNode); + public: enum Mode { MODE_PRESSED, @@ -944,7 +946,7 @@ VARIANT_ENUM_CAST(VisualScriptInputAction::Mode) class VisualScriptDeconstruct : public VisualScriptNode { - GDCLASS(VisualScriptDeconstruct, VisualScriptNode) + GDCLASS(VisualScriptDeconstruct, VisualScriptNode); struct Element { StringName name; diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 1588243bc1..6235e4ba1d 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -36,7 +36,7 @@ #include "scene/gui/rich_text_label.h" class VisualScriptPropertySelector : public ConfirmationDialog { - GDCLASS(VisualScriptPropertySelector, ConfirmationDialog) + GDCLASS(VisualScriptPropertySelector, ConfirmationDialog); LineEdit *search_box; Tree *search_options; diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h index 851bf6aa74..647f2b1e2e 100644 --- a/modules/visual_script/visual_script_yield_nodes.h +++ b/modules/visual_script/visual_script_yield_nodes.h @@ -35,7 +35,8 @@ class VisualScriptYield : public VisualScriptNode { - GDCLASS(VisualScriptYield, VisualScriptNode) + GDCLASS(VisualScriptYield, VisualScriptNode); + public: enum YieldMode { YIELD_RETURN, @@ -84,7 +85,8 @@ VARIANT_ENUM_CAST(VisualScriptYield::YieldMode) class VisualScriptYieldSignal : public VisualScriptNode { - GDCLASS(VisualScriptYieldSignal, VisualScriptNode) + GDCLASS(VisualScriptYieldSignal, VisualScriptNode); + public: enum CallMode { CALL_MODE_SELF, diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 692705e411..e652abbe6a 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -143,8 +143,7 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { bool ok = ov_time_seek(&vf, loop_restart_time) == 0; if (!ok) { playing = false; - //ERR_EXPLAIN("loop restart time rejected"); - ERR_PRINT("loop restart time rejected") + ERR_PRINT("Loop restart time rejected"); } frames_mixed = stream_srate * loop_restart_time; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index fa9d5fe664..a37867d9f9 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -127,7 +127,6 @@ public: }; class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderAudioStreamOGGVorbis, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/webm/config.py b/modules/webm/config.py index 72a4073423..ba4dcce2f5 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -6,7 +6,6 @@ def configure(env): def get_doc_classes(): return [ - "ResourceImporterWebm", "VideoStreamWebm", ] diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index a6be1380a6..c76585013c 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -384,8 +384,6 @@ elif webm_cpu_arm: env_libvpx.add_source_files(env.modules_sources, [libvpx_dir + "third_party/android/cpu-features.c"]) env_libvpx_neon = env_libvpx.Clone() - if env["platform"] == 'android' and env["android_arch"] == 'armv6': - env_libvpx_neon.Append(CCFLAGS=['-mfpu=neon']) env_libvpx_neon.add_source_files(env.modules_sources, libvpx_sources_arm_neon) if env["platform"] == 'uwp': diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 6485c95360..3670edc9ea 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -413,10 +413,11 @@ void VideoStreamPlaybackWebm::delete_pointers() { if (audio_frame) memdelete(audio_frame); - for (int i = 0; i < video_frames_capacity; ++i) - memdelete(video_frames[i]); - if (video_frames) + if (video_frames) { + for (int i = 0; i < video_frames_capacity; ++i) + memdelete(video_frames[i]); memfree(video_frames); + } if (video) memdelete(video); diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index 992095ba4c..ddcbb1eb08 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -41,7 +41,7 @@ class OpusVorbisDecoder; class VideoStreamPlaybackWebm : public VideoStreamPlayback { - GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback) + GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback); String file_name; int audio_track; @@ -127,7 +127,6 @@ public: }; class ResourceFormatLoaderWebm : public ResourceFormatLoader { - GDCLASS(ResourceFormatLoaderWebm, ResourceFormatLoader) public: virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual void get_recognized_extensions(List<String> *p_extensions) const; diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py index 2e3a18ad0e..48b4c33c5d 100644 --- a/modules/webrtc/config.py +++ b/modules/webrtc/config.py @@ -7,7 +7,8 @@ def configure(env): def get_doc_classes(): return [ "WebRTCPeerConnection", - "WebRTCDataChannel" + "WebRTCDataChannel", + "WebRTCMultiplayer" ] def get_doc_path(): diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml index dcc14d4ddb..7cce97244d 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml @@ -11,85 +11,106 @@ <return type="void"> </return> <description> + Closes this data channel, notifying the other peer. </description> </method> <method name="get_id" qualifiers="const"> <return type="int"> </return> <description> + Returns the id assigned to this channel during creation (or auto-assigned during negotiation). + If the channel is not negotiated out-of-band the id will only be available after the connection is established (will return [code]65535[/code] until then). </description> </method> <method name="get_label" qualifiers="const"> <return type="String"> </return> <description> + Returns the label assigned to this channel during creation. </description> </method> <method name="get_max_packet_life_time" qualifiers="const"> <return type="int"> </return> <description> + Returns the [code]maxPacketLifeTime[/code] value assigned to this channel during creation. + Will be [code]65535[/code] if not specified. </description> </method> <method name="get_max_retransmits" qualifiers="const"> <return type="int"> </return> <description> + Returns the [code]maxRetransmits[/code] value assigned to this channel during creation. + Will be [code]65535[/code] if not specified. </description> </method> <method name="get_protocol" qualifiers="const"> <return type="String"> </return> <description> + Returns the sub-protocol assigned to this channel during creation. An empty string if not specified. </description> </method> <method name="get_ready_state" qualifiers="const"> <return type="int" enum="WebRTCDataChannel.ChannelState"> </return> <description> + Returns the current state of this channel, see [enum WebRTCDataChannel.ChannelState]. </description> </method> <method name="is_negotiated" qualifiers="const"> <return type="bool"> </return> <description> + Returns [code]true[/code] if this channel was created with out-of-band configuration. </description> </method> <method name="is_ordered" qualifiers="const"> <return type="bool"> </return> <description> + Returns [code]true[/code] if this channel was created with ordering enabled (default). </description> </method> <method name="poll"> <return type="int" enum="Error"> </return> <description> + Reserved, but not used for now. </description> </method> <method name="was_string_packet" qualifiers="const"> <return type="bool"> </return> <description> + Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode]. </description> </method> </methods> <members> <member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode"> + The transfer mode to use when sending outgoing packet. Either text or binary. </member> </members> <constants> <constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode"> + Tells the channel to send data over this channel as text. An external peer (non-Godot) would receive this as a string. </constant> <constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode"> + Tells the channel to send data over this channel as binary. An external peer (non-Godot) would receive this as array buffer or blob. </constant> <constant name="STATE_CONNECTING" value="0" enum="ChannelState"> + The channel was created, but it's still trying to connect. </constant> <constant name="STATE_OPEN" value="1" enum="ChannelState"> + The channel is currently open, and data can flow over it. </constant> <constant name="STATE_CLOSING" value="2" enum="ChannelState"> + The channel is being closed, no new messages will be accepted, but those already in queue will be flushed. </constant> <constant name="STATE_CLOSED" value="3" enum="ChannelState"> + The channel was closed, or connection failed. </constant> </constants> </class> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml new file mode 100644 index 0000000000..2b0622fffa --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCMultiplayer.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCMultiplayer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2"> + <brief_description> + A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. + </brief_description> + <description> + This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer]. + You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. + [signal NetworkedMultiplayerPeer.connection_succeeded] and [signal NetworkedMultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [NetworkedMultiplayerPeer]. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_peer"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="peer" type="WebRTCPeerConnection"> + </argument> + <argument index="1" name="peer_id" type="int"> + </argument> + <argument index="2" name="unreliable_lifetime" type="int" default="1"> + </argument> + <description> + Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW]. + Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]). + </description> + </method> + <method name="close"> + <return type="void"> + </return> + <description> + Close all the add peer connections and channels, freeing all resources. + </description> + </method> + <method name="get_peer"> + <return type="Dictionary"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open). + </description> + </method> + <method name="get_peers"> + <return type="Dictionary"> + </return> + <description> + Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer] + </description> + </method> + <method name="has_peer"> + <return type="bool"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though). + </description> + </method> + <method name="initialize"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <argument index="1" name="server_compatibility" type="bool" default="false"> + </argument> + <description> + Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). + If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED] and [signal NetworkedMultiplayerPeer.connection_succeeded] will not be emitted. + If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal NetworkedMultiplayerPeer.peer_connected] signals until a peer with id [constant NetworkedMultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal NetworkedMultiplayerPeer.connection_succeeded]. After that the signal [signal NetworkedMultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal NetworkedMultiplayerPeer.server_disconnected] will be emitted and state will become [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED]. + </description> + </method> + <method name="remove_peer"> + <return type="void"> + </return> + <argument index="0" name="peer_id" type="int"> + </argument> + <description> + Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal NetworkedMultiplayerPeer.peer_connected] was emitted for it, then [signal NetworkedMultiplayerPeer.peer_disconnected] will be emitted. + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 8b14c60deb..ae709877f4 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -1,8 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="WebRTCPeerConnection" inherits="Reference" category="Core" version="3.2"> <brief_description> + Interface to a WebRTC peer connection. </brief_description> <description> + A WebRTC connection between the local computer and a remote peer. Provides an interface to connect, maintain and monitor the connection. + Setting up a WebRTC connection between two peers from now on) may not seem a trivial task, but it can be broken down into 3 main steps: + - The peer that wants to initiate the connection ([code]A[/code] from now on) creates an offer and send it to the other peer ([code]B[/code] from now on). + - [code]B[/code] receives the offer, generate and answer, and sends it to [code]B[/code]). + - [code]A[/code] and [code]B[/code] then generates and exchange ICE candidates with each other. + After these steps, the connection should become connected. Keep on reading or look into the tutorial for more information. </description> <tutorials> </tutorials> @@ -17,12 +24,14 @@ <argument index="2" name="name" type="String"> </argument> <description> + Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created]. </description> </method> <method name="close"> <return type="void"> </return> <description> + Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize]. </description> </method> <method name="create_data_channel"> @@ -35,18 +44,38 @@ }"> </argument> <description> + Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW]. + There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code]. + Valid [code]options[/code] are: + [codeblock] + { + "negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. data_channel_received will not be called. + "id": 1, # When "negotiated" is true this value must also be set to the same value on both peer. + + # Only one of maxRetransmits and maxPacketLifeTime can be specified, not both. They make the channel unreliable (but also better at real time). + "maxRetransmits": 1, # Specify the maximum number of attempt the peer will make to retransmits packets if they are not acknowledged. + "maxPacketLifeTime": 100, # Specify the maximum amount of time before giving up retransmitions of unacknowledged packets (in milliseconds). + "ordered": true, # When in unreliable mode (i.e. either "maxRetransmits" or "maxPacketLifetime" is set), "ordered" (true by default) specify if packet ordering is to be enforced. + + "protocol": "my-custom-protocol", # A custom sub-protocol string for this channel. + } + [/codeblock] + NOTE: You must keep a reference to channels created this way, or it will be closed. </description> </method> <method name="create_offer"> <return type="int" enum="Error"> </return> <description> + Creates a new SDP offer to start a WebRTC connection with a remote peer. At least one [WebRTCDataChannel] must have been created before calling this method. + If this functions returns [code]OK[/code], [signal session_description_created] will be called when the session is ready to be sent. </description> </method> <method name="get_connection_state" qualifiers="const"> <return type="int" enum="WebRTCPeerConnection.ConnectionState"> </return> <description> + Returns the connection state. See [enum ConnectionState]. </description> </method> <method name="initialize"> @@ -57,12 +86,29 @@ }"> </argument> <description> + Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection. + Valid [code]options[/code] are: + [codeblock] + { + "iceServers": [ + { + "urls": [ "stun:stun.example.com:3478" ], # One or more STUN servers. + }, + { + "urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers. + "username": "a_username", # Optional username for the TURN server. + "credentials": "a_password", # Optional password for the TURN server. + } + ] + } + [/codeblock] </description> </method> <method name="poll"> <return type="int" enum="Error"> </return> <description> + Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals. </description> </method> <method name="set_local_description"> @@ -73,6 +119,8 @@ <argument index="1" name="sdp" type="String"> </argument> <description> + Sets the SDP description of the local peer. This should be called in response to [signal session_description_created]. + If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. </description> </method> <method name="set_remote_description"> @@ -83,6 +131,9 @@ <argument index="1" name="sdp" type="String"> </argument> <description> + Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server. + If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer. + If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. </description> </method> </methods> @@ -91,6 +142,8 @@ <argument index="0" name="channel" type="Object"> </argument> <description> + Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default). + The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel] </description> </signal> <signal name="ice_candidate_created"> @@ -101,6 +154,7 @@ <argument index="2" name="name" type="String"> </argument> <description> + Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server. </description> </signal> <signal name="session_description_created"> @@ -109,21 +163,28 @@ <argument index="1" name="sdp" type="String"> </argument> <description> + Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server. </description> </signal> </signals> <constants> <constant name="STATE_NEW" value="0" enum="ConnectionState"> + The connection is new, data channels and an offer can be created in this state. </constant> <constant name="STATE_CONNECTING" value="1" enum="ConnectionState"> + The peer is connecting, ICE is in progress, non of the transports has failed. </constant> <constant name="STATE_CONNECTED" value="2" enum="ConnectionState"> + The peer is connected, all ICE transports are connected. </constant> <constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState"> + At least one ICE transport is disconnected. </constant> <constant name="STATE_FAILED" value="4" enum="ConnectionState"> + One or more of the ICE transports failed. </constant> <constant name="STATE_CLOSED" value="5" enum="ConnectionState"> + The peer connection is closed (after calling [method close] for example). </constant> </constants> </class> diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index 44e072cc89..58b68d926b 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -40,6 +40,7 @@ #include "webrtc_data_channel_gdnative.h" #include "webrtc_peer_connection_gdnative.h" #endif +#include "webrtc_multiplayer.h" void register_webrtc_types() { #ifdef JAVASCRIPT_ENABLED @@ -54,6 +55,7 @@ void register_webrtc_types() { ClassDB::register_class<WebRTCDataChannelGDNative>(); #endif ClassDB::register_virtual_class<WebRTCDataChannel>(); + ClassDB::register_class<WebRTCMultiplayer>(); } void unregister_webrtc_types() {} diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp index 2e7c64aa72..069918cc9c 100644 --- a/modules/webrtc/webrtc_data_channel_js.cpp +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -68,7 +68,7 @@ void WebRTCDataChannelJS::_on_error() { } void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) { - if (in_buffer.space_left() < p_size + 5) { + if (in_buffer.space_left() < (int)(p_size + 5)) { ERR_EXPLAIN("Buffer full! Dropping data"); ERR_FAIL(); } @@ -205,30 +205,45 @@ String WebRTCDataChannelJS::get_label() const { } /* clang-format off */ -#define _JS_GET(PROP) \ +#define _JS_GET(PROP, DEF) \ EM_ASM_INT({ \ var dict = Module.IDHandler.get($0); \ if (!dict || !dict["channel"]) { \ - return 0; \ - }; \ - return dict["channel"].PROP; \ + return DEF; \ + } \ + var out = dict["channel"].PROP; \ + return out === null ? DEF : out; \ }, _js_id) /* clang-format on */ bool WebRTCDataChannelJS::is_ordered() const { - return _JS_GET(ordered); + return _JS_GET(ordered, true); } int WebRTCDataChannelJS::get_id() const { - return _JS_GET(id); + return _JS_GET(id, 65535); } int WebRTCDataChannelJS::get_max_packet_life_time() const { - return _JS_GET(maxPacketLifeTime); + // Can't use macro, webkit workaround. + /* clang-format off */ + return EM_ASM_INT({ + var dict = Module.IDHandler.get($0); + if (!dict || !dict["channel"]) { + return 65535; + } + if (dict["channel"].maxRetransmitTime !== undefined) { + // Guess someone didn't appreciate the standardization process. + return dict["channel"].maxRetransmitTime; + } + var out = dict["channel"].maxPacketLifeTime; + return out === null ? 65535 : out; + }, _js_id); + /* clang-format on */ } int WebRTCDataChannelJS::get_max_retransmits() const { - return _JS_GET(maxRetransmits); + return _JS_GET(maxRetransmits, 65535); } String WebRTCDataChannelJS::get_protocol() const { @@ -236,7 +251,7 @@ String WebRTCDataChannelJS::get_protocol() const { } bool WebRTCDataChannelJS::is_negotiated() const { - return _JS_GET(negotiated); + return _JS_GET(negotiated, false); } WebRTCDataChannelJS::WebRTCDataChannelJS() { diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp new file mode 100644 index 0000000000..17dafff93a --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer.cpp @@ -0,0 +1,384 @@ +/*************************************************************************/ +/* webrtc_multiplayer.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_multiplayer.h" + +#include "core/io/marshalls.h" +#include "core/os/os.h" + +void WebRTCMultiplayer::_bind_methods() { + ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayer::initialize, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayer::add_peer, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayer::remove_peer); + ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayer::has_peer); + ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayer::get_peer); + ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayer::get_peers); + ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayer::close); +} + +void WebRTCMultiplayer::set_transfer_mode(TransferMode p_mode) { + transfer_mode = p_mode; +} + +NetworkedMultiplayerPeer::TransferMode WebRTCMultiplayer::get_transfer_mode() const { + return transfer_mode; +} + +void WebRTCMultiplayer::set_target_peer(int p_peer_id) { + target_peer = p_peer_id; +} + +/* Returns the ID of the NetworkedMultiplayerPeer who sent the most recent packet: */ +int WebRTCMultiplayer::get_packet_peer() const { + return next_packet_peer; +} + +bool WebRTCMultiplayer::is_server() const { + return unique_id == TARGET_PEER_SERVER; +} + +void WebRTCMultiplayer::poll() { + if (peer_map.size() == 0) + return; + + List<int> remove; + List<int> add; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + Ref<ConnectedPeer> peer = E->get(); + peer->connection->poll(); + // Check peer state + switch (peer->connection->get_connection_state()) { + case WebRTCPeerConnection::STATE_NEW: + case WebRTCPeerConnection::STATE_CONNECTING: + // Go to next peer, not ready yet. + continue; + case WebRTCPeerConnection::STATE_CONNECTED: + // Good to go, go ahead and check channel state. + break; + default: + // Peer is closed or in error state. Got to next peer. + remove.push_back(E->key()); + continue; + } + // Check channels state + int ready = 0; + for (List<Ref<WebRTCDataChannel> >::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) { + Ref<WebRTCDataChannel> ch = C->get(); + switch (ch->get_ready_state()) { + case WebRTCDataChannel::STATE_CONNECTING: + continue; + case WebRTCDataChannel::STATE_OPEN: + ready++; + continue; + default: + // Channel was closed or in error state, remove peer id. + remove.push_back(E->key()); + } + // We got a closed channel break out, the peer will be removed. + break; + } + // This peer has newly connected, and all channels are now open. + if (ready == peer->channels.size() && !peer->connected) { + peer->connected = true; + add.push_back(E->key()); + } + } + // Remove disconnected peers + for (List<int>::Element *E = remove.front(); E; E = E->next()) { + remove_peer(E->get()); + if (next_packet_peer == E->get()) + next_packet_peer = 0; + } + // Signal newly connected peers + for (List<int>::Element *E = add.front(); E; E = E->next()) { + // Already connected to server: simply notify new peer. + // NOTE: Mesh is always connected. + if (connection_status == CONNECTION_CONNECTED) + emit_signal("peer_connected", E->get()); + + // Server emulation mode suppresses peer_conencted until server connects. + if (server_compat && E->get() == TARGET_PEER_SERVER) { + // Server connected. + connection_status = CONNECTION_CONNECTED; + emit_signal("peer_connected", TARGET_PEER_SERVER); + emit_signal("connection_succeeded"); + // Notify of all previously connected peers + for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) { + if (F->key() != 1 && F->get()->connected) + emit_signal("peer_connected", F->key()); + } + break; // Because we already notified of all newly added peers. + } + } + // Fetch next packet + if (next_packet_peer == 0) + _find_next_peer(); +} + +void WebRTCMultiplayer::_find_next_peer() { + Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.find(next_packet_peer); + if (E) E = E->next(); + // After last. + while (E) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + E = E->next(); + } + E = peer_map.front(); + // Before last + while (E) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + if (F->get()->get_available_packet_count()) { + next_packet_peer = E->key(); + return; + } + } + if (E->key() == (int)next_packet_peer) + break; + E = E->next(); + } + // No packet found + next_packet_peer = 0; +} + +void WebRTCMultiplayer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool WebRTCMultiplayer::is_refusing_new_connections() const { + return refuse_connections; +} + +NetworkedMultiplayerPeer::ConnectionStatus WebRTCMultiplayer::get_connection_status() const { + return connection_status; +} + +Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) { + ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); + unique_id = p_self_id; + server_compat = p_server_compat; + + // Mesh and server are always connected + if (!server_compat || p_self_id == 1) + connection_status = CONNECTION_CONNECTED; + else + connection_status = CONNECTION_CONNECTING; + return OK; +} + +int WebRTCMultiplayer::get_unique_id() const { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1); + return unique_id; +} + +void WebRTCMultiplayer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) { + Array channels; + for (List<Ref<WebRTCDataChannel> >::Element *F = p_connected_peer->channels.front(); F; F = F->next()) { + channels.push_back(F->get()); + } + r_dict["connection"] = p_connected_peer->connection; + r_dict["connected"] = p_connected_peer->connected; + r_dict["channels"] = channels; +} + +bool WebRTCMultiplayer::has_peer(int p_peer_id) { + return peer_map.has(p_peer_id); +} + +Dictionary WebRTCMultiplayer::get_peer(int p_peer_id) { + ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary()); + Dictionary out; + _peer_to_dict(peer_map[p_peer_id], out); + return out; +} + +Dictionary WebRTCMultiplayer::get_peers() { + Dictionary out; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + Dictionary d; + _peer_to_dict(E->get(), d); + out[E->key()] = d; + } + return out; +} + +Error WebRTCMultiplayer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) { + ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED); + // Peer must be valid, and in new state (to create data channels) + ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER); + + Ref<ConnectedPeer> peer = memnew(ConnectedPeer); + peer->connection = p_peer; + + // Initialize data channels + Dictionary cfg; + cfg["negotiated"] = true; + cfg["ordered"] = true; + + cfg["id"] = 1; + peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED); + + cfg["id"] = 2; + cfg["maxPacketLifetime"] = p_unreliable_lifetime; + peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED); + + cfg["id"] = 3; + cfg["ordered"] = false; + peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg); + ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED); + + peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map + + return OK; +} + +void WebRTCMultiplayer::remove_peer(int p_peer_id) { + ERR_FAIL_COND(!peer_map.has(p_peer_id)); + Ref<ConnectedPeer> peer = peer_map[p_peer_id]; + peer_map.erase(p_peer_id); + if (peer->connected) { + peer->connected = false; + emit_signal("peer_disconnected", p_peer_id); + if (server_compat && p_peer_id == TARGET_PEER_SERVER) { + emit_signal("server_disconnected"); + connection_status = CONNECTION_DISCONNECTED; + } + } +} + +Error WebRTCMultiplayer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + // Peer not available + if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) { + _find_next_peer(); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + for (List<Ref<WebRTCDataChannel> >::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) { + if (E->get()->get_available_packet_count()) { + Error err = E->get()->get_packet(r_buffer, r_buffer_size); + _find_next_peer(); + return err; + } + } + // Channels for that peer were empty. Bug? + _find_next_peer(); + ERR_FAIL_V(ERR_BUG); +} + +Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); + + int ch = CH_RELIABLE; + switch (transfer_mode) { + case TRANSFER_MODE_RELIABLE: + ch = CH_RELIABLE; + break; + case TRANSFER_MODE_UNRELIABLE_ORDERED: + ch = CH_ORDERED; + break; + case TRANSFER_MODE_UNRELIABLE: + ch = CH_UNRELIABLE; + break; + } + + Map<int, Ref<ConnectedPeer> >::Element *E = NULL; + + if (target_peer > 0) { + + E = peer_map.find(target_peer); + if (!E) { + ERR_EXPLAIN("Invalid Target Peer: " + itos(target_peer)); + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG); + ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG); + return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + + } else { + int exclude = -target_peer; + + for (Map<int, Ref<ConnectedPeer> >::Element *F = peer_map.front(); F; F = F->next()) { + + // Exclude packet. If target_peer == 0 then don't exclude any packets + if (target_peer != 0 && F->key() == exclude) + continue; + + ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); + F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); + } + } + return OK; +} + +int WebRTCMultiplayer::get_available_packet_count() const { + if (next_packet_peer == 0) + return 0; // To be sure next call to get_packet works if size > 0 . + int size = 0; + for (Map<int, Ref<ConnectedPeer> >::Element *E = peer_map.front(); E; E = E->next()) { + for (List<Ref<WebRTCDataChannel> >::Element *F = E->get()->channels.front(); F; F = F->next()) { + size += F->get()->get_available_packet_count(); + } + } + return size; +} + +int WebRTCMultiplayer::get_max_packet_size() const { + return 1200; +} + +void WebRTCMultiplayer::close() { + peer_map.clear(); + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + connection_status = CONNECTION_DISCONNECTED; +} + +WebRTCMultiplayer::WebRTCMultiplayer() { + unique_id = 0; + next_packet_peer = 0; + target_peer = 0; + transfer_mode = TRANSFER_MODE_RELIABLE; + refuse_connections = false; + connection_status = CONNECTION_DISCONNECTED; + server_compat = false; +} + +WebRTCMultiplayer::~WebRTCMultiplayer() { + close(); +} diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h new file mode 100644 index 0000000000..82bbfd4f68 --- /dev/null +++ b/modules/webrtc/webrtc_multiplayer.h @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* webrtc_multiplayer.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_MULTIPLAYER_H +#define WEBRTC_MULTIPLAYER_H + +#include "core/io/networked_multiplayer_peer.h" +#include "webrtc_peer_connection.h" + +class WebRTCMultiplayer : public NetworkedMultiplayerPeer { + + GDCLASS(WebRTCMultiplayer, NetworkedMultiplayerPeer); + +protected: + static void _bind_methods(); + +private: + enum { + CH_RELIABLE = 0, + CH_ORDERED = 1, + CH_UNRELIABLE = 2, + CH_RESERVED_MAX = 3 + }; + + class ConnectedPeer : public Reference { + + public: + Ref<WebRTCPeerConnection> connection; + List<Ref<WebRTCDataChannel> > channels; + bool connected; + + ConnectedPeer() { + connected = false; + for (int i = 0; i < CH_RESERVED_MAX; i++) + channels.push_front(Ref<WebRTCDataChannel>()); + } + }; + + uint32_t unique_id; + int target_peer; + int client_count; + bool refuse_connections; + ConnectionStatus connection_status; + TransferMode transfer_mode; + int next_packet_peer; + bool server_compat; + + Map<int, Ref<ConnectedPeer> > peer_map; + + void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict); + void _find_next_peer(); + +public: + WebRTCMultiplayer(); + ~WebRTCMultiplayer(); + + Error initialize(int p_self_id, bool p_server_compat = false); + Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1); + void remove_peer(int p_peer_id); + bool has_peer(int p_peer_id); + Dictionary get_peer(int p_peer_id); + Dictionary get_peers(); + void close(); + + // PacketPeer + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + int get_available_packet_count() const; + int get_max_packet_size() const; + + // NetworkedMultiplayerPeer + void set_transfer_mode(TransferMode p_mode); + TransferMode get_transfer_mode() const; + void set_target_peer(int p_peer_id); + + int get_unique_id() const; + int get_packet_peer() const; + + bool is_server() const; + + void poll(); + + void set_refuse_new_connections(bool p_enable); + bool is_refusing_new_connections() const; + + ConnectionStatus get_connection_status() const; +}; + +#endif diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 8548d21323..103dbd771b 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -18,6 +18,24 @@ Returns the [WebSocketPeer] associated to the given [code]peer_id[/code]. </description> </method> + <method name="set_buffers"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="input_buffer_size_kb" type="int"> + </argument> + <argument index="1" name="input_max_packets" type="int"> + </argument> + <argument index="2" name="output_buffer_size_kb" type="int"> + </argument> + <argument index="3" name="output_max_packets" type="int"> + </argument> + <description> + Configure the buffers sizes for this WebSocket peer. Default values can be specified in project settings under [code]network/limits[/code]. For server, values are meant per connected peer. + The first two parameters define the size and queued packets limits of the input buffer, the last two of the output buffer. + Buffer sizes are expressed in KiB, so [code]4 = 2^12 = 4096 bytes[/code]. All parameters will be rounded up to the nearest power of two. + NOTE: HTML5 exports only use the input buffer since the output one is managed by browsers. + </description> + </method> </methods> <signals> <signal name="peer_packet"> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index aafae8f1c2..409cc9f699 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -205,6 +205,12 @@ int EMWSClient::get_max_packet_size() const { return (1 << _in_buf_size) - PROTO_SIZE; } +Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + return OK; +} + EMWSClient::EMWSClient() { _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 9bc29c1913..1811d05eea 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -49,6 +49,7 @@ private: public: bool _is_connecting; + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 0eef1c4ba9..c4bb459ad0 100644 --- a/modules/websocket/emws_server.cpp +++ b/modules/websocket/emws_server.cpp @@ -79,6 +79,10 @@ int EMWSServer::get_max_packet_size() const { return 0; } +Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + return OK; +} + EMWSServer::EMWSServer() { } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index bb101cd155..a5e5b4090e 100644 --- a/modules/websocket/emws_server.h +++ b/modules/websocket/emws_server.h @@ -42,6 +42,7 @@ class EMWSServer : public WebSocketServer { GDCIIMPL(EMWSServer, WebSocketServer); public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index 08df76293b..f139a4ef66 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -215,6 +215,17 @@ uint16_t LWSClient::get_connected_port() const { return 1025; }; +Error LWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting"); + ERR_FAIL_COND_V(context != NULL, FAILED); + + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + _out_buf_size = nearest_shift(p_out_buffer - 1) + 10; + _out_pkt_size = nearest_shift(p_out_packets - 1); + return OK; +} + LWSClient::LWSClient() { _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10; _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1); diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index b3a1237550..df4aabec7f 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_client.h @@ -51,6 +51,7 @@ private: int _out_pkt_size; public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index 61ccdca74a..482c02dc60 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -197,6 +197,17 @@ void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) { get_peer(p_peer_id)->close(p_code, p_reason); } +Error LWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { + ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting"); + ERR_FAIL_COND_V(context != NULL, FAILED); + + _in_buf_size = nearest_shift(p_in_buffer - 1) + 10; + _in_pkt_size = nearest_shift(p_in_packets - 1); + _out_buf_size = nearest_shift(p_out_buffer - 1) + 10; + _out_pkt_size = nearest_shift(p_out_packets - 1); + return OK; +} + LWSServer::LWSServer() { _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10; _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1); diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 5096ee0f88..b331852d26 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_server.h @@ -52,6 +52,7 @@ private: int _out_pkt_size; public: + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index c464d97c7f..7ddb9468a5 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -67,6 +67,8 @@ public: void _on_disconnect(bool p_was_clean); void _on_error(); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; + WebSocketClient(); ~WebSocketClient(); }; diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 6aab8a7e81..23cbf916eb 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -87,6 +87,7 @@ void WebSocketMultiplayerPeer::_clear() { void WebSocketMultiplayerPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_buffers", "input_buffer_size_kb", "input_max_packets", "output_buffer_size_kb", "output_max_packets"), &WebSocketMultiplayerPeer::set_buffers); ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer); ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source"))); diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index b050449ee0..089bc25fe9 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -97,6 +97,7 @@ public: virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); /* WebSocketPeer */ + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0; void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id); diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 7a94c4047b..83c0c10419 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -62,6 +62,8 @@ public: void _on_disconnect(int32_t p_peer_id, bool p_was_clean); void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; + WebSocketServer(); ~WebSocketServer(); }; |